using System; using System.Collections.Generic; using MessagePack; namespace GameCult.Caching.MessagePack; // UntrustedData, plus a collision-resistant comparer for CultRecordRef dictionary keys over the key string. public sealed class CultMessagePackSecurity : MessagePackSecurity { /// /// The deepest a document payload may nest, on read and on write, in every CultCache runtime: every MessagePack array and /// map counts, read by a formatter or skipped past it, and the outermost array is depth 1. A payload of this depth is /// accepted and one container deeper is refused. MessagePack's own object-graph counter is set to the same value as a /// backstop; the rule itself is CultPayloadDepth.Check, run by the document serialization entry points. /// public const int MaximumPayloadDepth = 256; public static readonly CultMessagePackSecurity Instance = new(UntrustedData.WithMaximumObjectGraphDepth(MaximumPayloadDepth)); private CultMessagePackSecurity(MessagePackSecurity copyFrom) : base(copyFrom) { } protected override IEqualityComparer GetHashCollisionResistantEqualityComparer() { var type = typeof(T); if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(CultRecordRef<>)) return base.GetHashCollisionResistantEqualityComparer(); var comparerType = typeof(RecordRefComparer<>).MakeGenericType(type.GetGenericArguments()[0]); return (IEqualityComparer)Activator.CreateInstance(comparerType, GetEqualityComparer())!; } protected override MessagePackSecurity Clone() => new CultMessagePackSecurity(this); private sealed class RecordRefComparer : IEqualityComparer> { private readonly IEqualityComparer _keys; public RecordRefComparer(IEqualityComparer keys) => _keys = keys; public bool Equals(CultRecordRef x, CultRecordRef y) => _keys.Equals(x.Key.Value, y.Key.Value); public int GetHashCode(CultRecordRef value) => _keys.GetHashCode(value.Key.Value); } }