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

net.openhft.chronicle.wire.BinaryWire Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016-2020 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.wire;

import net.openhft.chronicle.bytes.*;
import net.openhft.chronicle.bytes.internal.NativeBytesStore;
import net.openhft.chronicle.bytes.ref.*;
import net.openhft.chronicle.bytes.util.BinaryLengthLength;
import net.openhft.chronicle.bytes.util.Bit8StringInterner;
import net.openhft.chronicle.bytes.util.Compression;
import net.openhft.chronicle.bytes.util.UTF8StringInterner;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.Maths;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.InvalidMarshallableException;
import net.openhft.chronicle.core.io.ValidatableUtil;
import net.openhft.chronicle.core.pool.ClassLookup;
import net.openhft.chronicle.core.pool.StringBuilderPool;
import net.openhft.chronicle.core.scoped.ScopedResource;
import net.openhft.chronicle.core.scoped.ScopedResourcePool;
import net.openhft.chronicle.core.util.*;
import net.openhft.chronicle.core.values.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.nio.BufferUnderflowException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.*;

import static net.openhft.chronicle.core.util.ReadResolvable.readResolve;
import static net.openhft.chronicle.wire.BinaryWire.AnyCodeMatch.ANY_CODE_MATCH;
import static net.openhft.chronicle.wire.BinaryWireCode.*;
import static net.openhft.chronicle.wire.Wires.GENERATE_TUPLES;

/**
 * Represents a binary translation of TextWire, which is a subset of YAML.
 * This class provides functionalities for reading from and writing to binary wire formats. It encapsulates
 * various configurations such as field representation, delta support, and compression settings.
 * Extends the `AbstractWire` and implements the `Wire` interface to ensure compatibility and a common API
 * with other wire formats.
 */
@SuppressWarnings({"rawtypes", "unchecked", "this-escape", "deprecation"})
public class BinaryWire extends AbstractWire implements Wire {

    static final ScopedResourcePool SBP = StringBuilderPool.createThreadLocal();

    // UTF-8 string interner for memory-efficient string operations
    private static final UTF8StringInterner UTF8 = new UTF8StringInterner(4096);

    // 8-bit string interner for memory-efficient string operations
    private static final Bit8StringInterner BIT8 = new Bit8StringInterner(1024);

    // Class value mapping to determine whether an object uses self-describing messages
    private static final ClassValue USES_SELF_DESCRIBING = ClassLocal.withInitial(k -> {
        Object m = ObjectUtils.newInstance(k);
        if (m instanceof Marshallable)
            return ((Marshallable) m).usesSelfDescribingMessage();
        return true;
    });

    // Flag to control warnings related to missing classes
    private static final AtomicBoolean FIRST_WARN_MISSING_CLASS = new AtomicBoolean();
    private static final ThreadLocal VANILLA_MESSAGE_HISTORY_TL = ThreadLocal.withInitial(VanillaMessageHistory::new);

    // Output handler for fixed binary values
    private final FixedBinaryValueOut fixedValueOut = new FixedBinaryValueOut();

    // Output handler for binary values
    @NotNull
    private final FixedBinaryValueOut valueOut;

    // Input handler for binary values
    @NotNull
    protected final BinaryValueIn valueIn;

    // Indicates whether fields are represented numerically
    private final boolean numericFields;

    // Indicates whether fields are absent
    private final boolean fieldLess;

    // Threshold size for compressed outputs
    private final int compressedSize;

    // Context for writing to the wire
    private final WriteDocumentContext writeContext = new BinaryWriteDocumentContext(this);

    // Context for reading from the wire
    @NotNull
    private final BinaryReadDocumentContext readContext;

    // String builder for various internal operations
    private final StringBuilder stringBuilder = new StringBuilder();

    // Default input handler
    private DefaultValueIn defaultValueIn;
    private final String compression;
    private Boolean overrideSelfDescribing = null;

    /**
     * Constructs a BinaryWire with default settings.
     *
     * @param bytes The bytes to be processed by this wire
     */
    public BinaryWire(@NotNull Bytes bytes) {
        this(bytes, false, false, false, Integer.MAX_VALUE, "binary", false);
    }

    /**
     * Constructs a BinaryWire with specified configurations.
     *
     * @param bytes The bytes to be processed by this wire
     * @param fixed Indicates whether the value output is fixed
     * @param numericFields Indicates if fields are represented numerically
     * @param fieldLess Indicates if fields are absent
     * @param compressedSize Threshold size for compression
     * @param compression Type of compression (e.g., "binary")
     */
    public BinaryWire(@NotNull Bytes bytes, boolean fixed, boolean numericFields, boolean fieldLess, int compressedSize, String compression) {
        super(bytes, false);
        this.numericFields = numericFields;
        this.fieldLess = fieldLess;
        this.compressedSize = compressedSize;
        valueOut = getFixedBinaryValueOut(fixed);
        this.compression = compression;
        valueIn = new BinaryValueIn();
        readContext = new BinaryReadDocumentContext(this);
    }

    /**
     * Constructs a BinaryWire with specified configurations.
     *
     * @param bytes The bytes to be processed by this wire
     * @param fixed Indicates whether the value output is fixed
     * @param numericFields Indicates if fields are represented numerically
     * @param fieldLess Indicates if fields are absent
     * @param compressedSize Threshold size for compression
     * @param compression Type of compression (e.g., "binary")
     * @param supportDelta must be false
     */
    @Deprecated(/* to be removed in x.29 */)
    public BinaryWire(@NotNull Bytes bytes, boolean fixed, boolean numericFields, boolean fieldLess, int compressedSize, String compression, boolean supportDelta) {
        this(bytes, fixed, numericFields, fieldLess, compressedSize, compression);
        assert !supportDelta;
    }

    /**
     * Creates and returns a new instance of BinaryWire with the delta support disabled.
     *
     * @param bytes The bytes to be processed by this wire
     * @return A new instance of BinaryWire
     */
    @NotNull
    public static BinaryWire binaryOnly(@NotNull Bytes bytes) {
        return new BinaryWire(bytes, false, false, false, Integer.MAX_VALUE, "binary", false);
    }

    /**
     * Determines if the provided BytesStore can be treated as textual.
     * This method checks each byte of the BytesStore to ensure it's a printable character or a newline.
     *
     * @param bytes The BytesStore to check
     * @return true if the BytesStore can be treated as text, false otherwise
     */
    static boolean textable(BytesStore bytes) {
        if (bytes == null)
            return false;
        for (long pos = bytes.readPosition(); pos < bytes.readLimit(); pos++) {
            byte b = bytes.readByte(pos);
            if (b < ' ' && b != '\n')
                return false;
        }
        return true;
    }

    /**
     * Determines if the provided CharSequence can be treated as textual.
     * This method checks each character of the CharSequence to ensure it's a printable character.
     *
     * @param cs The CharSequence to check
     * @return true if the CharSequence can be treated as text, false otherwise
     */
    static boolean textable(CharSequence cs) {
        if (cs == null)
            return false;
        for (int pos = 0; pos < cs.length(); pos++) {
            char b = cs.charAt(pos);
            if (b < ' ')
                return false;
        }
        return true;
    }

    /**
     * Checks if the provided character is a digit (0-9).
     *
     * @param c The character to check
     * @return true if the character is a digit, false otherwise
     */
    static boolean isDigit(char c) {
        // use underflow to make digits below '0' large.
        c -= '0';
        return c <= 9;
    }

    @Override
    public void reset() {
        writeContext.reset();
        readContext.reset();
        valueIn.resetState();
        valueOut.resetState();
        bytes.clear();
    }

    @Override
    public void rollbackIfNotComplete() {
        writeContext.rollbackIfNotComplete();
    }

    @Override
    public boolean isBinary() {
        return true;
    }

    /**
     * Retrieves the current override setting for the self-describing nature of this BinaryWire.
     *
     * @return null if there's no override, true if it always uses self-describing messages,
     *         false if it never uses self-describing messages.
     */
    public Boolean getOverrideSelfDescribing() {
        return overrideSelfDescribing;
    }

    /**
     * Sets an override for the self-describing nature of this BinaryWire.
     *
     * @param overrideSelfDescribing null if there's no override, true if it should always use self-describing messages,
     *                               false if it should never use self-describing messages.
     * @return The current instance of the BinaryWire class (following the builder pattern).
     */
    public BinaryWire setOverrideSelfDescribing(Boolean overrideSelfDescribing) {
        this.overrideSelfDescribing = overrideSelfDescribing;
        return this;
    }

    /**
     * Acquires and clears the internal StringBuilder for use. This method is used to avoid frequent
     * instantiation of new StringBuilder objects, improving performance.
     *
     * @return A cleared instance of the internal StringBuilder.
     */
    @NotNull
    protected StringBuilder acquireStringBuilder() {
        // Reset the StringBuilder to its default state.
        stringBuilder.setLength(0);
        return stringBuilder;
    }

    /**
     * Provides a FixedBinaryValueOut instance based on the given boolean parameter. If 'fixed' is true,
     * the method returns a fixed instance; otherwise, it creates and returns a new BinaryValueOut instance.
     *
     * @param fixed Determines which type of FixedBinaryValueOut to return.
     * @return An instance of FixedBinaryValueOut.
     */
    @NotNull
    protected FixedBinaryValueOut getFixedBinaryValueOut(boolean fixed) {
        return fixed ? fixedValueOut : new BinaryValueOut();
    }

    @Override
    public void clear() {
        bytes.clear();
        valueIn.resetState();
        valueOut.resetState();
    }

    /**
     * Checks and returns if this BinaryWire instance is field-less.
     *
     * @return true if the BinaryWire is field-less, false otherwise.
     */
    public boolean fieldLess() {
        return fieldLess;
    }

    @NotNull
    @Override
    public DocumentContext writingDocument(boolean metaData) {
        writeContext.start(metaData);
        return writeContext;
    }

    @Override
    public DocumentContext acquireWritingDocument(boolean metaData) {
        if (writeContext.isOpen() && writeContext.chainedElement())
            return writeContext;
        return writingDocument(metaData);
    }

    @NotNull
    @Override
    public DocumentContext readingDocument() {
        readContext.start();
        return readContext;
    }

    @NotNull
    @Override
    public DocumentContext readingDocument(long readLocation) {
        @NotNull Bytes bytes = bytes();
        final long readPosition = bytes.readPosition();
        final long readLimit = bytes.readLimit();
        bytes.readPositionUnlimited(readLocation);

        readContext.start();
        readContext.closeReadLimit(readLimit);
        readContext.closeReadPosition(readPosition);
        return readContext;
    }

    /**
     * Typicality used for debugging, this method does not progress the read position and should
     * only be used when inside a reading document.
     *
     * @return when readReading a document returns the current document as a YAML String, if you are
     * not currently reading a document, and empty string will be return.
     */
    @Override
    @NotNull
    public String readingPeekYaml() {
        long start = readContext.start;
        if (start == -1)
            return "";
        return Wires.fromSizePrefixedBlobs(bytes, start, usePadding());
    }

    @Override
    public void copyTo(@NotNull WireOut wire) throws InvalidMarshallableException {
        if (wire.getClass() == getClass()) {
            final Bytes bytes2 = wire.bytes();
            if (bytes2.retainedHexDumpDescription())
                bytes2.writeHexDumpDescription("passed-through");
            bytes2.write(this.bytes);
            this.bytes.readPosition(this.bytes.readLimit());
            return;
        }

        boolean first = true;
        copyTo(wire, first);
    }

    /**
     * Copies the content of this BinaryWire instance to the provided WireOut instance.
     * It does this by copying each data point sequentially.
     *
     * @param wire The destination WireOut instance.
     * @param first A flag indicating if the data point being copied is the first one.
     */
    void copyTo(@NotNull WireOut wire, boolean first) {
        while (bytes.readRemaining() > 0) {
            copyOne(wire, first);
            first = false; // Subsequent data points aren't the first ones.
        }
    }

    /**
     * Copies one unit of data from this BinaryWire to the provided WireOut instance.
     *
     * @param wire The destination WireOut instance.
     * @throws InvalidMarshallableException if the operation encounters an error during marshalling.
     */
    public void copyOne(@NotNull WireOut wire) throws InvalidMarshallableException {
        copyOne(wire, true);
    }

    /**
     * Copies one unit of data from this BinaryWire to the provided WireOut instance,
     * considering the provided 'first' flag.
     *
     * @param wire The destination WireOut instance.
     * @param first A flag indicating if the data point being copied is the first one.
     * @throws InvalidMarshallableException if the operation encounters an error during marshalling.
     */
    private void copyOne(@NotNull WireOut wire, boolean first) throws InvalidMarshallableException {
        int peekCode = peekCode();
        outerSwitch:
        switch (peekCode >> 4) {
            // For numeric codes, validate and copy accordingly.
            case BinaryWireHighCode.NUM0:
            case BinaryWireHighCode.NUM1:
            case BinaryWireHighCode.NUM2:
            case BinaryWireHighCode.NUM3:
            case BinaryWireHighCode.NUM4:
            case BinaryWireHighCode.NUM5:
            case BinaryWireHighCode.NUM6:
            case BinaryWireHighCode.NUM7:
                if (first)
                    throw new IllegalArgumentException();
                bytes.uncheckedReadSkipOne();
                wire.getValueOut().uint8checked(peekCode);
                break;

            // For control codes, handle padding and lengths.
            case BinaryWireHighCode.CONTROL:
                switch (peekCode) {
                    case PADDING:
                        // Handle padding and skip reading.
                        bytes.uncheckedReadSkipOne();
                        break outerSwitch;
                    case PADDING32:
                        // Handle 32-bit padding and skip reading.
                        bytes.uncheckedReadSkipOne();
                        bytes.readSkip(bytes.readUnsignedInt());
                        break outerSwitch;

                    // Handle byte lengths and read accordingly.
                    case BYTES_LENGTH8: {
                        bytes.uncheckedReadSkipOne();
                        int len = bytes.readUnsignedByte();
                        readWithLength(wire, len);
                        break outerSwitch;
                    }

                    case BYTES_LENGTH16: {
                        bytes.uncheckedReadSkipOne();
                        int len = bytes.readUnsignedShort();
                        readWithLength(wire, len);
                        break outerSwitch;
                    }

                    case BYTES_LENGTH32: {
                        // Handle 32-bit length bytes and read accordingly.
                        bytes.uncheckedReadSkipOne();
                        int len = bytes.readInt();
                        readWithLength(wire, len);
                        break outerSwitch;
                    }

                    case I64_ARRAY:
                        // Handle array of 64-bit integers.
                        bytes.uncheckedReadSkipOne();
                        long len2 = bytes.readLong();
                        long used = bytes.readLong();
                        if (len2 == used && len2 <= 2)
                            wire.getValueOut().sequence(o -> {
                                for (long i = 0; i < len2; i++) {
                                    long v = bytes.readLong();
                                    o.int64(v);
                                }
                            });
                        else
                            wire.getValueOut().sequence(o -> {
                                // Write comments indicating length and usage.
                                wire.writeComment("length: " + len2 + ", used: " + used);
                                for (long i = 0; i < len2; i++) {
                                    long v = bytes.readLong();
                                    if (i == used) {
                                        o.swapLeaf(true);
                                    }
                                    o.int64(v);
                                }
                                o.swapLeaf(false);
                            });

                        break outerSwitch;

                    case FIELD_ANCHOR: { // Process field anchors.
                        fieldAnchor(wire);
                        break outerSwitch;
                    }

                    case ANCHOR:
                    case UPDATED_ALIAS: { // Process anchors and updated aliases.
                        anchor(wire);
                        break outerSwitch;
                    }

                    case HISTORY_MESSAGE: {
                        bytes.uncheckedReadSkipOne();
                        copyHistoryMessage(bytes(), wire);
                        break outerSwitch;
                    }
                    case U8_ARRAY:
                        unexpectedCode();
                }
                unknownCode(wire);
                break;

            case BinaryWireHighCode.FLOAT:
                // Handle floating-point values.
                bytes.uncheckedReadSkipOne();
                try {
                    Number d = readFloat0(peekCode);
                    wire.getValueOut().object(d);
                } catch (Exception e) {
                    unknownCode(wire);
                }
                break;

            case BinaryWireHighCode.INT:
                // Handle integer values.
                bytes.uncheckedReadSkipOne();
                try {
                    if (peekCode == INT64_0x) {
                        wire.getValueOut().int64_0x(bytes.readLong());
                    } else {
                        Number l = readInt0object(peekCode);
                        if (l instanceof Integer)
                            wire.getValueOut().int32(l.intValue());
                        else
                            wire.getValueOut().object(l);
                    }
                } catch (Exception e) {
                    unknownCode(wire);
                }
                break;

            case BinaryWireHighCode.SPECIAL:
                // Process special codes.
                copySpecial(wire, peekCode);
                break;

            case BinaryWireHighCode.FIELD0:
            case BinaryWireHighCode.FIELD1:
                // Process field values.
                @Nullable StringBuilder fsb = readField(peekCode, ANY_CODE_MATCH.name(), ANY_CODE_MATCH.code(), acquireStringBuilder(), false);
                if (!textable(fsb))
                    throw new IllegalArgumentException();
                wire.write(fsb);
                break;

            case BinaryWireHighCode.STR0:
            case BinaryWireHighCode.STR1:
                // Process string values.
                bytes.uncheckedReadSkipOne();
                @Nullable StringBuilder sb = readText(peekCode, acquireStringBuilder());
                wire.getValueOut().text(sb);
                break;
        }
    }

