Let Multiaddr implement marshalers: binary, text, json

This commit is contained in:
Hector Sanjuan 2019-02-25 22:48:24 +00:00
parent 0297994296
commit 0aa80854ab
5 changed files with 224 additions and 17 deletions

View File

@ -3,6 +3,7 @@ package multiaddr
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
) )
@ -18,6 +19,54 @@ func (c *Component) Bytes() []byte {
return c.bytes return c.bytes
} }
func (c *Component) MarshalBinary() ([]byte, error) {
return c.Bytes(), nil
}
func (c *Component) UnmarshalBinary(data []byte) error {
_, comp, err := readComponent(data)
if err != nil {
return err
}
*c = comp
return nil
}
func (c *Component) MarshalText() ([]byte, error) {
return []byte(c.String()), nil
}
func (c *Component) UnmarshalText(data []byte) error {
bytes, err := stringToBytes(string(data))
if err != nil {
return err
}
_, comp, err := readComponent(bytes)
if err != nil {
return err
}
*c = comp
return nil
}
func (c *Component) MarshalJSON() ([]byte, error) {
txt, err := c.MarshalText()
if err != nil {
return nil, err
}
return json.Marshal(string(txt))
}
func (m *Component) UnmarshalJSON(data []byte) error {
var v string
if err := json.Unmarshal(data, &v); err != nil {
return err
}
return m.UnmarshalText([]byte(v))
}
func (c *Component) Equal(o Multiaddr) bool { func (c *Component) Equal(o Multiaddr) bool {
return bytes.Equal(c.bytes, o.Bytes()) return bytes.Equal(c.bytes, o.Bytes())
} }
@ -34,7 +83,7 @@ func (c *Component) Decapsulate(o Multiaddr) Multiaddr {
} }
func (c *Component) Encapsulate(o Multiaddr) Multiaddr { func (c *Component) Encapsulate(o Multiaddr) Multiaddr {
m := multiaddr{bytes: c.bytes} m := &multiaddr{bytes: c.bytes}
return m.Encapsulate(o) return m.Encapsulate(o)
} }

View File

