Skip to content

Commit 0bad913

Browse files
committed
Initial commit
0 parents  commit 0bad913

File tree

7 files changed

+465
-0
lines changed

7 files changed

+465
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

Cargo.lock

+86
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "small-fixed-array"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
serde = { version = "1.0.193", optional = true }
10+
typesize = { version = "0.1.3", optional = true }
11+
12+
[features]
13+
typesize = ["dep:typesize"]
14+
serde = ["dep:serde"]

src/array.rs

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use std::{fmt::Debug, hash::Hash};
2+
3+
use crate::non_empty_array::NonEmptyFixedArray;
4+
5+
/// A fixed size array with length provided at creation with length denoted in [`u32`]
6+
#[derive(Clone)]
7+
pub struct FixedArray<T>(Option<NonEmptyFixedArray<T>>);
8+
9+
impl<T> FixedArray<T> {
10+
pub fn new() -> Self {
11+
Self::empty()
12+
}
13+
14+
pub fn empty() -> Self {
15+
Self(None)
16+
}
17+
18+
pub fn len(&self) -> u32 {
19+
self.0
20+
.as_ref()
21+
.map(NonEmptyFixedArray::small_len)
22+
.unwrap_or_default()
23+
}
24+
25+
pub fn is_empty(&self) -> bool {
26+
self.0.is_none()
27+
}
28+
29+
pub fn as_slice(&self) -> &[T] {
30+
self
31+
}
32+
33+
pub fn into_vec(self) -> Vec<T> {
34+
self.into()
35+
}
36+
}
37+
38+
impl<T> Default for FixedArray<T> {
39+
fn default() -> Self {
40+
Self::empty()
41+
}
42+
}
43+
44+
impl<T> std::ops::Deref for FixedArray<T> {
45+
type Target = [T];
46+
fn deref(&self) -> &Self::Target {
47+
self.0
48+
.as_ref()
49+
.map(NonEmptyFixedArray::as_slice)
50+
.unwrap_or_default()
51+
}
52+
}
53+
54+
impl<T> std::ops::DerefMut for FixedArray<T> {
55+
fn deref_mut(&mut self) -> &mut Self::Target {
56+
self.0
57+
.as_mut()
58+
.map(NonEmptyFixedArray::as_mut_slice)
59+
.unwrap_or_default()
60+
}
61+
}
62+
63+
impl<T> std::ops::Index<u32> for FixedArray<T> {
64+
type Output = T;
65+
fn index(&self, index: u32) -> &Self::Output {
66+
let inner: &[T] = self;
67+
&inner[index as usize]
68+
}
69+
}
70+
71+
impl<T> std::ops::IndexMut<u32> for FixedArray<T> {
72+
fn index_mut(&mut self, index: u32) -> &mut Self::Output {
73+
let inner: &mut [T] = self;
74+
&mut inner[index as usize]
75+
}
76+
}
77+
78+
impl<T: Hash> Hash for FixedArray<T> {
79+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
80+
self.as_slice().hash(state)
81+
}
82+
}
83+
84+
impl<T: PartialEq> PartialEq for FixedArray<T> {
85+
fn eq(&self, other: &Self) -> bool {
86+
self.as_slice().eq(other.as_slice())
87+
}
88+
}
89+
90+
impl<T: Eq> Eq for FixedArray<T> {}
91+
92+
impl<T: Debug> Debug for FixedArray<T> {
93+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94+
<[T] as Debug>::fmt(self, f)
95+
}
96+
}
97+
98+
impl<T> IntoIterator for FixedArray<T> {
99+
type Item = T;
100+
type IntoIter = std::vec::IntoIter<T>;
101+
102+
fn into_iter(self) -> Self::IntoIter {
103+
self.into_vec().into_iter()
104+
}
105+
}
106+
107+
impl<'a, T> IntoIterator for &'a FixedArray<T> {
108+
type Item = &'a T;
109+
type IntoIter = std::slice::Iter<'a, T>;
110+
111+
fn into_iter(self) -> Self::IntoIter {
112+
self.as_slice().iter()
113+
}
114+
}
115+
116+
impl<T> std::iter::FromIterator<T> for FixedArray<T> {
117+
fn from_iter<Iter: IntoIterator<Item = T>>(iter: Iter) -> Self {
118+
Vec::from_iter(iter).into()
119+
}
120+
}
121+
122+
impl<T> From<FixedArray<T>> for Box<[T]> {
123+
fn from(value: FixedArray<T>) -> Self {
124+
value.0.map(Box::from).unwrap_or_default()
125+
}
126+
}
127+
128+
impl<T> From<FixedArray<T>> for Vec<T> {
129+
fn from(value: FixedArray<T>) -> Self {
130+
let boxed_array: Box<[T]> = value.into();
131+
boxed_array.into_vec()
132+
}
133+
}
134+
135+
impl<T> From<Box<[T]>> for FixedArray<T> {
136+
fn from(boxed_array: Box<[T]>) -> Self {
137+
Self((!boxed_array.is_empty()).then(|| NonEmptyFixedArray::from(boxed_array)))
138+
}
139+
}
140+
141+
impl<T> From<Vec<T>> for FixedArray<T> {
142+
fn from(value: Vec<T>) -> Self {
143+
let boxed_array = value.into_boxed_slice();
144+
Self::from(boxed_array)
145+
}
146+
}
147+
148+
#[cfg(feature = "serde")]
149+
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for FixedArray<T> {
150+
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
151+
Box::<[T]>::deserialize(deserializer).map(Self::from)
152+
}
153+
}
154+
155+
#[cfg(feature = "serde")]
156+
impl<T: serde::Serialize> serde::Serialize for FixedArray<T> {
157+
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
158+
self.as_slice().serialize(serializer)
159+
}
160+
}
161+
162+
#[cfg(feature = "typesize")]
163+
impl<T: typesize::TypeSize> typesize::TypeSize for FixedArray<T> {
164+
fn extra_size(&self) -> usize {
165+
self.iter().map(T::get_size).sum()
166+
}
167+
}

