multiaddr.go 3.18 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
package multiaddr

import (
	"bytes"
	"fmt"
	"strings"
)

// multiaddr is the data structure representing a Multiaddr
type multiaddr struct {
	bytes []byte
}

// NewMultiaddr parses and validates an input string, returning a *Multiaddr
func NewMultiaddr(s string) (Multiaddr, error) {
	b, err := stringToBytes(s)
	if err != nil {
		return nil, err
	}
	return &multiaddr{bytes: b}, nil
}

// NewMultiaddrBytes initializes a Multiaddr from a byte representation.
// It validates it as an input string.
func NewMultiaddrBytes(b []byte) (Multiaddr, error) {
	s, err := bytesToString(b)
	if err != nil {
		return nil, err
	}
	return NewMultiaddr(s)
}

// Equal tests whether two multiaddrs are equal
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
}

// String returns the string representation of a Multiaddr
func (m *multiaddr) String() string {
	s, err := bytesToString(m.bytes)
	if err != nil {
		panic("multiaddr failed to convert back to string. corrupted?")
	}
	return s
}

// Protocols returns the list of protocols this Multiaddr has.
// will panic in case we access bytes incorrectly.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57
func (m *multiaddr) Protocols() []Protocol {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58 59 60 61 62 63 64 65 66

	// panic handler, in case we try accessing bytes incorrectly.
	defer func() {
		if e := recover(); e != nil {
			err := e.(error)
			panic("Multiaddr.Protocols error: " + err.Error())
		}
	}()

67
	size := 0
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
68
	ps := []Protocol{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70
	b := m.bytes[:]
	for len(b) > 0 {
71 72
		code, n := ReadVarintCode(b)
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
73
		if p.Code == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
74 75 76 77 78
			// this is a panic (and not returning err) because this should've been
			// caught on constructing the Multiaddr
			panic(fmt.Errorf("no protocol with code %d", b[0]))
		}
		ps = append(ps, p)
79 80 81 82
		b = b[n:]

		size = sizeForAddr(p, b)
		b = b[size:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
83 84 85 86 87 88 89 90 91
	}
	return ps
}

// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr {
	mb := m.bytes
	ob := o.Bytes()

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
92 93 94 95
	b := make([]byte, len(mb)+len(ob))
	copy(b, mb)
	copy(b[len(mb):], ob)
	return &multiaddr{bytes: b}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
}

// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr {
	s1 := m.String()
	s2 := o.String()
	i := strings.LastIndex(s1, s2)
	if i < 0 {
		// if multiaddr not contained, returns a copy.
		cpy := make([]byte, len(m.bytes))
		copy(cpy, m.bytes)
		return &multiaddr{bytes: cpy}
	}

	ma, err := NewMultiaddr(s1[:i])
	if err != nil {
		panic("Multiaddr.Decapsulate incorrect byte boundaries.")
	}
	return ma
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr")

func (m *multiaddr) ValueForProtocol(code int) (string, error) {
	protos := m.Protocols()
	found := -1
	index := 2

	for i, p := range protos {
		if code == p.Code {
			if p.Size == 0 {
				return "", nil
			}
			found = i
			break
		} else {
			index += 2
			if p.Size == 0 {
				index--
			}
		}
	}

	if found == -1 {
		return "", ErrProtocolNotFound
	}

	return strings.Split(m.String(), "/")[index], nil
}