@ -1,5 +1,10 @@
package multiaddr package multiaddr
import (
"encoding"
"encoding/json"
)
/* /*
Multiaddr is a cross-protocol, cross-platform format for representing Multiaddr is a cross-protocol, cross-platform format for representing
internet addresses. It emphasizes explicitness and self-description. internet addresses. It emphasizes explicitness and self-description.
@ -14,6 +19,13 @@ Multiaddrs have both a binary and string representation.
*/ */
type Multiaddr interface { type Multiaddr interface {
json.Marshaler
json.Unmarshaler
encoding.TextMarshaler
encoding.TextUnmarshaler
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
// Equal returns whether two Multiaddrs are exactly equal // Equal returns whether two Multiaddrs are exactly equal
Equal(Multiaddr) bool Equal(Multiaddr) bool

View File

@ -2,6 +2,7 @@ package multiaddr
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"log" "log"
"strings" "strings"
@ -24,7 +25,7 @@ func NewMultiaddr(s string) (a Multiaddr, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return multiaddr{bytes: b}, nil return &multiaddr{bytes: b}, nil
} }
// NewMultiaddrBytes initializes a Multiaddr from a byte representation. // NewMultiaddrBytes initializes a Multiaddr from a byte representation.
@ -41,23 +42,23 @@ func NewMultiaddrBytes(b []byte) (a Multiaddr, err error) {
return nil, err return nil, err
} }
return multiaddr{bytes: b}, nil return &multiaddr{bytes: b}, nil
} }
// Equal tests whether two multiaddrs are equal // Equal tests whether two multiaddrs are equal
func (m multiaddr) Equal(m2 Multiaddr) bool { func (m *multiaddr) Equal(m2 Multiaddr) bool {
return bytes.Equal(m.bytes, m2.Bytes()) return bytes.Equal(m.bytes, m2.Bytes())
} }
// Bytes returns the []byte representation of this Multiaddr // Bytes returns the []byte representation of this Multiaddr
// //
// Do not modify the returned buffer, it may be shared. // Do not modify the returned buffer, it may be shared.
func (m multiaddr) Bytes() []byte { func (m *multiaddr) Bytes() []byte {
return m.bytes return m.bytes
} }
// String returns the string representation of a Multiaddr // String returns the string representation of a Multiaddr
func (m multiaddr) String() string { func (m *multiaddr) String() string {
s, err := bytesToString(m.bytes) s, err := bytesToString(m.bytes)
if err != nil { if err != nil {
panic(fmt.Errorf("multiaddr failed to convert back to string. corrupted? %s", err)) panic(fmt.Errorf("multiaddr failed to convert back to string. corrupted? %s", err))
@ -65,9 +66,49 @@ func (m multiaddr) String() string {
return s return s
} }
func (m *multiaddr) MarshalBinary() ([]byte, error) {
return m.Bytes(), nil
}
func (m *multiaddr) UnmarshalBinary(data []byte) error {
new, err := NewMultiaddrBytes(data)
if err != nil {
return err
}
*m = *(new.(*multiaddr))
return nil
}
func (m *multiaddr) MarshalText() ([]byte, error) {
return []byte(m.String()), nil
}
func (m *multiaddr) UnmarshalText(data []byte) error {
new, err := NewMultiaddr(string(data))
if err != nil {
return err
}
*m = *(new.(*multiaddr))
return nil
}
func (m *multiaddr) MarshalJSON() ([]byte, error) {
return json.Marshal(m.String())
}
func (m *multiaddr) UnmarshalJSON(data []byte) error {
var v string
if err := json.Unmarshal(data, &v); err != nil {
return err
}
new, err := NewMultiaddr(v)
*m = *(new.(*multiaddr))
return err
}
// Protocols returns the list of protocols this Multiaddr has. // Protocols returns the list of protocols this Multiaddr has.
// will panic in case we access bytes incorrectly. // will panic in case we access bytes incorrectly.
func (m multiaddr) Protocols() []Protocol { func (m *multiaddr) Protocols() []Protocol {
ps := make([]Protocol, 0, 8) ps := make([]Protocol, 0, 8)
b := m.bytes b := m.bytes
for len(b) > 0 { for len(b) > 0 {
@ -96,18 +137,18 @@ func (m multiaddr) Protocols() []Protocol {
} }
// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr // Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
func (m multiaddr) Encapsulate(o Multiaddr) Multiaddr { func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr {
mb := m.bytes mb := m.bytes
ob := o.Bytes() ob := o.Bytes()
b := make([]byte, len(mb)+len(ob)) b := make([]byte, len(mb)+len(ob))
copy(b, mb) copy(b, mb)
copy(b[len(mb):], ob) copy(b[len(mb):], ob)
return multiaddr{bytes: b} return &multiaddr{bytes: b}
} }
// Decapsulate unwraps Multiaddr up until the given Multiaddr is found. // Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
func (m multiaddr) Decapsulate(o Multiaddr) Multiaddr { func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr {
s1 := m.String() s1 := m.String()
s2 := o.String() s2 := o.String()
i := strings.LastIndex(s1, s2) i := strings.LastIndex(s1, s2)
@ -115,7 +156,7 @@ func (m multiaddr) Decapsulate(o Multiaddr) Multiaddr {
// if multiaddr not contained, returns a copy. // if multiaddr not contained, returns a copy.
cpy := make([]byte, len(m.bytes)) cpy := make([]byte, len(m.bytes))
copy(cpy, m.bytes) copy(cpy, m.bytes)
return multiaddr{bytes: cpy} return &multiaddr{bytes: cpy}
} }
ma, err := NewMultiaddr(s1[:i]) ma, err := NewMultiaddr(s1[:i])
@ -127,7 +168,7 @@ func (m multiaddr) Decapsulate(o Multiaddr) Multiaddr {
var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr") var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr")
func (m multiaddr) ValueForProtocol(code int) (value string, err error) { func (m *multiaddr) ValueForProtocol(code int) (value string, err error) {
err = ErrProtocolNotFound err = ErrProtocolNotFound
ForEach(m, func(c Component) bool { ForEach(m, func(c Component) bool {
if c.Protocol().Code == code { if c.Protocol().Code == code {

View File

@ -555,3 +555,108 @@ func TestZone(t *testing.T) {
t.Errorf("expected %s, got %s", ip6String, ma2.String()) t.Errorf("expected %s, got %s", ip6String, ma2.String())
} }
} }
func TestBinaryMarshaler(t *testing.T) {
addr := newMultiaddr(t, "/ip4/0.0.0.0/tcp/4001")
b, err := addr.MarshalBinary()
if err != nil {
t.Fatal(err)
}
addr2 := newMultiaddr(t, "")
if err = addr2.UnmarshalBinary(b); err != nil {
t.Fatal(err)
}
if !addr.Equal(addr2) {
t.Error("expected equal addresses in circular marshaling test")
}
}
func TestTextMarshaler(t *testing.T) {
addr := newMultiaddr(t, "/ip4/0.0.0.0/tcp/4001")
b, err := addr.MarshalText()
if err != nil {
t.Fatal(err)
}
addr2 := newMultiaddr(t, "")
if err = addr2.UnmarshalText(b); err != nil {
t.Fatal(err)
}
if !addr.Equal(addr2) {
t.Error("expected equal addresses in circular marshaling test")
}
}
func TestJSONMarshaler(t *testing.T) {
addr := newMultiaddr(t, "/ip4/0.0.0.0/tcp/4001")
b, err := addr.MarshalJSON()
if err != nil {
t.Fatal(err)
}
addr2 := newMultiaddr(t, "")
if err = addr2.UnmarshalJSON(b); err != nil {
t.Fatal(err)
}
if !addr.Equal(addr2) {
t.Error("expected equal addresses in circular marshaling test")
}
}
func TestComponentBinaryMarshaler(t *testing.T) {
comp, err := NewComponent("ip4", "0.0.0.0")
if err != nil {
t.Fatal(err)
}
b, err := comp.MarshalBinary()
if err != nil {
t.Fatal(err)
}
comp2 := &Component{}
if err = comp2.UnmarshalBinary(b); err != nil {
t.Fatal(err)
}
if !comp.Equal(comp2) {
t.Error("expected equal components in circular marshaling test")
}
}
func TestComponentTextMarshaler(t *testing.T) {
comp, err := NewComponent("ip4", "0.0.0.0")
if err != nil {
t.Fatal(err)
}
b, err := comp.MarshalText()
if err != nil {
t.Fatal(err)
}
comp2 := &Component{}
if err = comp2.UnmarshalText(b); err != nil {
t.Fatal(err)
}
if !comp.Equal(comp2) {
t.Error("expected equal components in circular marshaling test")
}
}
func TestComponentJSONMarshaler(t *testing.T) {
comp, err := NewComponent("ip4", "0.0.0.0")
if err != nil {
t.Fatal(err)
}
b, err := comp.MarshalJSON()
if err != nil {
t.Fatal(err)
}
comp2 := &Component{}
if err = comp2.UnmarshalJSON(b); err != nil {
t.Fatal(err)
}
if !comp.Equal(comp2) {
t.Error("expected equal components in circular marshaling test")
}
}

10
util.go
View File

@ -21,7 +21,7 @@ func Join(ms ...Multiaddr) Multiaddr {
case 0: case 0:
// empty multiaddr, unfortunately, we have callers that rely on // empty multiaddr, unfortunately, we have callers that rely on
// this contract. // this contract.
return multiaddr{} return &multiaddr{}
case 1: case 1:
return ms[0] return ms[0]
} }
@ -38,7 +38,7 @@ func Join(ms ...Multiaddr) Multiaddr {
for _, mb := range bs { for _, mb := range bs {
bidx += copy(b[bidx:], mb) bidx += copy(b[bidx:], mb)
} }
return multiaddr{bytes: b} return &multiaddr{bytes: b}
} }
// Cast re-casts a byte slice as a multiaddr. will panic if it fails to parse. // Cast re-casts a byte slice as a multiaddr. will panic if it fails to parse.
@ -77,7 +77,7 @@ func SplitFirst(m Multiaddr) (*Component, Multiaddr) {
if len(b) == n { if len(b) == n {
return &c, nil return &c, nil
} }
return &c, multiaddr{b[n:]} return &c, &multiaddr{b[n:]}
} }
// SplitLast returns the rest of the multiaddr and the last component. // SplitLast returns the rest of the multiaddr and the last component.
@ -109,7 +109,7 @@ func SplitLast(m Multiaddr) (Multiaddr, *Component) {
// Only one component // Only one component
return nil, &c return nil, &c
} }
return multiaddr{b[:offset]}, &c return &multiaddr{b[:offset]}, &c
} }
offset += n offset += n
} }
@ -152,7 +152,7 @@ func SplitFunc(m Multiaddr, cb func(Component) bool) (Multiaddr, Multiaddr) {
case len(b): case len(b):
return m, nil return m, nil
default: default:
return multiaddr{b[:offset]}, multiaddr{b[offset:]} return &multiaddr{b[:offset]}, &multiaddr{b[offset:]}
} }
} }