stop copying when calling Bytes

This is a huge performance hit. Really, we just need to tell users not to modify
the result.

Also, get rid of an unnecessary pointer indirection (no api change).
This commit is contained in:
Steven Allen 2018-10-01 15:39:57 -07:00
parent fe1c46f8be
commit 61e31ed48b
4 changed files with 20 additions and 25 deletions

View File

@ -18,6 +18,8 @@ type Multiaddr interface {
Equal(Multiaddr) bool
// Bytes returns the []byte representation of this Multiaddr
//
// This function may expose immutable, internal state. Do not modify.
Bytes() []byte
// String returns the string representation of this Multiaddr

View File

@ -24,7 +24,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,34 +41,33 @@ 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
func (m *multiaddr) Bytes() []byte {
// consider returning copy to prevent changing underneath us?
cpy := make([]byte, len(m.bytes))
copy(cpy, m.bytes)
return cpy
//
// Do not modify the returned buffer, it may be shared.
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("multiaddr failed to convert back to string. corrupted?")
panic(fmt.Errorf("multiaddr failed to convert back to string. corrupted? %s", err))
}
return s
}
// 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 {
@ -97,18 +96,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)
@ -116,7 +115,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])
@ -128,7 +127,7 @@ func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr {
var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr")
func (m *multiaddr) ValueForProtocol(code int) (string, error) {
func (m multiaddr) ValueForProtocol(code int) (string, error) {
for _, sub := range Split(m) {
p := sub.Protocols()[0]
if p.Code == code {

View File

@ -229,12 +229,6 @@ func TestBytesSplitAndJoin(t *testing.T) {
t.Errorf("joined components failed: %s != %s", m, joined)
}
// modifying underlying bytes is fine.
m2 := m.(*multiaddr)
for i := range m2.bytes {
m2.bytes[i] = 0
}
for i, a := range split {
if a.String() != res[i] {
t.Errorf("split component failed: %s != %s", a, res[i])

View File

@ -11,7 +11,7 @@ func Split(m Multiaddr) []Multiaddr {
addrs := make([]Multiaddr, len(split))
for i, addr := range split {
addrs[i] = &multiaddr{bytes: addr}
addrs[i] = multiaddr{bytes: addr}
}
return addrs
}
@ -34,7 +34,7 @@ func Join(ms ...Multiaddr) Multiaddr {
bidx++
}
}
return &multiaddr{bytes: b}
return multiaddr{bytes: b}
}
// Cast re-casts a byte slice as a multiaddr. will panic if it fails to parse.
@ -43,7 +43,7 @@ func Cast(b []byte) Multiaddr {
if err != nil {
panic(fmt.Errorf("multiaddr failed to parse: %s", err))
}
return &multiaddr{bytes: b}
return multiaddr{bytes: b}
}
// StringCast like Cast, but parses a string. Will also panic if it fails to parse.