triblespace_core/value/schemas/
iu256.rs1use 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;
21pub struct U256LE;
23
24pub struct U256BE;
26
27pub struct I256LE;
29
30pub struct I256BE;
32
33pub type I256 = I256BE;
36
37pub 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}