package multiaddr import ( "bytes" "encoding/binary" "encoding/json" "fmt" "strings" "github.com/multiformats/go-varint" ) // Component is a single multiaddr Component. type Component struct { // bytes is the raw bytes of the component. It includes the protocol code as // varint, possibly the size of the value, and the value. bytes string // string for immutability. protocol *Protocol valueStartIdx int // Index of the first byte of the Component's value in the bytes array } func (c *Component) Multiaddr() Multiaddr { if c == nil { return nil } return []Component{*c} } func (c *Component) Encapsulate(o Multiaddrer) Multiaddr { return c.Multiaddr().Encapsulate(o) } func (c *Component) Decapsulate(o Multiaddrer) Multiaddr { return c.Multiaddr().Decapsulate(o) } func (c *Component) Bytes() []byte { if c == nil { return nil } return []byte(c.bytes) } func (c *Component) MarshalBinary() ([]byte, error) { if c == nil { return nil, errNilPtr } return c.Bytes(), nil } func (c *Component) UnmarshalBinary(data []byte) error { if c == nil { return errNilPtr } _, comp, err := readComponent(data) if err != nil { return err } *c = *comp return nil } func (c *Component) MarshalText() ([]byte, error) { if c == nil { return nil, errNilPtr } return []byte(c.String()), nil } func (c *Component) UnmarshalText(data []byte) error { if c == nil { return errNilPtr } 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) { if c == nil { return nil, errNilPtr } txt, err := c.MarshalText() if err != nil { return nil, err } return json.Marshal(string(txt)) } func (c *Component) UnmarshalJSON(data []byte) error { if c == nil { return errNilPtr } var v string if err := json.Unmarshal(data, &v); err != nil { return err } return c.UnmarshalText([]byte(v)) } func (c *Component) Equal(o *Component) bool { if c == nil || o == nil { return c == o } return c.bytes == o.bytes } func (c *Component) Compare(o *Component) int { if c == nil && o == nil { return 0 } if c == nil { return -1 } if o == nil { return 1 } return strings.Compare(c.bytes, o.bytes) } func (c *Component) Protocols() []Protocol { if c == nil { return nil } if c.protocol == nil { return nil } return []Protocol{*c.protocol} } func (c *Component) ValueForProtocol(code int) (string, error) { if c == nil { return "", fmt.Errorf("component is nil") } if c.protocol == nil { return "", fmt.Errorf("component has nil protocol") } if c.protocol.Code != code { return "", ErrProtocolNotFound } return c.Value(), nil } func (c *Component) Protocol() Protocol { if c == nil { return Protocol{} } if c.protocol == nil { return Protocol{} } return *c.protocol } func (c *Component) RawValue() []byte { if c == nil { return nil } return []byte(c.bytes[c.valueStartIdx:]) } func (c *Component) Value() string { if c == nil { return "" } // This Component MUST have been checked by validateComponent when created value, _ := c.valueAndErr() return value } func (c *Component) valueAndErr() (string, error) { if c == nil { return "", errNilPtr } if c.protocol == nil { return "", fmt.Errorf("component has nil protocol") } if c.protocol.Transcoder == nil { return "", nil } value, err := c.protocol.Transcoder.BytesToString([]byte(c.bytes[c.valueStartIdx:])) if err != nil { return "", err } return value, nil } func (c *Component) String() string { if c == nil { return "" } var b strings.Builder c.writeTo(&b) return b.String() } // writeTo is an efficient, private function for string-formatting a multiaddr. // Trust me, we tend to allocate a lot when doing this. func (c *Component) writeTo(b *strings.Builder) { if c == nil { return } if c.protocol == nil { return } b.WriteByte('/') b.WriteString(c.protocol.Name) value := c.Value() if len(value) == 0 { return } if !(c.protocol.Path && value[0] == '/') { b.WriteByte('/') } b.WriteString(value) } // NewComponent constructs a new multiaddr component func NewComponent(protocol, value string) (*Component, error) { p := ProtocolWithName(protocol) if p.Code == 0 { return nil, fmt.Errorf("unsupported protocol: %s", protocol) } if p.Transcoder != nil { bts, err := p.Transcoder.StringToBytes(value) if err != nil { return nil, err } return newComponent(p, bts) } else if value != "" { return nil, fmt.Errorf("protocol %s doesn't take a value", p.Name) } return newComponent(p, nil) } func newComponent(protocol Protocol, bvalue []byte) (*Component, error) { protocolPtr := protocolPtrByCode[protocol.Code] if protocolPtr == nil { protocolPtr = &protocol } size := len(bvalue) size += len(protocol.VCode) if protocol.Size < 0 { size += varint.UvarintSize(uint64(len(bvalue))) } maddr := make([]byte, size) var offset int offset += copy(maddr[offset:], protocol.VCode) if protocol.Size < 0 { offset += binary.PutUvarint(maddr[offset:], uint64(len(bvalue))) } copy(maddr[offset:], bvalue) // Shouldn't happen if len(maddr) != offset+len(bvalue) { return nil, fmt.Errorf("component size mismatch: %d != %d", len(maddr), offset+len(bvalue)) } c := &Component{ bytes: string(maddr), protocol: protocolPtr, valueStartIdx: offset, } err := validateComponent(c) if err != nil { return nil, err } return c, nil } // validateComponent MUST be called after creating a non-zero Component. // It ensures that we will be able to call all methods on Component without // error. func validateComponent(c *Component) error { if c == nil { return errNilPtr } if c.protocol == nil { return fmt.Errorf("component is missing its protocol") } if c.valueStartIdx > len(c.bytes) { return fmt.Errorf("component valueStartIdx is greater than the length of the component's bytes") } if len(c.protocol.VCode) == 0 { return fmt.Errorf("Component is missing its protocol's VCode field") } if len(c.bytes) < len(c.protocol.VCode) { return fmt.Errorf("component size mismatch: %d != %d", len(c.bytes), len(c.protocol.VCode)) } if !bytes.Equal([]byte(c.bytes[:len(c.protocol.VCode)]), c.protocol.VCode) { return fmt.Errorf("component's VCode field is invalid: %v != %v", []byte(c.bytes[:len(c.protocol.VCode)]), c.protocol.VCode) } if c.protocol.Size < 0 { size, n, err := ReadVarintCode([]byte(c.bytes[len(c.protocol.VCode):])) if err != nil { return err } if size != len(c.bytes[c.valueStartIdx:]) { return fmt.Errorf("component value size mismatch: %d != %d", size, len(c.bytes[c.valueStartIdx:])) } if len(c.protocol.VCode)+n+size != len(c.bytes) { return fmt.Errorf("component size mismatch: %d != %d", len(c.protocol.VCode)+n+size, len(c.bytes)) } } else { // Fixed size value size := c.protocol.Size / 8 if size != len(c.bytes[c.valueStartIdx:]) { return fmt.Errorf("component value size mismatch: %d != %d", size, len(c.bytes[c.valueStartIdx:])) } if len(c.protocol.VCode)+size != len(c.bytes) { return fmt.Errorf("component size mismatch: %d != %d", len(c.protocol.VCode)+size, len(c.bytes)) } } _, err := c.valueAndErr() if err != nil { return err } if c.protocol.Transcoder != nil { err = c.protocol.Transcoder.ValidateBytes([]byte(c.bytes[c.valueStartIdx:])) if err != nil { return err } } return nil }