Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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(']');
}
}
}
}