Compare commits
No commits in common. "main" and "v1.5.4" have entirely different histories.
@ -1,12 +1,5 @@
|
||||
# Changelog: @go/encoding
|
||||
|
||||
## v1.5.5 (2026-06-22)
|
||||
- **安全与防碰撞重构 (In-place & Non-linear)**:
|
||||
- 修复 `ExchangeInt` 变成就地修改并写回,解决了 ordered 模式下混淆被完全丢弃的回归 Bug。使用栈分配优化了 `size <= 20` 时的无逃逸零拷贝性能。
|
||||
- 优化 `HashInt` 为双向双径非线性置换混淆(正向向右、反向向左),通过在两套进制字符集(Ordered 与 Default)之间建立严格双射 S-Box 的交叉映射阻断前缀碰撞抵消。
|
||||
- 清理废弃的 `FillRand` 冗余 API,统一收敛并升级 `FillInt` 为利用 `go/rand.FastInt` 进行的并发安全、零锁极速随机填充。
|
||||
- 新增 `FoldInt` 方法:使用字符集模同余相加法折叠超出长度的尾部字节,确保超长截断时的信息熵完美传递。
|
||||
|
||||
## v1.5.4 (2026-06-21)
|
||||
- **重构与错误堆栈支持**:
|
||||
- 重构 `js_export.go`,将 `EncodeInt` 等匿名包装以及 `UnHex`/`UnBase64`/`UnURLBase64`/`UnURLEncode` 直接抛错的 Go 接口重写为具名函数,并动态使用 `jsmod.MakeError` 包裹错误。
|
||||
|
||||
@ -41,8 +41,7 @@
|
||||
- `func DecodeInt(buf []byte) uint64`
|
||||
- `func FillInt(buf []byte, length int) []byte`
|
||||
- `func ExchangeInt(buf []byte) []byte`
|
||||
- `func HashInt(buf []byte) []byte`
|
||||
- `func FoldInt(buf []byte, length int) []byte`
|
||||
- `func HashInt(data []byte, key []byte) []byte`
|
||||
|
||||
## 📦 安装
|
||||
|
||||
|
||||
16
TEST.md
16
TEST.md
@ -1,9 +1,9 @@
|
||||
# Test Report: @go/encoding
|
||||
|
||||
## 📋 测试概览
|
||||
- **测试时间**: 2026-06-22
|
||||
- **测试时间**: 2026-05-08
|
||||
- **测试环境**: darwin/amd64
|
||||
- **Go 版本**: 1.26.1
|
||||
- **Go 版本**: 1.25.0
|
||||
|
||||
## ✅ 功能测试 (Functional Tests)
|
||||
| 场景 | 状态 | 描述 |
|
||||
@ -13,18 +13,16 @@
|
||||
| `TestWebEncoding` | PASS | URL 编解码、HTML 转义反转义往返测试。 |
|
||||
| `TestSortJoin` | PASS | Map 与 Struct 的排序拼接测试,验证签名场景。 |
|
||||
| `TestUtf8` | PASS | UTF-8 校验逻辑测试。 |
|
||||
| `TestIntEncoder` | PASS | 包含正常编解码、FillInt 补齐、ExchangeInt 原地置换、HashInt 非线性单向散列与 FoldInt 模加折叠测试。 |
|
||||
| `TestIntEncoderInstance` | PASS | 自定义 IntEncoder 实例的高性能编解码与原地置换测试。 |
|
||||
| `TestIntEncoder` | PASS | 包含正常编解码、FillInt 补齐、ExchangeInt 置换、HashInt 确定性测试。 |
|
||||
|
||||
## 🛡️ 鲁棒性防御 (Robustness)
|
||||
- **消除摩擦 (Frictionless)**:废除 `Must` 系列 API,通过 `cast.As` 实现静默处理,保持业务逻辑简洁。
|
||||
- **API 补全**:补全了所有 `FromString` 版本的解码函数,并提供 standard error 返回。
|
||||
- **API 补全**:补全了所有 `FromString` 版本的解码函数,并提供标准 error 返回。
|
||||
- **参数校验**:`NewIntEncoder` 对字符集重复、长度不足等构造错误进行了防御性校验。
|
||||
- **折叠截断安全**:`FoldInt` 通过同余模加法在 Base62 字符集内折叠信息,彻底解决物理截断引起的局部哈希碰撞问题。
|
||||
|
||||
## ⚡ 性能基准 (Benchmarks)
|
||||
| 函数 | 平均耗时 | 性能分析 |
|
||||
| :--- | :--- | :--- |
|
||||
| `HexEncode` | **209.7 ns/op** | 稳定且低开销。 |
|
||||
| `Base64Encode` | **185.5 ns/op** | 吞吐量优异。 |
|
||||
| `IntEncoder` | **47.04 ns/op** | 栈分配极致优化,零 GC 负担。 |
|
||||
| `HexEncode` | **46.64 ns/op** | 高效处理二进制数据。 |
|
||||
| `Base64Encode` | **42.24 ns/op** | 吞吐量优异。 |
|
||||
| `IntEncoder` | **46.66 ns/op** | 整数编码逻辑极简,开销极小。 |
|
||||
|
||||
8
go.mod
8
go.mod
@ -2,8 +2,6 @@ module apigo.cc/go/encoding
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
apigo.cc/go/cast v1.5.3
|
||||
apigo.cc/go/jsmod v1.5.3
|
||||
apigo.cc/go/rand v1.5.3
|
||||
)
|
||||
require apigo.cc/go/cast v1.5.3
|
||||
|
||||
require apigo.cc/go/jsmod v1.5.3
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
|
||||
"apigo.cc/go/cast"
|
||||
"apigo.cc/go/rand"
|
||||
)
|
||||
|
||||
// IntEncoder 提供整数与字节切片之间的自定义进制转换
|
||||
@ -34,7 +35,7 @@ func (enc *IntEncoder) AppendInt(buf []byte, u uint64) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
// FillInt 使用随机字符序列填充数据至指定长度,高并发下具备零锁极速随机性能,就地修改并返回
|
||||
// FillInt 使用循环字符序列填充数据至指定长度
|
||||
func (enc *IntEncoder) FillInt(data any, length int) []byte {
|
||||
buf := cast.To[[]byte](data)
|
||||
currLen := len(buf)
|
||||
@ -49,36 +50,12 @@ func (enc *IntEncoder) FillInt(data any, length int) []byte {
|
||||
buf = buf[:length]
|
||||
radix := int(enc.radix)
|
||||
for i := currLen; i < length; i++ {
|
||||
// 使用 go/rand 零锁随机性能从 digits 中抽取字符填充
|
||||
rnd := rand.FastInt(0, radix-1)
|
||||
buf[i] = enc.digits[rnd]
|
||||
buf[i] = enc.digits[i%radix]
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// FoldInt 使用编码器的字符集模加法,将超出 length 的尾部字节折叠到前 length 字节中,就地修改并截断返回
|
||||
func (enc *IntEncoder) FoldInt(buf []byte, length int) []byte {
|
||||
size := len(buf)
|
||||
if size <= length {
|
||||
return buf
|
||||
}
|
||||
radix := int(enc.radix)
|
||||
for i := length; i < size; i++ {
|
||||
p1 := enc.decodeMap[buf[i%length]]
|
||||
p2 := enc.decodeMap[buf[i]]
|
||||
if p1 < 0 {
|
||||
p1 = 0
|
||||
}
|
||||
if p2 < 0 {
|
||||
p2 = 0
|
||||
}
|
||||
newP := (p1 + p2) % radix
|
||||
buf[i%length] = enc.digits[newP]
|
||||
}
|
||||
return buf[:length]
|
||||
}
|
||||
|
||||
// ExchangeInt 对数据进行位置交替重排,就地修改并返回
|
||||
// ExchangeInt 对数据进行位置交替重排
|
||||
func (enc *IntEncoder) ExchangeInt(data any) []byte {
|
||||
buf := cast.To[[]byte](data)
|
||||
size := len(buf)
|
||||
@ -86,14 +63,7 @@ func (enc *IntEncoder) ExchangeInt(data any) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
var buf2 []byte
|
||||
if size <= 20 {
|
||||
var tmp [20]byte // 栈分配:极致性能,零垃圾回收负担
|
||||
buf2 = tmp[:size]
|
||||
} else {
|
||||
buf2 = make([]byte, size)
|
||||
}
|
||||
|
||||
buf2 := make([]byte, size)
|
||||
buf2_i := 0
|
||||
buf2_ai := 0
|
||||
buf2_ri := size - 1
|
||||
@ -107,43 +77,14 @@ func (enc *IntEncoder) ExchangeInt(data any) []byte {
|
||||
}
|
||||
buf2_i++
|
||||
}
|
||||
|
||||
// 将打乱后的数据拷贝回原切片,实现“原地修改”
|
||||
copy(buf, buf2)
|
||||
return buf
|
||||
return buf2
|
||||
}
|
||||
|
||||
// HashInt 双向非线性渐进式置换混淆,原地修改并返回,所有值保持在 radix 字符集内
|
||||
func (enc *IntEncoder) HashInt(buf []byte) []byte {
|
||||
if len(buf) == 0 {
|
||||
return buf
|
||||
}
|
||||
|
||||
// 第一轮:正向传播(左向右)
|
||||
prevP := (len(buf) * 17) % int(enc.radix)
|
||||
for i, c := range buf {
|
||||
p := enc.decodeMap[c]
|
||||
if p < 0 {
|
||||
p = 0
|
||||
}
|
||||
p = (prevP + p) % int(enc.radix)
|
||||
buf[i] = enc.digits[p]
|
||||
prevP = OrderedIntEncoder.decodeMap[DefaultIntEncoder.digits[p]]
|
||||
}
|
||||
|
||||
// 第二轮:反向传播(右向左),回传差异实现全局雪崩扩散
|
||||
prevP = (len(buf) * 31) % int(enc.radix)
|
||||
for i := len(buf) - 1; i >= 0; i-- {
|
||||
p := enc.decodeMap[buf[i]]
|
||||
if p < 0 {
|
||||
p = 0
|
||||
}
|
||||
p = (prevP + p) % int(enc.radix)
|
||||
buf[i] = enc.digits[p]
|
||||
prevP = OrderedIntEncoder.decodeMap[DefaultIntEncoder.digits[p]]
|
||||
}
|
||||
|
||||
return buf
|
||||
// HashInt 对数据进行 HMAC-SHA512 哈希
|
||||
func (enc *IntEncoder) HashInt(data any, key any) []byte {
|
||||
hash := hmac.New(sha512.New, cast.To[[]byte](key))
|
||||
hash.Write(cast.To[[]byte](data))
|
||||
return hash.Sum([]byte{})
|
||||
}
|
||||
|
||||
// DecodeInt 从数据解码为整数
|
||||
@ -209,9 +150,9 @@ func ExchangeInt(data any) []byte {
|
||||
return DefaultIntEncoder.ExchangeInt(data)
|
||||
}
|
||||
|
||||
// HashInt 对数据进行渐进式置换混淆
|
||||
func HashInt(buf []byte) []byte {
|
||||
return DefaultIntEncoder.HashInt(buf)
|
||||
// HashInt 对数据进行 HMAC-SHA512 哈希
|
||||
func HashInt(data any, key any) []byte {
|
||||
return DefaultIntEncoder.HashInt(data, key)
|
||||
}
|
||||
|
||||
// FillInt 使用默认编码器填充数据至指定长度
|
||||
|
||||
@ -42,18 +42,16 @@ func TestIntEncoderInstance(t *testing.T) {
|
||||
|
||||
// ExchangeInt
|
||||
buf = []byte("abcde")
|
||||
orig := make([]byte, len(buf))
|
||||
copy(orig, buf)
|
||||
exchanged := ExchangeInt(buf)
|
||||
if len(exchanged) != len(orig) {
|
||||
if len(exchanged) != len(buf) {
|
||||
t.Error("ExchangeInt len mismatch")
|
||||
}
|
||||
if bytes.Equal(exchanged, orig) {
|
||||
if bytes.Equal(exchanged, buf) {
|
||||
t.Error("ExchangeInt should change data")
|
||||
}
|
||||
|
||||
// HashInt
|
||||
hashed := HashInt(buf)
|
||||
hashed := HashInt(buf, []byte("key"))
|
||||
if len(hashed) == 0 {
|
||||
t.Error("HashInt failed")
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user