diff --git a/component.go b/component.go index e31fe5d..69baf7a 100644 --- a/component.go +++ b/component.go @@ -3,6 +3,7 @@ package multiaddr import ( "bytes" "encoding/binary" + "encoding/json" "fmt" "strings" ) @@ -18,6 +19,54 @@ func (c *Component) Bytes() []byte { 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 { 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 { - m := multiaddr{bytes: c.bytes} + m := &multiaddr{bytes: c.bytes} return m.Encapsulate(o) } diff --git a/interface.go b/interface.go index 34bffd9..82cc764 100644 --- a/interface.go +++ b/interface.go @@ -1,5 +1,10 @@ package multiaddr +import ( + "encoding" + "encoding/json" +) + /* Multiaddr is a cross-protocol, cross-platform format for representing internet addresses. It emphasizes explicitness and self-description. @@ -14,6 +19,13 @@ Multiaddrs have both a binary and string representation. */ type Multiaddr interface { + json.Marshaler + json.Unmarshaler + encoding.TextMarshaler + encoding.TextUnmarshaler + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + // Equal returns whether two Multiaddrs are exactly equal Equal(Multiaddr) bool diff --git a/multiaddr.go b/multiaddr.go index 2c07dd3..6794b92 100644 --- a/multiaddr.go +++ b/multiaddr.go @@ -2,6 +2,7 @@ package multiaddr import ( "bytes" + "encoding/json" "fmt" "log" "strings" @@ -24,7 +25,7 @@ func NewMultiaddr(s string) (a Multiaddr, err error) { if err != nil { return nil, err } - return multiaddr{bytes: b}, nil + return &multiaddr{bytes: b}, nil } // NewMultiaddrBytes initializes a Multiaddr from a byte representation. @@ -41,23 +42,23 @@ func NewMultiaddrBytes(b []byte) (a Multiaddr, err error) { return nil, err } - return multiaddr{bytes: b}, nil + return &multiaddr{bytes: b}, nil } // 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()) } // Bytes returns the []byte representation of this Multiaddr // // Do not modify the returned buffer, it may be shared. -func (m multiaddr) Bytes() []byte { +func (m *multiaddr) Bytes() []byte { return m.bytes } // String returns the string representation of a Multiaddr -func (m multiaddr) String() string { +func (m *multiaddr) String() string { s, err := bytesToString(m.bytes) if err != nil { panic(fmt.Errorf("multiaddr failed to convert back to string. corrupted? %s", err)) @@ -65,9 +66,49 @@ func (m multiaddr) String() string { 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. // will panic in case we access bytes incorrectly. -func (m multiaddr) Protocols() []Protocol { +func (m *multiaddr) Protocols() []Protocol { ps := make([]Protocol, 0, 8) b := m.bytes for len(b) > 0 { @@ -96,18 +137,18 @@ func (m multiaddr) Protocols() []Protocol { } // 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 ob := o.Bytes() b := make([]byte, len(mb)+len(ob)) copy(b, mb) copy(b[len(mb):], ob) - return multiaddr{bytes: b} + return &multiaddr{bytes: b} } // 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() s2 := o.String() i := strings.LastIndex(s1, s2) @@ -115,7 +156,7 @@ func (m multiaddr) Decapsulate(o Multiaddr) Multiaddr { // if multiaddr not contained, returns a copy. cpy := make([]byte, len(m.bytes)) copy(cpy, m.bytes) - return multiaddr{bytes: cpy} + return &multiaddr{bytes: cpy} } 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") -func (m multiaddr) ValueForProtocol(code int) (value string, err error) { +func (m *multiaddr) ValueForProtocol(code int) (value string, err error) { err = ErrProtocolNotFound ForEach(m, func(c Component) bool { if c.Protocol().Code == code { diff --git a/multiaddr_test.go b/multiaddr_test.go index 138fcd7..f97f92c 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -555,3 +555,108 @@ func TestZone(t *testing.T) { 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") + } +} diff --git a/util.go b/util.go index d1044da..cf4469a 100644 --- a/util.go +++ b/util.go @@ -21,7 +21,7 @@ func Join(ms ...Multiaddr) Multiaddr { case 0: // empty multiaddr, unfortunately, we have callers that rely on // this contract. - return multiaddr{} + return &multiaddr{} case 1: return ms[0] } @@ -38,7 +38,7 @@ func Join(ms ...Multiaddr) Multiaddr { for _, mb := range bs { 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. @@ -77,7 +77,7 @@ func SplitFirst(m Multiaddr) (*Component, Multiaddr) { if len(b) == n { return &c, nil } - return &c, multiaddr{b[n:]} + return &c, &multiaddr{b[n:]} } // SplitLast returns the rest of the multiaddr and the last component. @@ -109,7 +109,7 @@ func SplitLast(m Multiaddr) (Multiaddr, *Component) { // Only one component return nil, &c } - return multiaddr{b[:offset]}, &c + return &multiaddr{b[:offset]}, &c } offset += n } @@ -152,7 +152,7 @@ func SplitFunc(m Multiaddr, cb func(Component) bool) (Multiaddr, Multiaddr) { case len(b): return m, nil default: - return multiaddr{b[:offset]}, multiaddr{b[offset:]} + return &multiaddr{b[:offset]}, &multiaddr{b[offset:]} } }