1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! This module provides ways to identify chunks of memory.
//!
//! It defines the trait [Identifier] and structs [Address], [Area] and [Cursor].

use std::hash::{Hash, Hasher};

use crate::data::atomic::Word;

/// A trait which means a type can transform into an `usize`.
pub trait Identifier: Hash {
    fn to_usize(&self) -> usize;
}

/// Describes an address in memory.
///
/// Addresses are 64 bits wide.
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Address(u64);

impl Address {
    /// Creates a new [Address] from an [u64].
    ///
    /// ```
    /// use osiris_data::data::identification::Address;
    /// assert_eq!(Address::new(0), Address::default())
    /// ```
    pub const fn new(value: u64) -> Self {
        Self(value)
    }

    /// Gets the address value as an [u64].
    pub const fn to_u64(&self) -> u64 { self.0 }

    /// Creates a new [Address] from a [Word].
    pub const fn from_word(word: Word) -> Self { Self::new(word.to_u64()) }

    /// Creates a new [Address] from any [Identifier].
    pub fn from<T: Identifier>(identifier: T) -> Self { Self::new(identifier.to_usize() as u64) }

    /// Gets the address value as a machine's [Word] to manipulate it with memory.
    pub const fn to_word(&self) -> Word { Word::new(self.0) }

    /// Increment the address value.
    pub fn increment(&mut self) {
        self.0 += 1;
    }

    /// Returns a new Address with an offset.
    pub fn offset(&self, offset: u64) -> Address { Address(self.0 + offset) }
}

impl Hash for Address {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write(&self.0.to_be_bytes());
    }
}

impl Identifier for Address {
    /// Gets the address value as an [usize].
    fn to_usize(&self) -> usize { self.to_u64() as usize }
}

/// A pointer to a region in memory.
#[derive(Copy, Clone)]
pub struct Area {
    start: Address,
    len: usize,
    count: usize,
}

impl Area {
    /// Creates a new area with specified item size (how many 64 bits values describes one element of the area).
    pub fn new(start: Address, item_size: usize, item_count: usize) -> Self {
        Self { start, len: item_size, count: item_count }
    }

    /// A one-sized cell area.
    pub fn region(start: Address, count: usize) -> Self { Self::new(start, 1, count) }

    /// Returns an area enclosing the specified addresses.
    pub fn from(start: Address, end: Address) -> Self {
        if start > end {
            panic!("start > end")
        }
        Self::new(start, 1, end.to_usize() - start.to_usize())
    }

    /// Returns the number of [Address]es distinguished in this area.
    pub fn count(&self) -> usize { self.count }

    /// Returns the number of [Word]s contained in this area.
    pub fn size(&self) -> usize { self.offset(self.count) }

    /// Returns true if the size of the area is 0.
    pub fn is_null(&self) -> bool { self.size() == 0 }

    /// Returns the first [Address] of this area.
    pub fn start(&self) -> Address { self.start }

    /// Returns the last [Address] of this area.
    pub fn end(&self) -> Address { self.start.offset(self.size() as u64) }

    /// Bounds the specified address inside the area, so it is always a valid pointer to this area.
    pub fn constraint(&self, pointer: Address) -> Address {
        match pointer {
            Address(addr) if addr < self.start.to_u64() => self.start,
            Address(addr) if addr > self.end().to_u64() => self.end(),
            address => address
        }
    }

    /// Returns the offset of the `index`-th element.
    pub fn offset(&self, index: usize) -> usize { self.len * index }

    /// Returns the next constrained address following `cursor`.
    pub fn next_address(&self, cursor: Address) -> Address {
        let cursor = cursor.offset(self.offset(1) as u64);
        self.constraint(cursor)
    }
}

/// A managed foreach-pointer to a region in memory.
#[derive(Copy, Clone)]
pub struct Cursor {
    /// The [Area] the [Cursor] points to.
    pub area: Area,
    pointer: Address,
}

impl Cursor {
    /// Creates a new cursor pointing at the `area` start.
    pub fn new(area: Area) -> Self {
        Self { area, pointer: area.start }
    }
    
    /// Return the next address in the area.
    pub fn next(&self) -> Address { self.area.next_address(self.pointer) }
    
    /// Returns the address currently pointed by the cursor.
    pub fn current(&self) -> Address { self.pointer }

    /// Sets the internal pointer to the next address in the area.
    pub fn advance(&mut self) {
        self.pointer = self.next();
    }

    /// Sets the internal pointer to the area start address.
    pub fn reset(&mut self) { self.pointer = self.area.start; }
}