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

net.openhft.chronicle.wire.WireDumper 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.Bytes;
import net.openhft.chronicle.bytes.BytesUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@SuppressWarnings("rawtypes")
/**
 * The WireDumper class provides utility methods to obtain a human-readable dump representation of {@link WireIn} content.
 * This class can operate on different WireIn or Bytes inputs and is designed to facilitate debugging and logging.
 */
public class WireDumper {

    // Instance of WireIn to read from
    @NotNull
    private final WireIn wireIn;

    // Bytes that encapsulate the raw data
    @NotNull
    private final Bytes bytes;

    // Tracks the header number for internal operations
    private long headerNumber = -1;

    /**
     * Private constructor for WireDumper.
     * It initializes the wireIn and bytes. If wireIn is null, a new BinaryWire is created.
     *
     * @param wireIn WireIn instance (nullable)
     * @param bytes  Bytes instance containing the raw data
     */
    private WireDumper(@Nullable WireIn wireIn, @NotNull Bytes bytes) {
        if (wireIn == null)
            wireIn = new BinaryWire(bytes);
        this.wireIn = wireIn;
        this.bytes = bytes;
    }

    /**
     * Factory method to create a new WireDumper instance given a {@link WireIn} object.
     *
     * @param wireIn The WireIn instance to be dumped
     * @return A new WireDumper instance
     */
    @NotNull
    public static WireDumper of(@NotNull WireIn wireIn) {
        return new WireDumper(wireIn, wireIn.bytes());
    }

    /**
     * Factory method to create a new WireDumper instance given a {@link Bytes} object.
     * This method uses default padding alignment.
     *
     * @param bytes The Bytes object containing the raw data to be dumped
     * @return A new WireDumper instance
     */
    @NotNull
    public static WireDumper of(@NotNull Bytes bytes) {
        return of(bytes, AbstractWire.DEFAULT_USE_PADDING);
    }

    /**
     * Factory method to create a new WireDumper instance given a {@link Bytes} object and an alignment preference.
     *
     * @param bytes The Bytes object containing the raw data to be dumped
     * @param align Boolean value indicating whether to align the dumped data
     * @return A new WireDumper instance
     */
    @SuppressWarnings("deprecation")
    @NotNull
    public static WireDumper of(@NotNull Bytes bytes, boolean align) {
        final BinaryWire wireIn = new BinaryWire(bytes);
        wireIn.usePadding(align);
        return new WireDumper(wireIn, bytes);
    }

    /**
     * Obtains a string representation of the entire content present in the bytes.
     * This method uses default abbreviated format.
     *
     * @return String representation of the content
     */
    @NotNull
    public String asString() {
        return asString(false);
    }

    /**
     * Obtains a string representation of the content from the current read position.
     * The length of content to be represented is determined by the remaining bytes to be read.
     *
     * @param abbrev Boolean value indicating whether to use abbreviated format
     * @return String representation of the content from the current read position
     */
    @NotNull
    public String asString(boolean abbrev) {
        return asString(bytes.readPosition(), bytes.readRemaining(), abbrev);
    }

    /**
     * Returns a string representation of the content located at the given position and length in the bytes.
     * This method uses the default abbreviated format.
     *
     * @param position Starting position of the content to be represented
     * @param length   Length of the content to be represented
     * @return String representation of the specified content
     */
    @NotNull
    public String asString(long position, long length) {
        return asString(position, length, false);
    }

    /**
     * Returns a string representation of the content located at the given position and length in the bytes.
     * The method provides an option to use an abbreviated format.
     *
     * @param position Starting position of the content to be represented
     * @param length   Length of the content to be represented
     * @param abbrev   Boolean value indicating whether to use abbreviated format
     * @return String representation of the specified content
     */
    @NotNull
    public String asString(long position, long length, boolean abbrev) {
        @NotNull StringBuilder sb = new StringBuilder();
        final long limit0 = bytes.readLimit();
        final long position0 = bytes.readPosition();

        Bytes bytes2 = Bytes.allocateElasticOnHeap();
        try {
            bytes.readPosition(position);
            long limit2 = Math.min(limit0, position + length);
            bytes.readLimit(limit2);

            long missing = position + length - limit2;
            while (bytes.readRemaining() >= 4) {
                if (dumpOne(sb, bytes2, abbrev))
                    break;

            }
            if (missing > 0 && !abbrev)
                sb.append(" # missing: ").append(missing);
        } catch (Throwable t) {
            sb.append(" ").append(t);
        } finally {
            bytes.readLimit(limit0);
            bytes.readPosition(position0);
            bytes2.releaseLast();
        }
        return sb.toString();
    }

    /**
     * Dumps a single wire entry from the internal byte buffer to the provided StringBuilder.
     * This method uses the default abbreviated format.
     *
     * @param sb     StringBuilder to which the wire entry will be appended
     * @param buffer Temporary buffer to assist in dumping the wire entry
     * @return Boolean value indicating whether the dump was successful
     */
    public boolean dumpOne(@NotNull StringBuilder sb, @Nullable Bytes buffer) {
        return dumpOne(sb, buffer, false);
    }

