using System.Buffers.Binary;
using System.Text.Json;
using NATS.Server.JetStream.Storage;
namespace NATS.Server.JetStream.Cluster;
///
/// Binary codec for meta-group snapshots.
/// Format: [2:version_le][N:S2-compressed JSON of assignment map]
/// Go reference: jetstream_cluster.go:2075-2145 (encodeMetaSnapshot/decodeMetaSnapshot)
///
internal static class MetaSnapshotCodec
{
private const ushort CurrentVersion = 1;
// Use Populate so the getter-only Consumers dictionary on StreamAssignment
// is populated in-place by the deserializer rather than requiring a setter.
// Go reference: jetstream_cluster.go streamAssignment consumers map restoration.
private static readonly JsonSerializerOptions SerializerOptions = new()
{
PreferredObjectCreationHandling = System.Text.Json.Serialization.JsonObjectCreationHandling.Populate,
};
///
/// Encodes into the versioned, S2-compressed binary format.
/// Go reference: jetstream_cluster.go:2075 encodeMetaSnapshot.
///
/// Current stream placement assignments to persist into the meta snapshot.
public static byte[] Encode(Dictionary assignments)
{
var json = JsonSerializer.SerializeToUtf8Bytes(assignments, SerializerOptions);
var compressed = S2Codec.Compress(json);
var result = new byte[2 + compressed.Length];
BinaryPrimitives.WriteUInt16LittleEndian(result, CurrentVersion);
compressed.CopyTo(result, 2);
return result;
}
///
/// Decodes a versioned, S2-compressed binary snapshot into a stream assignment map.
/// Go reference: jetstream_cluster.go:2100 decodeMetaSnapshot.
///
/// Versioned binary snapshot payload received from replicated state.
///
/// Thrown when is too short or contains an unrecognised version.
///
public static Dictionary Decode(byte[] data)
{
if (data.Length < 2)
throw new InvalidOperationException("Meta snapshot too short to contain version header.");
var version = BinaryPrimitives.ReadUInt16LittleEndian(data);
if (version != CurrentVersion)
throw new InvalidOperationException($"Unknown meta snapshot version: {version}");
var compressed = data.AsSpan(2);
var json = S2Codec.Decompress(compressed);
return JsonSerializer.Deserialize>(json, SerializerOptions)
?? new Dictionary();
}
}