-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
244b979
commit 17dd135
Showing
10 changed files
with
414 additions
and
1 deletion.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* @nikolaydubina |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
name: test | ||
|
||
on: [push] | ||
|
||
permissions: read-all | ||
|
||
jobs: | ||
test: | ||
name: test | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: code | ||
uses: actions/checkout@v4 | ||
|
||
- name: go | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: ^1.22 | ||
|
||
- name: test | ||
run: | | ||
go install github.com/jstemmer/go-junit-report/v2@latest | ||
go test -coverprofile=coverage.out -covermode=atomic -cover -json -v ./... 2>&1 | go-junit-report -set-exit-code > tests.xml | ||
- name: fuzz | ||
run: go test -list . | grep Fuzz | xargs -P 8 -I {} go test -fuzz {} -fuzztime 15s . | ||
|
||
- name: Upload test results to Codecov | ||
uses: codecov/test-results-action@v1 | ||
with: | ||
token: ${{ secrets.CODECOV_TOKEN }} | ||
files: tests.xml | ||
|
||
- name: Upload coverage to Codecov | ||
uses: codecov/codecov-action@v4.1.1 | ||
with: | ||
token: ${{ secrets.CODECOV_TOKEN }} | ||
files: coverage.out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,15 @@ | ||
# base32quant | ||
fast `base32` encoding into `uint32` and `uint64` | ||
|
||
```bash | ||
$ go test -bench=. -benchmem . | ||
goos: darwin | ||
goarch: arm64 | ||
pkg: github.com/ndx-technologies/mm-go/base32 | ||
cpu: Apple M3 Max | ||
BenchmarkEncodeDecode_Standard/uint32-16 32816378 36.46 ns/op 16 B/op 2 allocs/op | ||
BenchmarkEncodeDecode_Standard/uint64-16 23218821 50.55 ns/op 24 B/op 2 allocs/op | ||
BenchmarkEncodeDecode/uint32-16 128822170 9.676 ns/op 0 B/op 0 allocs/op | ||
BenchmarkEncodeDecode/uint64-16 93620481 12.96 ns/op 0 B/op 0 allocs/op | ||
PASS | ||
ok github.com/ndx-technologies/mm-go/base32 7.052s | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package base32quant | ||
|
||
import "errors" | ||
|
||
var ( | ||
ErrInvalidLength = errors.New("invalid length") | ||
ErrInvalidCharacter = errors.New("invalid character") | ||
) | ||
|
||
var digits = [...]byte{ | ||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', | ||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', | ||
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', | ||
'y', 'z', '2', '3', '4', '5', '6', '7', | ||
} | ||
|
||
func Encode[T uint32 | uint64](b []byte, n T) error { | ||
for i := range len(b) { | ||
b[len(b)-i-1], n = digits[n&0x1f], n>>5 | ||
} | ||
return nil | ||
} | ||
|
||
func Decode[T uint32 | uint64](b []byte) (n T, err error) { | ||
for i := 0; i < len(b); i++ { | ||
n <<= 5 | ||
switch { | ||
case b[i] >= 'a' && b[i] <= 'z': | ||
n |= T(b[i] - 'a') | ||
case b[i] >= 'A' && b[i] <= 'Z': | ||
n |= T(b[i] - 'A') | ||
case b[i] >= '2' && b[i] <= '7': | ||
n |= T(b[i] - '2' + 26) | ||
default: | ||
return 0, ErrInvalidCharacter | ||
} | ||
} | ||
return n, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package base32quant_test | ||
|
||
import ( | ||
"encoding/base32" | ||
"encoding/binary" | ||
"fmt" | ||
"testing" | ||
|
||
base32quant "github.com/ndx-technologies/base32quant" | ||
) | ||
|
||
func Benchmark__________________________________(b *testing.B) { b.SkipNow() } | ||
|
||
func BenchmarkEncodeDecode_Standard(b *testing.B) { | ||
enc := base32.StdEncoding.WithPadding(base32.NoPadding) | ||
|
||
b.Run("uint32", func(b *testing.B) { | ||
v := make([]byte, 7) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
for j := range v { | ||
v[j] = 0 | ||
} | ||
|
||
enc.Encode(v, binary.BigEndian.AppendUint32(nil, uint32(i))) | ||
|
||
g := [4]byte{} | ||
_, err := enc.Decode(g[:], v) | ||
|
||
o := binary.BigEndian.Uint32(g[:]) | ||
|
||
if o != uint32(i) || err != nil { | ||
b.Fatalf("invalid value: %d %d %v, err: %s", o, i, v, err) | ||
} | ||
} | ||
}) | ||
|
||
b.Run("uint64", func(b *testing.B) { | ||
v := make([]byte, 13) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
for j := range v { | ||
v[j] = 0 | ||
} | ||
|
||
enc.Encode(v, binary.BigEndian.AppendUint64(nil, uint64(i))) | ||
|
||
g := [8]byte{} | ||
_, err := enc.Decode(g[:], v) | ||
|
||
o := binary.BigEndian.Uint64(g[:]) | ||
|
||
if o != uint64(i) || err != nil { | ||
b.Fatalf("invalid value: %d %d %v, err: %s", o, i, v, err) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
func BenchmarkEncodeDecode(b *testing.B) { | ||
b.Run("uint32", func(b *testing.B) { | ||
v := make([]byte, 7) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
for j := range v { | ||
v[j] = 0 | ||
} | ||
|
||
base32quant.Encode(v, uint32(i)) | ||
o, err := base32quant.Decode[uint32](v) | ||
|
||
if o != uint32(i) || err != nil { | ||
b.Fatalf("invalid value: %d %d %v, err: %s", o, i, v, err) | ||
} | ||
} | ||
}) | ||
|
||
b.Run("uint64", func(b *testing.B) { | ||
v := make([]byte, 13) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
for j := range v { | ||
v[j] = 0 | ||
} | ||
|
||
base32quant.Encode(v, uint64(i)) | ||
o, err := base32quant.Decode[uint64](v) | ||
|
||
if o != uint64(i) || err != nil { | ||
b.Fatalf("invalid value: %d %d %v, err: %s", o, i, v, err) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
func ExampleEncode() { | ||
var x uint32 = 1234567890 | ||
|
||
b := make([]byte, 7) | ||
base32quant.Encode(b, x) | ||
|
||
fmt.Println(string(b)) | ||
// Output: bezmaws | ||
} | ||
|
||
func ExampleDecode() { | ||
x, _ := base32quant.Decode[uint32]([]byte("bezmaws")) | ||
fmt.Println(x) | ||
// Output: 1234567890 | ||
} | ||
|
||
func ExampleDecode_mixedCase() { | ||
x, _ := base32quant.Decode[uint32]([]byte("beZmAwS")) | ||
fmt.Println(x) | ||
// Output: 1234567890 | ||
} | ||
|
||
func TestEncodeDecode(t *testing.T) { | ||
s := "-1" | ||
_, err := base32quant.Decode[uint32]([]byte(s)) | ||
if err == nil { | ||
t.Fatalf("must be error") | ||
} | ||
} | ||
|
||
func FuzzEncodeDecode_32(f *testing.F) { | ||
b := make([]byte, 7) | ||
|
||
f.Add(uint32(0)) | ||
f.Add(uint32(11)) | ||
f.Add(uint32(32123)) | ||
f.Add(uint32(222)) | ||
|
||
f.Fuzz(func(t *testing.T, v uint32) { | ||
base32quant.Encode(b, v) | ||
o, err := base32quant.Decode[uint32](b) | ||
|
||
if o != v || err != nil { | ||
t.Fatalf("invalid value: %d %d %v, err: %s", o, v, b, err) | ||
} | ||
}) | ||
} | ||
|
||
func FuzzEncodeDecode_64(f *testing.F) { | ||
b := make([]byte, 13) | ||
|
||
f.Add(uint64(0)) | ||
f.Add(uint64(11)) | ||
|
||
f.Fuzz(func(t *testing.T, v uint64) { | ||
base32quant.Encode(b, v) | ||
o, err := base32quant.Decode[uint64](b) | ||
|
||
if o != v || err != nil { | ||
t.Fatalf("invalid value: %d %d %v, err: %s", o, v, b, err) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/ndx-technologies/base32quant | ||
|
||
go 1.23.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package base32quant | ||
|
||
// UInt32 is a base32 encoded uint32 into 7 bytes. | ||
type UInt32 struct{ v uint32 } | ||
|
||
func NewUInt32(v uint32) UInt32 { return UInt32{v} } | ||
|
||
func (s UInt32) UInt32() uint32 { return s.v } | ||
|
||
func (s UInt32) MarshalText() ([]byte, error) { | ||
b := make([]byte, 7) | ||
Encode(b, s.v) | ||
return b, nil | ||
} | ||
|
||
func (s *UInt32) UnmarshalText(b []byte) (err error) { | ||
if len(b) != 7 { | ||
return ErrInvalidLength | ||
} | ||
s.v, err = Decode[uint32](b) | ||
return err | ||
} | ||
|
||
func NewUInt32FromString(s string) (v UInt32, err error) { | ||
err = (&v).UnmarshalText([]byte(s)) | ||
return v, err | ||
} | ||
|
||
func (s UInt32) String() string { | ||
b := make([]byte, 7) | ||
Encode(b, s.v) | ||
return string(b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package base32quant_test | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/ndx-technologies/base32quant" | ||
) | ||
|
||
func ExampleNewUInt32() { | ||
u := base32quant.NewUInt32(42) | ||
fmt.Println(u.UInt32()) | ||
// Output: 42 | ||
} | ||
|
||
func ExampleUInt32_MarshalText() { | ||
u := base32quant.NewUInt32(42) | ||
b, _ := u.MarshalText() | ||
fmt.Println(string(b)) | ||
// Output: aaaaabk | ||
} | ||
|
||
func ExampleUInt32_UnmarshalText() { | ||
var u base32quant.UInt32 | ||
u.UnmarshalText([]byte("aaaaabk")) | ||
fmt.Println(u.UInt32()) | ||
// Output: 42 | ||
} | ||
|
||
func ExampleUInt32_UnmarshalText_error() { | ||
var u base32quant.UInt32 | ||
fmt.Println(u.UnmarshalText([]byte("aaaaaaaaaabk"))) | ||
// Output: invalid length | ||
} | ||
|
||
func ExampleUInt32_String() { | ||
u := base32quant.NewUInt32(42) | ||
fmt.Println(u.String()) | ||
// Output: aaaaabk | ||
} | ||
|
||
func ExampleNewUInt32FromString() { | ||
u, _ := base32quant.NewUInt32FromString("aaaaabk") | ||
fmt.Println(u.UInt32()) | ||
// Output: 42 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package base32quant | ||
|
||
// UInt64 is a base32 encoded uint64 into 13 bytes. | ||
type UInt64 struct{ v uint64 } | ||
|
||
func NewUInt64(v uint64) UInt64 { return UInt64{v} } | ||
|
||
func (s UInt64) UInt64() uint64 { return s.v } | ||
|
||
func (s UInt64) MarshalText() ([]byte, error) { | ||
b := make([]byte, 13) | ||
Encode(b, s.v) | ||
return b, nil | ||
} | ||
|
||
func (s *UInt64) UnmarshalText(b []byte) (err error) { | ||
if len(b) != 13 { | ||
return ErrInvalidLength | ||
} | ||
s.v, err = Decode[uint64](b) | ||
return err | ||
} | ||
|
||
func NewUInt64FromString(s string) (v UInt64, err error) { | ||
err = (&v).UnmarshalText([]byte(s)) | ||
return v, err | ||
} | ||
|
||
func (s UInt64) String() string { | ||
b := make([]byte, 13) | ||
Encode(b, s.v) | ||
return string(b) | ||
} |
Oops, something went wrong.