Skip to content

Commit 8f8441b

Browse files
committed
chore: release version 3.12.1 with enhancements and fixes
- Fixed ByteBuffer to auto-grow when writing beyond current capacity on non-fixed buffers, preventing ArrayIndexOutOfBounds exceptions. - Updated ByteBufferIOStream.align method to require a positive power-of-two alignment, throwing IllegalArgumentException for invalid values. - Redesigned documentation site homepage and updated version references to 3.12.1. - Added an Android compatibility guide in the documentation.
1 parent 95d8129 commit 8f8441b

10 files changed

Lines changed: 145 additions & 24 deletions

File tree

CHANGELOG-zh.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# 更新日志
22

3-
该文件记录项目的主要更新内容。
3+
## [3.12.1] - 2025-08-19
4+
### 修复
5+
- ByteBuffer:在非固定缓冲区写入超出当前容量时自动扩容,避免数组越界异常。
6+
- ByteBufferIOStream.align:对齐参数改为必须是正的 2 的幂,非法值抛出 IllegalArgumentException。
7+
8+
### 变更
9+
- 文档站点:重设计 `docs/index.html` 主页(概览、特性、基础用法),帮助页默认落点改为 Quick Start,特性区块增加小图标。
10+
- 文档内版本号统一更新为 3.12.1。
11+
12+
### 新增
13+
- 新增 Android 兼容性指南 `docs/android.md` 并在相关页面添加链接。
414

515
## [3.12.0] - 2025-08-11
616
### 新增

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [3.12.1] - 2025-08-19
6+
### Fixed
7+
- ByteBuffer: auto-growth when writing beyond current capacity on non-fixed buffers to prevent ArrayIndexOutOfBounds.
8+
- ByteBufferIOStream.align: validation now requires a positive power-of-two alignment; incorrect values throw IllegalArgumentException.
9+
10+
### Changed
11+
- Docs site: redesigned `docs/index.html` homepage (overview, features, basic usage), set Quick Start as default in help, added icons to features.
12+
- Version references in docs updated to 3.12.1.
13+
14+
### Added
15+
- Android compatibility guide `docs/android.md` and cross-links.
16+
517
## [3.12.0] - 2025-08-11
618
### Added
719
- Single-annotation checksum: `@Checksum(start, length, offset, type, byteOrder)` with encode auto-write and decode auto-verify flows.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ See the [CHANGELOG](CHANGELOG.md) for recent updates.
4949
<dependency>
5050
<groupId>org.indunet</groupId>
5151
<artifactId>fastproto</artifactId>
52-
<version>3.12.0</version>
52+
<version>3.12.1</version>
5353
</dependency>
5454
```
5555

5656
* Gradle
5757

5858
```gradle
59-
implementation "org.indunet:fastproto:3.12.0"
59+
implementation "org.indunet:fastproto:3.12.1"
6060
```
6161

6262

docs/index.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@
125125
"applicationCategory":"DeveloperApplication",
126126
"operatingSystem":"Cross-platform",
127127
"description":"Annotation-driven binary protocol toolkit for Java. Built-in checksum/CRC support.",
128-
"softwareVersion":"3.12.0",
128+
"softwareVersion":"3.12.1",
129129
"license":"https://www.apache.org/licenses/LICENSE-2.0",
130130
"url":"https://indunet.github.io/fastproto/",
131-
"downloadUrl":"https://repo1.maven.org/maven2/org/indunet/fastproto/3.12.0/",
131+
"downloadUrl":"https://repo1.maven.org/maven2/org/indunet/fastproto/3.12.1/",
132132
"programmingLanguage":"Java"
133133
}
134134
</script>
@@ -153,7 +153,7 @@ <h1>FastProto</h1>
153153
<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 .5A12 12 0 0 0 8.21 23.9c.6.11.82-.26.82-.58 0-.29-.01-1.05-.02-2.06-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.83 1.23 1.83 1.23 1.07 1.84 2.8 1.31 3.49 1 .11-.78.42-1.31.76-1.61-2.66-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.23-3.22-.12-.3-.54-1.52.12-3.17 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.65.24 2.87.12 3.17.77.84 1.23 1.9 1.23 3.22 0 4.61-2.81 5.62-5.49 5.92.43.37.81 1.1.81 2.22 0 1.6-.01 2.89-.01 3.29 0 .32.22.7.82.58A12 12 0 0 0 12 .5z"/></svg>
154154
<span>GitHub</span>
155155
</a>
156-
<a href="https://search.maven.org/artifact/org.indunet/fastproto/3.12.0/jar" title="Maven">
156+
<a href="https://search.maven.org/artifact/org.indunet/fastproto/3.12.1/jar" title="Maven">
157157
<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 2l7 4v8l-7 4-7-4V6l7-4zm0 2.2L7 6.7v6.6l5 2.9 5-2.9V6.7L12 4.2z"/></svg>
158158
<span>Maven</span>
159159
</a>
@@ -248,18 +248,18 @@ <h3>Install</h3>
248248
<pre><code>&lt;dependency&gt;
249249
&lt;groupId&gt;org.indunet&lt;/groupId&gt;
250250
&lt;artifactId&gt;fastproto&lt;/artifactId&gt;
251-
&lt;version&gt;3.12.0&lt;/version&gt;
251+
&lt;version&gt;3.12.1&lt;/version&gt;
252252
&lt;/dependency&gt;</code></pre>
253253
<p style="margin:12px 0 6px; color:var(--muted)">Gradle</p>
254-
<pre><code>implementation "org.indunet:fastproto:3.12.0"</code></pre>
254+
<pre><code>implementation "org.indunet:fastproto:3.12.1"</code></pre>
255255
</div>
256256
</section>
257257

258258
<section id="license" class="grid">
259259
<div class="card span-12">
260260
<h3>License</h3>
261261
<p>FastProto is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache License 2.0</a>. Commercial‑friendly and widely adopted.</p>
262-
<p>Source code on <a href="https://github.com/indunet/fastproto">GitHub</a>. Artifacts on <a href="https://search.maven.org/artifact/org.indunet/fastproto/3.12.0/jar">Maven Central</a>.</p>
262+
<p>Source code on <a href="https://github.com/indunet/fastproto">GitHub</a>. Artifacts on <a href="https://search.maven.org/artifact/org.indunet/fastproto/3.12.1/jar">Maven Central</a>.</p>
263263
</div>
264264
</section>
265265

docs/quick-start.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ Add the dependency via Maven or Gradle.
1010
<dependency>
1111
<groupId>org.indunet</groupId>
1212
<artifactId>fastproto</artifactId>
13-
<version>3.12.0</version>
13+
<version>3.12.1</version>
1414
</dependency>
1515
```
1616

