package multiaddr
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"strings"
"github.com/multiformats/go-varint"
)
type Component struct {
bytes string
protocol *Protocol
valueStartIdx int
}
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 ) Code () int {
if c == nil {
return 0
}
return c .Protocol ().Code
}
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 ""
}
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 "<nil component>"
}
var b strings .Builder
c .writeTo (&b )
return b .String ()
}
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 )
}
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 )
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
}
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 {
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .