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::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;
20pub struct U256LE;
22
23pub struct U256BE;
25
26pub struct I256LE;
28
29pub struct I256BE;
31
32pub type I256 = I256BE;
35
36pub 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}