diff --git a/src/at/cbor.gleam b/src/at/cbor.gleam index 74575ac..084df23 100644 --- a/src/at/cbor.gleam +++ b/src/at/cbor.gleam @@ -1,10 +1,12 @@ // IMPORTS --------------------------------------------------------------------- +import at/cid.{type Cid} import gleam/bit_array import gleam/dynamic.{type Dynamic} import gleam/dynamic/decode.{type Decoder} import gleam/list import gleam/option.{type Option, None, Some} +import gleam/order.{type Order} import gleam/string // TYPES ----------------------------------------------------------------------- @@ -132,7 +134,7 @@ fn parse_dynamic(bits: BitArray) -> Result(#(Dynamic, BitArray), DecodeError) { if size <= 23 -> case bits { - <<0:8, _:bits>> -> Ok(#(dynamic.bit_array(bits), remaining)) + <<0:8, cid:bits>> -> Ok(#(dynamic.bit_array(cid), remaining)) _ -> Error(MalformedInput) } @@ -177,7 +179,7 @@ fn parse_dynamic(bits: BitArray) -> Result(#(Dynamic, BitArray), DecodeError) { remaining:bits, >> -> case bits { - <<0:8, _:bits>> -> Ok(#(dynamic.bit_array(bits), remaining)) + <<0:8, cid:bits>> -> Ok(#(dynamic.bit_array(cid), remaining)) _ -> Error(MalformedInput) } @@ -329,14 +331,19 @@ fn do_array( } /// Encode an object or map in CBOR binary format. Entries can be provided in -/// any order and will be sorted according to the lexical order of their keys. +/// any order and will be sorted according to DAG-CBOR canonical key ordering: +/// shortest UTF-8 byte length first, then lexicographically. /// pub fn object(entries: List(#(String, Cbor))) -> Cbor { - do_object( - list.sort(entries, by: fn(a, b) { string.compare(a.0, b.0) }), - 0, - <<>>, - ) + do_object(list.sort(entries, by: canonical_key_order), 0, <<>>) +} + +fn canonical_key_order(a: #(String, _), b: #(String, _)) -> Order { + case bit_array.byte_size(<>), bit_array.byte_size(<>) { + a, b if a < b -> order.Lt + a, b if a > b -> order.Gt + _, _ -> string.compare(a.0, b.0) + } } fn do_object( @@ -364,3 +371,21 @@ pub fn option(value: Option(a), inner: fn(a) -> Cbor) -> Cbor { None -> null } } + +/// +/// +pub fn bytes(value: BitArray) -> Cbor { + let value = bit_array.pad_to_bytes(value) + let length = bit_array.byte_size(value) + + Cbor(<<2:3, do_int(length):bits, value:bits>>) +} + +/// +/// +pub fn cid(value: Cid) -> Cbor { + let bits = cid.to_bit_array(value) + let length = bit_array.byte_size(bits) + 1 + + Cbor(<<6:3, 24:5, 42:8, 2:3, do_int(length):bits, 0:8, bits:bits>>) +}