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

io.questdb.cairo.vm.PagedVirtualMemory Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2020 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.cairo.vm;

import io.questdb.cairo.CairoException;
import io.questdb.cairo.TableUtils;
import io.questdb.griffin.engine.LimitOverflowException;
import io.questdb.log.Log;
import io.questdb.log.LogFactory;
import io.questdb.std.*;
import io.questdb.std.str.AbstractCharSequence;
import io.questdb.std.str.CharSink;
import org.jetbrains.annotations.NotNull;

import java.io.Closeable;

import static io.questdb.cairo.vm.VmUtils.STRING_LENGTH_BYTES;

public class PagedVirtualMemory implements ReadWriteVirtualMemory, Closeable {
    private static final Log LOG = LogFactory.getLog(PagedVirtualMemory.class);
    protected final LongList pages = new LongList(4, 0);
    private final ByteSequenceView bsview = new ByteSequenceView();
    private final CharSequenceView csview = new CharSequenceView();
    private final CharSequenceView csview2 = new CharSequenceView();
    private final Long256Impl long256 = new Long256Impl();
    private final Long256Impl long256B = new Long256Impl();
    private final int maxPages;
    private final InPageLong256FromCharSequenceDecoder inPageLong256Decoder = new InPageLong256FromCharSequenceDecoder();
    private final StradlingPageLong256FromCharSequenceDecoder stradlingPageLong256Decoder = new StradlingPageLong256FromCharSequenceDecoder();
    private long pageSize;
    private int bits;
    private long mod;
    private long appendPointer = -1;
    private long pageHi = -1;
    private long pageLo = -1;
    private long baseOffset = 1;
    private long roOffsetLo = 0;
    private long roOffsetHi = 0;
    private long absolutePointer;

    public PagedVirtualMemory(long pageSize, int maxPages) {
        setPageSize(pageSize);
        this.maxPages = maxPages;
    }

    protected PagedVirtualMemory() {
        maxPages = Integer.MAX_VALUE;
    }

    private static void copyStrChars(CharSequence value, int pos, int len, long address) {
        for (int i = 0; i < len; i++) {
            char c = value.charAt(i + pos);
            Unsafe.getUnsafe().putChar(address + 2L * i, c);
        }
    }

    public boolean isMapped(long offset, long size) {
        int pageIndex = pageIndex(offset);
        int pageEndIndex = pageIndex(offset + size - 1);
        if (pageIndex == pageEndIndex) {
            return getPageAddress(pageIndex) > 0;
        }
        return false;
    }

