/* 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.Linq; using System.Reflection; using System.Reflection.Emit; using System.Threading.Tasks; using OpenSearch.Net.Utf8Json.Internal; using OpenSearch.Net.Utf8Json.Internal.Emit; namespace OpenSearch.Net.Utf8Json { // NonGeneric API internal static partial class JsonSerializer { public static class NonGeneric { private static readonly Func CreateCompiledMethods; private static readonly ThreadsafeTypeKeyHashTable serializes = new ThreadsafeTypeKeyHashTable(capacity: 64); private delegate void SerializeJsonWriter(ref JsonWriter writer, object value, IJsonFormatterResolver resolver); private delegate object DeserializeJsonReader(ref JsonReader reader, IJsonFormatterResolver resolver); static NonGeneric() => CreateCompiledMethods = t => new CompiledMethods(t); private static CompiledMethods GetOrAdd(Type type) => serializes.GetOrAdd(type, CreateCompiledMethods); /// /// Serialize to binary with default resolver. /// public static byte[] Serialize(object value) => value == null ? Serialize(value) : Serialize(value.GetType(), value, _defaultResolver); /// /// Serialize to binary with default resolver. /// public static byte[] Serialize(Type type, object value) => Serialize(type, value, _defaultResolver); /// /// Serialize to binary with specified resolver. /// public static byte[] Serialize(object value, IJsonFormatterResolver resolver) => value == null ? Serialize(value, resolver) : Serialize(value.GetType(), value, resolver); /// /// Serialize to binary with specified resolver. /// public static byte[] Serialize(Type type, object value, IJsonFormatterResolver resolver) => GetOrAdd(type).serialize1.Invoke(value, resolver); /// /// Serialize to stream. /// public static void Serialize(Stream stream, object value) { if (value == null) { Serialize(stream, value); return; } Serialize(value.GetType(), stream, value, _defaultResolver); } /// /// Serialize to stream. /// public static void Serialize(Type type, Stream stream, object value) => Serialize(type, stream, value, _defaultResolver); /// /// Serialize to stream with specified resolver. /// public static void Serialize(Stream stream, object value, IJsonFormatterResolver resolver) { if (value == null) { Serialize(stream, value, resolver); return; } Serialize(value.GetType(), stream, value, resolver); } /// /// Serialize to stream with specified resolver. /// public static void Serialize(Type type, Stream stream, object value, IJsonFormatterResolver resolver) => GetOrAdd(type).serialize2.Invoke(stream, value, resolver); /// /// Serialize to stream. /// public static Task SerializeAsync(Stream stream, object value) => value == null ? SerializeAsync(stream, value) : SerializeAsync(value.GetType(), stream, value, _defaultResolver); /// /// Serialize to stream. /// public static Task SerializeAsync(Type type, Stream stream, object value) => SerializeAsync(type, stream, value, _defaultResolver); /// /// Serialize to stream with specified resolver. /// public static Task SerializeAsync(Stream stream, object value, IJsonFormatterResolver resolver) { if (value == null) { return SerializeAsync(stream, value, resolver); } return SerializeAsync(value.GetType(), stream, value, resolver); } /// /// Serialize to stream with specified resolver. /// public static Task SerializeAsync(Type type, Stream stream, object value, IJsonFormatterResolver resolver) => GetOrAdd(type).serializeAsync.Invoke(stream, value, resolver); public static void Serialize(ref JsonWriter writer, object value, IJsonFormatterResolver resolver) { if (value == null) { writer.WriteNull(); return; } Serialize(value.GetType(), ref writer, value, resolver); } public static void Serialize(Type type, ref JsonWriter writer, object value) => Serialize(type, ref writer, value, _defaultResolver); public static void Serialize(Type type, ref JsonWriter writer, object value, IJsonFormatterResolver resolver) => GetOrAdd(type).serialize3.Invoke(ref writer, value, resolver); /// /// 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(object value) { if (value == null) return SerializeUnsafe(value); return SerializeUnsafe(value.GetType(), value); } /// /// 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(Type type, object value) => SerializeUnsafe(type, value, _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(object value, IJsonFormatterResolver resolver) { if (value == null) return SerializeUnsafe(value); return SerializeUnsafe(value.GetType(), value, resolver); } /// /// 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(Type type, object value, IJsonFormatterResolver resolver) => GetOrAdd(type).serializeUnsafe.Invoke(value, resolver); /// /// Serialize to JsonString. /// public static string ToJsonString(object value) { if (value == null) return "null"; return ToJsonString(value.GetType(), value); } /// /// Serialize to JsonString. /// public static string ToJsonString(Type type, object value) => ToJsonString(type, value, _defaultResolver); /// /// Serialize to JsonString with specified resolver. /// public static string ToJsonString(object value, IJsonFormatterResolver resolver) { if (value == null) return "null"; return ToJsonString(value.GetType(), value, resolver); } /// /// Serialize to JsonString with specified resolver. /// public static string ToJsonString(Type type, object value, IJsonFormatterResolver resolver) => GetOrAdd(type).toJsonString.Invoke(value, resolver); public static object Deserialize(Type type, string json) => Deserialize(type, json, _defaultResolver); public static object Deserialize(Type type, string json, IJsonFormatterResolver resolver) => GetOrAdd(type).deserialize1.Invoke(json, resolver); public static object Deserialize(Type type, byte[] bytes) => Deserialize(type, bytes, _defaultResolver); public static object Deserialize(Type type, byte[] bytes, IJsonFormatterResolver resolver) => Deserialize(type, bytes, 0, _defaultResolver); public static object Deserialize(Type type, byte[] bytes, int offset) => Deserialize(type, bytes, offset, _defaultResolver); public static object Deserialize(Type type, byte[] bytes, int offset, IJsonFormatterResolver resolver) => GetOrAdd(type).deserialize2.Invoke(bytes, offset, resolver); public static object Deserialize(Type type, Stream stream) => Deserialize(type, stream, _defaultResolver); public static object Deserialize(Type type, Stream stream, IJsonFormatterResolver resolver) => GetOrAdd(type).deserialize3.Invoke(stream, resolver); public static object Deserialize(Type type, ref JsonReader reader) => Deserialize(type, ref reader, _defaultResolver); public static object Deserialize(Type type, ref JsonReader reader, IJsonFormatterResolver resolver) => GetOrAdd(type).deserialize4.Invoke(ref reader, resolver); public static Task DeserializeAsync(Type type, Stream stream) => DeserializeAsync(type, stream, _defaultResolver); public static Task DeserializeAsync(Type type, Stream stream, IJsonFormatterResolver resolver) => GetOrAdd(type).deserializeAsync.Invoke(stream, resolver); private class CompiledMethods { public readonly Func serialize1; public readonly Action serialize2; public readonly SerializeJsonWriter serialize3; public readonly Func> serializeUnsafe; public readonly Func toJsonString; public readonly Func deserialize1; public readonly Func deserialize2; public readonly Func deserialize3; public readonly DeserializeJsonReader deserialize4; public readonly Func serializeAsync; public readonly Func> deserializeAsync; public CompiledMethods(Type type) { { var dm = new DynamicMethod(nameof(serialize1), typeof(byte[]), new[] { typeof(object), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // obj il.EmitUnboxOrCast(type); il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(Serialize), new[] { null, typeof(IJsonFormatterResolver) })); il.Emit(OpCodes.Ret); serialize1 = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(serialize2), null, new[] { typeof(Stream), typeof(object), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // stream il.EmitLdarg(1); il.EmitUnboxOrCast(type); il.EmitLdarg(2); il.EmitCall(GetMethod(type, nameof(Serialize), new[] { typeof(Stream), null, typeof(IJsonFormatterResolver) })); il.Emit(OpCodes.Ret); serialize2 = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(serialize3), null, new[] { typeof(JsonWriter).MakeByRefType(), typeof(object), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // ref writer il.EmitLdarg(1); il.EmitUnboxOrCast(type); il.EmitLdarg(2); il.EmitCall(GetMethod(type, nameof(Serialize), new[] { typeof(JsonWriter).MakeByRefType(), null, typeof(IJsonFormatterResolver) })); il.Emit(OpCodes.Ret); serialize3 = CreateDelegate(dm); } { var dm = new DynamicMethod(nameof(serializeUnsafe), typeof(ArraySegment), new[] { typeof(object), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // obj il.EmitUnboxOrCast(type); il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(SerializeUnsafe), new[] { null, typeof(IJsonFormatterResolver) })); il.Emit(OpCodes.Ret); serializeUnsafe = CreateDelegate>>(dm); } { var dm = new DynamicMethod(nameof(toJsonString), typeof(string), new[] { typeof(object), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // obj il.EmitUnboxOrCast(type); il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(ToJsonString), new[] { null, typeof(IJsonFormatterResolver) })); il.Emit(OpCodes.Ret); toJsonString = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(Deserialize), typeof(object), new[] { typeof(string), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(Deserialize), new[] { typeof(string), typeof(IJsonFormatterResolver) })); il.EmitBoxOrDoNothing(type); il.Emit(OpCodes.Ret); deserialize1 = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(Deserialize), typeof(object), new[] { typeof(byte[]), typeof(int), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(GetMethod(type, nameof(Deserialize), new[] { typeof(byte[]), typeof(int), typeof(IJsonFormatterResolver) })); il.EmitBoxOrDoNothing(type); il.Emit(OpCodes.Ret); deserialize2 = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(Deserialize), typeof(object), new[] { typeof(Stream), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(Deserialize), new[] { typeof(Stream), typeof(IJsonFormatterResolver) })); il.EmitBoxOrDoNothing(type); il.Emit(OpCodes.Ret); deserialize3 = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(Deserialize), typeof(object), new[] { typeof(JsonReader).MakeByRefType(), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // ref reader il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(Deserialize), new[] { typeof(JsonReader).MakeByRefType(), typeof(IJsonFormatterResolver) })); il.EmitBoxOrDoNothing(type); il.Emit(OpCodes.Ret); deserialize4 = CreateDelegate(dm); } { var dm = new DynamicMethod(nameof(SerializeAsync), typeof(Task), new[] { typeof(Stream), typeof(object), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); // stream il.EmitLdarg(1); il.EmitUnboxOrCast(type); il.EmitLdarg(2); il.EmitCall(GetMethod(type, nameof(SerializeAsync), new[] { typeof(Stream), null, typeof(IJsonFormatterResolver) })); il.Emit(OpCodes.Ret); serializeAsync = CreateDelegate>(dm); } { var dm = new DynamicMethod(nameof(DeserializeAsync), typeof(Task), new[] { typeof(Stream), typeof(IJsonFormatterResolver) }, type.Module, true); var il = dm.GetILGenerator(); il.EmitLdarg(0); il.EmitLdarg(1); il.EmitCall(GetMethod(type, nameof(DeserializeAsync), new[] { typeof(Stream), typeof(IJsonFormatterResolver) })); il.EmitCall(typeof(CompiledMethods).GetMethod(nameof(TaskCast), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(type)); il.Emit(OpCodes.Ret); deserializeAsync = CreateDelegate>>(dm); } } private static async Task TaskCast(Task task) { var t = await task.ConfigureAwait(false); return t; } private static T CreateDelegate(DynamicMethod dm) => (T)(object)dm.CreateDelegate(typeof(T)); private static MethodInfo GetMethod(Type type, string name, Type[] arguments) => typeof(JsonSerializer).GetMethods(BindingFlags.Static | BindingFlags.Public) .Where(x => x.Name == name) .Single(x => { var ps = x.GetParameters(); if (ps.Length != arguments.Length) return false; for (var i = 0; i < ps.Length; i++) { // null for . if (arguments[i] == null && ps[i].ParameterType.IsGenericParameter) continue; if (ps[i].ParameterType != arguments[i]) return false; } return true; }) .MakeGenericMethod(type); } } } }