osiris-data 0.2.1

A data management package.
Documentation
//! 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, Eq, PartialEq)]
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)
    }
}

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

impl Identifier for Area {
    /// Gets the address value as an usize.
    fn to_usize(&self) -> usize { self.offset(0) }
}

/// A managed foreach-pointer to a region in memory.
#[derive(Copy, Clone, Eq, PartialEq)]
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; }
}

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

impl Identifier for Cursor {
    /// Gets the current address value as an usize.
    fn to_usize(&self) -> usize { self.current().to_usize() }
}