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

it.auties.protobuf.stream.ProtobufOutputStream Maven / Gradle / Ivy

The newest version!
package it.auties.protobuf.stream;

import it.auties.protobuf.exception.ProtobufSerializationException;
import it.auties.protobuf.model.ProtobufString;
import it.auties.protobuf.model.ProtobufWireType;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.Collection;

public abstract class ProtobufOutputStream {
    public static int getFieldSize(int fieldNumber, int wireType) {
        return getVarIntSize(ProtobufWireType.makeTag(fieldNumber, wireType));
    }

    // Long values go from [-2^63, 2^63)
    // A negative var-int always take up 10 bits
    // A positive var int takes up log_2(value) / 7 + 1
    // Constants were folded here to save time
    public static int getVarIntSize(long value) {
        if(value < 0) {
            return 10;
        }else if (value < 128) {
            return 1;
        } else if (value < 16384) {
            return 2;
        } else if (value < 2097152) {
            return 3;
        } else if (value < 268435456) {
            return 4;
        } else if(value < 34359738368L) {
            return 5;
        }else if(value < 4398046511104L) {
            return 6;
        }else if(value < 562949953421312L) {
            return 7;
        }else if(value < 72057594037927936L) {
            return 8;
        }else {
            return 9;
        }
    }

    public static ProtobufOutputStream toBytes(int length) {
        return new ProtobufOutputStream.Bytes(new byte[length], 0);
    }

    public static ProtobufOutputStream toBytes(byte[] bytes, int offset) {
        return new ProtobufOutputStream.Bytes(bytes, offset);
    }

    public static ProtobufOutputStream toBuffer(ByteBuffer buffer) {
        return new ProtobufOutputStream.Buffer(buffer);
    }

    public static ProtobufOutputStream toStream(OutputStream buffer) {
        return new ProtobufOutputStream.Stream(buffer);
    }

    public static int getStringSize(ProtobufString value) {
        var count = value.encodedLength();
        return getVarIntSize(count) + count;
    }

    public static int getBytesSize(ByteBuffer value) {
        if(value == null) {
            return 0;
        }

        return getVarIntSize(value.remaining()) + value.remaining();
    }

    public static int getVarIntPackedSize(int fieldNumber, Collection values) {
        if(values == null){
            return 0;
        }

        var size = getFieldSize(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        var valueSize = 0;
        for (var value : values) {
            valueSize += getVarIntSize(value.longValue());
        }
        size += getVarIntSize(valueSize);
        size += valueSize;
        return size;
    }

    public int getFixed32PackedSize(int fieldNumber, Collection values) {
        if(values == null){
            return 0;
        }

        var valuesSize = values.size() * 4;
        return getFieldSize(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED)
                + getVarIntSize(valuesSize)
                + valuesSize;
    }

    public static int getFixed64PackedSize(int fieldNumber, Collection values) {
        if(values == null){
            return 0;
        }

        var valuesSize = values.size() * 8;
        return getFieldSize(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED)
                + getVarIntSize(valuesSize)
                + valuesSize;
    }

    public static int getBoolPackedSize(int fieldNumber, Collection values) {
        if(values == null){
            return 0;
        }

        var valuesSize = values.size();
        return getFieldSize(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED)
                + getVarIntSize(valuesSize)
                + valuesSize;
    }

    private void writeTag(int fieldNumber, int wireType) {
        writeVarIntNoTag(ProtobufWireType.makeTag(fieldNumber, wireType));
    }

    public void writeGroupStart(int fieldNumber) {
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_START_OBJECT);
    }

