Skip to content

Commit

Permalink
Update to Rust 2024 edition and enhance documentation and refactoring…
Browse files Browse the repository at this point in the history
… of methods (version 0.2.0) (#1)

* update Cargo.toml to use edition 2024

* add doc tests for methods, refactor some methods

* formatted

* Bump to 0.2.0
  • Loading branch information
aarkegz authored Dec 5, 2024
1 parent 6c7a690 commit a48772a
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 24 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "flatten_objects"
version = "0.1.0"
edition = "2021"
version = "0.2.0"
edition = "2024"
authors = ["Yuekai Jia <[email protected]>"]
description = "A container that stores numbered objects. Each object can be assigned with a unique ID."
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
Expand Down
210 changes: 188 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#![no_std]
#![feature(maybe_uninit_uninit_array)]
#![feature(const_maybe_uninit_uninit_array)]

use bitmaps::Bitmap;
use core::mem::MaybeUninit;
Expand All @@ -51,6 +50,25 @@ pub struct FlattenObjects<T, const CAP: usize> {

impl<T, const CAP: usize> FlattenObjects<T, CAP> {
/// Creates a new empty `FlattenObjects`.
///
/// # Panics
///
/// Panics if `CAP` is greater than 1024.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let objects = FlattenObjects::<u32, 20>::new();
/// assert_eq!(objects.capacity(), 20);
/// ```
///
/// ```should_panic
/// use flatten_objects::FlattenObjects;
///
/// let objects = FlattenObjects::<u32, 1025>::new();
/// ```
pub const fn new() -> Self {
assert!(CAP <= 1024);
Self {
Expand All @@ -64,25 +82,81 @@ impl<T, const CAP: usize> FlattenObjects<T, CAP> {
/// Returns the maximum number of objects that can be held.
///
/// It also equals the maximum ID that can be assigned plus one.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let objects = FlattenObjects::<u32, 20>::new();
/// assert_eq!(objects.capacity(), 20);
/// ```
#[inline]
pub const fn capacity(&self) -> usize {
CAP
}

/// Returns the number of objects that have been added.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// assert_eq!(objects.count(), 0);
/// objects.add(23); // Assign ID 0.
/// assert_eq!(objects.count(), 1);
/// objects.add(42); // Assign ID 1.
/// assert_eq!(objects.count(), 2);
/// objects.remove(0); // ID 0 is assigned.
/// assert_eq!(objects.count(), 1);
/// objects.remove(10); // ID 10 is not assigned.
/// assert_eq!(objects.count(), 1);
/// ```
#[inline]
pub const fn count(&self) -> usize {
self.count
}

/// Returns `true` if the given `id` is already be assigned.
/// Checks if the given `id` is assigned.
///
/// Returns `false` if the `id` is out of range.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// objects.add(23); // Assign ID 0.
/// objects.add_at(5, 42); // Assign ID 5.
/// assert!(objects.is_assigned(0));
/// assert!(!objects.is_assigned(1));
/// assert!(objects.is_assigned(5));
/// assert!(!objects.is_assigned(10));
/// ```
#[inline]
pub fn is_assigned(&self, id: usize) -> bool {
id < CAP && self.id_bitmap.get(id)
}

/// Returns the reference of the element with the given `id` if it already
/// be assigned. Otherwise, returns `None`.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// objects.add(23); // Assign ID 0.
/// objects.add_at(5, 42); // Assign ID 5.
/// assert_eq!(objects.get(0), Some(&23));
/// assert_eq!(objects.get(1), None);
/// assert_eq!(objects.get(5), Some(&42));
/// assert_eq!(objects.get(10), None);
/// ```
#[inline]
pub fn get(&self, id: usize) -> Option<&T> {
if self.is_assigned(id) {
Expand All @@ -96,6 +170,22 @@ impl<T, const CAP: usize> FlattenObjects<T, CAP> {

/// Returns the mutable reference of the element with the given `id` if it
/// exists. Otherwise, returns `None`.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// objects.add(23); // Assign ID 0.
/// objects.add_at(5, 42); // Assign ID 5.
/// *objects.get_mut(0).unwrap() = 24;
/// assert_eq!(objects.get_mut(1), None);
/// *objects.get_mut(5).unwrap() = 43;
/// assert_eq!(objects.get_mut(10), None);
/// assert_eq!(objects.get(0), Some(&24));
/// assert_eq!(objects.get(5), Some(&43));
/// ```
#[inline]
pub fn get_mut(&mut self, id: usize) -> Option<&mut T> {
if self.is_assigned(id) {
Expand All @@ -107,41 +197,117 @@ impl<T, const CAP: usize> FlattenObjects<T, CAP> {
}
}

/// Add an object and assigns it a unique ID.
/// Add an object and assigns it the smallest available ID.
///
/// Returns the ID if there is one available. Otherwise, returns `None`.
pub fn add(&mut self, value: T) -> Option<usize> {
let id = self.id_bitmap.first_false_index()?;
if id < CAP {
self.count += 1;
self.id_bitmap.set(id, true);
self.objects[id].write(value);
Some(id)
} else {
None
/// Returns the ID if there is one available. Otherwise, returns the object
/// itself wrapped in `Err`.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 3>::new();
/// assert_eq!(objects.add(23), Ok(0));
/// assert_eq!(objects.add(42), Ok(1));
/// assert_eq!(objects.add(23), Ok(2));
/// assert_eq!(objects.add(42), Err(42));
/// objects.remove(1);
/// assert_eq!(objects.add(42), Ok(1));
/// ```
pub fn add(&mut self, value: T) -> Result<usize, T> {
match self.id_bitmap.first_false_index() {
Some(id) if id < CAP => {
self.count += 1;
self.id_bitmap.set(id, true);
self.objects[id].write(value);
Ok(id)
}
_ => Err(value),
}
}

/// Add an object and assigns it a specific ID.
/// Add an object with the given ID.
///
/// Returns the ID if it's not used by others. Otherwise, returns `None`.
pub fn add_at(&mut self, id: usize, value: T) -> Option<usize> {
if self.is_assigned(id) {
return None;
/// Returns the ID if the object is added successfully. Otherwise, returns
/// the object itself wrapped in `Err`.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// assert_eq!(objects.add_at(5, 23), Ok(5));
/// assert_eq!(objects.add_at(5, 42), Err(42));
/// assert_eq!(objects.add_at(20, 42), Err(42));
/// ```
pub fn add_at(&mut self, id: usize, value: T) -> Result<usize, T> {
if id >= CAP || self.is_assigned(id) {
return Err(value);
}
self.count += 1;
self.id_bitmap.set(id, true);
self.objects[id].write(value);
Some(id)
Ok(id)
}

/// Removes the object with the given ID.
/// Adds an object with the given ID, replacing and returning the old object
/// if the ID is already assigned.
///
/// Returns the ID if the object is added successfully. Returns `Err(Some(old))`
/// if the ID is already assigned. Returns `Err(None)` if the ID is out of
/// range.
///
/// Returns the object if there is one assigned to the ID. Otherwise,
/// returns `None`.
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// assert_eq!(objects.add_or_replace_at(5, 23), Ok(5));
/// assert_eq!(objects.add_or_replace_at(5, 42), Err(Some(23)));
/// assert_eq!(objects.get(5), Some(&42));
/// assert_eq!(objects.add_or_replace_at(20, 42), Err(None));
/// ```
pub fn add_or_replace_at(&mut self, id: usize, value: T) -> Result<usize, Option<T>> {
if id >= CAP {
return Err(None);
}

if self.is_assigned(id) {
// SAFETY: the object at `id` should be initialized by `add` or
// `add_at`, and can not be retrieved by `get` or `get_mut` unless
// it be added again.
let old = unsafe { Some(self.objects[id].assume_init_read()) };
self.objects[id].write(value);

Err(old)
} else {
self.count += 1;
self.id_bitmap.set(id, true);
self.objects[id].write(value);

Ok(id)
}
}

/// Removes and returns the object with the given ID.
///
/// After this operation, the ID is freed and can be assigned for next
/// object again.
///
/// # Example
///
/// ```
/// use flatten_objects::FlattenObjects;
///
/// let mut objects = FlattenObjects::<u32, 20>::new();
/// let id = objects.add(23).unwrap();
/// assert_eq!(objects.remove(id), Some(23));
/// assert!(!objects.is_assigned(id));
/// assert_eq!(objects.remove(id), None);
/// ```
pub fn remove(&mut self, id: usize) -> Option<T> {
if self.is_assigned(id) {
self.id_bitmap.set(id, false);
Expand Down

0 comments on commit a48772a

Please sign in to comment.