    private static void copyHistoryMessage(Bytes bytes, @NotNull WireOut wire) {
        VanillaMessageHistory vmh = VANILLA_MESSAGE_HISTORY_TL.get();
        vmh.useBytesMarshallable(true);
        vmh.addSourceDetails(false);
        vmh.readMarshallable(bytes);
        wire.getValueOut().object(VanillaMessageHistory.class, vmh);
    }

    /**
     * Throws an exception indicating an unexpected code was encountered.
     *
     * @throws IORuntimeException Always thrown with a specific message.
     */
    protected static void unexpectedCode() {
        throw new IORuntimeException("Unexpected code in this context");
    }

    /**
     * Placeholder or handler for anchor processing in the WireOut stream.
     * This implementation throws an exception indicating it's unexpected in this context.
     *
     * @param wire The wire output stream.
     */
    protected void anchor(@NotNull WireOut wire) {
        unexpectedCode();
    }

    /**
     * Placeholder or handler for field anchor processing in the WireOut stream.
     * This implementation throws an exception indicating it's unexpected in this context.
     *
     * @param wire The wire output stream.
     */
    protected void fieldAnchor(@NotNull WireOut wire) {
        unexpectedCode();
    }

    /**
     * Reads data of a specified length from the bytes stream and writes to the WireOut stream
     * while interpreting the type of data (Map, Sequence, or Object).
     *
     * @param wire The wire output stream to write data to.
     * @param len  The length of data to be read.
     * @throws InvalidMarshallableException If there's an issue during marshalling.
     */
    @SuppressWarnings("incomplete-switch")
    public void readWithLength(@NotNull WireOut wire, int len) throws InvalidMarshallableException {
        long limit = bytes.readLimit();
        long newLimit = bytes.readPosition() + len;
        if (newLimit > limit)
            throw new IORuntimeException("Can't extend the limit");
        try {
            bytes.readLimit(newLimit);
            @NotNull final ValueOut wireValueOut = wire.getValueOut();
            BracketType bracketType = getBracketTypeNext();
            switch (bracketType) {
                case HISTORY_MESSAGE:
                    bytes.uncheckedReadSkipOne();
                    copyHistoryMessage(bytes(), wire);
                    return;
                case MAP:
                    // For MAP type data, use marshallable to process.
                    wireValueOut.marshallable(this::copyTo);
                    break;
                case SEQ:
                    // For SEQ type data, use sequence for processing.
                    wireValueOut.sequence(v -> copyTo(v.wireOut(), false));
                    break;
                case NONE:
                    // For simple or NONE type, just read and process the object.
                    @Nullable Object object = this.getValueIn().object();
                    if (object instanceof BytesStore) {
                        @Nullable BytesStore bytes = (BytesStore) object;
                        if (textable(bytes)) {
                            wireValueOut.text(bytes);
                            bytes.releaseLast();
                            break;
                        }
                    }
                    wireValueOut.object(object);
                    break;
            }
        } finally {
            bytes.readLimit(limit);  // Reset the read limit to its original value.
        }
    }

    /**
     * Throws an exception indicating an unknown code was encountered.
     *
     * @param wire The wire output stream.
     * @throws IllegalArgumentException with the corresponding message for the unknown code.
     */
    protected void unknownCode(@NotNull WireOut wire) {
        throw new IllegalArgumentException(stringForCode(bytes.readUnsignedByte()));
    }

    /**
     * Peeks the next code from the stream and determines its corresponding bracket type.
     *
     * @return BracketType which could be MAP, SEQ, or NONE based on the peeked code.
     */
    @NotNull
    private BracketType getBracketTypeNext() {
        int peekCode = peekCode();
        return getBracketTypeFor(peekCode);
    }

    /**
     * Determines the bracket type for a given code.
     *
     * @param peekCode The code to determine the bracket type for.
     * @return BracketType corresponding to the provided code.
     */
    @NotNull
    BracketType getBracketTypeFor(int peekCode) {
        // If the code indicates a field name, return MAP
        if (peekCode >= FIELD_NAME0 && peekCode <= FIELD_NAME31)
            return BracketType.MAP;
        switch (peekCode) {
            case HISTORY_MESSAGE:
                return BracketType.HISTORY_MESSAGE;
            case FIELD_NUMBER:
            case FIELD_NAME_ANY:
            case EVENT_NAME:
            case EVENT_OBJECT:
                return BracketType.MAP;  // If the code corresponds to field numbers, events or any field name, return MAP

            case U8_ARRAY:
            case I64_ARRAY:
                return BracketType.NONE;  // For certain array types, return NONE

            default:
                return BracketType.SEQ;   // For all other codes, default to SEQ
        }
    }

    @NotNull
    @Override
    public ValueIn read(@NotNull String fieldName) {
        return read(fieldName, fieldName.hashCode(), null, Function.identity());
    }

    @NotNull
    @Override
    public ValueIn read() {
        readField(acquireStringBuilder(), null, ANY_CODE_MATCH.code());
        return bytes.readRemaining() <= 0
                ? acquireDefaultValueIn()
                : valueIn;
    }

    @NotNull
    @Override
    public ValueIn read(@NotNull WireKey key) {
        return read(key.name(), key.code(), key, WireKey::defaultValue);
    }

    /**
     * Attempts to read a field based on a given keyName and keyCode. If the field isn't immediately found, the method
     * will default to the provided source and lookup function. This method manages the parsing position, and may resort
     * to older fields if necessary.
     *
     * @param keyName       The name of the key to be searched for.
     * @param keyCode       The code corresponding to the key.
     * @param defaultSource The source to revert to if the key isn't found.
     * @param defaultLookup The function used to derive a default value based on the defaultSource.
     * @param            The type of the default source.
     * @return Returns a ValueIn object which represents the found or default value.
     */
    private  ValueIn read(CharSequence keyName, int keyCode, T defaultSource, @NotNull Function defaultLookup) {
        ValueInState curr = valueIn.curr();
        @NotNull StringBuilder sb = acquireStringBuilder();
        // did we save the position last time
        // so we could go back and parseOne an older field?
        if (curr.savedPosition() > 0) {
            bytes.readPosition(curr.savedPosition() - 1);
            curr.savedPosition(0L);
        }

        // Iterate through remaining bytes to find the field.
        while (bytes.readRemaining() > 0) {
            long position = bytes.readPosition();
            // at the current position look for the field.
            readField(sb, keyName, keyCode);
            if (sb.length() == 0 || StringUtils.isEqual(sb, keyName))
                return valueIn;

            // if no old field nor current field matches, set to default values.
            // we may come back and set the field later if we find it.
            curr.addUnexpected(position);

            ValidatableUtil.startValidateDisabled();
            try {
                valueIn.consumeNext();
            } catch (InvalidMarshallableException e) {
                throw new AssertionError(e);
            } finally {
                ValidatableUtil.endValidateDisabled();
            }
            consumePadding();
        }

        return read2(keyName, keyCode, defaultSource, defaultLookup, curr, sb, keyName);
    }

    /**
     * A secondary method to continue the field search process, specifically to handle cases where the field may
     * have been missed in a previous pass or if the field still hasn't been found. It will revert to the default
     * value if the field cannot be located.
     *
     * @param keyName       The name of the key to be searched for.
     * @param keyCode       The code corresponding to the key.
     * @param defaultSource The source to revert to if the key isn't found.
     * @param defaultLookup The function used to derive a default value based on the defaultSource.
     * @param curr          The current state of ValueIn.
     * @param sb            The StringBuilder used for string manipulations during search.
     * @param name          The actual name of the field being sought.
     * @param            The type of the default source.
     * @return Returns a ValueIn object which represents the found or default value.
     */
    protected  ValueIn read2(CharSequence keyName,
                                int keyCode,
                                T defaultSource,
                                @NotNull Function defaultLookup,
                                @NotNull ValueInState curr,
                                @NotNull StringBuilder sb,
                                CharSequence name) {
        long position2 = bytes.readLimit();

        // if not a match go back and look at old fields.
        for (int i = 0; i < curr.unexpectedSize(); i++) {
            bytes.readPosition(curr.unexpected(i));
            readField(sb, keyName, keyCode);
            if (sb.length() == 0 || StringUtils.isEqual(sb, name)) {
                // if an old field matches, remove it, save the current position
                curr.removeUnexpected(i);
                curr.savedPosition(position2 + 1);
                return valueIn;
            }
        }
        bytes.readPosition(position2);

        acquireDefaultValueIn();
        defaultValueIn.defaultValue = defaultLookup.apply(defaultSource);
        return defaultValueIn;
    }

    /**
     * Acquires an instance of DefaultValueIn. If one doesn't exist, a new instance is created.
     * Resets the default value to null each time it's acquired.
     *
     * @return The acquired or newly created DefaultValueIn instance.
     */
    private DefaultValueIn acquireDefaultValueIn() {
        if (defaultValueIn == null)
            defaultValueIn = new DefaultValueIn(this);
        // Reset the default value to null.
        defaultValueIn.defaultValue = null;
        return defaultValueIn;
    }

    @Override
    public long readEventNumber() {
        int peekCode = peekCodeAfterPadding();
        if (peekCode == BinaryWireCode.FIELD_NUMBER) {
            bytes.uncheckedReadSkipOne();
            int peekCode2 = bytes.peekUnsignedByte();
            if (0 <= peekCode2 && peekCode2 < 128) {
                bytes.uncheckedReadSkipOne();
                return peekCode2;
            }
            return bytes.readStopBit();
        }
        return Long.MIN_VALUE;
    }

    @NotNull
    @Override
    public ValueIn readEventName(@NotNull StringBuilder name) {
        return readField(name, null, ANY_CODE_MATCH.code()) == null ? acquireDefaultValueIn() : valueIn;
    }

    @NotNull
    @Override
    public ValueIn read(@NotNull StringBuilder name) {
        return readField(name, null, ANY_CODE_MATCH.code()) == null ? acquireDefaultValueIn() : valueIn;
    }

    @NotNull
    @Override
    public ValueIn getValueIn() {
        return valueIn;
    }

    @NotNull
    @Override
    public Wire readComment(@NotNull StringBuilder s) {
        if (peekCode() == COMMENT) {
            bytes.uncheckedReadSkipOne();
            bytes.readUtf8(s);
        } else {
            s.setLength(0);
        }
        return this;
    }

    /**
     * Reads the field value into a StringBuilder. It uses the provided key name and key code for the operation.
     *
     * @param name     The StringBuilder where the field name will be stored.
     * @param keyName  The name of the key to read.
     * @param keyCode  The code corresponding to the key.
     * @return The updated StringBuilder containing the field name.
     */
    @Nullable
    private StringBuilder readField(@NotNull StringBuilder name, CharSequence keyName, int keyCode) {
        // Peek at the next code after any padding or comment bytes.
        int peekCode = peekCodeAfterPadding();

        // Continue reading the field.
        return readField(peekCode, keyName, keyCode, name, true);
    }

    /**
     * Peeks at the next code after any padding or comments. If padding or comments are encountered,
     * they are consumed and the method looks again for the next meaningful code.
     *
     * @return The code that appears after any padding or comments.
     */
    private int peekCodeAfterPadding() {
        // Peek at the next available code.
        int peekCode = peekCode();

        // If the code corresponds to padding or a comment, consume it and peek again.
        if (peekCode == PADDING || peekCode == PADDING32 || peekCode == COMMENT) {
            consumePadding();
            peekCode = peekCode();
        }
        return peekCode;
    }

    @Nullable
    @Override
    public  K readEvent(@NotNull Class expectedClass) throws InvalidMarshallableException {
        int peekCode = peekCodeAfterPadding();
        switch (peekCode >> 4) {
            case BinaryWireHighCode.END_OF_STREAM:
                return null;

            case BinaryWireHighCode.CONTROL:
            case BinaryWireHighCode.SPECIAL:
                return readSpecialField(peekCode, expectedClass);

            case BinaryWireHighCode.FIELD0:
            case BinaryWireHighCode.FIELD1:
                return readSmallField(peekCode, expectedClass);

            default:
                return null;
        }
    }

    /**
     * Reads a small field based on the given peek code and converts it to the expected class type.
     * This method is optimized for fields with a length up to 31 characters.
     *
     * @param peekCode      The peek code used to determine the length and type of the field.
     * @param expectedClass The class type to which the read value should be converted.
     * @param            The type of the returned value.
     * @return The read value converted to the expected class type.
     */
    @NotNull
    private  K readSmallField(int peekCode, Class expectedClass) {
        // Skip the peek code as it's already read.
        bytes.uncheckedReadSkipOne();
        // Extract the length from the lower 5 bits of the peek code.
        final int length = peekCode & 0x1F;
        // Read and intern the field string.
        final String s = BIT8.intern(bytes, length);
        // Move the read position after the field.
        bytes.readSkip(length);
        if (expectedClass == String.class)
            // Return the interned string if the expected class is String.
            return (K) WireInternal.INTERNER.intern(s);
        // Otherwise, convert the string to the expected class type.
        return ObjectUtils.convertTo(expectedClass, s);
    }

    /**
     * Reads a special field based on the given peek code and converts it to the expected class type.
     * This method handles special cases like field numbers, field names, events, and anchors.
     *
     * @param peekCode      The peek code representing the type of the field.
     * @param expectedClass The class type to which the read value should be converted.
     * @param            The type of the returned value.
     * @return The read value converted to the expected class type or null if the peek code doesn't match any known type.
     * @throws InvalidMarshallableException If there's an error during the marshalling process.
     */
    @Nullable
    private  K readSpecialField(int peekCode, @NotNull Class expectedClass) throws InvalidMarshallableException {
        switch (peekCode) {
            case FIELD_NUMBER:
                // Skip the peek code and read the field number.
                bytes.uncheckedReadSkipOne();
                long fieldId = bytes.readStopBit();
                return ObjectUtils.convertTo(expectedClass, fieldId);

            case FIELD_NAME_ANY:
            case EVENT_NAME: {
                // Skip the peek code and read the field or event name.
                bytes.uncheckedReadSkipOne();
                @Nullable StringBuilder sb = read8bit();
                return ObjectUtils.convertTo(expectedClass, WireInternal.INTERNER.intern(sb));
            }

            case EVENT_OBJECT:
                // Skip the peek code and read the event object.
                bytes.uncheckedReadSkipOne();
                return valueIn.object(expectedClass);
        }

        // If the peek code doesn't match any known type, return null.
        return null;
    }

    /**
     * Reads a field with an 8-bit character set encoding into a StringBuilder.
     *
     * @return A StringBuilder containing the read field or null if the read operation failed.
     */
    @Nullable
    StringBuilder read8bit() {
        @NotNull StringBuilder sb = acquireStringBuilder();
        // Try to read the field and return the StringBuilder if successful.
        return bytes.read8bit(sb) ? sb : null;
    }

    @Override
    public void consumePadding() {
        while (true) {
            int code = peekCode();
            switch (code) {
                case PADDING:
                    bytes.uncheckedReadSkipOne();
                    break;

                case PADDING32:
                    bytes.uncheckedReadSkipOne();
                    bytes.readSkip(bytes.readUnsignedInt());
                    break;

                case COMMENT: {
                    bytes.uncheckedReadSkipOne();
                    commentListener.accept(readUtf8());
                    break;
                }

                default:
                    return;
            }
        }
    }

    /**
     * Peeks the code from the current position without advancing the read pointer.
     *
     * @return The unsigned byte at the current read position.
     */
    protected int peekCode() {
        return bytes.peekUnsignedByte();
    }

    /**
     * Reads a field from the wire based on the provided peek code.
     * This method is designed to handle different types of fields including control, special, small fields, etc.
     *
     * @param peekCode   The peek code indicating the type of the field to be read.
     * @param keyName    The key name of the field to be matched against.
     * @param keyCode    The key code of the field to be matched against.
     * @param sb         The StringBuilder to be populated with the read field.
     * @param missingOk  Indicates if missing fields are acceptable.
     * @return The populated StringBuilder with the read field or null if no field matched the given keyName and keyCode.
     */
    private StringBuilder readField(int peekCode, CharSequence keyName, int keyCode, @NotNull StringBuilder sb, boolean missingOk) {
        sb.setLength(0);
        switch (peekCode >> 4) {
            case BinaryWireHighCode.END_OF_STREAM:
                break;

            case BinaryWireHighCode.CONTROL:
            case BinaryWireHighCode.SPECIAL:
                return readSpecialField(peekCode, keyName, keyCode, sb);

            case BinaryWireHighCode.FIELD0:
            case BinaryWireHighCode.FIELD1:
                return readSmallField(peekCode, sb);
            default:
                if (missingOk)
                    // Allow cases where a field might not have been written.
                    break;
                throw new UnsupportedOperationException("Unknown code " + stringForCode(peekCode));
        }

        // In field-less mode, accept any field in order.
        if (fieldLess) {
            return sb;
        }

        return null;
    }

    /**
     * Reads a small field from the wire based on the provided peek code.
     * This method is optimized for reading fields with a length up to 31 characters.
     *
     * @param peekCode   The peek code indicating the type and length of the field to be read.
     * @param sb         The StringBuilder to be populated with the read field.
     * @return The populated StringBuilder with the read field.
     */
    @NotNull
    private StringBuilder readSmallField(int peekCode, @NotNull StringBuilder sb) {
        // Skip the peek code as it's already read.
        bytes.uncheckedReadSkipOne();
        if (bytes.isDirectMemory() && bytes.bytesStore() instanceof NativeBytesStore) {
            // Optimized parsing for direct memory access.
            AppendableUtil.parse8bit_SB1(bytes, sb, peekCode & 0x1f);
        } else {
            try {
                // General parsing for non-direct memory.
                AppendableUtil.parse8bit(bytes, sb, peekCode & 0x1f);
            } catch (IOException e) {
                throw new AssertionError(e);
            }
        }
        return sb;
    }

    /**
     * Reads a field from the wire based on the provided peek code, specifically for special types of fields.
     * The special fields include field number, field name, event name, field anchor, and event object.
     *
     * @param peekCode   The peek code indicating the type of the special field to be read.
     * @param keyName    The key name of the field to be matched against.
     * @param keyCode    The key code of the field to be matched against.
     * @param sb         The StringBuilder to be populated with the read field.
     * @return The populated StringBuilder with the read special field or null if the peek code does not match any known special field.
     */
    @Nullable
    private StringBuilder readSpecialField(int peekCode, CharSequence keyName, int keyCode, @NotNull StringBuilder sb) {
        switch (peekCode) {
            case FIELD_NUMBER:
                // Skip the peek code as it's already read.
                bytes.uncheckedReadSkipOne();
                long fieldId = bytes.readStopBit();
                // special handling for MethodReader.HISTORY messages as this has a more compact form as it can be very common
                if (fieldId == MethodReader.MESSAGE_HISTORY_METHOD_ID) {
                    sb.setLength(0);
                    sb.append(MethodReader.HISTORY);
                    return sb;
                }
                return readFieldNumber(keyName, keyCode, sb, fieldId);
            case FIELD_NAME_ANY:
            case EVENT_NAME:
                // Read and return the field or event name.
                bytes.uncheckedReadSkipOne();
                bytes.read8bit(sb);
                return sb;

            case EVENT_OBJECT:
                // Get the textual representation of the event object.
                valueIn.text(sb);
                return sb;
        }

        // Return null for unknown special fields.
        return null;
    }


    /**
     * Reads a field number from the wire and populates the provided StringBuilder.
     *
     * @param keyName   The key name of the field to be matched against.
     * @param keyCode   The key code of the field to be matched against.
     * @param sb        The StringBuilder to be populated with the read field number.
     * @param fieldId   The ID of the field read from the wire.
     * @return The populated StringBuilder with the read field number.
     */
    @NotNull
    protected StringBuilder readFieldNumber(CharSequence keyName, int keyCode, @NotNull StringBuilder sb, long fieldId) {
        // Check if the keyCode matches a predefined "ANY" match code.
        if (keyCode == ANY_CODE_MATCH.code()) {
            sb.append(fieldId);  // Append field ID to the StringBuilder.
            return sb;
        }

        // If fieldId doesn't match the expected keyCode, reset the StringBuilder.
        if (fieldId != keyCode)
            return sb;

        // If the fieldId matches the keyCode, append the keyName to the StringBuilder.
        sb.append(keyName);
        return sb;
    }

    /**
     * Gets the string representation of the field data based on the provided code.
     *
     * @param code The code indicating the type of the field data.
     * @param sb   The Appendable and CharSequence object to be populated with the field data.
     * @return The populated Appendable and CharSequence object with the field data.
     */
    @NotNull
     T getStringBuilder(int code, @NotNull T sb) {
        // Parse the UTF-8 encoded data from bytes based on the provided code and populate the StringBuilder.
        bytes.parseUtf8(sb, true, code & 0x1f);
        return sb;
    }

    /**
     * Copies special types of fields from the input wire to the output wire based on the provided peek code.
     *
     * @param wire     The output wire to which the field should be written.
     * @param peekCode The peek code indicating the type of the special field to be copied.
     * @throws InvalidMarshallableException If there's an error during the copy operation.
     */
    private void copySpecial(@NotNull WireOut wire, int peekCode) throws InvalidMarshallableException {
        // Switch based on the type of the field indicated by peekCode.
        switch (peekCode) {
            case COMMENT: {
                // Skip reading one byte and then read the comment.
                bytes.uncheckedReadSkipOne();
                @Nullable StringBuilder sb = readUtf8();
                wire.writeComment(sb);  // Write the comment to the output wire.
                break;
            }

            // Handling timestamp related fields.
            case TIME:
                wire.getValueOut().time(getValueIn().time());  // Get the time from the input and write to the output.
                break;
            case DATE:
                wire.getValueOut().date(getValueIn().date());  // Similarly handle for date...
                break;
            case DATE_TIME:
                wire.getValueOut().dateTime(getValueIn().dateTime());  // ...date-time...
                break;
            case ZONED_DATE_TIME:
                wire.getValueOut().zonedDateTime(getValueIn().zonedDateTime());
                break;

            case TYPE_PREFIX: {
                // Store the current read position.
                long readPosition = bytes.readPosition();
                bytes.uncheckedReadSkipOne();  // Skip one byte.
                @Nullable StringBuilder sb = readUtf8();
                // Check if the prefix indicates specific encoding/compression.
                if (StringUtils.isEqual("gzip", sb) || StringUtils.isEqual("lzw", sb)) {
                    bytes.readPosition(readPosition);  // Reset the read position.
                    wire.writeComment(sb);  // Write the prefix as a comment.
                    wire.getValueOut().text(valueIn.text());  // Write the actual value.

                } else {
                    // Set the type prefix for the output.
                    wire.getValueOut().typePrefix(sb);

                    try {
                        // Attempt to find the class for the name found in the type prefix.
                        Class aClass = classLookup.forName(sb);

                        // Special handling based on the class type.
                        if (aClass == byte[].class) {
                            wire.getValueOut().text(BytesStore.wrap(valueIn.bytes()));
                            break;
                        }

                        if (aClass.isEnum()) {
                            wire.getValueOut().object(aClass, valueIn.object(aClass));
                            break;
                        }
                        if (aClass.isInterface() || usesSelfDescribing(aClass))
                            break;
                        Marshallable m = (Marshallable) ObjectUtils.newInstance(aClass);
                        valueIn.marshallable(m);
                        wire.getValueOut().marshallable(m);
                    } catch (ClassNotFoundRuntimeException ex) {
                        // Log a warning if the class is not found.
                        if (FIRST_WARN_MISSING_CLASS.compareAndSet(false, true))
                            Jvm.warn().on(BinaryWire.class, "Unable to copy object safely, message will not be repeated: " + ex);
                        copyOne(wire, false);
                    } catch (Exception e) {
                        // Log a warning for any other exceptions.
                        Jvm.warn().on(getClass(), "Unable to copy " + sb + " safely will try anyway " + e);
                    }
                    wire.getValueOut().endTypePrefix();
                }
                break;
            }

            // Handle literals in the wire.
            case TYPE_LITERAL: {
                bytes.uncheckedReadSkipOne();
                @Nullable StringBuilder sb = readUtf8();
                wire.getValueOut().typeLiteral(sb);
                break;
            }

            // Handle event or field names.
            case EVENT_NAME:
            case FIELD_NAME_ANY:
                @Nullable StringBuilder fsb = readField(peekCode, null, ANY_CODE_MATCH.code(), acquireStringBuilder(), true);
                wire.write(fsb);
                break;

            // Start an event object.
            case EVENT_OBJECT:
                bytes.uncheckedReadSkipOne();
                wire.writeStartEvent();
                boolean wasLeaf = wire.getValueOut().swapLeaf(true);
                if (peekCode() == TYPE_PREFIX)
                    copyOne(wire);
                copyOne(wire);
                wire.getValueOut().swapLeaf(wasLeaf);
                wire.writeEndEvent();
                break;

            // Handle strings.
            case STRING_ANY: {
                bytes.uncheckedReadSkipOne();
                @Nullable StringBuilder sb1 = readUtf8();
                wire.getValueOut().text(sb1);
                break;
            }

            // Handle field numbers.
            case FIELD_NUMBER: {
                bytes.uncheckedReadSkipOne();
                long code2 = bytes.readStopBit();
                if (code2 == MethodReader.MESSAGE_HISTORY_METHOD_ID && !wire.isBinary()) {
                    wire.write(MethodReader.HISTORY);
                    break;
                }
                wire.write(new WireKey() {
                    @NotNull
                    @Override
                    public String name() {
                        return Long.toString(code2);
                    }

                    @Override
                    public int code() {
                        return (int) code2;
                    }
                });
                break;
            }

            // Handle boolean values and null.
            case NULL:
                bytes.uncheckedReadSkipOne();
                wire.getValueOut().bool(null);
                break;

            case FALSE:
                bytes.uncheckedReadSkipOne();
                wire.getValueOut().bool(false);
                break;

            case TRUE:
                bytes.uncheckedReadSkipOne();
                wire.getValueOut().bool(true);
                break;
            default:
                unknownCode(wire);
        }
    }

    /**
     * Checks if a given class is self-describing.
     *
     * @param aClass The class to check.
     * @return True if the class is self-describing, false otherwise.
     */
    private boolean usesSelfDescribing(Class aClass) {
        Boolean selfDesc = overrideSelfDescribing == null ? USES_SELF_DESCRIBING.get(aClass) : overrideSelfDescribing;

        // Return true if the class is marked as self-describing.
        return Boolean.TRUE.equals(selfDesc);
    }

    /**
     * Decodes an integer from its binary representation based on a code.
     *
     * @param code The code that indicates the encoding.
     * @return The decoded integer.
     */
    long readInt(int code) {
        // Direct value for codes less than 128.
        if (code < 128)
            return code;
        switch (code >> 4) {
            case BinaryWireHighCode.SPECIAL:
                switch (code) {
                    // Handle special cases for boolean false and true.
                    case FALSE:
                        return 0;
                    case TRUE:
                        return 1;
                }
                break;

            case BinaryWireHighCode.FLOAT:
                // For float codes, read the float and cast to long.
                double d = readFloat0(code);
                return (long) d;

            case BinaryWireHighCode.INT:
                // For integer codes, decode the integer.
                return readInt0(code);
        }

        // If we can't decode, throw an exception.
        throw new UnsupportedOperationException(stringForCode(code));
    }

    /**
     * Decodes a floating point number from its binary representation based on a code.
     *
     * @param code The code that indicates the encoding.
     * @return The decoded floating point number.
     */
    double readFloat0(int code) {
        // Check if the high bit is set (indicating a special encoding).
        // This might be redundant in some call sites, so optimization is needed.
        if ((code & 0x80) == 0) {
            return code;
        }

        switch (code) {
            case FLOAT32:
                // 32-bit floating point representation.
                return bytes.readFloat();
            case FLOAT_STOP_2:
                // Decode and adjust by factor of 1e2.
                return bytes.readStopBit() / 1e2;
            case FLOAT_STOP_4:
                // Decode and adjust by factor of 1e4.
                return bytes.readStopBit() / 1e4;
            case FLOAT_STOP_6:
                // Decode and adjust by factor of 1e6.
                return bytes.readStopBit() / 1e6;
            case FLOAT64:
                // 64-bit floating point representation.
                return bytes.readDouble();
        }
        throw new UnsupportedOperationException(stringForCode(code));
    }

    /**
     * Decodes a floating point number from its binary representation and boxes it as a Number.
     * NOTE: This method boxes primitives, which might produce garbage.
     *
     * @param code The code indicating the encoding.
     * @return The decoded floating point number as a Number.
     */
    // TODO: boxes and creates garbage
    private Number readFloat0bject(int code) {
        // TODO: in some places we have already called this before invoking the function,
        // so we should review them and optimize the calls to do the check only once
        // Handle small positive integers without special encoding.
        if (code < 128 && code >= 0) {
            return code;
        }

        switch (code) {
            case FLOAT32:
                // 32-bit floating point representation.
                return bytes.readFloat();
            case FLOAT_STOP_2:
                // Decode and adjust by a factor of 1e2.
                return bytes.readStopBit() / 1e2;
            case FLOAT_STOP_4:
                // Decode and adjust by a factor of 1e4.
                return bytes.readStopBit() / 1e4;
            case FLOAT_STOP_6:
                // Decode and adjust by a factor of 1e6.
                return bytes.readStopBit() / 1e6;
            case FLOAT64:
                // 64-bit floating point representation.
                return bytes.readDouble();
        }

        // If we can't decode, throw an exception.
        throw new UnsupportedOperationException(stringForCode(code));
    }

    /**
     * Decodes an integer from its binary representation based on a code.
     *
     * @param code The code that indicates the encoding.
     * @return The decoded integer.
     */
    long readInt0(int code) {
        // Handle small positive integers without special encoding.
        if (isSmallInt(code))
            return code;

        switch (code) {
            case INT8:
                // 8-bit signed integer.
                return bytes.readByte();
            case UINT8:
            case SET_LOW_INT8:
                // 8-bit unsigned integer.
                return bytes.readUnsignedByte();
            case INT16:
                // 16-bit signed integer.
                return bytes.readShort();
            case UINT16:
            case SET_LOW_INT16:
                // 16-bit unsigned integer.
                return bytes.readUnsignedShort();
            case INT32:
                // 32-bit signed integer.
                return bytes.readInt();
            case UINT32:
                // 32-bit unsigned integer.
                return bytes.readUnsignedInt();
            case INT64:
            case INT64_0x:
                // 64-bit signed integer.
                return bytes.readLong();
        }

        // If we can't decode, throw an exception.
        throw new UnsupportedOperationException(stringForCode(code));
    }

    /**
     * Decodes an integer value from its binary representation and boxes it as a Number.
     * NOTE: This method boxes primitive numbers, which might introduce garbage in a garbage-collected environment.
     *
     * @param code The code that indicates the encoding.
     * @return The decoded integer value as a Number.
     */
    // TODO: boxes and creates garbage
    Number readInt0object(int code) {
        // Check if the number is a small integer (no encoding needed).
        if (isSmallInt(code))
            return code;

        switch (code) {
            case INT8:
                // 8-bit signed integer.
                return bytes.readByte();
            case UINT8:
            case SET_LOW_INT8:
                // 8-bit unsigned integer.
                return bytes.readUnsignedByte();
            case INT16:
                // 16-bit signed integer.
                return bytes.readShort();
            case SET_LOW_INT16:
            // 16-bit unsigned integer.
            case UINT16:
                return bytes.readUnsignedShort();
            case INT32:
                // 32-bit signed integer.
                return bytes.readInt();
            case UINT32:
                // 32-bit unsigned integer.
                return bytes.readUnsignedInt();
            case INT64:
            case INT64_0x:
                // 64-bit signed integer.
                return bytes.readLong();
        }

        // If the encoding is unrecognized, throw an exception.
        throw new UnsupportedOperationException(stringForCode(code));
    }

