sdbd-demo.git

git clone https://git.crispbyte.dev/sdbd-demo.git

sdbd-demo.git / SDBD.Codec
CheddarCrisp  ·  2024-01-10

Codec.cs

 1namespace SDBD;
 2
 3public class Codec : ICodec {
 4  public Document Decode(byte[] data) {
 5    using var input = new MemoryStream(data);
 6
 7    var version = input.ReadByte();
 8
 9    return version switch {
10      0x01 => DecodeV1(input),
11      _ => throw new Exception("Unsupported version")
12    };
13  }
14
15  public byte[] Encode(Document document) {
16    var dataLength = document.Data.Length;
17    var contentLength = new Dictionary<string, string> () {
18      { "content-length", dataLength.ToString() }
19    };
20    var headers = document.Metadata.Union(contentLength);
21
22    var packedHeaders = packHeaders(headers);
23    var headerLength = Convert.ToUInt16(packedHeaders.Length);
24
25    using var output = new MemoryStream();
26    output.WriteByte(0x01);
27    output.Write(BitConverter.GetBytes(headerLength));
28    output.Write(packedHeaders);
29    output.Write(document.Data);
30
31    return output.ToArray();
32  }
33
34  private Document DecodeV1(Stream stream) {
35    var headerLengthBytes = new byte[2];
36    stream.ReadExactly(headerLengthBytes);
37
38    var headerLength = BitConverter.ToUInt16(headerLengthBytes);
39
40    var headerBytes = new byte[headerLength];
41    stream.ReadExactly(headerBytes);
42
43    var headers = unpackHeaders(headerBytes);
44    string contentLengthString;
45    headers.Remove("content-length", out contentLengthString);
46    var contentLength = int.Parse(contentLengthString);
47
48    var data = new byte[contentLength];
49    stream.ReadExactly(data);
50
51    return new Document(headers, data);
52  }
53
54  private Dictionary<string, string> unpackHeaders(byte[] packedHeaders) {
55    var decoder = new hpack.Decoder(8192, 4096);
56    var listener = new HeaderListener();
57
58    using var reader = new BinaryReader(new MemoryStream(packedHeaders));
59    decoder.Decode(reader, listener);
60    decoder.EndHeaderBlock();
61
62    return listener.Headers;
63  }
64
65  private byte[] packHeaders(IEnumerable<KeyValuePair<string, string>> headers) {
66    var encoder = new hpack.Encoder(0); //0 will disable dynamic table that we don't need anyways
67
68    using var output = new MemoryStream();
69    using var writer = new BinaryWriter(output);
70
71    foreach(var (name, value) in headers) {
72      encoder.EncodeHeader(writer, name, value);
73    }
74
75    return output.ToArray();
76  }
77}