///|
/// Helper to encode the initial byte and integer argument (up to 64 bits)
fn encode_type_and_val(buf : Buffer, major : Int, val : UInt64) -> Unit {
let m = major << 5
if val < 24 {
buf.write_byte((m | val.to_int()).to_byte())
} else if val <= 0xFF {
buf.write_byte((m | 24).to_byte())
buf.write_byte(val.to_int().to_byte())
} else if val <= 0xFFFF {
buf.write_byte((m | 25).to_byte())
buf.write_byte((val.to_int() >> 8).to_byte())
buf.write_byte((val.to_int() & 0xFF).to_byte())
} else if val <= 0xFFFFFFFF {
buf.write_byte((m | 26).to_byte())
buf.write_byte((val.to_int() >> 24).to_byte())
buf.write_byte(((val.to_int() >> 16) & 0xFF).to_byte())
buf.write_byte(((val.to_int() >> 8) & 0xFF).to_byte())
buf.write_byte((val.to_int() & 0xFF).to_byte())
} else {
buf.write_byte((m | 27).to_byte())
let v1 = (val >> 32).to_int()
let v2 = (val & 0xFFFFFFFF).to_int()
buf.write_byte((v1 >> 24).to_byte())
buf.write_byte(((v1 >> 16) & 0xFF).to_byte())
buf.write_byte(((v1 >> 8) & 0xFF).to_byte())
buf.write_byte((v1 & 0xFF).to_byte())
buf.write_byte((v2 >> 24).to_byte())
buf.write_byte(((v2 >> 16) & 0xFF).to_byte())
buf.write_byte(((v2 >> 8) & 0xFF).to_byte())
buf.write_byte((v2 & 0xFF).to_byte())
}
}
///|
fn write_uint16_be(buf : Buffer, value : UInt64) -> Unit {
buf.write_byte((value >> 8).to_byte())
buf.write_byte(value.to_byte())
}
///|
fn write_uint32_be(buf : Buffer, value : UInt) -> Unit {
buf.write_byte((value >> 24).to_byte())
buf.write_byte((value >> 16).to_byte())
buf.write_byte((value >> 8).to_byte())
buf.write_byte(value.to_byte())
}
///|
fn write_uint64_be(buf : Buffer, value : UInt64) -> Unit {
buf.write_byte((value >> 56).to_byte())
buf.write_byte((value >> 48).to_byte())
buf.write_byte((value >> 40).to_byte())
buf.write_byte((value >> 32).to_byte())
buf.write_byte((value >> 24).to_byte())
buf.write_byte((value >> 16).to_byte())
buf.write_byte((value >> 8).to_byte())
buf.write_byte(value.to_byte())
}
///|
fn preferred_half_bits(value : Double) -> UInt64? {
if value.is_nan() {
Some(0x7E00UL)
} else if value.is_pos_inf() {
Some(0x7C00UL)
} else if value.is_neg_inf() {
Some(0xFC00UL)
} else {
let sign = value.reinterpret_as_uint64() >> 63 << 15
let absolute = value.abs()
if absolute < 0.00006103515625 {
let scaled = absolute * 16777216.0
let fraction = scaled.to_uint64()
if fraction.to_double() == scaled && fraction <= 0x3FFUL {
Some(sign | fraction)
} else {
None
}
} else if absolute <= 65504.0 {
let bits = absolute.reinterpret_as_uint64()
let exponent = ((bits >> 52) & 0x7FFUL).to_int()
let fraction = bits & 0xFFFFFFFFFFFFFUL
let unbiased_exponent = exponent - 1023
if unbiased_exponent >= -14 &&
unbiased_exponent <= 15 &&
(fraction & 0x3FFFFFFFFFFUL) == 0UL {
let half_exponent = (unbiased_exponent + 15).to_uint64()
let half_fraction = fraction >> 42
Some(sign | (half_exponent << 10) | half_fraction)
} else {
None
}
} else {
None
}
}
}
///|
fn encode_float(buf : Buffer, value : Double) -> Unit {
match preferred_half_bits(value) {
Some(bits) => {
buf.write_byte(0xF9)
write_uint16_be(buf, bits)
}
None => {
let single = Float::from_double(value)
if single.to_double() == value {
buf.write_byte(0xFA)
write_uint32_be(buf, single.reinterpret_as_uint())
} else {
buf.write_byte(0xFB)
write_uint64_be(buf, value.reinterpret_as_uint64())
}
}
}
}
///|
/// Helper to encode a string to UTF-8
fn encode_utf8(buf : Buffer, s : String) -> Unit {
for c in s {
let scalar = c.to_int()
if scalar <= 0x7F {
buf.write_byte(scalar.to_byte())
} else if scalar <= 0x7FF {
buf.write_byte(((scalar >> 6) | 0xC0).to_byte())
buf.write_byte(((scalar & 0x3F) | 0x80).to_byte())
} else if scalar <= 0xFFFF {
buf.write_byte(((scalar >> 12) | 0xE0).to_byte())
buf.write_byte((((scalar >> 6) & 0x3F) | 0x80).to_byte())
buf.write_byte(((scalar & 0x3F) | 0x80).to_byte())
} else {
buf.write_byte(((scalar >> 18) | 0xF0).to_byte())
buf.write_byte((((scalar >> 12) & 0x3F) | 0x80).to_byte())
buf.write_byte((((scalar >> 6) & 0x3F) | 0x80).to_byte())
buf.write_byte(((scalar & 0x3F) | 0x80).to_byte())
}
}
}
///|
fn bytes_less(lhs : Bytes, rhs : Bytes) -> Bool {
if lhs.length() != rhs.length() {
return lhs.length() < rhs.length()
}
let shared = lhs.length()
for i = 0; i < shared; i = i + 1 {
if lhs[i] != rhs[i] {
return lhs[i] < rhs[i]
}
}
false
}
///|
fn sort_map_entries(
entries : Array[(CborValue, CborValue)],
) -> Array[(CborValue, CborValue)] {
let sorted = []
for i = 0; i < entries.length(); i = i + 1 {
sorted.push(entries[i])
}
for i = 1; i < sorted.length(); i = i + 1 {
let current = sorted[i]
let current_key = encode(current.0)
let mut j = i
while j > 0 {
let previous_key = encode(sorted[j - 1].0)
if bytes_less(current_key, previous_key) {
sorted[j] = sorted[j - 1]
j = j - 1
} else {
break
}
}
sorted[j] = current
}
sorted
}
///|
/// Encode a CBOR value into a mutable Buffer.
pub fn encode_to_buffer(value : CborValue, buf : Buffer) -> Unit {
match value {
Unsigned(i) => encode_type_and_val(buf, 0, i)
Integer(i) =>
if i >= 0L {
encode_type_and_val(buf, 0, i.reinterpret_as_uint64())
} else {
encode_type_and_val(buf, 1, (-1L - i).reinterpret_as_uint64())
}
Bytes(b) => {
encode_type_and_val(buf, 2, b.length().to_uint64())
buf.write_bytes(b)
}
Text(s) => {
let tmp = Buffer()
encode_utf8(tmp, s)
let utf8_bytes = tmp.to_bytes()
encode_type_and_val(buf, 3, utf8_bytes.length().to_uint64())
buf.write_bytes(utf8_bytes)
}
Array(arr) => {
encode_type_and_val(buf, 4, arr.length().to_uint64())
for i = 0; i < arr.length(); i = i + 1 {
encode_to_buffer(arr[i], buf)
}
}
Map(m) => {
let sorted_entries = sort_map_entries(m)
encode_type_and_val(buf, 5, sorted_entries.length().to_uint64())
for i = 0; i < sorted_entries.length(); i = i + 1 {
encode_to_buffer(sorted_entries[i].0, buf)
encode_to_buffer(sorted_entries[i].1, buf)
}
}
Tag(t, v) => {
encode_type_and_val(buf, 6, t)
encode_to_buffer(v, buf)
}
Simple(s) => encode_type_and_val(buf, 7, s.to_uint64())
Float64(f) => encode_float(buf, f)
}
}
///|
/// Encode a CBOR value to a byte array.
pub fn encode(value : CborValue) -> Bytes {
let buf = Buffer()
encode_to_buffer(value, buf)
buf.to_bytes()
}