/* 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.Runtime.CompilerServices; using OpenSearch.Net.Utf8Json.Internal.DoubleConversion; namespace OpenSearch.Net.Utf8Json.Internal { /// /// zero-allocate itoa, dtoa, atoi, atod converters. /// internal static class NumberConverter { /// /// e or E /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsENotation(byte c) => c == (byte)'E' || c == (byte)'e'; /// /// 0 ~ 9 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNumber(byte c) => (byte)'0' <= c && c <= (byte)'9'; /// /// Is 0 ~ 9, '.', '+', '-'? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNumberRepresentation(byte c) { switch (c) { case 43: // + case 45: // - case 46: // . case 48: // 0 case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 9 return true; case 44: case 47: default: return false; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte ReadSByte(byte[] bytes, int offset, out int readCount) => checked((sbyte)ReadInt64(bytes, offset, out readCount)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ReadInt16(byte[] bytes, int offset, out int readCount) => checked((short)ReadInt64(bytes, offset, out readCount)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReadInt32(byte[] bytes, int offset, out int readCount) => checked((int)ReadInt64(bytes, offset, out readCount)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ReadInt64(byte[] bytes, int offset, out int readCount) { var value = 0L; var sign = 1; if (bytes[offset] == '-') sign = -1; for (var i = ((sign == -1) ? offset + 1 : offset); i < bytes.Length; i++) { if (!IsNumber(bytes[i])) { readCount = i - offset; goto END; } // long.MinValue causes overflow so use unchecked. value = unchecked(value * 10 + (bytes[i] - '0')); } readCount = bytes.Length - offset; END: return unchecked(value * sign); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ReadByte(byte[] bytes, int offset, out int readCount) => checked((byte)ReadUInt64(bytes, offset, out readCount)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ReadUInt16(byte[] bytes, int offset, out int readCount) => checked((ushort)ReadUInt64(bytes, offset, out readCount)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ReadUInt32(byte[] bytes, int offset, out int readCount) => checked((uint)ReadUInt64(bytes, offset, out readCount)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ReadUInt64(byte[] bytes, int offset, out int readCount) { var value = 0UL; for (var i = offset; i < bytes.Length; i++) { if (!IsNumber(bytes[i])) { readCount = i - offset; goto END; } value = checked(value * 10 + (ulong)(bytes[i] - '0')); } readCount = bytes.Length - offset; END: return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ReadSingle(byte[] bytes, int offset, out int readCount) => StringToDoubleConverter.ToSingle(bytes, offset, out readCount); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ReadDouble(byte[] bytes, int offset, out int readCount) => StringToDoubleConverter.ToDouble(bytes, offset, out readCount); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteByte(ref byte[] buffer, int offset, byte value) => WriteUInt64(ref buffer, offset, (ulong)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteUInt16(ref byte[] buffer, int offset, ushort value) => WriteUInt64(ref buffer, offset, (ulong)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteUInt32(ref byte[] buffer, int offset, uint value) => WriteUInt64(ref buffer, offset, (ulong)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteUInt64(ref byte[] buffer, int offset, ulong value) { var startOffset = offset; ulong num1 = value, num2, num3, num4, num5, div; if (num1 < 10000) { if (num1 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 1); goto L1; } if (num1 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 2); goto L2; } if (num1 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 3); goto L3; } BinaryUtil.EnsureCapacity(ref buffer, offset, 4); goto L4; } else { num2 = num1 / 10000; num1 -= num2 * 10000; if (num2 < 10000) { if (num2 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 5); goto L5; } if (num2 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 6); goto L6; } if (num2 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 7); goto L7; } BinaryUtil.EnsureCapacity(ref buffer, offset, 8); goto L8; } else { num3 = num2 / 10000; num2 -= num3 * 10000; if (num3 < 10000) { if (num3 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 9); goto L9; } if (num3 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 10); goto L10; } if (num3 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 11); goto L11; } BinaryUtil.EnsureCapacity(ref buffer, offset, 12); goto L12; } else { num4 = num3 / 10000; num3 -= num4 * 10000; if (num4 < 10000) { if (num4 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 13); goto L13; } if (num4 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 14); goto L14; } if (num4 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 15); goto L15; } BinaryUtil.EnsureCapacity(ref buffer, offset, 16); goto L16; } else { num5 = num4 / 10000; num4 -= num5 * 10000; if (num5 < 10000) { if (num5 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 17); goto L17; } if (num5 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 18); goto L18; } if (num5 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 19); goto L19; } BinaryUtil.EnsureCapacity(ref buffer, offset, 20); goto L20; } L20: buffer[offset++] = (byte)('0' + (div = (num5 * 8389UL) >> 23)); num5 -= div * 1000; L19: buffer[offset++] = (byte)('0' + (div = (num5 * 5243UL) >> 19)); num5 -= div * 100; L18: buffer[offset++] = (byte)('0' + (div = (num5 * 6554UL) >> 16)); num5 -= div * 10; L17: buffer[offset++] = (byte)('0' + (num5)); } L16: buffer[offset++] = (byte)('0' + (div = (num4 * 8389UL) >> 23)); num4 -= div * 1000; L15: buffer[offset++] = (byte)('0' + (div = (num4 * 5243UL) >> 19)); num4 -= div * 100; L14: buffer[offset++] = (byte)('0' + (div = (num4 * 6554UL) >> 16)); num4 -= div * 10; L13: buffer[offset++] = (byte)('0' + (num4)); } L12: buffer[offset++] = (byte)('0' + (div = (num3 * 8389UL) >> 23)); num3 -= div * 1000; L11: buffer[offset++] = (byte)('0' + (div = (num3 * 5243UL) >> 19)); num3 -= div * 100; L10: buffer[offset++] = (byte)('0' + (div = (num3 * 6554UL) >> 16)); num3 -= div * 10; L9: buffer[offset++] = (byte)('0' + (num3)); } L8: buffer[offset++] = (byte)('0' + (div = (num2 * 8389UL) >> 23)); num2 -= div * 1000; L7: buffer[offset++] = (byte)('0' + (div = (num2 * 5243UL) >> 19)); num2 -= div * 100; L6: buffer[offset++] = (byte)('0' + (div = (num2 * 6554UL) >> 16)); num2 -= div * 10; L5: buffer[offset++] = (byte)('0' + (num2)); } L4: buffer[offset++] = (byte)('0' + (div = (num1 * 8389UL) >> 23)); num1 -= div * 1000; L3: buffer[offset++] = (byte)('0' + (div = (num1 * 5243UL) >> 19)); num1 -= div * 100; L2: buffer[offset++] = (byte)('0' + (div = (num1 * 6554UL) >> 16)); num1 -= div * 10; L1: buffer[offset++] = (byte)('0' + (num1)); return offset - startOffset; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteSByte(ref byte[] buffer, int offset, sbyte value) => WriteInt64(ref buffer, offset, (long)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteInt16(ref byte[] buffer, int offset, short value) => WriteInt64(ref buffer, offset, (long)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteInt32(ref byte[] buffer, int offset, int value) => WriteInt64(ref buffer, offset, (long)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteInt64(ref byte[] buffer, int offset, long value) { var startOffset = offset; long num1 = value, num2, num3, num4, num5, div; if (value < 0) { if (value == long.MinValue) // -9223372036854775808 { BinaryUtil.EnsureCapacity(ref buffer, offset, 20); buffer[offset++] = (byte)'-'; buffer[offset++] = (byte)'9'; buffer[offset++] = (byte)'2'; buffer[offset++] = (byte)'2'; buffer[offset++] = (byte)'3'; buffer[offset++] = (byte)'3'; buffer[offset++] = (byte)'7'; buffer[offset++] = (byte)'2'; buffer[offset++] = (byte)'0'; buffer[offset++] = (byte)'3'; buffer[offset++] = (byte)'6'; buffer[offset++] = (byte)'8'; buffer[offset++] = (byte)'5'; buffer[offset++] = (byte)'4'; buffer[offset++] = (byte)'7'; buffer[offset++] = (byte)'7'; buffer[offset++] = (byte)'5'; buffer[offset++] = (byte)'8'; buffer[offset++] = (byte)'0'; buffer[offset++] = (byte)'8'; return offset - startOffset; } BinaryUtil.EnsureCapacity(ref buffer, offset, 1); buffer[offset++] = (byte)'-'; num1 = unchecked(-value); } // WriteUInt64(inlined) if (num1 < 10000) { if (num1 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 1); goto L1; } if (num1 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 2); goto L2; } if (num1 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 3); goto L3; } BinaryUtil.EnsureCapacity(ref buffer, offset, 4); goto L4; } else { num2 = num1 / 10000; num1 -= num2 * 10000; if (num2 < 10000) { if (num2 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 5); goto L5; } if (num2 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 6); goto L6; } if (num2 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 7); goto L7; } BinaryUtil.EnsureCapacity(ref buffer, offset, 8); goto L8; } else { num3 = num2 / 10000; num2 -= num3 * 10000; if (num3 < 10000) { if (num3 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 9); goto L9; } if (num3 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 10); goto L10; } if (num3 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 11); goto L11; } BinaryUtil.EnsureCapacity(ref buffer, offset, 12); goto L12; } else { num4 = num3 / 10000; num3 -= num4 * 10000; if (num4 < 10000) { if (num4 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 13); goto L13; } if (num4 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 14); goto L14; } if (num4 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 15); goto L15; } BinaryUtil.EnsureCapacity(ref buffer, offset, 16); goto L16; } else { num5 = num4 / 10000; num4 -= num5 * 10000; if (num5 < 10000) { if (num5 < 10) { BinaryUtil.EnsureCapacity(ref buffer, offset, 17); goto L17; } if (num5 < 100) { BinaryUtil.EnsureCapacity(ref buffer, offset, 18); goto L18; } if (num5 < 1000) { BinaryUtil.EnsureCapacity(ref buffer, offset, 19); goto L19; } BinaryUtil.EnsureCapacity(ref buffer, offset, 20); goto L20; } L20: buffer[offset++] = (byte)('0' + (div = (num5 * 8389L) >> 23)); num5 -= div * 1000; L19: buffer[offset++] = (byte)('0' + (div = (num5 * 5243L) >> 19)); num5 -= div * 100; L18: buffer[offset++] = (byte)('0' + (div = (num5 * 6554L) >> 16)); num5 -= div * 10; L17: buffer[offset++] = (byte)('0' + (num5)); } L16: buffer[offset++] = (byte)('0' + (div = (num4 * 8389L) >> 23)); num4 -= div * 1000; L15: buffer[offset++] = (byte)('0' + (div = (num4 * 5243L) >> 19)); num4 -= div * 100; L14: buffer[offset++] = (byte)('0' + (div = (num4 * 6554L) >> 16)); num4 -= div * 10; L13: buffer[offset++] = (byte)('0' + (num4)); } L12: buffer[offset++] = (byte)('0' + (div = (num3 * 8389L) >> 23)); num3 -= div * 1000; L11: buffer[offset++] = (byte)('0' + (div = (num3 * 5243L) >> 19)); num3 -= div * 100; L10: buffer[offset++] = (byte)('0' + (div = (num3 * 6554L) >> 16)); num3 -= div * 10; L9: buffer[offset++] = (byte)('0' + (num3)); } L8: buffer[offset++] = (byte)('0' + (div = (num2 * 8389L) >> 23)); num2 -= div * 1000; L7: buffer[offset++] = (byte)('0' + (div = (num2 * 5243L) >> 19)); num2 -= div * 100; L6: buffer[offset++] = (byte)('0' + (div = (num2 * 6554L) >> 16)); num2 -= div * 10; L5: buffer[offset++] = (byte)('0' + (num2)); } L4: buffer[offset++] = (byte)('0' + (div = (num1 * 8389L) >> 23)); num1 -= div * 1000; L3: buffer[offset++] = (byte)('0' + (div = (num1 * 5243L) >> 19)); num1 -= div * 100; L2: buffer[offset++] = (byte)('0' + (div = (num1 * 6554L) >> 16)); num1 -= div * 10; L1: buffer[offset++] = (byte)('0' + (num1)); return offset - startOffset; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteSingle(ref byte[] bytes, int offset, float value) => DoubleToStringConverter.GetBytes(ref bytes, offset, value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteDouble(ref byte[] bytes, int offset, double value) => DoubleToStringConverter.GetBytes(ref bytes, offset, value); // boolean is not number:) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool ReadBoolean(byte[] bytes, int offset, out int readCount) { if (bytes[offset] == 't') { if (bytes[offset + 1] != 'r') goto ERROR_TRUE; if (bytes[offset + 2] != 'u') goto ERROR_TRUE; if (bytes[offset + 3] != 'e') goto ERROR_TRUE; readCount = 4; return true; } else if (bytes[offset] == 'f') { if (bytes[offset + 1] != 'a') goto ERROR_FALSE; if (bytes[offset + 2] != 'l') goto ERROR_FALSE; if (bytes[offset + 3] != 's') goto ERROR_FALSE; if (bytes[offset + 4] != 'e') goto ERROR_FALSE; readCount = 5; return false; } else { throw new InvalidOperationException("value is not boolean."); } ERROR_TRUE: throw new InvalidOperationException("value is not boolean(true)."); ERROR_FALSE: throw new InvalidOperationException("value is not boolean(false)."); } } }