blob: 80eaaa18921f46ef8cad31e261ed68890c174092 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package naming
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/hex"
"io"
"v.io/v23/verror"
)
var (
errInvalidString = verror.Register(pkgPath+".errInvalidString", verror.NoRetry, "{1:}{2:} string is of the wrong format and/or size{:_}")
errNotARoutingID = verror.Register(pkgPath+".errNotARoutingID", verror.NoRetry, "{1:}{2:} Not a RoutingID{:_}")
)
// RoutingIDs have one essential property, namely that they are, to a very
// high probability globally unique. Global uniqueness is required in order
// to support comparing Endpoints for equality; this is required for sharing
// connections, for proxying (though global uniqueness is not strictly
// required) and determining if different names resolve to the same endpoint.
type RoutingID struct {
value [routingIDLength]byte
}
const (
routingIDLength = 16
firstUnreservedRoutingID = 1024
)
var (
// NullRoutingID is a special value representing the nil route.
NullRoutingID = FixedRoutingID(0)
)
// FixedRoutingID returns a routing ID from a constant.
func FixedRoutingID(i uint64) RoutingID {
var rid RoutingID
binary.BigEndian.PutUint64(rid.value[8:16], i)
return rid
}
// IsReserved() returns true iff the RoutingID is in the reserved range.
func (rid RoutingID) IsReserved() bool {
return isZero(rid.value[0:14]) && isLessThan(rid.value[15:16], firstUnreservedRoutingID)
}
func isZero(buf []byte) bool {
for _, b := range buf {
if b != 0 {
return false
}
}
return true
}
func isLessThan(buf []byte, j uint16) bool {
return binary.BigEndian.Uint16(buf) < j
}
// String returns a print representation of the RoutingID.
func (rid RoutingID) String() string {
return hex.EncodeToString(rid.value[:])
}
// FromString reads an RoutingID from a hex encoded string. If the argument
// string is of zero length the RoutingID will be set to NullRoutingID
func (rid *RoutingID) FromString(s string) error {
if len(s) == 0 {
*rid = NullRoutingID
return nil
}
b, err := hex.DecodeString(s)
if err != nil {
return err
}
if len(b) != routingIDLength {
return verror.New(errInvalidString, nil)
}
copy(rid.value[:], b)
return nil
}
// Read a RoutingID from an io.Reader.
func ReadRoutingID(reader io.Reader) (RoutingID, error) {
var rid RoutingID
_, err := io.ReadFull(reader, rid.value[:])
return rid, err
}
// Write a RoutingID to an io.Writer.
func (rid RoutingID) Write(writer io.Writer) error {
_, err := writer.Write(rid.value[:])
return err
}
func NewRoutingID() (RoutingID, error) {
var rid RoutingID
for {
_, err := io.ReadFull(rand.Reader, rid.value[:])
if err != nil {
return NullRoutingID, err
}
if !rid.IsReserved() {
return rid, nil
}
}
}
func Compare(a, b RoutingID) bool {
return bytes.Compare(a.value[:], b.value[:]) == 0
}
// Implement EndpointOpt so that RoutingID can be passed as an optional
// argument to FormatEndpoint
func (RoutingID) EndpointOpt() {}