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

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

The newest version!
/*
 * Copyright (c) 2016-2022 chronicle.software
 *
 *     https://chronicle.software
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.openhft.chronicle.bytes.internal;

import net.openhft.chronicle.bytes.*;
import net.openhft.chronicle.bytes.util.DecoratedBufferOverflowException;
import net.openhft.chronicle.bytes.util.DecoratedBufferUnderflowException;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.Memory;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.annotation.NonNegative;
import net.openhft.chronicle.core.io.ClosedIllegalStateException;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.ThreadingIllegalStateException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;

import static net.openhft.chronicle.core.Jvm.uncheckedCast;
import static net.openhft.chronicle.core.util.Ints.requireNonNegative;
import static net.openhft.chronicle.core.util.Longs.requireNonNegative;
import static net.openhft.chronicle.core.util.ObjectUtils.requireNonNull;

/**
 * Bytes to wrap memory mapped data.
 * 

* NOTE These Bytes are single Threaded as are all Bytes. */ @SuppressWarnings("rawtypes") public class ChunkedMappedBytes extends CommonMappedBytes { // assume the mapped file is reserved already. public ChunkedMappedBytes(@NotNull final MappedFile mappedFile) throws ClosedIllegalStateException, ThreadingIllegalStateException { this(mappedFile, ""); } protected ChunkedMappedBytes(@NotNull final MappedFile mappedFile, final String name) throws ClosedIllegalStateException, ThreadingIllegalStateException { super(mappedFile, name); } @Override public @NotNull ChunkedMappedBytes write(@NonNegative final long offsetInRDO, final byte[] byteArray, @NonNegative int offset, @NonNegative final int length) throws ClosedIllegalStateException, BufferOverflowException, ThreadingIllegalStateException { requireNonNegative(offsetInRDO); requireNonNull(byteArray); requireNonNegative(offset); requireNonNegative(length); throwExceptionIfClosed(); long wp = offsetInRDO; if ((length + offset) > byteArray.length) throw new ArrayIndexOutOfBoundsException("bytes.length=" + byteArray.length + ", " + "length=" + length + ", offset=" + offset); if (length > writeRemaining()) throw new DecoratedBufferOverflowException( String.format("write failed. Length: %d > writeRemaining: %d", length, writeRemaining())); int remaining = length; MappedBytesStore bytesStore = acquireNextByteStore(wp, false); while (remaining > 0) { long safeCopySize = copySize(wp); if (safeCopySize + mappedFile.overlapSize() >= remaining) { bytesStore.write(wp, byteArray, offset, remaining); return this; } bytesStore.write(wp, byteArray, offset, (int) safeCopySize); offset += (int) safeCopySize; wp += safeCopySize; remaining -= (int) safeCopySize; // move to the next chunk bytesStore = acquireNextByteStore0(wp, false); } return this; } @Override public @NotNull ChunkedMappedBytes write(@NonNegative final long writeOffset, @NotNull final RandomDataInput bytes, @NonNegative long readOffset, @NonNegative final long length) throws BufferOverflowException, BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNegative(writeOffset); ReferenceCountedUtil.throwExceptionIfReleased(bytes); requireNonNegative(readOffset); requireNonNegative(length); throwExceptionIfClosed(); long wp = writeOffset; if (length > writeRemaining()) throw new DecoratedBufferOverflowException( String.format("write failed. Length: %d > writeRemaining: %d", length, writeRemaining())); long remaining = length; MappedBytesStore bytesStore = acquireNextByteStore(wp, false); while (remaining > 0) { long safeCopySize = copySize(wp); if (safeCopySize + mappedFile.overlapSize() >= remaining) { bytesStore.write(wp, bytes, readOffset, remaining); return this; } bytesStore.write(wp, bytes, readOffset, safeCopySize); readOffset += safeCopySize; wp += safeCopySize; remaining -= safeCopySize; // move to the next chunk bytesStore = acquireNextByteStore0(wp, false); } return this; } private long copySize(@NonNegative final long writePosition) { long size = mappedFile.chunkSize(); return size - writePosition % size; } @NotNull @Override public Bytes readPositionRemaining(@NonNegative final long position, @NonNegative final long remaining) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { final long limit = position + remaining; acquireNextByteStore(position, true); if (writeLimit < limit) writeLimit(limit); if (Jvm.isAssertEnabled()) readLimit(limit); else uncheckedWritePosition(limit); return readPosition(position); } @NotNull @Override public Bytes readPosition(@NonNegative final long position) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { if (bytesStore.inside(position)) { return super.readPosition(position); } else { acquireNextByteStore0(position, true); return this; } } /** * This single-argument version of the call returns an address which is guaranteed safe for a contiguous * read up to the overlap size. *

* NOTE: If called with an offset which is already in the overlap region this call will therefore * prompt a remapping to the new segment, which in turn may unmap the current segment. * Any other handles using data in the current segment may therefore result in a memory violation * when next used. *

* If manipulating offsets which may reside in the overlap region, always use the 2-argument version below */ @Override public long addressForRead(@NonNegative final long offset) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNegative(offset); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset)) bytesStore = acquireNextByteStore0(offset, true); return bytesStore.addressForRead(offset); } /** * This two-argument version of the call returns an address which is guaranteed safe for a contiguous * read up to the requested buffer size. *

* NOTE: In contrast to the single-argument version this call will not prompt a remapping if * called within the overlap region (provided the full extent remains in the overlap region) *

* This version is therefore safe to use cooperatively with other handles in a defined sequence * of bytes (eg returned from a DocumentContext) regardless of whether the handles span the * overlap region */ @Override public long addressForRead(@NonNegative final long offset, @NonNegative final int bufferSize) throws UnsupportedOperationException, BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset, bufferSize)) bytesStore = acquireNextByteStore0(offset, true); return bytesStore.addressForRead(offset); } @Override public long addressForWrite(@NonNegative final long offset) throws UnsupportedOperationException, BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNegative(offset); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset)) { bytesStore = acquireNextByteStore0(offset, true); } return bytesStore.addressForWrite(offset); } @Override protected void readCheckOffset(@NonNegative final long offset, final long adding, final boolean given) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { final long check = adding >= 0 ? offset : offset + adding; BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(check, adding)) { acquireNextByteStore0(offset, false); } super.readCheckOffset(offset, adding, given); } @Override protected void writeCheckOffset(final @NonNegative long offset, final @NonNegative long adding) throws BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); if (offset + adding < start() || offset > mappedFile.capacity() - adding) throw writeBufferOverflowException0(offset); BytesStore bytesStore = this.bytesStore; if (adding > 0 && !bytesStore.inside(offset, checkSize0(adding))) { acquireNextByteStore0(offset, false); if (!this.bytesStore.inside(offset, checkSize0(adding))) throw new DecoratedBufferUnderflowException(String.format("Acquired the next BytesStore, but still not room to add %d when realCapacity %d", adding, this.bytesStore.realCapacity())); } } private long checkSize0(long adding) { if (adding < 0 || adding > MAX_CAPACITY) throw new IllegalArgumentException("Invalid size " + adding); return adding; } @Override public void ensureCapacity(@NonNegative final long desiredCapacity) throws IllegalArgumentException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); // TODO: should not accept desiredCapacity == 0 BytesStore bytesStore = this.bytesStore; if (desiredCapacity > capacity()) throw new DecoratedBufferOverflowException("Cannot extend capacity beyond " + capacity()); // we deliberately check writePosition here - the javadoc of this method explicitly references // growing an elastic Bytes and it feels like the least surprising behaviour is to do this if (desiredCapacity > writePosition()) { long adding = desiredCapacity - writePosition(); if (!bytesStore.inside(writePosition(), checkSize0(adding))) { acquireNextByteStore0(writePosition() + adding, false); } } } @Override public @NotNull Bytes writeSkip(long bytesToSkip) throws BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { // only check up to 128 bytes are real. writeCheckOffset(writePosition(), Math.min(128, bytesToSkip)); // the rest can be lazily allocated. uncheckedWritePosition(writePosition() + bytesToSkip); return this; } @NotNull private BufferOverflowException writeBufferOverflowException0(final long offset) { BufferOverflowException exception = new BufferOverflowException(); exception.initCause(new IllegalArgumentException("Offset out of bound " + offset)); return exception; } private @NotNull MappedBytesStore acquireNextByteStore(final long offset, final boolean set) throws ClosedIllegalStateException, ThreadingIllegalStateException { // if in the same chunk, can continue even if closed, but not released. BytesStore bytesStore = this.bytesStore; if (bytesStore.inside(offset)) return (MappedBytesStore) bytesStore; // not allowed if closed. throwExceptionIfReleased(); return acquireNextByteStore0(offset, set); } // DON'T call this directly. // TODO Check whether we need synchronized; original comment; require protection from concurrent mutation to bytesStore field private synchronized @NotNull MappedBytesStore acquireNextByteStore0(@NonNegative final long offset, final boolean set) throws ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); @Nullable final BytesStore oldBS = this.bytesStore; @NotNull final MappedBytesStore newBS; try { newBS = mappedFile.acquireByteStore(this, offset, oldBS); if (newBS != oldBS) { this.bytesStore(uncheckedCast(newBS)); if (oldBS != null) oldBS.release(this); if (lastActualSize < newBS.maximumLimit) lastActualSize = newBS.maximumLimit; } } catch (@NotNull IOException e) { throw new IORuntimeException(String.format("Failed to acquireByteStore start: 0x%X offset: 0x%X safeLimit: 0x%X", start(), offset, safeLimit()), e); } if (set) { if (writeLimit() < readPosition) writeLimit(readPosition); if (readLimit() < readPosition) readLimit(readPosition); readPosition = offset; } return newBS; } @NotNull @Override public Bytes readSkip(final long bytesToSkip) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { if (bytesToSkip == 0) return this; if (readPosition + bytesToSkip > readLimit()) throw new BufferUnderflowException(); long check = bytesToSkip >= 0 ? this.readPosition : this.readPosition + bytesToSkip; BytesStore bytesStore = this.bytesStore; if (bytesToSkip != (int) bytesToSkip || !bytesStore.inside(readPosition, (int) bytesToSkip)) { acquireNextByteStore0(check, false); } this.readPosition += bytesToSkip; return this; } @NotNull @Override public Bytes clear() throws ClosedIllegalStateException { readPosition = 0L; uncheckedWritePosition(0L); writeLimit = mappedFile.capacity(); if (writeLimit == 16843020) throw new AssertionError(); return this; } @Override public boolean isElastic() { return true; } @NotNull @Override public Bytes write(@NotNull final BytesStore bytes, @NonNegative final long offset, @NonNegative final long length) throws BufferUnderflowException, BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNull(bytes); requireNonNegative(offset); requireNonNegative(length); throwExceptionIfClosed(); if (length == 8) { writeLong(bytes.readLong(offset)); } else if (length > 0) { if (bytes.isDirectMemory()) { // need to check this to pull in the right bytesStore() long fromAddress = bytes.addressForRead(offset); if (length <= bytes.bytesStore().realCapacity() - offset) { this.acquireNextByteStore(writePosition(), false); // can we do a direct copy of raw memory? if (bytesStore.realCapacity() - writePosition() >= length) { rawCopy(length, fromAddress); return this; } } } BytesInternal.writeFully(bytes, offset, length, this); } return this; } void rawCopy(@NonNegative final long length, final long fromAddress) throws BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { this.throwExceptionIfReleased(); final long offset = writePosition(); writeCheckOffset(offset, length); long address = bytesStore.addressForWrite(offset); OS.memory().copyMemory(fromAddress, address, length); uncheckedWritePosition(writePosition() + length); } @Override public byte readVolatileByte(@NonNegative long offset) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset, Byte.BYTES)) { bytesStore = acquireNextByteStore0(offset, false); } return bytesStore.readVolatileByte(offset); } @Override public short readVolatileShort(@NonNegative long offset) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset, Short.BYTES)) { bytesStore = acquireNextByteStore0(offset, false); } return bytesStore.readVolatileShort(offset); } @Override public int readVolatileInt(@NonNegative long offset) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset, Integer.BYTES)) { bytesStore = acquireNextByteStore0(offset, false); } return bytesStore.readVolatileInt(offset); } @Override public long readVolatileLong(@NonNegative long offset) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset, Long.BYTES)) { bytesStore = acquireNextByteStore0(offset, false); } return bytesStore.readVolatileLong(offset); } @Override public int peekUnsignedByte() throws ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(readPosition, Byte.BYTES)) { bytesStore = acquireNextByteStore0(readPosition, false); } try { return readPosition >= writePosition() ? -1 : bytesStore.readUnsignedByte(readPosition); } catch (BufferUnderflowException e) { return -1; } } @Override public int peekUnsignedByte(@NonNegative final long offset) throws BufferUnderflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(offset, Byte.BYTES)) { bytesStore = acquireNextByteStore0(offset, false); } return offset < start() || readLimit() <= offset ? -1 : bytesStore.peekUnsignedByte(offset); } @SuppressWarnings("restriction") @Override public int peekVolatileInt() throws ClosedIllegalStateException, ThreadingIllegalStateException { BytesStore bytesStore = this.bytesStore; if (!bytesStore.inside(readPosition, Integer.BYTES)) { bytesStore = acquireNextByteStore0(readPosition, true); } MappedBytesStore mbs = (MappedBytesStore) bytesStore; long address = mbs.address + mbs.translate(readPosition); @Nullable Memory memory = mbs.memory; return memory.readVolatileInt(address); } @NotNull @Override public Bytes appendUtf8(char[] chars, @NonNegative int offset, @NonNegative int length) throws BufferOverflowException, IllegalArgumentException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNull(chars); throwExceptionIfClosed(); if (writePosition() < 0 || writePosition() > capacity() - 1L + length) throw writeBufferOverflowException0(writePosition()); int i; ascii: { for (i = 0; i < length; i++) { char c = chars[offset + i]; if (c > 0x007F) break ascii; long oldPosition = writePosition(); BytesStore bytesStore = this.bytesStore; if ((writePosition() & 0xff) == 0 && !bytesStore.inside(writePosition(), (length - i) * 3L)) { bytesStore = acquireNextByteStore0(writePosition(), false); } uncheckedWritePosition(writePosition() + 1); bytesStore.writeByte(oldPosition, (byte) c); } return this; } for (; i < length; i++) { char c = chars[offset + i]; BytesInternal.appendUtf8Char(this, c); } return this; } // used by the Pretoucher, don't change this without considering the impact. @Override public boolean compareAndSwapLong(@NonNegative long offset, long expected, long value) throws BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfClosed(); if (offset < 0 || offset > mappedFile.capacity() - 8L) throw writeBufferOverflowException0(offset); // this is correct that it uses the maximumLimit, yes it is different from the method above. BytesStore bytesStore = this.bytesStore; if (bytesStore.start() > offset || offset + 8L > bytesStore.safeLimit()) { bytesStore = acquireNextByteStore0(offset, false); } return bytesStore.compareAndSwapLong(offset, expected, value); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy