From ece734f18d7ebc37e135c8e04842f6b0fe4d74d0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 22 Jun 2018 18:32:03 -0700 Subject: [PATCH 1/4] remove redundant case --- codec.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/codec.go b/codec.go index d95a0da..1f912f8 100644 --- a/codec.go +++ b/codec.go @@ -155,12 +155,6 @@ func sizeForAddr(p Protocol, b []byte) (skip, size int, err error) { return 0, (p.Size / 8), nil case p.Size == 0: return 0, 0, nil - case p.Path: - size, n, err := ReadVarintCode(b) - if err != nil { - return 0, 0, err - } - return n, size, nil default: size, n, err := ReadVarintCode(b) if err != nil { From 47e3ab13b22b77c4c9221698be44c1df5fafce45 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 22 Jun 2018 18:34:55 -0700 Subject: [PATCH 2/4] validate protocol definition in add protocol --- protocols.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/protocols.go b/protocols.go index 175c5fe..5fe65b5 100644 --- a/protocols.go +++ b/protocols.go @@ -120,6 +120,12 @@ func SwapToP2pMultiaddrs() { } func AddProtocol(p Protocol) error { + if p.Size != 0 && p.Transcoder == nil { + return fmt.Errorf("protocols with arguments must define transcoders") + } + if p.Path && p.Size >= 0 { + return fmt.Errorf("path protocols must have variable-length sizes") + } for _, pt := range Protocols { if pt.Code == p.Code { return fmt.Errorf("protocol code %d already taken by %q", p.Code, pt.Name) From 668837645262f6fec895ebfa3db3c957ac2144a4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 22 Jun 2018 18:39:50 -0700 Subject: [PATCH 3/4] unexport ProtocolsByName and add protocolsByCode --- protocols.go | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/protocols.go b/protocols.go index 5fe65b5..64606e5 100644 --- a/protocols.go +++ b/protocols.go @@ -81,16 +81,18 @@ var ( protoUNIX = Protocol{P_UNIX, LengthPrefixedVarSize, "unix", CodeToVarint(P_UNIX), true, TranscoderUnix} ) -var ProtocolsByName = map[string]Protocol{} +var protocolsByName = map[string]Protocol{} +var protocolsByCode = map[int]Protocol{} func init() { for _, p := range Protocols { - ProtocolsByName[p.Name] = p + protocolsByName[p.Name] = p + protocolsByCode[p.Code] = p } // explicitly set both of these - ProtocolsByName["p2p"] = protoP2P - ProtocolsByName["ipfs"] = protoP2P + protocolsByName["p2p"] = protoP2P + protocolsByName["ipfs"] = protoP2P } // SwapToP2pMultiaddrs is a function to make the transition from /ipfs/... @@ -115,44 +117,41 @@ func SwapToP2pMultiaddrs() { protoP2P.Name = "p2p" - ProtocolsByName["ipfs"] = protoP2P - ProtocolsByName["p2p"] = protoP2P + protocolsByName["ipfs"] = protoP2P + protocolsByName["p2p"] = protoP2P + protocolsByCode[protoP2P.Code] = protoP2P } func AddProtocol(p Protocol) error { + if _, ok := protocolsByName[p.Name]; ok { + return fmt.Errorf("protocol by the name %q already exists", p.Name) + } + + if _, ok := protocolsByCode[p.Code]; ok { + return fmt.Errorf("protocol code %d already taken by %q", p.Code, p.Code) + } + if p.Size != 0 && p.Transcoder == nil { return fmt.Errorf("protocols with arguments must define transcoders") } if p.Path && p.Size >= 0 { return fmt.Errorf("path protocols must have variable-length sizes") } - for _, pt := range Protocols { - if pt.Code == p.Code { - return fmt.Errorf("protocol code %d already taken by %q", p.Code, pt.Name) - } - if pt.Name == p.Name { - return fmt.Errorf("protocol by the name %q already exists", p.Name) - } - } Protocols = append(Protocols, p) - ProtocolsByName[p.Name] = p + protocolsByName[p.Name] = p + protocolsByCode[p.Code] = p return nil } // ProtocolWithName returns the Protocol description with given string name. func ProtocolWithName(s string) Protocol { - return ProtocolsByName[s] + return protocolsByName[s] } // ProtocolWithCode returns the Protocol description with given protocol code. func ProtocolWithCode(c int) Protocol { - for _, p := range Protocols { - if p.Code == c { - return p - } - } - return Protocol{} + return protocolsByCode[c] } // ProtocolsWithString returns a slice of protocols matching given string. From 1ae13209557ac03cb1ee628d3f3550b46b2f4f88 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Jul 2018 09:09:29 -0700 Subject: [PATCH 4/4] refactor the protocol definition files Also use `AddProtocol` for *all* protocols (even predefined ones). --- protocol.go | 129 ++++++++++++++++++++++++ protocols.go | 276 +++++++++++++++++++++------------------------------ varint.go | 38 +++++++ 3 files changed, 279 insertions(+), 164 deletions(-) create mode 100644 protocol.go create mode 100644 varint.go diff --git a/protocol.go b/protocol.go new file mode 100644 index 0000000..f9d7036 --- /dev/null +++ b/protocol.go @@ -0,0 +1,129 @@ +package multiaddr + +import ( + "fmt" + "strings" +) + +// These are special sizes +const ( + LengthPrefixedVarSize = -1 +) + +// Protocol is a Multiaddr protocol description structure. +type Protocol struct { + // Name is the string representation of the protocol code. E.g., ip4, + // ip6, tcp, udp, etc. + Name string + + // Code is the protocol's multicodec (a normal, non-varint number). + Code int + + // VCode is a precomputed varint encoded version of Code. + VCode []byte + + // Size is the size of the argument to this protocol. + // + // * Size == 0 means this protocol takes no argument. + // * Size > 0 means this protocol takes a constant sized argument. + // * Size < 0 means this protocol takes a variable length, varint + // prefixed argument. + Size int // a size of -1 indicates a length-prefixed variable size + + // Path indicates a path protocol (e.g., unix). When parsing multiaddr + // strings, path protocols consume the remainder of the address instead + // of stopping at the next forward slash. + // + // Size must be LengthPrefixedVarSize. + Path bool + + // Transcoder converts between the byte representation and the string + // representation of this protocol's argument (if any). + // + // This should only be non-nil if Size != 0 + Transcoder Transcoder +} + +var protocolsByName = map[string]Protocol{} +var protocolsByCode = map[int]Protocol{} + +// Protocols is the list of multiaddr protocols supported by this module. +var Protocols = []Protocol{} + +// SwapToP2pMultiaddrs is a function to make the transition from /ipfs/... +// multiaddrs to /p2p/... multiaddrs easier +// The first stage of the rollout is to ship this package to all users so +// that all users of multiaddr can parse both /ipfs/ and /p2p/ multiaddrs +// as the same code (P_P2P). During this stage of the rollout, all addresses +// with P_P2P will continue printing as /ipfs/, so that older clients without +// the new parsing code won't break. +// Once the network has adopted the new parsing code broadly enough, users of +// multiaddr can add a call to this method to an init function in their codebase. +// This will cause any P_P2P multiaddr to print out as /p2p/ instead of /ipfs/. +// Note that the binary serialization of this multiaddr does not change at any +// point. This means that this code is not a breaking network change at any point +func SwapToP2pMultiaddrs() { + for i := range Protocols { + if Protocols[i].Code == P_P2P { + Protocols[i].Name = "p2p" + break + } + } + + protoP2P.Name = "p2p" + + protocolsByName["ipfs"] = protoP2P + protocolsByName["p2p"] = protoP2P + protocolsByCode[protoP2P.Code] = protoP2P +} + +func AddProtocol(p Protocol) error { + if _, ok := protocolsByName[p.Name]; ok { + return fmt.Errorf("protocol by the name %q already exists", p.Name) + } + + if _, ok := protocolsByCode[p.Code]; ok { + return fmt.Errorf("protocol code %d already taken by %q", p.Code, p.Code) + } + + if p.Size != 0 && p.Transcoder == nil { + return fmt.Errorf("protocols with arguments must define transcoders") + } + if p.Path && p.Size >= 0 { + return fmt.Errorf("path protocols must have variable-length sizes") + } + + Protocols = append(Protocols, p) + protocolsByName[p.Name] = p + protocolsByCode[p.Code] = p + return nil +} + +// ProtocolWithName returns the Protocol description with given string name. +func ProtocolWithName(s string) Protocol { + return protocolsByName[s] +} + +// ProtocolWithCode returns the Protocol description with given protocol code. +func ProtocolWithCode(c int) Protocol { + return protocolsByCode[c] +} + +// ProtocolsWithString returns a slice of protocols matching given string. +func ProtocolsWithString(s string) ([]Protocol, error) { + s = strings.Trim(s, "/") + sp := strings.Split(s, "/") + if len(sp) == 0 { + return nil, nil + } + + t := make([]Protocol, len(sp)) + for i, name := range sp { + p := ProtocolWithName(name) + if p.Code == 0 { + return nil, fmt.Errorf("no protocol with name: %s", name) + } + t[i] = p + } + return t, nil +} diff --git a/protocols.go b/protocols.go index 64606e5..bc123a7 100644 --- a/protocols.go +++ b/protocols.go @@ -1,22 +1,5 @@ package multiaddr -import ( - "encoding/binary" - "fmt" - "math/bits" - "strings" -) - -// Protocol is a Multiaddr protocol description structure. -type Protocol struct { - Code int - Size int // a size of -1 indicates a length-prefixed variable size - Name string - VCode []byte - Path bool // indicates a path protocol (eg unix, http) - Transcoder Transcoder -} - // You **MUST** register your multicodecs with // https://github.com/multiformats/multicodec before adding them here. // @@ -40,161 +23,126 @@ const ( P_ONION = 0x01BC ) -// These are special sizes -const ( - LengthPrefixedVarSize = -1 -) - -// Protocols is the list of multiaddr protocols supported by this module. -var Protocols = []Protocol{ - protoIP4, - protoTCP, - protoUDP, - protoDCCP, - protoIP6, - protoSCTP, - protoONION, - protoUTP, - protoUDT, - protoQUIC, - protoHTTP, - protoHTTPS, - protoP2P, - protoUNIX, -} - var ( - protoIP4 = Protocol{P_IP4, 32, "ip4", CodeToVarint(P_IP4), false, TranscoderIP4} - protoTCP = Protocol{P_TCP, 16, "tcp", CodeToVarint(P_TCP), false, TranscoderPort} - protoUDP = Protocol{P_UDP, 16, "udp", CodeToVarint(P_UDP), false, TranscoderPort} - protoDCCP = Protocol{P_DCCP, 16, "dccp", CodeToVarint(P_DCCP), false, TranscoderPort} - protoIP6 = Protocol{P_IP6, 128, "ip6", CodeToVarint(P_IP6), false, TranscoderIP6} + protoIP4 = Protocol{ + Name: "ip4", + Code: P_IP4, + VCode: CodeToVarint(P_IP4), + Size: 32, + Path: false, + Transcoder: TranscoderIP4, + } + protoTCP = Protocol{ + Name: "tcp", + Code: P_TCP, + VCode: CodeToVarint(P_TCP), + Size: 16, + Path: false, + Transcoder: TranscoderPort, + } + protoUDP = Protocol{ + Name: "udp", + Code: P_UDP, + VCode: CodeToVarint(P_UDP), + Size: 16, + Path: false, + Transcoder: TranscoderPort, + } + protoDCCP = Protocol{ + Name: "dccp", + Code: P_DCCP, + VCode: CodeToVarint(P_DCCP), + Size: 16, + Path: false, + Transcoder: TranscoderPort, + } + protoIP6 = Protocol{ + Name: "ip6", + Code: P_IP6, + VCode: CodeToVarint(P_IP6), + Size: 128, + Transcoder: TranscoderIP6, + } // these require varint - protoSCTP = Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP), false, TranscoderPort} - protoONION = Protocol{P_ONION, 96, "onion", CodeToVarint(P_ONION), false, TranscoderOnion} - protoUTP = Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP), false, nil} - protoUDT = Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT), false, nil} - protoQUIC = Protocol{P_QUIC, 0, "quic", CodeToVarint(P_QUIC), false, nil} - protoHTTP = Protocol{P_HTTP, 0, "http", CodeToVarint(P_HTTP), false, nil} - protoHTTPS = Protocol{P_HTTPS, 0, "https", CodeToVarint(P_HTTPS), false, nil} - protoP2P = Protocol{P_P2P, LengthPrefixedVarSize, "ipfs", CodeToVarint(P_P2P), false, TranscoderP2P} - protoUNIX = Protocol{P_UNIX, LengthPrefixedVarSize, "unix", CodeToVarint(P_UNIX), true, TranscoderUnix} + protoSCTP = Protocol{ + Name: "sctp", + Code: P_SCTP, + VCode: CodeToVarint(P_SCTP), + Size: 16, + Transcoder: TranscoderPort, + } + protoONION = Protocol{ + Name: "onion", + Code: P_ONION, + VCode: CodeToVarint(P_ONION), + Size: 96, + Transcoder: TranscoderOnion, + } + protoUTP = Protocol{ + Name: "utp", + Code: P_UTP, + VCode: CodeToVarint(P_UTP), + } + protoUDT = Protocol{ + Name: "udt", + Code: P_UDT, + VCode: CodeToVarint(P_UDT), + } + protoQUIC = Protocol{ + Name: "quic", + Code: P_QUIC, + VCode: CodeToVarint(P_QUIC), + } + protoHTTP = Protocol{ + Name: "http", + Code: P_HTTP, + VCode: CodeToVarint(P_HTTP), + } + protoHTTPS = Protocol{ + Name: "https", + Code: P_HTTPS, + VCode: CodeToVarint(P_HTTPS), + } + protoP2P = Protocol{ + Name: "ipfs", + Code: P_P2P, + VCode: CodeToVarint(P_P2P), + Size: LengthPrefixedVarSize, + Transcoder: TranscoderP2P, + } + protoUNIX = Protocol{ + Name: "unix", + Code: P_UNIX, + VCode: CodeToVarint(P_UNIX), + Size: LengthPrefixedVarSize, + Path: true, + Transcoder: TranscoderUnix, + } ) -var protocolsByName = map[string]Protocol{} -var protocolsByCode = map[int]Protocol{} - func init() { - for _, p := range Protocols { - protocolsByName[p.Name] = p - protocolsByCode[p.Code] = p + for _, p := range []Protocol{ + protoIP4, + protoTCP, + protoUDP, + protoDCCP, + protoIP6, + protoSCTP, + protoONION, + protoUTP, + protoUDT, + protoQUIC, + protoHTTP, + protoHTTPS, + protoP2P, + protoUNIX, + } { + if err := AddProtocol(p); err != nil { + panic(err) + } } // explicitly set both of these protocolsByName["p2p"] = protoP2P protocolsByName["ipfs"] = protoP2P } - -// SwapToP2pMultiaddrs is a function to make the transition from /ipfs/... -// multiaddrs to /p2p/... multiaddrs easier -// The first stage of the rollout is to ship this package to all users so -// that all users of multiaddr can parse both /ipfs/ and /p2p/ multiaddrs -// as the same code (P_P2P). During this stage of the rollout, all addresses -// with P_P2P will continue printing as /ipfs/, so that older clients without -// the new parsing code won't break. -// Once the network has adopted the new parsing code broadly enough, users of -// multiaddr can add a call to this method to an init function in their codebase. -// This will cause any P_P2P multiaddr to print out as /p2p/ instead of /ipfs/. -// Note that the binary serialization of this multiaddr does not change at any -// point. This means that this code is not a breaking network change at any point -func SwapToP2pMultiaddrs() { - for i := range Protocols { - if Protocols[i].Code == P_P2P { - Protocols[i].Name = "p2p" - break - } - } - - protoP2P.Name = "p2p" - - protocolsByName["ipfs"] = protoP2P - protocolsByName["p2p"] = protoP2P - protocolsByCode[protoP2P.Code] = protoP2P -} - -func AddProtocol(p Protocol) error { - if _, ok := protocolsByName[p.Name]; ok { - return fmt.Errorf("protocol by the name %q already exists", p.Name) - } - - if _, ok := protocolsByCode[p.Code]; ok { - return fmt.Errorf("protocol code %d already taken by %q", p.Code, p.Code) - } - - if p.Size != 0 && p.Transcoder == nil { - return fmt.Errorf("protocols with arguments must define transcoders") - } - if p.Path && p.Size >= 0 { - return fmt.Errorf("path protocols must have variable-length sizes") - } - - Protocols = append(Protocols, p) - protocolsByName[p.Name] = p - protocolsByCode[p.Code] = p - return nil -} - -// ProtocolWithName returns the Protocol description with given string name. -func ProtocolWithName(s string) Protocol { - return protocolsByName[s] -} - -// ProtocolWithCode returns the Protocol description with given protocol code. -func ProtocolWithCode(c int) Protocol { - return protocolsByCode[c] -} - -// ProtocolsWithString returns a slice of protocols matching given string. -func ProtocolsWithString(s string) ([]Protocol, error) { - s = strings.Trim(s, "/") - sp := strings.Split(s, "/") - if len(sp) == 0 { - return nil, nil - } - - t := make([]Protocol, len(sp)) - for i, name := range sp { - p := ProtocolWithName(name) - if p.Code == 0 { - return nil, fmt.Errorf("no protocol with name: %s", name) - } - t[i] = p - } - return t, nil -} - -// CodeToVarint converts an integer to a varint-encoded []byte -func CodeToVarint(num int) []byte { - buf := make([]byte, bits.Len(uint(num))/7+1) - n := binary.PutUvarint(buf, uint64(num)) - return buf[:n] -} - -// VarintToCode converts a varint-encoded []byte to an integer protocol code -func VarintToCode(buf []byte) int { - num, _, err := ReadVarintCode(buf) - if err != nil { - panic(err) - } - return num -} - -// ReadVarintCode reads a varint code from the beginning of buf. -// returns the code, and the number of bytes read. -func ReadVarintCode(buf []byte) (int, int, error) { - num, n := binary.Uvarint(buf) - if n < 0 { - return 0, 0, fmt.Errorf("varints larger than uint64 not yet supported") - } - return int(num), n, nil -} diff --git a/varint.go b/varint.go new file mode 100644 index 0000000..79ebe2f --- /dev/null +++ b/varint.go @@ -0,0 +1,38 @@ +package multiaddr + +import ( + "encoding/binary" + "fmt" + "math/bits" +) + +// VarintSize returns the size (in bytes) of `num` encoded as a varint. +func VarintSize(num int) int { + return bits.Len(uint(num))/7 + 1 +} + +// CodeToVarint converts an integer to a varint-encoded []byte +func CodeToVarint(num int) []byte { + buf := make([]byte, bits.Len(uint(num))/7+1) + n := binary.PutUvarint(buf, uint64(num)) + return buf[:n] +} + +// VarintToCode converts a varint-encoded []byte to an integer protocol code +func VarintToCode(buf []byte) int { + num, _, err := ReadVarintCode(buf) + if err != nil { + panic(err) + } + return num +} + +// ReadVarintCode reads a varint code from the beginning of buf. +// returns the code, and the number of bytes read. +func ReadVarintCode(buf []byte) (int, int, error) { + num, n := binary.Uvarint(buf) + if n < 0 { + return 0, 0, fmt.Errorf("varints larger than uint64 not yet supported") + } + return int(num), n, nil +}