305 lines
6.5 KiB
Go
305 lines
6.5 KiB
Go
|
package capnp
|
||
|
|
||
|
// A Ptr is a reference to a Cap'n Proto struct, list, or interface.
|
||
|
// The zero value is a null pointer.
|
||
|
type Ptr struct {
|
||
|
seg *Segment
|
||
|
off Address
|
||
|
lenOrCap uint32
|
||
|
size ObjectSize
|
||
|
depthLimit uint
|
||
|
flags ptrFlags
|
||
|
}
|
||
|
|
||
|
func toPtr(p Pointer) Ptr {
|
||
|
if p == nil {
|
||
|
return Ptr{}
|
||
|
}
|
||
|
switch p := p.underlying().(type) {
|
||
|
case Struct:
|
||
|
return p.ToPtr()
|
||
|
case List:
|
||
|
return p.ToPtr()
|
||
|
case Interface:
|
||
|
return p.ToPtr()
|
||
|
}
|
||
|
return Ptr{}
|
||
|
}
|
||
|
|
||
|
// Struct converts p to a Struct. If p does not hold a Struct pointer,
|
||
|
// the zero value is returned.
|
||
|
func (p Ptr) Struct() Struct {
|
||
|
if p.flags.ptrType() != structPtrType {
|
||
|
return Struct{}
|
||
|
}
|
||
|
return Struct{
|
||
|
seg: p.seg,
|
||
|
off: p.off,
|
||
|
size: p.size,
|
||
|
flags: p.flags.structFlags(),
|
||
|
depthLimit: p.depthLimit,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// StructDefault attempts to convert p into a struct, reading the
|
||
|
// default value from def if p is not a struct.
|
||
|
func (p Ptr) StructDefault(def []byte) (Struct, error) {
|
||
|
s := p.Struct()
|
||
|
if s.seg == nil {
|
||
|
if def == nil {
|
||
|
return Struct{}, nil
|
||
|
}
|
||
|
defp, err := unmarshalDefault(def)
|
||
|
if err != nil {
|
||
|
return Struct{}, err
|
||
|
}
|
||
|
return defp.Struct(), nil
|
||
|
}
|
||
|
return s, nil
|
||
|
}
|
||
|
|
||
|
// List converts p to a List. If p does not hold a List pointer,
|
||
|
// the zero value is returned.
|
||
|
func (p Ptr) List() List {
|
||
|
if p.flags.ptrType() != listPtrType {
|
||
|
return List{}
|
||
|
}
|
||
|
return List{
|
||
|
seg: p.seg,
|
||
|
off: p.off,
|
||
|
length: int32(p.lenOrCap),
|
||
|
size: p.size,
|
||
|
flags: p.flags.listFlags(),
|
||
|
depthLimit: p.depthLimit,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ListDefault attempts to convert p into a list, reading the default
|
||
|
// value from def if p is not a list.
|
||
|
func (p Ptr) ListDefault(def []byte) (List, error) {
|
||
|
l := p.List()
|
||
|
if l.seg == nil {
|
||
|
if def == nil {
|
||
|
return List{}, nil
|
||
|
}
|
||
|
defp, err := unmarshalDefault(def)
|
||
|
if err != nil {
|
||
|
return List{}, err
|
||
|
}
|
||
|
return defp.List(), nil
|
||
|
}
|
||
|
return l, nil
|
||
|
}
|
||
|
|
||
|
// Interface converts p to an Interface. If p does not hold a List
|
||
|
// pointer, the zero value is returned.
|
||
|
func (p Ptr) Interface() Interface {
|
||
|
if p.flags.ptrType() != interfacePtrType {
|
||
|
return Interface{}
|
||
|
}
|
||
|
return Interface{
|
||
|
seg: p.seg,
|
||
|
cap: CapabilityID(p.lenOrCap),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Text attempts to convert p into Text, returning an empty string if
|
||
|
// p is not a valid 1-byte list pointer.
|
||
|
func (p Ptr) Text() string {
|
||
|
b, ok := p.text()
|
||
|
if !ok {
|
||
|
return ""
|
||
|
}
|
||
|
return string(b)
|
||
|
}
|
||
|
|
||
|
// TextDefault attempts to convert p into Text, returning def if p is
|
||
|
// not a valid 1-byte list pointer.
|
||
|
func (p Ptr) TextDefault(def string) string {
|
||
|
b, ok := p.text()
|
||
|
if !ok {
|
||
|
return def
|
||
|
}
|
||
|
return string(b)
|
||
|
}
|
||
|
|
||
|
// TextBytes attempts to convert p into Text, returning nil if p is not
|
||
|
// a valid 1-byte list pointer. It returns a slice directly into the
|
||
|
// segment.
|
||
|
func (p Ptr) TextBytes() []byte {
|
||
|
b, ok := p.text()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
// TextBytesDefault attempts to convert p into Text, returning def if p
|
||
|
// is not a valid 1-byte list pointer. It returns a slice directly into
|
||
|
// the segment.
|
||
|
func (p Ptr) TextBytesDefault(def string) []byte {
|
||
|
b, ok := p.text()
|
||
|
if !ok {
|
||
|
return []byte(def)
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func (p Ptr) text() (b []byte, ok bool) {
|
||
|
if !isOneByteList(p) {
|
||
|
return nil, false
|
||
|
}
|
||
|
l := p.List()
|
||
|
b = l.seg.slice(l.off, Size(l.length))
|
||
|
if len(b) == 0 || b[len(b)-1] != 0 {
|
||
|
// Text must be null-terminated.
|
||
|
return nil, false
|
||
|
}
|
||
|
return b[:len(b)-1 : len(b)], true
|
||
|
}
|
||
|
|
||
|
// Data attempts to convert p into Data, returning nil if p is not a
|
||
|
// valid 1-byte list pointer.
|
||
|
func (p Ptr) Data() []byte {
|
||
|
return p.DataDefault(nil)
|
||
|
}
|
||
|
|
||
|
// DataDefault attempts to convert p into Data, returning def if p is
|
||
|
// not a valid 1-byte list pointer.
|
||
|
func (p Ptr) DataDefault(def []byte) []byte {
|
||
|
if !isOneByteList(p) {
|
||
|
return def
|
||
|
}
|
||
|
l := p.List()
|
||
|
b := l.seg.slice(l.off, Size(l.length))
|
||
|
if b == nil {
|
||
|
return def
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func (p Ptr) toPointer() Pointer {
|
||
|
if p.seg == nil {
|
||
|
return nil
|
||
|
}
|
||
|
switch p.flags.ptrType() {
|
||
|
case structPtrType:
|
||
|
return p.Struct()
|
||
|
case listPtrType:
|
||
|
return p.List()
|
||
|
case interfacePtrType:
|
||
|
return p.Interface()
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// IsValid reports whether p is valid.
|
||
|
func (p Ptr) IsValid() bool {
|
||
|
return p.seg != nil
|
||
|
}
|
||
|
|
||
|
// Segment returns the segment this pointer points into.
|
||
|
// If nil, then this is an invalid pointer.
|
||
|
func (p Ptr) Segment() *Segment {
|
||
|
return p.seg
|
||
|
}
|
||
|
|
||
|
// Default returns p if it is valid, otherwise it unmarshals def.
|
||
|
func (p Ptr) Default(def []byte) (Ptr, error) {
|
||
|
if !p.IsValid() {
|
||
|
return unmarshalDefault(def)
|
||
|
}
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
// SamePtr reports whether p and q refer to the same object.
|
||
|
func SamePtr(p, q Ptr) bool {
|
||
|
return p.seg == q.seg && p.off == q.off
|
||
|
}
|
||
|
|
||
|
// A value that implements Pointer is a reference to a Cap'n Proto object.
|
||
|
//
|
||
|
// Deprecated: Using this type introduces an unnecessary allocation.
|
||
|
// Use Ptr instead.
|
||
|
type Pointer interface {
|
||
|
// Segment returns the segment this pointer points into.
|
||
|
// If nil, then this is an invalid pointer.
|
||
|
Segment() *Segment
|
||
|
|
||
|
// HasData reports whether the object referenced by the pointer has
|
||
|
// non-zero size.
|
||
|
HasData() bool
|
||
|
|
||
|
// underlying returns a Pointer that is one of a Struct, a List, or an
|
||
|
// Interface.
|
||
|
underlying() Pointer
|
||
|
}
|
||
|
|
||
|
// IsValid reports whether p is valid.
|
||
|
//
|
||
|
// Deprecated: Use Ptr.IsValid instead.
|
||
|
func IsValid(p Pointer) bool {
|
||
|
return p != nil && p.Segment() != nil
|
||
|
}
|
||
|
|
||
|
// HasData reports whether p has non-zero size.
|
||
|
//
|
||
|
// Deprecated: There are usually better ways to determine this
|
||
|
// information: length of a list, checking fields, or using HasFoo
|
||
|
// accessors.
|
||
|
func HasData(p Pointer) bool {
|
||
|
return IsValid(p) && p.HasData()
|
||
|
}
|
||
|
|
||
|
// PointerDefault returns p if it is valid, otherwise it unmarshals def.
|
||
|
//
|
||
|
// Deprecated: Use Ptr.Default.
|
||
|
func PointerDefault(p Pointer, def []byte) (Pointer, error) {
|
||
|
pp, err := toPtr(p).Default(def)
|
||
|
return pp.toPointer(), err
|
||
|
}
|
||
|
|
||
|
func unmarshalDefault(def []byte) (Ptr, error) {
|
||
|
msg, err := Unmarshal(def)
|
||
|
if err != nil {
|
||
|
return Ptr{}, err
|
||
|
}
|
||
|
p, err := msg.RootPtr()
|
||
|
if err != nil {
|
||
|
return Ptr{}, err
|
||
|
}
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
type ptrFlags uint8
|
||
|
|
||
|
const interfacePtrFlag ptrFlags = interfacePtrType << 6
|
||
|
|
||
|
func structPtrFlag(f structFlags) ptrFlags {
|
||
|
return structPtrType<<6 | ptrFlags(f)&ptrLowerMask
|
||
|
}
|
||
|
|
||
|
func listPtrFlag(f listFlags) ptrFlags {
|
||
|
return listPtrType<<6 | ptrFlags(f)&ptrLowerMask
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
structPtrType = iota
|
||
|
listPtrType
|
||
|
interfacePtrType
|
||
|
)
|
||
|
|
||
|
func (f ptrFlags) ptrType() int {
|
||
|
return int(f >> 6)
|
||
|
}
|
||
|
|
||
|
const ptrLowerMask ptrFlags = 0x3f
|
||
|
|
||
|
func (f ptrFlags) listFlags() listFlags {
|
||
|
return listFlags(f & ptrLowerMask)
|
||
|
}
|
||
|
|
||
|
func (f ptrFlags) structFlags() structFlags {
|
||
|
return structFlags(f & ptrLowerMask)
|
||
|
}
|