feat: make gb32960 archive history query production ready
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package com.lingniu.ingest.codec;
|
||||
|
||||
/** BCC(异或校验)。GB/T 32960 与 JT/T 808 都使用此方式做帧尾校验。 */
|
||||
public final class BccChecksum {
|
||||
|
||||
private BccChecksum() {}
|
||||
|
||||
public static byte compute(byte[] data, int offset, int length) {
|
||||
byte sum = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
sum ^= data[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static boolean verify(byte[] data, int offset, int length, byte expected) {
|
||||
return compute(data, offset, length) == expected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.lingniu.ingest.codec;
|
||||
|
||||
/** BCD (Binary-Coded Decimal) 编解码。JT/T 808 的手机号、时间戳常用。 */
|
||||
public final class BcdCodec {
|
||||
|
||||
private BcdCodec() {}
|
||||
|
||||
public static byte[] encode(String decimal) {
|
||||
int len = (decimal.length() + 1) / 2;
|
||||
byte[] out = new byte[len];
|
||||
int idx = decimal.length() % 2;
|
||||
if (idx == 1) {
|
||||
out[0] = (byte) Character.digit(decimal.charAt(0), 10);
|
||||
}
|
||||
for (int i = idx; i < decimal.length(); i += 2) {
|
||||
int hi = Character.digit(decimal.charAt(i), 10);
|
||||
int lo = Character.digit(decimal.charAt(i + 1), 10);
|
||||
out[(i + idx) / 2] = (byte) ((hi << 4) | lo);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static String decode(byte[] bcd) {
|
||||
StringBuilder sb = new StringBuilder(bcd.length * 2);
|
||||
for (byte b : bcd) {
|
||||
sb.append((b >> 4) & 0x0F).append(b & 0x0F);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.lingniu.ingest.codec;
|
||||
|
||||
/** 位运算工具:从字节数组读写无符号整数。 */
|
||||
public final class BitUtils {
|
||||
|
||||
private BitUtils() {}
|
||||
|
||||
public static int readUint8(byte[] buf, int offset) {
|
||||
return buf[offset] & 0xFF;
|
||||
}
|
||||
|
||||
public static int readUint16BE(byte[] buf, int offset) {
|
||||
return ((buf[offset] & 0xFF) << 8) | (buf[offset + 1] & 0xFF);
|
||||
}
|
||||
|
||||
public static long readUint32BE(byte[] buf, int offset) {
|
||||
return ((long) (buf[offset] & 0xFF) << 24)
|
||||
| ((long) (buf[offset + 1] & 0xFF) << 16)
|
||||
| ((long) (buf[offset + 2] & 0xFF) << 8)
|
||||
| (buf[offset + 3] & 0xFF);
|
||||
}
|
||||
|
||||
public static void writeUint16BE(byte[] buf, int offset, int value) {
|
||||
buf[offset] = (byte) ((value >> 8) & 0xFF);
|
||||
buf[offset + 1] = (byte) (value & 0xFF);
|
||||
}
|
||||
|
||||
public static void writeUint32BE(byte[] buf, int offset, long value) {
|
||||
buf[offset] = (byte) ((value >> 24) & 0xFF);
|
||||
buf[offset + 1] = (byte) ((value >> 16) & 0xFF);
|
||||
buf[offset + 2] = (byte) ((value >> 8) & 0xFF);
|
||||
buf[offset + 3] = (byte) (value & 0xFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lingniu.ingest.codec;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class BcdCodecTest {
|
||||
|
||||
@Test
|
||||
void roundTripEvenLength() {
|
||||
// 偶数位直接等值
|
||||
byte[] bcd = BcdCodec.encode("1380013800");
|
||||
assertThat(BcdCodec.decode(bcd)).isEqualTo("1380013800");
|
||||
}
|
||||
|
||||
@Test
|
||||
void roundTripOddLengthPadsLeadingZero() {
|
||||
// 奇数位会在高位补 0
|
||||
byte[] bcd = BcdCodec.encode("13800138000");
|
||||
assertThat(BcdCodec.decode(bcd)).isEqualTo("013800138000");
|
||||
}
|
||||
|
||||
@Test
|
||||
void checksumMatches() {
|
||||
byte[] data = {0x01, 0x02, 0x03, 0x04};
|
||||
assertThat(BccChecksum.compute(data, 0, data.length)).isEqualTo((byte) 0x04);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user