    public long addressOf(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi) {
            return absolutePointer + offset;
        }
        return addressOf0(offset);
    }

    public void clearHotPage() {
        roOffsetLo = roOffsetHi = 0;
    }

    @Override
    public void close() {
        clearPages();
        appendPointer = -1;
        pageHi = -1;
        pageLo = -1;
        baseOffset = 1;
        clearHotPage();
    }

    public void clear() {
        int n = pages.size();
        if (n > 1) {
            for (int i = 1; i < n; i++) {
                release(i, pages.getQuick(i));
                pages.set(i, 0);
            }
        }
        appendPointer = -1;
        pageHi = -1;
        pageLo = -1;
        baseOffset = 1;
        clearHotPage();
    }

    public int getPageCount() {
        return pages.size();
    }

    public final long getAppendOffset() {
        return baseOffset + appendPointer;
    }

    @Override
    public long size() {
        return getAppendOffset();
    }

    public final BinarySequence getBin(long offset) {
        final long len = getLong(offset);
        if (len == -1) {
            return null;
        }
        return bsview.of(offset + 8, len);
    }

    public final long getBinLen(long offset) {
        return getLong(offset);
    }

    public boolean getBool(long offset) {
        return getByte(offset) == 1;
    }

    public final byte getByte(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - 1) {
            return Unsafe.getUnsafe().getByte(absolutePointer + offset);
        }
        return getByte0(offset);
    }

    public final char getChar(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - Character.BYTES) {
            return Unsafe.getUnsafe().getChar(absolutePointer + offset);
        }
        return getChar0(offset);
    }

    public final double getDouble(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - Double.BYTES) {
            return Unsafe.getUnsafe().getDouble(absolutePointer + offset);
        }
        return getDouble0(offset);
    }

    public final float getFloat(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - Float.BYTES) {
            return Unsafe.getUnsafe().getFloat(absolutePointer + offset);
        }
        return getFloat0(offset);
    }

    public final int getInt(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - Integer.BYTES) {
            return Unsafe.getUnsafe().getInt(absolutePointer + offset);
        }
        return getInt0(offset);
    }

    public long getLong(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - Long.BYTES) {
            return Unsafe.getUnsafe().getLong(absolutePointer + offset);
        }
        return getLong0(offset);
    }

    public void getLong256(long offset, CharSink sink) {
        final long a, b, c, d;
        if (roOffsetLo < offset && offset < roOffsetHi - Long256.BYTES) {
            a = Unsafe.getUnsafe().getLong(absolutePointer + offset);
            b = Unsafe.getUnsafe().getLong(absolutePointer + offset + Long.BYTES);
            c = Unsafe.getUnsafe().getLong(absolutePointer + offset + Long.BYTES * 2);
            d = Unsafe.getUnsafe().getLong(absolutePointer + offset + Long.BYTES * 3);
        } else {
            a = getLong(offset);
            b = getLong(offset + Long.BYTES);
            c = getLong(offset + Long.BYTES * 2);
            d = getLong(offset + Long.BYTES * 3);
        }
        Numbers.appendLong256(a, b, c, d, sink);
    }

    public void getLong256(long offset, Long256Sink sink) {
        if (roOffsetLo < offset && offset < roOffsetHi - Long256.BYTES) {
            sink.setLong0(Unsafe.getUnsafe().getLong(absolutePointer + offset));
            sink.setLong1(Unsafe.getUnsafe().getLong(absolutePointer + offset + Long.BYTES));
            sink.setLong2(Unsafe.getUnsafe().getLong(absolutePointer + offset + Long.BYTES * 2));
            sink.setLong3(Unsafe.getUnsafe().getLong(absolutePointer + offset + Long.BYTES * 3));
        } else {
            sink.setLong0(getLong(offset));
            sink.setLong1(getLong(offset + Long.BYTES));
            sink.setLong2(getLong(offset + Long.BYTES * 2));
            sink.setLong3(getLong(offset + Long.BYTES * 3));
        }
    }

    public Long256 getLong256A(long offset) {
        getLong256(offset, long256);
        return long256;
    }

    public Long256 getLong256B(long offset) {
        getLong256(offset, long256B);
        return long256B;
    }

    public final short getShort(long offset) {
        if (roOffsetLo < offset && offset < roOffsetHi - Short.BYTES) {
            return Unsafe.getUnsafe().getShort(absolutePointer + offset);
        }
        return getShort0(offset);
    }

    public final CharSequence getStr(long offset) {
        return getStr0(offset, csview);
    }

    public final CharSequence getStr0(long offset, CharSequenceView view) {
        final int len = getInt(offset);
        if (len == TableUtils.NULL_LEN) {
            return null;
        }

        if (len == 0) {
            return "";
        }

        return view.of(offset + STRING_LENGTH_BYTES, len);
    }

    public final CharSequence getStr2(long offset) {
        return getStr0(offset, csview2);
    }

    public final int getStrLen(long offset) {
        return getInt(offset);
    }

    public long hash(long offset, long size) {
        if (roOffsetLo < offset && offset < roOffsetHi - size) {
            long n = size - (size % 8);
            long address = absolutePointer + offset;

            long h = 179426491L;
            for (long i = 0; i < n; i += 8) {
                h = (h << 5) - h + Unsafe.getUnsafe().getLong(address + i);
            }

            for (; n < size; n++) {
                h = (h << 5) - h + Unsafe.getUnsafe().getByte(address + n);
            }
            return h;
        }

        return hashSlow(offset, size);
    }

    /**
     * Updates append pointer with address for the given offset. All put* functions will be
     * appending from this offset onwards effectively overwriting data. Size of virtual memory remains
     * unaffected until the moment memory has to be extended.
     *
     * @param offset position from 0 in virtual memory.
     */
    @Override
    public void jumpTo(long offset) {
        assert offset > -1;
        final long p = offset - baseOffset;
        if (p > pageLo && p < pageHi) {
            appendPointer = p;
        } else {
            jumpTo0(offset);
        }
    }

    public long pageRemaining(long offset) {
        return getPageSize(pageIndex(offset)) - offsetInPage(offset);
    }

    @Override
    public final long putBin(BinarySequence value) {
        final long offset = getAppendOffset();
        if (value == null) {
            putLong(TableUtils.NULL_LEN);
        } else {
            final long len = value.length();
            putLong(len);
            final long remaining = pageHi - appendPointer;
            if (len < remaining) {
                putBinSequence(value, 0, len);
                appendPointer += len;
            } else {
                putBin0(value, len, remaining);
            }
        }
        return offset;
    }

    public void copyTo(long address, long offset, long len) {
        while (len > 0) {
            final int page = pageIndex(offset);
            final long pageSize = getPageSize(page);
            final long pageAddress = getPageAddress(page);
            assert pageAddress > 0;
            final long offsetInPage = offsetInPage(offset);
            final long bytesToCopy = Math.min(len, pageSize - offsetInPage);
            Vect.memcpy(pageAddress + offsetInPage, address, bytesToCopy);
            len -= bytesToCopy;
            offset += bytesToCopy;
            address += bytesToCopy;
        }
    }

    @Override
    public void putBool(boolean value) {
        putByte((byte) (value ? 1 : 0));
    }

    @Override
    public void putBool(long offset, boolean value) {
        putByte(offset, (byte) (value ? 1 : 0));
    }

    @Override
    public final void putByte(long offset, byte value) {
        if (roOffsetLo < offset && offset < roOffsetHi - 1) {
            Unsafe.getUnsafe().putByte(absolutePointer + offset, value);
        } else {
            putByteRnd(offset, value);
        }
    }

    @Override
    public void putByte(byte b) {
        if (pageHi == appendPointer) {
            pageAt(getAppendOffset() + 1);
        }
        Unsafe.getUnsafe().putByte(appendPointer++, b);
    }

    @Override
    public void putChar(long offset, char value) {
        if (roOffsetLo < offset && offset < roOffsetHi - 2) {
            Unsafe.getUnsafe().putChar(absolutePointer + offset, value);
        } else {
            putCharBytes(offset, value);
        }
    }

    @Override
    public final void putChar(char value) {
        if (pageHi - appendPointer > 1) {
            Unsafe.getUnsafe().putChar(appendPointer, value);
            appendPointer += 2;
        } else {
            putCharBytes(value);
        }
    }

    @Override
    public void putDouble(long offset, double value) {
        if (roOffsetLo < offset && offset < roOffsetHi - 8) {
            Unsafe.getUnsafe().putDouble(absolutePointer + offset, value);
        } else {
            putDoubleBytes(offset, value);
        }
    }

    @Override
    public final void putDouble(double value) {
        if (pageHi - appendPointer > 7) {
            Unsafe.getUnsafe().putDouble(appendPointer, value);
            appendPointer += 8;
        } else {
            putDoubleBytes(value);
        }
    }

    @Override
    public void putFloat(long offset, float value) {
        if (roOffsetLo < offset && offset < roOffsetHi - 4) {
            Unsafe.getUnsafe().putFloat(absolutePointer + offset, value);
        } else {
            putFloatBytes(offset, value);
        }
    }

    @Override
    public final void putFloat(float value) {
        if (pageHi - appendPointer > 3) {
            Unsafe.getUnsafe().putFloat(appendPointer, value);
            appendPointer += 4;
        } else {
            putFloatBytes(value);
        }
    }

    @Override
    public void putInt(long offset, int value) {
        if (roOffsetLo < offset && offset < roOffsetHi - Integer.BYTES) {
            Unsafe.getUnsafe().putInt(absolutePointer + offset, value);
        } else {
            putIntBytes(offset, value);
        }
    }

    @Override
    public final void putInt(int value) {
        if (pageHi - appendPointer > 3) {
            Unsafe.getUnsafe().putInt(appendPointer, value);
            appendPointer += 4;
        } else {
            putIntBytes(value);
        }
    }

    @Override
    public void putLong(long offset, long value) {
        if (roOffsetLo < offset && offset < roOffsetHi - 8) {
            Unsafe.getUnsafe().putLong(absolutePointer + offset, value);
        } else {
            putLongBytes(offset, value);
        }
    }

    @Override
    public final void putLong(long value) {
        if (pageHi - appendPointer > 7) {
            Unsafe.getUnsafe().putLong(appendPointer, value);
            appendPointer += 8;
        } else {
            putLongBytes(value);
        }
    }

    @Override
    public final void putLong128(long l1, long l2) {
        if (pageHi - appendPointer > 15) {
            Unsafe.getUnsafe().putLong(appendPointer, l1);
            Unsafe.getUnsafe().putLong(appendPointer + Long.BYTES, l2);
            appendPointer += 16;
        } else {
            putLong(l1);
            putLong(l2);
        }
    }

    @Override
    public void putLong256(long offset, Long256 value) {
        putLong256(
                offset,
                value.getLong0(),
                value.getLong1(),
                value.getLong2(),
                value.getLong3()
        );
    }

    @Override
    public void putLong256(long offset, long l0, long l1, long l2, long l3) {
        if (roOffsetLo < offset && offset < roOffsetHi - Long256.BYTES) {
            Unsafe.getUnsafe().putLong(absolutePointer + offset, l0);
            Unsafe.getUnsafe().putLong(absolutePointer + offset + Long.BYTES, l1);
            Unsafe.getUnsafe().putLong(absolutePointer + offset + Long.BYTES * 2, l2);
            Unsafe.getUnsafe().putLong(absolutePointer + offset + Long.BYTES * 3, l3);
        } else {
            putLong(offset, l0);
            putLong(offset + Long.BYTES, l1);
            putLong(offset + Long.BYTES * 2, l2);
            putLong(offset + Long.BYTES * 3, l3);
        }
    }

    @Override
    public final void putLong256(long l0, long l1, long l2, long l3) {
        if (pageHi - appendPointer > Long256.BYTES - 1) {
            Unsafe.getUnsafe().putLong(appendPointer, l0);
            Unsafe.getUnsafe().putLong(appendPointer + Long.BYTES, l1);
            Unsafe.getUnsafe().putLong(appendPointer + Long.BYTES * 2, l2);
            Unsafe.getUnsafe().putLong(appendPointer + Long.BYTES * 3, l3);
            appendPointer += Long256.BYTES;
        } else {
            putLong(l0);
            putLong(l1);
            putLong(l2);
            putLong(l3);
        }
    }

    @Override
    public final void putLong256(Long256 value) {
        putLong256(
                value.getLong0(),
                value.getLong1(),
                value.getLong2(),
                value.getLong3()
        );
    }

    @Override
    public final void putLong256(CharSequence hexString) {
        if (pageHi - appendPointer < 4 * Long.BYTES) {
            stradlingPageLong256Decoder.putLong256(hexString);
        } else {
            inPageLong256Decoder.putLong256(hexString);
        }
    }

    @Override
    public final void putLong256(@NotNull CharSequence hexString, int start, int end) {
        if (pageHi - appendPointer < 4 * Long.BYTES) {
            stradlingPageLong256Decoder.putLong256(hexString, start, end);
        } else {
            inPageLong256Decoder.putLong256(hexString, start, end);
        }
    }

    @Override
    public final long putNullBin() {
        final long offset = getAppendOffset();
        putLong(TableUtils.NULL_LEN);
        return offset;
    }

    @Override
    public final long putNullStr() {
        final long offset = getAppendOffset();
        putInt(TableUtils.NULL_LEN);
        return offset;
    }

    @Override
    public final void putNullStr(long offset) {
        putInt(offset, TableUtils.NULL_LEN);
    }

    @Override
    public final void putBlockOfBytes(long from, long len) {
        if (len < pageHi - appendPointer) {
            Vect.memcpy(from, appendPointer, len);
            appendPointer += len;
        } else {
            putBinSlit(from, len);
        }
    }

    @Override
    public void putShort(long offset, short value) {
        if (roOffsetLo < offset && offset < roOffsetHi - 2) {
            Unsafe.getUnsafe().putShort(absolutePointer + offset, value);
        } else {
            putShortBytes(offset, value);
        }
    }

    @Override
    public final void putShort(short value) {
        if (pageHi - appendPointer > 1) {
            Unsafe.getUnsafe().putShort(appendPointer, value);
            appendPointer += 2;
        } else {
            putShortBytes(value);
        }
    }

    @Override
    public final long putStr(CharSequence value) {
        return value == null ? putNullStr() : putStr0(value, 0, value.length());
    }

    @Override
    public final long putStr(char value) {
        if (value == 0) return putNullStr();
        else {
            final long offset = getAppendOffset();
            putInt(1);
            if (pageHi - appendPointer < Character.BYTES) {
                putSplitChar(value);
            } else {
                Unsafe.getUnsafe().putChar(appendPointer, value);
                appendPointer += Character.BYTES;
            }
            return offset;
        }
    }

    @Override
    public final long putStr(CharSequence value, int pos, int len) {
        if (value == null) {
            return putNullStr();
        }
        return putStr0(value, pos, len);
    }

    @Override
    public void putStr(long offset, CharSequence value) {
        if (value == null) {
            putNullStr(offset);
        } else {
            putStr(offset, value, 0, value.length());
        }
    }

    @Override
    public void putStr(long offset, CharSequence value, int pos, int len) {
        putInt(offset, len);
        if (roOffsetLo < offset && offset < roOffsetHi - len * 2L - 4) {
            copyStrChars(value, pos, len, absolutePointer + offset + 4);
        } else {
            putStrSplit(offset + 4, value, pos, len);
        }
    }

    /**
     * Skips given number of bytes. Same as logically appending 0-bytes. Advantage of this method is that
     * no memory write takes place.
     *
     * @param bytes number of bytes to skip
     */
    public void skip(long bytes) {
        assert bytes >= 0;
        if (pageHi - appendPointer > bytes) {
            appendPointer += bytes;
        } else {
            skip0(bytes);
        }
    }

    @Override
    public final long putBin(long from, long len) {
        final long offset = getAppendOffset();
        putLong(len > 0 ? len : TableUtils.NULL_LEN);
        if (len < 1) {
            return offset;
        }

        if (len < pageHi - appendPointer) {
            Vect.memcpy(from, appendPointer, len);
            appendPointer += len;
        } else {
            putBinSlit(from, len);
        }

        return offset;
    }

    private long addressOf0(long offset) {
        return computeHotPage(pageIndex(offset)) + offsetInPage(offset);
    }

    protected long allocateNextPage(int page) {
        LOG.debug().$("new page [size=").$(getMapPageSize()).I$();
        if (page >= maxPages) {
            throw LimitOverflowException.instance().put("Maximum number of pages (").put(maxPages).put(") breached in VirtualMemory");
        }
        return Unsafe.malloc(getMapPageSize());
    }

    protected long cachePageAddress(int index, long address) {
        pages.extendAndSet(index, address);
        return address;
    }

    private void clearPages() {
        int n = pages.size();
        if (n > 0) {
            for (int i = 0; i < n; i++) {
                release(i, pages.getQuick(i));
            }
        }
        pages.erase();
    }

    /**
     * Computes boundaries of read-only memory page to enable fast-path check of offsets
     */
    private long computeHotPage(int page) {
        long pageAddress = getPageAddress(page);
        assert pageAddress > 0;
        roOffsetLo = pageOffset(page) - 1;
        roOffsetHi = roOffsetLo + getPageSize(page) + 1;
        absolutePointer = pageAddress - roOffsetLo - 1;
        return pageAddress;
    }

    protected void ensurePagesListCapacity(long size) {
        int capacity = pageIndex(size) + 1;
        pages.ensureCapacity(capacity);
    }

    private byte getByte0(long offset) {
        return Unsafe.getUnsafe().getByte(computeHotPage(pageIndex(offset)) + offsetInPage(offset));
    }

    private char getChar0(long offset) {
        int page = pageIndex(offset);
        long pageOffset = offsetInPage(offset);
        final long pageSize = getPageSize(page);

        if (pageSize - pageOffset > 1) {
            return Unsafe.getUnsafe().getChar(computeHotPage(page) + pageOffset);
        }

        return getCharBytes(page, pageOffset, pageSize);
    }

    char getCharBytes(int page, long pageOffset, long pageSize) {
        char value = 0;
        long pageAddress = getPageAddress(page);

        for (int i = 0; i < 2; i++) {
            if (pageOffset == pageSize) {
                pageAddress = getPageAddress(++page);
                pageOffset = 0;
            }
            char b = (char) (Unsafe.getUnsafe().getByte(pageAddress + pageOffset++));
            value = (char) ((b << (8 * i)) | value);
        }

        return value;
    }

    private double getDouble0(long offset) {
        int page = pageIndex(offset);
        long pageOffset = offsetInPage(offset);
        final long pageSize = getPageSize(page);

        if (pageSize - pageOffset > 7) {
            return Unsafe.getUnsafe().getDouble(computeHotPage(page) + pageOffset);
        }
        return getDoubleBytes(page, pageOffset, pageSize);
    }

    double getDoubleBytes(int page, long pageOffset, long pageSize) {
        return Double.longBitsToDouble(getLongBytes(page, pageOffset, pageSize));
    }

    private float getFloat0(long offset) {
        int page = pageIndex(offset);
        long pageOffset = offsetInPage(offset);

        if (getPageSize(page) - pageOffset > 3) {
            return Unsafe.getUnsafe().getFloat(computeHotPage(page) + pageOffset);
        }
        return getFloatBytes(page, pageOffset);
    }

    float getFloatBytes(int page, long pageOffset) {
        return Float.intBitsToFloat(getIntBytes(page, pageOffset));
    }

    private int getInt0(long offset) {
        int page = pageIndex(offset);
        long pageOffset = offsetInPage(offset);

        if (getPageSize(page) - pageOffset > 3) {
            return Unsafe.getUnsafe().getInt(computeHotPage(page) + pageOffset);
        }
        return getIntBytes(page, pageOffset);
    }

    int getIntBytes(int page, long pageOffset) {
        int value = 0;
        long pageAddress = getPageAddress(page);
        final long pageSize = getPageSize(page);

        for (int i = 0; i < 4; i++) {
            if (pageOffset == pageSize) {
                pageAddress = getPageAddress(++page);
                pageOffset = 0;
            }
            int b = Unsafe.getUnsafe().getByte(pageAddress + pageOffset++) & 0xff;
            value = (b << (8 * i)) | value;
        }
        return value;
    }

    private long getLong0(long offset) {
        int page = pageIndex(offset);
        long pageOffset = offsetInPage(offset);
        final long pageSize = getPageSize(page);

        if (pageSize - pageOffset > 7) {
            return Unsafe.getUnsafe().getLong(computeHotPage(page) + pageOffset);
        }
        return getLongBytes(page, pageOffset, pageSize);
    }

    long getLongBytes(int page, long pageOffset, long pageSize) {
        long value = 0;
        long pageAddress = getPageAddress(page);

        for (int i = 0; i < 8; i++) {
            if (pageOffset == pageSize) {
                pageAddress = getPageAddress(++page);
                pageOffset = 0;
            }
            long b = Unsafe.getUnsafe().getByte(pageAddress + pageOffset++) & 0xff;
            value = (b << (8 * i)) | value;
        }
        return value;
    }

    public long getMapPageSize() {
        return pageSize;
    }

    /**
     * Provides address of page for read operations. Memory writes never call this.
     *
     * @param page page index, starting from 0
     * @return native address of page
     */
    public long getPageAddress(int page) {
        if (page < pages.size()) {
            return pages.getQuick(page);
        }
        return 0L;
    }

    public long getPageSize(int page) {
        return getMapPageSize();
    }

    private short getShort0(long offset) {
        int page = pageIndex(offset);
        long pageOffset = offsetInPage(offset);
        final long pageSize = getPageSize(page);

        if (pageSize - pageOffset > 1) {
            return Unsafe.getUnsafe().getShort(computeHotPage(page) + pageOffset);
        }

        return getShortBytes(page, pageOffset, pageSize);
    }

    short getShortBytes(int page, long pageOffset, long pageSize) {
        short value = 0;
        long pageAddress = getPageAddress(page);

        for (int i = 0; i < 2; i++) {
            if (pageOffset == pageSize) {
                pageAddress = getPageAddress(++page);
                assert pageAddress != 0;
                pageOffset = 0;
            }
            short b = (short) (Unsafe.getUnsafe().getByte(pageAddress + pageOffset++) & 0xff);
            value = (short) ((b << (8 * i)) | value);
        }

        return value;
    }

    private long hashSlow(long offset, long size) {
        long n = size - (size & 7);
        long h = 179426491L;
        for (long i = 0; i < n; i += 8) {
            h = (h << 5) - h + getLong(offset + i);
        }

        for (; n < size; n++) {
            h = (h << 5) - h + getByte(offset + n);
        }
        return h;
    }

    @Override
    public void grow(long size) {
        jumpTo(size);
    }

    private void jumpTo0(long offset) {
        int page = pageIndex(offset);
        pageLo = mapWritePage(page);
        pageHi = pageLo + getPageSize(page);
        baseOffset = pageOffset(page + 1) - pageHi;
        appendPointer = pageLo + offsetInPage(offset);
        pageLo--;
    }

    private long mapRandomWritePage(long offset) {
        int page = pageIndex(offset);
        long pageAddress = mapWritePage(page);
        assert pageAddress != 0;
        roOffsetLo = pageOffset(page) - 1;
        roOffsetHi = roOffsetLo + getPageSize(page) + 1;
        absolutePointer = pageAddress - roOffsetLo - 1;
        return pageAddress;
    }

    protected long mapWritePage(int page) {
        long address;
        if (page < pages.size()) {
            address = pages.getQuick(page);
            if (address != 0) {
                return address;
            }
        }
        return cachePageAddress(page, allocateNextPage(page));
    }

    public long offsetInPage(long offset) {
        return offset & mod;
    }

    private void pageAt(long offset) {
        int page = pageIndex(offset);
        updateLimits(page, mapWritePage(page));
    }

    public final int pageIndex(long offset) {
        return (int) (offset >> bits);
    }

    protected final long pageOffset(int page) {
        return ((long) page << bits);
    }

    private void putBin0(BinarySequence value, long len, long remaining) {
        long pos = 0;
        do {
            putBinSequence(value, pos, remaining);
            pos += remaining;
            len -= remaining;

            pageAt(baseOffset + pageHi);
            remaining = pageHi - appendPointer;
            if (len < remaining) {
                putBinSequence(value, pos, len);
                appendPointer += len;
                break;
            }
        } while (true);
    }

    private void putBinSequence(BinarySequence value, long pos, long len) {
        value.copyTo(appendPointer, pos, len);
    }

    public void zero() {
        for (int i = 0, n = pages.size(); i < n; i++) {
            long address = pages.getQuick(i);
            if (address == 0) {
                address = allocateNextPage(i);
                pages.setQuick(i, address);
            }
            Vect.memset(address, pageSize, 0);
        }
    }

    private void putByteRnd(long offset, byte value) {
        Unsafe.getUnsafe().putByte(mapRandomWritePage(offset) + offsetInPage(offset), value);
    }

    void putCharBytes(char value) {
        putByte((byte) (value & 0xff));
        putByte((byte) ((value >> 8) & 0xff));
    }

    void putCharBytes(long offset, char value) {
        putByte(offset, (byte) (value & 0xff));
        putByte(offset + 1, (byte) ((value >> 8) & 0xff));
    }

    void putDoubleBytes(double value) {
        putLongBytes(Double.doubleToLongBits(value));
    }

    void putDoubleBytes(long offset, double value) {
        putLongBytes(offset, Double.doubleToLongBits(value));
    }

    void putFloatBytes(float value) {
        putIntBytes(Float.floatToIntBits(value));
    }

    void putFloatBytes(long offset, float value) {
        putIntBytes(offset, Float.floatToIntBits(value));
    }

    void putIntBytes(int value) {
        putByte((byte) (value & 0xff));
        putByte((byte) ((value >> 8) & 0xff));
        putByte((byte) ((value >> 16) & 0xff));
        putByte((byte) ((value >> 24) & 0xff));
    }

    void putIntBytes(long offset, int value) {
        putByte(offset, (byte) (value & 0xff));
        putByte(offset + 1, (byte) ((value >> 8) & 0xff));
        putByte(offset + 2, (byte) ((value >> 16) & 0xff));
        putByte(offset + 3, (byte) ((value >> 24) & 0xff));
    }

    private void putLong256Null() {
        Long256Impl.putNull(appendPointer);
    }

    void putLongBytes(long value) {
        putByte((byte) (value & 0xffL));
        putByte((byte) ((value >> 8) & 0xffL));
        putByte((byte) ((value >> 16) & 0xffL));
        putByte((byte) ((value >> 24) & 0xffL));
        putByte((byte) ((value >> 32) & 0xffL));
        putByte((byte) ((value >> 40) & 0xffL));
        putByte((byte) ((value >> 48) & 0xffL));
        putByte((byte) ((value >> 56) & 0xffL));
    }

    void putLongBytes(long offset, long value) {
        putByte(offset, (byte) (value & 0xffL));
        putByte(offset + 1, (byte) ((value >> 8) & 0xffL));
        putByte(offset + 2, (byte) ((value >> 16) & 0xffL));
        putByte(offset + 3, (byte) ((value >> 24) & 0xffL));
        putByte(offset + 4, (byte) ((value >> 32) & 0xffL));
        putByte(offset + 5, (byte) ((value >> 40) & 0xffL));
        putByte(offset + 6, (byte) ((value >> 48) & 0xffL));
        putByte(offset + 7, (byte) ((value >> 56) & 0xffL));
    }

    void putShortBytes(short value) {
        putByte((byte) (value & 0xff));
        putByte((byte) ((value >> 8) & 0xff));
    }

    void putShortBytes(long offset, short value) {
        putByte(offset, (byte) (value & 0xff));
        putByte(offset + 1, (byte) ((value >> 8) & 0xff));
    }

    private void putSplitChar(char c) {
        putByte((byte) c);
        putByte((byte) (c >> 8));
    }

    private long putStr0(CharSequence value, int pos, int len) {
        final long offset = getAppendOffset();
        putInt(len);
        if (pageHi - appendPointer < (long) len << 1) {
            putStrSplit(value, pos, len);
        } else {
            copyStrChars(value, pos, len, appendPointer);
            appendPointer += len * 2L;
        }
        return offset;
    }

    private void putStrSplit(long offset, CharSequence value, int pos, int len) {
        int start = pos;
        do {
            int half = (int) ((roOffsetHi - offset) / 2);

            if (len <= half) {
                copyStrChars(value, start, len, absolutePointer + offset);
                break;
            }

            copyStrChars(value, start, half, absolutePointer + offset);
            offset += half * 2L;
            if (offset < roOffsetHi) {
                char c = value.charAt(start + half);
                putByte(offset, (byte) c);
                putByte(offset + 1, (byte) (c >> 8));
                offset += 2;
                half++;
            } else {
                mapRandomWritePage(offset);
            }

            len -= half;
            start += half;
        } while (true);
    }

    private void putStrSplit(CharSequence value, int pos, int len) {
        int end = pos + len;
        int at = pos;
        while (at < end) {
            putSplitChar(value.charAt(at++));
        }
    }

    protected void release(int page, long address) {
        if (address != 0) {
            Unsafe.free(address, getPageSize(page));
        }
    }

    protected final void setPageSize(long pageSize) {
        clearPages();
        this.pageSize = Numbers.ceilPow2(pageSize);
        this.bits = Numbers.msb(this.pageSize);
        this.mod = this.pageSize - 1;
        clearHotPage();
    }

    private void skip0(long bytes) {
        jumpTo(getAppendOffset() + bytes);
    }

    protected final void updateLimits(int page, long pageAddress) {
        pageLo = pageAddress - 1;
        pageHi = pageAddress + getPageSize(page);
        baseOffset = pageOffset(page + 1) - pageHi;
        this.appendPointer = pageAddress;
    }

    public class CharSequenceView extends AbstractCharSequence {
        private int len;
        private long offset;

        @Override
        public int length() {
            return len;
        }

        @Override
        public char charAt(int index) {
            return PagedVirtualMemory.this.getChar(offset + index * 2L);
        }

        CharSequenceView of(long offset, int len) {
            this.offset = offset;
            this.len = len;
            return this;
        }
    }

    private class ByteSequenceView implements BinarySequence {
        private long offset;
        private long len = -1;
        private long lastIndex = -1;
        private long readAddress;
        private long readLimit;

        @Override
        public byte byteAt(long index) {
            try {
                if (index == lastIndex + 1 && readAddress < readLimit) {
                    return Unsafe.getUnsafe().getByte(readAddress++);
                }
                return updatePosAndGet(index);
            } finally {
                lastIndex = index;
            }
        }

        @Override
        public void copyTo(long address, final long start, final long length) {
            PagedVirtualMemory.this.copyTo(address, this.offset + start, Math.min(length, this.len - start));
        }

        @Override
        public long length() {
            return len;
        }

        ByteSequenceView of(long offset, long len) {
            this.offset = offset;
            this.len = len;
            this.lastIndex = -1;
            calculateBlobAddress(offset);
            return this;
        }

        private void calculateBlobAddress(long offset) {
            final int page = pageIndex(offset);
            final long pa = getPageAddress(page);
            this.readAddress = pa + offsetInPage(offset);
            this.readLimit = pa + getPageSize(page);
        }

        private byte updatePosAndGet(long index) {
            calculateBlobAddress(this.offset + index);
            return Unsafe.getUnsafe().getByte(readAddress++);
        }
    }

    private void putBinSlit(long start, long len) {
        do {
            int half = (int) (pageHi - appendPointer);
            if (len <= half) {
                Vect.memcpy(start, appendPointer, len);
                appendPointer += len;
                break;
            }

            Vect.memcpy(start, appendPointer, half);
            pageAt(getAppendOffset() + half);  // +1?
            len -= half;
            start += half;
        } while (true);
    }

    private class InPageLong256FromCharSequenceDecoder extends Long256FromCharSequenceDecoder {
        private void putLong256(CharSequence hexString) {
            final int len;
            if (hexString == null || (len = hexString.length()) == 0) {
                putLong256Null();
                appendPointer += Long256.BYTES;
            } else {
                putLong256(hexString, 2, len);
            }
        }

        @Override
        public void onDecoded(long l0, long l1, long l2, long l3) {
            Unsafe.getUnsafe().putLong(appendPointer, l0);
            Unsafe.getUnsafe().putLong(appendPointer + 8, l1);
            Unsafe.getUnsafe().putLong(appendPointer + 16, l2);
            Unsafe.getUnsafe().putLong(appendPointer + 24, l3);
        }

        private void putLong256(CharSequence hexString, int start, int end) {
            try {
                decode(hexString, start, end, inPageLong256Decoder);
            } catch (NumericException e) {
                throw CairoException.instance(0).put("invalid long256 [hex=").put(hexString).put(']');
            }
            appendPointer += Long256.BYTES;
        }
    }

    private class StradlingPageLong256FromCharSequenceDecoder extends Long256FromCharSequenceDecoder {
        private void putLong256(CharSequence hexString) {
            final int len;
            if (hexString == null || (len = hexString.length()) == 0) {
                putLong(Long256Impl.NULL_LONG256.getLong0());
                putLong(Long256Impl.NULL_LONG256.getLong1());
                putLong(Long256Impl.NULL_LONG256.getLong2());
                putLong(Long256Impl.NULL_LONG256.getLong3());
            } else {
                putLong256(hexString, 2, len);
            }
        }

        @Override
        public void onDecoded(long l0, long l1, long l2, long l3) {
            putLong(l0);
            putLong(l1);
            putLong(l2);
            putLong(l3);
        }

        private void putLong256(CharSequence hexString, int start, int end) {
            try {
                decode(hexString, start, end, this);
            } catch (NumericException e) {
                throw CairoException.instance(0).put("invalid long256 [hex=").put(hexString).put(']');
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy