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