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}