Skip to content

Commit 6595c6d

Browse files
committed
fix(determin): align DECIMAL wire format with RFC-0111
RFC-0111 §Canonical Byte Format specifies: Byte 0: Version (0x01), Byte 1-3: Reserved, Byte 4: Scale, Bytes 5-7: Reserved, Bytes 8-23: Mantissa The determin crate had the old (incorrect) format with mantissa at bytes 0-15 and scale at byte 23. Also fixed mission 0111-decimal-serialization acceptance criteria to match RFC-0111.
1 parent 8a1fcf1 commit 6595c6d

2 files changed

Lines changed: 27 additions & 14 deletions

File tree

determin/src/decimal.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -236,23 +236,33 @@ impl Decimal {
236236
}
237237
}
238238

239-
/// Serialize Decimal to 24-byte canonical wire format
239+
/// Serialize Decimal to 24-byte canonical wire format per RFC-0111 §Canonical Byte Format:
240+
/// Byte 0: Version (0x01)
241+
/// Byte 1: Reserved (0x00)
242+
/// Bytes 2-3: Reserved (0x00)
243+
/// Byte 4: Scale (u8, range 0-36)
244+
/// Bytes 5-7: Reserved (0x00)
245+
/// Bytes 8-23: Mantissa (i128 big-endian, two's complement)
240246
pub fn decimal_to_bytes(d: &Decimal) -> [u8; 24] {
241247
let mut bytes = [0u8; 24];
242-
bytes[0..16].copy_from_slice(&d.mantissa.to_be_bytes());
243-
// bytes[16..23] remain zero padding
244-
bytes[23] = d.scale;
248+
bytes[0] = 0x01; // Version
249+
bytes[4] = d.scale;
250+
bytes[8..24].copy_from_slice(&d.mantissa.to_be_bytes());
245251
bytes
246252
}
247253

248-
/// Deserialize from 24-byte canonical wire format
254+
/// Deserialize from 24-byte canonical wire format per RFC-0111 §Canonical Byte Format
249255
pub fn decimal_from_bytes(bytes: [u8; 24]) -> Result<Decimal, DecimalError> {
250-
// Verify zero padding
251-
if bytes[16..23] != [0u8; 7] {
256+
// Verify version
257+
if bytes[0] != 0x01 {
252258
return Err(DecimalError::NonCanonical);
253259
}
254-
let mantissa = i128::from_be_bytes(bytes[0..16].try_into().unwrap());
255-
let scale = bytes[23];
260+
// Verify reserved bytes
261+
if bytes[1] != 0x00 || bytes[2..4] != [0x00, 0x00] || bytes[5..8] != [0x00, 0x00, 0x00] {
262+
return Err(DecimalError::NonCanonical);
263+
}
264+
let scale = bytes[4];
265+
let mantissa = i128::from_be_bytes(bytes[8..24].try_into().unwrap());
256266

257267
// Check scale bounds first
258268
if scale > MAX_DECIMAL_SCALE {

missions/completed/0111-decimal-serialization.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ RFC-0111 (Numeric): Deterministic DECIMAL
1010
Implement DECIMAL serialization (to wire format) and deserialization (from wire format) per RFC-0111 §Canonical Byte Format.
1111

1212
## Acceptance Criteria
13-
- [ ] SERIALIZE: Decimal → 24-byte canonical wire format
14-
- bytes 0-15: mantissa (big-endian i128, two's complement)
15-
- bytes 16-22: zero padding
16-
- byte 23: scale (u8)
13+
- [ ] SERIALIZE: Decimal → 24-byte canonical wire format per RFC-0111 §Canonical Byte Format
14+
- Byte 0: Version (0x01)
15+
- Byte 1: Reserved (0x00)
16+
- Bytes 2-3: Reserved (0x00)
17+
- Byte 4: Scale (u8, range 0-36)
18+
- Bytes 5-7: Reserved (0x00)
19+
- Bytes 8-23: Mantissa (i128 big-endian, two's complement)
1720
- [ ] DESERIALIZE: 24-byte → Decimal with validation
18-
- Reject non-canonical representations
21+
- Reject non-canonical representations (bytes 1-3, 5-7 must be 0x00)
1922
- Validate mantissa range
2023
- Validate scale ≤ 36
2124
- [ ] Byte format uses big-endian for network order

0 commit comments

Comments
 (0)