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

io.questdb.cutlass.line.tcp.LineTcpEventBuffer Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2024 QuestDB
 *
 *  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 io.questdb.cutlass.line.tcp;

import io.questdb.cairo.CairoException;
import io.questdb.cairo.ColumnType;
import io.questdb.cairo.GeoHashes;
import io.questdb.cairo.sql.SymbolTable;
import io.questdb.std.*;
import io.questdb.std.str.*;

import static io.questdb.cutlass.line.tcp.LineTcpParser.ENTITY_TYPE_NULL;

public class LineTcpEventBuffer {
    private final long bufLo;
    private final long bufSize;
    private final FlyweightDirectUtf16Sink tempSink = new FlyweightDirectUtf16Sink();
    private final FlyweightDirectUtf16Sink tempSinkB = new FlyweightDirectUtf16Sink();
    private final DirectUtf8String utf8Sequence = new DirectUtf8String();

    public LineTcpEventBuffer(long bufLo, long bufSize) {
        this.bufLo = bufLo;
        this.bufSize = bufLo + bufSize;
    }

    public long addBoolean(long address, byte value) {
        checkCapacity(address, Byte.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_BOOLEAN);
        Unsafe.getUnsafe().putByte(address + Byte.BYTES, value);
        return address + Byte.BYTES + Byte.BYTES;
    }

    public long addByte(long address, byte value) {
        checkCapacity(address, Byte.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_BYTE);
        Unsafe.getUnsafe().putByte(address + Byte.BYTES, value);
        return address + Byte.BYTES + Byte.BYTES;
    }

    public long addChar(long address, char value) {
        checkCapacity(address, Character.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_CHAR);
        Unsafe.getUnsafe().putChar(address + Byte.BYTES, value);
        return address + Character.BYTES + Byte.BYTES;
    }

    public long addColumnIndex(long address, int colIndex) {
        checkCapacity(address, Integer.BYTES);
        Unsafe.getUnsafe().putInt(address, colIndex);
        return address + Integer.BYTES;
    }

    public long addColumnName(long address, CharSequence colName, CharSequence principal) {
        int colNameLen = colName.length();
        int principalLen = principal != null ? principal.length() : 0;
        int colNameCapacity = Integer.BYTES + 2 * colNameLen;
        int fullCapacity = colNameCapacity + Integer.BYTES + 2 * principalLen;
        checkCapacity(address, fullCapacity);

        // Negative length indicates to the writer thread that column is passed by
        // name rather than by index. When value is positive (on the else branch)
        // the value is treated as column index.
        Unsafe.getUnsafe().putInt(address, -colNameLen);
        Chars.copyStrChars(colName, 0, colNameLen, address + Integer.BYTES);

        // Now write principal name, so that we can call DdlListener#onColumnAdded()
        // when adding the column.
        Unsafe.getUnsafe().putInt(address + colNameCapacity, principalLen);
        if (principalLen > 0) {
            Chars.copyStrChars(principal, 0, principalLen, address + colNameCapacity + Integer.BYTES);
        }

        return address + fullCapacity;
    }

    public long addDate(long address, long value) {
        checkCapacity(address, Long.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_DATE);
        Unsafe.getUnsafe().putLong(address + Byte.BYTES, value);
        return address + Long.BYTES + Byte.BYTES;
    }

