Compare commits

..

No commits in common. "main" and "v1.3.2" have entirely different histories.
main ... v1.3.2

8 changed files with 329 additions and 241 deletions

View File

@ -1,12 +1,5 @@
# Changelog: @go/encoding # Changelog: @go/encoding
## v1.5.2 (2026-06-08)
- **回滚与修复**: 修正了 `v1.5.1` 中架构设计的失误,将 Go 核心层的 `EncodeInt``IntEncoder.EncodeInt` 返回值从 `string` 恢复为 `[]byte`,彻底消除由于频繁强制转换导致的内存逃逸,恢复了高频并发场景下的零拷贝性能基准。
- **JS 智能桥接**: 通过在 `js_export.go` 的注册阶段采用匿名闭包函数 `func(u uint64) string`,完美兼顾了底层的高效性和 JS 环境面向字符串开发的友好体验。
## v1.5.1 (2026-06-08)
- **JS 对齐**: 将所有注册到 `jsmod` 的导出方法名统一为 PascalCase并将缩写名称如 URL, HTML, UTF8全大写以消除 JS 与 Go 调用体感上的摩擦。合并了冗余的 `xxxToString` 方法。
## [v1.3.2] - 2026-05-30 ## [v1.3.2] - 2026-05-30
### Added ### Added

View File