    /**
     * Determines if the given code corresponds to a small integer (7-bits).
     *
     * @param code The integer code.
     * @return True if it's a small integer; false otherwise.
     */
    private boolean isSmallInt(int code) {
        return (code & 128) == 0;
    }

    /**
     * Decodes a floating point number from its binary representation.
     *
     * @param code The code indicating the encoding type.
     * @return The decoded floating point value.
     */
    double readFloat(int code) {
        // If the number is a small integer, return it directly as a double.
        if (code < 128)
            return code;
        switch (code >> 4) {
            case BinaryWireHighCode.FLOAT:
                // Decode the floating point number.
                return readFloat0(code);

            case BinaryWireHighCode.INT:
                // Convert the integer to double.
                return readInt0(code);
        }

        // If the encoding is unrecognized, throw an exception.
        throw new UnsupportedOperationException(stringForCode(code));
    }

    @NotNull
    @Override
    public ValueOut write() {
        if (!fieldLess) {
            writeField("");
        }
        return valueOut;
    }

    @NotNull
    @Override
    public ValueOut writeEventName(@NotNull WireKey key) {
        return writeEventName(key.name());
    }

    @NotNull
    @Override
    public ValueOut writeEventName(@NotNull CharSequence name) {
        if (bytes.retainedHexDumpDescription())
            bytes.writeHexDumpDescription(name + ": (event)");
        writeCode(EVENT_NAME).write8bit(name);
        return valueOut;
    }

    @Override
    public ValueOut writeEventId(int methodId) {
        writeCode(FIELD_NUMBER).writeStopBit(methodId);
        return valueOut;
    }

    @Override
    public ValueOut writeEventId(String name, int methodId) {
        if (bytes.retainedHexDumpDescription()) {
            writeEventIdDescription(name, methodId);
        }
        writeCode(FIELD_NUMBER).writeStopBit(methodId);
        return valueOut;
    }

    /**
     * Writes a description of an event ID which consists of the event name and its corresponding method ID.
     *
     * @param name     The name of the event.
     * @param methodId The ID of the method associated with the event.
     */
    private void writeEventIdDescription(String name, int methodId) {
        try (ScopedResource sbTl = SBP.get()) {
            final StringBuilder sb = sbTl.get();
            sb.append(name).append(" (");

            // Check if the methodId falls within the printable ASCII character range.
            if (' ' < methodId && methodId <= '~')
                sb.append('\'').append((char) methodId).append('\''); // Represent methodId as a character.
            else
                sb.append(methodId); // Use the integer representation.

            sb.append(')');

            // Write the description to bytes in hexadecimal format.
            bytes.writeHexDumpDescription(sb);
        }
    }

    @Override
    public void writeStartEvent() {
        writeCode(EVENT_OBJECT);
    }

    @Override
    public void writeEndEvent() {
        // Do nothing
    }

    @NotNull
    @Override
    public ValueOut write(@NotNull WireKey key) {
        if (!fieldLess) {
            if (numericFields)
                writeField(key.code());
            else
                writeField(key.name());
        }
        return valueOut;
    }

    @NotNull
    @Override
    public ValueOut write(@NotNull CharSequence key) {
        if (!fieldLess) {
            if (numericFields)
                writeField(WireKey.toCode(key));
            else
                writeField(key);
        }
        return valueOut;
    }

    @NotNull
    @Override
    public ValueOut getValueOut() {
        return valueOut;
    }

    @NotNull
    @Override
    public Wire writeComment(CharSequence s) {
        writeCode(COMMENT);
        bytes.writeUtf8(s);
        return BinaryWire.this;
    }

    @NotNull
    @Override
    public WireOut addPadding(int paddingToAdd) {
        if (paddingToAdd < 0)
            throw new IllegalStateException("Cannot add " + paddingToAdd + " bytes of padding");
        if (paddingToAdd >= 5) {
            writeCode(PADDING32)
                    .writeUnsignedInt(paddingToAdd - 5L)
                    .writeSkip(paddingToAdd - 5L);

        } else {
            for (int i = 0; i < paddingToAdd; i++)
                writeCode(PADDING);
        }
        return this;
    }

    /**
     * Writes a field to the byte buffer with its name.
     *
     * @param name The name of the field.
     */
    private void writeField(@NotNull CharSequence name) {
        // If hex dump retention is enabled, write the field's name as a hex dump description.
        if (bytes.retainedHexDumpDescription())
            bytes.writeHexDumpDescription(name + ":");
        int len = name.length();

        // Decide on encoding strategy based on length of name.
        if (len < 0x20) {
            writeField0(name, len);

        } else {
            writeCode(FIELD_NAME_ANY).write8bit(name);
        }
    }

    /**
     * Writes a field to the byte buffer with the given name and length.
     * If the name starts with a digit, tries to parse it as an integer.
     *
     * @param name The name of the field.
     * @param len  The length of the field name.
     */
    private void writeField0(@NotNull CharSequence name, int len) {
        // If name starts with a digit, attempt to parse it as an integer.
        if (len > 0 && isDigit(name.charAt(0))) {
            try {
                writeField(StringUtils.parseInt(name, 10));
                return;
            } catch (NumberFormatException ignored) {
            }
        }

        // Write the length-prefixed name.
        bytes.writeByte((byte) (FIELD_NAME0 + len));
        bytes.append8bit(name);
    }

    /**
     * Writes a field to the byte buffer using a numeric code representation.
     *
     * @param code The numeric code of the field.
     */
    private void writeField(int code) {
        // If hex dump retention is enabled, write the code as a hex dump description.
        if (bytes.retainedHexDumpDescription())
            bytes.writeHexDumpDescription(Integer.toString(code));

        // Write the code for the field and then its numeric representation.
        writeCode(FIELD_NUMBER);
        bytes.writeStopBit(code);
    }

    /**
     * Writes a byte code to the byte buffer.
     *
     * @param code The byte code to write.
     * @return Returns the byte buffer after writing the code.
     */
    protected Bytes writeCode(int code) {
        return bytes.writeByte((byte) code);
    }

    /**
     * Reads and decodes binary data based on a given code into a textual form.
     *
     * @param code The code indicating the type of data to be read.
     * @param sb   An appendable and char sequence target where the decoded text will be appended.
     * @param   A type that extends both Appendable and CharSequence.
     * @return Returns the passed-in appendable populated with decoded text or null.
     */
    @Nullable  T readText(int code, @NotNull T sb) {

        // If the code is an ASCII value, simply append it to the StringBuilder.
        if (code <= 127) {
            AppendableUtil.append(sb, code);
            return sb;
        }

        // Split the parsing based on the high bits of the code.
        switch (code >> 4) {
            case BinaryWireHighCode.CONTROL:
                // Handle various control codes.
                switch (code) {
                    // Handle byte-length based data reading.
                    case BYTES_LENGTH8:
                    case BYTES_LENGTH16:
                    case BYTES_LENGTH32:
                        if (sb instanceof StringBuilder) {
                            bytes.uncheckedReadSkipBackOne();
                            valueIn.bytesStore((StringBuilder) sb);
                        } else if (sb instanceof Bytes) {
                            bytes.uncheckedReadSkipBackOne();
                            valueIn.bytesStore((Bytes) sb);
                        } else {
                            throw new IllegalArgumentException("Expected a StringBuilder or Bytes");
                        }
                        return sb;

                    // Handle padding.
                    case PADDING:
                        return readText(bytes.readUnsignedByte(), sb);
                    case PADDING32:
                        bytes.readSkip(bytes.readUnsignedInt());
                        return readText(bytes.readUnsignedByte(), sb);
                }
                throw unknownCode(code);

            case BinaryWireHighCode.SPECIAL:
                // Handle special codes.
                switch (code) {
                    case NULL:
                        AppendableUtil.append(sb, "null");
                        return sb;
                    case TRUE:
                        AppendableUtil.append(sb, "true");
                        return sb;
                    case FALSE:
                        // Handle common special values.
                        AppendableUtil.append(sb, "false");
                        return sb;
                    case TIME:
                    case DATE:
                    case DATE_TIME:
                    case ZONED_DATE_TIME:
                    case TYPE_LITERAL:
                    case STRING_ANY:
                        // Handle various string/textual representations.
                        if (bytes.readUtf8(sb))
                            return sb;
                        return null;
                    case EVENT_OBJECT:
                        valueIn.text((StringBuilder) sb);
                        return sb;
                }
                throw unknownCode(code);

            case BinaryWireHighCode.FLOAT:
                // Handle float values.
                AppendableUtil.append(sb, readFloat(code));
                return sb;
            case BinaryWireHighCode.INT:
                // Handle integer values.
                AppendableUtil.append(sb, readInt(code));
                return sb;
            case BinaryWireHighCode.STR0:
            case BinaryWireHighCode.STR1:
                // Handle string values.
                return getStringBuilder(code, sb);

            case BinaryWireHighCode.FIELD0:
            case BinaryWireHighCode.FIELD1:
                try (final ScopedResource sbTl = SBP.get()) {
                    readField(sbTl.get(), "", code);
                }
                AppendableUtil.setLength(sb, 0);
                return readText(peekCode(), sb);
            default:
                throw unknownCode(code);
        }
    }

