19 Commits

Author SHA1 Message Date
Kevin Atkinson
bb91b53e56 gx publish 0.3.0 2018-08-31 20:32:43 -04:00
Steven Allen
964f55ad40 Merge pull request #23 from multiformats/kevina/encoder
Don't return an error on NewEncoder, panic on invalid encodings instead.
2018-09-01 00:20:04 +00:00
Kevin Atkinson
3b3047873d Use MustNewEncoder instead for version that does not panic. 2018-08-31 20:08:55 -04:00
Kevin Atkinson
2170058ef9 Add CheckEncoding function. 2018-08-31 18:59:07 -04:00
Kevin Atkinson
ac3d23441b Don't return an error on NewEncoder, panic on invalid encodings instead.
Most of the time this method will be used with a constant and the error
will be thrown away anyway.  By not returning an error we can use this
function to initialize global variables.  The function EncoderByName should
be used when working with user provided input and we care about the error.
2018-08-31 15:23:22 -04:00
Kevin Atkinson
ecd5d58562 Gofmt. 2018-08-31 15:01:41 -04:00
Steven Allen
b46f1c99f0 Merge pull request #22 from ianlopshire/master
Improve test coverage
2018-08-23 17:41:56 +00:00
Ian Lopshire
5fb339e88a Improve test coverage 2018-08-23 13:29:45 -04:00
Kevin Atkinson
caebba6233 gx publish 0.2.7 2018-07-27 17:34:30 -04:00
Kevin Atkinson
83915a874d Merge pull request #21 from multiformats/kevina/map
Enhance Multibase
2018-07-27 17:29:59 -04:00
Steven Allen
03643c33f5 ci: bump minimum go version 2018-07-27 17:02:59 -04:00
Kevin Atkinson
2eb83a994b Remove "magical" NewPrefix function, rename Prefix to Encoder. 2018-07-27 17:02:59 -04:00
Kevin Atkinson
5547437445 Enhance constructor for Prefix type. 2018-07-27 16:59:07 -04:00
Kevin Atkinson
a0557075ec Add prefix type that guarantees a valid multibase prefix. 2018-07-27 16:59:07 -04:00
Kevin Atkinson
3ea5c212ef Add maps for converting from the string repr. to the code and back. 2018-07-27 16:59:01 -04:00
Steven Allen
dfd5076869 gx publish 0.2.6 2017-12-18 10:32:30 -08:00
Steven Allen
3f064509b3 Merge pull request #20 from multiformats/feat/faster
Switch to a faster base58 library
2017-12-18 18:19:05 +00:00
Steven Allen
23774f6467 add benchmarks 2017-12-18 10:08:13 -08:00
Steven Allen
f61622a056 significantly faster b58
* 8-10x faster round trip.
* only 3 allocations each way.
2017-12-17 18:15:25 -08:00
7 changed files with 237 additions and 11 deletions

View File

@@ -1 +1 @@
0.2.5: QmafgXF3u3QSWErQoZ2URmQp5PFG384htoE7J338nS2H7T
0.3.0: QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd

View File

@@ -4,7 +4,7 @@ os:
language: go
go:
- 1.8.3
- 1.10.2
install:
- go get -u github.com/whyrusleeping/gx

63
encoder.go Normal file
View File

