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

net.openhft.chronicle.bytes.Bytes 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;

import net.openhft.chronicle.bytes.internal.BytesInternal;
import net.openhft.chronicle.bytes.internal.EmbeddedBytes;
import net.openhft.chronicle.bytes.util.DecoratedBufferOverflowException;
import net.openhft.chronicle.core.annotation.NonNegative;
import net.openhft.chronicle.core.annotation.SingleThreaded;
import net.openhft.chronicle.core.annotation.UsedViaReflection;
import net.openhft.chronicle.core.io.*;
import net.openhft.chronicle.core.util.ObjectUtils;
import net.openhft.chronicle.core.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.nio.charset.StandardCharsets;

import static net.openhft.chronicle.bytes.internal.ReferenceCountedUtil.throwExceptionIfReleased;
import static net.openhft.chronicle.core.util.Longs.requireNonNegative;
import static net.openhft.chronicle.core.util.ObjectUtils.requireNonNull;

/**
 * The {@code Bytes} class is a versatile container for raw byte data, providing rich functionality to read and write
 * data in various formats including integers, longs, floating-point values, strings, and more. It also supports
 * advanced operations such as seeking, slicing, and copying.
 * 

* This class serves as an abstraction layer over a ByteBuffer, byte[], POJO, or native memory * *

* Besides basic primitive types, {@code Bytes} can serialize and deserialize more complex structures, * including custom user-defined objects that implement {@code ReadBytesMarshallable} or {@code WriteBytesMarshallable}. * *

* {@code Bytes} can be used for a variety of purposes such as: *

    *
  • Reading and writing binary data to and from files
  • *
  • Bytes level serialization and deserialization of objects
  • *
  • Performing low-level binary manipulation for networking applications
  • *
  • Temporary storage (in-memory) of binary data
  • *
  • Shared memory in memory mapped files
  • *
* *

* {@code Bytes} is essentially a view of a region within a {@link BytesStore} and is equipped with separate read and * write cursors, boasting a 63-bit addressing capacity (approximately 8 EiB), as opposed to Java's built-in * {@link ByteBuffer}, which only supports 31-bit addressing with a single cursor. * *

* A {@code Bytes} instance can represent either a fixed region of memory or an elastic buffer that dynamically * resizes as needed. It is mutable and designed for single-threaded use as it is not thread-safe. * *

* The cursors within Bytes consist of a write-position and a read-position, which must adhere to these constraints: *

    *
  • {@code start() <= readPosition() <= writePosition() <= writeLimit() <= capacity()}
  • *
  • {@code readLimit() == writePosition() && readPosition() <= safeLimit()}
  • *
*

* It is important to note that a Bytes object is a resource that is ReferenceCounted. Operations on a Bytes * object that has been released may result in an {@link IllegalStateException}. *

* Note: Some operations on {@code Bytes} may throw {@code IllegalStateException} if the underlying storage has been released. * Always ensure proper release of resources when finished with a {@code Bytes} instance, especially if it's backed by system resources * like file handles or network sockets. * * @param The type of the {@link BytesStore} that backs this {@code Bytes} instance. * @see BytesStore * @see ReadBytesMarshallable * @see WriteBytesMarshallable */ @SingleThreaded @SuppressWarnings({"rawtypes", "unchecked"}) public interface Bytes extends BytesStore, U>, BytesIn, BytesOut, SingleThreadedChecked { /** * The maximum capacity a Bytes object can have, approximately 8 EiB (Exbibytes) minus 16 bytes. */ long MAX_CAPACITY = Long.MAX_VALUE & ~0xF; /** * The maximum capacity a Bytes object can have if it is allocated on the heap, which is 2 GiB (Gibibytes) minus 16 bytes. */ int MAX_HEAP_CAPACITY = Integer.MAX_VALUE & ~0xF; /** * The default initial size of an elastic Bytes object backed by a ByteBuffer, which is 256 bytes. */ int DEFAULT_BYTE_BUFFER_CAPACITY = 256; /** * Creates and returns a new elastic wrapper for a direct (off-heap) ByteBuffer with a default capacity, * which can be resized as needed. The default capacity is determined by the value of {@code DEFAULT_BYTE_BUFFER_CAPACITY}. *

* The memory for the returned Bytes object is allocated off-heap, meaning it's not under the JVM's garbage collector's * immediate purview. It's crucial to conscientiously release these resources when they're no longer required to ensure * timely memory deallocation. If these resources are not manually released, they will still eventually be reclaimed by * the garbage collector, but possibly with a delay. * * @return a new elastic wrapper for an off-heap ByteBuffer with default capacity */ @NotNull static Bytes elasticByteBuffer() { return elasticByteBuffer(DEFAULT_BYTE_BUFFER_CAPACITY); } /** * Creates and returns a new elastic wrapper for a direct (off-heap) ByteBuffer with * the given {@code initialCapacity} which will be resized as required. *

* The memory for the returned Bytes object is allocated off-heap, meaning it's not under the JVM's garbage collector's * immediate purview. It's crucial to conscientiously release these resources when they're no longer required to ensure * timely memory deallocation. If these resources are not manually released, they will still eventually be reclaimed by * the garbage collector, but possibly with a delay. * * @param initialCapacity the initial non-negative capacity given in bytes * @return a new elastic wrapper for an off-heap ByteBuffer with the specified initial capacity * @throws IllegalArgumentException if the provided {@code initialCapacity} is negative. */ @NotNull static Bytes elasticByteBuffer(@NonNegative int initialCapacity) { return elasticByteBuffer(initialCapacity, MAX_HEAP_CAPACITY); } /** * Creates and returns a new elastic wrapper for a direct (off-heap) ByteBuffer with * the given {@code initialCapacity} which will be resized as required up * to the given {@code maxCapacity}. *

* The memory for the returned Bytes object is allocated off-heap, meaning it's not under the JVM's garbage collector's * immediate purview. It's crucial to conscientiously release these resources when they're no longer required to ensure * timely memory deallocation. If these resources are not manually released, they will still eventually be reclaimed by * the garbage collector, but possibly with a delay. *

* This method allows for fine-tuned control over the buffer's capacity, enabling performance optimization * and resource management. * * @param initialCapacity the initial non-negative capacity given in bytes * @param maxCapacity the maximum capacity given in bytes; must be non-negative and at least as large as {@code initialCapacity} * @return a new elastic wrapper for an off-heap ByteBuffer with the specified initial capacity and maximum capacity * @throws IllegalArgumentException if the provided {@code initialCapacity} or {@code maxCapacity} is negative, * or if {@code maxCapacity} is less than {@code initialCapacity}. */ @NotNull static Bytes elasticByteBuffer(@NonNegative final int initialCapacity, @NonNegative final int maxCapacity) { requireNonNegative(initialCapacity); requireNonNegative(maxCapacity); @NotNull BytesStore bs = BytesStore.elasticByteBuffer(initialCapacity, maxCapacity); try { return bs.bytesForWrite(); } finally { bs.release(ReferenceOwner.INIT); } } /** * Creates and returns a new elastic wrapper for a heap ByteBuffer, with the provided {@code initialCapacity}, * that can dynamically resize as required. *

* The returned Bytes object utilizes heap memory, which is managed by the JVM's garbage collector. * This is suitable for scenarios where the buffer size may change, and the application requires * the flexibility of automatic memory management. *

* Please note that, since this is heap memory, the garbage collector will manage the memory deallocation. * * @param initialCapacity the initial capacity of the buffer in bytes; must be non-negative * @return a new elastic wrapper for a heap ByteBuffer * @throws IllegalArgumentException if the provided {@code initialCapacity} is negative */ @NotNull static Bytes elasticHeapByteBuffer(@NonNegative int initialCapacity) { requireNonNegative(initialCapacity); @NotNull BytesStore bs = BytesStore.wrap(ByteBuffer.allocate(initialCapacity)); try { return NativeBytes.wrapWithNativeBytes(bs, Bytes.MAX_HEAP_CAPACITY); } finally { bs.release(INIT); } } /** * Creates and returns a new elastic wrapper for a heap ByteBuffer with a default {@code initialCapacity} * of 128 bytes, that can dynamically resize as required. *

* This is a convenience method that behaves similarly to {@code elasticHeapByteBuffer(int initialCapacity)}, * but with a preset default initial capacity. *

* The returned Bytes object utilizes heap memory, which is managed by the JVM's garbage collector. * * @return a new elastic wrapper for a heap ByteBuffer with default initial capacity of 128 bytes */ @NotNull static Bytes elasticHeapByteBuffer() { return elasticHeapByteBuffer(128); } /** * Creates and returns a new Bytes view that maps to a group of fields within the provided {@code object}. * The group of fields is identified by the {@link FieldGroup} annotation with a name specified by {@code groupName}. * This method effectively turns the group of fields into a contiguous memory segment that can be accessed and manipulated * as a single entity using the returned Bytes object. * *

Example of using {@link FieldGroup} to group fields: *

{@code
     *     static class Padding extends Parent {
     *         {@literal @}FieldGroup("p")
     *         // 128 bytes
     *         transient long p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15;
     *     }
     * }
* *

Example of creating a Bytes view for a field group: *

{@code
     *     Padding example = new Padding();
     *     Bytes bytes = Bytes.forFieldGroup(example, "p");
     * }
*

Note: The data in the resulting Bytes view is preceded by the length of the data encoded as an unsigned byte. * This consumes one byte of storage and limits the maximum size of the data in the view to 255 usable bytes. * * @param The type of the underlying object * @param object The non-null object containing the fields to be mapped * @param groupName The non-null name of the field group as specified in {@link FieldGroup} * @return A new Bytes view mapping to the specified field group within the provided object * @throws NullPointerException If the provided {@code object} or the provided {@code groupName} is {@code null} */ static Bytes forFieldGroup(@NotNull final T object, @NotNull final String groupName) { requireNonNull(object); requireNonNull(groupName); @NotNull BytesStore bs = BytesStore.forFields(object, groupName, 1); try { final EmbeddedBytes bytes = EmbeddedBytes.wrap(bs); return bytes.writeLimit(bs.writeLimit()); } finally { bs.release(INIT); } } /** * Creates and returns a new Bytes instance that wraps the provided {@code byteBuffer}. * The returned Bytes instance is configured for reading from the current position of the * {@code byteBuffer} up to its limit. * *

This method is provided for convenience. However, it's important to note that this method * may create intermediate objects, which might not be ideal for performance-sensitive applications * that require minimizing garbage creation. To avoid creating intermediate objects, consider using * a different approach as demonstrated in the example below. * * @param byteBuffer The ByteBuffer to wrap. Must be non-null and must not be read-only. * @return A new Bytes instance wrapping the provided {@code byteBuffer}, ready for reading. * @throws NullPointerException If the provided {@code byteBuffer} is {@code null}. * @throws ReadOnlyBufferException If the provided {@code byteBuffer} is read-only. * @throws BufferUnderflowException If there is not enough data in the buffer. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way. */ @NotNull static Bytes wrapForRead(@NotNull final ByteBuffer byteBuffer) throws ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNull(byteBuffer); BytesStore bs = BytesStore.wrap(byteBuffer); try { Bytes bytesForRead = bs.bytesForRead(); bytesForRead.readLimit(byteBuffer.limit()); bytesForRead.readPosition(byteBuffer.position()); return bytesForRead; } finally { bs.release(INIT); } } /** * Creates and returns a new Bytes instance that wraps the provided {@code byteBuffer}. * The returned Bytes instance is configured for writing, starting at the current position of the * {@code byteBuffer} up to its limit. * *

Important Note: When the returned Bytes instance is closed, any direct {@code byteBuffer} will be deallocated * and should no longer be used. * * @param byteBuffer The ByteBuffer to wrap. Must be non-null and must not be read-only. * @return A new Bytes instance wrapping the provided {@code byteBuffer}, ready for writing. * @throws NullPointerException If the provided {@code byteBuffer} is {@code null}. * @throws ReadOnlyBufferException If the provided {@code byteBuffer} is read-only. * @throws BufferOverflowException If there is not enough space in the buffer. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way. */ @NotNull static Bytes wrapForWrite(@NotNull final ByteBuffer byteBuffer) throws ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNull(byteBuffer); BytesStore bs = BytesStore.wrap(byteBuffer); try { Bytes bytesForWrite = bs.bytesForWrite(); bytesForWrite.writePosition(byteBuffer.position()); bytesForWrite.writeLimit(byteBuffer.limit()); return bytesForWrite; } finally { bs.release(INIT); } } /** * Creates and returns a new Bytes instance that wraps the provided {@code byteArray}. * The returned Bytes instance is configured for reading, starting from the beginning of the * {@code byteArray} up to its length. * *

Important Note: This method creates intermediate objects and might not be ideal for performance-sensitive applications * due to garbage creation. * * @param byteArray The byte array to wrap. Must be non-null. * @return A new Bytes instance wrapping the provided {@code byteArray}, ready for reading. * @throws NullPointerException If the provided {@code byteArray} is {@code null}. */ @NotNull static Bytes wrapForRead(byte[] byteArray) { requireNonNull(byteArray); @NotNull BytesStore bs = BytesStore.wrap(byteArray); try { return bs.bytesForRead(); } finally { bs.release(INIT); } } /** * Creates and returns a new Bytes instance that wraps the provided {@code byteArray} and * is ready for writing operations. * *

Important Note: This method is intended for convenience, but might not be optimal for performance-sensitive scenarios * as it creates intermediate objects which might lead to garbage collection overhead. * * @param byteArray The byte array to wrap for writing operations. Must be non-null. * @return A new Bytes instance wrapping the provided {@code byteArray}, ready for writing. * @throws NullPointerException If the provided {@code byteArray} is {@code null}. */ @NotNull static Bytes wrapForWrite(byte[] byteArray) { requireNonNull(byteArray); final BytesStore bs = BytesStore.wrap(byteArray); try { return bs.bytesForWrite(); } finally { bs.release(INIT); } } /** * Constructs and returns a new Bytes instance which contains the provided {@code text} encoded in ISO-8859-1. * The returned Bytes instance is ready for reading and is allocated on the heap. * * @param text The non-null text to be converted and wrapped in a Bytes instance. * @return A new Bytes instance containing the provided text encoded in ISO-8859-1. * @throws NullPointerException If the provided {@code text} is {@code null}. */ @NotNull static Bytes from(@NotNull CharSequence text) { requireNonNull(text); return from(text.toString()); } /** * Constructs and returns a new Bytes instance which contains the provided {@code text} encoded in ISO-8859-1. * The returned Bytes instance is ready for reading and is allocated using native memory. * * @param text The non-null text to be converted and wrapped in a Bytes instance. * @return A new Bytes instance containing the provided text encoded in ISO-8859-1. * @throws NullPointerException If the provided {@code text} is {@code null}. */ static Bytes fromDirect(@NotNull CharSequence text) { requireNonNull(text); return NativeBytes.nativeBytes(text.length()).append(text); } /** * Constructs and returns a new Bytes instance which contains the provided {@code text} encoded in ISO-8859-1. * The returned Bytes instance is ready for reading and is allocated using native memory. * * @param text The non-null text to be converted and wrapped in a Bytes instance. * @return A new Bytes instance containing the provided text encoded in ISO-8859-1. * @throws NullPointerException If the provided {@code text} is {@code null}. */ @NotNull static Bytes directFrom(@NotNull String text) { BytesStore from = BytesStore.from(text); try { return from.bytesForRead(); } finally { from.release(INIT); } } /** * Creates and returns a new object containing the given {@code text} encoded in ISO-8859-1 character set. *

* The returned {@code Bytes} object is allocated on the heap and is ready for reading. *

* The ISO-8859-1 encoding is used because it is a single-byte encoding that supports the first 256 Unicode characters, * making it well-suited for encoding texts that primarily consist of Western European characters. * * @param text A non-null String to be converted into bytes using ISO-8859-1 encoding and wrapped in a {@code Bytes} object. * @return A new {@code Bytes} object containing the text, encoded using the ISO-8859-1 character set. * @throws NullPointerException If the provided {@code text} is {@code null}. */ @NotNull static Bytes from(@NotNull String text) { return wrapForRead(text.getBytes(StandardCharsets.ISO_8859_1)); } /** * Creates and returns an empty, fixed-size, immutable object. * * @return An empty {@code Bytes} object with fixed size and immutable contents. */ static Bytes empty() { return BytesStore.empty().bytesForRead(); } /** * A reflection-friendly alias for the {@link #from(String)} method. *

* This method is functionally equivalent to {@link #from(String)} and is primarily intended for invocation through reflection mechanisms. * *

* It creates and returns a new object containing the given {@code text}, encoded in the ISO-8859-1 character set. * * @param text A non-null String to be converted into bytes using ISO-8859-1 encoding and wrapped in a {@code Bytes} object. * @return A new {@code Bytes} object containing the text, encoded using the ISO-8859-1 character set. * @throws NullPointerException If the provided {@code text} is {@code null}. */ @UsedViaReflection static Bytes valueOf(String text) { return from(text); } /** * Creates and returns a new {@link VanillaBytes} object that is a fixed-size wrapper for native memory * (accessible via 64-bit addresses) with the specified {@code capacity}. *

* This method is used to allocate a fixed amount of native memory, which can be useful for performance-sensitive applications. * * @param capacity The non-negative capacity of the native memory to be allocated, measured in bytes. * @return A new {@code VanillaBytes} object that wraps the allocated native memory. * @throws IllegalArgumentException If the provided {@code capacity} is negative. */ @NotNull static VanillaBytes allocateDirect(@NonNegative long capacity) throws IllegalArgumentException { @NotNull BytesStore bs = BytesStore.nativeStoreWithFixedCapacity(requireNonNegative(capacity)); try { return new NativeBytes<>(bs); } finally { bs.release(INIT); } } /** * Creates and returns a new elastic wrapper for native memory (accessible via 64-bit addresses) * with an initial capacity of zero bytes. The capacity of the wrapper will be automatically * resized as needed. *

* Native memory allocation can be faster and is not subject to Java's garbage collection, * making it suitable for performance-sensitive applications. * * @return A new {@code NativeBytes} object representing an elastic wrapper for native memory. */ @NotNull static NativeBytes allocateElasticDirect() { return NativeBytes.nativeBytes(); } /** * Creates and returns a new elastic wrapper for native memory (accessible via 64-bit addresses) * with the given {@code initialCapacity}. The capacity of the wrapper will be automatically * resized as needed. *

* Native memory allocation can be faster and is not subject to Java's garbage collection, * making it suitable for performance-sensitive applications. * * @param initialCapacity The initial non-negative capacity of the wrapper in bytes. * @return A new {@code NativeBytes} object representing an elastic wrapper for native memory. * @throws IllegalArgumentException If the provided {@code initialCapacity} is negative. */ @NotNull static NativeBytes allocateElasticDirect(@NonNegative long initialCapacity) throws IllegalArgumentException { return NativeBytes.nativeBytes(requireNonNegative(initialCapacity)); } /** * Creates and returns a new elastic wrapper for memory allocated on the heap, * with an initial capacity of 32 bytes. The capacity of the wrapper will be * automatically resized as needed. *

* Heap memory allocation is managed by the Java Virtual Machine and is subject * to garbage collection. * * @return A new {@code OnHeapBytes} object representing an elastic wrapper for heap memory. */ @NotNull static OnHeapBytes allocateElasticOnHeap() { return allocateElasticOnHeap(32); } /** * Creates and returns a new elastic wrapper for memory allocated on the heap, * with the specified {@code initialCapacity}. The capacity of the wrapper will * be automatically resized as needed. *

* Heap memory allocation is managed by the Java Virtual Machine and is subject * to garbage collection. * * @param initialCapacity The initial non-negative capacity of the wrapper in bytes. * @return A new {@code OnHeapBytes} object representing an elastic wrapper for heap memory. * @throws IllegalArgumentException If the provided {@code initialCapacity} is negative. */ @NotNull static OnHeapBytes allocateElasticOnHeap(@NonNegative int initialCapacity) { requireNonNegative(initialCapacity); BytesStore wrap = BytesStore.wrap(new byte[initialCapacity]); try { return new OnHeapBytes(wrap, true); } finally { wrap.release(INIT); } } /** * Creates and returns a substring from the provided {@code buffer} between its current * {@code readPosition} and {@code readLimit}. * The buffer's state remains unchanged by this method. *

* If the length of the extracted substring is greater than {@code maxLen}, * an ellipsis "..." is appended to the resulting string. Note that in this case, the * length of the returned string will be {@code maxLen + 3}. * * @param buffer The buffer to extract the string from. Must not be {@code null}. * @return A string extracted from the buffer, possibly truncated and appended with an ellipsis. * @throws BufferUnderflowException If there is not enough data in the buffer. * @throws IllegalArgumentException If {@code maxLen} is negative. * @throws NullPointerException If the provided {@code buffer} is {@code null}. */ @NotNull static String toString(@NotNull final Bytes buffer) throws BufferUnderflowException, IllegalArgumentException { return toString(buffer, MAX_HEAP_CAPACITY); } /** * Creates and returns a substring from the provided {@code buffer} between its current * {@code readPosition} and {@code readLimit}, truncated to the specified maximum length. * The buffer's state remains unchanged by this method. *

* If the length of the extracted substring is greater than {@code maxLen}, * an ellipsis "..." is appended to the resulting string. Note that in this case, the * length of the returned string will be {@code maxLen + 3}. * * @param buffer The buffer to extract the string from. Must not be {@code null}. * @param maxLen The maximum length of the string to be extracted. * @return A string extracted from the buffer, possibly truncated and appended with an ellipsis. * @throws BufferUnderflowException If there is not enough data in the buffer. * @throws IllegalArgumentException If {@code maxLen} is negative. * @throws NullPointerException If the provided {@code buffer} is {@code null}. */ @NotNull static String toString(@NotNull final Bytes buffer, @NonNegative final long maxLen) throws BufferUnderflowException, IllegalArgumentException { return toString(buffer, buffer.readPosition(), Math.min(maxLen, buffer.readRemaining())); } /** *

Extracts a string from the provided {@code buffer} starting at the specified {@code position}, * and spanning for the specified {@code length} number of characters. The buffer's state * remains unchanged by this method. * *

The method reads {@code length} bytes from the {@code buffer}, starting at {@code position}, * and constructs a string from these bytes. * *

This method supports all characters in the Basic Latin Unicode block, but does not handle the * upper half of ISO-8859-1. For strings using the upper block of ISO-8859-1, use {@link #to8bitString()}

* * @param buffer The buffer to extract the string from. Must not be {@code null}. * @param position The position in the buffer to start extracting the string from. * @param length The number of characters to include in the extracted string. * @return A string extracted from the buffer. * @throws IllegalArgumentException If the provided {@code position} or {@code length} is negative. * @throws NullPointerException If the provided {@code buffer} is {@code null}. * @see Basic Latin Unicode block */ @NotNull static String toString(@NotNull final Bytes buffer, @NonNegative final long position, @NonNegative final long length) { requireNonNull(buffer); requireNonNegative(position); requireNonNegative(length); try { final long pos = buffer.readPosition(); final long limit = buffer.readLimit(); buffer.readPositionRemaining(position, length); try { @NotNull final StringBuilder builder = new StringBuilder(); while (buffer.readRemaining() > 0) { builder.append((char) buffer.readByte()); } // remove the last comma return builder.toString(); } finally { buffer.readLimit(limit); buffer.readPosition(pos); } } catch (Exception e) { return e.toString(); } } /** * Creates and returns a new fix sized wrapper for native (64-bit address) * memory with the contents copied from the given {@code bytes} array. *

* Changes in the given {@code bytes} will not be affected by writes in * the returned wrapper or vice versa. * * @param bytes array to copy, the array reference must not be null * @return a new fix sized wrapper * @throws NullPointerException If the provided {@code bytes} is {@code null} * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way. */ @NotNull static VanillaBytes allocateDirect(byte[] bytes) throws ClosedIllegalStateException, ThreadingIllegalStateException { VanillaBytes result = allocateDirect(bytes.length); result.write(bytes); return result; } /** * Converts a hexadecimal string, generated by one of the {@code toHexString} methods, back into a {@code Bytes} object. *

* This method takes a string that represents hexadecimal data along with comments (which describe the meaning of * the bytes), and converts it back into a {@code Bytes} object. The input string should have been generated by the * {@code toHexString} method. It can contain hexadecimal values, spaces, and comments. The method is able to * correctly parse and ignore spaces and comments during conversion. *

* For example, given the string: *

     * "c3 76 6d 68                                     # vmh:
     *  b6 03 56 4d 48                                  # VMH"
     * 
* This method would convert it to a {@code Bytes} object representing the original bytes before they were converted * to the hexadecimal representation. * * @param s the hexadecimal string to convert, which must have been generated by the {@code toHexString} method. * This parameter must not be {@code null}. * @return a {@code Bytes} object representing the original byte values of the provided hexadecimal string. * @throws IllegalArgumentException If the provided string is not in the valid format produced by the {@code toHexString} * method. */ @NotNull static Bytes fromHexString(@NotNull String s) { return BytesInternal.fromHexString(s); } /** * Creates and returns a new Bytes which is optionally unchecked as indicated by the provided {@code unchecked}. *

* This allows bounds checks to be turned off. * Note: this means that the result is no longer elastic, even if {@code this} is elastic. * Note: It is only possible to go from a checked Bytes to an unchecked bytes and not vice versa. * * @param unchecked if true, minimal bounds checks will be performed * @return a new, potentially unchecked, Bytes * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way. */ @NotNull default Bytes unchecked(boolean unchecked) throws ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfReleased(this); if (unchecked) { if (isElastic()) BytesUtil.WarnUncheckedElasticBytes.warn(); Bytes underlyingBytes = start() == 0 && bytesStore().isDirectMemory() ? new UncheckedNativeBytes<>(this) : new UncheckedBytes<>(this); release(INIT); return underlyingBytes; } return this; } /** * Checks whether this Bytes object operates in an unchecked mode. *

* When a Bytes object is unchecked, it performs minimal or no bounds checking on read and write operations. * This can improve performance but may result in undefined behavior if attempting to access out-of-bounds elements. * * @return {@code true} if this Bytes object operates in unchecked mode; {@code false} otherwise. */ default boolean unchecked() { return false; } @Override default long safeLimit() { return bytesStore().safeLimit(); } @Override default boolean isClear() { return start() == readPosition() && writeLimit() == capacity(); } /** * If this Bytes {@link #isElastic()} the {@link #realCapacity()} can be * lower than the virtual {@link #capacity()}. */ @SuppressWarnings("EmptyMethod") @Override default @NonNegative long realCapacity() { return BytesStore.super.realCapacity(); } /** * Creates and returns a deep copy of this Bytes object, including the data between * {@link RandomCommon#readPosition()} and {@link RandomCommon#readLimit()}. *

* The copy will have its own separate storage and state, and modifications to the copy will not affect the original * Bytes object, and vice versa. * * @return A new Bytes object that is a copy of this object. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @Override BytesStore, U> copy() throws IllegalStateException, ClosedIllegalStateException, ThreadingIllegalStateException; /** * Creates and returns a new String representing the contents of this Bytes object in hexadecimal form, * with comments describing the meaning of the bytes. The representation starts at the {@code readPosition()} * and encodes at most 1024 bytes. *

* Each line of the output string contains hexadecimal representation of 16 bytes, followed by comments * describing the meaning of those bytes. *

* For example, if the Bytes object contains bytes representing the ASCII string "VMH", * the output would be similar to this: *

     * c3 76 6d 68                                     # vmh:
     * b6 03 56 4d 48                                  # VMH
     * 
*

* This is assumed to be used to print the contents on a best effort basis. If an Error occurs it will be returned in the String. * * @return A new hexadecimal String representing the content with comments describing the bytes. */ @NotNull default String toHexString() { return toHexString(1024); } /** * Creates and returns a new String representing the contents of this Bytes object in hexadecimal form, * with comments describing the meaning of the bytes. The representation starts at the {@code readPosition()} * and encodes at most the provided {@code maxLength} bytes. *

* Each line of the output string contains hexadecimal representation of 16 bytes, followed by comments * describing the meaning of those bytes. *

* For example, if the Bytes object contains bytes representing the ASCII string "VMH", and maxLength is * large enough, the output would be similar to this: *

     * c3 76 6d 68                                     # vmh:
     * b6 03 56 4d 48                                  # VMH
     * 
*

* This is assumed to be used to print the contents on a best effort basis. If an Error occurs it will be returned in the String. * * @param maxLength Limit the number of bytes to be encoded. * @return A new hexadecimal String representing the content with comments describing the bytes. * @throws IllegalArgumentException If the provided {@code maxLength} is negative. */ @NotNull default String toHexString(@NonNegative long maxLength) { return toHexString(readPosition(), maxLength); } /** * Creates and returns a new String representing the contents of this Bytes object in hexadecimal form, * with comments describing the meaning of the bytes. The representation starts at the provided {@code offset} * and encodes at most the provided {@code maxLength} bytes. *

* The method reads data starting from the specified offset. Each line of the output string contains * hexadecimal representation of 16 bytes, followed by comments describing the meaning of those bytes. * If the number of bytes from the offset to the end exceeds {@code maxLength}, the output string * will be suffixed with "... truncated". *

* For example, if the Bytes object contains bytes representing the ASCII string "VMH", and offset is 0, * and maxLength is large enough, the output would be similar to this: *

     * c3 76 6d 68                                     # vmh:
     * b6 03 56 4d 48                                  # VMH
     * 
*

* This is assumed to be used to print the contents on a best effort basis. If an Error occurs it will be returned in the String. * * @param offset The starting offset within the Bytes object to begin the hexadecimal conversion. * @param maxLength Limit the number of bytes to be encoded. If the number of bytes exceeds maxLength, * the output will be suffixed with "... truncated". * @return A new hexadecimal String representing the content with comments describing the bytes. * @throws IllegalArgumentException If the provided {@code offset} or {@code maxLength} is negative. */ @NotNull default String toHexString(@NonNegative long offset, @NonNegative long maxLength) { requireNonNegative(offset); requireNonNegative(maxLength); final long maxLength2 = Math.min(maxLength, readLimit() - offset); final String ret = BytesInternal.toHexString(this, offset, maxLength2); return maxLength2 < readLimit() - offset ? ret + "... truncated" : ret; } /** * Checks if this Bytes object is elastic, meaning it can dynamically resize when more data is written * than its current {@link #realCapacity()}. *

* Elastic Bytes objects can automatically grow to accommodate additional data, whereas * non-elastic ones have a fixed capacity. * * @return {@code true} if this Bytes object is elastic; {@code false} otherwise. */ boolean isElastic(); /** * Ensures that this Bytes object has the capacity to accommodate the specified amount of data. *

* If this Bytes object is elastic and doesn't have enough capacity, it will be resized. If it is not * elastic and doesn't have enough capacity, a {@link DecoratedBufferOverflowException} will be thrown. * * @param desiredCapacity The minimum capacity, in bytes, that is required. * @throws DecoratedBufferOverflowException If the Bytes object is not elastic and there isn't enough space * or if the provided {@code desiredCapacity} is negative. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ default void ensureCapacity(@NonNegative long desiredCapacity) throws DecoratedBufferOverflowException, IllegalStateException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNegative(desiredCapacity); if (desiredCapacity > capacity()) throw new DecoratedBufferOverflowException(isElastic() ? "Resizing required" : "Buffer is not elastic"); } /** * Creates and returns a new slice of this Bytes object with its start position set to the current * read position and its capacity determined by the current limit. *

* Note that the slice is a subsection of this Bytes object and will not be elastic regardless of * the elasticity of the parent Bytes object. * * @return A new slice of this Bytes object. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @NotNull @Override default Bytes bytesForRead() throws IllegalStateException, ClosedIllegalStateException, ThreadingIllegalStateException { throwExceptionIfReleased(this); BytesStore bytesStore = bytesStore(); assert bytesStore != null : "bytesStore is null"; return isClear() ? bytesStore.bytesForRead() : new SubBytes<>(bytesStore, readPosition(), readLimit() + start()); } /** * Creates and returns a Bytes object that wraps the bytesStore of this Bytes object, * ranging from the {@code start} position to the {@code realCapacity}. *

* The returned Bytes object is non-elastic and supports both read and write operations using cursors. * * @return A Bytes object wrapping the bytesStore of this Bytes object. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @Override @NotNull default Bytes bytesForWrite() throws IllegalStateException, ClosedIllegalStateException { throwExceptionIfReleased(this); BytesStore bytesStore = bytesStore(); assert bytesStore != null : "bytesStore is null"; return new VanillaBytes<>(bytesStore, writePosition(), writeLimit()); } /** * Returns the backing BytesStore that this Bytes object wraps. If this Bytes object doesn't have * a backing BytesStore, this method returns null. *

* The BytesStore represents the underlying storage of bytes that the Bytes object is manipulating. * * @return The backing BytesStore, or null if there isn't one. */ @Override @Nullable BytesStore bytesStore(); /** * Compares the contents of this Bytes object with the provided {@code other} string for equality. *

* This method returns {@code true} if the contents of this Bytes object is equal to the contents * of the {@code other} string. Otherwise, it returns {@code false}. * * @param other The string to compare with the contents of this Bytes object, can be null. * @return {@code true} if the contents of this Bytes object equals the contents of the provided * {@code other} string; {@code false} otherwise. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ default boolean isEqual(@Nullable String other) throws IllegalStateException { return StringUtils.isEqual(this, other); } /** * Compacts this Bytes object by moving the readPosition() to the start(), * adjusting the readLimit()/writePosition() appropriately so that the readRemaining() is unchanged. *

* This operation is useful to free up space for writing by discarding bytes that have already * been read. *

* NOTE: If the space freed in longs is less than 1/4 the readRemaining, this method does nothing to reduce movement overhead * * @return This Bytes object, for method chaining. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @NotNull Bytes compact() throws ClosedIllegalStateException, ThreadingIllegalStateException; /** * Copies bytes from this Bytes object into the provided {@code targetByteStore}. *

* The number of bytes copied is the minimum of the remaining bytes in this Bytes object and the * remaining capacity in the {@code targetByteStore}. * * @param targetByteStore The target BytesStore to copy bytes into, must not be null. * @return The actual number of bytes that were copied. * @throws NullPointerException If the provided {@code targetByteStore} is null. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @Override default long copyTo(@NotNull final BytesStore targetByteStore) throws ClosedIllegalStateException { return BytesStore.super.copyTo(targetByteStore); } /** * Copies bytes from this Bytes object to the provided {@code outputStream}. *

* Bytes are written from the current read position up to the limit of this Bytes object. * * @param outputStream The OutputStream to write the bytes to, must not be null. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws IOException If an I/O error occurs while writing to the provided * {@code outputStream}. * @throws NullPointerException If the provided {@code outputStream} is null. */ @Override default void copyTo(@NotNull OutputStream outputStream) throws IOException, ClosedIllegalStateException { BytesStore.super.copyTo(outputStream); } /** * Determines whether this Bytes object is backed by memory that can be shared across multiple * processes. *

* A Bytes object backed by a memory-mapped file, for example, can be shared among processes, * whereas a Bytes object backed by heap memory cannot be shared in this way. * * @return {@code true} if this Bytes object uses memory that can be shared across processes, * {@code false} otherwise. */ @Override default boolean sharedMemory() { return bytesStore().sharedMemory(); } /** * Removes a specified number of bytes starting from a given offset within this Bytes object. *

* This method effectively removes {@code count} bytes beginning at the specified {@code fromOffset}, * shifting any subsequent bytes to the left (reduces their index). * The write position is adjusted accordingly, and the capacity remains unchanged. * * @param fromOffset The offset from which to start removing bytes. * @param count The number of bytes to remove. * @throws BufferUnderflowException If {@code fromOffset} is negative or greater than the * write position. * @throws BufferOverflowException If there isn't enough space to perform the unwrite operation. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ default void unwrite(@NonNegative long fromOffset, @NonNegative int count) throws BufferUnderflowException, BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { long wp = writePosition(); if (wp < fromOffset) return; write(fromOffset, this, fromOffset + count, wp - fromOffset - count); writePosition(wp - count); } /** * Returns the index within this Bytes object of the first occurrence of the specified * sub-bytes represented by the {@code source} Bytes object. *

* The method returns the lowest index {@code k} at which the contents of this Bytes object * equals the provided {@code source} Bytes object. If no such sequence exists, then {@code -1} * is returned. * *

* Formally, the method returns the lowest index {@code k} such that: * *

     * this.startsWith(source, k)
     * 
*

* If no such value of {@code k} exists, then {@code -1} is returned. * * @param source The sub-bytes to search for within this Bytes object, must not be null. * @return The index of the first occurrence of the specified sub-bytes, or {@code -1} if there * is no such occurrence. * @throws NullPointerException If the provided {@code source} is null. * @throws ClosedIllegalStateException If the resource has been released or closed. */ default long indexOf(@NotNull Bytes source) throws ClosedIllegalStateException { // TODO use indexOf(Bytes, long); throwExceptionIfReleased(this); throwExceptionIfReleased(source); long sourceOffset = readPosition(); long otherOffset = source.readPosition(); long sourceCount = readRemaining(); long otherCount = source.readRemaining(); if (sourceCount <= 0) { return Math.toIntExact(otherCount == 0 ? sourceCount : -1); } if (otherCount == 0) { return 0; } byte firstByte = source.readByte(otherOffset); long max = sourceOffset + (sourceCount - otherCount); for (long i = sourceOffset; i <= max; i++) { /* Look for first character. */ if (readByte(i) != firstByte) { while (++i <= max && readByte(i) != firstByte) ; } /* Found first character, now look at the rest of v2 */ if (i <= max) { long j = i + 1; long end = j + otherCount - 1; for (long k = otherOffset + 1; j < end && readByte(j) == source.readByte(k); j++, k++) { // Do nothing } if (j == end) { /* Found whole string. */ return Math.toIntExact(i - sourceOffset); } } } return -1; } /** * Returns the lowest index value starting from the provided {@code fromIndex} for which the contents of this * Bytes object equals the provided {@code source }, or -1 if no such index value exists. *

* On other words, returns the index within this Bytes object of the first occurrence of the * provided {@code source} *

* The returned index is the smallest value k for which: *

     * this.startsWith(bytes, k)
     * 
* If no such value of k exists, then {@code -1} is returned. * * @param source the non-null sub-bytes to search for. * @param fromIndex to start searching from * @return index of equal contents or -1 * @throws NullPointerException If the provided {@code source } is {@code null} * @throws ClosedIllegalStateException If the resource has been released or closed. */ default int indexOf(@NotNull BytesStore source, @NonNegative int fromIndex) throws ClosedIllegalStateException { // TODO shouldn't fromIndex be absolute instead of relative throwExceptionIfReleased(this); throwExceptionIfReleased(source); long sourceOffset = readPosition(); long otherOffset = source.readPosition(); long sourceCount = readRemaining(); long otherCount = source.readRemaining(); if (fromIndex < 0) { fromIndex = 0; } if (fromIndex >= sourceCount) { return Math.toIntExact(otherCount == 0 ? sourceCount : -1); } if (otherCount == 0) { return fromIndex; } byte firstByte = source.readByte(otherOffset); long max = sourceOffset + (sourceCount - otherCount); for (long i = sourceOffset + fromIndex; i <= max; i++) { /* Look for first character. */ if (readByte(i) != firstByte) { while (++i <= max && readByte(i) != firstByte) ; } /* Found first character, now look at the rest of v2 */ if (i <= max) { long j = i + 1; long end = j + otherCount - 1; for (long k = otherOffset + 1; j < end && readByte(j) == source.readByte(k); j++, k++) { // Do nothing } if (j == end) { /* Found whole string. */ return Math.toIntExact(i - sourceOffset); } } } return -1; } /** * Clears the content of this Bytes object and resets its state. * * @return This Bytes object. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @Override @NotNull Bytes clear() throws IllegalStateException, ClosedIllegalStateException, ThreadingIllegalStateException; /** * Checks if this Bytes object supports both reading and writing. * * @return {@code true} if this Bytes object supports both reading and writing, {@code false} otherwise. */ @Override default boolean readWrite() { return bytesStore().readWrite(); } /** * Writes the specified number of bytes from this Bytes object into the provided {@code bytesOut}. *

* This operation does not affect the readLimit or readPosition of this Bytes object. * * @param length The number of bytes to write. Must be non-negative. * @param bytesOut The target BytesOut object to write to. Must not be null. * @throws BufferUnderflowException If the specified {@code length} is greater than the number of bytes available for reading from this Bytes object. * @throws IORuntimeException If an error occurs while reading from this Bytes object or writing to {@code bytesOut}. * @throws BufferOverflowException If {@code bytesOut} does not have enough capacity to hold the bytes being written. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way * @throws IllegalArgumentException If the specified {@code length} is negative. * @throws NullPointerException If the provided {@code bytesOut} is null. */ default void readWithLength(@NonNegative long length, @NotNull BytesOut bytesOut) throws BufferUnderflowException, IORuntimeException, BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNegative(length); if (length > readRemaining()) throw new BufferUnderflowException(); long limit0 = readLimit(); long limit = readPosition() + length; boolean lenient = lenient(); try { lenient(true); readLimit(limit); bytesOut.write(this); } finally { readLimit(limit0); readPosition(limit); lenient(lenient); } } /** * Reads the content of this Bytes object and converts it into an instance of the specified class. * Optionally updates the provided instance if not null. The content is assumed to have a * 16-bit length indicator preceding the actual object data. *

* If the {@code using} parameter is not null, this method will update its state with the read content. * Otherwise, it will create a new instance of the specified class. * * @param clazz The class of the object to be read. Must not be null. * @param using An optional instance to update with the read content. Can be null. * @param The type of the object being read. * @return The object that was read from this Bytes object, or the updated instance if {@code using} was provided. * @throws BufferUnderflowException If there are not enough bytes in this Bytes object to read the content. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws InvalidMarshallableException If the data in this Bytes object cannot be converted into an instance of the specified class. * @throws NullPointerException If the provided {@code clazz} is null. * @see #writeMarshallableLength16(WriteBytesMarshallable) */ default T readMarshallableLength16(@NotNull final Class clazz, @Nullable final T using) throws BufferUnderflowException, ClosedIllegalStateException, InvalidMarshallableException, ThreadingIllegalStateException { final T object = (using == null) ? ObjectUtils.newInstance(clazz) : using; int length = readUnsignedShort(); long limit = readLimit(); long end = readPosition() + length; boolean lenient = lenient(); try { lenient(true); readLimit(end); object.readMarshallable(this); } finally { readPosition(end); readLimit(limit); lenient(lenient); } return object; } /** * Writes the content of the provided {@code marshallable} into this Bytes object, including a 16-bit length indicator * preceding the actual object data. The length indicator denotes the number of bytes taken up by the object data. *

* This method is useful for serialization, as it writes not only the content of the {@code marshallable} object * but also information about its size. * * @param marshallable The object to be serialized and written to this Bytes object. Must not be null. * @throws BufferOverflowException If there is not enough capacity in this Bytes object to store the serialized data. * @throws ClosedIllegalStateException If the resource has been released or closed. * cannot be addressed with 16-bits. * @throws NullPointerException If the provided {@code marshallable} is null. * @see #readMarshallableLength16(Class, ReadBytesMarshallable) */ default void writeMarshallableLength16(@NotNull final WriteBytesMarshallable marshallable) throws BufferOverflowException, ClosedIllegalStateException, BufferUnderflowException, InvalidMarshallableException, ThreadingIllegalStateException { requireNonNull(marshallable); long position = writePosition(); ValidatableUtil.validate(marshallable); writeUnsignedShort(0); marshallable.writeMarshallable(this); long length = lengthWritten(position) - 2; if (length >= 1 << 16) throw new IllegalStateException("Marshallable " + marshallable.getClass() + " too long was " + length); writeUnsignedShort(position, (int) length); } /** * Writes the contents of the provided {@code inputStream} into this Bytes object. Continues reading from the * {@code inputStream} and writing into this Bytes object until the end of the stream is reached. *

* Note: This method does not close the provided {@code inputStream}. * * @param inputStream The input stream from which data is to be read. Must not be null. * @return This Bytes object. * @throws IOException If an I/O error occurs when reading from the {@code inputStream}. * @throws BufferOverflowException If there is not enough capacity in this Bytes object to write the data read from the input stream. * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way * @throws NullPointerException If the provided {@code inputStream} is null. */ default Bytes write(@NotNull final InputStream inputStream) throws IOException, BufferOverflowException, ClosedIllegalStateException, ThreadingIllegalStateException { requireNonNull(inputStream); for (; ; ) { int read; read = inputStream.read(); if (read == -1) break; writeByte((byte) read); } return this; } }