1717
```gradle
18-
implementation "org.indunet:fastproto:3.12.0"
18+
implementation "org.indunet:fastproto:3.12.1"
1919
```
2020

2121
## Define Your Protocol Model

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
<groupId>org.indunet</groupId>
88
<artifactId>fastproto</artifactId>
9-
<version>3.12.0</version>
9+
<version>3.12.1</version>
1010
<packaging>jar</packaging>
1111
<name>FastProto</name>
12-
<description>FastProto is a lightweight Java library that makes binary protocols effortless, with annotation-driven mapping and built-in checksum/CRC support (CRC8/16/32/64, LRC, XOR).</description>
12+
<description>FastProto is a lightweight Java library that makes binary protocols effortless, with annotation-driven mapping and built-in checksum/CRC.</description>
1313

1414
<url>http://fastproto.indunet.org</url>
1515
<inceptionYear>2019</inceptionYear>

src/main/java/org/indunet/fastproto/io/ByteBuffer.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,30 @@ public void set(int offset, byte value) {
7979
}
8080

8181
private void grow(int index) {
82-
if (index >= length) {
83-
length = index + 1;
84-
} else if (length == bytes.length && !this.fixed) {
85-
int newLength = Math.min(bytes.length * 2, MAX_ARRAY_SIZE);
86-
bytes = Arrays.copyOf(bytes, newLength);
87-
88-
this.grow(index);
89-
}
90-
}
82+
// If buffer is fixed-length and index exceeds capacity, fail fast.
83+
if (this.fixed && index >= this.bytes.length) {
84+
throw new IndexOutOfBoundsException();
85+
}
86+
87+
// Auto-grow when not fixed and the target index exceeds current capacity.
88+
if (!this.fixed && index >= this.bytes.length) {
89+
int newCapacity = this.bytes.length > 0 ? this.bytes.length : 1;
90+
while (newCapacity <= index && newCapacity < MAX_ARRAY_SIZE) {
91+
int next = newCapacity << 1;
92+
if (next <= 0 || next > MAX_ARRAY_SIZE) {
93+
newCapacity = MAX_ARRAY_SIZE;
94+
break;
95+
}
96+
newCapacity = next;
97+
}
98+
this.bytes = Arrays.copyOf(this.bytes, newCapacity);
99+
}
100+
101+
// Update logical length to include the written index.
102+
if (index >= this.length) {
103+
this.length = index + 1;
104+
}
105+
}
91106

92107
public byte get(int offset) {
93108
int o = this.reverse(offset);

src/main/java/org/indunet/fastproto/io/ByteBufferIOStream.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ public ByteBufferIOStream(ByteBuffer buffer) {
3434
}
3535

3636
public void align(int alignment) {
37-
if (alignment <= 0 || (alignment & 0x01) != 0) {
38-
throw new IllegalArgumentException("alignment must be a positive even number");
37+
if (alignment <= 0 || (alignment & (alignment - 1)) != 0) {
38+
throw new IllegalArgumentException("alignment must be a positive power of two");
3939
}
4040

4141
int index = this.byteIndex;

src/test/java/org/indunet/fastproto/io/ByteBufferIOStreamTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,49 @@ public void testAlign() {
5656
.length);
5757
}
5858

59+
@Test
60+
public void testAlignPowerOfTwoValidation() {
61+
val stream = new MockByteBufferOutputStream();
62+
63+
// Valid power-of-two alignments should work
64+
stream.align(1);
65+
stream.align(2);
66+
stream.align(4);
67+
stream.align(8);
68+
stream.align(16);
69+
stream.align(32);
70+
71+
// Invalid alignments should throw IllegalArgumentException
72+
assertThrows(IllegalArgumentException.class, () -> stream.align(0));
73+
assertThrows(IllegalArgumentException.class, () -> stream.align(-1));
74+
assertThrows(IllegalArgumentException.class, () -> stream.align(3));
75+
assertThrows(IllegalArgumentException.class, () -> stream.align(5));
76+
assertThrows(IllegalArgumentException.class, () -> stream.align(6));
77+
assertThrows(IllegalArgumentException.class, () -> stream.align(7));
78+
assertThrows(IllegalArgumentException.class, () -> stream.align(9));
79+
assertThrows(IllegalArgumentException.class, () -> stream.align(10));
80+
assertThrows(IllegalArgumentException.class, () -> stream.align(12));
81+
assertThrows(IllegalArgumentException.class, () -> stream.align(15));
82+
}
83+
84+
@Test
85+
public void testAlignBehavior() {
86+
val stream = new MockByteBufferOutputStream();
87+
88+
// Test alignment from various starting positions
89+
stream.append((byte) 0x01); // byteIndex = 1
90+
stream.align(4); // Should align to 4
91+
assertEquals(4, stream.byteIndex);
92+
93+
stream.append((byte) 0x02); // byteIndex = 5
94+
stream.align(8); // Should align to 8
95+
assertEquals(8, stream.byteIndex);
96+
97+
// Test alignment when already aligned
98+
stream.align(8); // Should stay at 8
99+
assertEquals(8, stream.byteIndex);
100+
}
101+
59102
@Test
60103
public void testSkip() {
61104
val stream = new MockByteBufferOutputStream();

src/test/java/org/indunet/fastproto/io/ByteBufferTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,47 @@ public void testSetAndGet() {
8585
assertThrows(CodecException.class, () -> byteBuffer.get(-1));
8686
}
8787

88+
@Test
89+
public void testDynamicGrowth() {
90+
// Test auto-expansion when writing beyond initial capacity
91+
ByteBuffer byteBuffer = new ByteBuffer();
92+
93+
// Write at a large offset to trigger capacity expansion
94+
byteBuffer.set(500, (byte) 0x42);
95+
96+
assertEquals((byte) 0x42, byteBuffer.get(500));
97+
assertEquals(501, byteBuffer.size());
98+
assertTrue(byteBuffer.toBytes().length >= 501);
99+
}
100+
101+
@Test
102+
public void testFixedBufferBounds() {
103+
// Test that fixed-length buffer throws on out-of-bounds write
104+
byte[] fixedArray = new byte[10];
105+
ByteBuffer fixedBuffer = new ByteBuffer(fixedArray);
106+
107+
// Should work within bounds
108+
fixedBuffer.set(9, (byte) 0x99);
109+
assertEquals((byte) 0x99, fixedBuffer.get(9));
110+
111+
// Should throw on out-of-bounds write
112+
assertThrows(IndexOutOfBoundsException.class, () -> fixedBuffer.set(10, (byte) 0x01));
113+
}
88114

115+
@Test
116+
public void testGrowthFromZeroCapacity() {
117+
// Test growth when starting with minimal capacity
118+
ByteBuffer byteBuffer = new ByteBuffer();
119+
120+
// Write at index 0 to establish baseline
121+
byteBuffer.set(0, (byte) 0x01);
122+
assertEquals(1, byteBuffer.size());
123+
124+
// Write at a much larger index to test multiple expansions
125+
byteBuffer.set(1000, (byte) 0x02);
126+
assertEquals(1001, byteBuffer.size());
127+
assertEquals((byte) 0x01, byteBuffer.get(0));
128+
assertEquals((byte) 0x02, byteBuffer.get(1000));
129+
}
89130
}
90131

0 commit comments

Comments
 (0)