@ -12,87 +12,145 @@ import (
"apigo.cc/go/cast" "apigo.cc/go/cast"
) )
// Hex 将数据转换为 Hex 编码的字符串 // Hex 将数据转换为 Hex 编码的字节切片
func Hex(data any) string { func Hex(data []byte) []byte {
b := cast.To[[]byte](data) dst := make([]byte, hex.EncodedLen(len(data)))
return hex.EncodeToString(b) hex.Encode(dst, data)
return dst
} }
// UnHex 将 Hex 编码的数据解码为字节切片 // HexToString 将数据转换为 Hex 编码的字符串
func UnHex(data any) ([]byte, error) { func HexToString(data []byte) string {
s := cast.String(data) return hex.EncodeToString(data)
return hex.DecodeString(s)
} }
// Base64 将数据转换为 Base64 编码的字符串 // UnHex 将 Hex 编码的字节切片解码
func Base64(data any) string { func UnHex(data []byte) ([]byte, error) {
b := cast.To[[]byte](data) dst := make([]byte, hex.DecodedLen(len(data)))
return base64.StdEncoding.EncodeToString(b) n, err := hex.Decode(dst, data)
return dst[:n], err
} }
// UnBase64 将 Base64 编码的数据解码为字节切片(自动兼容有无填充) // UnHexFromString 将 Hex 编码的字符串解码
func UnBase64(data any) ([]byte, error) { func UnHexFromString(data string) ([]byte, error) {
s := cast.String(data) return hex.DecodeString(data)
if len(s) > 0 && s[len(s)-1] == '=' { }
return base64.StdEncoding.DecodeString(s)
// Base64 将数据转换为 Base64 编码的字节切片
func Base64(data []byte) []byte {
buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(buf, data)
return buf
}
// Base64ToString 将数据转换为 Base64 编码的字符串
func Base64ToString(data []byte) string {
return base64.StdEncoding.EncodeToString(data)
}
// Base64Raw 将数据转换为无填充的 Base64 编码的字节切片
func Base64Raw(data []byte) []byte {
buf := make([]byte, base64.RawStdEncoding.EncodedLen(len(data)))
base64.RawStdEncoding.Encode(buf, data)
return buf
}
// Base64RawToString 将数据转换为无填充的 Base64 编码的字符串
func Base64RawToString(data []byte) string {
return base64.RawStdEncoding.EncodeToString(data)
}
// UrlBase64 将数据转换为 URL 安全的 Base64 编码的字节切片
func UrlBase64(data []byte) []byte {
buf := make([]byte, base64.URLEncoding.EncodedLen(len(data)))
base64.URLEncoding.Encode(buf, data)
return buf
}
// UrlBase64ToString 将数据转换为 URL 安全的 Base64 编码的字符串
func UrlBase64ToString(data []byte) string {
return base64.URLEncoding.EncodeToString(data)
}
// UrlBase64Raw 将数据转换为 URL 安全且无填充的 Base64 编码的字节切片
func UrlBase64Raw(data []byte) []byte {
buf := make([]byte, base64.RawURLEncoding.EncodedLen(len(data)))
base64.RawURLEncoding.Encode(buf, data)
return buf
}
// UrlBase64RawToString 将数据转换为 URL 安全且无填充的 Base64 编码的字符串
func UrlBase64RawToString(data []byte) string {
return base64.RawURLEncoding.EncodeToString(data)
}
// UnBase64 将 Base64 编码的字节切片解码(自动兼容有无填充)
func UnBase64(data []byte) ([]byte, error) {
if len(data) > 0 && data[len(data)-1] == '=' {
dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
n, err := base64.StdEncoding.Decode(dbuf, data)
return dbuf[:n], err
} }
return base64.RawStdEncoding.DecodeString(s) dbuf := make([]byte, base64.RawStdEncoding.DecodedLen(len(data)))
n, err := base64.RawStdEncoding.Decode(dbuf, data)
return dbuf[:n], err
} }
// Base64Raw 将数据转换为无填充的 Base64 编码的字符串 // UnBase64FromString 将 Base64 编码的字符串解码(自动兼容有无填充)
func Base64Raw(data any) string { func UnBase64FromString(data string) ([]byte, error) {
b := cast.To[[]byte](data) if len(data) > 0 && data[len(data)-1] == '=' {
return base64.RawStdEncoding.EncodeToString(b) return base64.StdEncoding.DecodeString(data)
}
// URLBase64 将数据转换为 URL 安全的 Base64 编码的字符串
func URLBase64(data any) string {
b := cast.To[[]byte](data)
return base64.URLEncoding.EncodeToString(b)
}
// UnURLBase64 将 URL 安全的 Base64 编码的数据解码为字节切片(自动兼容有无填充)
func UnURLBase64(data any) ([]byte, error) {
s := cast.String(data)
if len(s) > 0 && s[len(s)-1] == '=' {
return base64.URLEncoding.DecodeString(s)
} }
return base64.RawURLEncoding.DecodeString(s) return base64.RawStdEncoding.DecodeString(data)
} }
// URLBase64Raw 将数据转换为 URL 安全且无填充的 Base64 编码的字符串 // UnUrlBase64 将 URL 安全的 Base64 编码的字节切片解码(自动兼容有无填充)
func URLBase64Raw(data any) string { func UnUrlBase64(data []byte) ([]byte, error) {
b := cast.To[[]byte](data) if len(data) > 0 && data[len(data)-1] == '=' {
return base64.RawURLEncoding.EncodeToString(b) dbuf := make([]byte, base64.URLEncoding.DecodedLen(len(data)))
n, err := base64.URLEncoding.Decode(dbuf, data)
return dbuf[:n], err
}
dbuf := make([]byte, base64.RawURLEncoding.DecodedLen(len(data)))
n, err := base64.RawURLEncoding.Decode(dbuf, data)
return dbuf[:n], err
} }
// URLEncode 对数据进行 URL 编码 // UnUrlBase64FromString 将 URL 安全的 Base64 编码的字符串解码(自动兼容有无填充)
func URLEncode(data any) string { func UnUrlBase64FromString(data string) ([]byte, error) {
return url.QueryEscape(cast.String(data)) if len(data) > 0 && data[len(data)-1] == '=' {
return base64.URLEncoding.DecodeString(data)
}
return base64.RawURLEncoding.DecodeString(data)
} }
// UnURLEncode 对字符串进行 URL 解码 // UrlEncode 对数据进行 URL 编码
func UnURLEncode(data any) ([]byte, error) { func UrlEncode(data []byte) string {
res, err := url.QueryUnescape(cast.String(data)) return url.QueryEscape(string(data))
}
// UnUrlEncode 对字符串进行 URL 解码
func UnUrlEncode(data string) ([]byte, error) {
res, err := url.QueryUnescape(data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []byte(res), nil return []byte(res), nil
} }
// HTMLEscape 对数据进行 HTML 转义 // HtmlEscape 对数据进行 HTML 转义
func HTMLEscape(data any) string { func HtmlEscape(data []byte) string {
return html.EscapeString(cast.String(data)) return html.EscapeString(string(data))
} }
// HTMLUnescape 对 HTML 字符串进行反转义 // HtmlUnescape 对 HTML 字符串进行反转义
func HTMLUnescape(data any) string { func HtmlUnescape(data string) string {
return html.UnescapeString(cast.String(data)) return html.UnescapeString(data)
} }
// UTF8Valid 检查数据是否为有效的 UTF-8 编码 // Utf8Valid 检查字节切片是否为有效的 UTF-8 编码
func UTF8Valid(data any) bool { func Utf8Valid(data []byte) bool {
return utf8.Valid(cast.To[[]byte](data)) return utf8.Valid(data)
} }
// SortJoin 将 Map 或 Struct 转换为排序并拼接后的字符串 (常用于签名) // SortJoin 将 Map 或 Struct 转换为排序并拼接后的字符串 (常用于签名)

View File

@ -1,96 +1,135 @@
package encoding package encoding_test
import ( import (
"bytes" "bytes"
"testing" "testing"
"apigo.cc/go/cast"
"apigo.cc/go/encoding"
) )
func TestEncoding(t *testing.T) { // --- Hex ---
data := []byte("hello world") func TestHex(t *testing.T) {
data := []byte("hello go")
// Hex encoded := encoding.HexToString(data)
encoded := Hex(data) decoded, err := encoding.UnHex([]byte(encoded))
decoded, err := UnHex(encoded) if err != nil || !bytes.Equal(data, decoded) {
if err != nil || !bytes.Equal(decoded, data) { t.Fatal("Hex roundtrip failed")
t.Errorf("Hex failed: got %v, error: %v", decoded, err)
} }
// Base64 if !bytes.Equal(cast.As(encoding.UnHexFromString(encoded)), data) {
enc := Base64(data) t.Error("UnHexFromString with cast.As failed")
dec, err := UnBase64(enc)
if err != nil || !bytes.Equal(dec, data) {
t.Errorf("Base64 failed: got %v, error: %v", dec, err)
} }
// Base64Raw if len(cast.As(encoding.UnHexFromString("!@#$"))) != 0 {
rawEnc := Base64Raw(data) t.Error("UnHexFromString should return empty for invalid hex chars with cast.As")
rawDec, err := UnBase64(rawEnc)
if err != nil || !bytes.Equal(rawDec, data) {
t.Errorf("Base64Raw failed: got %v, error: %v", rawDec, err)
}
// URLBase64
uData := []byte("https://apigo.cc?a=1&b=2")
uEnc := URLBase64(uData)
uDec, _ := UnURLBase64(uEnc)
if !bytes.Equal(uDec, uData) {
t.Errorf("URLBase64 failed: got %v", uDec)
}
// URLBase64Raw
uRawEnc := URLBase64Raw(uData)
uRawDec, err := UnURLBase64(uRawEnc)
if err != nil || !bytes.Equal(uRawDec, uData) {
t.Errorf("URLBase64Raw failed: got %v, error: %v", uRawDec, err)
}
// URLEncode
urlEnc := URLEncode(data)
urlDec, err := UnURLEncode(urlEnc)
if err != nil || !bytes.Equal(urlDec, data) {
t.Errorf("URLEncode failed: got %v, error: %v", urlDec, err)
}
// HTMLEscape
htmlData := "<div>hello</div>"
escaped := HTMLEscape(htmlData)
if HTMLUnescape(escaped) != htmlData {
t.Errorf("HTMLEscape failed")
}
// UTF8Valid
if !UTF8Valid("你好") {
t.Error("UTF8Valid failed")
} }
} }
// --- Base64 ---
func TestBase64(t *testing.T) {
data := []byte("hello world")
// Standard
enc := encoding.Base64ToString(data)
dec, err := encoding.UnBase64([]byte(enc))
if err != nil || !bytes.Equal(data, dec) {
t.Error("Base64 roundtrip failed")
}
if !bytes.Equal(cast.As(encoding.UnBase64FromString(enc)), data) {
t.Error("UnBase64FromString with cast.As failed")
}
// Raw (Unpadded)
rawEnc := encoding.Base64RawToString(data)
if bytes.HasSuffix([]byte(rawEnc), []byte("=")) {
t.Error("Base64Raw should not have padding")
}
rawDec, err := encoding.UnBase64FromString(rawEnc)
if err != nil || !bytes.Equal(data, rawDec) {
t.Error("Base64Raw smart decoding failed")
}
// URL
uData := []byte("hello/world+")
uEnc := encoding.UrlBase64ToString(uData)
uDec, _ := encoding.UnUrlBase64([]byte(uEnc))
if !bytes.Equal(uData, uDec) {
t.Error("UrlBase64 roundtrip failed")
}
if !bytes.Equal(cast.As(encoding.UnUrlBase64FromString(uEnc)), uData) {
t.Error("UnUrlBase64FromString with cast.As failed")
}
// URL Raw (Unpadded)
uRawEnc := encoding.UrlBase64RawToString(uData)
if bytes.HasSuffix([]byte(uRawEnc), []byte("=")) {
t.Error("UrlBase64Raw should not have padding")
}
uRawDec, err := encoding.UnUrlBase64FromString(uRawEnc)
if err != nil || !bytes.Equal(uData, uRawDec) {
t.Error("UrlBase64Raw smart decoding failed")
}
}
// --- Web ---
func TestWebEncoding(t *testing.T) {
// URL
data := []byte("a b+c")
enc := encoding.UrlEncode(data)
if !bytes.Equal(cast.As(encoding.UnUrlEncode(enc)), data) {
t.Error("UrlEncode roundtrip failed")
}
if len(cast.As(encoding.UnUrlEncode("%ZZ"))) != 0 {
t.Error("UnUrlEncode should return empty for invalid input with cast.As")
}
// HTML
htmlData := []byte("<script>")
escaped := encoding.HtmlEscape(htmlData)
if encoding.HtmlUnescape(escaped) != string(htmlData) {
t.Error("HtmlEscape roundtrip failed")
}
}
// --- Utf8 ---
func TestUtf8(t *testing.T) {
if !encoding.Utf8Valid([]byte("你好")) {
t.Error("Valid UTF-8 should pass")
}
if encoding.Utf8Valid([]byte{0xff, 0xff}) {
t.Error("Invalid UTF-8 should fail")
}
}
// --- SortJoin ---
func TestSortJoin(t *testing.T) { func TestSortJoin(t *testing.T) {
m := map[string]any{ m := map[string]any{
"b": 2, "B": 2,
"a": 1, "A": 1,
"c": "3", "C": "hello world",
}
res := SortJoin(m, "&", "=", true)
if res != "a=1&b=2&c=3" {
t.Errorf("SortJoin map failed: %s", res)
} }
// Test Map
res := encoding.SortJoin(m, "&", "=", true)
expected := "A=1&B=2&C=hello+world"
if res != expected {
t.Errorf("SortJoin Map failed: expected %s, got %s", expected, res)
}
// Test Struct
type User struct { type User struct {
ID int Name string `json:"name"`
Name string Age int `json:"age"`
ID string
} }
u := User{ID: 1, Name: "sam"} u := User{Name: "Alice", Age: 30, ID: "123"}
resStruct := SortJoin(u, ";", ":", false) resStruct := encoding.SortJoin(u, ";", ":", false)
if resStruct != "ID:1;name:sam" { // Keys: age, name, ID. Sorted: ID, age, name
t.Errorf("SortJoin struct failed: %s", resStruct) expectedStruct := "ID:123;age:30;name:Alice"
} if resStruct != expectedStruct {
} t.Errorf("SortJoin Struct failed: expected %s, got %s", expectedStruct, resStruct)
func TestIntEncoder(t *testing.T) {
val := uint64(123456789)
encoded := EncodeInt(val)
decoded := DecodeInt(encoded)
if val != decoded {
t.Errorf("IntEncoder failed: expected %d, got %d", val, decoded)
} }
} }

