multiaddr.go 5.52 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3 4
package multiaddr

import (
	"bytes"
5
	"encoding/json"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"fmt"
Jeromy's avatar
Jeromy committed
7
	"log"
tavit ohanian's avatar
tavit ohanian committed
8 9

	"golang.org/x/exp/slices"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11 12 13 14 15 16 17
)

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

// NewMultiaddr parses and validates an input string, returning a *Multiaddr
Jeromy's avatar
Jeromy committed
18 19 20 21 22 23 24
func NewMultiaddr(s string) (a Multiaddr, err error) {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Panic in NewMultiaddr on input %q: %s", s, e)
			err = fmt.Errorf("%v", e)
		}
	}()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25 26 27 28
	b, err := stringToBytes(s)
	if err != nil {
		return nil, err
	}
29
	return &multiaddr{bytes: b}, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30 31 32 33
}

// NewMultiaddrBytes initializes a Multiaddr from a byte representation.
// It validates it as an input string.
Jeromy's avatar
Jeromy committed
34 35 36 37 38 39 40 41
func NewMultiaddrBytes(b []byte) (a Multiaddr, err error) {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Panic in NewMultiaddrBytes on input %q: %s", b, e)
			err = fmt.Errorf("%v", e)
		}
	}()

42
	if err := validateBytes(b); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
43 44
		return nil, err
	}
45

46
	return &multiaddr{bytes: b}, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
47 48 49
}

// Equal tests whether two multiaddrs are equal
50
func (m *multiaddr) Equal(m2 Multiaddr) bool {
tavit ohanian's avatar
tavit ohanian committed
51 52 53
	if m2 == nil {
		return false
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54 55 56 57
	return bytes.Equal(m.bytes, m2.Bytes())
}

// Bytes returns the []byte representation of this Multiaddr
58 59
//
// Do not modify the returned buffer, it may be shared.
60
func (m *multiaddr) Bytes() []byte {
61
	return m.bytes
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62 63 64
}

// String returns the string representation of a Multiaddr
65
func (m *multiaddr) String() string {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66 67
	s, err := bytesToString(m.bytes)
	if err != nil {
68
		panic(fmt.Errorf("multiaddr failed to convert back to string. corrupted? %s", err))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70 71 72
	}
	return s
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
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
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
113 114
// Protocols returns the list of protocols this Multiaddr has.
// will panic in case we access bytes incorrectly.
115
func (m *multiaddr) Protocols() []Protocol {
Steven Allen's avatar
Steven Allen committed
116
	ps := make([]Protocol, 0, 8)
117
	b := m.bytes
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118
	for len(b) > 0 {
119 120 121 122 123
		code, n, err := ReadVarintCode(b)
		if err != nil {
			panic(err)
		}

124
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125
		if p.Code == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126 127 128 129 130
			// 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)
131 132
		b = b[n:]

133
		n, size, err := sizeForAddr(p, b)
Jeromy's avatar
Jeromy committed
134 135 136 137
		if err != nil {
			panic(err)
		}

138
		b = b[n+size:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
139 140 141 142 143
	}
	return ps
}

// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
144
func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr {
tavit ohanian's avatar
tavit ohanian committed
145 146 147 148
	if o == nil {
		return m
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149 150 151
	mb := m.bytes
	ob := o.Bytes()

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
152 153 154
	b := make([]byte, len(mb)+len(ob))
	copy(b, mb)
	copy(b[len(mb):], ob)
155
	return &multiaddr{bytes: b}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
156 157 158
}

// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
tavit ohanian's avatar
tavit ohanian committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
func (m *multiaddr) Decapsulate(right Multiaddr) Multiaddr {
	if right == nil {
		return m
	}

	leftParts := Split(m)
	rightParts := Split(right)

	lastIndex := -1
	for i := range leftParts {
		foundMatch := false
		for j, rightC := range rightParts {
			if len(leftParts) <= i+j {
				foundMatch = false
				break
			}

			foundMatch = rightC.Equal(leftParts[i+j])
			if !foundMatch {
				break
			}
		}

		if foundMatch {
			lastIndex = i
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
185 186
	}

tavit ohanian's avatar
tavit ohanian committed
187
	if lastIndex == 0 {
Steven Allen's avatar
Steven Allen committed
188 189 190
		return nil
	}

tavit ohanian's avatar
tavit ohanian committed
191 192 193 194 195
	if lastIndex < 0 {
		// if multiaddr not contained, returns a copy.
		cpy := make([]byte, len(m.bytes))
		copy(cpy, m.bytes)
		return &multiaddr{bytes: cpy}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
196
	}
tavit ohanian's avatar
tavit ohanian committed
197 198

	return Join(leftParts[:lastIndex]...)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199
}
200 201 202

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

203
func (m *multiaddr) ValueForProtocol(code int) (value string, err error) {
Steven Allen's avatar
Steven Allen committed
204 205 206 207 208 209
	err = ErrProtocolNotFound
	ForEach(m, func(c Component) bool {
		if c.Protocol().Code == code {
			value = c.Value()
			err = nil
			return false
210
		}
Steven Allen's avatar
Steven Allen committed
211 212 213
		return true
	})
	return
214
}
tavit ohanian's avatar
tavit ohanian committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

// FilterAddrs is a filter that removes certain addresses, according to the given filters.
// If all filters return true, the address is kept.
func FilterAddrs(a []Multiaddr, filters ...func(Multiaddr) bool) []Multiaddr {
	b := make([]Multiaddr, 0, len(a))
addrloop:
	for _, addr := range a {
		for _, filter := range filters {
			if !filter(addr) {
				continue addrloop
			}
		}
		b = append(b, addr)
	}
	return b
}

// Contains reports whether addr is contained in addrs.
func Contains(addrs []Multiaddr, addr Multiaddr) bool {
	for _, a := range addrs {
		if addr.Equal(a) {
			return true
		}
	}
	return false
}

// Unique deduplicates addresses in place, leave only unique addresses.
// It doesn't allocate.
func Unique(addrs []Multiaddr) []Multiaddr {
	if len(addrs) == 0 {
		return addrs
	}
	// Use the new slices package here, as the sort function doesn't allocate (sort.Slice does).
	slices.SortFunc(addrs, func(a, b Multiaddr) int { return bytes.Compare(a.Bytes(), b.Bytes()) })
	idx := 1
	for i := 1; i < len(addrs); i++ {
		if !addrs[i-1].Equal(addrs[i]) {
			addrs[idx] = addrs[i]
			idx++
		}
	}
	for i := idx; i < len(addrs); i++ {
		addrs[i] = nil
	}
	return addrs[:idx]
}