Skip to main content

triblespace_core/blob/encodings/
array.rs

1//! Flat typed array blob encoding.
2//!
3//! `Array<T>` is a structural blob encoding: it says "this blob is a flat array
4//! of T values in native byte order." The semantics (weight tensor, audio
5//! samples, embeddings) come from the TribleSpace attributes that reference
6//! the blob, not the schema itself — same as `LongString` being structural
7//! rather than semantic.
8
9use core::marker::PhantomData;
10
11use anybytes::view::ViewError;
12use anybytes::{Bytes, View};
13use zerocopy::{Immutable, IntoBytes, KnownLayout, TryFromBytes};
14
15use crate::blob::{Blob, BlobEncoding, TryFromBlob};
16use crate::macros::entity;
17use crate::metadata;
18use crate::metadata::MetaDescribe;
19use crate::trible::Fragment;
20
21/// Maps a schema element marker to its native Rust type.
22///
23/// Implement this for zero-sized marker types (e.g. `F32`, `BF16`, `I8`)
24/// that identify an element format. `Native` provides the actual data
25/// type for zerocopy access; `MetaDescribe` (super-trait via `BlobEncoding`
26/// downstream) provides schema identity.
27pub trait ArrayElement: MetaDescribe + 'static {
28    /// The native Rust type for this element.
29    type Native: IntoBytes + Immutable + TryFromBytes + KnownLayout + Sync + Send + 'static;
30}
31
32/// A flat array of `T` values in native byte order.
33///
34/// The blob encoding ID is *derived* from describing the schema — including
35/// `T::id()` as `metadata::blob_encoding` — so `Array<u8>` and `Array<f32>`
36/// get distinct ids without needing compile-time hashing.
37///
38/// Shape metadata lives in TribleSpace triples, not in the blob.
39/// Use `View<[T::Native]>` for zero-copy access via `TryFromBlob`.
40pub struct Array<T: ArrayElement>(PhantomData<T>);
41
42impl<T: ArrayElement> BlobEncoding for Array<T> {}
43
44impl<T: ArrayElement> MetaDescribe for Array<T> {
45    fn describe() -> Fragment {
46        // Entity core via `*:` spread. `T::describe()` runs once: its
47        // root becomes the value of `metadata::array_item_schema`,
48        // its facts and blobs fold in automatically. The element
49        // schema discriminates `Array<u8>` from `Array<f32>` etc.;
50        // element schemas aren't themselves `BlobEncoding`s, so they
51        // get their own attribute (not `metadata::blob_encoding`).
52        // Annotations share the derived root so `+=` is idempotent on
53        // exports and folds facts + auto-put blobs into the core.
54        let mut core = entity! {
55            metadata::array_item_schema*: T::describe(),
56            metadata::tag: metadata::KIND_BLOB_ENCODING,
57        };
58        let id = core.root().expect("rooted");
59        let id_ref = crate::id::ExclusiveId::force_ref(&id);
60        core += entity! { id_ref @
61            metadata::name: "array",
62            metadata::description:
63                "Flat array of typed values in native byte order. \
64                 Shape is stored externally in TribleSpace triples.",
65        };
66        core
67    }
68}
69
70/// Store a `Vec<T::Native>` as an `Array<T>` blob (zero-copy via ByteSource).
71impl<T: ArrayElement> crate::inline::Encodes<Vec<T::Native>> for Array<T>
72where
73    crate::inline::encodings::hash::Handle<Array<T>>: crate::inline::InlineEncoding,
74{
75    type Output = Blob<Array<T>>;
76    fn encode(source: Vec<T::Native>) -> Blob<Array<T>> {
77        Blob::new(Bytes::from_source(source))
78    }
79}
80
81/// Retrieve raw bytes from an Array blob.
82impl<T: ArrayElement> TryFromBlob<Array<T>> for Bytes {
83    type Error = core::convert::Infallible;
84    fn try_from_blob(blob: Blob<Array<T>>) -> Result<Self, Self::Error> {
85        Ok(blob.bytes)
86    }
87}
88
89/// Built-in element types for common native Rust types.
90///
91/// Access as `blobencodings::array::F32`, `blobencodings::array::U8`, etc.
92pub mod elements {
93    use super::ArrayElement;
94    use crate::macros::entity;
95    use crate::metadata::{self, MetaDescribe};
96    use crate::trible::Fragment;
97
98    macro_rules! impl_array_element {
99        ($marker:ident, $native:ty, $id:expr, $doc:expr) => {
100            #[doc = $doc]
101            pub struct $marker;
102
103            impl MetaDescribe for $marker {
104                fn describe() -> Fragment {
105                    // Fixed-id schema: root is the hex id, no derived
106                    // facts contribute to identity. The entity!{}
107                    // form auto-puts the name + doc-comment bytes
108                    // into the fragment's local blob store so a
109                    // consumer querying `Array<F32>`'s metadata can
110                    // resolve the element schema id to a human-
111                    // readable name.
112                    let id = crate::id_hex!($id);
113                    entity! { crate::id::ExclusiveId::force_ref(&id) @
114                        metadata::name:        stringify!($marker),
115                        metadata::description: $doc,
116                    }
117                }
118            }
119
120            impl ArrayElement for $marker {
121                type Native = $native;
122            }
123        };
124    }
125
126    impl_array_element!(
127        F32,
128        f32,
129        "92F4DB8D84519C8D6E212CB810FF40D4",
130        "32-bit IEEE-754 float."
131    );
132    impl_array_element!(
133        F64,
134        f64,
135        "FA3AD8DEC844D5F409AB728269B7A3FE",
136        "64-bit IEEE-754 float."
137    );
138    impl_array_element!(
139        U8,
140        u8,
141        "D16AC7C02F25E4799F4D47EB1E51EF6E",
142        "Unsigned 8-bit integer."
143    );
144    impl_array_element!(
145        U16,
146        u16,
147        "C14453D98F283B96A1010A9F24D53B17",
148        "Unsigned 16-bit integer."
149    );
150    impl_array_element!(
151        U32,
152        u32,
153        "1B9DD214A02C58D9141EF802273120F8",
154        "Unsigned 32-bit integer."
155    );
156    impl_array_element!(
157        U64,
158        u64,
159        "323C0143534D3AD4898D69EA5597414A",
160        "Unsigned 64-bit integer."
161    );
162    impl_array_element!(
163        I8,
164        i8,
165        "E68060AF27227583CB1AEDF89E17E278",
166        "Signed 8-bit integer."
167    );
168    impl_array_element!(
169        I16,
170        i16,
171        "E72199687209A576562B5BD7196FD755",
172        "Signed 16-bit integer."
173    );
174    impl_array_element!(
175        I32,
176        i32,
177        "AB831A6CCDAF7F49BA5BEADEA32CA04E",
178        "Signed 32-bit integer."
179    );
180    impl_array_element!(
181        I64,
182        i64,
183        "53426475A3C695420B23C329285DCA57",
184        "Signed 64-bit integer."
185    );
186}
187
188/// Zero-copy typed view directly from an Array blob.
189///
190/// ```ignore
191/// let floats: View<[f32]> = blobs.get(handle)?;
192/// ```
193impl<T: ArrayElement> TryFromBlob<Array<T>> for View<[T::Native]> {
194    type Error = ViewError;
195    fn try_from_blob(blob: Blob<Array<T>>) -> Result<Self, Self::Error> {
196        blob.bytes.view()
197    }
198}