diff --git a/src/kmer.gleam b/src/kmer.gleam index 651af3a..0922231 100644 --- a/src/kmer.gleam +++ b/src/kmer.gleam @@ -1,4 +1,5 @@ import gleam/bit_array +import gleam/list.{reverse} import gleam/result.{try} import ieee_float.{ type IEEEFloat, from_bytes_16_be, from_bytes_32_be, from_bytes_64_be, @@ -10,6 +11,7 @@ pub type DecodedValue { Boolean(value: Bool) Bytes(value: BitArray) String(value: String) + Array(List(DecodedValue)) Null Undefined } @@ -42,6 +44,9 @@ pub fn decode(bits: BitArray) -> DecodeResult { <<3:3, rest:bits>> -> decode_string(rest) |> map_decoded(String) + <<4:3, rest:bits>> -> + decode_array(rest) + |> map_decoded(Array) <<7:3, x:5, rest:bits>> if x < 25 -> decode_simple(<>) <<7:3, x:5, rest:bits>> if x >= 25 -> decode_float(<>) @@ -83,6 +88,11 @@ fn decode_string(bits: BitArray) -> DecoderResult(String) { } } +fn decode_array(bits: BitArray) -> DecoderResult(List(DecodedValue)) { + use #(length, rest) <- try(decode_int(bits)) + decode_next_n([], rest, length) +} + fn decode_float(bits: BitArray) -> DecoderResult(IEEEFloat) { case bits { <<25:5, float16:bytes-size(2), rest:bits>> -> @@ -105,6 +115,22 @@ fn decode_simple(bits: BitArray) -> DecodeResult { } } +fn decode_next_n( + retrieved: List(DecodedValue), + rest: BitArray, + n: Int, +) -> DecoderResult(List(DecodedValue)) { + // Due to how Gleam handles lists, we build it in reverse + // and then reverse it at the end before returning + case n { + 0 -> Ok(#(reverse(retrieved), rest)) + n -> { + use #(next, rest) <- try(decode(rest)) + decode_next_n([next, ..retrieved], rest, n - 1) + } + } +} + fn map_decoded( result: DecoderResult(t), map_fn: fn(t) -> DecodedValue, diff --git a/test/kmer_test.gleam b/test/kmer_test.gleam index 00f6ca9..9933741 100644 --- a/test/kmer_test.gleam +++ b/test/kmer_test.gleam @@ -3,8 +3,8 @@ import gleeunit import gleeunit/should import ieee_float.{finite, nan, positive_infinity} import kmer.{ - type DecodeResult, type DecodedValue, Boolean, Bytes, Float, Integer, Null, - String, Undefined, + type DecodeResult, type DecodedValue, Array, Boolean, Bytes, Float, Integer, + Null, String, Undefined, } pub fn main() { @@ -122,6 +122,20 @@ pub fn decode_string_test() { |> table_fn(table_test_decode) } +pub fn decode_list_test() { + [ + #( + <<4:3, 5:5, 1, 2, 3, 4, 5>>, + Array([Integer(1), Integer(2), Integer(3), Integer(4), Integer(5)]), + ), + #( + <<4:3, 24:5, 3:8, 3:3, 11:5, "Hello world", 1:3, 24:5, 123:8, 3>>, + Array([String("Hello world"), Integer(-124), Integer(3)]), + ), + ] + |> 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