diff --git a/README.md b/README.md index 603ebb4..40fab4b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Based on Noah Pederson's [gleemor] module, licensed under MIT ## License -This library is licensed under the GNU Affero General Public License version 3, see `LICENSE`. +This library is licensed under the GNU Affero General Public License version 3, +see `LICENSE`. [Pdor]:https://www.youtube.com/watch?v=k3aehC2SSPw [gleemor]:https://github.com/chiefnoah/gleebor diff --git a/src/kmer.gleam b/src/kmer.gleam index 0922231..2bcfb24 100644 --- a/src/kmer.gleam +++ b/src/kmer.gleam @@ -12,6 +12,7 @@ pub type DecodedValue { Bytes(value: BitArray) String(value: String) Array(List(DecodedValue)) + Map(List(#(DecodedValue, DecodedValue))) Null Undefined } @@ -47,6 +48,9 @@ pub fn decode(bits: BitArray) -> DecodeResult { <<4:3, rest:bits>> -> decode_array(rest) |> map_decoded(Array) + <<5:3, rest:bits>> -> + decode_map(rest) + |> map_decoded(Map) <<7:3, x:5, rest:bits>> if x < 25 -> decode_simple(<>) <<7:3, x:5, rest:bits>> if x >= 25 -> decode_float(<>) @@ -93,6 +97,15 @@ fn decode_array(bits: BitArray) -> DecoderResult(List(DecodedValue)) { decode_next_n([], rest, length) } +fn decode_map( + bits: BitArray, +) -> DecoderResult(List(#(DecodedValue, DecodedValue))) { + use #(length, rest) <- try(decode_int(bits)) + use #(entries, remainder) <- try(decode_next_n([], rest, length * 2)) + use pairs <- try(into_pairs(entries)) + Ok(#(pairs, remainder)) +} + fn decode_float(bits: BitArray) -> DecoderResult(IEEEFloat) { case bits { <<25:5, float16:bytes-size(2), rest:bits>> -> @@ -115,6 +128,21 @@ fn decode_simple(bits: BitArray) -> DecodeResult { } } +fn into_pairs(list: List(a)) -> Result(List(#(a, a)), DecodeError) { + into_pairs_aggr([], list) +} + +fn into_pairs_aggr( + accum: List(#(a, a)), + remaining: List(a), +) -> Result(List(#(a, a)), DecodeError) { + case remaining { + [] -> Ok(reverse(accum)) + [_] -> Error(Malformed) + [key, value, ..rest] -> into_pairs_aggr([#(key, value), ..accum], rest) + } +} + fn decode_next_n( retrieved: List(DecodedValue), rest: BitArray, diff --git a/test/kmer_test.gleam b/test/kmer_test.gleam index 9933741..51ab296 100644 --- a/test/kmer_test.gleam +++ b/test/kmer_test.gleam @@ -4,7 +4,7 @@ import gleeunit/should import ieee_float.{finite, nan, positive_infinity} import kmer.{ type DecodeResult, type DecodedValue, Array, Boolean, Bytes, Float, Integer, - Null, String, Undefined, + Map, Null, String, Undefined, } pub fn main() { @@ -136,6 +136,25 @@ pub fn decode_list_test() { |> table_fn(table_test_decode) } +pub fn decode_map_test() { + [ + #( + <<5:3, 2:5, 3:3, 8:5, "test-key", 5, 3:3, 5:5, "other", 10>>, + Map([#(String("test-key"), Integer(5)), #(String("other"), Integer(10))]), + ), + #( + <<5:3, 24:5, 4:8, 1, 2, 3, 4, 5, 6, 7, 8>>, + Map([ + #(Integer(1), Integer(2)), + #(Integer(3), Integer(4)), + #(Integer(5), Integer(6)), + #(Integer(7), Integer(8)), + ]), + ), + ] + |> table_fn(table_test_decode) +} + fn table_fn(tests: List(#(a, b)), test_fn: fn(a, b) -> Nil) { list.map(tests, fn(t) { let #(test_case, expected) = t