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.ContiguousVirtualMemory 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;
/**
* A version of {@link PagedVirtualMemory} that uses a single contiguous memory region instead of pages. Note that it still has the concept of a page such that the contiguous memory region will grow in page sizes.
*
* @author Patrick Mackinlay
*/
public class ContiguousVirtualMemory implements ReadWriteVirtualMemory, Mutable, Closeable {
private static final Log LOG = LogFactory.getLog(ContiguousVirtualMemory.class);
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 long pageSize;
private long pageSizeMsb;
private long baseAddress = 0;
private long baseAddressHi = 0;
private long appendAddress = 0;
public ContiguousVirtualMemory(long pageSize, int maxPages) {
this.maxPages = maxPages;
setPageSize(pageSize);
}
public long addressOf(long offset) {
return baseAddress + offset;
}
@Override
public void clear() {
releaseMemory();
}
@Override
public void close() {
releaseMemory();
baseAddress = 0;
baseAddressHi = 0;
appendAddress = 0;
}
public final long getAppendOffset() {
return appendAddress - baseAddress;
}
public final BinarySequence getBin(long offset) {
final long len = getLong(offset);
if (len > -1) {
return bsview.of(offset + 8, len);
}
return null;
}
public final long getBinLen(long offset) {
return getLong(offset);
}
public boolean getBool(long offset) {
return getByte(offset) == 1;
}
public final byte getByte(long offset) {
return Unsafe.getUnsafe().getByte(addressOf(offset));
}
public final double getDouble(long offset) {
return Unsafe.getUnsafe().getDouble(addressOf(offset));
}
public final float getFloat(long offset) {
return Unsafe.getUnsafe().getFloat(addressOf(offset));
}
public final int getInt(long offset) {
return Unsafe.getUnsafe().getInt(addressOf(offset));
}
public long getLong(long offset) {
return Unsafe.getUnsafe().getLong(addressOf(offset));
}
@Override
public long getPageAddress(int pageIndex) {
return baseAddress;
}
@Override
public int getPageCount() {
return 1;
}
@Override
public long getPageSize(int pageIndex) {
return pageSize;
}
public final short getShort(long offset) {
return Unsafe.getUnsafe().getShort(addressOf(offset));
}
public final CharSequence getStr(long offset) {
return getStr0(offset, csview);
}
public final CharSequence getStr2(long offset) {
return getStr0(offset, csview2);
}
public Long256 getLong256A(long offset) {
getLong256(offset, long256);
return long256;
}
public void getLong256(long offset, CharSink sink) {
final long a, b, c, d;
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 Long256 getLong256B(long offset) {
getLong256(offset, long256B);
return long256B;
}
public final char getChar(long offset) {
return Unsafe.getUnsafe().getChar(addressOf(offset));
}
public final int getStrLen(long offset) {
return getInt(offset);
}
@Override
public void grow(long size) {
long nPages = (size >>> pageSizeMsb) + 1;
size = nPages << pageSizeMsb;
final long oldSize = getMemorySize();
if (nPages > maxPages) {
throw LimitOverflowException.instance().put("Maximum number of pages (").put(maxPages).put(") breached in VirtualMemory");
}
final long newBaseAddress = reallocateMemory(baseAddress, getMemorySize(), size);
if (oldSize > 0) {
LOG.debug().$("extended [oldBase=").$(baseAddress).$(", newBase=").$(newBaseAddress).$(", oldSize=").$(oldSize).$(", newSize=").$(size).$(']').$();
}
handleMemoryReallocation(newBaseAddress, size);
}
@Override
public long size() {
return baseAddressHi - baseAddress;
}
public void getLong256(long offset, Long256Sink sink) {
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 final CharSequence getStr0(long offset, CharSequenceView view) {
final int len = getInt(offset);
if (len != TableUtils.NULL_LEN) {
return view.of(offset + VmUtils.STRING_LENGTH_BYTES, len);
}
return null;
}
public long hash(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;
}
/**
* 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.
*/
public void jumpTo(long offset) {
checkLimits(offset, 0);
appendAddress = baseAddress + offset;
}
@Override
public void putBlockOfBytes(long from, long len) {
checkLimits(len);
Vect.memcpy(from, appendAddress, len);
appendAddress += len;
}
@Override
public final long putBin(BinarySequence value) {
final long offset = getAppendOffset();
if (value != null) {
final long len = value.length();
checkLimits(len + Long.BYTES);
putLong(len);
value.copyTo(appendAddress, 0, len);
appendAddress += len;
} else {
putLong(TableUtils.NULL_LEN);
}
return offset;
}
public final long putBin(long from, long len) {
checkLimits(len + Long.BYTES);
final long offset = getAppendOffset();
putLong(len > 0 ? len : TableUtils.NULL_LEN);
if (len < 1) {
return offset;
}
Vect.memcpy(from, appendAddress, len);
appendAddress += len;
return offset;
}
public void putBool(boolean value) {
putByte((byte) (value ? 1 : 0));
}
public void putBool(long offset, boolean value) {
putByte(offset, (byte) (value ? 1 : 0));
}
public final void putByte(long offset, byte value) {
checkLimits(offset, Byte.BYTES);
Unsafe.getUnsafe().putByte(baseAddress + offset, value);
}
public void putByte(byte b) {
checkLimits(Byte.BYTES);
Unsafe.getUnsafe().putByte(appendAddress, b);
appendAddress++;
}
public void putChar(long offset, char value) {
checkLimits(offset, Character.BYTES);
Unsafe.getUnsafe().putChar(baseAddress + offset, value);
}
public final void putChar(char value) {
checkLimits(Character.BYTES);
Unsafe.getUnsafe().putChar(appendAddress, value);
appendAddress += Character.BYTES;
}
public void putDouble(long offset, double value) {
checkLimits(offset, Double.BYTES);
Unsafe.getUnsafe().putDouble(baseAddress + offset, value);
}
public final void putDouble(double value) {
checkLimits(Double.BYTES);
Unsafe.getUnsafe().putDouble(appendAddress, value);
appendAddress += Double.BYTES;
}
public void putFloat(long offset, float value) {
checkLimits(offset, Float.BYTES);
Unsafe.getUnsafe().putFloat(baseAddress + offset, value);
}
public final void putFloat(float value) {
checkLimits(Float.BYTES);
Unsafe.getUnsafe().putFloat(appendAddress, value);
appendAddress += Float.BYTES;
}
public void putInt(long offset, int value) {
checkLimits(offset, Integer.BYTES);
Unsafe.getUnsafe().putInt(baseAddress + offset, value);
}
public final void putInt(int value) {
checkLimits(Integer.BYTES);
Unsafe.getUnsafe().putInt(appendAddress, value);
appendAddress += Integer.BYTES;
}
public void putLong(long offset, long value) {
checkLimits(offset, Long.BYTES);
putLongUnsafe(offset, value);
}
public final void putLong(long value) {
checkLimits(8);
Unsafe.getUnsafe().putLong(appendAddress, value);
appendAddress += Long.BYTES;
}
public final void putLong128(long l1, long l2) {
checkLimits(16);
Unsafe.getUnsafe().putLong(appendAddress, l1);
Unsafe.getUnsafe().putLong(appendAddress + 8, l2);
appendAddress += 16;
}
public void putLong256(long offset, Long256 value) {
putLong256(
offset,
value.getLong0(),
value.getLong1(),
value.getLong2(),
value.getLong3());
}
public void putLong256(long offset, long l0, long l1, long l2, long l3) {
putLong(offset, l0);
putLong(offset + Long.BYTES, l1);
putLong(offset + Long.BYTES * 2, l2);
putLong(offset + Long.BYTES * 3, l3);
}
public final void putLong256(long l0, long l1, long l2, long l3) {
putLong(l0);
putLong(l1);
putLong(l2);
putLong(l3);
}
public final void putLong256(Long256 value) {
putLong256(
value.getLong0(),
value.getLong1(),
value.getLong2(),
value.getLong3());
}
public final void putLong256(CharSequence hexString) {
inPageLong256Decoder.putLong256(hexString);
}
public final void putLong256(@NotNull CharSequence hexString, int start, int end) {
inPageLong256Decoder.putLong256(hexString, start, end);
}
public final long putNullBin() {
final long offset = getAppendOffset();
putLong(TableUtils.NULL_LEN);
return offset;
}
public final long putNullStr() {
final long offset = getAppendOffset();
putInt(TableUtils.NULL_LEN);
return offset;
}
public final void putNullStr(long offset) {
putInt(offset, TableUtils.NULL_LEN);
}
public void putShort(long offset, short value) {
checkLimits(offset, 2);
Unsafe.getUnsafe().putShort(baseAddress + offset, value);
}
public final void putShort(short value) {
checkLimits(2);
Unsafe.getUnsafe().putShort(appendAddress, value);
appendAddress += 2;
}
public final long putStr(CharSequence value) {
return value != null ? putStr0(value, 0, value.length()) : putNullStr();
}
public final long putStr(char value) {
if (value != 0) {
checkLimits(6);
final long offset = getAppendOffset();
putInt(1);
Unsafe.getUnsafe().putChar(appendAddress, value);
appendAddress += Character.BYTES;
return offset;
}
return putNullStr();
}
public final long putStr(CharSequence value, int pos, int len) {
if (value != null) {
return putStr0(value, pos, len);
}
return putNullStr();
}
public void putStr(long offset, CharSequence value) {
if (value != null) {
putStr(offset, value, 0, value.length());
} else {
putNullStr(offset);
}
}
public void putStr(long offset, CharSequence value, int pos, int len) {
checkLimits(offset, VmUtils.getStorageLength(len));
putInt(offset, len);
copyStrChars(value, pos, len, baseAddress + offset + VmUtils.STRING_LENGTH_BYTES);
}
public void putLongUnsafe(long offset, long value) {
Unsafe.getUnsafe().putLong(baseAddress + offset, value);
}
public void replacePage(long address, long size) {
long appendOffset = getAppendOffset();
this.baseAddress = this.appendAddress = address;
this.baseAddressHi = baseAddress + size;
jumpTo(appendOffset);
}
public long resize(long size) {
checkAndExtend(baseAddress + size);
return baseAddress;
}
/**
* 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) {
checkLimits(bytes);
appendAddress += bytes;
}
public void zero() {
long baseLength = baseAddressHi - baseAddress;
Vect.memset(baseAddress, baseLength, 0);
}
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);
}
}
private void checkAndExtend(long addressHi) {
assert appendAddress <= baseAddressHi;
assert addressHi >= baseAddress;
if (addressHi <= baseAddressHi) {
return;
}
grow(addressHi - baseAddress);
}
protected final void checkLimits(long size) {
checkAndExtend(appendAddress + size);
}
protected final void checkLimits(long offset, long size) {
checkAndExtend(baseAddress + offset + size);
}
protected long getMapPageSize() {
return pageSize;
}
protected final long getMemorySize() {
return baseAddressHi - baseAddress;
}
protected final void handleMemoryReallocation(long newBaseAddress, long newSize) {
assert newBaseAddress != 0;
long appendOffset = appendAddress - baseAddress;
baseAddress = newBaseAddress;
baseAddressHi = baseAddress + newSize;
appendAddress = baseAddress + appendOffset;
if (appendAddress > baseAddressHi) {
appendAddress = baseAddressHi;
}
}
protected final void handleMemoryReleased() {
baseAddress = 0;
baseAddressHi = 0;
appendAddress = 0;
}
private void putLong256Null() {
checkLimits(32);
Long256Impl.putNull(appendAddress);
}
private long putStr0(CharSequence value, int pos, int len) {
final long storageLen = VmUtils.getStorageLength(len);
checkLimits(storageLen);
final long offset = getAppendOffset();
Unsafe.getUnsafe().putInt(appendAddress, len);
copyStrChars(value, pos, len, appendAddress + Integer.BYTES);
appendAddress += storageLen;
return offset;
}
protected long reallocateMemory(long currentBaseAddress, long currentSize, long newSize) {
if (currentBaseAddress != 0) {
return Unsafe.realloc(currentBaseAddress, currentSize, newSize);
}
return Unsafe.malloc(newSize);
}
protected void releaseMemory() {
if (baseAddress != 0) {
long baseLength = baseAddressHi - baseAddress;
Unsafe.free(baseAddress, baseLength);
handleMemoryReleased();
}
}
protected final void setPageSize(long pageSize) {
this.pageSize = Numbers.ceilPow2(pageSize);
this.pageSizeMsb = Numbers.msb(this.pageSize);
}
public class CharSequenceView extends AbstractCharSequence {
private int len;
private long offset;
@Override
public int length() {
return len;
}
@Override
public char charAt(int index) {
return ContiguousVirtualMemory.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;
@Override
public byte byteAt(long index) {
return getByte(offset + index);
}
@Override
public void copyTo(long address, final long start, final long length) {
long bytesRemaining = Math.min(length, this.len - start);
long offset = this.offset + start;
Vect.memcpy(baseAddress + offset, address, bytesRemaining);
}
@Override
public long length() {
return len;
}
ByteSequenceView of(long offset, long len) {
this.offset = offset;
this.len = len;
return this;
}
}
private class InPageLong256FromCharSequenceDecoder extends Long256FromCharSequenceDecoder {
@Override
public void onDecoded(long l0, long l1, long l2, long l3) {
checkLimits(Long256.BYTES);
Unsafe.getUnsafe().putLong(appendAddress, l0);
Unsafe.getUnsafe().putLong(appendAddress + 8, l1);
Unsafe.getUnsafe().putLong(appendAddress + 16, l2);
Unsafe.getUnsafe().putLong(appendAddress + 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(']');
}
appendAddress += Long256.BYTES;
}
private void putLong256(CharSequence hexString) {
final int len;
if (hexString == null || (len = hexString.length()) == 0) {
putLong256Null();
appendAddress += Long256.BYTES;
} else {
putLong256(hexString, 2, len);
}
}
}
}