All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.bytes.internal.UnsafeText Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016-2022 chronicle.software
 *
 *     https://chronicle.software
 *
 * Licensed 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.
 */
package net.openhft.chronicle.bytes.internal;

import net.openhft.chronicle.core.*;

import static net.openhft.chronicle.core.UnsafeMemory.MEMORY;
import static net.openhft.chronicle.core.UnsafeMemory.UNSAFE;

/**
 * These are fast, unsafe ways to render text.
 * NOTE: The caller has to ensure there is always plenty of memory to perform this operation.
 */
public final class UnsafeText {

    public static final long MASK32 = 0xFFFF_FFFFL;

    // Suppresses default constructor, ensuring non-instantiability.
    private UnsafeText() {
    }

    private static final long MAX_VALUE_DIVIDE_5 = Long.MAX_VALUE / 5;
    private static final String MIN_VALUE_STR = "" + Long.MIN_VALUE;
    private static final long ARRAY_BYTE_BASE_OFFSET = Jvm.arrayByteBaseOffset();

    public static long appendFixed(long address, long num) {
        if (num >= 0) {
            // nothing
        } else if (num > Long.MIN_VALUE) {
            MEMORY.writeByte(address++, (byte) '-');
            num = -num;
        } else {
            return appendText(address, MIN_VALUE_STR);
        }

        long start = address;
        do {
            long div = num / 10;
            long mod = num % 10;
            MEMORY.writeByte(address++, (byte) ('0' + mod));
            num = div;
        } while (num > 0);
        // reverse the order
        reverseTheOrder(address, start);
        return address;
    }

    private static void reverseTheOrder(long address, long start) {
        int end = (int) (address - start) - 1;
        for (int i = 0; i < end; i++, end--) {
            long a1 = start + i;
            long a2 = start + end;
            byte b1 = MEMORY.readByte(a1);
            byte b2 = MEMORY.readByte(a2);
            MEMORY.writeByte(a2, b1);
            MEMORY.writeByte(a1, b2);
        }
    }

    public static long appendFixed(long address, double num, int digits) {
        long tens = Maths.tens(digits);
        double mag = num * tens;
        if (Math.abs(mag) < 1L << 53) {
            long num2 = Math.round(mag);
            return appendBase10d(address, num2, digits);
        } else {
            return appendDouble(address, num);
        }
    }

    public static long appendBase10d(long address, long num, int decimal) {
        if (num >= 0) {
            // nothing
        } else if (num > Long.MIN_VALUE) {
            MEMORY.writeByte(address++, (byte) '-');
            num = -num;
        } else {
            throw new AssertionError();
        }

        long start = address;
        do {
            long div = num / 10;
            long mod = num % 10;
            MEMORY.writeByte(address++, (byte) ('0' + mod));
            if (--decimal == 0)
                MEMORY.writeByte(address++, (byte) '.');
            num = div;
        } while (num > 0 || decimal >= 0);
        // reverse the order
        reverseTheOrder(address, start);
        return address;
    }

    /**
     * Internal method for low level appending a String. The caller must ensure there is at least 32 bytes available.
     *
     * @param address to start writing
     * @param d       double value
     * @return endOfAddress
     */
    //      throws BufferOverflowException, IllegalArgumentException
    public static long appendDouble(long address, double d) {
        double abs = Math.abs(d);
        // outside range so that !Double.isFinite(d) implicitly added.
        if (6e-8 > abs || abs > 1e31) {
            return appendDoubleString(address, d);
        } else {
            return appendDouble0(address, d);
        }
    }

    static long appendDouble0(long address, double d) {
        long val = Double.doubleToRawLongBits(d);
        int sign = (int) (val >>> 63);
        int exp = (int) ((val >>> 52) & 2047);
        long mantissa = val & ((1L << 52) - 1);
        if (sign != 0) {
            MEMORY.writeByte(address++, (byte) '-');
        }
        if (exp == 0 && mantissa == 0) {
            MEMORY.writeByte(address, (byte) '0');
            MEMORY.writeShort(address + 1, (short) ('.' + ('0' << 8)));
            address += 3;
            return address;

        } else if (exp == 2047) {
            return appendText(address,
                    mantissa == 0 ? "Infinity" : "NaN");

        } else if (exp > 0) {
            mantissa += 1L << 52;
        }
        final int shift = (1023 + 52) - exp;

        if (shift > 0) {
            // integer and faction
            if (shift < 53) {
                return appendIntegerAndFraction(address, d, sign, mantissa, shift);

            } else {
                return appendFraction(address, d, sign, mantissa, shift);
            }
        }
        // large number
        return appendLargeNumber(address, mantissa, shift);
    }