    public void writeGroupEnd(int fieldNumber) {
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_END_OBJECT);
    }

    public void writeInt32Packed(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = 0;
        for (var value : values) {
            size += getVarIntSize(value);
        }
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            writeVarIntNoTag(value);
        }
    }

    public void writeInt32(int fieldNumber, Integer value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_VAR_INT);
        writeVarIntNoTag(value);
    }

    public void writeUInt32Packed(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = 0;
        for (var value : values) {
            size += getVarIntSize(value);
        }
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            writeVarIntNoTag(value);
        }
    }

    public void writeUInt32(int fieldNumber, Integer value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_VAR_INT);
        writeVarIntNoTag(value);
    }

    public void writeFloatPacked(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = values.size() * 4;
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            writeFixed32NoTag(Float.floatToRawIntBits(value));
        }
    }

    public void writeFloat(int fieldNumber, Float value) {
        if(value == null){
            return;
        }

        writeFixed32(fieldNumber, Float.floatToRawIntBits(value));
    }

    public void writeFixed32Packed(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = values.size() * 4;
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            writeFixed32NoTag(value);
        }
    }

    public void writeFixed32(int fieldNumber, Integer value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_FIXED32);
        writeFixed32NoTag(value);
    }

    private void writeFixed32NoTag(Integer value) {
        write((byte) (value & 0xFF));
        write((byte) ((value >> 8) & 0xFF));
        write((byte) ((value >> 16) & 0xFF));
        write((byte) ((value >> 24) & 0xFF));
    }

    public void writeInt64Packed(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = values.size() * 8;
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            writeFixed64NoTag(value);
        }
    }

    public void writeInt64(int fieldNumber, Long value) {
        if(value == null){
            return;
        }

        writeUInt64(fieldNumber, value);
    }

    public void writeUInt64Packed(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        for (var value : values) {
            writeUInt64(fieldNumber, value);
        }
    }

    public void writeUInt64(int fieldNumber, Long value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_VAR_INT);
        writeVarIntNoTag(value);
    }

    public void writeDoublePacked(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = values.size() * 8;
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            writeFixed64NoTag(Double.doubleToRawLongBits(value));
        }
    }

    public void writeDouble(int fieldNumber, Double value) {
        if(value == null){
            return;
        }

        writeFixed64(fieldNumber, Double.doubleToRawLongBits(value));
    }

    public void writeFixed64Packed(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        for (var value : values) {
            writeFixed64(fieldNumber, value);
        }
    }

    public void writeFixed64(int fieldNumber, Long value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_FIXED64);
        writeFixed64NoTag(value);
    }

    private void writeFixed64NoTag(Long value) {
        write((byte) ((int) ((long) value) & 0xFF));
        write((byte) ((int) (value >> 8) & 0xFF));
        write((byte) ((int) (value >> 16) & 0xFF));
        write((byte) ((int) (value >> 24) & 0xFF));
        write((byte) ((int) (value >> 32) & 0xFF));
        write((byte) ((int) (value >> 40) & 0xFF));
        write((byte) ((int) (value >> 48) & 0xFF));
        write((byte) ((int) (value >> 56) & 0xFF));
    }

    public void writeBoolPacked(int fieldNumber, Collection values) {
        if(values == null){
            return;
        }

        var size = values.size();
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        for (var value : values) {
            write((byte)( value ? 1 : 0));
        }
    }

    public void writeBool(int fieldNumber, Boolean value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_VAR_INT);
        write((byte) (value ? 1 : 0));
    }

    public void writeString(int fieldNumber, ProtobufString value) {
        if(value == null){
            return;
        }

        value.write(fieldNumber, this);
    }

    public void writeBytes(int fieldNumber, ByteBuffer value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        var size = value.remaining();
        writeVarIntNoTag(size);
        write(value);
    }

    public void writeBytes(int fieldNumber, byte[] value) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(value.length);
        write(value);
    }

    public void writeBytes(int fieldNumber, byte[] value, int offset, int size) {
        if(value == null){
            return;
        }

        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
        write(value, offset, size);
    }

    public void writeMessage(int fieldNumber, int size) {
        writeTag(fieldNumber, ProtobufWireType.WIRE_TYPE_LENGTH_DELIMITED);
        writeVarIntNoTag(size);
    }

    private void writeVarIntNoTag(long value) {
        while (true) {
            if ((value & ~0x7FL) == 0) {
                write((byte) value);
                return;
            } else {
                write((byte) (((int) value & 0x7F) | 0x80));
                value >>>= 7;
            }
        }
    }

    protected abstract void write(byte entry);
    protected abstract void write(byte[] entry);
    protected abstract void write(byte[] entry, int offset, int length);
    protected abstract void write(ByteBuffer entry);
    public abstract OUTPUT toOutput();

    private static final class Stream extends ProtobufOutputStream {
        private final OutputStream outputStream;
        private Stream(OutputStream outputStream) {
            this.outputStream = outputStream;
        }

        @Override
        public void write(byte entry) {
            try {
                outputStream.write(entry);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public void write(byte[] entry) {
            try {
                outputStream.write(entry);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public void write(byte[] entry, int offset, int length) {
            try {
                outputStream.write(entry, offset, length);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public void write(ByteBuffer entry) {
            try {
                var size = entry.remaining();
                var bufferPosition = entry.position();
                for(var i = 0; i < size; i++) {
                    outputStream.write(entry.get(bufferPosition + i));
                }
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public OutputStream toOutput() {
            return outputStream;
        }
    }

    private static final class Bytes extends ProtobufOutputStream {
        private final byte[] buffer;
        private int position;
        private Bytes(byte[] buffer, int offset) {
            this.buffer = buffer;
            this.position = offset;
        }

        @Override
        public void write(byte entry) {
            buffer[position++] = entry;
        }

        @Override
        public void write(byte[] entry) {
            var length = entry.length;
            System.arraycopy(entry, 0, buffer, position, length);
            position += length;
        }

        @Override
        public void write(byte[] entry, int offset, int length) {
            System.arraycopy(entry, offset, buffer, position, length);
            position += length;
        }

        @Override
        public void write(ByteBuffer entry) {
            var length = entry.remaining();
            entry.get(entry.position(), buffer, position, length);
            position += length;
        }

        @Override
        public byte[] toOutput() {
            var delta = buffer.length - position;
            if(delta != 0) {
                throw ProtobufSerializationException.sizeMismatch(delta);
            }

            return buffer;
        }
    }

    private static final class Buffer extends ProtobufOutputStream {
        private final ByteBuffer buffer;
        private Buffer(ByteBuffer buffer) {
            this.buffer = buffer;
        }

        @Override
        public void write(byte entry) {
            buffer.put(entry);
        }

        @Override
        public void write(byte[] entry) {
            buffer.put(entry);
        }

        @Override
        public void write(byte[] entry, int offset, int length) {
            buffer.put(entry, offset, length);
        }

        @Override
        public void write(ByteBuffer entry) {
            buffer.put(entry);
        }

        @Override
        public ByteBuffer toOutput() {
            var remaining = buffer.limit() - buffer.position();
            if(remaining != 0) {
                throw ProtobufSerializationException.sizeMismatch(remaining);
            }

            buffer.flip();
            return buffer;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy