Skip to main content

truthlinked_sdk/
codec.rs

1//! Serialization and deserialization traits and utilities.
2//!
3//! This module provides two encoding strategies for contract data:
4//!
5//! - **`Codec32`**: Fixed 32-byte encoding for storage slots
6//! - **`BytesCodec`**: Variable-length encoding for complex types
7//!
8//! Both traits support `#[derive]` macros for automatic implementation.
9//!
10//! # Codec32 - Fixed-Size Storage
11//!
12//! Used for values stored in 32-byte storage slots. All primitive integers,
13//! booleans, and 32-byte arrays implement this trait.
14//!
15//! ```ignore
16//! use truthlinked_sdk::codec::Codec32;
17//!
18//! let value = 42u64;
19//! let encoded = value.encode_32(); // [42, 0, 0, ..., 0] (32 bytes)
20//! let decoded = u64::decode_32(&encoded)?; // 42
21//! ```
22//!
23//! # BytesCodec - Variable-Length Encoding
24//!
25//! Used for complex types, strings, and variable-length data. Supports
26//! automatic derivation for structs and enums.
27//!
28//! ```ignore
29//! #[derive(BytesCodec)]
30//! struct Transfer {
31//!     to: [u8; 32],
32//!     amount: u64,
33//! }
34//!
35//! let transfer = Transfer { to: [0; 32], amount: 100 };
36//! let bytes = transfer.encode_bytes();
37//! let decoded = Transfer::decode_bytes(&bytes)?;
38//! ```
39//!
40//! # Encoder/Decoder - Builder Pattern
41//!
42//! For manual encoding of complex types:
43//!
44//! ```ignore
45//! use truthlinked_sdk::codec::{Encoder, Decoder};
46//!
47//! // Encoding
48//! let mut enc = Encoder::new();
49//! enc.push_u64(42);
50//! enc.push_string("hello");
51//! enc.push_bool(true);
52//! let bytes = enc.into_vec();
53//!
54//! // Decoding
55//! let mut dec = Decoder::new(&bytes);
56//! let num = dec.read_u64()?;
57//! let text = dec.read_string()?;
58//! let flag = dec.read_bool()?;
59//! dec.finish()?; // Ensure all bytes consumed
60//! ```
61
62extern crate alloc;
63
64use alloc::string::String;
65use alloc::vec;
66use alloc::vec::Vec;
67
68use crate::error::{Error, Result};
69
70/// Error code for general codec failures.
71pub const ERR_CODEC: i32 = 20;
72
73/// Error code for unexpected end of input during decoding.
74pub const ERR_CODEC_EOF: i32 = 21;
75
76/// Error code for invalid UTF-8 sequences in string decoding.
77pub const ERR_CODEC_UTF8: i32 = 22;
78
79/// Trait for types that can be encoded/decoded to/from 32-byte slots.
80///
81/// This trait is used for storage operations where all values must fit
82/// in fixed 32-byte slots. Primitive integers are stored in little-endian
83/// format with zero-padding.
84///
85/// # Derivable
86///
87/// Use `#[derive(Codec32)]` for custom types (requires `BytesCodec`):
88///
89/// ```ignore
90/// #[derive(BytesCodec, Codec32)]
91/// struct Balance {
92///     amount: u64,
93///     locked: u64,
94/// }
95/// ```
96///
97/// # Example
98///
99/// ```ignore
100/// let value = 1000u64;
101/// let slot = value.encode_32();
102/// let decoded = u64::decode_32(&slot)?;
103/// assert_eq!(decoded, 1000);
104/// ```
105pub trait Codec32: Sized {
106    /// Encodes the value into a 32-byte array.
107    fn encode_32(&self) -> [u8; 32];
108
109    /// Decodes a value from a 32-byte array.
110    fn decode_32(bytes: &[u8; 32]) -> Result<Self>;
111}
112
113/// Trait for types that can be encoded/decoded to/from variable-length bytes.
114///
115/// This trait is used for complex types, strings, and data that doesn't fit
116/// in 32 bytes. All encoding uses little-endian format for integers.
117///
118/// # Derivable
119///
120/// Use `#[derive(BytesCodec)]` for automatic implementation:
121///
122/// ```ignore
123/// #[derive(BytesCodec)]
124/// struct User {
125///     name: String,
126///     age: u32,
127///     active: bool,
128/// }
129/// ```
130///
131/// # Example
132///
133/// ```ignore
134/// let data = vec![1, 2, 3, 4];
135/// let bytes = data.encode_bytes();
136/// let decoded = Vec::<u8>::decode_bytes(&bytes)?;
137/// assert_eq!(decoded, data);
138/// ```
139pub trait BytesCodec: Sized {
140    /// Encodes the value into a byte vector.
141    fn encode_bytes(&self) -> Vec<u8>;
142
143    /// Decodes a value from a byte slice.
144    fn decode_bytes(bytes: &[u8]) -> Result<Self>;
145}
146
147/// Builder for encoding multiple values into a byte stream.
148///
149/// `Encoder` provides a convenient API for serializing complex types
150/// by chaining method calls. All integers are encoded in little-endian format.
151///
152/// # Example
153///
154/// ```ignore
155/// let mut enc = Encoder::with_capacity(64);
156/// enc.push_u64(42);
157/// enc.push_string("hello");
158/// enc.push_bool(true);
159/// enc.push_bytes(&[1, 2, 3]);
160///
161/// let bytes = enc.into_vec();
162/// ```
163#[derive(Clone, Debug, Default)]
164pub struct Encoder {
165    bytes: Vec<u8>,
166}
167
168impl Encoder {
169    /// Creates a new empty encoder.
170    pub fn new() -> Self {
171        Self { bytes: Vec::new() }
172    }
173
174    /// Creates a new encoder with pre-allocated capacity.
175    ///
176    /// Use this when you know the approximate size to avoid reallocations.
177    pub fn with_capacity(capacity: usize) -> Self {
178        Self {
179            bytes: Vec::with_capacity(capacity),
180        }
181    }
182
183    /// Appends a `u8` value.
184    pub fn push_u8(&mut self, value: u8) {
185        self.bytes.push(value);
186    }
187
188    /// Appends a `u16` value (little-endian).
189    pub fn push_u16(&mut self, value: u16) {
190        self.bytes.extend_from_slice(&value.to_le_bytes());
191    }
192
193    /// Appends a `u32` value (little-endian).
194    pub fn push_u32(&mut self, value: u32) {
195        self.bytes.extend_from_slice(&value.to_le_bytes());
196    }
197
198    /// Appends a `u64` value (little-endian).
199    pub fn push_u64(&mut self, value: u64) {
200        self.bytes.extend_from_slice(&value.to_le_bytes());
201    }
202
203    /// Appends a `u128` value (little-endian).
204    pub fn push_u128(&mut self, value: u128) {
205        self.bytes.extend_from_slice(&value.to_le_bytes());
206    }
207
208    /// Appends a boolean value (0 or 1).
209    pub fn push_bool(&mut self, value: bool) {
210        self.push_u8(if value { 1 } else { 0 });
211    }
212
213    /// Appends raw bytes without length prefix.
214    pub fn push_raw(&mut self, bytes: &[u8]) {
215        self.bytes.extend_from_slice(bytes);
216    }
217
218    /// Appends a byte slice with a u32 length prefix.
219    ///
220    /// Format: `[length: u32][data: bytes]`
221    pub fn push_bytes(&mut self, bytes: &[u8]) {
222        self.push_u32(bytes.len() as u32);
223        self.push_raw(bytes);
224    }
225
226    /// Appends a string with a u32 length prefix.
227    ///
228    /// The string is encoded as UTF-8 bytes.
229    pub fn push_string(&mut self, value: &str) {
230        self.push_bytes(value.as_bytes());
231    }
232
233    /// Appends a value that implements `BytesCodec` with a length prefix.
234    pub fn push_codec<T: BytesCodec>(&mut self, value: &T) {
235        self.push_bytes(&value.encode_bytes());
236    }
237
238    /// Returns the encoded bytes as a slice.
239    pub fn as_slice(&self) -> &[u8] {
240        &self.bytes
241    }
242
243    /// Consumes the encoder and returns the encoded bytes.
244    pub fn into_vec(self) -> Vec<u8> {
245        self.bytes
246    }
247}
248
249/// Parser for decoding values from a byte stream.
250///
251/// `Decoder` provides a cursor-based API for deserializing values encoded
252/// by `Encoder`. All integers are decoded from little-endian format.
253///
254/// # Example
255///
256/// ```ignore
257/// let mut dec = Decoder::new(&bytes);
258/// let num = dec.read_u64()?;
259/// let text = dec.read_string()?;
260/// let flag = dec.read_bool()?;
261/// dec.finish()?; // Verify all bytes consumed
262/// ```
263pub struct Decoder<'a> {
264    bytes: &'a [u8],
265    cursor: usize,
266}
267
268impl<'a> Decoder<'a> {
269    /// Creates a new decoder from a byte slice.
270    pub fn new(bytes: &'a [u8]) -> Self {
271        Self { bytes, cursor: 0 }
272    }
273
274    /// Returns the number of unread bytes remaining.
275    pub fn remaining(&self) -> usize {
276        self.bytes.len().saturating_sub(self.cursor)
277    }
278
279    /// Reads exactly `len` bytes, advancing the cursor.
280    ///
281    /// Returns an error if not enough bytes are available.
282    fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> {
283        let end = self
284            .cursor
285            .checked_add(len)
286            .ok_or_else(|| Error::new(ERR_CODEC_EOF))?;
287        if end > self.bytes.len() {
288            return Err(Error::new(ERR_CODEC_EOF));
289        }
290        let out = &self.bytes[self.cursor..end];
291        self.cursor = end;
292        Ok(out)
293    }
294
295    /// Reads a fixed-size byte array.
296    pub fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
297        let bytes = self.read_exact(N)?;
298        let mut out = [0u8; N];
299        out.copy_from_slice(bytes);
300        Ok(out)
301    }
302
303    /// Reads a `u8` value.
304    pub fn read_u8(&mut self) -> Result<u8> {
305        Ok(self.read_exact(1)?[0])
306    }
307
308    /// Reads a `u16` value (little-endian).
309    pub fn read_u16(&mut self) -> Result<u16> {
310        Ok(u16::from_le_bytes(self.read_array()?))
311    }
312
313    /// Reads a `u32` value (little-endian).
314    pub fn read_u32(&mut self) -> Result<u32> {
315        Ok(u32::from_le_bytes(self.read_array()?))
316    }
317
318    /// Reads a `u64` value (little-endian).
319    pub fn read_u64(&mut self) -> Result<u64> {
320        Ok(u64::from_le_bytes(self.read_array()?))
321    }
322
323    /// Reads a `u128` value (little-endian).
324    pub fn read_u128(&mut self) -> Result<u128> {
325        Ok(u128::from_le_bytes(self.read_array()?))
326    }
327
328    /// Reads a boolean value (0 = false, 1 = true).
329    ///
330    /// Returns an error if the byte is neither 0 nor 1.
331    pub fn read_bool(&mut self) -> Result<bool> {
332        match self.read_u8()? {
333            0 => Ok(false),
334            1 => Ok(true),
335            _ => Err(Error::new(ERR_CODEC)),
336        }
337    }
338
339    /// Reads exactly `len` raw bytes without a length prefix.
340    pub fn read_raw(&mut self, len: usize) -> Result<&'a [u8]> {
341        self.read_exact(len)
342    }
343
344    /// Reads a byte slice with a u32 length prefix.
345    ///
346    /// Format: `[length: u32][data: bytes]`
347    pub fn read_bytes(&mut self) -> Result<&'a [u8]> {
348        let len = self.read_u32()? as usize;
349        self.read_exact(len)
350    }
351
352    /// Reads a UTF-8 string with a u32 length prefix.
353    ///
354    /// Returns an error if the bytes are not valid UTF-8.
355    pub fn read_string(&mut self) -> Result<String> {
356        let bytes = self.read_bytes()?;
357        let value = core::str::from_utf8(bytes).map_err(|_| Error::new(ERR_CODEC_UTF8))?;
358        Ok(String::from(value))
359    }
360
361    /// Reads a value that implements `BytesCodec` with a length prefix.
362    pub fn read_codec<T: BytesCodec>(&mut self) -> Result<T> {
363        let bytes = self.read_bytes()?;
364        T::decode_bytes(bytes)
365    }
366
367    /// Verifies that all bytes have been consumed.
368    ///
369    /// Returns an error if there are unread bytes remaining.
370    /// Use this at the end of decoding to ensure data integrity.
371    ///
372    /// # Example
373    ///
374    /// ```ignore
375    /// let mut dec = Decoder::new(&bytes);
376    /// let value = dec.read_u64()?;
377    /// dec.finish()?; // Ensure no trailing bytes
378    /// ```
379    pub fn finish(self) -> Result<()> {
380        if self.cursor == self.bytes.len() {
381            Ok(())
382        } else {
383            Err(Error::new(ERR_CODEC))
384        }
385    }
386}
387
388// ============================================================================
389// Codec32 Implementations
390// ============================================================================
391
392impl Codec32 for [u8; 32] {
393    fn encode_32(&self) -> [u8; 32] {
394        *self
395    }
396
397    fn decode_32(bytes: &[u8; 32]) -> Result<Self> {
398        Ok(*bytes)
399    }
400}
401
402impl Codec32 for bool {
403    fn encode_32(&self) -> [u8; 32] {
404        let mut out = [0u8; 32];
405        out[0] = if *self { 1 } else { 0 };
406        out
407    }
408
409    fn decode_32(bytes: &[u8; 32]) -> Result<Self> {
410        match bytes[0] {
411            0 => Ok(false),
412            1 => Ok(true),
413            _ => Err(Error::new(ERR_CODEC)),
414        }
415    }
416}
417
418/// Implements `Codec32` for integer types using little-endian encoding.
419macro_rules! impl_codec32_int {
420    ($ty:ty, $width:expr) => {
421        impl Codec32 for $ty {
422            fn encode_32(&self) -> [u8; 32] {
423                let mut out = [0u8; 32];
424                out[..$width].copy_from_slice(&self.to_le_bytes());
425                out
426            }
427
428            fn decode_32(bytes: &[u8; 32]) -> Result<Self> {
429                let mut raw = [0u8; $width];
430                raw.copy_from_slice(&bytes[..$width]);
431                Ok(<$ty>::from_le_bytes(raw))
432            }
433        }
434    };
435}
436
437impl_codec32_int!(u8, 1);
438impl_codec32_int!(u16, 2);
439impl_codec32_int!(u32, 4);
440impl_codec32_int!(u64, 8);
441impl_codec32_int!(u128, 16);
442impl_codec32_int!(i8, 1);
443impl_codec32_int!(i16, 2);
444impl_codec32_int!(i32, 4);
445impl_codec32_int!(i64, 8);
446impl_codec32_int!(i128, 16);
447
448// ============================================================================
449// BytesCodec Implementations
450// ============================================================================
451
452impl BytesCodec for bool {
453    fn encode_bytes(&self) -> Vec<u8> {
454        vec![if *self { 1 } else { 0 }]
455    }
456
457    fn decode_bytes(bytes: &[u8]) -> Result<Self> {
458        if bytes.len() != 1 {
459            return Err(Error::new(ERR_CODEC));
460        }
461        match bytes[0] {
462            0 => Ok(false),
463            1 => Ok(true),
464            _ => Err(Error::new(ERR_CODEC)),
465        }
466    }
467}
468
469/// Implements `BytesCodec` for integer types using little-endian encoding.
470macro_rules! impl_bytescodec_int {
471    ($ty:ty, $width:expr) => {
472        impl BytesCodec for $ty {
473            fn encode_bytes(&self) -> Vec<u8> {
474                self.to_le_bytes().to_vec()
475            }
476
477            fn decode_bytes(bytes: &[u8]) -> Result<Self> {
478                if bytes.len() != $width {
479                    return Err(Error::new(ERR_CODEC));
480                }
481                let mut raw = [0u8; $width];
482                raw.copy_from_slice(bytes);
483                Ok(<$ty>::from_le_bytes(raw))
484            }
485        }
486    };
487}
488
489impl_bytescodec_int!(u8, 1);
490impl_bytescodec_int!(u16, 2);
491impl_bytescodec_int!(u32, 4);
492impl_bytescodec_int!(u64, 8);
493impl_bytescodec_int!(u128, 16);
494impl_bytescodec_int!(i8, 1);
495impl_bytescodec_int!(i16, 2);
496impl_bytescodec_int!(i32, 4);
497impl_bytescodec_int!(i64, 8);
498impl_bytescodec_int!(i128, 16);
499
500impl BytesCodec for Vec<u8> {
501    fn encode_bytes(&self) -> Vec<u8> {
502        self.clone()
503    }
504
505    fn decode_bytes(bytes: &[u8]) -> Result<Self> {
506        Ok(bytes.to_vec())
507    }
508}
509
510impl<const N: usize> BytesCodec for [u8; N] {
511    fn encode_bytes(&self) -> Vec<u8> {
512        self.to_vec()
513    }
514
515    fn decode_bytes(bytes: &[u8]) -> Result<Self> {
516        if bytes.len() != N {
517            return Err(Error::new(ERR_CODEC));
518        }
519        let mut out = [0u8; N];
520        out.copy_from_slice(bytes);
521        Ok(out)
522    }
523}
524
525impl BytesCodec for String {
526    fn encode_bytes(&self) -> Vec<u8> {
527        self.as_bytes().to_vec()
528    }
529
530    fn decode_bytes(bytes: &[u8]) -> Result<Self> {
531        let s = core::str::from_utf8(bytes).map_err(|_| Error::new(ERR_CODEC_UTF8))?;
532        Ok(String::from(s))
533    }
534}
535
536impl<T: BytesCodec> BytesCodec for Option<T> {
537    fn encode_bytes(&self) -> Vec<u8> {
538        match self {
539            Some(value) => {
540                let mut out = Vec::with_capacity(1);
541                out.push(1);
542                out.extend_from_slice(&value.encode_bytes());
543                out
544            }
545            None => vec![0],
546        }
547    }
548
549    fn decode_bytes(bytes: &[u8]) -> Result<Self> {
550        if bytes.is_empty() {
551            return Err(Error::new(ERR_CODEC));
552        }
553        match bytes[0] {
554            0 => {
555                if bytes.len() != 1 {
556                    return Err(Error::new(ERR_CODEC));
557                }
558                Ok(None)
559            }
560            1 => Ok(Some(T::decode_bytes(&bytes[1..])?)),
561            _ => Err(Error::new(ERR_CODEC)),
562        }
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569    use alloc::string::String;
570
571    #[derive(crate::BytesCodec, crate::Codec32, Debug, PartialEq, Eq)]
572    struct SmallRecord {
573        id: u16,
574        enabled: bool,
575    }
576
577    #[derive(crate::BytesCodec, Debug, PartialEq, Eq)]
578    enum Action {
579        Inc(u64),
580        Set { value: u64 },
581        Reset,
582    }
583
584    #[test]
585    fn codec32_roundtrip_u64() {
586        let raw = 55u64.encode_32();
587        let decoded = u64::decode_32(&raw).unwrap();
588        assert_eq!(decoded, 55);
589    }
590
591    #[test]
592    fn bytescodec_roundtrip_string() {
593        let s = String::from("hello");
594        let bytes = s.encode_bytes();
595        let decoded = String::decode_bytes(&bytes).unwrap();
596        assert_eq!(decoded, "hello");
597    }
598
599    #[test]
600    fn encoder_decoder_roundtrip() {
601        let mut enc = Encoder::new();
602        enc.push_u64(99);
603        enc.push_bool(true);
604        enc.push_string("abc");
605
606        let bytes = enc.into_vec();
607        let mut dec = Decoder::new(&bytes);
608        assert_eq!(dec.read_u64().unwrap(), 99);
609        assert!(dec.read_bool().unwrap());
610        assert_eq!(dec.read_string().unwrap(), "abc");
611        dec.finish().unwrap();
612    }
613
614    #[test]
615    fn derive_bytescodec_roundtrip_struct_and_enum() {
616        let rec = SmallRecord {
617            id: 7,
618            enabled: true,
619        };
620        let rec_bytes = rec.encode_bytes();
621        let rec_decoded = SmallRecord::decode_bytes(&rec_bytes).unwrap();
622        assert_eq!(rec, rec_decoded);
623
624        let action = Action::Set { value: 44 };
625        let action_bytes = action.encode_bytes();
626        let action_decoded = Action::decode_bytes(&action_bytes).unwrap();
627        assert_eq!(action, action_decoded);
628    }
629
630    #[test]
631    fn derive_codec32_roundtrip_small_record() {
632        let rec = SmallRecord {
633            id: 42,
634            enabled: false,
635        };
636        let raw = rec.encode_32();
637        let decoded = SmallRecord::decode_32(&raw).unwrap();
638        assert_eq!(decoded, rec);
639    }
640}