4
go.mod
View File

@ -2,6 +2,6 @@ module apigo.cc/go/encoding
go 1.25.0 go 1.25.0
require apigo.cc/go/cast v1.5.0 require apigo.cc/go/cast v1.3.3
require apigo.cc/go/jsmod v1.5.0 require apigo.cc/go/jsmod v1.0.0

8
go.sum
View File

@ -1,4 +1,4 @@
apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE= apigo.cc/go/cast v1.3.3 h1:aln5eDR5DZVWVzZ/y5SJh1gQNgWv2sT82I25NaO9g34=
apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8= apigo.cc/go/cast v1.3.3/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk=
apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec= apigo.cc/go/jsmod v1.0.0 h1:lVQMq0tCno4kbHlQ3j5wzsm+v24J+bznIoHxpton0pE=
apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw= apigo.cc/go/jsmod v1.0.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=

View File

@ -4,8 +4,6 @@ import (
"crypto/hmac" "crypto/hmac"
"crypto/sha512" "crypto/sha512"
"errors" "errors"
"apigo.cc/go/cast"
) )
// IntEncoder 提供整数与字节切片之间的自定义进制转换 // IntEncoder 提供整数与字节切片之间的自定义进制转换
@ -35,9 +33,8 @@ func (enc *IntEncoder) AppendInt(buf []byte, u uint64) []byte {
return buf return buf
} }
// FillInt 使用循环字符序列填充数据至指定长度 // FillInt 使用循环字符序列填充字节切片至指定长度
func (enc *IntEncoder) FillInt(data any, length int) []byte { func (enc *IntEncoder) FillInt(buf []byte, length int) []byte {
buf := cast.To[[]byte](data)
currLen := len(buf) currLen := len(buf)
if currLen >= length { if currLen >= length {
return buf return buf
@ -55,9 +52,8 @@ func (enc *IntEncoder) FillInt(data any, length int) []byte {
return buf return buf
} }
// ExchangeInt 对数据进行位置交替重排 // ExchangeInt 对字节切片进行位置交替重排
func (enc *IntEncoder) ExchangeInt(data any) []byte { func (enc *IntEncoder) ExchangeInt(buf []byte) []byte {
buf := cast.To[[]byte](data)
size := len(buf) size := len(buf)
if size <= 1 { if size <= 1 {
return buf return buf
@ -80,16 +76,15 @@ func (enc *IntEncoder) ExchangeInt(data any) []byte {
return buf2 return buf2
} }
// HashInt 对数据进行 HMAC-SHA512 哈希 // HashInt 对字节切片进行 HMAC-SHA512 哈希
func (enc *IntEncoder) HashInt(data any, key any) []byte { func (enc *IntEncoder) HashInt(data []byte, key []byte) []byte {
hash := hmac.New(sha512.New, cast.To[[]byte](key)) hash := hmac.New(sha512.New, key)
hash.Write(cast.To[[]byte](data)) hash.Write(data)
return hash.Sum([]byte{}) return hash.Sum([]byte{})
} }
// DecodeInt 从数据解码为整数 // DecodeInt 从字节切片解码为整数
func (enc *IntEncoder) DecodeInt(data any) uint64 { func (enc *IntEncoder) DecodeInt(buf []byte) uint64 {
buf := cast.To[[]byte](data)
radix := uint64(enc.radix) radix := uint64(enc.radix)
if buf == nil { if buf == nil {
return 0 return 0
@ -132,7 +127,7 @@ var OrderedIntEncoder, _ = NewIntEncoder("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZab
// EncodeInt 使用默认编码器将整数转换为字节切片 // EncodeInt 使用默认编码器将整数转换为字节切片
func EncodeInt(u uint64) []byte { func EncodeInt(u uint64) []byte {
return DefaultIntEncoder.EncodeInt(u) return DefaultIntEncoder.AppendInt(nil, u)
} }
// AppendInt 使用默认编码器将整数追加到已有字节切片中 // AppendInt 使用默认编码器将整数追加到已有字节切片中
@ -140,22 +135,19 @@ func AppendInt(buf []byte, u uint64) []byte {
return DefaultIntEncoder.AppendInt(buf, u) return DefaultIntEncoder.AppendInt(buf, u)
} }
// DecodeInt 使用默认编码器从数据解码为整数 // DecodeInt 使用默认编码器从字节切片解码为整数
func DecodeInt(data any) uint64 { func DecodeInt(buf []byte) uint64 {
return DefaultIntEncoder.DecodeInt(data) return DefaultIntEncoder.DecodeInt(buf)
} }
// ExchangeInt 使用默认编码器对数据进行位置交替重排 func ExchangeInt(buf []byte) []byte {
func ExchangeInt(data any) []byte { return DefaultIntEncoder.ExchangeInt(buf)
return DefaultIntEncoder.ExchangeInt(data)
} }
// HashInt 对数据进行 HMAC-SHA512 哈希 func HashInt(data []byte, key []byte) []byte {
func HashInt(data any, key any) []byte {
return DefaultIntEncoder.HashInt(data, key) return DefaultIntEncoder.HashInt(data, key)
} }
// FillInt 使用默认编码器填充数据至指定长度 func FillInt(buf []byte, length int) []byte {
func FillInt(data any, length int) []byte { return DefaultIntEncoder.FillInt(buf, length)
return DefaultIntEncoder.FillInt(data, length)
} }

View File

@ -1,58 +1,58 @@
package encoding package encoding_test
import ( import (
"bytes" "bytes"
"testing" "testing"
"apigo.cc/go/encoding"
) )
func TestIntEncoderInstance(t *testing.T) { // --- 正常逻辑测试 ---
enc := DefaultIntEncoder func TestIntEncoderNormal(t *testing.T) {
val := uint64(123456789) enc := encoding.DefaultIntEncoder
encoded := enc.EncodeInt(val) nums := []uint64{0, 1, 100, 123456789, 18446744073709551615}
decoded := enc.DecodeInt(encoded) for _, n := range nums {
if val != decoded { if enc.DecodeInt(enc.EncodeInt(n)) != n {
t.Errorf("IntEncoder instance failed: expected %d, got %d", val, decoded) t.Errorf("Roundtrip failed for %d", n)
} }
}
// NewIntEncoder errors }
_, err := NewIntEncoder("12", 5) // 字符集不足
if err == nil { // --- 边界与异常测试 ---
t.Error("expected error for insufficient digits") func TestIntEncoderEdge(t *testing.T) {
} // 初始化异常
_, err := encoding.NewIntEncoder("12", 5) // 字符集不足
_, err = NewIntEncoder("112345", 6) // 重复字符 if err == nil { t.Error("NewIntEncoder failed to detect short digits") }
if err == nil {
t.Error("expected error for repeated digits") _, err = encoding.NewIntEncoder("112345", 2) // 重复字符
} if err == nil { t.Error("NewIntEncoder failed to detect repeated digits") }
// DecodeInt(nil) // 空输入
if DecodeInt(nil) != 0 { if encoding.DecodeInt(nil) != 0 { t.Error("DecodeInt(nil) != 0") }
t.Error("DecodeInt(nil) != 0")
} // 填充逻辑边界
buf := encoding.EncodeInt(123)
// FillInt filled := encoding.DefaultIntEncoder.FillInt(buf, 2) // 长度小于原长
buf := []byte(EncodeInt(123)) if len(filled) != len(buf) {
filled := FillInt(buf, 10) t.Error("FillInt should not truncate data")
if len(filled) != 10 { }
t.Errorf("FillInt failed: expected len 10, got %d", len(filled)) }
}
if !bytes.HasPrefix(filled, buf) { // --- 混淆与哈希测试 ---
t.Error("FillInt prefix mismatch") func TestIntEncoderAdvanced(t *testing.T) {
} enc := encoding.DefaultIntEncoder
buf := enc.EncodeInt(12345)
// ExchangeInt
buf = []byte("abcde") // Exchange
exchanged := ExchangeInt(buf) exchanged := enc.ExchangeInt(buf)
if len(exchanged) != len(buf) { if len(exchanged) != len(buf) {
t.Error("ExchangeInt len mismatch") t.Fatal("Exchange length mismatch")
} }
if bytes.Equal(exchanged, buf) {
t.Error("ExchangeInt should change data") // Hash
} h1 := enc.HashInt(buf, []byte("key"))
h2 := enc.HashInt(buf, []byte("key"))
// HashInt if !bytes.Equal(h1, h2) {
hashed := HashInt(buf, []byte("key")) t.Error("HashInt non-deterministic")
if len(hashed) == 0 {
t.Error("HashInt failed")
} }
} }

View File

@ -4,26 +4,32 @@ import "apigo.cc/go/jsmod"
func init() { func init() {
jsmod.Register("encoding", map[string]any{ jsmod.Register("encoding", map[string]any{
"Base64": Base64, "base64": Base64,
"Base64Raw": Base64Raw, "base64ToString": Base64ToString,
"UnBase64": UnBase64, "base64Raw": Base64Raw,
"URLBase64": URLBase64, "base64RawToString": Base64RawToString,
"URLBase64Raw": URLBase64Raw, "unBase64": UnBase64,
"UnURLBase64": UnURLBase64, "unBase64FromString": UnBase64FromString,
"Hex": Hex, "urlBase64": UrlBase64,
"UnHex": UnHex, "urlBase64ToString": UrlBase64ToString,
"URLEncode": URLEncode, "urlBase64Raw": UrlBase64Raw,
"UnURLEncode": UnURLEncode, "urlBase64RawToString": UrlBase64RawToString,
"HTMLEscape": HTMLEscape, "unUrlBase64": UnUrlBase64,
"HTMLUnescape": HTMLUnescape, "unUrlBase64FromString": UnUrlBase64FromString,
"UTF8Valid": UTF8Valid, "hex": Hex,
"SortJoin": SortJoin, "hexToString": HexToString,
"EncodeInt": func(u uint64) string { "unHex": UnHex,
return string(EncodeInt(u)) "unHexFromString": UnHexFromString,
}, "urlEncode": UrlEncode,
"DecodeInt": DecodeInt, "unUrlEncode": UnUrlEncode,
"FillInt": FillInt, "htmlEscape": HtmlEscape,
"ExchangeInt": ExchangeInt, "htmlUnescape": HtmlUnescape,
"HashInt": HashInt, "utf8Valid": Utf8Valid,
"sortJoin": SortJoin,
"encodeInt": EncodeInt,
"decodeInt": DecodeInt,
"fillInt": FillInt,
"exchangeInt": ExchangeInt,
"hashInt": HashInt,
}) })
} }