Skip to content

Commit 7a1b740

Browse files
authored
Merge pull request #206 from indunet/develop
Develop
2 parents 4ac5c81 + 5dcbf34 commit 7a1b740

18 files changed

Lines changed: 479 additions & 28 deletions

CHANGELOG-zh.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# 更新日志
22

3+
本文件用于记录本项目的所有重要变更。
4+
35
## [3.12.3] - 2025-11-23
46
### 新增
57
- 新增 `@BcdType` 注解与 `BcdCodec`,支持定长 packed BCD 整数(支持大小端),映射到 `int` / `Integer` 类型。

README-zh.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,16 @@ FastProto 是一个面向 **二进制通信协议** 的高性能序列化 / 反
1515
## *设计理念*
1616

1717
* **声明式优于命令式:** 协议字段用注解定义,结构一目了然,代码即文档。
18-
* **性能与可维护并重:** 在架构层做反射缓存与零拷贝等优化,在保持高吞吐的同时兼顾可维护性。
18+
* **性能与可维护并重:** 在架构层做反射缓存等优化,在保持高吞吐的同时兼顾可维护性。
1919
* **兼容工程现实:** 支持大小端混排、位域、BCD、CRC 等传统协议特征,方便对接存量系统。
2020

2121
## *核心功能*
2222

