/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #region Utf8Json License https://github.com/neuecc/Utf8Json/blob/master/LICENSE // MIT License // // Copyright (c) 2017 Yoshifumi Kawai // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #endregion using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using OpenSearch.Net.Utf8Json.Internal; namespace OpenSearch.Net.Utf8Json.Formatters { internal class ArrayFormatter : IJsonFormatter { private static readonly ArrayPool ArrayPool = new ArrayPool(99); public void Serialize(ref JsonWriter writer, T[] value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } writer.WriteBeginArray(); var formatter = formatterResolver.GetFormatterWithVerify(); if (value.Length != 0) formatter.Serialize(ref writer, value[0], formatterResolver); for (var i = 1; i < value.Length; i++) { writer.WriteValueSeparator(); formatter.Serialize(ref writer, value[i], formatterResolver); } writer.WriteEndArray(); } public T[] Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var workingArea = ArrayPool.Rent(); try { var array = workingArea; reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) { if (array.Length < count) Array.Resize(ref array, array.Length * 2); array[count - 1] = formatter.Deserialize(ref reader, formatterResolver); } var result = new T[count]; Array.Copy(array, result, count); Array.Clear(workingArea, 0, Math.Min(count, workingArea.Length)); return result; } finally { ArrayPool.Return(workingArea); } } } internal class ArraySegmentFormatter : IJsonFormatter> { private static readonly ArrayPool ArrayPool = new ArrayPool(99); public void Serialize(ref JsonWriter writer, ArraySegment value, IJsonFormatterResolver formatterResolver) { if (value.Array == null) { writer.WriteNull(); return; } var array = value.Array; var offset = value.Offset; var count = value.Count; writer.WriteBeginArray(); var formatter = formatterResolver.GetFormatterWithVerify(); if (count != 0) formatter.Serialize(ref writer, value.Array[offset], formatterResolver); for (var i = 1; i < count; i++) { writer.WriteValueSeparator(); formatter.Serialize(ref writer, array[offset + i], formatterResolver); } writer.WriteEndArray(); } public ArraySegment Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return default(ArraySegment); var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var workingArea = ArrayPool.Rent(); try { var array = workingArea; reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) { if (array.Length < count) Array.Resize(ref array, array.Length * 2); array[count - 1] = formatter.Deserialize(ref reader, formatterResolver); } var result = new T[count]; Array.Copy(array, result, count); Array.Clear(workingArea, 0, Math.Min(count, workingArea.Length)); return new ArraySegment(result, 0, result.Length); } finally { ArrayPool.Return(workingArea); } } } internal class ListFormatter : IJsonFormatter> { public void Serialize(ref JsonWriter writer, List value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } writer.WriteBeginArray(); var formatter = formatterResolver.GetFormatterWithVerify(); if (value.Count != 0) formatter.Serialize(ref writer, value[0], formatterResolver); for (var i = 1; i < value.Count; i++) { writer.WriteValueSeparator(); formatter.Serialize(ref writer, value[i], formatterResolver); } writer.WriteEndArray(); } public List Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var list = new List(); reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) list.Add(formatter.Deserialize(ref reader, formatterResolver)); return list; } } internal abstract class CollectionFormatterBase : IJsonFormatter where TCollection : class, IEnumerable where TEnumerator : IEnumerator { public void Serialize(ref JsonWriter writer, TCollection value, IJsonFormatterResolver formatterResolver) { if (value == null) writer.WriteNull(); else { writer.WriteBeginArray(); var formatter = formatterResolver.GetFormatterWithVerify(); // Unity's foreach struct enumerator causes boxing so iterate manually. var e = GetSourceEnumerator(value); try { var isFirst = true; while (e.MoveNext()) { if (isFirst) isFirst = false; else writer.WriteValueSeparator(); formatter.Serialize(ref writer, e.Current, formatterResolver); } } finally { e.Dispose(); } writer.WriteEndArray(); } } public TCollection Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var formatter = formatterResolver.GetFormatterWithVerify(); var builder = Create(); var count = 0; reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) Add(ref builder, count - 1, formatter.Deserialize(ref reader, formatterResolver)); return Complete(ref builder); } // Some collections can use struct iterator, this is optimization path protected abstract TEnumerator GetSourceEnumerator(TCollection source); // abstraction for deserialize protected abstract TIntermediate Create(); protected abstract void Add(ref TIntermediate collection, int index, TElement value); protected abstract TCollection Complete(ref TIntermediate intermediateCollection); } internal abstract class CollectionFormatterBase : CollectionFormatterBase, TCollection> where TCollection : class, IEnumerable { protected override IEnumerator GetSourceEnumerator(TCollection source) => source.GetEnumerator(); } internal abstract class CollectionFormatterBase : CollectionFormatterBase where TCollection : class, IEnumerable { protected sealed override TCollection Complete(ref TCollection intermediateCollection) => intermediateCollection; } internal sealed class GenericCollectionFormatter : CollectionFormatterBase where TCollection : class, ICollection, new() { protected override TCollection Create() => new TCollection(); protected override void Add(ref TCollection collection, int index, TElement value) => collection.Add(value); } internal sealed class LinkedListFormatter : CollectionFormatterBase, LinkedList.Enumerator, LinkedList> { protected override void Add(ref LinkedList collection, int index, T value) => collection.AddLast(value); protected override LinkedList Complete(ref LinkedList intermediateCollection) => intermediateCollection; protected override LinkedList Create() => new LinkedList(); protected override LinkedList.Enumerator GetSourceEnumerator(LinkedList source) => source.GetEnumerator(); } internal sealed class QueueFormatter : CollectionFormatterBase, Queue.Enumerator, Queue> { protected override void Add(ref Queue collection, int index, T value) => collection.Enqueue(value); protected override Queue Create() => new Queue(); protected override Queue.Enumerator GetSourceEnumerator(Queue source) => source.GetEnumerator(); protected override Queue Complete(ref Queue intermediateCollection) => intermediateCollection; } // should deserialize reverse order. internal sealed class StackFormatter : CollectionFormatterBase, Stack.Enumerator, Stack> { protected override void Add(ref ArrayBuffer collection, int index, T value) => collection.Add(value); protected override ArrayBuffer Create() => new ArrayBuffer(4); protected override Stack.Enumerator GetSourceEnumerator(Stack source) => source.GetEnumerator(); protected override Stack Complete(ref ArrayBuffer intermediateCollection) { var bufArray = intermediateCollection.Buffer; var stack = new Stack(intermediateCollection.Size); for (var i = intermediateCollection.Size - 1; i >= 0; i--) stack.Push(bufArray[i]); return stack; } } internal sealed class HashSetFormatter : CollectionFormatterBase, HashSet.Enumerator, HashSet> { protected override void Add(ref HashSet collection, int index, T value) => collection.Add(value); protected override HashSet Complete(ref HashSet intermediateCollection) => intermediateCollection; protected override HashSet Create() => new HashSet(); protected override HashSet.Enumerator GetSourceEnumerator(HashSet source) => source.GetEnumerator(); } internal sealed class ReadOnlyCollectionFormatter : CollectionFormatterBase, ReadOnlyCollection> { protected override void Add(ref ArrayBuffer collection, int index, T value) => collection.Add(value); protected override ReadOnlyCollection Complete(ref ArrayBuffer intermediateCollection) => new ReadOnlyCollection(intermediateCollection.ToArray()); protected override ArrayBuffer Create() => new ArrayBuffer(4); } internal sealed class InterfaceListFormatter : CollectionFormatterBase, IList> { protected override void Add(ref List collection, int index, T value) => collection.Add(value); protected override List Create() => new List(); protected override IList Complete(ref List intermediateCollection) => intermediateCollection; } internal sealed class InterfaceCollectionFormatter : CollectionFormatterBase, ICollection> { protected override void Add(ref List collection, int index, T value) => collection.Add(value); protected override List Create() => new List(); protected override ICollection Complete(ref List intermediateCollection) => intermediateCollection; } internal sealed class InterfaceEnumerableFormatter : CollectionFormatterBase, IEnumerable> { protected override void Add(ref ArrayBuffer collection, int index, T value) => collection.Add(value); protected override ArrayBuffer Create() => new ArrayBuffer(4); protected override IEnumerable Complete(ref ArrayBuffer intermediateCollection) => intermediateCollection.ToArray(); } // {Key:key, Elements:[Array]} (not compatible with JSON.NET) internal sealed class InterfaceGroupingFormatter : IJsonFormatter> { public void Serialize(ref JsonWriter writer, IGrouping value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } writer.WriteRaw(CollectionFormatterHelper.groupingName[0]); formatterResolver.GetFormatterWithVerify().Serialize(ref writer, value.Key, formatterResolver); writer.WriteRaw(CollectionFormatterHelper.groupingName[1]); formatterResolver.GetFormatterWithVerify>().Serialize(ref writer, value.AsEnumerable(), formatterResolver); writer.WriteEndObject(); } public IGrouping Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; TKey resultKey = default; IEnumerable resultValue = default; reader.ReadIsBeginObjectWithVerify(); var count = 0; while (!reader.ReadIsEndObjectWithSkipValueSeparator(ref count)) { var keyString = reader.ReadPropertyNameSegmentRaw(); CollectionFormatterHelper.groupingAutomata.TryGetValue(keyString, out var key); switch (key) { case 0: resultKey = formatterResolver.GetFormatterWithVerify().Deserialize(ref reader, formatterResolver); break; case 1: resultValue = formatterResolver.GetFormatterWithVerify>().Deserialize(ref reader, formatterResolver); break; default: reader.ReadNextBlock(); break; } } return new Grouping(resultKey, resultValue); } } internal sealed class InterfaceLookupFormatter : IJsonFormatter> { public void Serialize(ref JsonWriter writer, ILookup value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } formatterResolver.GetFormatterWithVerify>>().Serialize(ref writer, value.AsEnumerable(), formatterResolver); } public ILookup Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; // check if TElement is null if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify>(); var intermediateCollection = new Dictionary>(); reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) { var g = formatter.Deserialize(ref reader, formatterResolver); intermediateCollection.Add(g.Key, g); } return new Lookup(intermediateCollection); } } internal class Grouping : IGrouping { private readonly TKey _key; private readonly IEnumerable _elements; public Grouping(TKey key, IEnumerable elements) { _key = key; _elements = elements; } public TKey Key => _key; public IEnumerator GetEnumerator() => _elements.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } internal class Lookup : ILookup { private readonly Dictionary> _groupings; public Lookup(Dictionary> groupings) => _groupings = groupings; public IEnumerable this[TKey key] => _groupings[key]; public int Count => _groupings.Count; public bool Contains(TKey key) => _groupings.ContainsKey(key); public IEnumerator> GetEnumerator() => _groupings.Values.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } // NonGenerics internal sealed class NonGenericListFormatter : IJsonFormatter where T : class, IList, new() { public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } var formatter = formatterResolver.GetFormatterWithVerify(); writer.WriteBeginArray(); if (value.Count != 0) formatter.Serialize(ref writer, value[0], formatterResolver); for (var i = 1; i < value.Count; i++) { writer.WriteValueSeparator(); formatter.Serialize(ref writer, value[i], formatterResolver); } writer.WriteEndArray(); } public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var list = new T(); reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) list.Add(formatter.Deserialize(ref reader, formatterResolver)); return list; } } internal sealed class NonGenericInterfaceEnumerableFormatter : IJsonFormatter { public static readonly IJsonFormatter Default = new NonGenericInterfaceEnumerableFormatter(); public void Serialize(ref JsonWriter writer, IEnumerable value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } var formatter = formatterResolver.GetFormatterWithVerify(); writer.WriteBeginArray(); var i = 0; foreach (var item in value) { if (i != 0) writer.WriteValueSeparator(); formatter.Serialize(ref writer, item, formatterResolver); ++i; } writer.WriteEndArray(); } public IEnumerable Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var list = new List(); reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) list.Add(formatter.Deserialize(ref reader, formatterResolver)); return list; } } internal sealed class NonGenericInterfaceCollectionFormatter : IJsonFormatter { public static readonly IJsonFormatter Default = new NonGenericInterfaceCollectionFormatter(); public void Serialize(ref JsonWriter writer, ICollection value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } var formatter = formatterResolver.GetFormatterWithVerify(); writer.WriteBeginArray(); var e = value.GetEnumerator(); try { if (e.MoveNext()) { formatter.Serialize(ref writer, e.Current, formatterResolver); while (e.MoveNext()) { writer.WriteValueSeparator(); formatter.Serialize(ref writer, e.Current, formatterResolver); } } } finally { if (e is IDisposable d) d.Dispose(); } writer.WriteEndArray(); } public ICollection Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var list = new List(); reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) list.Add(formatter.Deserialize(ref reader, formatterResolver)); return list; } } internal sealed class NonGenericInterfaceListFormatter : IJsonFormatter { public static readonly IJsonFormatter Default = new NonGenericInterfaceListFormatter(); public void Serialize(ref JsonWriter writer, IList value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } var formatter = formatterResolver.GetFormatterWithVerify(); writer.WriteBeginArray(); if (value.Count != 0) formatter.Serialize(ref writer, value[0], formatterResolver); for (var i = 1; i < value.Count; i++) { writer.WriteValueSeparator(); formatter.Serialize(ref writer, value[i], formatterResolver); } writer.WriteEndArray(); } public IList Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var count = 0; var formatter = formatterResolver.GetFormatterWithVerify(); var list = new List(); reader.ReadIsBeginArrayWithVerify(); while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count)) list.Add(formatter.Deserialize(ref reader, formatterResolver)); return list; } } internal sealed class ObservableCollectionFormatter : CollectionFormatterBase> { protected override void Add(ref ObservableCollection collection, int index, T value) => collection.Add(value); protected override ObservableCollection Create() => new ObservableCollection(); } internal sealed class ReadOnlyObservableCollectionFormatter : CollectionFormatterBase, ReadOnlyObservableCollection> { protected override void Add(ref ObservableCollection collection, int index, T value) => collection.Add(value); protected override ObservableCollection Create() => new ObservableCollection(); protected override ReadOnlyObservableCollection Complete(ref ObservableCollection intermediateCollection) => new ReadOnlyObservableCollection(intermediateCollection); } internal sealed class InterfaceReadOnlyListFormatter : CollectionFormatterBase, IReadOnlyList> { protected override void Add(ref ArrayBuffer collection, int index, T value) => collection.Add(value); protected override ArrayBuffer Create() => new ArrayBuffer(4); protected override IReadOnlyList Complete(ref ArrayBuffer intermediateCollection) => intermediateCollection.ToArray(); } internal sealed class InterfaceReadOnlyCollectionFormatter : CollectionFormatterBase, IReadOnlyCollection> { protected override void Add(ref ArrayBuffer collection, int index, T value) => collection.Add(value); protected override ArrayBuffer Create() => new ArrayBuffer(4); protected override IReadOnlyCollection Complete(ref ArrayBuffer intermediateCollection) => intermediateCollection.ToArray(); } internal sealed class InterfaceSetFormatter : CollectionFormatterBase, ISet> { protected override void Add(ref HashSet collection, int index, T value) => collection.Add(value); protected override ISet Complete(ref HashSet intermediateCollection) => intermediateCollection; protected override HashSet Create() => new HashSet(); } internal sealed class ConcurrentBagFormatter : CollectionFormatterBase> { protected override void Add(ref ConcurrentBag collection, int index, T value) => collection.Add(value); protected override ConcurrentBag Create() => new ConcurrentBag(); } internal sealed class ConcurrentQueueFormatter : CollectionFormatterBase> { protected override void Add(ref ConcurrentQueue collection, int index, T value) => collection.Enqueue(value); protected override ConcurrentQueue Create() => new ConcurrentQueue(); } internal sealed class ConcurrentStackFormatter : CollectionFormatterBase, ConcurrentStack> { protected override void Add(ref ArrayBuffer collection, int index, T value) => collection.Add(value); protected override ArrayBuffer Create() => new ArrayBuffer(4); protected override ConcurrentStack Complete(ref ArrayBuffer intermediateCollection) { var bufArray = intermediateCollection.Buffer; var stack = new ConcurrentStack(); for (var i = intermediateCollection.Size - 1; i >= 0; i--) stack.Push(bufArray[i]); return stack; } } internal static class CollectionFormatterHelper { internal static readonly byte[][] groupingName; internal static readonly AutomataDictionary groupingAutomata; static CollectionFormatterHelper() { groupingName = new[] { JsonWriter.GetEncodedPropertyNameWithBeginObject("Key"), JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator("Elements"), }; groupingAutomata = new AutomataDictionary { {JsonWriter.GetEncodedPropertyNameWithoutQuotation("Key"), 0 }, {JsonWriter.GetEncodedPropertyNameWithoutQuotation("Elements"), 1 }, }; } } }