From a10494da6ece493050dab9c7d1fd8b6b64e62be7 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sun, 26 Oct 2014 04:10:58 -0400 Subject: [PATCH] generic base256 encoder --- base256/README.md | 14 +++++ base256/base256.go | 116 ++++++++++++++++++++++++++++++++++++++++ base256/base256_test.go | 71 ++++++++++++++++++++++++ base256/example_test.go | 26 +++++++++ braille/README.md | 5 -- braille/braille.go | 89 ------------------------------ braille/braille_test.go | 100 ---------------------------------- 7 files changed, 227 insertions(+), 194 deletions(-) create mode 100644 base256/README.md create mode 100644 base256/base256.go create mode 100644 base256/base256_test.go create mode 100644 base256/example_test.go delete mode 100644 braille/README.md delete mode 100644 braille/braille.go delete mode 100644 braille/braille_test.go diff --git a/base256/README.md b/base256/README.md new file mode 100644 index 0000000..69089b4 --- /dev/null +++ b/base256/README.md @@ -0,0 +1,14 @@ +Base256 encoding using UTF-8, configured by specifiying the +starting rune for 0x00. + +Only tested with U+0800-U+FFFF. + +### sha256 digest in hex +`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` + +### sha256 digest in braille (U+2800) +`⣣⢰⣄⡂⢘⣼⠜⠔⢚⣻⣴⣈⢙⡯⢹⠤⠧⢮⡁⣤⡤⢛⢓⡌⢤⢕⢙⠛⡸⡒⢸⡕` + +### sha256 digest in "Supplemental Arrows-B" (U+2900) +`⧣⦰⧄⥂⦘⧼⤜⤔⦚⧻⧴⧈⦙⥯⦹⤤⤧⦮⥁⧤⥤⦛⦓⥌⦤⦕⦙⤛⥸⥒⦸⥕` + diff --git a/base256/base256.go b/base256/base256.go new file mode 100644 index 0000000..61261a7 --- /dev/null +++ b/base256/base256.go @@ -0,0 +1,116 @@ +// Copyright 2014 Emery Hemingway. All rights reserved. +// Use of this source code is governed by the GPLv3+. + +// Package base256 implements radix 256 +// encoding and decoding using UTF-8. +package base256 + +import ( + "errors" + "fmt" + "unicode/utf8" +) + +type Encoding []byte + +var InvalidEncoding = errors.New("Invalid Encoding") + +type InvalidZero rune + +func (e InvalidZero) Error() string { + return fmt.Sprintf("base256: invalid zero rune %q", rune(e)) +} + +// NewEncoding generates a new Encoding using r as 0x00. +func NewEncoding(r rune) (Encoding, error) { + if !utf8.ValidRune(r) { + return nil, InvalidEncoding + } + + b := make([]byte, utf8.UTFMax) + l := utf8.EncodeRune(b, r) + b = b[:l] + if b[2] != 0x80 { + return nil, InvalidZero(r) + } + return b, nil +} + +// EncodedLen returns the length of an encoding of n source bytes. +func (e Encoding) EncodedLen(n int) int { return n * len(e) } + +// Encode encodes src into EncodedLen(len(src)) +// bytes of dst. As a convenience, it returns the number +// of bytes written to dst, but this value is always +// EncodedLen(len(src)). +// Encode implements Base256 encoding. +func (e Encoding) Encode(dst, src []byte) (n int) { + var i int + for i = 0; i < len(dst); i += len(e) { + copy(dst[i:], e) + } + + var j = len(e) - 2 + var k = len(e) - 1 + for i = 0; i < len(src); i++ { + dst[j] |= (src[i] >> 6) + dst[k] |= (src[i]) & 0x3F + j += len(e) + k += len(e) + } + return len(src) * 3 +} + +// InvalidRuneError values describe errors resulting from an invalid byte in a Base256 string. +type InvalidRuneError rune + +func (e InvalidRuneError) Error() string { + return fmt.Sprintf("base256: invalid rune: %#U", rune(e)) +} + +var ErrLength = errors.New("base256: bad length") + +func (e Encoding) DecodedLen(x int) int { return x / len(e) } + +// Decode has weak error checking, use utf8.Valid before +// decoding if corruption is a concern. +func (e Encoding) Decode(dst, src []byte) (int, error) { + if len(dst) < len(src)/3 { + return 0, ErrLength + } + + var i int + for i = 0; i < len(src); i += 3 { + if src[i] != 0xE2 { + r, _ := utf8.DecodeRune(src[i:]) + + return 0, InvalidRuneError(r) + } + } + var j = 1 + var k = 2 + for i = 0; i < len(dst); i++ { + dst[i] = (src[j] << 6) | (src[k] ^ 0x80) + j += 3 + k += 3 + } + return len(src) / 3, nil +} + +// EncodeToString returns the bralle encoding of src. +func (e Encoding) EncodeToString(src []byte) string { + dst := make([]byte, e.EncodedLen(len(src))) + e.Encode(dst, src) + return string(dst) +} + +// DecodeString returns the bytes represented by the base256 string s. +func (e Encoding) DecodeString(s string) ([]byte, error) { + src := []byte(s) + dst := make([]byte, e.DecodedLen(len(src))) + _, err := e.Decode(dst, src) + if err != nil { + return nil, err + } + return dst, nil +} diff --git a/base256/base256_test.go b/base256/base256_test.go new file mode 100644 index 0000000..9391bc3 --- /dev/null +++ b/base256/base256_test.go @@ -0,0 +1,71 @@ +package base256 + +import "testing" + +func newEncT(t *testing.T) Encoding { + e, err := NewEncoding('⤀') + if err != nil { + t.Fatal(err) + } + return e +} + +func newEncB(t *testing.B) Encoding { + e, err := NewEncoding('⤀') + if err != nil { + t.Fatal(err) + } + return e +} + +func TestIterative(t *testing.T) { + e := newEncT(t) + + var in, out byte + enc := make([]byte, e.EncodedLen(1)) + dec := make([]byte, 1) + for in = 0; in < 0xFF; in++ { + dec[0] = in + e.Encode(enc, dec) + e.Decode(dec, enc) + out = dec[0] + + if in != out { + t.Errorf("Wanted %08b, got %08b", in, out) + } + } +} + +func BenchmarkEncode(b *testing.B) { + e := newEncB(b) + + dec := make([]byte, 255) + enc := make([]byte, e.EncodedLen(255)) + + var c byte + for c = 0; c < 255; c++ { + dec[c] = c + } + for i := 0; i < b.N; i++ { + e.Encode(enc, dec) + b.SetBytes(255) + } +} + +func BenchmarkDecode(b *testing.B) { + e := newEncB(b) + + dec := make([]byte, 255) + enc := make([]byte, e.EncodedLen(255)) + + var c byte + for c = 0; c < 255; c++ { + dec[c] = c + } + e.Encode(enc, dec) + + for i := 0; i < b.N; i++ { + e.Decode(dec, enc) + b.SetBytes(255) + } +} diff --git a/base256/example_test.go b/base256/example_test.go new file mode 100644 index 0000000..2b051ca --- /dev/null +++ b/base256/example_test.go @@ -0,0 +1,26 @@ +package base256_test + +import ( + "crypto/sha256" + "fmt" + "github.com/ehmry/encoding/base256" +) + +func ExampleNewEncoding() { + h := sha256.New() + digest := h.Sum(nil) + + fmt.Printf("%x\n", digest) + + e, _ := base256.NewEncoding('⠀') // U+2800 BRAILLE PATTERN BLANK + fmt.Println(e.EncodeToString(digest)) + + e, _ = base256.NewEncoding('⤀') + fmt.Println(e.EncodeToString(digest)) + + // Output: + // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + // ⣣⢰⣄⡂⢘⣼⠜⠔⢚⣻⣴⣈⢙⡯⢹⠤⠧⢮⡁⣤⡤⢛⢓⡌⢤⢕⢙⠛⡸⡒⢸⡕ + // ⧣⦰⧄⥂⦘⧼⤜⤔⦚⧻⧴⧈⦙⥯⦹⤤⤧⦮⥁⧤⥤⦛⦓⥌⦤⦕⦙⤛⥸⥒⦸⥕ + +} diff --git a/braille/README.md b/braille/README.md deleted file mode 100644 index be21a2a..0000000 --- a/braille/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# sha256 digest in hex -`da39a3ee5e6b4b0d3255bfef95601890afd80709` - -# sha256 digest in braille -`⣣⢰⣄⡂⢘⣼⠜⠔⢚⣻⣴⣈⢙⡯⢹⠤⠧⢮⡁⣤⡤⢛⢓⡌⢤⢕⢙⠛⡸⡒⢸⡕` \ No newline at end of file diff --git a/braille/braille.go b/braille/braille.go deleted file mode 100644 index 65ff27e..0000000 --- a/braille/braille.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2014 Emery Hemingway. All rights reserved. -// Use of this source code is governed by the GPLv3+. - -// Package braille implements radix 256 encoding and decoding using -// the UTF-8 Braille encoding. -package braille - -import ( - "errors" - "fmt" - "unicode/utf8" -) - -// EncodedLen returns the length of an encoding of n source bytes. -func EncodedLen(n int) int { return n * 3 } - -// Encode encodes src into EncodedLen(len(src)) -// bytes of dst. As a convenience, it returns the number -// of bytes written to dst, but this value is always -// EncodedLen(len(src)). -// Encode implements Braille encoding. -func Encode(dst, src []byte) (n int) { - var i int - for i = 0; i < len(dst); i += 3 { - dst[i] = 0xE2 - } - - var j = 1 - var k = 2 - for i = 0; i < len(src); i++ { - dst[j] = 0xA0 | (src[i] >> 6) - dst[k] = 0x80 | (src[i])&0x3F - j += 3 - k += 3 - } - return len(src) * 3 -} - -// InvalidRuneError values describe errors resulting from an invalid byte in a Braille string. -type InvalidRuneError rune - -func (e InvalidRuneError) Error() string { - return fmt.Sprintf("braille: invalid rune: %#U", rune(e)) -} - -var ErrLength = errors.New("braille: bad length") - -func DecodedLen(x int) int { return x / 3 } - -func Decode(dst, src []byte) (int, error) { - if len(dst) < len(src)/3 { - return 0, ErrLength - } - - var i int - for i = 0; i < len(src); i += 3 { - if src[i] != 0xE2 { - r, _ := utf8.DecodeRune(src[i:]) - - return 0, InvalidRuneError(r) - } - } - var j = 1 - var k = 2 - for i = 0; i < len(dst); i++ { - dst[i] = (src[j] << 6) | (src[k] ^ 0x80) - j += 3 - k += 3 - } - return len(src) / 3, nil -} - -// EncodeToString returns the bralle encoding of src. -func EncodeToString(src []byte) string { - dst := make([]byte, EncodedLen(len(src))) - Encode(dst, src) - return string(dst) -} - -// DecodeString returns the bytes represented by the braille string s. -func DecodeString(s string) ([]byte, error) { - src := []byte(s) - dst := make([]byte, DecodedLen(len(src))) - _, err := Decode(dst, src) - if err != nil { - return nil, err - } - return dst, nil -} diff --git a/braille/braille_test.go b/braille/braille_test.go deleted file mode 100644 index 7322a69..0000000 --- a/braille/braille_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package braille - -import ( - "bytes" - "testing" -) - -type encDecTest struct { - enc string - dec []byte -} - -var encDecTests = []encDecTest{ - {"⠀", []byte{0x00}}, - {"⠁", []byte{0x01}}, - {"⠂", []byte{0x02}}, - {"⠃", []byte{0x03}}, - {"⠄", []byte{0x04}}, - {"⣿", []byte{0xFF}}, - {"⠀⠀", []byte{0x00, 0x00}}, - {"⣿⣿", []byte{0xFF, 0xFF}}, - {"⣿⣿", []byte{0xFF, 0xFF}}, - {"⠀⠀⠀", []byte{0x00, 0x00, 0x00}}, - {"⣿⣿⣿⣿⣿⣿", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, -} - -func TestEncode(t *testing.T) { - for i, test := range encDecTests { - dst := make([]byte, EncodedLen(len(test.dec))) - n := Encode(dst, test.dec) - if n != len(dst) { - t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst)) - } - if string(dst) != test.enc { - t.Errorf("#%d: %X: got: %X want: %X - %q %q", i, test.dec, dst, test.enc, dst, test.enc) - } - } -} - -func TestDecode(t *testing.T) { - for i, test := range encDecTests { - dst := make([]byte, DecodedLen(len(test.enc))) - n, err := Decode(dst, []byte(test.enc)) - if err != nil { - t.Error(err) - } - if n != len(dst) { - t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst)) - } - if !bytes.Equal(dst, test.dec) { - t.Errorf("#%d: %X: got: %X want: %X - %q %q", i, test.dec, dst, test.enc, dst, test.enc) - } - } -} - -func TestIterative(t *testing.T) { - var in, out byte - enc := make([]byte, 3) - dec := make([]byte, 1) - for in = 0; in < 0xFF; in++ { - dec[0] = in - Encode(enc, dec) - Decode(dec, enc) - out = dec[0] - - if in != out { - t.Errorf("Wanted %08b, got %08b", in, out) - } - } -} - -func BenchmarkEncode(b *testing.B) { - dec := make([]byte, 255) - enc := make([]byte, EncodedLen(255)) - - var c byte - for c = 0; c < 255; c++ { - dec[c] = c - } - for i := 0; i < b.N; i++ { - Encode(enc, dec) - b.SetBytes(255) - } -} - -func BenchmarkDecode(b *testing.B) { - dec := make([]byte, 255) - enc := make([]byte, EncodedLen(255)) - - var c byte - for c = 0; c < 255; c++ { - dec[c] = c - } - Encode(enc, dec) - - for i := 0; i < b.N; i++ { - Decode(dec, enc) - b.SetBytes(255) - } -}