Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,14 @@ func (e *Encoding) Encode(source []byte) string
```
Encode function receives a byte slice and encodes it to a string using the
alphabet provided

#### Variables

```go
var (
ErrAmbiguousAlphabet error
ErrNonBaseCharacter error
)
```
These errors are returned by `NewEncoding` and `Decode` respectively and can be
checked with `errors.Is`.
35 changes: 22 additions & 13 deletions basex.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ import (
"errors"
)

var (
// ErrAmbiguousAlphabet is returned when the alphabet contains duplicate characters.
ErrAmbiguousAlphabet = errors.New("Ambiguous alphabet.")
// ErrNonBaseCharacter is returned when Decode encounters a character that
// is not part of the encoding alphabet.
ErrNonBaseCharacter = errors.New("Non Base Character")
)

// Encoding is a custom base encoding defined by an alphabet.
// It should bre created using NewEncoding function
// It should be created using NewEncoding function.
type Encoding struct {
base int
alphabet []rune
Expand All @@ -29,7 +37,7 @@ func NewEncoding(alphabet string) (*Encoding, error) {

for i := 0; i < len(runes); i++ {
if _, ok := runeMap[runes[i]]; ok {
return nil, errors.New("Ambiguous alphabet.")
return nil, ErrAmbiguousAlphabet
}

runeMap[runes[i]] = i
Expand Down Expand Up @@ -66,6 +74,7 @@ func (e *Encoding) Encode(source []byte) string {
}

var res bytes.Buffer
res.Grow(len(digits))

for k := 0; source[k] == 0 && k < len(source)-1; k++ {
res.WriteRune(e.alphabet[0])
Expand All @@ -79,44 +88,44 @@ func (e *Encoding) Encode(source []byte) string {
}

// Decode function decodes a string previously obtained from Encode, using the same alphabet and returns a byte slice
// In case the input is not valid an arror will be returned
// In case the input is not valid an error will be returned.
func (e *Encoding) Decode(source string) ([]byte, error) {
if len(source) == 0 {
return []byte{}, nil
}

runes := []rune(source)

bytes := []byte{0}
out := []byte{0}
for i := 0; i < len(runes); i++ {
value, ok := e.alphabetMap[runes[i]]

if !ok {
return nil, errors.New("Non Base Character")
return nil, ErrNonBaseCharacter
}

carry := int(value)

for j := 0; j < len(bytes); j++ {
carry += int(bytes[j]) * e.base
bytes[j] = byte(carry & 0xff)
for j := 0; j < len(out); j++ {
carry += int(out[j]) * e.base
out[j] = byte(carry & 0xff)
carry >>= 8
}

for carry > 0 {
bytes = append(bytes, byte(carry&0xff))
out = append(out, byte(carry&0xff))
carry >>= 8
}
}

for k := 0; runes[k] == e.alphabet[0] && k < len(runes)-1; k++ {
bytes = append(bytes, 0)
out = append(out, 0)
}

// Reverse bytes
for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {
out[i], out[j] = out[j], out[i]
}

return bytes, nil
return out, nil
}
25 changes: 19 additions & 6 deletions basex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import (
h "encoding/hex"
"errors"
"testing"
)

Expand Down Expand Up @@ -122,7 +123,9 @@

func Test_AmbiguousAlphabet(t *testing.T) {
_, err := NewEncoding("01gh1")
expect(err.Error(), "Ambiguous alphabet.", t)
if !errors.Is(err, ErrAmbiguousAlphabet) {

Check failure on line 126 in basex_test.go

View workflow job for this annotation

GitHub Actions / Build

undefined: errors.Is
t.Fatalf("expected ErrAmbiguousAlphabet, got %v", err)
}
}

func Test_Encode(t *testing.T) {
Expand All @@ -146,15 +149,25 @@
func Test_NonDecodable(t *testing.T) {
enc, _ := NewEncoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
_, err := enc.Decode("invalid")
expect(err.Error(), "Non Base Character", t)
if !errors.Is(err, ErrNonBaseCharacter) {

Check failure on line 152 in basex_test.go

View workflow job for this annotation

GitHub Actions / Build

undefined: errors.Is
t.Fatalf("expected ErrNonBaseCharacter, got %v", err)
}
_, err = enc.Decode("c2F0b3NoaQo=")
expect(err.Error(), "Non Base Character", t)
if !errors.Is(err, ErrNonBaseCharacter) {

Check failure on line 156 in basex_test.go

View workflow job for this annotation

GitHub Actions / Build

undefined: errors.Is
t.Fatalf("expected ErrNonBaseCharacter, got %v", err)
}
_, err = enc.Decode(" 1111111111")
expect(err.Error(), "Non Base Character", t)
if !errors.Is(err, ErrNonBaseCharacter) {

Check failure on line 160 in basex_test.go

View workflow job for this annotation

GitHub Actions / Build

undefined: errors.Is
t.Fatalf("expected ErrNonBaseCharacter, got %v", err)
}
_, err = enc.Decode("1111111111 ")
expect(err.Error(), "Non Base Character", t)
if !errors.Is(err, ErrNonBaseCharacter) {

Check failure on line 164 in basex_test.go

View workflow job for this annotation

GitHub Actions / Build

undefined: errors.Is
t.Fatalf("expected ErrNonBaseCharacter, got %v", err)
}
_, err = enc.Decode(" \t\n\u000b\f\r skip \r\f\u000b\n\t a")
expect(err.Error(), "Non Base Character", t)
if !errors.Is(err, ErrNonBaseCharacter) {

Check failure on line 168 in basex_test.go

View workflow job for this annotation

GitHub Actions / Build

undefined: errors.Is
t.Fatalf("expected ErrNonBaseCharacter, got %v", err)
}
}

func hex(in string) []byte {
Expand Down
Loading