@@ -0,0 +1,63 @@
package multibase
import (
"fmt"
)
// Encoder is a multibase encoding that is verified to be supported and
// supports an Encode method that does not return an error
type Encoder struct {
enc Encoding
}
// NewEncoder create a new Encoder from an Encoding
func NewEncoder(base Encoding) (Encoder, error) {
_, ok := EncodingToStr[base]
if !ok {
return Encoder{-1}, fmt.Errorf("Unsupported multibase encoding: %d", base)
}
return Encoder{base}, nil
}
// MustNewEncoder is like NewEncoder but will panic if the encoding is
// invalid.
func MustNewEncoder(base Encoding) Encoder {
_, ok := EncodingToStr[base]
if !ok {
panic("Unsupported multibase encoding")
}
return Encoder{base}
}
// EncoderByName creates an encoder from a string, the string can
// either be the multibase name or single character multibase prefix
func EncoderByName(str string) (Encoder, error) {
var base Encoding
ok := true
if len(str) == 0 {
return Encoder{-1}, fmt.Errorf("Empty multibase encoding")
} else if len(str) == 1 {
base = Encoding(str[0])
_, ok = EncodingToStr[base]
} else {
base, ok = Encodings[str]
}
if !ok {
return Encoder{-1}, fmt.Errorf("Unsupported multibase encoding: %s", str)
}
return Encoder{base}, nil
}
func (p Encoder) Encoding() Encoding {
return p.enc
}
// Encode encodes the multibase using the given Encoder.
func (p Encoder) Encode(data []byte) string {
str, err := Encode(p.enc, data)
if err != nil {
// should not happen
panic(err)
}
return str
}

51
encoder_test.go Normal file
View File

@@ -0,0 +1,51 @@
package multibase
import (
"testing"
)
func TestInvalidCode(t *testing.T) {
_, err := NewEncoder('q')
if err == nil {
t.Error("expected failure")
}
}
func TestInvalidName(t *testing.T) {
values := []string{"invalid", "", "q"}
for _, val := range values {
_, err := EncoderByName(val)
if err == nil {
t.Errorf("EncoderByName(%v) expected failure", val)
}
}
}
func TestEncoder(t *testing.T) {
for name, code := range Encodings {
encoder, err := NewEncoder(code)
if err != nil {
t.Fatal(err)
}
// Make sure the MustNewEncoder doesn't panic
MustNewEncoder(code)
str, err := Encode(code, sampleBytes)
if err != nil {
t.Fatal(err)
}
str2 := encoder.Encode(sampleBytes)
if str != str2 {
t.Errorf("encoded string mismatch: %s != %s", str, str2)
}
_, err = EncoderByName(name)
if err != nil {
t.Fatalf("EncoderByName(%s) failed: %v", name, err)
}
// Test that an encoder can be created from the single letter
// prefix
_, err = EncoderByName(str[0:1])
if err != nil {
t.Fatalf("EncoderByName(%s) failed: %v", str[0:1], err)
}
}
}

View File

