Skip to main content

triblespace_core/value/schemas/
iu256.rs

1use crate::id::ExclusiveId;
2use crate::id::Id;
3use crate::id_hex;
4use crate::macros::entity;
5use crate::metadata;
6use crate::metadata::ConstMetadata;
7use crate::repo::BlobStore;
8use crate::trible::TribleSet;
9use crate::value::schemas::hash::Blake3;
10use crate::value::FromValue;
11use crate::value::ToValue;
12use crate::value::Value;
13use crate::value::ValueSchema;
14use std::convert::Infallible;
15
16use ethnum;
17
18#[cfg(feature = "wasm")]
19use crate::blob::schemas::wasmcode::WasmCode;
20/// A value schema for a 256-bit unsigned integer in little-endian byte order.
21pub struct U256LE;
22
23/// A value schema for a 256-bit unsigned integer in big-endian byte order.
24pub struct U256BE;
25
26/// A value schema for a 256-bit signed integer in little-endian byte order.
27pub struct I256LE;
28
29/// A value schema for a 256-bit signed integer in big-endian byte order.
30pub struct I256BE;
31
32/// A type alias for a 256-bit signed integer.
33/// This type is an alias for [I256BE].
34pub type I256 = I256BE;
35
36/// A type alias for a 256-bit unsigned integer.
37/// This type is an alias for [U256BE].
38pub type U256 = U256BE;
39
40impl ConstMetadata for U256LE {
41    fn id() -> Id {
42        id_hex!("49E70B4DBD84DC7A3E0BDDABEC8A8C6E")
43    }
44
45    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
46    where
47        B: BlobStore<Blake3>,
48    {
49        let id = Self::id();
50        let description = blobs.put(
51            "Unsigned 256-bit integer stored in little-endian byte order. The full 32 bytes are dedicated to the magnitude.\n\nUse for large counters, identifiers, or domain-specific fixed-width numbers that exceed u128. Prefer U256BE when bytewise ordering or protocol encoding matters.\n\nIf a smaller width suffices, prefer U64 or U128 in your schema to reduce storage and improve readability.",
52        )?;
53        let tribles = entity! {
54            ExclusiveId::force_ref(&id) @
55                metadata::name: blobs.put("u256le".to_string())?,
56                metadata::description: description,
57                metadata::tag: metadata::KIND_VALUE_SCHEMA,
58        };
59
60        #[cfg(feature = "wasm")]
61        let tribles = {
62            let mut tribles = tribles;
63            tribles += entity! { ExclusiveId::force_ref(&id) @
64                metadata::value_formatter: blobs.put(wasm_formatter::U256_LE_WASM)?,
65            };
66            tribles
67        };
68        Ok(tribles)
69    }
70}
71impl ValueSchema for U256LE {
72    type ValidationError = Infallible;
73}
74impl ConstMetadata for U256BE {
75    fn id() -> Id {
76        id_hex!("DC3CFB719B05F019FB8101A6F471A982")
77    }
78
79    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
80    where
81        B: BlobStore<Blake3>,
82    {
83        let id = Self::id();
84        let description = blobs.put(
85            "Unsigned 256-bit integer stored in big-endian byte order. Bytewise comparisons align with numeric order.\n\nUse when ordering or network serialization matters. Prefer U256LE for local storage or interop with little-endian APIs.\n\nIf you do not need the full 256-bit range, smaller integer schemas are easier to handle and faster to encode.",
86        )?;
87        let tribles = entity! {
88            ExclusiveId::force_ref(&id) @
89                metadata::name: blobs.put("u256be".to_string())?,
90                metadata::description: description,
91                metadata::tag: metadata::KIND_VALUE_SCHEMA,
92        };
93
94        #[cfg(feature = "wasm")]
95        let tribles = {
96            let mut tribles = tribles;
97            tribles += entity! { ExclusiveId::force_ref(&id) @
98                metadata::value_formatter: blobs.put(wasm_formatter::U256_BE_WASM)?,
99            };
100            tribles
101        };
102        Ok(tribles)
103    }
104}
105impl ValueSchema for U256BE {
106    type ValidationError = Infallible;
107}
108impl ConstMetadata for I256LE {
109    fn id() -> Id {
110        id_hex!("DB94325A37D96037CBFC6941A4C3B66D")
111    }
112
113    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
114    where
115        B: BlobStore<Blake3>,
116    {
117        let id = Self::id();
118        let description = blobs.put(
119            "Signed 256-bit integer stored in little-endian twos-complement. This enables extremely large signed ranges in a fixed width.\n\nUse for large signed quantities such as balances or offsets beyond i128. Prefer I256BE when bytewise ordering or external protocols require big-endian.\n\nIf values fit within i64 or i128, smaller schemas are more compact and easier to interoperate with.",
120        )?;
121        let tribles = entity! {
122            ExclusiveId::force_ref(&id) @
123                metadata::name: blobs.put("i256le".to_string())?,
124                metadata::description: description,
125                metadata::tag: metadata::KIND_VALUE_SCHEMA,
126        };
127
128        #[cfg(feature = "wasm")]
129        let tribles = {
130            let mut tribles = tribles;
131            tribles += entity! { ExclusiveId::force_ref(&id) @
132                metadata::value_formatter: blobs.put(wasm_formatter::I256_LE_WASM)?,
133            };
134            tribles
135        };
136        Ok(tribles)
137    }
138}
139impl ValueSchema for I256LE {
140    type ValidationError = Infallible;
141}
142impl ConstMetadata for I256BE {
143    fn id() -> Id {
144        id_hex!("CE3A7839231F1EB390E9E8E13DAED782")
145    }
146
147    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
148    where
149        B: BlobStore<Blake3>,
150    {
151        let id = Self::id();
152        let description = blobs.put(
153            "Signed 256-bit integer stored in big-endian twos-complement. This variant is convenient for protocol encoding and deterministic ordering.\n\nUse for interoperability or stable bytewise comparisons across systems. Prefer I256LE for local storage or when endianness does not matter.\n\nAs with any signed integer, consider whether the sign bit has semantic meaning and avoid mixing signed and unsigned ranges.",
154        )?;
155        let tribles = entity! {
156            ExclusiveId::force_ref(&id) @
157                metadata::name: blobs.put("i256be".to_string())?,
158                metadata::description: description,
159                metadata::tag: metadata::KIND_VALUE_SCHEMA,
160        };
161
162        #[cfg(feature = "wasm")]
163        let tribles = {
164            let mut tribles = tribles;
165            tribles += entity! { ExclusiveId::force_ref(&id) @
166                metadata::value_formatter: blobs.put(wasm_formatter::I256_BE_WASM)?,
167            };
168            tribles
169        };
170        Ok(tribles)
171    }
172}
173impl ValueSchema for I256BE {
174    type ValidationError = Infallible;
175}
176
177#[cfg(feature = "wasm")]
178mod wasm_formatter {
179    use core::fmt::Write;
180
181    use triblespace_core_macros::value_formatter;
182
183    #[value_formatter(const_wasm = U256_LE_WASM)]
184    pub(crate) fn u256_le(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
185        fn div_mod10(limbs: &mut [u64; 4]) -> u8 {
186            let mut rem: u128 = 0;
187            for limb in limbs.iter_mut() {
188                let n = (rem << 64) | (*limb as u128);
189                *limb = (n / 10) as u64;
190                rem = n % 10;
191            }
192            rem as u8
193        }
194
195        fn is_zero(limbs: &[u64; 4]) -> bool {
196            limbs.iter().all(|&limb| limb == 0)
197        }
198
199        let mut buf = [0u8; 8];
200        buf.copy_from_slice(&raw[0..8]);
201        let w0 = u64::from_le_bytes(buf);
202        buf.copy_from_slice(&raw[8..16]);
203        let w1 = u64::from_le_bytes(buf);
204        buf.copy_from_slice(&raw[16..24]);
205        let w2 = u64::from_le_bytes(buf);
206        buf.copy_from_slice(&raw[24..32]);
207        let w3 = u64::from_le_bytes(buf);
208
209        let mut limbs = [w3, w2, w1, w0];
210        if is_zero(&limbs) {
211            out.write_char('0').map_err(|_| 1u32)?;
212            return Ok(());
213        }
214
215        let mut digits = [0u8; 78];
216        let mut len = 0usize;
217        while !is_zero(&limbs) {
218            let digit = div_mod10(&mut limbs);
219            digits[len] = b'0' + digit;
220            len += 1;
221        }
222
223        for &digit in digits[..len].iter().rev() {
224            out.write_char(digit as char).map_err(|_| 1u32)?;
225        }
226
227        Ok(())
228    }
229
230    #[value_formatter(const_wasm = U256_BE_WASM)]
231    pub(crate) fn u256_be(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
232        fn div_mod10(limbs: &mut [u64; 4]) -> u8 {
233            let mut rem: u128 = 0;
234            for limb in limbs.iter_mut() {
235                let n = (rem << 64) | (*limb as u128);
236                *limb = (n / 10) as u64;
237                rem = n % 10;
238            }
239            rem as u8
240        }
241
242        fn is_zero(limbs: &[u64; 4]) -> bool {
243            limbs.iter().all(|&limb| limb == 0)
244        }
245
246        let mut buf = [0u8; 8];
247        buf.copy_from_slice(&raw[0..8]);
248        let w0 = u64::from_be_bytes(buf);
249        buf.copy_from_slice(&raw[8..16]);
250        let w1 = u64::from_be_bytes(buf);
251        buf.copy_from_slice(&raw[16..24]);
252        let w2 = u64::from_be_bytes(buf);
253        buf.copy_from_slice(&raw[24..32]);
254        let w3 = u64::from_be_bytes(buf);
255
256        let mut limbs = [w0, w1, w2, w3];
257        if is_zero(&limbs) {
258            out.write_char('0').map_err(|_| 1u32)?;
259            return Ok(());
260        }
261
262        let mut digits = [0u8; 78];
263        let mut len = 0usize;
264        while !is_zero(&limbs) {
265            let digit = div_mod10(&mut limbs);
266            digits[len] = b'0' + digit;
267            len += 1;
268        }
269
270        for &digit in digits[..len].iter().rev() {
271            out.write_char(digit as char).map_err(|_| 1u32)?;
272        }
273
274        Ok(())
275    }
276
277    #[value_formatter(const_wasm = I256_LE_WASM)]
278    pub(crate) fn i256_le(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
279        fn div_mod10(limbs: &mut [u64; 4]) -> u8 {
280            let mut rem: u128 = 0;
281            for limb in limbs.iter_mut() {
282                let n = (rem << 64) | (*limb as u128);
283                *limb = (n / 10) as u64;
284                rem = n % 10;
285            }
286            rem as u8
287        }
288
289        fn is_zero(limbs: &[u64; 4]) -> bool {
290            limbs.iter().all(|&limb| limb == 0)
291        }
292
293        fn twos_complement(limbs: &mut [u64; 4]) {
294            for limb in limbs.iter_mut() {
295                *limb = !*limb;
296            }
297
298            let mut carry: u128 = 1;
299            for limb in limbs.iter_mut().rev() {
300                let sum = (*limb as u128) + carry;
301                *limb = sum as u64;
302                carry = sum >> 64;
303                if carry == 0 {
304                    break;
305                }
306            }
307        }
308
309        let mut buf = [0u8; 8];
310        buf.copy_from_slice(&raw[0..8]);
311        let w0 = u64::from_le_bytes(buf);
312        buf.copy_from_slice(&raw[8..16]);
313        let w1 = u64::from_le_bytes(buf);
314        buf.copy_from_slice(&raw[16..24]);
315        let w2 = u64::from_le_bytes(buf);
316        buf.copy_from_slice(&raw[24..32]);
317        let w3 = u64::from_le_bytes(buf);
318
319        let mut limbs = [w3, w2, w1, w0];
320        let negative = (limbs[0] & (1u64 << 63)) != 0;
321        if negative {
322            twos_complement(&mut limbs);
323        }
324
325        if is_zero(&limbs) {
326            out.write_char('0').map_err(|_| 1u32)?;
327            return Ok(());
328        }
329
330        let mut digits = [0u8; 78];
331        let mut len = 0usize;
332        while !is_zero(&limbs) {
333            let digit = div_mod10(&mut limbs);
334            digits[len] = b'0' + digit;
335            len += 1;
336        }
337
338        if negative {
339            out.write_char('-').map_err(|_| 1u32)?;
340        }
341
342        for &digit in digits[..len].iter().rev() {
343            out.write_char(digit as char).map_err(|_| 1u32)?;
344        }
345
346        Ok(())
347    }
348
349    #[value_formatter(const_wasm = I256_BE_WASM)]
350    pub(crate) fn i256_be(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
351        fn div_mod10(limbs: &mut [u64; 4]) -> u8 {
352            let mut rem: u128 = 0;
353            for limb in limbs.iter_mut() {
354                let n = (rem << 64) | (*limb as u128);
355                *limb = (n / 10) as u64;
356                rem = n % 10;
357            }
358            rem as u8
359        }
360
361        fn is_zero(limbs: &[u64; 4]) -> bool {
362            limbs.iter().all(|&limb| limb == 0)
363        }
364
365        fn twos_complement(limbs: &mut [u64; 4]) {
366            for limb in limbs.iter_mut() {
367                *limb = !*limb;
368            }
369
370            let mut carry: u128 = 1;
371            for limb in limbs.iter_mut().rev() {
372                let sum = (*limb as u128) + carry;
373                *limb = sum as u64;
374                carry = sum >> 64;
375                if carry == 0 {
376                    break;
377                }
378            }
379        }
380
381        let mut buf = [0u8; 8];
382        buf.copy_from_slice(&raw[0..8]);
383        let w0 = u64::from_be_bytes(buf);
384        buf.copy_from_slice(&raw[8..16]);
385        let w1 = u64::from_be_bytes(buf);
386        buf.copy_from_slice(&raw[16..24]);
387        let w2 = u64::from_be_bytes(buf);
388        buf.copy_from_slice(&raw[24..32]);
389        let w3 = u64::from_be_bytes(buf);
390
391        let mut limbs = [w0, w1, w2, w3];
392        let negative = (limbs[0] & (1u64 << 63)) != 0;
393        if negative {
394            twos_complement(&mut limbs);
395        }
396
397        if is_zero(&limbs) {
398            out.write_char('0').map_err(|_| 1u32)?;
399            return Ok(());
400        }
401
402        let mut digits = [0u8; 78];
403        let mut len = 0usize;
404        while !is_zero(&limbs) {
405            let digit = div_mod10(&mut limbs);
406            digits[len] = b'0' + digit;
407            len += 1;
408        }
409
410        if negative {
411            out.write_char('-').map_err(|_| 1u32)?;
412        }
413
414        for &digit in digits[..len].iter().rev() {
415            out.write_char(digit as char).map_err(|_| 1u32)?;
416        }
417
418        Ok(())
419    }
420}
421
422impl ToValue<U256BE> for ethnum::U256 {
423    fn to_value(self) -> Value<U256BE> {
424        Value::new(self.to_be_bytes())
425    }
426}
427
428impl FromValue<'_, U256BE> for ethnum::U256 {
429    fn from_value(v: &Value<U256BE>) -> Self {
430        ethnum::U256::from_be_bytes(v.raw)
431    }
432}
433
434impl ToValue<U256LE> for ethnum::U256 {
435    fn to_value(self) -> Value<U256LE> {
436        Value::new(self.to_le_bytes())
437    }
438}
439
440impl FromValue<'_, U256LE> for ethnum::U256 {
441    fn from_value(v: &Value<U256LE>) -> Self {
442        ethnum::U256::from_le_bytes(v.raw)
443    }
444}
445
446impl ToValue<I256BE> for ethnum::I256 {
447    fn to_value(self) -> Value<I256BE> {
448        Value::new(self.to_be_bytes())
449    }
450}
451
452impl FromValue<'_, I256BE> for ethnum::I256 {
453    fn from_value(v: &Value<I256BE>) -> Self {
454        ethnum::I256::from_be_bytes(v.raw)
455    }
456}
457
458impl ToValue<I256LE> for ethnum::I256 {
459    fn to_value(self) -> Value<I256LE> {
460        Value::new(self.to_le_bytes())
461    }
462}
463
464impl FromValue<'_, I256LE> for ethnum::I256 {
465    fn from_value(v: &Value<I256LE>) -> Self {
466        ethnum::I256::from_le_bytes(v.raw)
467    }
468}
469
470impl ToValue<U256LE> for u8 {
471    fn to_value(self) -> Value<U256LE> {
472        Value::new(ethnum::U256::new(self.into()).to_le_bytes())
473    }
474}
475
476impl ToValue<U256LE> for u16 {
477    fn to_value(self) -> Value<U256LE> {
478        Value::new(ethnum::U256::new(self.into()).to_le_bytes())
479    }
480}
481
482impl ToValue<U256LE> for u32 {
483    fn to_value(self) -> Value<U256LE> {
484        Value::new(ethnum::U256::new(self.into()).to_le_bytes())
485    }
486}
487
488impl ToValue<U256LE> for u64 {
489    fn to_value(self) -> Value<U256LE> {
490        Value::new(ethnum::U256::new(self.into()).to_le_bytes())
491    }
492}
493
494impl ToValue<U256LE> for u128 {
495    fn to_value(self) -> Value<U256LE> {
496        Value::new(ethnum::U256::new(self).to_le_bytes())
497    }
498}
499
500impl ToValue<U256BE> for u8 {
501    fn to_value(self) -> Value<U256BE> {
502        Value::new(ethnum::U256::new(self.into()).to_be_bytes())
503    }
504}
505
506impl ToValue<U256BE> for u16 {
507    fn to_value(self) -> Value<U256BE> {
508        Value::new(ethnum::U256::new(self.into()).to_be_bytes())
509    }
510}
511
512impl ToValue<U256BE> for u32 {
513    fn to_value(self) -> Value<U256BE> {
514        Value::new(ethnum::U256::new(self.into()).to_be_bytes())
515    }
516}
517
518impl ToValue<U256BE> for u64 {
519    fn to_value(self) -> Value<U256BE> {
520        Value::new(ethnum::U256::new(self.into()).to_be_bytes())
521    }
522}
523
524impl ToValue<U256BE> for u128 {
525    fn to_value(self) -> Value<U256BE> {
526        Value::new(ethnum::U256::new(self).to_be_bytes())
527    }
528}
529
530impl ToValue<I256LE> for i8 {
531    fn to_value(self) -> Value<I256LE> {
532        Value::new(ethnum::I256::new(self.into()).to_le_bytes())
533    }
534}
535
536impl ToValue<I256LE> for i16 {
537    fn to_value(self) -> Value<I256LE> {
538        Value::new(ethnum::I256::new(self.into()).to_le_bytes())
539    }
540}
541
542impl ToValue<I256LE> for i32 {
543    fn to_value(self) -> Value<I256LE> {
544        Value::new(ethnum::I256::new(self.into()).to_le_bytes())
545    }
546}
547
548impl ToValue<I256LE> for i64 {
549    fn to_value(self) -> Value<I256LE> {
550        Value::new(ethnum::I256::new(self.into()).to_le_bytes())
551    }
552}
553
554impl ToValue<I256LE> for i128 {
555    fn to_value(self) -> Value<I256LE> {
556        Value::new(ethnum::I256::new(self).to_le_bytes())
557    }
558}
559
560impl ToValue<I256BE> for i8 {
561    fn to_value(self) -> Value<I256BE> {
562        Value::new(ethnum::I256::new(self.into()).to_be_bytes())
563    }
564}
565
566impl ToValue<I256BE> for i32 {
567    fn to_value(self) -> Value<I256BE> {
568        Value::new(ethnum::I256::new(self.into()).to_be_bytes())
569    }
570}
571
572impl ToValue<I256BE> for i64 {
573    fn to_value(self) -> Value<I256BE> {
574        Value::new(ethnum::I256::new(self.into()).to_be_bytes())
575    }
576}
577
578impl ToValue<I256BE> for i128 {
579    fn to_value(self) -> Value<I256BE> {
580        Value::new(ethnum::I256::new(self).to_be_bytes())
581    }
582}