criware_utf_core/
value.rs

1use std::borrow::Cow;
2
3/// All of the primitives that can be stored in a table
4///
5#[derive(Debug, Clone, Copy)]
6#[repr(u8)]
7pub enum ValueKind {
8    U8 = 0,
9    I8 = 1,
10    U16 = 2,
11    I16 = 3,
12    U32 = 4,
13    I32 = 5,
14    U64 = 6,
15    I64 = 7,
16    F32 = 8,
17    STR = 0xa,
18    BLOB = 0xb,
19}
20
21pub(crate) mod sealed {
22    use std::{borrow::Cow, collections::HashMap};
23
24    #[doc(hidden)]
25    pub trait Primitive: ToOwned {
26        type Buffer: AsRef<[u8]> + AsMut<[u8]> + Default;
27
28        const SIZE_IN_UTF: usize = std::mem::size_of::<Self::Buffer>();
29        const TYPE_FLAG: super::ValueKind;
30
31        fn parse<'a>(
32            data: Self::Buffer,
33            strings: &'a HashMap<u32, String>,
34            blobs: &Vec<u8>,
35        ) -> Option<Self::Owned>;
36
37        fn write<'a>(
38            value: Cow<'a, Self>,
39            strings: &mut HashMap<Cow<'a, str>, u32>,
40            string_buffer: &mut Vec<u8>,
41            blobs: &mut Vec<u8>,
42        ) -> Self::Buffer;
43    }
44
45    macro_rules! impl_primitive_number {
46        ($($name:ident $flag:ident),+) => {
47            $(
48                impl Primitive for $name {
49                    type Buffer = [u8; std::mem::size_of::<$name>()];
50
51                    const TYPE_FLAG: super::ValueKind = super::ValueKind::$flag;
52
53                    #[inline]
54                    fn parse<'a>(
55                        data: Self::Buffer,
56                        _: &HashMap<u32, String>,
57                        _: &Vec<u8>,
58                    ) -> Option<Self> {
59                        Some($name::from_be_bytes(data))
60                    }
61                    #[inline]
62                    fn write<'a>(
63                        value: Cow<'a, Self>,
64                        _: &mut HashMap<Cow<'a, str>, u32>,
65                        _: &mut Vec<u8>,
66                        _: &mut Vec<u8>,
67                    ) -> Self::Buffer {
68                        value.to_be_bytes()
69                    }
70                }
71            )*
72        };
73    }
74
75    impl_primitive_number!(u8 U8, i8 I8, u16 U16, i16 I16, u32 U32, i32 I32, u64 U64, i64 I64, f32 F32);
76
77    impl Primitive for str {
78        type Buffer = [u8; 4];
79
80        const TYPE_FLAG: super::ValueKind = super::ValueKind::STR;
81
82        fn parse<'a>(
83            data: Self::Buffer,
84            strings: &HashMap<u32, String>,
85            _: &Vec<u8>,
86        ) -> Option<Self::Owned> {
87            strings
88                .get(&u32::from_be_bytes(data))
89                .map(|v| v.to_string())
90        }
91        fn write<'a>(
92            value: Cow<'a, Self>,
93            strings: &mut HashMap<Cow<'a, str>, u32>,
94            string_buffer: &mut Vec<u8>,
95            _: &mut Vec<u8>,
96        ) -> Self::Buffer {
97            match strings.get(&value) {
98                Some(idx) => (*idx).to_be_bytes(),
99                None => {
100                    let position = string_buffer.len() as u32;
101                    string_buffer.extend_from_slice(&value.as_bytes());
102                    string_buffer.push(0u8);
103                    strings.insert(value, position);
104                    position.to_be_bytes()
105                }
106            }
107        }
108    }
109
110    impl Primitive for [u8] {
111        type Buffer = [u8; 8];
112
113        const TYPE_FLAG: super::ValueKind = super::ValueKind::BLOB;
114
115        fn parse<'a>(
116            data: Self::Buffer,
117            _: &HashMap<u32, String>,
118            blobs: &Vec<u8>,
119        ) -> Option<Self::Owned> {
120            let idx = u32::from_be_bytes(data[0..4].try_into().unwrap()) as usize;
121            let len = u32::from_be_bytes(data[4..8].try_into().unwrap()) as usize;
122            let end = idx + len;
123            if end > blobs.len() {
124                None
125            } else {
126                Some(blobs[idx..end].into())
127            }
128        }
129        fn write<'a>(
130            value: Cow<'a, Self>,
131            _: &mut HashMap<Cow<'a, str>, u32>,
132            _: &mut Vec<u8>,
133            blobs: &mut Vec<u8>,
134        ) -> Self::Buffer {
135            let data = ((blobs.len() << 32) | value.len()) as u64;
136            blobs.extend(value.iter());
137            data.to_be_bytes()
138        }
139    }
140}
141
142macro_rules! blanket_impl {
143    ($trait:ident for $($name:ty),+) => {
144        $(
145            impl $trait for $name {}
146        )*
147    };
148}
149
150/// A value that can be directly stored in a table (sealed)
151///
152pub trait Primitive: sealed::Primitive + ToOwned {}
153
154blanket_impl!(Primitive for u8, u16, u32, u64, i8, i16, i32, i64, f32, str, [u8]);
155
156/**
157A value that can be stored in a table, but must be converted first
158
159This trait allows for any arbitrary type to be encodable and decodable
160from a UTF table, as long as it can be converted into one of the core
161storeable types (implementors of trait [`Primitive`]).
162
163# Example
164```
165# use std::{borrow::Cow, error::Error};
166# use criware_utf_core::Value;
167#[derive(Default)]
168struct Buffer(String);
169
170impl Value for Buffer {
171    type Primitive = str;
172    fn from_primitive(value: String) -> Result<Self, Box<dyn Error>> {
173        Ok(Buffer(value))
174    }
175    fn to_primitive<'a>(&'a self) -> Result<Cow<'a, str>, Box<dyn Error>> {
176        Ok(Cow::Borrowed(&self.0))
177    }
178}
179```
180*/
181pub trait Value: Sized {
182    /**
183    The primitive to which this value will be converted to/from
184
185    This may be [`u8`], [`i8`], [`u16`], [`i16`], [`u32`], [`i32`], [`u64`],
186    [`i64`], [`f32`], [`str`], or `[u8]`
187    */
188    type Primitive: Primitive + ?Sized;
189
190    /// Attempts to convert from the chosen primitive to this type.
191    ///
192    fn from_primitive(
193        value: <Self::Primitive as ToOwned>::Owned,
194    ) -> Result<Self, Box<dyn std::error::Error>>;
195
196    /// Attempts to convert this value to the chosen primitive type.
197    ///
198    fn to_primitive<'a>(&'a self) -> Result<Cow<'a, Self::Primitive>, Box<dyn std::error::Error>>;
199}
200
201type BoxRes<T> = Result<T, Box<dyn std::error::Error>>;
202
203macro_rules! impl_value_number {
204    ($($type:ty),*) => {
205        $(
206            impl Value for $type {
207                type Primitive = $type;
208                #[inline]
209                fn from_primitive(value: Self) -> BoxRes<Self> {
210                    Ok(value)
211                }
212                #[inline]
213                fn to_primitive<'a>(&'a self) -> BoxRes<Cow<'a, Self::Primitive>> {
214                    Ok(Cow::Owned(*self))
215                }
216            }
217        )*
218    };
219}
220
221impl_value_number!(u8, u16, u32, u64, i8, i16, i32, i64, f32);
222
223impl Value for String {
224    type Primitive = str;
225
226    #[inline]
227    fn from_primitive(value: String) -> Result<Self, Box<dyn std::error::Error>> {
228        Ok(value)
229    }
230    #[inline]
231    fn to_primitive<'a>(&'a self) -> Result<Cow<'a, Self::Primitive>, Box<dyn std::error::Error>> {
232        Ok(Cow::Borrowed(self.as_str()))
233    }
234}
235
236impl Value for Vec<u8> {
237    type Primitive = [u8];
238
239    #[inline]
240    fn from_primitive(value: Vec<u8>) -> Result<Self, Box<dyn std::error::Error>> {
241        Ok(value)
242    }
243    #[inline]
244    fn to_primitive<'a>(&'a self) -> Result<Cow<'a, Self::Primitive>, Box<dyn std::error::Error>> {
245        Ok(Cow::Borrowed(self.as_slice()))
246    }
247}
248
249impl Value for Box<[u8]> {
250    type Primitive = [u8];
251
252    #[inline]
253    fn from_primitive(value: Vec<u8>) -> Result<Self, Box<dyn std::error::Error>> {
254        Ok(value.into_boxed_slice())
255    }
256    #[inline]
257    fn to_primitive<'a>(&'a self) -> Result<Cow<'a, Self::Primitive>, Box<dyn std::error::Error>> {
258        Ok(Cow::Borrowed(&self))
259    }
260}
261
262impl<const N: usize> Value for [u8; N] {
263    type Primitive = [u8];
264
265    fn from_primitive(value: Vec<u8>) -> Result<Self, Box<dyn std::error::Error>> {
266        match value.try_into() {
267            Ok(value) => Ok(value),
268            Err(_) => Err(crate::Error::BlobWrongSize.into()),
269        }
270    }
271    #[inline]
272    fn to_primitive<'a>(&'a self) -> Result<Cow<'a, Self::Primitive>, Box<dyn std::error::Error>> {
273        Ok(Cow::Borrowed(self))
274    }
275}
276
277/**
278Returns the space (in bytes) a value would take up in column/row space
279
280When a constant or rowed value is written, it is stored in the column or row
281space (respectively). The space taken up in said space does not depend on the
282data itself and can be determined at compile-time.
283
284# Example
285```
286# extern crate criware_utf_core as criware_utf;
287# use criware_utf::{Value, utf_size_of};
288#[derive(Default)]
289struct SplitU8(u8, u8);
290
291impl Value for SplitU8 {
292    type Primitive = u8;
293    // ...
294#   fn from_primitive(value: Self::Primitive) -> Result<Self, Box<dyn std::error::Error>> {
295#       Ok(SplitU8(value >> 4, value & 15))
296#   }
297#   fn to_primitive<'a>(&'a self) -> Result<Cow<'a, Self::Primitive>, Box<dyn std::error::Error>> {
298#       Ok(Cow::Owned((self.0 << 4) | self.1))
299#   }
300}
301
302fn main() {
303    assert_eq!(utf_size_of::<u8>(), 1);
304    assert_eq!(utf_size_of::<i32>(), 4);
305    assert_eq!(utf_size_of::<Vec<u8>>(), 8);
306    assert_eq!(utf_size_of::<String>(), 4);
307    assert_eq!(utf_size_of::<SplitU8>(), utf_size_of::<u8>());
308}
309```
310*/
311pub const fn utf_size_of<T: Value>() -> usize {
312    <T::Primitive as sealed::Primitive>::SIZE_IN_UTF
313}