Skip to main content

triblespace_core/value/schemas/
iu256.rs

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