/* 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);
}
}
}