src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod array;
2+
mod string;
3+
// Internal only!
4+
mod non_empty_array;
5+
6+
pub use array::FixedArray;
7+
pub use string::FixedString;

src/non_empty_array.rs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use std::{mem::ManuallyDrop, num::NonZeroU32, ptr::NonNull};
2+
3+
#[repr(packed)]
4+
pub(crate) struct NonEmptyFixedArray<T> {
5+
ptr: NonNull<T>,
6+
len: NonZeroU32,
7+
}
8+
9+
impl<T> NonEmptyFixedArray<T> {
10+
pub(crate) fn small_len(&self) -> u32 {
11+
self.len.get()
12+
}
13+
14+
fn len(&self) -> usize {
15+
self.small_len() as usize
16+
}
17+
18+
pub(crate) fn as_slice(&self) -> &[T] {
19+
// SAFETY: `self.ptr` and `self.len` are both valid and derived from `Box<[T]>`.
20+
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
21+
}
22+
23+
pub(crate) fn as_mut_slice(&mut self) -> &mut [T] {
24+
// SAFETY: `self.ptr` and `self.len` are both valid and derived from `Box<[T]>`.
25+
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len()) }
26+
}
27+
28+
/// Converts the NonEmptyFixedArray to it's original [`Box<T>`].
29+
///
30+
/// # Safety
31+
/// `self` must never be used again, and it is highly recommended to wrap in ManuallyDrop before calling.
32+
pub(crate) unsafe fn as_box(&mut self) -> Box<[T]> {
33+
let slice = self.as_mut_slice();
34+
35+
// SAFETY: `self` has been derived from `Box<[T]>`
36+
unsafe { Box::from_raw(slice as *mut [T]) }
37+
}
38+
}
39+
40+
impl<T> From<Box<[T]>> for NonEmptyFixedArray<T> {
41+
fn from(boxed_array: Box<[T]>) -> Self {
42+
let len = NonZeroU32::new(boxed_array.len().try_into().unwrap()).unwrap();
43+
let array_ptr = Box::into_raw(boxed_array) as *mut T;
44+
45+
NonEmptyFixedArray {
46+
ptr: NonNull::new(array_ptr).expect("Box ptr != nullptr"),
47+
len,
48+
}
49+
}
50+
}
51+
52+
impl<T> From<NonEmptyFixedArray<T>> for Box<[T]> {
53+
fn from(value: NonEmptyFixedArray<T>) -> Self {
54+
let mut value = ManuallyDrop::new(value);
55+
unsafe {
56+
// SAFETY: We don't use value again, and it is ManuallyDrop.
57+
value.as_box()
58+
}
59+
}
60+
}
61+
62+
impl<T: Clone> Clone for NonEmptyFixedArray<T> {
63+
fn clone(&self) -> Self {
64+
self.as_slice().to_vec().into_boxed_slice().into()
65+
}
66+
}
67+
68+
impl<T> Drop for NonEmptyFixedArray<T> {
69+
fn drop(&mut self) {
70+
unsafe {
71+
// SAFETY: We never use `self` again, and we are in the drop impl.
72+
self.as_box();
73+
}
74+
}
75+
}
76+
77+
unsafe impl<T: Send> Send for NonEmptyFixedArray<T> {}
78+
unsafe impl<T: Sync> Sync for NonEmptyFixedArray<T> {}

0 commit comments

Comments
 (0)