feat: add Base64 Raw support and smart decoding upgrade

This commit is contained in:
AI Engineer 2026-05-06 01:11:39 +08:00
parent 5b4d5a8f3a
commit 47a62a3c16
4 changed files with 86 additions and 15 deletions

View File

@ -1,5 +1,13 @@
# Changelog: @go/encoding # Changelog: @go/encoding
## [v1.1.0] - 2026-05-06
### Added
- **Base64 无填充支持**:新增 `Base64Raw``Base64RawToString``UrlBase64Raw``UrlBase64RawToString` 接口,支持生成不带填充符(`=`)的编码。
### Changed
- **智能解码升级**:升级了 `UnBase64``UnUrlBase64` 系列函数,通过 O(1) 零分配检测自动兼容“带填充”与“无填充”的输入数据,无需额外调用 Raw 解码接口。
## [v1.0.6] - 2026-05-06 ## [v1.0.6] - 2026-05-06
### Changed ### Changed

View File

@ -18,9 +18,11 @@
- `func Hex(data []byte) []byte` / `func HexToString(data []byte) string` - `func Hex(data []byte) []byte` / `func HexToString(data []byte) string`
- `func UnHex(data []byte) ([]byte, error)` / `func UnHexFromString(data string) ([]byte, error)` - `func UnHex(data []byte) ([]byte, error)` / `func UnHexFromString(data string) ([]byte, error)`
- `func Base64(data []byte) []byte` / `func Base64ToString(data []byte) string` - `func Base64(data []byte) []byte` / `func Base64ToString(data []byte) string`
- `func UnBase64(data []byte) ([]byte, error)` / `func UnBase64FromString(data string) ([]byte, error)` - `func Base64Raw(data []byte) []byte` / `func Base64RawToString(data []byte) string` (无填充版本)
- `func UnBase64(data []byte) ([]byte, error)` / `func UnBase64FromString(data string) ([]byte, error)` (智能兼容填充与无填充)
- `func UrlBase64(data []byte) []byte` / `func UrlBase64ToString(data []byte) string` - `func UrlBase64(data []byte) []byte` / `func UrlBase64ToString(data []byte) string`
- `func UnUrlBase64(data []byte) ([]byte, error)` / `func UnUrlBase64FromString(data string) ([]byte, error)` - `func UrlBase64Raw(data []byte) []byte` / `func UrlBase64RawToString(data []byte) string` (无填充版本)
- `func UnUrlBase64(data []byte) ([]byte, error)` / `func UnUrlBase64FromString(data string) ([]byte, error)` (智能兼容填充与无填充)
### Web 编码 (URL/HTML) ### Web 编码 (URL/HTML)
- `func UrlEncode(data []byte) string` - `func UrlEncode(data []byte) string`

View File

@ -44,6 +44,18 @@ func Base64ToString(data []byte) string {
return base64.StdEncoding.EncodeToString(data) 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 编码的字节切片 // UrlBase64 将数据转换为 URL 安全的 Base64 编码的字节切片
func UrlBase64(data []byte) []byte { func UrlBase64(data []byte) []byte {
buf := make([]byte, base64.URLEncoding.EncodedLen(len(data))) buf := make([]byte, base64.URLEncoding.EncodedLen(len(data)))
@ -56,28 +68,56 @@ func UrlBase64ToString(data []byte) string {
return base64.URLEncoding.EncodeToString(data) return base64.URLEncoding.EncodeToString(data)
} }
// UnBase64 将 Base64 编码的字节切片解码 // 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) { func UnBase64(data []byte) ([]byte, error) {
if len(data) > 0 && data[len(data)-1] == '=' {
dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(data))) dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
n, err := base64.StdEncoding.Decode(dbuf, data) n, err := base64.StdEncoding.Decode(dbuf, data)
return dbuf[:n], err return dbuf[:n], err
} }
dbuf := make([]byte, base64.RawStdEncoding.DecodedLen(len(data)))
// UnBase64FromString 将 Base64 编码的字符串解码 n, err := base64.RawStdEncoding.Decode(dbuf, data)
func UnBase64FromString(data string) ([]byte, error) {
return base64.StdEncoding.DecodeString(data)
}
// UnUrlBase64 将 URL 安全的 Base64 编码的字节切片解码
func UnUrlBase64(data []byte) ([]byte, error) {
dbuf := make([]byte, base64.URLEncoding.DecodedLen(len(data)))
n, err := base64.URLEncoding.Decode(dbuf, data)
return dbuf[:n], err return dbuf[:n], err
} }
// UnUrlBase64FromString 将 URL 安全的 Base64 编码的字符串解码 // UnBase64FromString 将 Base64 编码的字符串解码(自动兼容有无填充)
func UnBase64FromString(data string) ([]byte, error) {
if len(data) > 0 && data[len(data)-1] == '=' {
return base64.StdEncoding.DecodeString(data)
}
return base64.RawStdEncoding.DecodeString(data)
}
// UnUrlBase64 将 URL 安全的 Base64 编码的字节切片解码(自动兼容有无填充)
func UnUrlBase64(data []byte) ([]byte, error) {
if len(data) > 0 && data[len(data)-1] == '=' {
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
}
// UnUrlBase64FromString 将 URL 安全的 Base64 编码的字符串解码(自动兼容有无填充)
func UnUrlBase64FromString(data string) ([]byte, error) { func UnUrlBase64FromString(data string) ([]byte, error) {
if len(data) > 0 && data[len(data)-1] == '=' {
return base64.URLEncoding.DecodeString(data) return base64.URLEncoding.DecodeString(data)
}
return base64.RawURLEncoding.DecodeString(data)
} }
// UrlEncode 对数据进行 URL 编码 // UrlEncode 对数据进行 URL 编码

View File

@ -41,16 +41,37 @@ func TestBase64(t *testing.T) {
t.Error("UnBase64FromString with cast.As failed") 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 // URL
uEnc := encoding.UrlBase64ToString([]byte("hello/world+")) uData := []byte("hello/world+")
uEnc := encoding.UrlBase64ToString(uData)
uDec, _ := encoding.UnUrlBase64([]byte(uEnc)) uDec, _ := encoding.UnUrlBase64([]byte(uEnc))
if !bytes.Equal([]byte("hello/world+"), uDec) { if !bytes.Equal(uData, uDec) {
t.Error("UrlBase64 roundtrip failed") t.Error("UrlBase64 roundtrip failed")
} }
if !bytes.Equal(cast.As(encoding.UnUrlBase64FromString(uEnc)), []byte("hello/world+")) { if !bytes.Equal(cast.As(encoding.UnUrlBase64FromString(uEnc)), uData) {
t.Error("UnUrlBase64FromString with cast.As failed") 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 --- // --- Web ---