/* 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.IO; using System.Threading.Tasks; using OpenSearch.Net.Utf8Json.Internal; using OpenSearch.Net.Utf8Json.Resolvers; namespace OpenSearch.Net.Utf8Json { /// /// High-Level API of Utf8Json. /// internal static partial class JsonSerializer { private static IJsonFormatterResolver _defaultResolver; /// /// FormatterResolver that used resolver less overloads. If does not set it, used StandardResolver.Default. /// public static IJsonFormatterResolver DefaultResolver => _defaultResolver ??= StandardResolver.Default; /// /// Set default resolver of Utf8Json APIs. /// /// public static void SetDefaultResolver(IJsonFormatterResolver resolver) => _defaultResolver = resolver; /// /// Serialize to binary with default resolver. /// public static byte[] Serialize(T obj) => Serialize(obj, _defaultResolver); /// /// Serialize to binary with specified resolver. /// public static byte[] Serialize(T value, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var buffer = MemoryPool.Rent(); try { var writer = new JsonWriter(buffer); var formatter = resolver.GetFormatterWithVerify(); formatter.Serialize(ref writer, value, resolver); return writer.ToUtf8ByteArray(); } finally { MemoryPool.Return(buffer); } } public static void Serialize(ref JsonWriter writer, T value) => Serialize(ref writer, value, _defaultResolver); public static void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var formatter = resolver.GetFormatterWithVerify(); formatter.Serialize(ref writer, value, resolver); } /// /// Serialize to stream. /// public static void Serialize(Stream stream, T value) => Serialize(stream, value, _defaultResolver); /// /// Serialize to stream with specified resolver. /// public static void Serialize(Stream stream, T value, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var buffer = SerializeUnsafe(value, resolver); stream.Write(buffer.Array, buffer.Offset, buffer.Count); } /// /// Serialize to stream(write async). /// public static Task SerializeAsync(Stream stream, T value) => SerializeAsync(stream, value, _defaultResolver); /// /// Serialize to stream(write async) with specified resolver. /// public static async Task SerializeAsync(Stream stream, T value, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var buf = MemoryPool.Rent(); try { var writer = new JsonWriter(buf); var formatter = resolver.GetFormatterWithVerify(); formatter.Serialize(ref writer, value, resolver); var buffer = writer.GetBuffer(); await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count).ConfigureAwait(false); } finally { MemoryPool.Return(buf); } } /// /// Serialize to binary. Get the raw memory pool byte[]. The result can not share across thread and can not hold, so use quickly. /// public static ArraySegment SerializeUnsafe(T obj) => SerializeUnsafe(obj, _defaultResolver); /// /// Serialize to binary with specified resolver. Get the raw memory pool byte[]. The result can not share across thread and can not hold, so use quickly. /// public static ArraySegment SerializeUnsafe(T value, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var buffer = MemoryPool.Rent(); try { var writer = new JsonWriter(buffer); var formatter = resolver.GetFormatterWithVerify(); formatter.Serialize(ref writer, value, resolver); var arraySegment = writer.GetBuffer(); return new ArraySegment(BinaryUtil.ToArray(ref arraySegment)); } finally { MemoryPool.Return(buffer); } } /// /// Serialize to JsonString. /// public static string ToJsonString(T value) => ToJsonString(value, _defaultResolver); /// /// Serialize to JsonString with specified resolver. /// public static string ToJsonString(T value, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var buffer = MemoryPool.Rent(); try { var writer = new JsonWriter(buffer); var formatter = resolver.GetFormatterWithVerify(); formatter.Serialize(ref writer, value, resolver); return writer.ToString(); } finally { MemoryPool.Return(buffer); } } public static T Deserialize(string json) => Deserialize(json, _defaultResolver); public static T Deserialize(string json, IJsonFormatterResolver resolver) => Deserialize(StringEncoding.UTF8.GetBytes(json), resolver); public static T Deserialize(byte[] bytes) => Deserialize(bytes, _defaultResolver); public static T Deserialize(byte[] bytes, IJsonFormatterResolver resolver) => Deserialize(bytes, 0, resolver); public static T Deserialize(byte[] bytes, int offset) => Deserialize(bytes, offset, _defaultResolver); public static T Deserialize(byte[] bytes, int offset, IJsonFormatterResolver resolver) { if (bytes == null || bytes.Length == 0) return default; if (resolver == null) resolver = DefaultResolver; var reader = new JsonReader(bytes, offset); var formatter = resolver.GetFormatterWithVerify(); return formatter.Deserialize(ref reader, resolver); } public static T Deserialize(ref JsonReader reader) => Deserialize(ref reader, _defaultResolver); public static T Deserialize(ref JsonReader reader, IJsonFormatterResolver resolver) { if (resolver == null) resolver = DefaultResolver; var formatter = resolver.GetFormatterWithVerify(); return formatter.Deserialize(ref reader, resolver); } public static T Deserialize(Stream stream) => Deserialize(stream, _defaultResolver); public static T Deserialize(Stream stream, IJsonFormatterResolver resolver) { if (stream == null || stream.CanSeek && stream.Length == 0) return default; if (resolver == null) resolver = DefaultResolver; if (stream is MemoryStream ms) { if (ms.TryGetBuffer(out var buf2)) { // when token is number, can not use from pool(can not find end line). var token = new JsonReader(buf2.Array, buf2.Offset).GetCurrentJsonToken(); if (token == JsonToken.Number) { var buf3 = new byte[buf2.Count]; Buffer.BlockCopy(buf2.Array, buf2.Offset, buf3, 0, buf3.Length); return Deserialize(buf3, 0, resolver); } return Deserialize(buf2.Array, buf2.Offset, resolver); } } var buf = MemoryPool.Rent(); var poolBuf = buf; try { var length = FillFromStream(stream, ref buf); if (length == 0) return default; // when token is number, can not use from pool(can not find end line). var token = new JsonReader(buf).GetCurrentJsonToken(); if (token == JsonToken.Number) { buf = BinaryUtil.FastCloneWithResize(buf, length); } return Deserialize(buf, resolver); } finally { MemoryPool.Return(poolBuf); } } public static Task DeserializeAsync(Stream stream) => DeserializeAsync(stream, _defaultResolver); public static async Task DeserializeAsync(Stream stream, IJsonFormatterResolver resolver) { if (stream == null || stream.CanSeek && stream.Length == 0) return default; if (resolver == null) resolver = DefaultResolver; if (stream is MemoryStream ms) { if (ms.TryGetBuffer(out var buf2)) { // when token is number, can not use from pool(can not find end line). var token = new JsonReader(buf2.Array, buf2.Offset).GetCurrentJsonToken(); if (token == JsonToken.Number) { var buf3 = new byte[buf2.Count]; Buffer.BlockCopy(buf2.Array, buf2.Offset, buf3, 0, buf3.Length); return Deserialize(buf3, 0, resolver); } return Deserialize(buf2.Array, buf2.Offset, resolver); } } var buffer = MemoryPool.Rent(); var buf = buffer; try { var length = 0; int read; while ((read = await stream.ReadAsync(buf, length, buf.Length - length).ConfigureAwait(false)) > 0) { length += read; if (length == buf.Length) BinaryUtil.FastResize(ref buf, length * 2); } if (length == 0) return default; // when token is number, can not use from pool(can not find end line). var token = new JsonReader(buf).GetCurrentJsonToken(); if (token == JsonToken.Number) { buf = BinaryUtil.FastCloneWithResize(buf, length); } return Deserialize(buf, resolver); } finally { MemoryPool.Return(buffer); } } private static int FillFromStream(Stream input, ref byte[] buffer) { var length = 0; int read; while ((read = input.Read(buffer, length, buffer.Length - length)) > 0) { length += read; if (length == buffer.Length) BinaryUtil.FastResize(ref buffer, length * 2); } return length; } internal static class MemoryPool { public static byte[] Rent(int minLength = 65535) => System.Buffers.ArrayPool.Shared.Rent(minLength); public static void Return(byte[] bytes) => System.Buffers.ArrayPool.Shared.Return(bytes); } } }