    /**
     * Generates an UnsupportedOperationException when an unknown code is encountered.
     * 

* The function formats the provided code to a hexadecimal string and * includes it in the exception message. * * @param code The code that was not recognized or understood. * @return A new UnsupportedOperationException initialized with a message detailing the unknown code. */ @NotNull private UnsupportedOperationException unknownCode(int code) { // Format the code as a hexadecimal string and return the exception. return new UnsupportedOperationException("code=0x" + String.format("%02X ", code).trim()); } /** * Reads a code from the byte storage without checks. *

* This method retrieves a code that indicates the format or type of the next data * to be read from the bytes storage. * * @return The code retrieved from the byte storage. */ int readCode() { // Use unchecked method to read the next unsigned byte as code. return bytes.uncheckedReadUnsignedByte(); } /** * Converts the current state of the bytes storage to a debug string. *

* This method provides a string representation of the bytes, useful for * debugging purposes. * * @return A string representation of the bytes. */ @NotNull public String toString() { return bytes.toDebugString(); } @NotNull @Override public LongValue newLongReference() { return new BinaryLongReference(); } @NotNull @Override public BooleanValue newBooleanReference() { return new BinaryBooleanReference(); } @NotNull @Override public TwoLongValue newTwoLongReference() { return new BinaryTwoLongReference(); } @NotNull @Override public IntValue newIntReference() { return new BinaryIntReference(); } @NotNull @Override public BinaryLongArrayReference newLongArrayReference() { return new BinaryLongArrayReference(); } @Override public @NotNull IntArrayValues newIntArrayReference() { return new BinaryIntArrayReference(); } /** * Reads a UTF-8 formatted string from the byte storage. *

* The method retrieves a string encoded in UTF-8 from the bytes storage, * appending the characters to a StringBuilder. * * @return A StringBuilder populated with the read string if successful, or null if not. */ @Nullable StringBuilder readUtf8() { @NotNull StringBuilder sb = acquireStringBuilder(); // Read the UTF-8 encoded string into the StringBuilder. return bytes.readUtf8(sb) ? sb : null; } /** * Determines whether a given object should use the self-describing message format. *

* This method first checks if an override value for self-describing is set. If not, * it uses the self-describing value from the provided object. * * @param object The object whose self-describing status needs to be checked. * @return true if the object should use the self-describing message format, false otherwise. */ public boolean useSelfDescribingMessage(@NotNull CommonMarshallable object) { // Check for override or get the value from the object. return overrideSelfDescribing == null ? object.usesSelfDescribingMessage() : overrideSelfDescribing; } @Override public boolean writingIsComplete() { return !writeContext.isNotComplete(); } /** * Enum representing any code match within the WireKey interface. * This enum is designed to provide a specific code that represents any possible match. */ enum AnyCodeMatch implements WireKey { ANY_CODE_MATCH; @Override public int code() { return Integer.MIN_VALUE; } } /** * Implementation of the ValueOut interface, providing methods to write values in a fixed binary format. * This inner class offers various methods to output values in a binary format that retains its order. */ protected class FixedBinaryValueOut implements ValueOut { @NotNull @Override public WireOut bool(@Nullable Boolean flag) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(flag == null ? "null" : flag ? "true" : "false"); bytes.writeUnsignedByte(flag == null ? NULL : (flag ? TRUE : FALSE)); return BinaryWire.this; } @NotNull @Override public WireOut nu11() { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("null"); writeCode(NULL); return BinaryWire.this; } @NotNull @Override public WireOut text(@Nullable CharSequence s) { if (s == null) { nu11(); } else { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(s); long utflen; if (s.length() < 0x20 && (utflen = AppendableUtil.findUtf8Length(s)) < 0x20) { bytes.writeUnsignedByte((int) (STRING_0 + utflen)).appendUtf8(s); } else { writeCode(STRING_ANY); bytes.writeUtf8(s); } } return BinaryWire.this; } @NotNull @Override public WireOut text(@Nullable String s) { if (s == null) { writeCode(NULL); } else { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(s); int len = s.length(); if (len < 0x20) len = (int) AppendableUtil.findUtf8Length(s); char ch; if (len == 0) { bytes.writeUnsignedByte(STRING_0); } else if (len == 1 && (ch = s.charAt(0)) < 128) { bytes.writeUnsignedByte(STRING_0 + 1).writeUnsignedByte(ch); } else if (len < 0x20) { bytes.writeUnsignedByte(STRING_0 + len).appendUtf8(s); } else { writeCode(STRING_ANY); bytes.writeUtf8(s); } } return BinaryWire.this; } @NotNull @Override public WireOut text(@Nullable BytesStore s) { if (s == null) { writeCode(NULL); } else { int len = s.length(); if (len < 0x20) { bytes.writeUnsignedByte(STRING_0 + len).appendUtf8(s); } else { writeCode(STRING_ANY).writeUtf8(s); } } return BinaryWire.this; } @NotNull @Override public WireOut bytes(@Nullable BytesStore fromBytes) { if (fromBytes == null) return nu11(); long remaining = fromBytes.readRemaining(); if (remaining >= compressedSize()) { compress(compression, fromBytes.bytesForRead()); } else { bytes0(fromBytes, remaining); } return BinaryWire.this; } @NotNull @Override public WireOut bytesLiteral(@Nullable BytesStore fromBytes) { if (fromBytes == null) return nu11(); long remaining = fromBytes.readRemaining(); writeLength(Maths.toInt32(remaining)); bytes.write(fromBytes); return BinaryWire.this; } @Override public int compressedSize() { return compressedSize; } /** * Writes bytes from the given BytesStore to the byte storage. *

* It first writes the length of the bytes and then the bytes themselves. * This method ensures the U8_ARRAY code is prefixed before the actual byte data. * * @param fromBytes The source of the bytes to be written. * @param remaining The number of bytes to be written. */ public void bytes0(@NotNull BytesStore fromBytes, long remaining) { // Write the length of the bytes. writeLength(Maths.toInt32(remaining + 1)); // Write the U8_ARRAY code. writeCode(U8_ARRAY); // Write the actual bytes if there are any. if (remaining > 0) bytes.write(fromBytes); } @NotNull @Override public WireOut rawBytes(@NotNull byte[] value) { typePrefix(byte[].class); writeLength(Maths.toInt32(value.length + 1L)); writeCode(U8_ARRAY); if (value.length > 0) bytes.write(value); return BinaryWire.this; } @Override @NotNull public ValueOut writeLength(long length) { if (length < 0) { throw new IllegalArgumentException("Invalid length " + length); } else if (length < 1 << 8) { writeCode(BYTES_LENGTH8); bytes.writeUnsignedByte((int) length); } else if (length < 1 << 16) { writeCode(BYTES_LENGTH16); bytes.writeUnsignedShort((int) length); } else { writeCode(BYTES_LENGTH32); bytes.writeUnsignedInt(length); } return this; } @NotNull @Override public WireOut bytes(@NotNull byte[] fromBytes) { writeLength(Maths.toInt32(fromBytes.length + 1L)); writeCode(U8_ARRAY); bytes.write(fromBytes); return BinaryWire.this; } @NotNull @Override public WireOut bytes(String type, @Nullable BytesStore fromBytes) { typePrefix(type); if (fromBytes != null) bytes0(fromBytes, fromBytes.readRemaining()); return BinaryWire.this; } @NotNull @Override public WireOut bytes(String type, @NotNull byte[] fromBytes) { typePrefix(type); return bytes(fromBytes); } @NotNull @Override public WireOut int8(byte i8) { return fixedInt8(i8); } @Override @NotNull public WireOut fixedInt8(byte i8) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Integer.toString(i8)); writeCode(INT8).writeByte(i8); return BinaryWire.this; } @NotNull @Override public WireOut uint8checked(int u8) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Integer.toString(u8)); writeCode(UINT8).writeUnsignedByte(u8); return BinaryWire.this; } @NotNull @Override public WireOut int16(short i16) { return fixedInt16(i16); } @Override @NotNull public WireOut fixedInt16(short i16) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Integer.toString(i16)); writeCode(INT16).writeShort(i16); return BinaryWire.this; } @NotNull @Override public WireOut uint16checked(int u16) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Integer.toString(u16)); writeCode(UINT16).writeUnsignedShort(u16); return BinaryWire.this; } @NotNull @Override public WireOut utf8(int codepoint) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(new String(Character.toChars(codepoint))); writeCode(UINT16); bytes.appendUtf8(codepoint); return BinaryWire.this; } @NotNull @Override public WireOut int32(int i32) { return fixedInt32(i32); } @Override @NotNull public WireOut fixedInt32(int i32) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Integer.toString(i32)); writeCode(INT32).writeInt(i32); return BinaryWire.this; } /** * Writes a 32-bit integer value to the byte storage in an order-preserving binary format. *

* Additionally, if hex dump description is retained, it writes a hex dump description. * * @param i32 The 32-bit integer value to be written. * @return The current WireOut instance. */ @NotNull public WireOut fixedOrderedInt32(int i32) { // Check if hex dump description should be written. if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Integer.toString(i32)); // Write the integer value in an ordered format. writeCode(INT32).writeOrderedInt(i32); return BinaryWire.this; } @NotNull @Override public WireOut uint32checked(long u32) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Long.toUnsignedString(u32)); writeCode(UINT32).writeUnsignedInt(u32); return BinaryWire.this; } @NotNull @Override public WireOut int64(long i64) { return fixedInt64(i64); } @Override @NotNull public WireOut fixedInt64(long i64) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Long.toString(i64)); writeCode(INT64).writeLong(i64); return BinaryWire.this; } @NotNull @Override public WireOut int64_0x(long i64) { writeCode(INT64_0x).writeLong(i64); return BinaryWire.this; } /** * Writes a 64-bit integer value to the byte storage in an order-preserving binary format. *

* Additionally, if hex dump description is retained, it writes a hex dump description. * This method ensures that the writing is aligned to 8 bytes and then the INT64 code is * prefixed before the actual 64-bit data. * * @param i64 The 64-bit integer value to be written. * @return The current WireOut instance. */ @NotNull private WireOut fixedOrderedInt64(long i64) { // Check if hex dump description should be written. if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Long.toString(i64)); // Align to 8 bytes before writing the 64-bit integer. writeAlignTo(8, 1); // Write the 64-bit integer value in an ordered format. writeCode(INT64).writeOrderedLong(i64); return BinaryWire.this; } @NotNull @Override public WireOut int64array(long capacity) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Long.toString(capacity)); writeAlignTo(8, 1); writeCode(I64_ARRAY); BinaryLongArrayReference.lazyWrite(bytes, capacity); return BinaryWire.this; } @NotNull @Override public WireOut int128forBinding(long value, long value2) { writeAlignTo(16, 1); writeCode(I64_ARRAY); bytes.writeLong(2); bytes.writeLong(2); bytes.writeLong(value); bytes.writeLong(value2); return BinaryWire.this; } @NotNull @Override public WireOut int64array(long capacity, @NotNull LongArrayValues values) { writeAlignTo(8, 1); writeCode(I64_ARRAY); long pos = bytes.writePosition(); BinaryLongArrayReference.lazyWrite(bytes, capacity); ((Byteable) values).bytesStore(bytes, pos, bytes.lengthWritten(pos)); return BinaryWire.this; } @NotNull @Override public WireOut float32(float f) { return fixedFloat32(f); } @Override @NotNull public WireOut fixedFloat32(float f) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Float.toString(f)); writeCode(FLOAT32).writeFloat(f); return BinaryWire.this; } @NotNull @Override public WireOut float64(double d) { return fixedFloat64(d); } @Override @NotNull public WireOut fixedFloat64(double d) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(Double.toString(d)); writeCode(FLOAT64).writeDouble(d); return BinaryWire.this; } @NotNull @Override public WireOut time(@NotNull LocalTime localTime) { final String text = localTime.toString(); if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(text); writeCode(TIME).writeUtf8(text); return BinaryWire.this; } @NotNull @Override public WireOut zonedDateTime(@NotNull ZonedDateTime zonedDateTime) { final String text = zonedDateTime.toString(); if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(text); writeCode(ZONED_DATE_TIME).writeUtf8(text); return BinaryWire.this; } @NotNull @Override public WireOut date(@NotNull LocalDate localDate) { final String text = localDate.toString(); if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(text); writeCode(DATE).writeUtf8(text); return BinaryWire.this; } @NotNull @Override public WireOut dateTime(@NotNull LocalDateTime localDateTime) { final String text = localDateTime.toString(); if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(text); writeCode(DATE_TIME).writeUtf8(text); return BinaryWire.this; } @NotNull @Override public ValueOut typePrefix(CharSequence typeName) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(typeName); if (typeName != null) writeCode(TYPE_PREFIX).writeUtf8(typeName); return this; } @Override public ClassLookup classLookup() { return BinaryWire.this.classLookup(); } @NotNull @Override public WireOut typeLiteral(CharSequence typeName) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(typeName); if (typeName == null) nu11(); else writeCode(TYPE_LITERAL).writeUtf8(typeName); return BinaryWire.this; } @NotNull @Override public WireOut typeLiteral(@Nullable Class type) { if (bytes.retainedHexDumpDescription() && type != null) bytes.writeHexDumpDescription(type.getName()); if (type == null) nu11(); else writeCode(TYPE_LITERAL).writeUtf8(classLookup().nameFor(type)); return BinaryWire.this; } @NotNull @Override public WireOut typeLiteral(@NotNull BiConsumer> typeTranslator, @Nullable Class type) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(type == null ? null : type.getName()); writeCode(TYPE_LITERAL); typeTranslator.accept(type, bytes); return BinaryWire.this; } @NotNull @Override public WireOut uuid(@NotNull UUID uuid) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(uuid.toString()); writeCode(UUID).writeLong(uuid.getMostSignificantBits()).writeLong(uuid.getLeastSignificantBits()); return BinaryWire.this; } @NotNull @Override public WireOut int32forBinding(int value) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("int32 for binding"); writeAlignTo(Integer.BYTES, 1); fixedInt32(value); return BinaryWire.this; } @NotNull @Override public WireOut int64forBinding(long value) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("int64 for binding"); writeAlignTo(Long.BYTES, 1); fixedOrderedInt64(value); return BinaryWire.this; } @NotNull @Override public WireOut int32forBinding(int value, @NotNull IntValue intValue) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("int32 for binding"); int32forBinding(value); ((BinaryIntReference) intValue).bytesStore(bytes, bytes.writePosition() - 4, 4); return BinaryWire.this; } @NotNull @Override public WireOut int64forBinding(long value, @NotNull LongValue longValue) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("int64 for binding"); int64forBinding(value); ((BinaryLongReference) longValue).bytesStore(bytes, bytes.writePosition() - 8, 8); return BinaryWire.this; } @NotNull @Override public WireOut boolForBinding(final boolean value, @NotNull final BooleanValue booleanValue) { bool(value); ((BinaryBooleanReference) booleanValue).bytesStore(bytes, bytes.writePosition() - 1, 1); return BinaryWire.this; } @NotNull @Override public WireOut int128forBinding(long i64x0, long i64x1, TwoLongValue longValue) { int128forBinding(i64x0, i64x1); ((BinaryTwoLongReference) longValue).bytesStore(bytes, bytes.writePosition() - 16, 16); return BinaryWire.this; } @NotNull @Override public WireOut sequence(T t, @NotNull BiConsumer writer) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("sequence"); writeCode(BYTES_LENGTH32); long position = bytes.writePosition(); bytes.writeInt(0); writer.accept(t, this); setSequenceLength(position); return BinaryWire.this; } /** * Sets the length of the sequence in the byte storage at the given position. *

* It calculates the length from the current position to the specified position, * then writes this length at the specified position. If the bytes instance is * of type HexDumpBytes, the length is written directly. Otherwise, a check is * performed to ensure that the length fits within a 32-bit integer. * * @param position The position in the byte storage where the length should be written. */ private void setSequenceLength(long position) { // Calculate the length from the current position to the given position. long length0 = bytes.lengthWritten(position) - 4; // Determine the actual length to be written based on the type of bytes instance. int length = bytes instanceof HexDumpBytes ? (int) length0 : Maths.toInt32(length0, "Document length %,d out of 32-bit int range."); // Write the calculated length at the specified position. bytes.writeInt(position, length); } @NotNull @Override public WireOut sequence(T t, K kls, @NotNull TriConsumer writer) throws InvalidMarshallableException { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription("sequence"); writeCode(BYTES_LENGTH32); long position = bytes.writePosition(); bytes.writeInt(0); writer.accept(t, kls, this); setSequenceLength(position); return BinaryWire.this; } @NotNull @Override public WireOut marshallable(@NotNull WriteMarshallable object) throws InvalidMarshallableException { if (bytes.retainedHexDumpDescription()) { Class aClass = object.getClass(); String simpleName = Jvm.isLambdaClass(aClass) ? "Marshallable" : aClass.getSimpleName(); bytes.writeHexDumpDescription(simpleName); } final BinaryLengthLength binaryLengthLength = object.binaryLengthLength(); long pos = binaryLengthLength.initialise(bytes); if (useSelfDescribingMessage(object)) object.writeMarshallable(BinaryWire.this); else ((WriteBytesMarshallable) object).writeMarshallable(BinaryWire.this.bytes()); binaryLengthLength.writeLength(bytes, pos, bytes.writePosition()); return BinaryWire.this; } @Override public WireOut bytesMarshallable(WriteBytesMarshallable object) throws InvalidMarshallableException { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(object.getClass().getSimpleName()); writeCode(BYTES_LENGTH32); long position = bytes.writePosition(); bytes.writeInt(0); object.writeMarshallable(BinaryWire.this.bytes()); long length = bytes.lengthWritten(position) - 4; if (length > Integer.MAX_VALUE && bytes instanceof HexDumpBytes) length = (int) length; bytes.writeOrderedInt(position, Maths.toInt32(length, "Document length %,d out of 32-bit int range.")); return BinaryWire.this; } @NotNull @Override public WireOut marshallable(@NotNull Serializable object) throws InvalidMarshallableException { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(object.getClass().getSimpleName()); writeCode(BYTES_LENGTH32); long position = bytes.writePosition(); bytes.writeInt(0); try { if (object instanceof Externalizable) { ((Externalizable) object).writeExternal(objectOutput()); } else { Wires.writeMarshallable(object, BinaryWire.this); } } catch (IOException e) { throw new IORuntimeException(e); } bytes.writeOrderedInt(position, Maths.toInt32(bytes.lengthWritten(position) - 4, "Document length %,d out of 32-bit int range.")); return BinaryWire.this; } @NotNull @Override public WireOut map(Map map) throws InvalidMarshallableException { return marshallable(map); } @NotNull @Override public WireOut wireOut() { return BinaryWire.this; } @Override public void resetState() { // Do nothing } @Override public void elementSeparator() { throw new UnsupportedOperationException(); } } /** * Extends the FixedBinaryValueOut class to support binary value outputs with additional logic for converting * and writing different types of numbers, namely integers and longs. */ protected class BinaryValueOut extends FixedBinaryValueOut { @Override public boolean isBinary() { return true; } @Override public WireOut writeInt(LongConverter converter, int i) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(converter.asString(i)); return writeInt(i); } @Override public WireOut writeLong(LongConverter longConverter, long l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(longConverter.asString(l)); return writeLong(l); } @NotNull @Override public WireOut int8(byte i8) { writeNumber(i8); return BinaryWire.this; } /** * Writes a number (long value) in an optimized binary format based on the value's size. * Uses the number of leading zeros to determine the smallest binary representation * that can accommodate the given value. * * @param l The number (long value) to be written. */ void writeNumber(long l) { // Check the number of leading zeros to determine the best representation. switch (Long.numberOfLeadingZeros(l)) { // Cases for very small numbers that can fit in 8 bits. case 64: case 63: case 62: case 61: case 60: case 59: case 58: case 57: case 56: super.uint8checked((short) l); return; // Cases for numbers that can fit in 16 bits. case 55: case 54: case 53: case 52: case 51: case 50: case 49: super.fixedInt16((short) l); return; case 48: super.uint16checked((int) l); return; // Cases for numbers that can fit in 32 bits. case 47: case 46: case 45: case 44: case 43: case 42: case 41: case 40: case 39: case 38: case 37: case 36: case 35: case 34: case 33: super.fixedInt32((int) l); return; case 32: super.uint32checked(l); return; case 0: // Handling negative numbers if (l >= Byte.MIN_VALUE) { super.int8((byte) l); return; } if (l >= Short.MIN_VALUE) { super.int16((short) l); return; } if (l >= Integer.MIN_VALUE) { super.int32((int) l); return; } break; default: break; } // Fallback checks if the number can be represented as a 32-bit float if ((long) (float) l == l) { super.float32(l); return; } // Default case for larger numbers. super.int64(l); } /** * Writes an integer number in an optimized binary format based on the value's size. * Uses the number of leading zeros to determine the smallest binary representation * that can accommodate the given value. * * @param l The number (int value) to be written. */ void writeNumber(int l) { // Offset leading zeros count for an integer to align with the long representation logic switch (Integer.numberOfLeadingZeros(l) + 32) { // Cases for very small numbers that can fit in 8 bits. case 64: case 63: case 62: case 61: case 60: case 59: case 58: case 57: case 56: super.uint8checked((short) l); return; // Cases for numbers that can fit in 16 bits. case 55: case 54: case 53: case 52: case 51: case 50: case 49: super.fixedInt16((short) l); return; case 48: super.uint16checked(l); return; // Cases for numbers that can fit in 32 bits. case 47: case 46: case 45: case 44: case 43: case 42: case 41: case 40: case 39: case 38: case 37: case 36: case 35: case 34: case 33: super.fixedInt32(l); return; case 32: // Handling negative numbers if (l >= Byte.MIN_VALUE) { super.int8((byte) l); } else if (l >= Short.MIN_VALUE) { super.int16((short) l); } else { super.int32(l); } return; default: assert false; // Assertion to ensure all possible cases are handled } } /** * Writes a float number either as a float or as an integer based on its value. * * @param l The float number to be written. */ void writeNumber(float l) { boolean canOnlyBeRepresentedAsFloatingPoint = ((long) l) != l; if (canOnlyBeRepresentedAsFloatingPoint) { writeAsFloat(l); } else { writeAsIntOrFloat(l); } } /** * Writes a double number either as a float or as an integer based on its value. * * @param l The double number to be written. */ void writeNumber(double l) { boolean canOnlyBeRepresentedAsFloatingPoint = ((long) l) != l; if (canOnlyBeRepresentedAsFloatingPoint) { writeAsFloat(l); } else { writeAsIntOrFloat(l); } } /** * Writes the given float value either as an integer or a float based on its value and range. * It checks for possible negative values and writes it in the most appropriate format. * * @param l The float value to be written. */ private void writeAsIntOrFloat(float l) { if (l >= 0) { writeAsPositive(l); } else if (l >= Byte.MIN_VALUE) { super.int8((byte) l); // Write as a signed byte if within the range } else if (l >= Short.MIN_VALUE) { super.int16((short) l); // Write as a signed short if within the range } else { super.float32(l); // Write as a float } } /** * Writes the given double value either as an integer, a float, or a double * based on its value and range. It checks for possible negative values * and writes it in the most appropriate format. * * @param l The double value to be written. */ private void writeAsIntOrFloat(double l) { if (l >= 0) { writeAsPositive(l); } else if (l >= Byte.MIN_VALUE) { super.int8((byte) l); // Write as a signed byte if within the range } else if (l >= Short.MIN_VALUE) { super.int16((short) l); // Write as a signed short if within the range } else if ((float) l == l) { super.float32((float) l); // Write as a float if can be precisely represented } else if (l >= Integer.MIN_VALUE) { super.int32((int) l); // Write as a signed int if within the range } else { super.float64(l); // Write as a double } } /** * Writes the given positive double value in an optimized binary format * based on its size. Uses the value to determine the smallest binary representation * that can accommodate it. * * @param l The positive double value to be written. */ private void writeAsPositive(double l) { if (l <= (1 << 8) - 1) { super.uint8checked((short) l); // Write as an unsigned byte } else if (l <= (1 << 16) - 1) { super.uint16checked((int) l); // Write as an unsigned short } else if ((float) l == l) { super.float32((float) l); // Write as a float if can be precisely represented } else if (l <= (1L << 32L) - 1) { super.uint32checked((long) l); // Write as an unsigned int } else { super.float64(l); // Write as a double } } /** * Writes the given float value either as a fixed-point representation or as a regular float. * It first attempts to represent the value as fixed-point, but if that's not possible, * writes it as a standard float. * * @param l The float value to be written. */ private void writeAsFloat(float l) { // Attempt to convert the float to a fixed-point representation with 6 decimal places long l6 = Math.round(l * 1e6); // Check if the fixed-point conversion is valid and within specific bounds if (l6 / 1e6f == l && l6 > (-1L << 2 * 7) && l6 < (1L << 3 * 7)) { if (writeAsFixedPoint(l, l6)) return; // If written successfully as fixed-point, exit the method } // Write as a standard float super.float32(l); } /** * Writes the given double value either as a fixed-point representation, a float, or a double. * It first tries to represent the value as fixed-point. If that's not possible, checks if * it can be represented precisely as a float; otherwise, writes it as a double. * * @param l The double value to be written. */ private void writeAsFloat(double l) { // Attempt to convert the double to a fixed-point representation with 6 decimal places long l6 = Math.round(l * 1e6); // Check if the fixed-point conversion is valid and within specific bounds if (l6 / 1e6 == l && l6 > (-1L << 5 * 7) && l6 < (1L << 6 * 7)) { if (writeAsFixedPoint(l, l6)) return; // If written successfully as fixed-point, exit the method } // Check if the double can be represented precisely as a float or if it's NaN if (((double) (float) l) == l || Double.isNaN(l)) { super.float32((float) l); // Write as a float return; } // Write as a double super.float64(l); } /** * Attempts to write the float value as a fixed-point number with 2, 4, or 6 decimal precision. * If successful, writes the value with the appropriate code. * * @param l The float value. * @param l6 The float value multiplied by 1e6 and rounded. * @return true if the float was written as fixed-point, otherwise false. */ private boolean writeAsFixedPoint(float l, long l6) { // Try 2 decimal precision long i2 = l6 / 10000; if (i2 / 1e2f == l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(i2 + "/1e2"); writeCode(FLOAT_STOP_2).writeStopBit(i2); return true; } // Try 4 decimal precision long i4 = l6 / 100; if (i4 / 1e4f == l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(i4 + "/1e4"); writeCode(FLOAT_STOP_4).writeStopBit(i4); return true; } // Try 6 decimal precision if (l6 / 1e6f == l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(l6 + "/1e6"); writeCode(FLOAT_STOP_6).writeStopBit(l6); return true; } // The float could not be represented as a fixed-point with 2, 4, or 6 decimal precision return false; } /** * Attempts to write the double value as a fixed-point number with 2, 4, or 6 decimal precision. * If successful, writes the value with the appropriate code. * * @param l The double value. * @param l6 The double value multiplied by 1e6 and rounded. * @return true if the double was written as fixed-point, otherwise false. */ private boolean writeAsFixedPoint(double l, long l6) { // (The logic here is the same as for the float method) long i2 = l6 / 10000; if (i2 / 1e2 == l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(i2 + "/1e2"); writeCode(FLOAT_STOP_2).writeStopBit(i2); return true; } long i4 = l6 / 100; if (i4 / 1e4 == l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(i4 + "/1e4"); writeCode(FLOAT_STOP_4).writeStopBit(i4); return true; } if (l6 / 1e6 == l) { if (bytes.retainedHexDumpDescription()) bytes.writeHexDumpDescription(l6 + "/1e6"); writeCode(FLOAT_STOP_6).writeStopBit(l6); return true; } return false; } @NotNull @Override public WireOut uint8checked(int u8) { writeNumber(u8); return BinaryWire.this; } @NotNull @Override public WireOut int16(short i16) { writeNumber(i16); return BinaryWire.this; } @NotNull @Override public WireOut uint16checked(int u16) { writeNumber(u16); return BinaryWire.this; } @NotNull @Override public WireOut int32(int i32) { writeNumber(i32); return BinaryWire.this; } @NotNull @Override public WireOut uint32checked(long u32) { writeNumber(u32); return BinaryWire.this; } @NotNull @Override public WireOut int64(long i64) { writeNumber(i64); return BinaryWire.this; } @NotNull @Override public WireOut float32(float f) { writeNumber(f); return BinaryWire.this; } @NotNull @Override public WireOut float64(double d) { writeNumber(d); return BinaryWire.this; } } /** * Represents a binary input value and provides methods to read and manage the state * of this binary input. The class encapsulates a stack for managing multiple states * and a reader for handling the binary data. */ protected class BinaryValueIn implements ValueIn { // Stack for managing the state of the binary input final ValueInStack stack = new ValueInStack(); // The reader responsible for handling the binary data final Reader reader0field = this::reader0; @Override public boolean isBinary() { return true; } @Override public long readLong(LongConverter longConverter) { return readLong(); } @Override public void resetState() { stack.reset(); } /** * Pushes the current state of the binary input onto the stack. * This is useful for saving a state to be restored later. */ public void pushState() { stack.push(); } /** * Pops the state from the top of the stack and restores the binary input to that state. * This method is useful for returning to a previously saved state. */ public void popState() { stack.pop(); } /** * Retrieves the current state of the binary input. * * @return The current state of the binary input. */ public ValueInState curr() { return stack.curr(); } @NotNull @Override public BracketType getBracketType() { consumePadding(); switch (peekCode()) { case BYTES_LENGTH8: return getBracketTypeFor(bytes.readUnsignedByte(bytes.readPosition() + 1 + 1)); case BYTES_LENGTH16: return getBracketTypeFor(bytes.readUnsignedByte(bytes.readPosition() + 2 + 1)); case BYTES_LENGTH32: return getBracketTypeFor(bytes.readUnsignedByte(bytes.readPosition() + 4 + 1)); case NULL: return BracketType.NONE; } return BracketType.NONE; } /** * Reads text from the wire and consumes it using the provided string consumer. * * @param s A {@link Consumer} that accepts and processes the read string. * @return The instance of BinaryWire that this handler belongs to. */ @NotNull WireIn text(@NotNull Consumer s) { // Consume any padding before reading text consumePadding(); int code = readCode(); switch (code) { case NULL: s.accept(null); break; case STRING_ANY: // Read the UTF-8 string from bytes and consume it s.accept(bytes.readUtf8()); break; default: // Check for special string codes if (code >= STRING_0 && code <= STRING_31) { @NotNull StringBuilder sb = acquireStringBuilder(); // Parse the UTF-8 string based on its length code and consume it bytes.parseUtf8(sb, code & 0b11111); s.accept(WireInternal.INTERNER.intern(sb)); } else { // If an unrecognized code is found, throw an exception cantRead(code); } } return BinaryWire.this; } /** * Checks if the provided code corresponds to text data. * * @param code The code to be checked. * @return true if the code corresponds to text, false otherwise. */ private boolean isText(int code) { // Check for general string code or specific length-based codes return code == STRING_ANY || (code >= STRING_0 && code <= STRING_31); } @Nullable @Override public StringBuilder textTo(@NotNull StringBuilder sb) { int code = readCode(); boolean wasNull = code == NULL; if (wasNull) { sb.setLength(0); return null; } else { @Nullable StringBuilder text = readText(code, sb); if (text == null) cantRead(code); return sb; } } @Nullable @Override public Bytes textTo(@NotNull Bytes bytes) { int code = readCode(); boolean wasNull = code == NULL; if (wasNull) { bytes.readPosition(0); return null; } else { @Nullable Bytes text = readText(code, bytes); if (text == null) cantRead(code); return bytes; } } @Nullable @Override public String text() { int code = readCode(); switch (code) { case NULL: return null; case STRING_ANY: { long len0 = bytes.readStopBit(); if (len0 == -1L) { return null; } int len = Maths.toUInt31(len0); long limit = bytes.readLimit(); long end = bytes.readPosition() + len; try { bytes.readLimit(end); return UTF8.intern(bytes); } finally { bytes.readLimit(limit); bytes.readPosition(end); } } case TYPE_PREFIX: { @Nullable StringBuilder sb = readUtf8(); if (sb != null) { @Nullable byte[] bytes = Compression.uncompress(sb, this, ValueIn::bytes); if (bytes != null) return new String(bytes, StandardCharsets.UTF_8); } @Nullable StringBuilder text = readText(code, acquireStringBuilder()); return WireInternal.INTERNER.intern(text); } default: { StringBuilder sb = acquireStringBuilder(); @Nullable StringBuilder text = ((code & 0xE0) == 0xE0) ? getStringBuilder(code, sb) : readText(code, sb); return text == null ? null : WireInternal.INTERNER.intern(text); } } } @Override @NotNull public WireIn bytes(@NotNull BytesOut toBytes) { return bytes(toBytes, true); } @NotNull @Override public WireIn bytes(@NotNull BytesOut toBytes, boolean clearBytes) { long length = readLength(); int code = readCode(); if (clearBytes) toBytes.clear(); if (code == NULL) { return BinaryWire.this; } if (code == TYPE_PREFIX) { @Nullable StringBuilder sb = readUtf8(); assert sb != null; long length2 = readLength(); int code2 = readCode(); if (code2 != U8_ARRAY) cantRead(code); bytes.readWithLength0(length2 - 1, (b, sb1, toBytes1) -> Compression.uncompress(sb1, b, toBytes1), sb, toBytes); return wireIn(); } if (code == U8_ARRAY) { ((Bytes) bytes).readWithLength(length - 1, toBytes); } else { bytes.uncheckedReadSkipBackOne(); textTo((Bytes) toBytes); } return wireIn(); } @NotNull @Override public WireIn bytesLiteral(@NotNull BytesOut toBytes) { long length = readLength(); toBytes.clear(); toBytes.write(bytes, bytes.readPosition(), length); bytes.readSkip(length); return wireIn(); } @NotNull @Override public BytesStore bytesLiteral() { int length = Maths.toUInt31(readLength()); @NotNull BytesStore toBytes = BytesStore.wrap(new byte[length]); toBytes.write(0, bytes, bytes.readPosition(), length); bytes.readSkip(length); return toBytes; } @Override @Nullable public WireIn bytesSet(@NotNull PointerBytesStore toBytes) { long length = readLength(); int code = readCode(); if (code == NULL) { return BinaryWire.this; } if (code != U8_ARRAY) cantRead(code); long startAddr = bytes.addressForRead(bytes.readPosition()); toBytes.set(startAddr, length - 1); bytes.readSkip(length - 1); return wireIn(); } @NotNull @Override public WireIn bytesMatch(@NotNull BytesStore compareBytes, @NotNull BooleanConsumer consumer) { long length = readLength(); int code = readCode(); if (code != U8_ARRAY) cantRead(code); length--; if (compareBytes.readRemaining() == length) { consumer.accept(bytes.equalBytes(compareBytes, length)); } else { consumer.accept(false); } bytes.readSkip(length); return wireIn(); } @Override @Nullable public BytesStore bytesStore() { long length = readLength() - 1; int code = readCode(); switch (code) { case I64_ARRAY: case U8_ARRAY: @NotNull BytesStore toBytes = BytesStore.lazyNativeBytesStoreWithFixedCapacity(length); toBytes.write(0, bytes, bytes.readPosition(), length); bytes.readSkip(length); return toBytes; case TYPE_PREFIX: { @Nullable StringBuilder sb = readUtf8(); @Nullable byte[] bytes = Compression.uncompress(sb, this, ValueIn::bytes); if (bytes != null) return BytesStore.wrap(bytes); throw new UnsupportedOperationException("Unsupported type " + sb); } case NULL: return null; default: cantRead(code); throw new AssertionError(); } } /** * Reads bytes from the wire and stores them into the provided StringBuilder. * * @param sb The StringBuilder to store the read bytes. */ public void bytesStore(@NotNull StringBuilder sb) { sb.setLength(0); // Consume any padding before reading bytes consumePadding(); long pos = bytes.readPosition(); long length = readLength(); // Validate if the read length is valid or recognized if (length < 0) throw cantRead(peekCode()); // Ensure enough bytes remain for reading the length specified if (length > bytes.readRemaining()) throw new BufferUnderflowException(); int code = readCode(); if (code == U8_ARRAY) { // Read each byte and cast to char to store in StringBuilder for (long i = 1; i < length; i++) sb.append((char) bytes.readUnsignedByte()); } else { // Reset the reading position and store from size-prefixed blobs bytes.readPosition(pos); long limit = bytes.readLimit(); bytes.readLimit(pos + 4 + length); try { sb.append(Wires.fromSizePrefixedBlobs(bytes)); } finally { // Restore the original limit and position after reading bytes.readLimit(limit); bytes.readPosition(limit); } } } /** * Reads bytes from the wire and stores them into the provided Bytes object. * * @param toBytes The Bytes object to store the read bytes. */ public void bytesStore(@NotNull Bytes toBytes) { // Clear the provided Bytes object before storing toBytes.clear(); long length = readLength() - 1; int code = readCode(); // If null code is encountered, terminate early if (code == NULL) { return; } if (code != U8_ARRAY) cantRead(code); // Validate length to ensure no reading past available bytes if (length > bytes.readRemaining()) throw new IllegalStateException("Length of Bytes " + length + " > " + bytes.readRemaining()); // Write the bytes read into the provided Bytes object toBytes.ensureCapacity(toBytes.writePosition() + length); toBytes.write(0, bytes, bytes.readPosition(), length); toBytes.readLimit(length); bytes.readSkip(length); } @Override @NotNull public WireIn bytes(@NotNull ReadBytesMarshallable bytesConsumer) { long length = readLength() - 1; int code = readCode(); if (code != U8_ARRAY) cantRead(code); if (length > bytes.readRemaining()) throw new BufferUnderflowException(); long limit0 = bytes.readLimit(); long limit = bytes.readPosition() + length; try { bytes.readLimit(limit); bytesConsumer.readMarshallable(bytes); } finally { bytes.readLimit(limit0); bytes.readPosition(limit); } return wireIn(); } @Nullable @Override public byte[] bytes(byte[] using) { long length = readLength(); int code = readCode(); if (code == NULL) { return null; } if (code == TYPE_PREFIX) { @Nullable StringBuilder sb = readUtf8(); assert "byte[]".contentEquals(sb); length = readLength(); code = readCode(); } if (code != U8_ARRAY) cantRead(code); length--; @NotNull byte[] bytes2 = using != null && using.length == length ? using : new byte[Maths.toUInt31(length)]; bytes.readWithLength(length, b -> b.read(bytes2)); return bytes2; } @NotNull @Override public WireIn wireIn() { return BinaryWire.this; } @Override public long readLength() { // TODO handle non length types as well. int code = peekCode(); if ((code & 0x80) == 0) return 1; switch (code) { case BYTES_LENGTH8: bytes.uncheckedReadSkipOne(); return bytes.uncheckedReadUnsignedByte(); case BYTES_LENGTH16: bytes.uncheckedReadSkipOne(); return bytes.readUnsignedShort(); case BYTES_LENGTH32: bytes.uncheckedReadSkipOne(); return bytes.readUnsignedInt(); case TYPE_PREFIX: bytes.uncheckedReadSkipOne(); long len = bytes.readStopBit(); bytes.readSkip(len); return readLength(); case FALSE: case TRUE: case NULL: return 1; case UINT8: case INT8: case FLOAT_SET_LOW_0: case FLOAT_SET_LOW_2: case FLOAT_SET_LOW_4: return 1 + 1L; case UINT16: case INT16: return 1 + 2L; case FLOAT32: case UINT32: case INT32: return 1 + 4L; case FLOAT64: case INT64: return 1 + 8L; case PADDING: case PADDING32: case COMMENT: consumePadding(); return readLength(); case FLOAT_STOP_2: case FLOAT_STOP_4: case FLOAT_STOP_6: { long pos = bytes.readPosition() + 1; while (pos < bytes.readLimit()) { if (bytes.readUnsignedByte(pos++) < 0x80) break; } return pos - bytes.readPosition(); } case UUID: return 1 + 8 + 8; case INT64_0x: return 1 + 8; case DATE: case TIME: case DATE_TIME: case ZONED_DATE_TIME: case TYPE_LITERAL: case STRING_ANY: { long pos0 = bytes.readPosition(); try { bytes.uncheckedReadSkipOne(); long len2 = bytes.readStopBit(); return bytes.readPosition() - pos0 + len2; } finally { bytes.readPosition(pos0); } } case I64_ARRAY: { long capacity = bytes.readLong(bytes.readPosition() + 1); return 1 + 2 * 8 + (capacity * Long.BYTES); } case -1: return 0; default: if (code >= STRING_0) return code + (1L - STRING_0); //System.out.println("code=" + code + ", bytes=" + bytes.toHexString()); return -1; } } @NotNull @Override public WireIn skipValue() { final long length = readLength(); if (length < 0) objectBestEffort(); else bytes.readSkip(length); return BinaryWire.this; } @NotNull @Override public WireIn bool(T t, @NotNull ObjBooleanConsumer tFlag) { int code = readCode(); switch (code) { case NULL: // todo take the default. tFlag.accept(t, null); break; case FALSE: tFlag.accept(t, false); break; case TRUE: tFlag.accept(t, true); break; case PADDING: case PADDING32: case COMMENT: bytes.uncheckedReadSkipBackOne(); consumePadding(); bool(t, tFlag); break; default: throw cantRead(code); } return BinaryWire.this; } @NotNull @Override public WireIn int8(@NotNull T t, @NotNull ObjByteConsumer tb) { int code = bytes.readUnsignedByte(); if (code < 128) { tb.accept(t, (byte) code); return BinaryWire.this; } int8b(t, tb, code); return BinaryWire.this; } /** * Interprets the byte data as an 8-bit integer based on the provided code. * Depending on the code, this method can handle padding and text-based integers. * * @param t The object to which the byte value should be applied. * @param tb The consumer that processes the byte value and the object. * @param code The code determining the format of the incoming data. */ private void int8b(@NotNull T t, @NotNull ObjByteConsumer tb, int code) { // Handle special codes for padding and comments switch (code) { case PADDING: case PADDING32: case COMMENT: // Move back one byte to handle padding correctly bytes.uncheckedReadSkipBackOne(); consumePadding(); code = bytes.readUnsignedByte(); break; } // Check if the code indicates a text format if (isText(code)) tb.accept(t, Byte.parseByte(text())); else // Parse the data as an integer and pass it to the consumer tb.accept(t, (byte) BinaryWire.this.readInt(code)); } @NotNull @Override public WireIn uint8(@NotNull T t, @NotNull ObjShortConsumer ti) { consumePadding(); final int code = readCode(); if (isText(code)) ti.accept(t, Short.parseShort(text())); else ti.accept(t, (short) BinaryWire.this.readInt(code)); return BinaryWire.this; } @NotNull @Override public WireIn int16(@NotNull T t, @NotNull ObjShortConsumer ti) { final int code = readCode(); if (isText(code)) ti.accept(t, Short.parseShort(text())); else ti.accept(t, (short) BinaryWire.this.readInt(code)); return BinaryWire.this; } @NotNull @Override public WireIn uint16(@NotNull T t, @NotNull ObjIntConsumer ti) { consumePadding(); final int code = readCode(); if (isText(code)) ti.accept(t, Integer.parseInt(text())); else ti.accept(t, (int) BinaryWire.this.readInt(code)); return BinaryWire.this; } @NotNull @Override public WireIn int32(@NotNull T t, @NotNull ObjIntConsumer ti) { consumePadding(); final int code = readCode(); if (isText(code)) ti.accept(t, Integer.parseInt(text())); else ti.accept(t, (int) BinaryWire.this.readInt(code)); return BinaryWire.this; } @NotNull @Override public WireIn uint32(@NotNull T t, @NotNull ObjLongConsumer tl) { consumePadding(); final int code = readCode(); if (isText(code)) tl.accept(t, Long.parseLong(text())); else tl.accept(t, BinaryWire.this.readInt(code)); return BinaryWire.this; } @NotNull @Override public WireIn int64(@NotNull T t, @NotNull ObjLongConsumer tl) { final int code = readCode(); if (isText(code)) tl.accept(t, Long.parseLong(text())); else tl.accept(t, BinaryWire.this.readInt(code)); return BinaryWire.this; } @NotNull @Override public WireIn float32(@NotNull T t, @NotNull ObjFloatConsumer tf) { consumePadding(); final int code = readCode(); if (isText(code)) tf.accept(t, Float.parseFloat(text())); else tf.accept(t, (float) BinaryWire.this.readFloat(code)); return BinaryWire.this; } @NotNull @Override public WireIn float64(@NotNull T t, @NotNull ObjDoubleConsumer td) { consumePadding(); final int code = readCode(); td.accept(t, BinaryWire.this.readFloat(code)); return BinaryWire.this; } @NotNull @Override public WireIn time(@NotNull T t, @NotNull BiConsumer setLocalTime) { consumePadding(); int code = readCode(); if (code == TIME) { setLocalTime.accept(t, readLocalTime()); } else { cantRead(code); } return BinaryWire.this; } /** * Reads UTF-8 encoded string data and converts it into a {@link LocalTime} object. * * @return A {@link LocalTime} object parsed from the read string. */ private LocalTime readLocalTime() { // Read the UTF-8 encoded string and attempt to parse it as a LocalTime @Nullable StringBuilder sb = readUtf8(); return LocalTime.parse(sb); } @NotNull @Override public WireIn zonedDateTime(@NotNull T t, @NotNull BiConsumer tZonedDateTime) { consumePadding(); int code = readCode(); if (code == ZONED_DATE_TIME) { @Nullable StringBuilder sb = readUtf8(); tZonedDateTime.accept(t, ZonedDateTime.parse(sb)); } else { cantRead(code); } return BinaryWire.this; } @NotNull @Override public WireIn date(@NotNull T t, @NotNull BiConsumer tLocalDate) { consumePadding(); int code = readCode(); if (code == DATE) { @Nullable StringBuilder sb = readUtf8(); tLocalDate.accept(t, LocalDate.parse(sb)); } else { cantRead(code); } return BinaryWire.this; } @Override public boolean hasNext() { return bytes.readRemaining() > 0; } @Override public boolean hasNextSequenceItem() { return hasNext(); } @NotNull @Override public UUID uuid() { consumePadding(); int code = readCode(); if (code == UUID) { return new UUID(bytes.readLong(), bytes.readLong()); } else { throw cantRead(code); } } @NotNull @Override public WireIn uuid(@NotNull T t, @NotNull BiConsumer tuuid) { consumePadding(); int code = readCode(); if (code == UUID) { tuuid.accept(t, new UUID(bytes.readLong(), bytes.readLong())); } else { cantRead(code); } return BinaryWire.this; } @NotNull @Override public WireIn int64array(@Nullable LongArrayValues values, T t, @NotNull BiConsumer setter) { consumePadding(); int code = readCode(); if (code == I64_ARRAY) { if (!(values instanceof BinaryLongArrayReference) || values.isClosing()) values = new BinaryLongArrayReference(); @Nullable Byteable b = (Byteable) values; long length = BinaryLongArrayReference.peakLength(bytes, bytes.readPosition()); b.bytesStore(bytes, bytes.readPosition(), length); bytes.readSkip(length); setter.accept(t, values); } else { cantRead(code); } return BinaryWire.this; } @NotNull @Override public WireIn int128(@NotNull TwoLongValue value) { consumePadding(); int code = readCode(); if (code == I64_ARRAY) { @Nullable Byteable b = (Byteable) value; long length = BinaryLongArrayReference.peakLength(bytes, bytes.readPosition()); b.bytesStore(bytes, bytes.readPosition() + 8 /* capacity */ + 8 /* used */, length - 2 * 8); bytes.readSkip(length); } else { cantRead(code); } return BinaryWire.this; } @NotNull @Override public WireIn int64(@NotNull LongValue value) { consumePadding(); int code = readCode(); if (code != INT64 && code != 0) cantRead(code); @NotNull Byteable b = (Byteable) value; long length = b.maxSize(); b.bytesStore(bytes, bytes.readPosition(), length); bytes.readSkip(length); return BinaryWire.this; } @NotNull @Override public WireIn int32(@NotNull IntValue value) { consumePadding(); int code = readCode(); if (code != INT32) cantRead(code); @NotNull Byteable b = (Byteable) value; long length = b.maxSize(); b.bytesStore(bytes, bytes.readPosition(), length); bytes.readSkip(length); return BinaryWire.this; } @Override public WireIn bool(@NotNull final BooleanValue value) { consumePadding(); int code = readCode(); if (code != TRUE && code != FALSE) cantRead(code); bytes.readSkip(-1); @NotNull Byteable b = (Byteable) value; long length = b.maxSize(); b.bytesStore(bytes, bytes.readPosition(), length); bytes.readSkip(length); return BinaryWire.this; } @NotNull @Override public WireIn int64(@Nullable LongValue value, T t, @NotNull BiConsumer setter) { // if the value is null, then we will create a LongDirectReference to write the data // into and then call setter.accept(), this will then update the value if (!(value instanceof BinaryLongReference)) { value = new BinaryLongReference(); setter.accept(t, value); } return int64(value); } @NotNull @Override public WireIn int32(@Nullable IntValue value, T t, @NotNull BiConsumer setter) { consumePadding(); int code = readCode(); if (code != INT32) cantRead(code); if (!(value instanceof Byteable) || ((Byteable) value).maxSize() != 4) { value = new BinaryIntReference(); setter.accept(t, value); } @NotNull Byteable b = (Byteable) value; long length = b.maxSize(); b.bytesStore(bytes, bytes.readPosition(), length); bytes.readSkip(length); return BinaryWire.this; } @Override public boolean sequence(@NotNull T t, @NotNull BiConsumer tReader) { if (isNull()) return false; long length = readLength(); if (length < 0) throw cantRead(peekCode()); long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { tReader.accept(t, this); } finally { bytes.readLimit(limit); bytes.readPosition(limit2); } return true; } @Override public boolean sequence(@NotNull List list, @NotNull List buffer, @NotNull Supplier bufferAdd) throws InvalidMarshallableException { list.clear(); return sequence(list, buffer, bufferAdd, reader0field); } @Override public boolean sequence(List list, @NotNull List buffer, Supplier bufferAdd, Reader tReader) throws InvalidMarshallableException { if (isNull()) return false; long length = readLength(); if (length < 0) throw cantRead(peekCode()); long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { tReader.accept(this, list, buffer, bufferAdd); } finally { bytes.readLimit(limit); bytes.readPosition(limit2); } return true; } @NotNull @Override public WireIn sequence(@NotNull T t, K kls, @NotNull TriConsumer tReader) throws InvalidMarshallableException { consumePadding(); int code = readCode(); long length = readLengthPrefixed(code); long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { tReader.accept(t, kls, this); } finally { bytes.readLimit(limit); bytes.readPosition(limit2); } return BinaryWire.this; } /** * Reads a length-prefixed value based on the provided code. * The length can be encoded as 8-bit, 16-bit, or 32-bit depending on the code. * * @param code The code determining the format of the length value. * @return The length value read from the binary data. */ private long readLengthPrefixed(int code) { long length; switch (code) { case BYTES_LENGTH8: // Read an 8-bit unsigned integer as the length length = bytes.readUnsignedByte(); break; case BYTES_LENGTH16: // Read a 16-bit unsigned integer as the length length = bytes.readUnsignedShort(); break; case BYTES_LENGTH32: // Read a 32-bit unsigned integer as the length length = bytes.readUnsignedInt(); break; default: // If an unrecognized code is encountered, throw an exception throw cantRead(code); } return length; } @Override public int sequenceWithLength(@NotNull T t, @NotNull ToIntBiFunction tReader) { consumePadding(); int code = readCode(); long length = readLengthPrefixed(code); long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { return tReader.applyAsInt(this, t); } finally { bytes.readLimit(limit); bytes.readPosition(limit2); } } @Override public T applyToMarshallable(@NotNull Function marshallableReader) { consumePadding(); pushState(); try { long length = readLength(); if (length >= 0) { long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { return marshallableReader.apply(BinaryWire.this); } finally { bytes.readLimit(limit); bytes.readPosition(limit2); } } else { return marshallableReader.apply(BinaryWire.this); } } finally { popState(); } } @Override public boolean isTyped() { int code = peekCode(); return code == TYPE_PREFIX; } @Override @Nullable public T typedMarshallable() throws IORuntimeException, InvalidMarshallableException { pushState(); try { int code = readCode(); switch (code) { case TYPE_PREFIX: return typedMarshallable0(); case NULL: return null; default: cantRead(code); return null; // only if the throw doesn't work. } } finally { popState(); } } /** * Tries to deserialize a typed Marshallable object from the current state of the wire. * The method identifies the type of the Marshallable object by reading a UTF-8 encoded class name * and then instantiates and initializes the object accordingly. * * @param Type of the expected return object. * @return The deserialized Marshallable object or null if the UTF-8 encoded class name is not found. * @throws InvalidMarshallableException If the deserialization encounters any issues. */ @Nullable protected T typedMarshallable0() throws InvalidMarshallableException { @Nullable StringBuilder sb = readUtf8(); if (sb == null) return null; // its possible that the object that you are allocating may not have a // default constructor final Class clazz = (Class) classLookup().forName(sb); if (Demarshallable.class.isAssignableFrom(clazz)) { return (T) demarshallable((Class) clazz); } // Check if the class is neither of type Marshallable nor Demarshallable if (!Marshallable.class.isAssignableFrom(clazz) && !Demarshallable.class.isAssignableFrom(clazz)) throw new IllegalStateException("its not possible to Marshallable and object that" + " is not of type Marshallable, type=" + sb); ReadMarshallable m = ObjectUtils.newInstance((Class) clazz); marshallable(m, true); return readResolve(m); } @Override @Nullable public T typedMarshallable(@NotNull Function, ReadMarshallable> marshallableFunction) throws IORuntimeException, InvalidMarshallableException { int code = peekCode(); if (code != TYPE_PREFIX) // todo get delta wire to support Function correctly return typedMarshallable(); @Nullable final ClassaClass = (Class) typePrefix(); if (ReadMarshallable.class.isAssignableFrom(aClass)) { final ReadMarshallable marshallable = marshallableFunction.apply(aClass); marshallable(marshallable); return (T) marshallable; } return object(null, aClass); } @Override public Class typePrefix() { int code = peekCode(); if (code != TYPE_PREFIX) { return null; } bytes.uncheckedReadSkipOne(); @Nullable StringBuilder sb = readUtf8(); try { return classLookup().forName(sb); } catch (ClassNotFoundRuntimeException e) { Jvm.warn().on(BinaryWire.this.getClass(), "Unable to find class " + sb); return null; } } @Override public Object typePrefixOrObject(Class tClass) { int code = peekCode(); if (code != TYPE_PREFIX) { return null; } bytes.uncheckedReadSkipOne(); @Nullable StringBuilder sb = readUtf8(); try { return sb == null ? null : classLookup().forName(sb); } catch (ClassNotFoundRuntimeException e) { if (Wires.dtoInterface(tClass) && GENERATE_TUPLES) { return Wires.tupleFor(tClass, sb.toString()); } throw e; } } @NotNull @Override public ValueIn typePrefix(T t, @NotNull BiConsumer ts) { @NotNull StringBuilder sb = acquireStringBuilder(); int code = readCode(); switch (code) { case TYPE_PREFIX: bytes.readUtf8(sb); break; case NULL: sb.setLength(0); sb.append("!null"); break; default: cantRead(code); break; } ts.accept(t, sb); return this; } @NotNull @Override public WireIn typeLiteralAsText(T t, @NotNull BiConsumer classNameConsumer) { int code = readCode(); switch (code) { case TYPE_LITERAL: @Nullable StringBuilder sb = readUtf8(); classNameConsumer.accept(t, sb); break; case NULL: classNameConsumer.accept(t, null); break; default: cantRead(code); break; } return BinaryWire.this; } @Override public ClassLookup classLookup() { return BinaryWire.this.classLookup(); } @Override public Type typeLiteral(BiFunction unresolvedHandler) { int code = readCode(); switch (code) { case TYPE_LITERAL: @Nullable StringBuilder sb = readUtf8(); try { return classLookup().forName(sb); } catch (ClassNotFoundRuntimeException e) { return unresolvedHandler.apply(sb, e.getCause()); } case NULL: return null; default: throw cantRead(code); } } @Override public boolean marshallable(@NotNull ReadMarshallable object) throws BufferUnderflowException, IORuntimeException, InvalidMarshallableException { return marshallable(object, true); } /** * Marshalls the given object and can optionally overwrite existing values. * * @param object The object to be marshalled. * @param overwrite Determines if the existing values should be overwritten. * @return True if the operation is successful. * @throws BufferUnderflowException If there's not enough data available in the buffer. * @throws IORuntimeException If there's a general IO error. * @throws InvalidMarshallableException If there's an error specific to marshalling. */ public boolean marshallable(@NotNull ReadMarshallable object, boolean overwrite) throws BufferUnderflowException, IORuntimeException, InvalidMarshallableException { consumePadding(); // Check for null value if (this.isNull()) return false; pushState(); // Get the length of the data to be read long length = readLength(); if (length >= 0) { long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { // Read the object based on its type if (useSelfDescribingMessage(object)) { if (overwrite) object.readMarshallable(BinaryWire.this); else Wires.readMarshallable(object, BinaryWire.this, false); } else { ((ReadBytesMarshallable) object).readMarshallable(BinaryWire.this.bytes); } } finally { bytes.readLimit(limit); bytes.readPosition(limit2); popState(); } } else { throw new IORuntimeException("Length unknown"); } return true; } @Override public boolean isNull() { consumePadding(); if (peekCode() == NULL) { bytes.uncheckedReadSkipOne(); return true; } return false; } @Override @Nullable public Object marshallable(@NotNull Object object, @NotNull SerializationStrategy strategy) throws BufferUnderflowException, IORuntimeException, InvalidMarshallableException { if (this.isNull()) return null; pushState(); consumePadding(); long length = readLength(); if (length >= 0) { long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); try { strategy.readUsing(null, Jvm.uncheckedCast(object), this, BracketType.MAP); } finally { bytes.readLimit(limit); bytes.readPosition(limit2); popState(); } } else { throw new IORuntimeException("Length unknown " + length); } return object; } /** * Deserializes an object of the given class from the wire data. * * @param clazz Class to be deserialized. * @return An instance of the provided class. * @throws BufferUnderflowException if there's not enough data in the buffer. * @throws IORuntimeException for general IO issues. */ @Nullable public Demarshallable demarshallable(@NotNull Class clazz) throws BufferUnderflowException, IORuntimeException { if (this.isNull()) return null; long length = readLength(); // Read the expected length of the data. if (length >= 0) { // If length is valid, proceed. long limit = bytes.readLimit(); long limit2 = bytes.readPosition() + length; bytes.readLimit(limit2); // Set a temporary limit for the buffer based on the expected length. try { return Demarshallable.newInstance(clazz, wireIn()); // Deserialize the object using the current wire input. } finally { bytes.readLimit(limit); // Reset the buffer limit. bytes.readPosition(limit2); // Move the buffer's position past the read data. } } else { // If length is not valid (negative), still try to deserialize the object. return Demarshallable.newInstance(clazz, wireIn()); } } /** * Reads a text from the wire and attempts to convert it into a long value. * If the text is not a valid representation of a long, tries to parse it as a double * and then rounds to the nearest long. * * @param otherwise Default value to return if the conversion fails. * @return Parsed long value or the provided default value. * @throws IORuntimeException for general IO issues. * @throws BufferUnderflowException if there's not enough data in the buffer. */ private long readTextAsLong(long otherwise) throws IORuntimeException, BufferUnderflowException { bytes.uncheckedReadSkipBackOne(); // Go back one position. @Nullable String text; try { text = text(); // Read text from the wire. } catch (Exception e) { return otherwise; // On any exception, return the default value. } if (text == null || text.length() == 0) // If text is empty or null, return the default value. return otherwise; try { return Long.parseLong(text); // Try to parse the text as a long. } catch (NumberFormatException e) { return Math.round(Double.parseDouble(text)); // If failed, try parsing as double and then round it. } } /** * Reads a text from the wire and attempts to convert it into a double value. * * @return Parsed double value or NaN if the conversion fails. * @throws IORuntimeException for general IO issues. * @throws BufferUnderflowException if there's not enough data in the buffer. */ private double readTextAsDouble() throws IORuntimeException, BufferUnderflowException { bytes.uncheckedReadSkipBackOne(); // Go back one position. @Nullable String text; try { text = text(); // Read text from the wire. } catch (BufferUnderflowException e) { return Double.NaN; // On buffer underflow, return NaN. } if (text == null || text.length() == 0) // If text is empty or null, return NaN. return Double.NaN; return Double.parseDouble(text); // Try to parse the text as a double. } @Override public boolean bool() throws IORuntimeException { int code = readCode(); if (isText(code)) return Boolean.valueOf(text()); switch (code) { case TRUE: return true; case FALSE: return false; case PADDING: case PADDING32: case COMMENT: bytes.uncheckedReadSkipBackOne(); consumePadding(); return bool(); } throw new IORuntimeException(stringForCode(code)); } @Override public byte int8() { int code = readCode(); if (code < 128) return (byte) code; return int8b(code); } /** * Reads an 8-bit integer value based on a given code. * * @param code The code representing the type of data. * @return The byte value read. */ private byte int8b(int code) { // Handle padding or comment codes by skipping back and consuming the padding, // then reading the next code. switch (code) { case PADDING: case PADDING32: case COMMENT: bytes.uncheckedReadSkipBackOne(); // Move back by one position. consumePadding(); // Handle the padding or comment. code = readCode(); // Read the next code. break; } // Determine if the code represents text or an integer value. final long value = isText(code) ? readTextAsLong(Byte.MIN_VALUE) : readInt0(code); // Ensure the value is within the byte range, otherwise throw an exception. if (value > Byte.MAX_VALUE || value < Byte.MIN_VALUE) throw new IllegalStateException(); return (byte) value; // Cast the long value to a byte and return. } @Override public short int16() { consumePadding(); int code = readCode(); final long value = isText(code) ? readTextAsLong(Short.MIN_VALUE) : readInt0(code); if (value > Short.MAX_VALUE || value < Short.MIN_VALUE) throw new IllegalStateException(); return (short) value; } @Override public int uint16() { consumePadding(); int code = readCode(); final long value = isText(code) ? readTextAsLong(0) : readInt0(code); if (value > (1L << 32L) || value < 0) throw new IllegalStateException("value " + value + " cannot be cast to an unsigned 16-bit int without loss of information"); return (int) value; } @Override public float float32() { consumePadding(); int code = readCode(); final double value; switch (code >> 16) { case BinaryWireHighCode.INT: value = readInt0(code); break; case BinaryWireHighCode.FLOAT: value = readFloat0(code); break; case BinaryWireHighCode.STR0: case BinaryWireHighCode.STR1: default: value = readTextAsDouble(); break; } return (float) value; } @Override public int int32() { consumePadding(); int code = readCode(); final long value = isText(code) ? readTextAsLong(Integer.MIN_VALUE) : readInt0(code); if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) throw new IllegalStateException("value " + value + " cannot be cast to int without loss of information"); return (int) value; } @Override public long int64() { int code = readCode(); if (code == PADDING || code == PADDING32 || code == COMMENT) { bytes.uncheckedReadSkipBackOne(); consumePadding(); code = readCode(); } if ((code & 0x80) == 0) return code; switch (code >> 4) { case BinaryWireHighCode.FLOAT: return (long) readFloat0(code); case BinaryWireHighCode.INT: return readInt0(code); default: return readTextAsLong(Long.MIN_VALUE); } } @Override public double float64() { int code = readCode(); if (code >> 4 == BinaryWireHighCode.FLOAT) return readFloat0(code); return isText(code) ? readTextAsDouble() : readInt0(code); } /** * Throws an exception indicating that reading the provided code is not supported. * * @param code The unsupported code. * @return A runtime exception (never actually returned since an exception is always thrown). */ @NotNull private RuntimeException cantRead(int code) { throw new UnsupportedOperationException(stringForCode(code)); } @Override public Object objectWithInferredType(Object using, @NotNull SerializationStrategy strategy, Class type) throws InvalidMarshallableException { int code = peekCode(); if ((code & 0x80) == 0) { bytes.uncheckedReadSkipOne(); return code; } switch (code >> 4) { case BinaryWireHighCode.CONTROL: switch (code) { case BYTES_LENGTH8: case BYTES_LENGTH16: case BYTES_LENGTH32: { if (using instanceof StringBuilder) { bytesStore((StringBuilder) using); return using; } else if (using instanceof Bytes) { bytesStore((Bytes) using); return using; } else { long pos = bytes.readPosition(); bytes.uncheckedReadSkipOne(); int len = readLength(code); code = peekCode(); if (code == U8_ARRAY) { bytes.readPosition(pos); return bytesStore(); } long lim = bytes.readLimit(); try { bytes.readLimit(bytes.readPosition() + len); Object using1 = using; if (using1 == null && type != null) using1 = strategy.newInstanceOrNull(type); if (isEvent(code)) return (strategy == SerializationStrategies.ANY_OBJECT ? SerializationStrategies.MAP : strategy) .readUsing(type, using1, this, BracketType.MAP); else return (strategy == SerializationStrategies.ANY_OBJECT ? SerializationStrategies.LIST : strategy) .readUsing(type, using1, this, BracketType.SEQ); } finally { bytes.readLimit(lim); } } } case U8_ARRAY: { bytes.uncheckedReadSkipOne(); long length = bytes.readRemaining(); if (length == 0) return BytesStore.empty(); @NotNull BytesStore toBytes = BytesStore.lazyNativeBytesStoreWithFixedCapacity(length); toBytes.write(0, bytes, bytes.readPosition(), length); bytes.readSkip(length); return toBytes; } case ANCHOR: case UPDATED_ALIAS: return typedMarshallable(); } break; case BinaryWireHighCode.SPECIAL: switch (code) { case FALSE: bytes.uncheckedReadSkipOne(); return Boolean.FALSE; case TRUE: bytes.uncheckedReadSkipOne(); return Boolean.TRUE; case NULL: bytes.uncheckedReadSkipOne(); return null; case STRING_ANY: return text(); case TYPE_PREFIX: { readCode(); @Nullable StringBuilder sb = readUtf8(); final Class clazz2 = classLookup().forName(sb); return object(null, clazz2); } case EVENT_OBJECT: { if (using == null) { strategy = SerializationStrategies.MAP; using = strategy.newInstanceOrNull(null); } strategy.readUsing(type, using, valueIn, BracketType.MAP); return ObjectUtils.convertTo(type, using); } case TIME: return time(); case DATE: return date(); case DATE_TIME: return dateTime(); case ZONED_DATE_TIME: return zonedDateTime(); case TYPE_LITERAL: return typeLiteral(); } break; case BinaryWireHighCode.FLOAT: bytes.uncheckedReadSkipOne(); return readFloat0bject(code); case BinaryWireHighCode.INT: bytes.uncheckedReadSkipOne(); if (code == UUID) return new java.util.UUID(bytes.readLong(), bytes.readLong()); return readInt0object(code); } // assume it a String return text(); } /** * Reads the length of the data based on a given code. * * @param code The code representing the length format. * @return The length value read. */ private int readLength(int code) { int len; // Determine the code and read the corresponding length. switch (code) { case BYTES_LENGTH8: len = bytes.readUnsignedByte(); // Read an unsigned 8-bit value. break; case BYTES_LENGTH16: len = bytes.readUnsignedShort(); // Read an unsigned 16-bit value. break; case BYTES_LENGTH32: len = bytes.readInt(); // Read a 32-bit integer value. break; default: throw new AssertionError(); // Throw an assertion error if the code is unrecognized. } return len; // Return the read length. } /** * Checks if the provided code corresponds to an event. * * @param code The code to check. * @return True if the code corresponds to an event, otherwise false. */ private boolean isEvent(int code) { // Return true if the code matches the EVENT_NAME or falls within the range of field names. return code == EVENT_NAME || (FIELD_NAME0 <= code && code <= FIELD_NAME31); } /** * Consumes the next set of bytes based on the provided code. * The method moves the reader pointer after the current item or structure. * * @throws InvalidMarshallableException If there's an error while marshalling. */ void consumeNext() throws InvalidMarshallableException { // Peek at the next byte to determine the code. int code = peekCode(); // If the highest bit of the code isn't set, skip the current byte. if ((code & 0x80) == 0) { bytes.uncheckedReadSkipOne(); return; } // Check the high 4 bits to determine the category of the code. switch (code >> 4) { case BinaryWireHighCode.CONTROL: switch (code) { // For lengths, skip the bytes that define the length and then // skip the number of bytes specified by the length. case BYTES_LENGTH8: bytes.readSkip(1); // Skip length definition byte. bytes.readSkip(bytes.readUnsignedByte()); // Skip the specified bytes. return; case BYTES_LENGTH16: bytes.readSkip(1); bytes.readSkip(bytes.readUnsignedShort()); return; case BYTES_LENGTH32: bytes.readSkip(1); bytes.readSkip(bytes.readUnsignedInt()); return; default: // Warn if an unrecognized control code is encountered. Jvm.warn().on(getClass(), "reading control code as text"); } break; case BinaryWireHighCode.SPECIAL: switch (code) { // For boolean values or null, just skip the code byte. case FALSE: case TRUE: case NULL: bytes.uncheckedReadSkipOne(); return; // For any string type, read the text. case STRING_ANY: text(); return; // For type prefix, skip the code byte, read the UTF-8 string, // and then recursively consume the next set of bytes. case TYPE_PREFIX: { readCode(); readUtf8(); consumeNext(); return; } } break; // Handling floating point numbers case BinaryWireHighCode.FLOAT: bytes.uncheckedReadSkipOne(); // Skip the current byte // If the code is a small integer (between 0 and 127), nothing more to do if (code < 128 && code >= 0) { return; } // copy/pasted from readFloat0bject to avoid auto-boxing switch (code) { case FLOAT32: bytes.readFloat(); // Read a 32-bit float return; // The next set of cases are special encoding mechanisms (StopBit) case FLOAT_STOP_2: bytes.readStopBit(); return; case FLOAT_STOP_4: bytes.readStopBit(); return; case FLOAT_STOP_6: bytes.readStopBit(); // Read a number encoded in StopBit format return; case FLOAT64: bytes.readDouble(); // Read a 64-bit double return; // Handling optimized cases for specific ranges of floats case FLOAT_SET_LOW_0: case FLOAT_SET_LOW_2: case FLOAT_SET_LOW_4: bytes.readUnsignedByte(); // Read a byte and treat it as unsigned return; } // If none of the known float codes were matched, throw an exception throw new UnsupportedOperationException(stringForCode(code)); // Handling integers case BinaryWireHighCode.INT: bytes.uncheckedReadSkipOne(); // Skip the current byte // Check if the integer can be represented by a small int (this method would be defined elsewhere) if (isSmallInt(code)) return; // The following is optimized code to read various integer representations without converting them to actual integers (avoiding auto-boxing) switch (code) { case INT8: bytes.readByte(); // Read an 8-bit signed integer return; case UINT8: case SET_LOW_INT8: bytes.readUnsignedByte(); // Read an 8-bit unsigned integer return; case INT16: bytes.readShort(); // Read a 16-bit signed integer return; case SET_LOW_INT16: case UINT16: bytes.readUnsignedShort(); // Read a 16-bit unsigned integer return; case INT32: bytes.readInt(); // Read a 32-bit signed integer return; case UINT32: bytes.readUnsignedInt(); // Read a 32-bit unsigned integer return; case INT64: case INT64_0x: bytes.readLong(); // Read a 64-bit signed integer return; } // If none of the known integer codes were matched, throw an exception throw new UnsupportedOperationException(stringForCode(code)); } // If the code doesn't match any known pattern, assume it's a text encoding text(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy