using System.Buffers.Binary; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using Newtonsoft.Json; namespace BililiveRecorder.Flv.Amf { [DebuggerTypeProxy(typeof(AmfDictionaryDebugView))] [DebuggerDisplay("AmfEcmaArray, Count = {Count}")] public class ScriptDataEcmaArray : IScriptDataValue, IDictionary, ICollection>, IEnumerable>, IReadOnlyCollection>, IReadOnlyDictionary { public ScriptDataType Type => ScriptDataType.EcmaArray; [JsonProperty] public Dictionary Value { get; set; } = new Dictionary(); public void WriteTo(Stream stream) { stream.WriteByte((byte)this.Type); { var buffer = new byte[sizeof(uint)]; BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)this.Value.Count); stream.Write(buffer); } foreach (var item in this.Value) { // key var bytes = Encoding.UTF8.GetBytes(item.Key); if (bytes.Length > ushort.MaxValue) throw new AmfException($"Cannot write more than {ushort.MaxValue} into ScriptDataString"); var buffer = new byte[sizeof(ushort)]; BinaryPrimitives.WriteUInt16BigEndian(buffer, (ushort)bytes.Length); stream.Write(buffer); stream.Write(bytes); // value item.Value.WriteTo(stream); } stream.Write(new byte[] { 0, 0, 9 }); } public IScriptDataValue this[string key] { get => ((IDictionary)this.Value)[key]; set => ((IDictionary)this.Value)[key] = value; } public ICollection Keys => ((IDictionary)this.Value).Keys; public ICollection Values => ((IDictionary)this.Value).Values; IEnumerable IReadOnlyDictionary.Keys => ((IReadOnlyDictionary)this.Value).Keys; IEnumerable IReadOnlyDictionary.Values => ((IReadOnlyDictionary)this.Value).Values; public int Count => ((IDictionary)this.Value).Count; public bool IsReadOnly => ((IDictionary)this.Value).IsReadOnly; public void Add(string key, IScriptDataValue value) => ((IDictionary)this.Value).Add(key, value); public void Add(KeyValuePair item) => ((IDictionary)this.Value).Add(item); public void Clear() => ((IDictionary)this.Value).Clear(); public bool Contains(KeyValuePair item) => ((IDictionary)this.Value).Contains(item); public bool ContainsKey(string key) => ((IDictionary)this.Value).ContainsKey(key); public void CopyTo(KeyValuePair[] array, int arrayIndex) => ((IDictionary)this.Value).CopyTo(array, arrayIndex); public IEnumerator> GetEnumerator() => ((IDictionary)this.Value).GetEnumerator(); public bool Remove(string key) => ((IDictionary)this.Value).Remove(key); public bool Remove(KeyValuePair item) => ((IDictionary)this.Value).Remove(item); #pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). public bool TryGetValue(string key, [MaybeNullWhen(false)] out IScriptDataValue value) => ((IDictionary)this.Value).TryGetValue(key, out value!); #pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). IEnumerator IEnumerable.GetEnumerator() => ((IDictionary)this.Value).GetEnumerator(); public static implicit operator Dictionary(ScriptDataEcmaArray ecmaArray) => ecmaArray.Value; public static implicit operator ScriptDataEcmaArray(Dictionary ecmaArray) => new ScriptDataEcmaArray { Value = ecmaArray }; public static implicit operator ScriptDataEcmaArray(ScriptDataObject @object) => new ScriptDataEcmaArray { Value = @object }; } }