using System.IO.Compression; using System.Text; namespace MeWeb.Core.Knowledge; public class NetworkInfo { public uint Number { get; set; } public string Organisation { get; set; } public List Upstreams { get; set; } = []; } public sealed class NetworkTable { const uint Magic = 0x324B574D; readonly Dictionary networks = []; public DateTimeOffset BuiltAt { get; private set; } public int Count => networks.Count; public NetworkInfo Find(uint number) => networks.GetValueOrDefault(number); public static NetworkTable Read(Stream compressed) { using var brotli = new BrotliStream(compressed, CompressionMode.Decompress); using var reader = new BinaryReader(brotli, Encoding.UTF8); if (reader.ReadUInt32() != Magic) throw new InvalidDataException("not a me-web? network table"); var table = new NetworkTable { BuiltAt = DateTimeOffset.FromUnixTimeSeconds(reader.ReadInt64()) }; var count = reader.Read7BitEncodedInt(); var previous = 0u; for (var i = 0; i < count; i++) { previous += (uint)reader.Read7BitEncodedInt64(); var info = new NetworkInfo { Number = previous, Organisation = reader.ReadString() }; var upstreams = reader.Read7BitEncodedInt(); for (var u = 0; u < upstreams; u++) info.Upstreams.Add((uint)reader.Read7BitEncodedInt64()); if (info.Organisation.Length == 0) info.Organisation = null; table.networks[info.Number] = info; } return table; } public static void Write(Stream output, DateTimeOffset builtAt, IEnumerable networks) { var list = networks.Where(n => n.Number != 0).OrderBy(n => n.Number).ToList(); using var brotli = new BrotliStream(output, CompressionLevel.SmallestSize, leaveOpen: true); using var writer = new BinaryWriter(brotli, Encoding.UTF8); writer.Write(Magic); writer.Write(builtAt.ToUnixTimeSeconds()); writer.Write7BitEncodedInt(list.Count); var previous = 0u; foreach (var info in list) { writer.Write7BitEncodedInt64(info.Number - previous); writer.Write(info.Organisation ?? ""); writer.Write7BitEncodedInt(info.Upstreams.Count); foreach (var upstream in info.Upstreams) writer.Write7BitEncodedInt64(upstream); previous = info.Number; } } }