Skip to content

Commit

Permalink
spacetimedb-data-structures: new crate + nstr! (#491)
Browse files Browse the repository at this point in the history
* spacetimedb-data-structures: new crate + nstr!

* nstr: address review comments
  • Loading branch information
Centril authored Oct 30, 2023
1 parent f15a3be commit c566dba
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"crates/data-structures",
"crates/standalone",
"crates/lib",
"crates/core",
Expand Down
8 changes: 8 additions & 0 deletions crates/data-structures/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "spacetimedb-data-structures"
version = "0.7.1"
edition = "2021"
license-file = "LICENSE"
description = "Assorted data structures used in spacetimedb"

[dependencies]
Empty file added crates/data-structures/LICENSE
Empty file.
4 changes: 4 additions & 0 deletions crates/data-structures/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! This crate provides assorted data structures that are used in spacetimedb.
//! These structures are general and could be used outside spacetimedb.
pub mod nstr;
81 changes: 81 additions & 0 deletions crates/data-structures/src/nstr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Provides UTF-8 strings of compile-time-known lengths.
//!
//! The strings can be constructed with `nstr!(string_literal)`
//! producing a type `NStr<N>` where `const N: usize`.
//! An example would be `nstr!("spacetime"): NStr<9>`.
use core::{fmt, ops::Deref, str};

/// A UTF-8 string of known length `N`.
///
/// The notion of length is that of the standard library's `String` type.
/// That is, the length is in bytes, not chars or graphemes.
///
/// It holds that `size_of::<NStr<N>>() == N`
/// but `&NStr<N>` is always word sized.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NStr<const N: usize>([u8; N]);

// This function exists due to macro-privacy interactions.
#[doc(hidden)]
pub const fn __nstr<const N: usize>(s: &str) -> NStr<N> {
// Ensure that the string was `N` in length.
// This has no runtime cost as the `nstr!` macro forces compile-time eval.
if N != s.len() {
panic!("string does not match claimed length");
};

// Convert the string to bytes.
// Need to use `while` to do this at compile time.
let src = s.as_bytes();
let mut dst = [0; N];
let mut i = 0;
while i < N {
dst[i] = src[i];
i += 1;
}

NStr(dst)
}

/// Constructs an `NStr<N>` given a string literal of `N` bytes in length.
///
/// # Example
///
/// ```
/// use spacetimedb_data_structures::{nstr, nstr::NStr};
/// let s: NStr<3> = nstr!("foo");
/// assert_eq!(&*s, "foo");
/// assert_eq!(s.len(), 3);
/// assert_eq!(format!("{s}"), "foo");
/// assert_eq!(format!("{s:?}"), "foo");
/// ```
#[macro_export]
macro_rules! nstr {
($lit:literal) => {
$crate::nstr::__nstr::<{ $lit.len() }>($lit).into()
};
}

impl<const N: usize> Deref for NStr<N> {
type Target = str;

#[inline]
fn deref(&self) -> &Self::Target {
// SAFETY: An `NStr<N>` can only be made through `__nstr(..)`
// and which receives an `&str` which is valid UTF-8.
unsafe { str::from_utf8_unchecked(&self.0) }
}
}

impl<const N: usize> fmt::Debug for NStr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.deref())
}
}

impl<const N: usize> fmt::Display for NStr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.deref())
}
}

1 comment on commit c566dba

@github-actions
Copy link

@github-actions github-actions bot commented on c566dba Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

Please sign in to comment.