    public void addDesignatedTimestamp(long address, long timestamp) {
        checkCapacity(address, Long.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putLong(address, timestamp);
    }

    public long addDouble(long address, double value) {
        checkCapacity(address, Double.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_DOUBLE);
        Unsafe.getUnsafe().putDouble(address + Byte.BYTES, value);
        return address + Double.BYTES + Byte.BYTES;
    }

    public long addFloat(long address, float value) {
        checkCapacity(address, Float.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_FLOAT);
        Unsafe.getUnsafe().putFloat(address + Byte.BYTES, value);
        return address + Float.BYTES + Byte.BYTES;
    }

    public long addGeoHash(long address, DirectUtf8Sequence value, int colTypeMeta) {
        long geohash;
        try {
            geohash = GeoHashes.fromAsciiTruncatingNl(value.lo(), value.hi(), Numbers.decodeLowShort(colTypeMeta));
        } catch (NumericException e) {
            geohash = GeoHashes.NULL;
        }
        switch (Numbers.decodeHighShort(colTypeMeta)) {
            default:
                checkCapacity(address, Long.BYTES + Byte.BYTES);
                Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_GEOLONG);
                Unsafe.getUnsafe().putLong(address + Byte.BYTES, geohash);
                return address + Long.BYTES + Byte.BYTES;
            case ColumnType.GEOINT:
                checkCapacity(address, Integer.BYTES + Byte.BYTES);
                Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_GEOINT);
                Unsafe.getUnsafe().putInt(address + Byte.BYTES, (int) geohash);
                return address + Integer.BYTES + Byte.BYTES;
            case ColumnType.GEOSHORT:
                checkCapacity(address, Short.BYTES + Byte.BYTES);
                Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_GEOSHORT);
                Unsafe.getUnsafe().putShort(address + Byte.BYTES, (short) geohash);
                return address + Short.BYTES + Byte.BYTES;
            case ColumnType.GEOBYTE:
                checkCapacity(address, Byte.BYTES + Byte.BYTES);
                Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_GEOBYTE);
                Unsafe.getUnsafe().putByte(address + Byte.BYTES, (byte) geohash);
                return address + Byte.BYTES + Byte.BYTES;
        }
    }

    public long addInt(long address, int value) {
        checkCapacity(address, Integer.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_INTEGER);
        Unsafe.getUnsafe().putInt(address + Byte.BYTES, value);
        return address + Integer.BYTES + Byte.BYTES;
    }

    public long addLong(long address, long value) {
        checkCapacity(address, Long.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_LONG);
        Unsafe.getUnsafe().putLong(address + Byte.BYTES, value);
        return address + Long.BYTES + Byte.BYTES;
    }

    public long addLong256(long address, DirectUtf8Sequence value) {
        return addString(address, value, LineTcpParser.ENTITY_TYPE_LONG256);
    }

    public long addNull(long address) {
        checkCapacity(address, Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_NULL);
        return address + Byte.BYTES;
    }

    public void addNumOfColumns(long address, int numOfColumns) {
        checkCapacity(address, Integer.BYTES);
        Unsafe.getUnsafe().putInt(address, numOfColumns);
    }

    public long addShort(long address, short value) {
        checkCapacity(address, Short.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_SHORT);
        Unsafe.getUnsafe().putShort(address + Byte.BYTES, value);
        return address + Short.BYTES + Byte.BYTES;
    }

    public long addString(long address, DirectUtf8Sequence value) {
        return addString(address, value, LineTcpParser.ENTITY_TYPE_STRING);
    }

    public void addStructureVersion(long address, long structureVersion) {
        checkCapacity(address, Long.BYTES);
        Unsafe.getUnsafe().putLong(address, structureVersion);
    }

    public long addSymbol(long address, DirectUtf8Sequence value, DirectUtf8SymbolLookup symbolLookup) {
        final int maxLen = 2 * value.size();
        checkCapacity(address, Byte.BYTES + Integer.BYTES + maxLen);
        final long strPos = address + Byte.BYTES + Integer.BYTES; // skip field type and string length

        // via temp string the utf8 decoder will be writing directly to our buffer
        tempSink.of(strPos, strPos + maxLen);

        final int symIndex = symbolLookup.keyOf(value);
        if (symIndex != SymbolTable.VALUE_NOT_FOUND) {
            // We know the symbol int value
            // Encode the int
            Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_CACHED_TAG);
            Unsafe.getUnsafe().putInt(address + Byte.BYTES, symIndex);
            return address + Integer.BYTES + Byte.BYTES;
        } else {
            // Symbol value cannot be resolved at this point
            // Encode whole string value into the message
            if (value.isAscii()) {
                tempSink.put(value);
            } else {
                Utf8s.utf8ToUtf16(value, tempSink);
            }
            final int length = tempSink.length();
            Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_TAG);
            Unsafe.getUnsafe().putInt(address + Byte.BYTES, length);
            return address + length * 2L + Integer.BYTES + Byte.BYTES;
        }
    }

    public long addTimestamp(long address, long value) {
        checkCapacity(address, Long.BYTES + Byte.BYTES);
        Unsafe.getUnsafe().putByte(address, LineTcpParser.ENTITY_TYPE_TIMESTAMP);
        Unsafe.getUnsafe().putLong(address + Byte.BYTES, value);
        return address + Long.BYTES + Byte.BYTES;
    }

    /**
     * Add UUID encoded as a string to the buffer.
     * 

* Technically, DirectByteCharSequence is UTF-8 encoded, but any non-ASCII character will cause the UUID * to be rejected by the parser. Hence, we do not have to bother with UTF-8 decoding. * * @param offset offset in the buffer to write to * @param value value to write * @return new offset * @throws NumericException if the value is not a valid UUID string */ public long addUuid(long offset, DirectUtf8Sequence value) throws NumericException { checkCapacity(offset, Byte.BYTES + 2 * Long.BYTES); final CharSequence csView = value.asAsciiCharSequence(); Uuid.checkDashesAndLength(csView); long hi = Uuid.parseHi(csView); long lo = Uuid.parseLo(csView); Unsafe.getUnsafe().putByte(offset, LineTcpParser.ENTITY_TYPE_UUID); offset += Byte.BYTES; Unsafe.getUnsafe().putLong(offset, lo); offset += Long.BYTES; Unsafe.getUnsafe().putLong(offset, hi); return offset + Long.BYTES; } public long addVarchar(long address, DirectUtf8Sequence value) { final int valueSize = value.size(); final int totalSize = Byte.BYTES + Byte.BYTES + Integer.BYTES + valueSize; checkCapacity(address, totalSize); Unsafe.getUnsafe().putByte(address++, LineTcpParser.ENTITY_TYPE_VARCHAR); Unsafe.getUnsafe().putByte(address++, (byte) (value.isAscii() ? 0 : 1)); Unsafe.getUnsafe().putInt(address, valueSize); address += Integer.BYTES; value.writeTo(address, 0, valueSize); return address + totalSize; } public long columnValueLength(byte entityType, long offset) { CharSequence cs; switch (entityType) { case LineTcpParser.ENTITY_TYPE_TAG: case LineTcpParser.ENTITY_TYPE_STRING: case LineTcpParser.ENTITY_TYPE_LONG256: cs = readUtf16Chars(offset); return cs.length() * 2L + Integer.BYTES; case LineTcpParser.ENTITY_TYPE_BYTE: case LineTcpParser.ENTITY_TYPE_GEOBYTE: case LineTcpParser.ENTITY_TYPE_BOOLEAN: return Byte.BYTES; case LineTcpParser.ENTITY_TYPE_SHORT: case LineTcpParser.ENTITY_TYPE_GEOSHORT: return Short.BYTES; case LineTcpParser.ENTITY_TYPE_CHAR: return Character.BYTES; case LineTcpParser.ENTITY_TYPE_CACHED_TAG: case LineTcpParser.ENTITY_TYPE_INTEGER: case LineTcpParser.ENTITY_TYPE_GEOINT: return Integer.BYTES; case LineTcpParser.ENTITY_TYPE_LONG: case LineTcpParser.ENTITY_TYPE_GEOLONG: case LineTcpParser.ENTITY_TYPE_DATE: case LineTcpParser.ENTITY_TYPE_TIMESTAMP: return Long.BYTES; case LineTcpParser.ENTITY_TYPE_FLOAT: return Float.BYTES; case LineTcpParser.ENTITY_TYPE_DOUBLE: return Double.BYTES; case LineTcpParser.ENTITY_TYPE_UUID: return Long128.BYTES; case ENTITY_TYPE_NULL: return 0; default: throw new UnsupportedOperationException("entityType " + entityType + " is not implemented!"); } } public long getAddress() { return bufLo; } public long getAddressAfterHeader() { // The header contains structure version (long), timestamp (long) and number of columns (int). return bufLo + 2 * Long.BYTES + Integer.BYTES; } public byte readByte(long address) { return Unsafe.getUnsafe().getByte(address); } public char readChar(long address) { return Unsafe.getUnsafe().getChar(address); } public double readDouble(long address) { return Unsafe.getUnsafe().getDouble(address); } public float readFloat(long address) { return Unsafe.getUnsafe().getFloat(address); } public int readInt(long address) { return Unsafe.getUnsafe().getInt(address); } public long readLong(long address) { return Unsafe.getUnsafe().getLong(address); } public short readShort(long address) { return Unsafe.getUnsafe().getShort(address); } public CharSequence readUtf16Chars(long address) { int len = readInt(address); return readUtf16Chars(address + Integer.BYTES, len); } public CharSequence readUtf16Chars(long address, int length) { return tempSink.asCharSequence(address, address + length * 2L); } public CharSequence readUtf16CharsB(long address, int length) { return tempSinkB.asCharSequence(address, address + length * 2L); } public Utf8Sequence readVarchar(long address, boolean ascii) { int size = readInt(address); return utf8Sequence.of(address + Integer.BYTES, address + Integer.BYTES + size, ascii); } private long addString(long address, DirectUtf8Sequence value, byte entityTypeString) { int maxLen = 2 * value.size(); checkCapacity(address, Byte.BYTES + Integer.BYTES + maxLen); long strPos = address + Byte.BYTES + Integer.BYTES; // skip field type and string length tempSink.of(strPos, strPos + maxLen); if (value.isAscii()) { tempSink.put(value); } else { Utf8s.utf8ToUtf16Unchecked(value, tempSink); } final int length = tempSink.length(); Unsafe.getUnsafe().putByte(address, entityTypeString); Unsafe.getUnsafe().putInt(address + Byte.BYTES, length); return address + length * 2L + Integer.BYTES + Byte.BYTES; } private void checkCapacity(long address, int length) { if (address + length > bufSize) { throw CairoException.critical(0).put("queue buffer overflow"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy