cloudflared-mirror/vendor/zombiezen.com/go/capnproto2/mem.go

914 lines
23 KiB
Go

package capnp
import (
"bufio"
"encoding/binary"
"errors"
"fmt"
"io"
"sync"
"zombiezen.com/go/capnproto2/internal/packed"
)
// Security limits. Matches C++ implementation.
const (
defaultTraverseLimit = 64 << 20 // 64 MiB
defaultDepthLimit = 64
maxStreamSegments = 512
defaultDecodeLimit = 64 << 20 // 64 MiB
)
const maxDepth = ^uint(0)
// A Message is a tree of Cap'n Proto objects, split into one or more
// segments of contiguous memory. The only required field is Arena.
// A Message is safe to read from multiple goroutines.
type Message struct {
// rlimit must be first so that it is 64-bit aligned.
// See sync/atomic docs.
rlimit ReadLimiter
rlimitInit sync.Once
Arena Arena
// CapTable is the indexed list of the clients referenced in the
// message. Capability pointers inside the message will use this table
// to map pointers to Clients. The table is usually populated by the
// RPC system.
//
// See https://capnproto.org/encoding.html#capabilities-interfaces for
// more details on the capability table.
CapTable []Client
// TraverseLimit limits how many total bytes of data are allowed to be
// traversed while reading. Traversal is counted when a Struct or
// List is obtained. This means that calling a getter for the same
// sub-struct multiple times will cause it to be double-counted. Once
// the traversal limit is reached, pointer accessors will report
// errors. See https://capnproto.org/encoding.html#amplification-attack
// for more details on this security measure.
//
// If not set, this defaults to 64 MiB.
TraverseLimit uint64
// DepthLimit limits how deeply-nested a message structure can be.
// If not set, this defaults to 64.
DepthLimit uint
// mu protects the following fields:
mu sync.Mutex
segs map[SegmentID]*Segment
firstSeg Segment // Preallocated first segment. msg is non-nil once initialized.
}
// NewMessage creates a message with a new root and returns the first
// segment. It is an error to call NewMessage on an arena with data in it.
func NewMessage(arena Arena) (msg *Message, first *Segment, err error) {
msg = &Message{Arena: arena}
switch arena.NumSegments() {
case 0:
first, err = msg.allocSegment(wordSize)
if err != nil {
return nil, nil, err
}
case 1:
first, err = msg.Segment(0)
if err != nil {
return nil, nil, err
}
if len(first.data) > 0 {
return nil, nil, errHasData
}
default:
return nil, nil, errHasData
}
if first.ID() != 0 {
return nil, nil, errors.New("capnp: arena allocated first segment with non-zero ID")
}
seg, _, err := alloc(first, wordSize) // allocate root
if err != nil {
return nil, nil, err
}
if seg != first {
return nil, nil, errors.New("capnp: arena didn't allocate first word in first segment")
}
return msg, first, nil
}
// Reset resets a message to use a different arena, allowing a single
// Message to be reused for reading multiple messages. This invalidates
// any existing pointers in the Message, so use with caution.
func (m *Message) Reset(arena Arena) {
m.mu.Lock()
m.Arena = arena
m.CapTable = nil
m.segs = nil
m.firstSeg = Segment{}
m.mu.Unlock()
if m.TraverseLimit == 0 {
m.ReadLimiter().Reset(defaultTraverseLimit)
} else {
m.ReadLimiter().Reset(m.TraverseLimit)
}
}
// Root returns the pointer to the message's root object.
//
// Deprecated: Use RootPtr.
func (m *Message) Root() (Pointer, error) {
p, err := m.RootPtr()
return p.toPointer(), err
}
// RootPtr returns the pointer to the message's root object.
func (m *Message) RootPtr() (Ptr, error) {
s, err := m.Segment(0)
if err != nil {
return Ptr{}, err
}
return s.root().PtrAt(0)
}
// SetRoot sets the message's root object to p.
//
// Deprecated: Use SetRootPtr.
func (m *Message) SetRoot(p Pointer) error {
return m.SetRootPtr(toPtr(p))
}
// SetRootPtr sets the message's root object to p.
func (m *Message) SetRootPtr(p Ptr) error {
s, err := m.Segment(0)
if err != nil {
return err
}
return s.root().SetPtr(0, p)
}
// AddCap appends a capability to the message's capability table and
// returns its ID.
func (m *Message) AddCap(c Client) CapabilityID {
n := CapabilityID(len(m.CapTable))
m.CapTable = append(m.CapTable, c)
return n
}
// ReadLimiter returns the message's read limiter. Useful if you want
// to reset the traversal limit while reading.
func (m *Message) ReadLimiter() *ReadLimiter {
m.rlimitInit.Do(func() {
if m.TraverseLimit == 0 {
m.rlimit.limit = defaultTraverseLimit
} else {
m.rlimit.limit = m.TraverseLimit
}
})
return &m.rlimit
}
func (m *Message) depthLimit() uint {
if m.DepthLimit != 0 {
return m.DepthLimit
}
return defaultDepthLimit
}
// NumSegments returns the number of segments in the message.
func (m *Message) NumSegments() int64 {
return int64(m.Arena.NumSegments())
}
// Segment returns the segment with the given ID.
func (m *Message) Segment(id SegmentID) (*Segment, error) {
if isInt32Bit && id > maxInt32 {
return nil, errSegment32Bit
}
if int64(id) >= m.Arena.NumSegments() {
return nil, errSegmentOutOfBounds
}
m.mu.Lock()
if seg := m.segment(id); seg != nil {
m.mu.Unlock()
return seg, nil
}
data, err := m.Arena.Data(id)
if err != nil {
m.mu.Unlock()
return nil, err
}
seg := m.setSegment(id, data)
m.mu.Unlock()
return seg, nil
}
// segment returns the segment with the given ID.
// The caller must be holding m.mu.
func (m *Message) segment(id SegmentID) *Segment {
if m.segs == nil {
if id == 0 && m.firstSeg.msg != nil {
return &m.firstSeg
}
return nil
}
return m.segs[id]
}
// setSegment creates or updates the Segment with the given ID.
// The caller must be holding m.mu.
func (m *Message) setSegment(id SegmentID, data []byte) *Segment {
if m.segs == nil {
if id == 0 {
m.firstSeg = Segment{
id: id,
msg: m,
data: data,
}
return &m.firstSeg
}
m.segs = make(map[SegmentID]*Segment)
if m.firstSeg.msg != nil {
m.segs[0] = &m.firstSeg
}
} else if seg := m.segs[id]; seg != nil {
seg.data = data
return seg
}
seg := &Segment{
id: id,
msg: m,
data: data,
}
m.segs[id] = seg
return seg
}
// allocSegment creates or resizes an existing segment such that
// cap(seg.Data) - len(seg.Data) >= sz.
func (m *Message) allocSegment(sz Size) (*Segment, error) {
m.mu.Lock()
if m.segs == nil && m.firstSeg.msg != nil {
m.segs = make(map[SegmentID]*Segment)
m.segs[0] = &m.firstSeg
}
id, data, err := m.Arena.Allocate(sz, m.segs)
if err != nil {
m.mu.Unlock()
return nil, err
}
if isInt32Bit && id > maxInt32 {
m.mu.Unlock()
return nil, errSegment32Bit
}
seg := m.setSegment(id, data)
m.mu.Unlock()
return seg, nil
}
// alloc allocates sz zero-filled bytes. It prefers using s, but may
// use a different segment in the same message if there's not sufficient
// capacity.
func alloc(s *Segment, sz Size) (*Segment, Address, error) {
sz = sz.padToWord()
if sz > maxSize-wordSize {
return nil, 0, errOverflow
}
if !hasCapacity(s.data, sz) {
var err error
s, err = s.msg.allocSegment(sz)
if err != nil {
return nil, 0, err
}
}
addr := Address(len(s.data))
end, ok := addr.addSize(sz)
if !ok {
return nil, 0, errOverflow
}
space := s.data[len(s.data):end]
s.data = s.data[:end]
for i := range space {
space[i] = 0
}
return s, addr, nil
}
// An Arena loads and allocates segments for a Message.
type Arena interface {
// NumSegments returns the number of segments in the arena.
// This must not be larger than 1<<32.
NumSegments() int64
// Data loads the data for the segment with the given ID. IDs are in
// the range [0, NumSegments()).
// must be tightly packed in the range [0, NumSegments()).
Data(id SegmentID) ([]byte, error)
// Allocate selects a segment to place a new object in, creating a
// segment or growing the capacity of a previously loaded segment if
// necessary. If Allocate does not return an error, then the
// difference of the capacity and the length of the returned slice
// must be at least minsz. segs is a map of segment slices returned
// by the Data method keyed by ID (although the length of these slices
// may have changed by previous allocations). Allocate must not
// modify segs.
//
// If Allocate creates a new segment, the ID must be one larger than
// the last segment's ID or zero if it is the first segment.
//
// If Allocate returns an previously loaded segment's ID, then the
// arena is responsible for preserving the existing data in the
// returned byte slice.
Allocate(minsz Size, segs map[SegmentID]*Segment) (SegmentID, []byte, error)
}
type singleSegmentArena []byte
// SingleSegment returns a new arena with an expanding single-segment
// buffer. b can be used to populate the segment for reading or to
// reserve memory of a specific size. A SingleSegment arena does not
// return errors unless you attempt to access another segment.
func SingleSegment(b []byte) Arena {
ssa := new(singleSegmentArena)
*ssa = b
return ssa
}
func (ssa *singleSegmentArena) NumSegments() int64 {
return 1
}
func (ssa *singleSegmentArena) Data(id SegmentID) ([]byte, error) {
if id != 0 {
return nil, errSegmentOutOfBounds
}
return *ssa, nil
}
func (ssa *singleSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (SegmentID, []byte, error) {
data := []byte(*ssa)
if segs[0] != nil {
data = segs[0].data
}
if len(data)%int(wordSize) != 0 {
return 0, nil, errors.New("capnp: segment size is not a multiple of word size")
}
if hasCapacity(data, sz) {
return 0, data, nil
}
inc, err := nextAlloc(int64(cap(data)), int64(maxSegmentSize()), sz)
if err != nil {
return 0, nil, fmt.Errorf("capnp: alloc %d bytes: %v", sz, err)
}
buf := make([]byte, len(data), cap(data)+inc)
copy(buf, data)
*ssa = buf
return 0, *ssa, nil
}
type roSingleSegment []byte
func (ss roSingleSegment) NumSegments() int64 {
return 1
}
func (ss roSingleSegment) Data(id SegmentID) ([]byte, error) {
if id != 0 {
return nil, errSegmentOutOfBounds
}
return ss, nil
}
func (ss roSingleSegment) Allocate(sz Size, segs map[SegmentID]*Segment) (SegmentID, []byte, error) {
return 0, nil, errors.New("capnp: segment is read-only")
}
type multiSegmentArena [][]byte
// MultiSegment returns a new arena that allocates new segments when
// they are full. b can be used to populate the buffer for reading or
// to reserve memory of a specific size.
func MultiSegment(b [][]byte) Arena {
msa := new(multiSegmentArena)
*msa = b
return msa
}
// demuxArena slices b into a multi-segment arena.
func demuxArena(hdr streamHeader, data []byte) (Arena, error) {
segs := make([][]byte, int(hdr.maxSegment())+1)
for i := range segs {
sz, err := hdr.segmentSize(uint32(i))
if err != nil {
return nil, err
}
segs[i], data = data[:sz:sz], data[sz:]
}
return MultiSegment(segs), nil
}
func (msa *multiSegmentArena) NumSegments() int64 {
return int64(len(*msa))
}
func (msa *multiSegmentArena) Data(id SegmentID) ([]byte, error) {
if int64(id) >= int64(len(*msa)) {
return nil, errSegmentOutOfBounds
}
return (*msa)[id], nil
}
func (msa *multiSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (SegmentID, []byte, error) {
var total int64
for i, data := range *msa {
id := SegmentID(i)
if s := segs[id]; s != nil {
data = s.data
}
if hasCapacity(data, sz) {
return id, data, nil
}
total += int64(cap(data))
if total < 0 {
// Overflow.
return 0, nil, fmt.Errorf("capnp: alloc %d bytes: message too large", sz)
}
}
n, err := nextAlloc(total, 1<<63-1, sz)
if err != nil {
return 0, nil, fmt.Errorf("capnp: alloc %d bytes: %v", sz, err)
}
buf := make([]byte, 0, n)
id := SegmentID(len(*msa))
*msa = append(*msa, buf)
return id, buf, nil
}
// nextAlloc computes how much more space to allocate given the number
// of bytes allocated in the entire message and the requested number of
// bytes. It will always return a multiple of wordSize. max must be a
// multiple of wordSize. The sum of curr and the returned size will
// always be less than max.
func nextAlloc(curr, max int64, req Size) (int, error) {
if req == 0 {
return 0, nil
}
maxinc := int64(1<<32 - 8) // largest word-aligned Size
if isInt32Bit {
maxinc = 1<<31 - 8 // largest word-aligned int
}
if int64(req) > maxinc {
return 0, errors.New("allocation too large")
}
req = req.padToWord()
want := curr + int64(req)
if want <= curr || want > max {
return 0, errors.New("allocation overflows message size")
}
new := curr
double := new + new
switch {
case want < 1024:
next := (1024 - curr + 7) &^ 7
if next < curr {
return int((curr + 7) &^ 7), nil
}
return int(next), nil
case want > double:
return int(req), nil
default:
for 0 < new && new < want {
new += new / 4
}
if new <= 0 {
return int(req), nil
}
delta := new - curr
if delta > maxinc {
return int(maxinc), nil
}
return int((delta + 7) &^ 7), nil
}
}
// A Decoder represents a framer that deserializes a particular Cap'n
// Proto input stream.
type Decoder struct {
r io.Reader
segbuf [msgHeaderSize]byte
hdrbuf []byte
reuse bool
buf []byte
msg Message
arena roSingleSegment
// Maximum number of bytes that can be read per call to Decode.
// If not set, a reasonable default is used.
MaxMessageSize uint64
}
// NewDecoder creates a new Cap'n Proto framer that reads from r.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// NewPackedDecoder creates a new Cap'n Proto framer that reads from a
// packed stream r.
func NewPackedDecoder(r io.Reader) *Decoder {
return NewDecoder(packed.NewReader(bufio.NewReader(r)))
}
// Decode reads a message from the decoder stream.
func (d *Decoder) Decode() (*Message, error) {
maxSize := d.MaxMessageSize
if maxSize == 0 {
maxSize = defaultDecodeLimit
}
if _, err := io.ReadFull(d.r, d.segbuf[:]); err != nil {
return nil, err
}
maxSeg := binary.LittleEndian.Uint32(d.segbuf[:])
if maxSeg > maxStreamSegments {
return nil, errTooManySegments
}
hdrSize := streamHeaderSize(maxSeg)
if hdrSize > maxSize || hdrSize > (1<<31-1) {
return nil, errDecodeLimit
}
d.hdrbuf = resizeSlice(d.hdrbuf, int(hdrSize))
copy(d.hdrbuf, d.segbuf[:])
if _, err := io.ReadFull(d.r, d.hdrbuf[msgHeaderSize:]); err != nil {
return nil, err
}
hdr, _, err := parseStreamHeader(d.hdrbuf)
if err != nil {
return nil, err
}
total, err := hdr.totalSize()
if err != nil {
return nil, err
}
// TODO(someday): if total size is greater than can fit in one buffer,
// attempt to allocate buffer per segment.
if total > maxSize-hdrSize || total > (1<<31-1) {
return nil, errDecodeLimit
}
if !d.reuse {
buf := make([]byte, int(total))
if _, err := io.ReadFull(d.r, buf); err != nil {
return nil, err
}
arena, err := demuxArena(hdr, buf)
if err != nil {
return nil, err
}
return &Message{Arena: arena}, nil
}
d.buf = resizeSlice(d.buf, int(total))
if _, err := io.ReadFull(d.r, d.buf); err != nil {
return nil, err
}
var arena Arena
if hdr.maxSegment() == 0 {
d.arena = d.buf[:len(d.buf):len(d.buf)]
arena = &d.arena
} else {
var err error
arena, err = demuxArena(hdr, d.buf)
if err != nil {
return nil, err
}
}
d.msg.Reset(arena)
return &d.msg, nil
}
func resizeSlice(b []byte, size int) []byte {
if cap(b) < size {
return make([]byte, size)
}
return b[:size]
}
// ReuseBuffer causes the decoder to reuse its buffer on subsequent decodes.
// The decoder may return messages that cannot handle allocations.
func (d *Decoder) ReuseBuffer() {
d.reuse = true
}
// Unmarshal reads an unpacked serialized stream into a message. No
// copying is performed, so the objects in the returned message read
// directly from data.
func Unmarshal(data []byte) (*Message, error) {
if len(data) == 0 {
return nil, io.EOF
}
hdr, data, err := parseStreamHeader(data)
if err != nil {
return nil, err
}
if tot, err := hdr.totalSize(); err != nil {
return nil, err
} else if tot > uint64(len(data)) {
return nil, io.ErrUnexpectedEOF
}
arena, err := demuxArena(hdr, data)
if err != nil {
return nil, err
}
return &Message{Arena: arena}, nil
}
// UnmarshalPacked reads a packed serialized stream into a message.
func UnmarshalPacked(data []byte) (*Message, error) {
if len(data) == 0 {
return nil, io.EOF
}
data, err := packed.Unpack(nil, data)
if err != nil {
return nil, err
}
return Unmarshal(data)
}
// MustUnmarshalRoot reads an unpacked serialized stream and returns
// its root pointer. If there is any error, it panics.
//
// Deprecated: Use MustUnmarshalRootPtr.
func MustUnmarshalRoot(data []byte) Pointer {
msg, err := Unmarshal(data)
if err != nil {
panic(err)
}
p, err := msg.Root()
if err != nil {
panic(err)
}
return p
}
// MustUnmarshalRootPtr reads an unpacked serialized stream and returns
// its root pointer. If there is any error, it panics.
func MustUnmarshalRootPtr(data []byte) Ptr {
msg, err := Unmarshal(data)
if err != nil {
panic(err)
}
p, err := msg.RootPtr()
if err != nil {
panic(err)
}
return p
}
// An Encoder represents a framer for serializing a particular Cap'n
// Proto stream.
type Encoder struct {
w io.Writer
hdrbuf []byte
bufs [][]byte
packed bool
packbuf []byte
}
// NewEncoder creates a new Cap'n Proto framer that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
// NewPackedEncoder creates a new Cap'n Proto framer that writes to a
// packed stream w.
func NewPackedEncoder(w io.Writer) *Encoder {
return &Encoder{w: w, packed: true}
}
// Encode writes a message to the encoder stream.
func (e *Encoder) Encode(m *Message) error {
nsegs := m.NumSegments()
if nsegs == 0 {
return errMessageEmpty
}
e.bufs = append(e.bufs[:0], nil) // first element is placeholder for header
maxSeg := uint32(nsegs - 1)
hdrSize := streamHeaderSize(maxSeg)
if uint64(cap(e.hdrbuf)) < hdrSize {
e.hdrbuf = make([]byte, 0, hdrSize)
}
e.hdrbuf = appendUint32(e.hdrbuf[:0], maxSeg)
for i := int64(0); i < nsegs; i++ {
s, err := m.Segment(SegmentID(i))
if err != nil {
return err
}
n := len(s.data)
if int64(n) > int64(maxSize) {
return errSegmentTooLarge
}
e.hdrbuf = appendUint32(e.hdrbuf, uint32(Size(n)/wordSize))
e.bufs = append(e.bufs, s.data)
}
if len(e.hdrbuf)%int(wordSize) != 0 {
e.hdrbuf = appendUint32(e.hdrbuf, 0)
}
e.bufs[0] = e.hdrbuf
if e.packed {
return e.writePacked(e.bufs)
}
return e.write(e.bufs)
}
func (e *Encoder) writePacked(bufs [][]byte) error {
for _, b := range bufs {
e.packbuf = packed.Pack(e.packbuf[:0], b)
if _, err := e.w.Write(e.packbuf); err != nil {
return err
}
}
return nil
}
func (m *Message) segmentSizes() ([]Size, error) {
nsegs := m.NumSegments()
sizes := make([]Size, nsegs)
for i := int64(0); i < nsegs; i++ {
s, err := m.Segment(SegmentID(i))
if err != nil {
return sizes[:i], err
}
n := len(s.data)
if int64(n) > int64(maxSize) {
return sizes[:i], errSegmentTooLarge
}
sizes[i] = Size(n)
}
return sizes, nil
}
// Marshal concatenates the segments in the message into a single byte
// slice including framing.
func (m *Message) Marshal() ([]byte, error) {
// Compute buffer size.
// TODO(light): error out if too many segments
nsegs := m.NumSegments()
if nsegs == 0 {
return nil, errMessageEmpty
}
maxSeg := uint32(nsegs - 1)
hdrSize := streamHeaderSize(maxSeg)
sizes, err := m.segmentSizes()
if err != nil {
return nil, err
}
// TODO(light): error out if too large
total := uint64(hdrSize) + totalSize(sizes)
// Fill in buffer.
buf := make([]byte, hdrSize, total)
// TODO: remove marshalStreamHeader and inline.
marshalStreamHeader(buf, sizes)
for i := int64(0); i < nsegs; i++ {
s, err := m.Segment(SegmentID(i))
if err != nil {
return nil, err
}
buf = append(buf, s.data...)
}
return buf, nil
}
// MarshalPacked marshals the message in packed form.
func (m *Message) MarshalPacked() ([]byte, error) {
data, err := m.Marshal()
if err != nil {
return nil, err
}
buf := make([]byte, 0, len(data))
buf = packed.Pack(buf, data)
return buf, nil
}
// Stream header sizes.
const (
msgHeaderSize = 4
segHeaderSize = 4
)
// streamHeaderSize returns the size of the header, given the
// first 32-bit number.
func streamHeaderSize(n uint32) uint64 {
return (msgHeaderSize + segHeaderSize*(uint64(n)+1) + 7) &^ 7
}
// marshalStreamHeader marshals the sizes into the byte slice, which
// must be of size streamHeaderSize(len(sizes) - 1).
//
// TODO: remove marshalStreamHeader and inline.
func marshalStreamHeader(b []byte, sizes []Size) {
binary.LittleEndian.PutUint32(b, uint32(len(sizes)-1))
for i, sz := range sizes {
loc := msgHeaderSize + i*segHeaderSize
binary.LittleEndian.PutUint32(b[loc:], uint32(sz/Size(wordSize)))
}
}
// appendUint32 appends a uint32 to a byte slice and returns the
// new slice.
func appendUint32(b []byte, v uint32) []byte {
b = append(b, 0, 0, 0, 0)
binary.LittleEndian.PutUint32(b[len(b)-4:], v)
return b
}
type streamHeader struct {
b []byte
}
// parseStreamHeader parses the header of the stream framing format.
func parseStreamHeader(data []byte) (h streamHeader, tail []byte, err error) {
if uint64(len(data)) < streamHeaderSize(0) {
return streamHeader{}, nil, io.ErrUnexpectedEOF
}
maxSeg := binary.LittleEndian.Uint32(data)
// TODO(light): check int
hdrSize := streamHeaderSize(maxSeg)
if uint64(len(data)) < hdrSize {
return streamHeader{}, nil, io.ErrUnexpectedEOF
}
return streamHeader{b: data}, data[hdrSize:], nil
}
func (h streamHeader) maxSegment() uint32 {
return binary.LittleEndian.Uint32(h.b)
}
func (h streamHeader) segmentSize(i uint32) (Size, error) {
s := binary.LittleEndian.Uint32(h.b[msgHeaderSize+i*segHeaderSize:])
sz, ok := wordSize.times(int32(s))
if !ok {
return 0, errSegmentTooLarge
}
return sz, nil
}
func (h streamHeader) totalSize() (uint64, error) {
var sum uint64
for i := uint64(0); i <= uint64(h.maxSegment()); i++ {
x, err := h.segmentSize(uint32(i))
if err != nil {
return sum, err
}
sum += uint64(x)
}
return sum, nil
}
func hasCapacity(b []byte, sz Size) bool {
return sz <= Size(cap(b)-len(b))
}
func totalSize(s []Size) uint64 {
var sum uint64
for _, sz := range s {
sum += uint64(sz)
}
return sum
}
const (
maxInt32 = 0x7fffffff
maxInt = int(^uint(0) >> 1)
isInt32Bit = maxInt == maxInt32
)
// maxSegmentSize returns the maximum permitted size of a single segment
// on this platform.
//
// This is effectively a compile-time constant, but can't be represented
// as a constant because it requires a conditional. It is trivially
// inlinable and optimizable, so should act like one.
func maxSegmentSize() Size {
if isInt32Bit {
return Size(maxInt32 - 7)
} else {
return maxSize - 7
}
}
var (
errSegmentOutOfBounds = errors.New("capnp: segment ID out of bounds")
errSegment32Bit = errors.New("capnp: segment ID larger than 31 bits")
errMessageEmpty = errors.New("capnp: marshalling an empty message")
errHasData = errors.New("capnp: NewMessage called on arena with data")
errSegmentTooLarge = errors.New("capnp: segment too large")
errTooManySegments = errors.New("capnp: too many segments to decode")
errDecodeLimit = errors.New("capnp: message too large")
)