From 17ef51db4692944314c3db1b113792eea15cfc3c Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Wed, 25 Mar 2026 23:57:21 +0200 Subject: [PATCH] =?UTF-8?q?=D0=91=D1=8B=D1=81=D1=82=D1=80=D1=8B=D0=B9=20wr?= =?UTF-8?q?ite/read=20int8,=20=D0=BF=D0=B0=D1=82=D1=87=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B4=D0=B0=D1=87=D0=B8=20reserved=20=D0=B1=D0=B0=D0=B9=D1=82?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20getBuffer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/orprotocol/Stream.java | 111 ++++++++++++++++-------- 1 file changed, 75 insertions(+), 36 deletions(-) diff --git a/src/main/java/io/orprotocol/Stream.java b/src/main/java/io/orprotocol/Stream.java index 5be1506..c796ad9 100644 --- a/src/main/java/io/orprotocol/Stream.java +++ b/src/main/java/io/orprotocol/Stream.java @@ -3,17 +3,17 @@ package io.orprotocol; import java.util.Arrays; public class Stream { - + private byte[] stream; - private int readPointer = 0; - private int writePointer = 0; + private int readPointer = 0; // bits + private int writePointer = 0; // bits public Stream() { this.stream = new byte[0]; } public Stream(byte[] stream) { - this.stream = stream; + this.stream = (stream == null) ? new byte[0] : stream; this.readPointer = 0; this.writePointer = this.stream.length << 3; } @@ -23,51 +23,72 @@ public class Stream { } public void setStream(byte[] stream) { - this.stream = stream; + this.stream = (stream == null) ? new byte[0] : stream; this.readPointer = 0; this.writePointer = this.stream.length << 3; } + // Fast path: write 9 bits (sign + value) in <= 2 byte writes public void writeInt8(int value) { int negationBit = value < 0 ? 1 : 0; int int8Value = Math.abs(value) & 0xFF; - /** - * 1 sign - * 8 bits for value - */ + int packed9 = (negationBit << 8) | int8Value; // 9 bits total + reserveBits(9); - stream[writePointer >> 3] |= (byte) (negationBit << (7 - (writePointer & 7))); - writePointer++; - - for (int i = 0; i < 8; i++) { - int bit = (int8Value >> (7 - i)) & 1; - stream[writePointer >> 3] |= (byte) (bit << (7 - (writePointer & 7))); - writePointer++; - } + + int wp = writePointer; + int byteIndex = wp >> 3; + int bitOffset = wp & 7; // 0..7 + + int firstCount = 8 - bitOffset; // bits into current byte + int secondCount = 9 - firstCount; // bits into next byte (1..8) + + int firstPart = packed9 >> secondCount; // fits firstCount bits + stream[byteIndex] |= (byte) firstPart; + + int secondMask = (1 << secondCount) - 1; + int secondPart = (packed9 & secondMask) << (8 - secondCount); + stream[byteIndex + 1] |= (byte) secondPart; + + writePointer = wp + 9; } + // Fast path: read 9 bits (sign + value) from <= 2 bytes public int readInt8() { - int value = 0; - int negationBit = (stream[readPointer >> 3] >> (7 - (readPointer & 7))) & 1; - readPointer++; - - for (int i = 0; i < 8; i++) { - int bit = (stream[readPointer >> 3] >> (7 - (readPointer & 7))) & 1; - value |= bit << (7 - i); - readPointer++; + if (remainingBits() < 9L) { + throw new IllegalStateException("Not enough bits to read Int8"); } - - return negationBit == 1 ? -value : value; + + int rp = readPointer; + int byteIndex = rp >> 3; + int bitOffset = rp & 7; + + int firstCount = 8 - bitOffset; + int secondCount = 9 - firstCount; + + int firstMask = (1 << firstCount) - 1; + int firstPart = stream[byteIndex] & firstMask; + int secondPart = ((stream[byteIndex + 1] & 0xFF) >> (8 - secondCount)); + + int packed9 = (firstPart << secondCount) | secondPart; + readPointer = rp + 9; + + int negationBit = (packed9 >> 8) & 1; + int int8Value = packed9 & 0xFF; + return negationBit == 1 ? -int8Value : int8Value; } public void writeBit(int value) { + reserveBits(1); int bit = value & 1; - ensureCapacity(writePointer >> 3); stream[writePointer >> 3] |= (byte) (bit << (7 - (writePointer & 7))); writePointer++; } public int readBit() { + if (remainingBits() < 1L) { + throw new IllegalStateException("Not enough bits to read bit"); + } int bit = (stream[readPointer >> 3] >> (7 - (readPointer & 7))) & 1; readPointer++; return bit; @@ -125,6 +146,10 @@ public class Stream { } public void writeString(String value) { + if (value == null) { + value = ""; + } + int length = value.length(); writeInt32(length); @@ -132,7 +157,7 @@ public class Stream { return; } - // writeInt16 -> 2 * writeInt8 -> 18 бит на символ + // writeInt16 -> 2 * writeInt8 -> 18 bits per char reserveBits((long) length * 18L); for (int i = 0; i < length; i++) { @@ -147,10 +172,8 @@ public class Stream { return ""; } - // На один символ тратится 18 бит (через writeInt16/readInt16) - long remainingBits = ((long) stream.length << 3) - readPointer; long requiredBits = (long) length * 18L; - if (requiredBits > remainingBits) { + if (requiredBits > remainingBits()) { return ""; } @@ -162,23 +185,34 @@ public class Stream { } public void writeBytes(byte[] value) { + if (value == null) { + value = new byte[0]; + } + writeInt32(value.length); if (value.length > 0) { + // writeInt8 = 9 bits per byte reserveBits((long) value.length * 9L); } - for (int i = 0; i < value.length; i++) { - writeInt8(value[i]); + for (byte b : value) { + writeInt8(b); } } public byte[] readBytes() { int length = readInt32(); + if (length < 0) { return new byte[0]; } + long requiredBits = (long) length * 9L; + if (requiredBits > remainingBits()) { + return new byte[0]; + } + byte[] value = new byte[length]; for (int i = 0; i < length; i++) { value[i] = (byte) readInt8(); @@ -187,15 +221,20 @@ public class Stream { } public boolean isEmpty() { - return this.stream.length == 0; + return writePointer == 0; } + // useful bytes without reserved tail public int length() { return (writePointer + 7) >> 3; } public byte[] getBuffer() { - return this.stream; + return getStream(); + } + + private long remainingBits() { + return (long) writePointer - readPointer; } private void reserveBits(long bitsToWrite) {