23-
* **注解驱动:** 字段用注解标记,解析和封装一目了然。
24-
* **类型丰富:** 支持 Java 原始类型、无符号类型、字符串、时间以及集合。
25-
* **灵活地址:** 提供反向地址,适配变长协议。
26-
* **可变长度:** 通过 `lengthRef` 引用计数字段,支持变长字符串/数组与结构体数组。详见[可变长度与结构体数组](docs/variable-length.md)
27-
* **动态偏移:** 使用 `offsetRef` 引用偏移字段,简化“头部存绝对位置、正文在后”的格式。详见[动态偏移](docs/dynamic-offset.md)
28-
* **字节顺序可选:** 大端或小端随心切换。
29-
* **公式支持:** Lambda 或自定义类均可实现编解码公式。
30-
* **校验和/CRC:** 使用 `@Checksum` 注解一次性定义起始地址、长度与存放地址,内置 CRC8(SMBus、MAXIM)、CRC16(MODBUS、CCITT)、CRC32/CRC32C、CRC64(ECMA/ISO)、LRC、XOR 等。
31-
* **生态集成:** 提供 Netty 编/解码器与 Kafka Serializer/Deserializer/Serde,可即插即用。详见[Netty 集成](docs/help.html#netty-integration)[Kafka 集成](docs/help.html#kafka-integration)
32-
* **多种API:** 兼顾效率与易用性。
23+
* **注解驱动协议映射:** 用少量注解描述二进制布局,替代手写偏移和位运算。
24+
* **类型与布局支持丰富:** 覆盖基础类型(含无符号)、字符串、时间、数组/集合,并支持可变长度、动态偏移和反向地址。
25+
* **表示精确可控:** 可配置字节序/位序,支持 BCD、位域以及自定义编解码公式。
26+
* **内置校验和/CRC:** 通过 `@Checksum` 或工具方法直接使用常见 CRC / 校验和算法。
27+
* **生态与 API 友好:** 提供 Netty 编解码器、Kafka 序列化组件,以及无需注解的链式 API。
3328

3429
查看[更新日志](CHANGELOG-zh.md)获取版本历史,英文版请查阅[CHANGELOG](CHANGELOG.md)
3530

@@ -190,6 +185,7 @@ FastProto支持Java基础数据类型,考虑到跨语言跨平台的数据交
190185
| @Int16Type | Short/short/Integer/int | short | 2 字节 |
191186
| @Int32Type | Integer/int | int | 4 字节 |
192187
| @Int64Type | Long/long | long | 8 字节 |
188+
| @BitFieldType | Integer/int | -- | 1..31 位 |
193189
| @UInt8Type | Integer/int | unsigned char | 1 字节 |
194190
| @UInt16Type | Integer/int | unsigned short | 2 字节 |
195191
| @UInt32Type | Long/long | unsigned int | 4 字节 |

README.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,16 @@ FastProto is a high-performance serialization/deserialization **library** design
1515
## *Design Philosophy*
1616

1717
- **Declarative over imperative:** Protocol fields are defined via annotations, making the structure self‑evident so that code doubles as documentation.
18-
- **Performance with maintainability:** Architectural optimizations such as reflection caching and zero‑copy are built in to keep high throughput without sacrificing readability and maintainability.
18+
- **Performance with maintainability:** Architectural optimizations such as reflection caching are built in to keep high throughput without sacrificing readability and maintainability.
1919
- **Grounded in engineering reality:** Supports mixed endianness, bit fields, BCD, CRC and other traditional protocol features to integrate smoothly with existing systems.
2020

2121
## *Key Features*
2222

23-
- **Annotation-Driven:** Quickly map binary data to Java fields.
24-
- **Broad Type Support:** Works with primitives, unsigned numbers, strings, time types, arrays and collections.
25-
- **Flexible Addressing:** Reverse addressing for variable-length packets.
26-
- **Variable-Length Fields:** Use `lengthRef` to reference a count field; supports variable-length strings/arrays and struct arrays. See [Variable Length and Struct Arrays](docs/variable-length.md).
27-
- **Dynamic Offset:** Use `offsetRef` to bind a field's offset to a previously decoded numeric field (useful for header-with-pointer formats). See [Dynamic Offset](docs/dynamic-offset.md).
28-
- **Configurable Byte Order:** Choose big-endian or little-endian to match your protocol.
29-
- **Custom Formulas:** Use lambdas or classes to transform values during encode/decode.
30-
- **Checksum/CRC:** Single-annotation `@Checksum` to define start, length and storage offset; built-ins include CRC8 (SMBus, MAXIM), CRC16 (MODBUS, CCITT), CRC32/CRC32C, CRC64 (ECMA/ISO), plus LRC and XOR.
31-
- **Integrations:** Netty codecs and Kafka Serializer/Deserializer/Serde for drop‑in use. See [Netty Integration](docs/help.html#netty-integration) and [Kafka Integration](docs/help.html#kafka-integration).
32-
- **Easy APIs:** Multiple APIs tuned for efficiency and reliability.
23+
- **Annotation‑driven protocol mapping:** Describe binary layouts with simple field annotations instead of manual offset and bit operations.
24+
- **Rich type & layout support:** Primitives (including unsigned), strings, time types, arrays/collections, variable‑length fields, dynamic offsets and reverse addressing.
25+
- **Precise control of representation:** Configurable byte/bit order, BCD, bit fields and custom encode/decode formulas.
26+
- **Built‑in checksum/CRC:** Single `@Checksum` annotation or utility methods covering common CRC and checksum algorithms.
27+
- **Ecosystem integrations:** Ready‑to‑use Netty codecs and Kafka Serializer/Deserializer/Serde, plus fluent APIs for use without annotations.
3328

3429
See the [CHANGELOG](CHANGELOG.md) for recent updates.
3530

@@ -187,9 +182,10 @@ FastProto supports Java primitive data types, taking into account cross-language
187182
| @AsciiType | Character/char | char | 1 bytes |
188183
| @CharType | Character/char | -- | 2 bytes |
189184
| @Int8Type | Byte/byte/Integer/int | char | 1 byte |
190-
| @Int16Type | Short/short/Integer/int | short | 2 bytes |
185+
| @Int16Type | Short/short/Integer/int | short | 2 bytes |
191186
| @Int32Type | Integer/int | int | 4 bytes |
192187
| @Int64Type | Long/long | long | 8 bytes |
188+
| @BitFieldType | Integer/int | -- | 1..31 bits |
193189
| @UInt8Type | Integer/int | unsigned char | 1 byte |
194190
| @UInt16Type | Integer/int | unsigned short | 2 bytes |
195191
| @UInt32Type | Long/long | unsigned int | 4 bytes |

docs/annotation-mapping.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ FastProto uses annotations placed on fields to describe how bytes map to values.
3333
| @Int16Type | Short/short / Integer/int | short | 2 bytes |
3434
| @Int32Type | Integer/int | int | 4 bytes |
3535
| @Int64Type | Long/long | long | 8 bytes |
36+
| @BitFieldType | Integer/int | -- | 1..31 bits |
3637
| @UInt8Type | Integer/int | unsigned char | 1 byte |
3738
| @UInt16Type | Integer/int | unsigned short | 2 bytes |
3839
| @UInt32Type | Long/long | unsigned int | 4 bytes |

docs/byte-and-bit-order.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ public class Flags {
6161
}
6262
```
6363

64+
### Bit field (1..31 bits, supports bitOrder + byteOrder)
65+
```java
66+
import org.indunet.fastproto.*;
67+
import org.indunet.fastproto.annotation.*;
68+
69+
@DefaultByteOrder(ByteOrder.LITTLE)
70+
@DefaultBitOrder(BitOrder.LSB_0)
71+
public class BitFields {
72+
// Use lengthRef to determine bit width dynamically; logical start bitOffset=6, spans bytes
73+
@BitFieldType(offset = 0, bitOffset = 6, lengthRef = "$width", byteOrder = ByteOrder.LITTLE, bitOrder = BitOrder.LSB_0)
74+
int value;
75+
76+
@UInt8Type(offset = 2)
77+
int width; // Provides the length; must be declared before use
78+
}
79+
```
80+
6481
### Array with per‑element endianness
6582
```java
6683
import org.indunet.fastproto.*;

docs/dynamic-offset.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## Dynamic Offset with offsetRef
22

33
### Overview
4-
Some formats store an absolute position in a header and place the actual data later in the file. Use `offsetRef` to point a field’s `offset()` to a previously decoded numeric field in the same class. The source field name can be prefixed with `$`.
4+
Some formats store an absolute position in a header and place the actual data later in the file. Use `offsetRef` to point a field’s `offset()` to a previously decoded numeric field in the same class. The source field name must be prefixed with `$`.
55

66
### Rules
77
- The referenced field must appear earlier in the class and be decoded before the target.

docs/variable-length.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Variable-length fields are common in binary protocols. FastProto supports them v
88

99
### Basics: length vs lengthRef
1010
- `length`: a fixed number of elements/bytes.
11-
- `lengthRef`: the name of a field in the same class that holds the length/count; you may optionally prefix it with `$` (e.g. `$count`).
11+
- `lengthRef`: the name of a field in the same class that holds the length/count; it **must** be prefixed with `$` (e.g. `$count`).
1212
- If both are set, `lengthRef` takes precedence for the effective length.
1313

1414
### Strings with lengthRef
@@ -87,7 +87,7 @@ public class PacketVar {
8787

8888
### Tips and caveats
8989
- Ensure the referenced length field is decoded before the variable-length field (i.e., it appears earlier in the class with a lower offset).
90-
- `lengthRef` accepts optional leading `$` and is case-sensitive with respect to the Java field name.
90+
- `lengthRef` is case-sensitive and must start with `$` followed by the field name.
9191
- For strings, you can also set `charset`.
9292
- For boolean bit arrays, use `BoolArrayType` (byte-aligned). Per-bit packed variable-length booleans are not supported.
9393
- With `@AutoType`, you must still supply `offset` and `lengthRef` for arrays/strings; FastProto infers only the data type, not the layout.

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@
226226
<version>3.9.1</version>
227227
<scope>provided</scope>
228228
</dependency>
229+
<!-- SLF4J binding for tests to avoid NOP logger warning -->
230+
<dependency>
231+
<groupId>org.slf4j</groupId>
232+
<artifactId>slf4j-simple</artifactId>
233+
<version>1.7.36</version>
234+
<scope>test</scope>
235+
</dependency>
229236
</dependencies>
230237

231238
<build>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2019-2025 indunet.org
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.indunet.fastproto.annotation;
18+
19+
import org.indunet.fastproto.BitOrder;
20+
import org.indunet.fastproto.ByteOrder;
21+
import org.indunet.fastproto.graph.resolve.validate.DecodingFormulaValidator;
22+
import org.indunet.fastproto.graph.resolve.validate.EncodingFormulaValidator;
23+
24+
import java.lang.annotation.ElementType;
25+
import java.lang.annotation.Retention;
26+
import java.lang.annotation.RetentionPolicy;
27+
import java.lang.annotation.Target;
28+
29+
/**
30+
* Bit field type, supports unsigned value extraction from 1..31 bits.
31+
*
32+
* @author Deng Ran
33+
* @since 3.13.0
34+
*/
35+
@DataType
36+
@Validator({DecodingFormulaValidator.class, EncodingFormulaValidator.class})
37+
@Target(ElementType.FIELD)
38+
@Retention(RetentionPolicy.RUNTIME)
39+
public @interface BitFieldType {
40+
/*
41+
* The byte offset of the field in the binary data.
42+
*/
43+
int offset() default Integer.MIN_VALUE;
44+
45+
/*
46+
* Reference to an offset field name in the same class. Accepts optional leading '$'.
47+
*/
48+
String offsetRef() default "";
49+
50+
/*
51+
* The bit offset (logical index 0..7) within the starting byte.
52+
*/
53+
int bitOffset();
54+
55+
/*
56+
* The bit width of the field (1..31). If 0, a valid lengthRef must be provided.
57+
*/
58+
int length() default 0;
59+
60+
/*
61+
* Reference to a length field name in the same class. Accepts optional leading '$'.
62+
*/
63+
String lengthRef() default "";
64+
65+
/*
66+
* The byte order for multi-byte bit ranges, priority higher than @DefaultByteOrder.
67+
*/
68+
ByteOrder[] byteOrder() default {};
69+
70+
/*
71+
* The bit order within each byte, priority higher than @DefaultBitOrder.
72+
*/
73+
BitOrder[] bitOrder() default {};
74+
}
75+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2019-2025 indunet.org
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.indunet.fastproto.codec;
18+
19+
import lombok.val;
20+
import org.indunet.fastproto.annotation.BitFieldType;
21+
import org.indunet.fastproto.exception.DecodingException;
22+
import org.indunet.fastproto.exception.EncodingException;
23+
import org.indunet.fastproto.io.ByteBufferInputStream;
24+
import org.indunet.fastproto.io.ByteBufferOutputStream;
25+
26+
/**
27+
* Codec for BitFieldType, unsigned width 1..31 bits mapped to Integer.
28+
*/
29+
public class BitFieldCodec implements Codec<Integer> {
30+
@Override
31+
public Integer decode(CodecContext context, ByteBufferInputStream inputStream) {
32+
try {
33+
val type = context.getDataTypeAnnotation(BitFieldType.class);
34+
val bitOrder = context.getBitOrder(type::bitOrder);
35+
val byteOrder = context.getByteOrder(type::byteOrder);
36+
int length = type.length();
37+
38+
return inputStream.readBits(type.offset(), type.bitOffset(), length, bitOrder, byteOrder);
39+
} catch (IndexOutOfBoundsException | IllegalArgumentException e) {
40+
throw new DecodingException("Fail decoding bit field type.", e);
41+
}
42+
}
43+
44+
@Override
45+
public void encode(CodecContext context, ByteBufferOutputStream outputStream, Integer value) {
46+
try {
47+
val type = context.getDataTypeAnnotation(BitFieldType.class);
48+
val bitOrder = context.getBitOrder(type::bitOrder);
49+
val byteOrder = context.getByteOrder(type::byteOrder);
50+
int length = type.length();
51+
52+
outputStream.writeBits(type.offset(), type.bitOffset(), length, bitOrder, byteOrder, value);
53+
} catch (IndexOutOfBoundsException | IllegalArgumentException e) {
54+
throw new EncodingException("Fail encoding bit field type.", e);
55+
}
56+
}
57+
}
58+

0 commit comments

Comments
 (0)