diff --git a/.travis.yml b/.travis.yml index 3cb0758..7462824 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: language: go go: - - 1.8.3 + - 1.10.2 install: - go get -u github.com/whyrusleeping/gx diff --git a/encoder.go b/encoder.go new file mode 100644 index 0000000..d1f87f0 --- /dev/null +++ b/encoder.go @@ -0,0 +1,53 @@ +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 +} + +// 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 +} diff --git a/encoder_test.go b/encoder_test.go new file mode 100644 index 0000000..714ba02 --- /dev/null +++ b/encoder_test.go @@ -0,0 +1,33 @@ +package multibase + +import ( + "testing" +) + +func TestInvalidPrefix(t *testing.T) { + _, err := NewEncoder('q') + if err == nil { + t.Error("expected failure") + } +} + +func TestPrefix(t *testing.T) { + for str, base := range Encodings { + prefix, err := NewEncoder(base) + if err != nil { + t.Fatalf("NewEncoder(%c) failed: %v", base, err) + } + str1, err := Encode(base, sampleBytes) + if err != nil { + t.Fatal(err) + } + str2 := prefix.Encode(sampleBytes) + if str1 != str2 { + t.Errorf("encoded string mismatch: %s != %s", str1, str2) + } + _, err = EncoderByName(str) + if err != nil { + t.Fatalf("NewEncoder(%s) failed: %v", str, err) + } + } +} diff --git a/multibase.go b/multibase.go index 4650376..478fa6d 100644 --- a/multibase.go +++ b/multibase.go @@ -12,7 +12,8 @@ import ( // 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") diff --git a/multibase_test.go b/multibase_test.go index a69d721..0b490df 100644 --- a/multibase_test.go +++ b/multibase_test.go @@ -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!!!",