@@ -5,14 +5,15 @@ import (
"encoding/hex"
"fmt"
b58 "github.com/jbenet/go-base58"
b58 "github.com/mr-tron/base58/base58"
b32 "github.com/whyrusleeping/base32"
)
// Encoding identifies the type of base-encoding that a multibase is carrying.
type Encoding int
// These are the supported encodings
// These are the encodings specified in the standard, not are all
// supported yet
const (
Identity = 0x00
Base1 = '1'
@@ -37,6 +38,48 @@ const (
Base64urlPad = 'U'
)
// Encodigs is a map of the supported encoding, unsupported encoding
// specified in standard are left out
var Encodings = map[string]Encoding{
"identity": 0x00,
"base16": 'f',
"base16upper": 'F',
"base32": 'b',
"base32upper": 'B',
"base32pad": 'c',
"base32padupper": 'C',
"base32hex": 'v',
"base32hexupper": 'V',
"base32hexpad": 't',
"base32hexpadupper": 'T',
"base58flickr": 'Z',
"base58btc": 'z',
"base64": 'm',
"base64url": 'u',
"base64pad": 'M',
"base64urlpad": 'U',
}
var EncodingToStr = map[Encoding]string{
0x00: "identity",
'f': "base16",
'F': "base16upper",
'b': "base32",
'B': "base32upper",
'c': "base32pad",
'C': "base32padupper",
'v': "base32hex",
'V': "base32hexupper",
't': "base32hexpad",
'T': "base32hexpadupper",
'Z': "base58flickr",
'z': "base58btc",
'm': "base64",
'u': "base64url",
'M': "base64pad",
'U': "base64urlpad",
}
// ErrUnsupportedEncoding is returned when the selected encoding is not known or
// implemented.
var ErrUnsupportedEncoding = fmt.Errorf("selected encoding not supported")
@@ -114,9 +157,11 @@ func Decode(data string) (Encoding, []byte, error) {
bytes, err := b32.HexEncoding.DecodeString(data[1:])
return enc, bytes, err
case Base58BTC:
return Base58BTC, b58.DecodeAlphabet(data[1:], b58.BTCAlphabet), nil
bytes, err := b58.DecodeAlphabet(data[1:], b58.BTCAlphabet)
return Base58BTC, bytes, err
case Base58Flickr:
return Base58Flickr, b58.DecodeAlphabet(data[1:], b58.FlickrAlphabet), nil
bytes, err := b58.DecodeAlphabet(data[1:], b58.FlickrAlphabet)
return Base58Flickr, bytes, err
case Base64pad:
bytes, err := base64.StdEncoding.DecodeString(data[1:])
return Base64pad, bytes, err

View File

@@ -6,6 +6,21 @@ import (
"testing"
)
func TestMap(t *testing.T) {
for s, e := range Encodings {
s2 := EncodingToStr[e]
if s != s2 {
t.Errorf("round trip failed on encoding map: %s != %s", s, s2)
}
}
for e, s := range EncodingToStr {
e2 := Encodings[s]
if e != e2 {
t.Errorf("round trip failed on encoding map: '%c' != '%c'", e, e2)
}
}
}
var sampleBytes = []byte("Decentralize everything!!!")
var encodedSamples = map[Encoding]string{
Identity: string(0x00) + "Decentralize everything!!!",
@@ -20,6 +35,8 @@ var encodedSamples = map[Encoding]string{
Base32hexPad: "t8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144======",
Base32hexPadUpper: "T8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E89144======",
Base58BTC: "z36UQrhJq9fNDS7DiAHM9YXqDHMPfr4EMArvt",
Base64: "mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE",
Base64url: "uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE",
Base64pad: "MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE=",
Base64urlPad: "URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE=",
}
@@ -92,3 +109,53 @@ func TestRoundTrip(t *testing.T) {
t.Fatal("shouldnt be able to decode empty string")
}
}
func BenchmarkRoundTrip(b *testing.B) {
buf := make([]byte, 32)
rand.Read(buf)
b.ResetTimer()
bases := map[string]Encoding{
"Identity": Identity,
"Base16": Base16,
"Base16Upper": Base16Upper,
"Base32": Base32,
"Base32Upper": Base32Upper,
"Base32pad": Base32pad,
"Base32padUpper": Base32padUpper,
"Base32hex": Base32hex,
"Base32hexUpper": Base32hexUpper,
"Base32hexPad": Base32hexPad,
"Base32hexPadUpper": Base32hexPadUpper,
"Base58Flickr": Base58Flickr,
"Base58BTC": Base58BTC,
"Base64": Base64,
"Base64url": Base64url,
"Base64pad": Base64pad,
"Base64urlPad": Base64urlPad,
}
for name, base := range bases {
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
enc, err := Encode(base, buf)
if err != nil {
b.Fatal(err)
}
e, out, err := Decode(enc)
if err != nil {
b.Fatal(err)
}
if e != base {
b.Fatal("got wrong encoding out")
}
if !bytes.Equal(buf, out) {
b.Fatal("input wasnt the same as output", buf, out)
}
}
})
}
}

View File

@@ -14,10 +14,10 @@
"version": "0.0.2"
},
{
"author": "whyrusleeping",
"hash": "QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf",
"name": "go-base58",
"version": "0.0.0"
"author": "mr-tron",
"hash": "QmWFAMPqsEyUX7gDUsRVmMWz59FxSpJ1b2v6bJ1yYzo7jY",
"name": "go-base58-fast",
"version": "0.1.1"
}
],
"gxVersion": "0.8.0",
@@ -25,6 +25,6 @@
"license": "",
"name": "go-multibase",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "0.2.5"
"version": "0.3.0"
}