    static final ThreadLocal TL_SB = ThreadLocal.withInitial(StringBuilder::new);
    static long appendDoubleString(long address, double d) {
        StringBuilder sb = TL_SB.get();
        sb.setLength(0);
        sb.append(d);
        for (int i = 0; i < sb.length(); i++)
            MEMORY.writeByte(address++, (byte) sb.charAt(i));
        return address;
    }

    private static long appendLargeNumber(long address, long mantissa, int shift) {
        mantissa <<= 10;
        int precision = -10 - shift;
        int digits = 0;
        while ((precision > 53 || mantissa > Long.MAX_VALUE >> precision) && precision > 0) {
            digits++;
            precision--;
            long mod = mantissa % 5;
            mantissa /= 5;
            int modDiv = 1;
            while (mantissa < MAX_VALUE_DIVIDE_5 && precision > 1) {
                precision -= 1;
                mantissa <<= 1;
                modDiv <<= 1;
            }
            mantissa += modDiv * mod / 5;
        }
        long val2 = precision > 0 ? mantissa << precision : mantissa >>> -precision;

        address = appendFixed(address, val2);
        for (int i = 0; i < digits; i++)
            MEMORY.writeByte(address++, (byte) '0');
        return address;
    }

    private static long appendFraction(long address, double d, int sign, long mantissa, int shift) {
        long value = 0;
        int digits = -1;
        mantissa = mantissa << 9;
        shift += 9;
        do {
            if (shift < 63) {
                value = value * 10 + (mantissa >> shift);
                mantissa &= (1L << shift) - 1;
            }
            if (mantissa >= Long.MAX_VALUE / 5) {
                mantissa >>>= 3;
                shift -= 3;
            }
            // times 10
            mantissa *= 5;
            shift--;
            digits++;
        } while (value < 1e17);
        long value3 = value;
        // back track
        while (true) {
            final long value2 = (value + 5) / 10;
            final double parsedValue = Maths.asDouble(value2, 0, sign != 0, digits - 1);
            if (parsedValue != d)
                break;
            digits--;
            value3 = value2;
            value = value / 10;
        }

        MEMORY.writeShort(address, (short) ('0' + ('.' << 8)));
        address += 2;

        long addressOfLastNonZero = address + digits;

        do {
            long num = value3 % 10;
            value3 /= 10;
            final char c = (char) ('0' + num);
            digits--;
            MEMORY.writeByte(address + digits, (byte) c);
        } while (value3 > 0);
        while (digits > 0) {
            digits--;
            MEMORY.writeByte(address + digits, (byte) '0');
        }

        return addressOfLastNonZero;
    }

    private static long appendIntegerAndFraction(long address, double d, int sign, long mantissa, int shift) {
        long intValue = mantissa >> shift;
        address = appendFixed(address, intValue);
        mantissa -= intValue << shift;
        if (mantissa > 0) {
            MEMORY.writeByte(address++, (byte) '.');
            mantissa <<= 1;
            mantissa++;
            int precision = shift + 1;
            long error = 1;

            long value = intValue;
            int decimalPlaces = 0;
            while (mantissa > error) {
                // times 5*2 = 10
                mantissa *= 5;
                error *= 5;
                precision--;
                long num = (mantissa >> precision);
                value = value * 10 + num;
                MEMORY.writeByte(address++, (byte) ('0' + num));
                mantissa -= num << precision;

                final double parsedValue = Maths.asDouble(value, 0, sign != 0, ++decimalPlaces);
                if (parsedValue == d)
                    break;
            }
        } else {
            MEMORY.writeShort(address, (short) ('.' + ('0' << 8)));
            address += 2;
        }
        return address;
    }

    private static long appendText(long address, String s) {
        for (int i = 0; i < s.length(); i++) {
            MEMORY.writeByte(address++, (byte) s.charAt(i));
        }
        return address;
    }

    public static long append8bit(long address, byte[] bytes) {
        final int len = bytes.length;
        int i;
        for (i = 0; i < len - 7; i += 8)
            MEMORY.writeLong(address + i, MEMORY.readLong(bytes, ARRAY_BYTE_BASE_OFFSET + i));
        for (; i < len; i++)
            MEMORY.writeByte(address + i, MEMORY.readByte(bytes, ARRAY_BYTE_BASE_OFFSET + i));
        return address + len;
    }

    public static long append8bit(long address, char[] chars) {
        final int len = chars.length;
        int i;
        for (i = 0; i < len; i++)
            MEMORY.writeByte(address + i, (byte) chars[i]);
        return address + len;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy