sdbd-demo/SDBD.Codec/Codec.cs
2024-01-09 21:04:25 -05:00

77 lines
2.2 KiB
C#

namespace SDBD;
public class Codec : ICodec {
public Document Decode(byte[] data) {
using var input = new MemoryStream(data);
var version = input.ReadByte();
return version switch {
0x01 => DecodeV1(input),
_ => throw new Exception("Unsupported version")
};
}
public byte[] Encode(Document document) {
var dataLength = document.Data.Length;
var contentLength = new Dictionary<string, string> () {
{ "content-length", dataLength.ToString() }
};
var headers = document.Metadata.Union(contentLength);
var packedHeaders = packHeaders(headers);
var headerLength = Convert.ToUInt16(packedHeaders.Length);
using var output = new MemoryStream();
output.WriteByte(0x01);
output.Write(BitConverter.GetBytes(headerLength));
output.Write(packedHeaders);
output.Write(document.Data);
return output.ToArray();
}
private Document DecodeV1(Stream stream) {
var headerLengthBytes = new byte[2];
stream.ReadExactly(headerLengthBytes);
var headerLength = BitConverter.ToUInt16(headerLengthBytes);
var headerBytes = new byte[headerLength];
stream.ReadExactly(headerBytes);
var headers = unpackHeaders(headerBytes);
string contentLengthString;
headers.Remove("content-length", out contentLengthString);
var contentLength = int.Parse(contentLengthString);
var data = new byte[contentLength];
stream.ReadExactly(data);
return new Document(headers, data);
}
private Dictionary<string, string> unpackHeaders(byte[] packedHeaders) {
var decoder = new hpack.Decoder(8192, 4096);
var listener = new HeaderListener();
using var reader = new BinaryReader(new MemoryStream(packedHeaders));
decoder.Decode(reader, listener);
decoder.EndHeaderBlock();
return listener.Headers;
}
private byte[] packHeaders(IEnumerable<KeyValuePair<string, string>> headers) {
var encoder = new hpack.Encoder(0); //0 will disable dynamic table that we don't need anyways
using var output = new MemoryStream();
using var writer = new BinaryWriter(output);
foreach(var (name, value) in headers) {
encoder.EncodeHeader(writer, name, value);
}
return output.ToArray();
}
}