    /**
     * Dumps a single wire entry from the internal byte buffer to the provided StringBuilder.
     * This method provides an option to use an abbreviated format.
     *
     * @param sb     StringBuilder to which the wire entry will be appended
     * @param buffer Temporary buffer to assist in dumping the wire entry
     * @param abbrev Boolean value indicating whether to use abbreviated format
     * @return Boolean value indicating whether the dump was successful
         */
    public boolean dumpOne(@NotNull StringBuilder sb, @Nullable Bytes buffer, boolean abbrev) {
        // Read position in the byte buffer for extracting the header
        bytes.readPositionForHeader(wireIn.usePadding());
        long start = this.bytes.readPosition();
        int header = this.bytes.readInt();

        // Check for empty header
        if (header == 0) {
            if (!abbrev) {
                sb.append("...\n");
                sb.append("# ").append(this.bytes.readRemaining()).append(" bytes remaining\n");
            }
            return true;
        }

        // Update header number if it's ready data
        if (Wires.isReadyData(header))
            headerNumber++;

        // Append position and header information if not abbreviated
        if (start > 0 && !abbrev) {
            sb.append("# position: ").append(start).append(", header: ");
            sb.append(headerNumber);
            if (Wires.isEndOfFile(header))
                sb.append(" EOF");
            else if (Wires.isNotComplete(header))
                sb.append(" or ").append(headerNumber + 1);
            sb.append("\n");
        }

        // Calculate the length based on the header and check its validity
        int len0 = Wires.lengthOf(header);
        int len = len0;
        if (len > this.bytes.readRemaining()) {
            sb.append("#  has a 4 byte size prefix, ").append(len).append(" > ").append(this.bytes.readRemaining()).append(" len is ").append(len);
            return true;
        }

        // Determine the type of the data
        @NotNull String type = Wires.isData(header)
                ? Wires.isReady(header) ? "!!data" : "!!not-ready-data"
                : Wires.isReady(header) ? "!!meta-data" : "!!not-ready-meta-data";

        boolean binary = false;
        if (Wires.isEndOfFile(header)) {
            binary = true;
        } else {
            // Check for binary data by peeking into the buffer
            for (int i = 0, end = Math.min(32, len0); i < end; i++) {
                byte b = (byte) this.bytes.peekUnsignedByte(this.bytes.readPosition() + i);
                if (b < ' ' && b != '\n') {
                    binary = true;
                    break;
                }
            }
        }

        // Appending type and format if not abbreviated
        if (!abbrev) {
            sb.append("--- ").append(type).append(binary ? " #binary" : "");
            if (len > this.bytes.readRemaining())
                sb.append(" # len: ").append(len).append(", remaining: ").append(this.bytes.readRemaining());
            sb.append("\n");
        }

        // Handling the case where length is zero
        if (len == 0) {
            if (!abbrev) {
                sb.append("...\n");
                sb.append("# ").append(this.bytes.readRemaining()).append(" bytes remaining\n");
            }
            return true;
        }

        Bytes textBytes = this.bytes;

        // If data is binary, it's converted to text format
        if (binary) {
            long readPosition = this.bytes.readPosition();
            long readLimit = this.bytes.readLimit();
            int sblen = sb.length();

            try {
                Bytes bytes2 = buffer == null ? Bytes.allocateElasticOnHeap() : buffer.clear();
                @NotNull TextWire textWire = new TextWire(bytes2);

                this.bytes.readLimit(readPosition + len);

                wireIn.copyTo(textWire);

                textBytes = bytes2;
                BytesUtil.combineDoubleNewline(bytes2);
            } catch (Exception e) {
                dumpAsHexadecimal(sb, len, readPosition, sblen);
                return false;

            } finally {
                this.bytes.readLimit(readLimit);
            }

            len = (int) textBytes.readRemaining();
        }
        try {
            // Trim spaces at the end of the textBytes
            for (; len > 0; len--)
                if (textBytes.readUnsignedByte(textBytes.readPosition() + len - 1) != ' ')
                    break;

            // Appending characters to the StringBuilder
            for (int i = 0; i < len; i++) {
                int ch = textBytes.readUnsignedByte();
                sb.append((char) ch);
            }
        } catch (Exception e) {
            sb.append(" ").append(e);
        }

        // Ensure each entry ends with a newline
        if (sb.charAt(sb.length() - 1) != '\n')
            sb.append('\n');

        // If padding is used, adjust the read position accordingly
        if (wireIn.usePadding())
            len0 = (len0 + 3) & ~3;
        this.bytes.readPosition(Math.min(this.bytes.readLimit(), start + 4 + len0));
        return false;
    }

    /**
     * Dumps the content of the byte buffer as a hexadecimal representation into the provided StringBuilder.
     * It positions the bytes at the specified read position, converts the content to hexadecimal, and sets
     * the resulting string to the StringBuilder.
     *
     * @param sb The StringBuilder to which the hexadecimal string will be appended.
     * @param len The length or number of bytes to read and convert to hexadecimal.
     * @param readPosition The starting position in the byte buffer from which to start reading.
     * @param sblen The length to reset the StringBuilder to. This is typically used to overwrite any existing content.
     */
    public void dumpAsHexadecimal(@NotNull StringBuilder sb, int len, long readPosition, int sblen) {
        // Set the read position and remaining length for the byte buffer.
        bytes.readPositionRemaining(readPosition, len);

        // Reset the StringBuilder to the specified length.
        sb.setLength(sblen);

        // Convert the content of the byte buffer to a hexadecimal string and append it to the StringBuilder.
        sb.append(bytes.toHexString(readPosition, Integer.MAX_VALUE));

        // Adjust the read position of the byte buffer after reading.
        bytes.readPosition(readPosition + len);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy