From e408d9e5b56eff3229d14365fee4442b880b3e6a Mon Sep 17 00:00:00 2001 From: Ian Denhardt Date: Sat, 31 Dec 2022 18:18:39 -0500 Subject: [PATCH 1/4] Get rid of format methods in exc package ...and update downstream call sites --- answer.go | 19 ++++-- canonical.go | 30 ++++----- capability.go | 11 ++-- error.go | 8 --- exc/exc.go | 17 ----- list.go | 30 ++++----- message.go | 126 +++++++++++++++++++------------------ pointer.go | 12 ++-- rpc/transport/transport.go | 6 +- segment.go | 69 ++++++++++---------- struct.go | 16 +++-- 11 files changed, 174 insertions(+), 170 deletions(-) diff --git a/answer.go b/answer.go index 9f095049..2aa46637 100644 --- a/answer.go +++ b/answer.go @@ -2,6 +2,7 @@ package capnp import ( "context" + "errors" "strconv" "sync" @@ -568,28 +569,36 @@ func Transform(p Ptr, transform []PipelineOp) (Ptr, error) { for i, op := range transform[:n-1] { field, err := s.Ptr(op.Field) if err != nil { - return Ptr{}, errorf("transform: op %d: pointer field %d: %v", i, op.Field, err) + return Ptr{}, newTransformError(i, op.Field, err, false) } s, err = field.StructDefault(op.DefaultValue) if err != nil { - return Ptr{}, errorf("transform: op %d: pointer field %d with default: %v", i, op.Field, err) + return Ptr{}, newTransformError(i, op.Field, err, true) } } op := transform[n-1] p, err := s.Ptr(op.Field) if err != nil { - return Ptr{}, errorf("transform: op %d: pointer field %d: %v", n-1, op.Field, err) + return Ptr{}, newTransformError(n-1, op.Field, err, false) } if op.DefaultValue != nil { p, err = p.Default(op.DefaultValue) if err != nil { - return Ptr{}, errorf("transform: op %d: pointer field %d with default: %v", n-1, op.Field, err) + return Ptr{}, newTransformError(n-1, op.Field, err, true) } return p, nil } return p, nil } +func newTransformError(index int, field uint16, err error, withDefault bool) error { + msg := "transform: op " + fmtIdecimal(index) + ": pointer field " + fmtUdecimal(field) + if withDefault { + msg += " with default" + } + return exc.WrapError(msg, err) +} + // A resolution is the outcome of a future. type resolution struct { method Method @@ -617,7 +626,7 @@ func (r resolution) client(transform []PipelineOp) Client { } iface := p.Interface() if p.IsValid() && !iface.IsValid() { - return ErrorClient(errorf("not a capability")) + return ErrorClient(errors.New("not a capability")) } return iface.Client() } diff --git a/canonical.go b/canonical.go index 4b43cc59..bb47a801 100644 --- a/canonical.go +++ b/canonical.go @@ -1,5 +1,7 @@ package capnp +import "capnproto.org/go/capnp/v3/exc" + // Canonicalize encodes a struct into its canonical form: a single- // segment blob without a segment table. The result will be identical // for equivalent structs, even as the schema evolves. The blob is @@ -11,13 +13,13 @@ func Canonicalize(s Struct) ([]byte, error) { } root, err := NewRootStruct(seg, canonicalStructSize(s)) if err != nil { - return nil, errorf("canonicalize: %v", err) + return nil, exc.WrapError("canonicalize", err) } if err := msg.SetRoot(root.ToPtr()); err != nil { - return nil, errorf("canonicalize: %v", err) + return nil, exc.WrapError("canonicalize", err) } if err := fillCanonicalStruct(root, s); err != nil { - return nil, annotatef(err, "canonicalize") + return nil, exc.WrapError("canonicalize", err) } return seg.Data(), nil } @@ -30,7 +32,7 @@ func canonicalPtr(dst *Segment, p Ptr) (Ptr, error) { case structPtrType: ss, err := NewStruct(dst, canonicalStructSize(p.Struct())) if err != nil { - return Ptr{}, errorf("struct: %v", err) + return Ptr{}, exc.WrapError("struct", err) } if err := fillCanonicalStruct(ss, p.Struct()); err != nil { return Ptr{}, err @@ -55,14 +57,14 @@ func fillCanonicalStruct(dst, s Struct) error { for i := uint16(0); i < dst.size.PointerCount; i++ { p, err := s.Ptr(i) if err != nil { - return annotatef(err, "struct pointer %d", i) + return exc.WrapError("struct pointer "+fmtUdecimal(i), err) } cp, err := canonicalPtr(dst.seg, p) if err != nil { - return annotatef(err, "struct pointer %d", i) + return exc.WrapError("struct pointer "+fmtUdecimal(i), err) } if err := dst.SetPtr(i, cp); err != nil { - return annotatef(err, "struct pointer %d", i) + return exc.WrapError("struct pointer "+fmtUdecimal(i), err) } } return nil @@ -98,7 +100,7 @@ func canonicalList(dst *Segment, l List) (List, error) { sz := l.allocSize() _, newAddr, err := alloc(dst, sz) if err != nil { - return List{}, errorf("list: %v", err) + return List{}, exc.WrapError("list", err) } cl := List{ seg: dst, @@ -115,19 +117,19 @@ func canonicalList(dst *Segment, l List) (List, error) { if l.flags&isCompositeList == 0 { cl, err := NewPointerList(dst, l.length) if err != nil { - return List{}, errorf("list: %v", err) + return List{}, exc.WrapError("list", err) } for i := 0; i < l.Len(); i++ { p, err := PointerList(l).At(i) if err != nil { - return List{}, errorf("list element %d: %v", i, err) + return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) } cp, err := canonicalPtr(dst, p) if err != nil { - return List{}, annotatef(err, "list element %d", i) + return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) } if err := cl.Set(i, cp); err != nil { - return List{}, errorf("list element %d: %v", i, err) + return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) } } return List(cl), nil @@ -146,11 +148,11 @@ func canonicalList(dst *Segment, l List) (List, error) { } cl, err := NewCompositeList(dst, elemSize, l.length) if err != nil { - return List{}, errorf("list: %v", err) + return List{}, exc.WrapError("list", err) } for i := 0; i < cl.Len(); i++ { if err := fillCanonicalStruct(cl.Struct(i), l.Struct(i)); err != nil { - return List{}, annotatef(err, "list element %d", i) + return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) } } return cl, nil diff --git a/capability.go b/capability.go index 71752296..460bb759 100644 --- a/capability.go +++ b/capability.go @@ -2,6 +2,7 @@ package capnp import ( "context" + "errors" "fmt" "runtime" "strconv" @@ -313,10 +314,10 @@ func (c Client) SendCall(ctx context.Context, s Send) (*Answer, ReleaseFunc) { h, _, released, finish := c.startCall() defer finish() if released { - return ErrorAnswer(s.Method, errorf("call on released client")), func() {} + return ErrorAnswer(s.Method, errors.New("call on released client")), func() {} } if h == nil { - return ErrorAnswer(s.Method, errorf("call on null client")), func() {} + return ErrorAnswer(s.Method, errors.New("call on null client")), func() {} } limiter := c.GetFlowLimiter() @@ -391,11 +392,11 @@ func (c Client) RecvCall(ctx context.Context, r Recv) PipelineCaller { h, _, released, finish := c.startCall() defer finish() if released { - r.Reject(errorf("call on released client")) + r.Reject(errors.New("call on released client")) return nil } if h == nil { - r.Reject(errorf("call on null client")) + r.Reject(errors.New("call on null client")) return nil } return h.Recv(ctx, r) @@ -430,7 +431,7 @@ func (c Client) Resolve(ctx context.Context) error { for { h, released, resolved := c.peek() if released { - return errorf("cannot resolve released client") + return errors.New("cannot resolve released client") } if resolved { diff --git a/error.go b/error.go index fdd6f553..331d3853 100644 --- a/error.go +++ b/error.go @@ -32,11 +32,3 @@ func Disconnected(s string) error { func IsDisconnected(e error) bool { return exc.TypeOf(e) == exc.Disconnected } - -func errorf(format string, args ...any) error { - return capnperr.Failedf(format, args...) -} - -func annotatef(err error, format string, args ...any) error { - return capnperr.Annotatef(err, format, args...) -} diff --git a/exc/exc.go b/exc/exc.go index 01bef389..c55506a3 100644 --- a/exc/exc.go +++ b/exc/exc.go @@ -3,7 +3,6 @@ package exc import ( "errors" - "fmt" "strconv" ) @@ -108,30 +107,14 @@ func (f Annotator) Failed(err error) *Exception { return f.New(Failed, err) } -func (f Annotator) Failedf(format string, args ...any) *Exception { - return f.Failed(fmt.Errorf(format, args...)) -} - func (f Annotator) Disconnected(err error) *Exception { return f.New(Disconnected, err) } -func (f Annotator) Disconnectedf(format string, args ...any) *Exception { - return f.Disconnected(fmt.Errorf(format, args...)) -} - func (f Annotator) Unimplemented(err error) *Exception { return f.New(Unimplemented, err) } -func (f Annotator) Unimplementedf(format string, args ...any) *Exception { - return f.Unimplemented(fmt.Errorf(format, args...)) -} - func (f Annotator) Annotate(err error, msg string) *Exception { return Annotate(string(f), msg, err) } - -func (f Annotator) Annotatef(err error, format string, args ...any) *Exception { - return f.Annotate(err, fmt.Sprintf(format, args...)) -} diff --git a/list.go b/list.go index 0d6fe15a..085c21ab 100644 --- a/list.go +++ b/list.go @@ -2,10 +2,12 @@ package capnp import ( "bytes" + "errors" "fmt" "math" "strconv" + "capnproto.org/go/capnp/v3/exc" "capnproto.org/go/capnp/v3/internal/strquote" ) @@ -27,14 +29,14 @@ type ListKind = struct { // newPrimitiveList allocates a new list of primitive values, preferring placement in s. func newPrimitiveList(s *Segment, sz Size, n int32) (List, error) { if n < 0 || n >= 1<<29 { - return List{}, errorf("new list: length out of range") + return List{}, errors.New("new list: length out of range") } // sz is [0, 8] and n is [0, 1<<29). // Range is [0, maxSegmentSize], thus there will never be overflow. total := sz.timesUnchecked(n) s, addr, err := alloc(s, total) if err != nil { - return List{}, annotatef(err, "new list") + return List{}, exc.WrapError("new list", err) } return List{ seg: s, @@ -49,19 +51,19 @@ func newPrimitiveList(s *Segment, sz Size, n int32) (List, error) { // in s. func NewCompositeList(s *Segment, sz ObjectSize, n int32) (List, error) { if !sz.isValid() { - return List{}, errorf("new composite list: invalid element size") + return List{}, errors.New("new composite list: invalid element size") } if n < 0 || n >= 1<<29 { - return List{}, errorf("new composite list: length out of range") + return List{}, errors.New("new composite list: length out of range") } sz.DataSize = sz.DataSize.padToWord() total, ok := sz.totalSize().times(n) if !ok || total > maxSegmentSize-wordSize { - return List{}, errorf("new composite list: size overflow") + return List{}, errors.New("new composite list: size overflow") } s, addr, err := alloc(s, wordSize+total) if err != nil { - return List{}, annotatef(err, "new composite list") + return List{}, exc.WrapError("new composite list", err) } // Add tag word s.writeRawPointer(addr, rawStructPointer(pointerOffset(n), sz)) @@ -194,11 +196,11 @@ func (p List) primitiveElem(i int, expectedSize ObjectSize) (address, error) { (p.size.DataSize < expectedSize.DataSize || p.size.PointerCount < expectedSize.PointerCount) { - return 0, errorf("mismatched list element size") + return 0, errors.New("mismatched list element size") } addr, ok := p.off.element(int32(i), p.size.totalSize()) if !ok { - return 0, errorf("read list element %d: address overflow", i) + return 0, errors.New("read list element " + fmtIdecimal(i) + ": address overflow") } return addr, nil } @@ -228,10 +230,10 @@ func (p List) Struct(i int) Struct { // SetStruct set the i'th element to the value in s. func (p List) SetStruct(i int, s Struct) error { if p.flags&isBitList != 0 { - return errorf("SetStruct called on bit list") + return errors.New("SetStruct called on bit list") } if err := copyStruct(p.Struct(i), s); err != nil { - return annotatef(err, "set list element %d", i) + return exc.WrapError("set list element "+fmtIdecimal(i), err) } return nil } @@ -253,11 +255,11 @@ var _ TypeParam[BitList] = BitList{} // NewBitList creates a new bit list, preferring placement in s. func NewBitList(s *Segment, n int32) (BitList, error) { if n < 0 || n >= 1<<29 { - return BitList{}, errorf("new bit list: length out of range") + return BitList{}, errors.New("new bit list: length out of range") } s, addr, err := alloc(s, bitListSize(n)) if err != nil { - return BitList{}, annotatef(err, "new %d-element bit list", n) + return BitList{}, exc.WrapError("new "+fmtIdecimal(n)+"-element bit list", err) } return BitList{ seg: s, @@ -335,11 +337,11 @@ var _ TypeParam[PointerList] = PointerList{} func NewPointerList(s *Segment, n int32) (PointerList, error) { total, ok := wordSize.times(n) if !ok { - return PointerList{}, errorf("new pointer list: size overflow") + return PointerList{}, errors.New("new pointer list: size overflow") } s, addr, err := alloc(s, total) if err != nil { - return PointerList{}, annotatef(err, "new %d-element pointer list", n) + return PointerList{}, exc.WrapError("new "+fmtIdecimal(n)+"-element pointer list", err) } return PointerList{ seg: s, diff --git a/message.go b/message.go index bd6135d1..f76a7cd4 100644 --- a/message.go +++ b/message.go @@ -3,11 +3,12 @@ package capnp import ( "bufio" "encoding/binary" - "fmt" + "errors" "io" "sync" "sync/atomic" + "capnproto.org/go/capnp/v3/exc" "capnproto.org/go/capnp/v3/exp/bufferpool" "capnproto.org/go/capnp/v3/packed" ) @@ -77,28 +78,28 @@ func NewMessage(arena Arena) (msg *Message, first *Segment, err error) { case 0: first, err = msg.allocSegment(wordSize) if err != nil { - return nil, nil, annotatef(err, "new message") + return nil, nil, exc.WrapError("new message", err) } case 1: first, err = msg.Segment(0) if err != nil { - return nil, nil, annotatef(err, "new message") + return nil, nil, exc.WrapError("new message", err) } if len(first.data) > 0 { - return nil, nil, errorf("new message: arena not empty") + return nil, nil, errors.New("new message: arena not empty") } default: - return nil, nil, errorf("new message: arena not empty") + return nil, nil, errors.New("new message: arena not empty") } if first.ID() != 0 { - return nil, nil, errorf("new message: arena allocated first segment with non-zero ID") + return nil, nil, errors.New("new message: arena allocated first segment with non-zero ID") } seg, _, err := alloc(first, wordSize) // allocate root if err != nil { - return nil, nil, annotatef(err, "new message") + return nil, nil, exc.WrapError("new message", err) } if seg != first { - return nil, nil, errorf("new message: arena allocated first word outside first segment") + return nil, nil, errors.New("new message: arena allocated first word outside first segment") } return msg, first, nil } @@ -185,11 +186,11 @@ func (m *Message) Unread(sz Size) { func (m *Message) Root() (Ptr, error) { s, err := m.Segment(0) if err != nil { - return Ptr{}, annotatef(err, "read root") + return Ptr{}, exc.WrapError("read root", err) } p, err := s.root().At(0) if err != nil { - return Ptr{}, annotatef(err, "read root") + return Ptr{}, exc.WrapError("read root", err) } return p, nil } @@ -198,10 +199,10 @@ func (m *Message) Root() (Ptr, error) { func (m *Message) SetRoot(p Ptr) error { s, err := m.Segment(0) if err != nil { - return annotatef(err, "set root") + return exc.WrapError("set root", err) } if err := s.root().Set(0, p); err != nil { - return annotatef(err, "set root") + return exc.WrapError("set root", err) } return nil } @@ -246,7 +247,7 @@ func (m *Message) NumSegments() int64 { // Segment returns the segment with the given ID. func (m *Message) Segment(id SegmentID) (*Segment, error) { if int64(id) >= m.Arena.NumSegments() { - return nil, errorf("segment %d: out of bounds", id) + return nil, errors.New("segment " + fmtUdecimal(id) + ": out of bounds") } m.mu.Lock() seg, err := m.segment(id) @@ -264,11 +265,11 @@ func (m *Message) segment(id SegmentID) (*Segment, error) { return s, nil } if len(m.segs) == maxInt { - return nil, errorf("segment %d: number of loaded segments exceeds int", id) + return nil, errors.New("segment " + fmtUdecimal(id) + ": number of loaded segments exceeds int") } data, err := m.Arena.Data(id) if err != nil { - return nil, errorf("load segment %d: %v", id, err) + return nil, exc.WrapError("load segment "+fmtUdecimal(id), err) } s := m.setSegment(id, data) return s, nil @@ -308,12 +309,12 @@ func (m *Message) setSegment(id SegmentID, data []byte) *Segment { // onto m.mu. func (m *Message) allocSegment(sz Size) (*Segment, error) { if sz > maxAllocSize() { - return nil, errorf("allocation: too large") + return nil, errors.New("allocation: too large") } m.mu.Lock() if len(m.segs) == maxInt { m.mu.Unlock() - return nil, errorf("allocation: number of loaded segments exceeds int") + return nil, errors.New("allocation: number of loaded segments exceeds int") } if m.segs == nil && m.firstSeg.msg != nil { // Transition from sole segment to segment map. @@ -323,7 +324,7 @@ func (m *Message) allocSegment(sz Size) (*Segment, error) { id, data, err := m.Arena.Allocate(sz, m.segs) if err != nil { m.mu.Unlock() - return nil, errorf("allocation: %v", err) + return nil, exc.WrapError("allocation", err) } seg := m.setSegment(id, data) m.mu.Unlock() @@ -335,7 +336,7 @@ func (m *Message) allocSegment(sz Size) (*Segment, error) { // capacity. func alloc(s *Segment, sz Size) (*Segment, address, error) { if sz > maxAllocSize() { - return nil, 0, errorf("allocation: too large") + return nil, 0, errors.New("allocation: too large") } sz = sz.padToWord() @@ -350,7 +351,7 @@ func alloc(s *Segment, sz Size) (*Segment, address, error) { addr := address(len(s.data)) end, ok := addr.addSize(sz) if !ok { - return nil, 0, errorf("allocation: address overflow") + return nil, 0, errors.New("allocation: address overflow") } space := s.data[len(s.data):end] s.data = s.data[:end] @@ -425,7 +426,7 @@ func (ssa SingleSegmentArena) NumSegments() int64 { func (ssa SingleSegmentArena) Data(id SegmentID) ([]byte, error) { if id != 0 { - return nil, errorf("segment %d requested in single segment arena", id) + return nil, errors.New("segment " + fmtUdecimal(id) + " requested in single segment arena") } return ssa, nil } @@ -436,7 +437,7 @@ func (ssa *SingleSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (S data = segs[0].data } if len(data)%int(wordSize) != 0 { - return 0, nil, errorf("segment size is not a multiple of word size") + return 0, nil, errors.New("segment size is not a multiple of word size") } if hasCapacity(data, sz) { return 0, data, nil @@ -463,17 +464,17 @@ func (ss roSingleSegment) NumSegments() int64 { func (ss roSingleSegment) Data(id SegmentID) ([]byte, error) { if id != 0 { - return nil, errorf("segment %d requested in single segment arena", id) + return nil, errors.New("segment " + fmtUdecimal(id) + " requested in single segment arena") } return ss, nil } func (ss roSingleSegment) Allocate(sz Size, segs map[SegmentID]*Segment) (SegmentID, []byte, error) { - return 0, nil, errorf("arena is read-only") + return 0, nil, errors.New("arena is read-only") } func (ss roSingleSegment) String() string { - return fmt.Sprintf("read-only single-segment arena [len=%d]", len(ss)) + return "read-only single-segment arena [len=" + fmtIdecimal(len(ss)) + "]" } // MultiSegment is an arena that stores object data across multiple []byte @@ -531,7 +532,7 @@ var multiSegmentPool = sync.Pool{ func demuxArena(hdr streamHeader, data []byte) (Arena, error) { maxSeg := hdr.maxSegment() if int64(maxSeg) > int64(maxInt-1) { - return nil, errorf("number of segments overflows int") + return nil, errors.New("number of segments overflows int") } segs := make([][]byte, int(maxSeg+1)) for i := range segs { @@ -550,7 +551,8 @@ func (msa *MultiSegmentArena) NumSegments() int64 { func (msa *MultiSegmentArena) Data(id SegmentID) ([]byte, error) { if int64(id) >= int64(len(*msa)) { - return nil, errorf("segment %d requested (arena only has %d segments)", id, len(*msa)) + return nil, errors.New("segment " + fmtUdecimal(id) + " requested (arena only has " + + fmtIdecimal(len(*msa)) + " segments)") } return (*msa)[id], nil } @@ -572,7 +574,7 @@ func (msa *MultiSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (Se total += int64(cap(data)) if total < 0 { // Overflow. - return 0, nil, errorf("alloc %d bytes: message too large", sz) + return 0, nil, errors.New("alloc " + fmtUdecimal(sz) + " bytes: message too large") } } n, err := nextAlloc(total, 1<<63-1, sz) @@ -599,12 +601,12 @@ func nextAlloc(curr, max int64, req Size) (int, error) { return 0, nil } if req > maxAllocSize() { - return 0, errorf("alloc %v: too large", req) + return 0, errors.New("alloc " + req.String() + ": too large") } padreq := req.padToWord() want := curr + int64(padreq) if want <= curr || want > max { - return 0, errorf("alloc %v: message size overflow", req) + return 0, errors.New("alloc " + req.String() + ": message size overflow") } new := curr double := new + new @@ -673,7 +675,7 @@ func (d *Decoder) Decode() (*Message, error) { if maxSize == 0 { maxSize = defaultDecodeLimit } else if maxSize < uint64(len(d.wordbuf)) { - return nil, errorf("decode: max message size is smaller than header size") + return nil, errors.New("decode: max message size is smaller than header size") } // Read first word (number of segments and first segment size). @@ -681,11 +683,11 @@ func (d *Decoder) Decode() (*Message, error) { if _, err := io.ReadFull(d.r, d.wordbuf[:]); err == io.EOF { return nil, io.EOF } else if err != nil { - return nil, errorf("decode: read header: %v", err) + return nil, exc.WrapError("decode: read header", err) } maxSeg := SegmentID(binary.LittleEndian.Uint32(d.wordbuf[:])) if maxSeg > maxStreamSegments { - return nil, errorf("decode: too many segments to decode") + return nil, errors.New("decode: too many segments to decode") } // Read the rest of the header if more than one segment. @@ -695,23 +697,23 @@ func (d *Decoder) Decode() (*Message, error) { } else { hdrSize := streamHeaderSize(maxSeg) if hdrSize > maxSize || hdrSize > uint64(maxInt) { - return nil, errorf("decode: message too large") + return nil, errors.New("decode: message too large") } d.hdrbuf = resizeSlice(d.hdrbuf, int(hdrSize)) copy(d.hdrbuf, d.wordbuf[:]) if _, err := io.ReadFull(d.r, d.hdrbuf[len(d.wordbuf):]); err != nil { - return nil, errorf("decode: read header: %v", err) + return nil, exc.WrapError("decode: read header", err) } hdr = streamHeader{d.hdrbuf} } total, err := hdr.totalSize() if err != nil { - return nil, annotatef(err, "decode") + return nil, exc.WrapError("decode", err) } // TODO(someday): if total size is greater than can fit in one buffer, // attempt to allocate buffer per segment. if total > maxSize-uint64(len(hdr.b)) || total > uint64(maxInt) { - return nil, errorf("decode: message too large") + return nil, errors.New("decode: message too large") } // Read segments. @@ -723,11 +725,11 @@ func (d *Decoder) Decode() (*Message, error) { buf = d.bufferPool.Get(int(total)) } if _, err := io.ReadFull(d.r, buf); err != nil { - return nil, errorf("decode: read segments: %v", err) + return nil, exc.WrapError("decode: read segments", err) } arena, err := demuxArena(hdr, buf) if err != nil { - return nil, annotatef(err, "decode") + return nil, exc.WrapError("decode", err) } return &Message{ Arena: arena, @@ -736,7 +738,7 @@ func (d *Decoder) Decode() (*Message, error) { } d.buf = resizeSlice(d.buf, int(total)) if _, err := io.ReadFull(d.r, d.buf); err != nil { - return nil, errorf("decode: read segments: %v", err) + return nil, exc.WrapError("decode: read segments", err) } var arena Arena if maxSeg == 0 { @@ -746,7 +748,7 @@ func (d *Decoder) Decode() (*Message, error) { var err error arena, err = demuxArena(hdr, d.buf) if err != nil { - return nil, annotatef(err, "decode") + return nil, exc.WrapError("decode", err) } } d.msg.Reset(arena) @@ -789,23 +791,23 @@ func Unmarshal(data []byte) (*Message, error) { return nil, io.EOF } if len(data) < int(wordSize) { - return nil, errorf("unmarshal: short header section") + return nil, errors.New("unmarshal: short header section") } maxSeg := SegmentID(binary.LittleEndian.Uint32(data)) hdrSize := streamHeaderSize(maxSeg) if uint64(len(data)) < hdrSize { - return nil, errorf("unmarshal: short header section") + return nil, errors.New("unmarshal: short header section") } hdr := streamHeader{data[:hdrSize]} data = data[hdrSize:] if total, err := hdr.totalSize(); err != nil { - return nil, annotatef(err, "unmarshal") + return nil, exc.WrapError("unmarshal", err) } else if total > uint64(len(data)) { - return nil, errorf("unmarshal: short data section") + return nil, errors.New("unmarshal: short data section") } arena, err := demuxArena(hdr, data) if err != nil { - return nil, annotatef(err, "unmarshal") + return nil, exc.WrapError("unmarshal", err) } return &Message{Arena: arena}, nil } @@ -817,7 +819,7 @@ func UnmarshalPacked(data []byte) (*Message, error) { } data, err := packed.Unpack(nil, data) if err != nil { - return nil, errorf("unmarshal: %v", err) + return nil, exc.WrapError("unmarshal", err) } return Unmarshal(data) } @@ -859,24 +861,24 @@ func NewPackedEncoder(w io.Writer) *Encoder { func (e *Encoder) Encode(m *Message) error { nsegs := m.NumSegments() if nsegs == 0 { - return errorf("encode: message has no segments") + return errors.New("encode: message has no segments") } e.bufs = append(e.bufs[:0], nil) // first element is placeholder for header maxSeg := SegmentID(nsegs - 1) hdrSize := streamHeaderSize(maxSeg) if hdrSize > uint64(maxInt) { - return errorf("encode: header size overflows int") + return errors.New("encode: header size overflows int") } e.hdrbuf = resizeSlice(e.hdrbuf, int(hdrSize)) e.hdrbuf = appendUint32(e.hdrbuf[:0], uint32(maxSeg)) for i := int64(0); i < nsegs; i++ { s, err := m.Segment(SegmentID(i)) if err != nil { - return annotatef(err, "encode") + return exc.WrapError("encode", err) } n := len(s.data) if int64(n) > int64(maxSegmentSize) { - return errorf("encode: segment %d too large", i) + return errors.New("encode: segment " + fmtIdecimal(i) + " too large") } e.hdrbuf = appendUint32(e.hdrbuf, uint32(Size(n)/wordSize)) e.bufs = append(e.bufs, s.data) @@ -887,7 +889,7 @@ func (e *Encoder) Encode(m *Message) error { e.bufs[0] = e.hdrbuf if err := e.write(e.bufs); err != nil { - return errorf("encode: %v", err) + return exc.WrapError("encode", err) } return nil @@ -899,11 +901,11 @@ func (m *Message) Marshal() ([]byte, error) { // Compute buffer size. nsegs := m.NumSegments() if nsegs == 0 { - return nil, errorf("marshal: message has no segments") + return nil, errors.New("marshal: message has no segments") } hdrSize := streamHeaderSize(SegmentID(nsegs - 1)) if hdrSize > uint64(maxInt) { - return nil, errorf("marshal: header size overflows int") + return nil, errors.New("marshal: header size overflows int") } var dataSize uint64 m.mu.Lock() @@ -911,27 +913,27 @@ func (m *Message) Marshal() ([]byte, error) { s, err := m.segment(SegmentID(i)) if err != nil { m.mu.Unlock() - return nil, annotatef(err, "marshal") + return nil, exc.WrapError("marshal", err) } n := uint64(len(s.data)) if n%uint64(wordSize) != 0 { m.mu.Unlock() - return nil, errorf("marshal: segment %d not word-aligned", i) + return nil, errors.New("marshal: segment " + fmtIdecimal(i) + " not word-aligned") } if n > uint64(maxSegmentSize) { m.mu.Unlock() - return nil, errorf("marshal: segment %d too large", i) + return nil, errors.New("marshal: segment " + fmtIdecimal(i) + " too large") } dataSize += n if dataSize > uint64(maxInt) { m.mu.Unlock() - return nil, errorf("marshal: message size overflows int") + return nil, errors.New("marshal: message size overflows int") } } total := hdrSize + dataSize if total > uint64(maxInt) { m.mu.Unlock() - return nil, errorf("marshal: message size overflows int") + return nil, errors.New("marshal: message size overflows int") } // Fill buffer. @@ -941,11 +943,11 @@ func (m *Message) Marshal() ([]byte, error) { s, err := m.segment(SegmentID(i)) if err != nil { m.mu.Unlock() - return nil, annotatef(err, "marshal") + return nil, exc.WrapError("marshal", err) } if len(s.data)%int(wordSize) != 0 { m.mu.Unlock() - return nil, errorf("marshal: segment %d not word-aligned", i) + return nil, errors.New("marshal: segment " + fmtIdecimal(i) + " not word-aligned") } binary.LittleEndian.PutUint32(buf[int(i+1)*4:], uint32(len(s.data)/int(wordSize))) buf = append(buf, s.data...) @@ -997,7 +999,7 @@ func (h streamHeader) segmentSize(i SegmentID) (Size, error) { s := binary.LittleEndian.Uint32(h.b[4+i*4:]) sz, ok := wordSize.times(int32(s)) if !ok { - return 0, errorf("segment %d: overflow size", i) + return 0, errors.New("segment " + fmtUdecimal(i) + ": overflow size") } return sz, nil } diff --git a/pointer.go b/pointer.go index b0ee3735..52e78133 100644 --- a/pointer.go +++ b/pointer.go @@ -2,6 +2,8 @@ package capnp import ( "bytes" + + "capnproto.org/go/capnp/v3/exc" ) // A Ptr is a reference to a Cap'n Proto struct, list, or interface. @@ -212,11 +214,11 @@ var _ TypeParam[Ptr] = Ptr{} func unmarshalDefault(def []byte) (Ptr, error) { msg, err := Unmarshal(def) if err != nil { - return Ptr{}, annotatef(err, "read default") + return Ptr{}, exc.WrapError("read default", err) } p, err := msg.Root() if err != nil { - return Ptr{}, annotatef(err, "read default") + return Ptr{}, exc.WrapError("read default", err) } return p, nil } @@ -324,11 +326,11 @@ func Equal(p1, p2 Ptr) (bool, error) { for i := 0; i < n; i++ { sp1, err := s1.Ptr(uint16(i)) if err != nil { - return false, annotatef(err, "equal") + return false, exc.WrapError("equal", err) } sp2, err := s2.Ptr(uint16(i)) if err != nil { - return false, annotatef(err, "equal") + return false, exc.WrapError("equal", err) } if ok, err := Equal(sp1, sp2); !ok || err != nil { return false, err @@ -361,7 +363,7 @@ func Equal(p1, p2 Ptr) (bool, error) { for i := 0; i < l1.Len(); i++ { e1, e2 := l1.Struct(i), l2.Struct(i) if ok, err := Equal(e1.ToPtr(), e2.ToPtr()); err != nil { - return false, annotatef(err, "equal: list element %d", i) + return false, exc.WrapError("equal: list element "+fmtIdecimal(i), err) } else if !ok { return false, nil } diff --git a/rpc/transport/transport.go b/rpc/transport/transport.go index 68c81f10..8d96d09a 100644 --- a/rpc/transport/transport.go +++ b/rpc/transport/transport.go @@ -7,10 +7,12 @@ package transport import ( + "errors" "fmt" "io" capnp "capnproto.org/go/capnp/v3" + "capnproto.org/go/capnp/v3/exc" "capnproto.org/go/capnp/v3/exp/bufferpool" rpccp "capnproto.org/go/capnp/v3/std/capnp/rpc" ) @@ -181,12 +183,12 @@ func (s *transport) RecvMessage() (IncomingMessage, error) { // Close concurrently with any other operations on the transport. func (s *transport) Close() error { if s.closed { - return transporterr.Disconnectedf("already closed").Annotate("", "stream transport") + return transporterr.Disconnected(errors.New("already closed")).Annotate("", "stream transport") } s.closed = true err := s.c.Close() if err != nil { - return transporterr.Annotate(fmt.Errorf("close: %w", err), "stream transport") + return transporterr.Annotate(exc.WrapError("close", err), "stream transport") } return nil } diff --git a/segment.go b/segment.go index bb7b5503..9d3bf72f 100644 --- a/segment.go +++ b/segment.go @@ -2,6 +2,9 @@ package capnp import ( "encoding/binary" + "errors" + + "capnproto.org/go/capnp/v3/exc" ) // A SegmentID is a numeric identifier for a Segment. @@ -115,38 +118,38 @@ func (s *Segment) lookupSegment(id SegmentID) (*Segment, error) { func (s *Segment) readPtr(paddr address, depthLimit uint) (ptr Ptr, err error) { s, base, val, err := s.resolveFarPointer(paddr) if err != nil { - return Ptr{}, annotatef(err, "read pointer") + return Ptr{}, exc.WrapError("read pointer", err) } if val == 0 { return Ptr{}, nil } if depthLimit == 0 { - return Ptr{}, errorf("read pointer: depth limit reached") + return Ptr{}, errors.New("read pointer: depth limit reached") } switch val.pointerType() { case structPointer: sp, err := s.readStructPtr(base, val) if err != nil { - return Ptr{}, annotatef(err, "read pointer") + return Ptr{}, exc.WrapError("read pointer", err) } if !s.msg.canRead(sp.readSize()) { - return Ptr{}, errorf("read pointer: read traversal limit reached") + return Ptr{}, errors.New("read pointer: read traversal limit reached") } sp.depthLimit = depthLimit - 1 return sp.ToPtr(), nil case listPointer: lp, err := s.readListPtr(base, val) if err != nil { - return Ptr{}, annotatef(err, "read pointer") + return Ptr{}, exc.WrapError("read pointer", err) } if !s.msg.canRead(lp.readSize()) { - return Ptr{}, errorf("read pointer: read traversal limit reached") + return Ptr{}, errors.New("read pointer: read traversal limit reached") } lp.depthLimit = depthLimit - 1 return lp.ToPtr(), nil case otherPointer: if val.otherPointerType() != 0 { - return Ptr{}, errorf("read pointer: unknown pointer type") + return Ptr{}, errors.New("read pointer: unknown pointer type") } return Interface{ seg: s, @@ -154,18 +157,18 @@ func (s *Segment) readPtr(paddr address, depthLimit uint) (ptr Ptr, err error) { }.ToPtr(), nil default: // Only other types are far pointers. - return Ptr{}, errorf("read pointer: far pointer landing pad is a far pointer") + return Ptr{}, errors.New("read pointer: far pointer landing pad is a far pointer") } } func (s *Segment) readStructPtr(base address, val rawPointer) (Struct, error) { addr, ok := val.offset().resolve(base) if !ok { - return Struct{}, errorf("struct pointer: invalid address") + return Struct{}, errors.New("struct pointer: invalid address") } sz := val.structSize() if !s.regionInBounds(addr, sz.totalSize()) { - return Struct{}, errorf("struct pointer: invalid address") + return Struct{}, errors.New("struct pointer: invalid address") } return Struct{ seg: s, @@ -177,14 +180,14 @@ func (s *Segment) readStructPtr(base address, val rawPointer) (Struct, error) { func (s *Segment) readListPtr(base address, val rawPointer) (List, error) { addr, ok := val.offset().resolve(base) if !ok { - return List{}, errorf("list pointer: invalid address") + return List{}, errors.New("list pointer: invalid address") } lsize, ok := val.totalListSize() if !ok { - return List{}, errorf("list pointer: size overflow") + return List{}, errors.New("list pointer: size overflow") } if !s.regionInBounds(addr, lsize) { - return List{}, errorf("list pointer: address out of bounds") + return List{}, errors.New("list pointer: address out of bounds") } lt := val.listType() if lt == compositeList { @@ -192,18 +195,18 @@ func (s *Segment) readListPtr(base address, val rawPointer) (List, error) { var ok bool addr, ok = addr.addSize(wordSize) if !ok { - return List{}, errorf("composite list pointer: content address overflow") + return List{}, errors.New("composite list pointer: content address overflow") } if hdr.pointerType() != structPointer { - return List{}, errorf("composite list pointer: tag word is not a struct") + return List{}, errors.New("composite list pointer: tag word is not a struct") } sz := hdr.structSize() n := int32(hdr.offset()) // TODO(someday): check that this has the same end address if tsize, ok := sz.totalSize().times(n); !ok { - return List{}, errorf("composite list pointer: size overflow") + return List{}, errors.New("composite list pointer: size overflow") } else if !s.regionInBounds(addr, tsize) { - return List{}, errorf("composite list pointer: address out of bounds") + return List{}, errors.New("composite list pointer: address out of bounds") } return List{ seg: s, @@ -237,49 +240,49 @@ func (s *Segment) resolveFarPointer(paddr address) (dst *Segment, base address, case doubleFarPointer: padSeg, err := s.lookupSegment(val.farSegment()) if err != nil { - return nil, 0, 0, annotatef(err, "double-far pointer") + return nil, 0, 0, exc.WrapError("double-far pointer", err) } padAddr := val.farAddress() if !padSeg.regionInBounds(padAddr, wordSize*2) { - return nil, 0, 0, errorf("double-far pointer: address out of bounds") + return nil, 0, 0, errors.New("double-far pointer: address out of bounds") } far := padSeg.readRawPointer(padAddr) if far.pointerType() != farPointer { - return nil, 0, 0, errorf("double-far pointer: first word in landing pad is not a far pointer") + return nil, 0, 0, errors.New("double-far pointer: first word in landing pad is not a far pointer") } tagAddr, ok := padAddr.addSize(wordSize) if !ok { - return nil, 0, 0, errorf("double-far pointer: landing pad address overflow") + return nil, 0, 0, errors.New("double-far pointer: landing pad address overflow") } tag := padSeg.readRawPointer(tagAddr) if pt := tag.pointerType(); (pt != structPointer && pt != listPointer) || tag.offset() != 0 { - return nil, 0, 0, errorf("double-far pointer: second word is not a struct or list with zero offset") + return nil, 0, 0, errors.New("double-far pointer: second word is not a struct or list with zero offset") } if dst, err = s.lookupSegment(far.farSegment()); err != nil { - return nil, 0, 0, annotatef(err, "double-far pointer") + return nil, 0, 0, exc.WrapError("double-far pointer", err) } return dst, 0, landingPadNearPointer(far, tag), nil case farPointer: var err error dst, err = s.lookupSegment(val.farSegment()) if err != nil { - return nil, 0, 0, annotatef(err, "far pointer") + return nil, 0, 0, exc.WrapError("far pointer", err) } padAddr := val.farAddress() if !dst.regionInBounds(padAddr, wordSize) { - return nil, 0, 0, errorf("far pointer: address out of bounds") + return nil, 0, 0, errors.New("far pointer: address out of bounds") } var ok bool base, ok = padAddr.addSize(wordSize) if !ok { - return nil, 0, 0, errorf("far pointer: landing pad address overflow") + return nil, 0, 0, errors.New("far pointer: landing pad address overflow") } return dst, base, dst.readRawPointer(padAddr), nil default: var ok bool base, ok = paddr.addSize(wordSize) if !ok { - return nil, 0, 0, errorf("pointer base address overflow") + return nil, 0, 0, errors.New("pointer base address overflow") } return s, base, val, nil } @@ -307,7 +310,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { if forceCopy || src.seg.msg != s.msg || st.flags&isListMember != 0 { newSeg, newAddr, err := alloc(s, st.size.totalSize()) if err != nil { - return annotatef(err, "write pointer: copy") + return exc.WrapError("write pointer: copy", err) } dst := Struct{ seg: newSeg, @@ -317,7 +320,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { // clear flags } if err := copyStruct(dst, st); err != nil { - return annotatef(err, "write pointer") + return exc.WrapError("write pointer", err) } st = dst src = dst.ToPtr() @@ -330,7 +333,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { sz := l.allocSize() newSeg, newAddr, err := alloc(s, sz) if err != nil { - return annotatef(err, "write pointer: copy") + return exc.WrapError("write pointer: copy", err) } dst := List{ seg: newSeg, @@ -346,7 +349,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { var ok bool dst.off, ok = dst.off.addSize(wordSize) if !ok { - return errorf("write pointer: copy composite list: content address overflow") + return errors.New("write pointer: copy composite list: content address overflow") } sz -= wordSize } @@ -357,7 +360,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { for i := 0; i < l.Len(); i++ { err := copyStruct(dst.Struct(i), l.Struct(i)) if err != nil { - return annotatef(err, "write pointer: copy list element %d", i) + return exc.WrapError("write pointer: copy list element"+fmtIdecimal(i), err) } } } @@ -397,7 +400,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { // Not enough room for a landing pad, need to use a double-far pointer. padSeg, padAddr, err := alloc(s, wordSize*2) if err != nil { - return annotatef(err, "write pointer: make landing pad") + return exc.WrapError("write pointer: make landing pad", err) } padSeg.writeRawPointer(padAddr, rawFarPointer(src.seg.id, srcAddr)) padSeg.writeRawPointer(padAddr.addSizeUnchecked(wordSize), srcRaw) diff --git a/struct.go b/struct.go index 8d1c94a1..35038f1f 100644 --- a/struct.go +++ b/struct.go @@ -1,5 +1,11 @@ package capnp +import ( + "errors" + + "capnproto.org/go/capnp/v3/exc" +) + // Struct is a pointer to a struct. type Struct StructKind @@ -17,12 +23,12 @@ type StructKind = struct { // NewStruct creates a new struct, preferring placement in s. func NewStruct(s *Segment, sz ObjectSize) (Struct, error) { if !sz.isValid() { - return Struct{}, errorf("new struct: invalid size") + return Struct{}, errors.New("new struct: invalid size") } sz.DataSize = sz.DataSize.padToWord() seg, addr, err := alloc(s, sz.totalSize()) if err != nil { - return Struct{}, annotatef(err, "new struct") + return Struct{}, exc.WrapError("new struct", err) } return Struct{ seg: seg, @@ -87,7 +93,7 @@ func (p Struct) Size() ObjectSize { // with future versions of the protocol. func (p Struct) CopyFrom(other Struct) error { if err := copyStruct(p, other); err != nil { - return annotatef(err, "copy struct") + return exc.WrapError("copy struct", err) } return nil } @@ -333,11 +339,11 @@ func copyStruct(dst, src Struct) error { dstAddr, _ := dstPtrSect.element(int32(j), wordSize) m, err := src.seg.readPtr(srcAddr, src.depthLimit) if err != nil { - return annotatef(err, "copy struct pointer %d", j) + return exc.WrapError("copy struct pointer "+fmtUdecimal(j), err) } err = dst.seg.writePtr(dstAddr, m, true) if err != nil { - return annotatef(err, "copy struct pointer %d", j) + return exc.WrapError("copy struct pointer "+fmtUdecimal(j), err) } } for j := numSrcPtrs; j < numDstPtrs; j++ { From 40dbc4fae1c9dfa255f936b7542a956a824b5975 Mon Sep 17 00:00:00 2001 From: Ian Denhardt Date: Sun, 1 Jan 2023 20:59:19 -0500 Subject: [PATCH 2/4] Get rid of format helpers. i.e. wrappers around fmt.Errorf and such. This mostly leaves direct calls to the fmt package. Note: the stuff from fmt.go has been moved into a package in internal, so it can be used outside of the main package. --- address.go | 24 +++++--- answer.go | 3 +- canonical.go | 19 +++--- capability.go | 7 ++- exc/exc.go | 12 ++++ fmt.go | 28 --------- list.go | 9 +-- message.go | 33 +++++------ pointer.go | 3 +- rpc/answer.go | 17 +++--- rpc/export.go | 18 +++--- rpc/import.go | 19 +++--- rpc/question.go | 20 +++---- rpc/rpc.go | 147 +++++++++++++++++++++++++++++++---------------- segment.go | 3 +- server/answer.go | 3 +- server/server.go | 4 -- struct.go | 5 +- 18 files changed, 210 insertions(+), 164 deletions(-) delete mode 100644 fmt.go diff --git a/address.go b/address.go index a8898313..81f910d5 100644 --- a/address.go +++ b/address.go @@ -2,8 +2,14 @@ package capnp import ( "strconv" + + "capnproto.org/go/capnp/v3/internal/str" ) +// An address is an index inside a segment's data (in bytes). +// It is bounded to [0, maxSegmentSize). +type address uint32 + // String returns the address in hex format. func (a address) String() string { return strconv.FormatUint(uint64(a), 16) @@ -58,12 +64,12 @@ func (sz Size) String() string { if sz == 1 { return "1 byte" } - return fmtUdecimal(sz) + " bytes" + return str.Utod(sz) + " bytes" } // GoString returns the size as a Go expression. func (sz Size) GoString() string { - return "capnp.Size(" + fmtUdecimal(sz) + ")" + return "capnp.Size(" + str.Utod(sz) + ")" } // times returns the size sz*n. ok is false if the result would be @@ -120,12 +126,12 @@ func (off DataOffset) String() string { if off == 1 { return "+1 byte" } - return "+" + fmtUdecimal(off) + " bytes" + return "+" + str.Utod(off) + " bytes" } // GoString returns the offset as a Go expression. func (off DataOffset) GoString() string { - return "capnp.DataOffset(" + fmtUdecimal(off) + ")" + return "capnp.DataOffset(" + str.Utod(off) + ")" } // ObjectSize records section sizes for a struct or list. @@ -177,13 +183,13 @@ func (sz ObjectSize) totalWordCount() int32 { // String returns a short, human readable representation of the object // size. func (sz ObjectSize) String() string { - return "{datasz=" + fmtUdecimal(sz.DataSize) + " ptrs=" + fmtUdecimal(sz.PointerCount) + "}" + return "{datasz=" + str.Utod(sz.DataSize) + " ptrs=" + str.Utod(sz.PointerCount) + "}" } // GoString formats the ObjectSize as a keyed struct literal. func (sz ObjectSize) GoString() string { - return "capnp.ObjectSize{DataSize: " + fmtUdecimal(sz.DataSize) + - ", PointerCount: " + fmtUdecimal(sz.PointerCount) + "}" + return "capnp.ObjectSize{DataSize: " + str.Utod(sz.DataSize) + + ", PointerCount: " + str.Utod(sz.PointerCount) + "}" } // BitOffset is an offset in bits from the beginning of a struct's data @@ -202,10 +208,10 @@ func (bit BitOffset) mask() byte { // String returns the offset in the format "bit X". func (bit BitOffset) String() string { - return "bit " + fmtUdecimal(bit) + return "bit " + str.Utod(bit) } // GoString returns the offset as a Go expression. func (bit BitOffset) GoString() string { - return "capnp.BitOffset(" + fmtUdecimal(bit) + ")" + return "capnp.BitOffset(" + str.Utod(bit) + ")" } diff --git a/answer.go b/answer.go index 2aa46637..76baa2ed 100644 --- a/answer.go +++ b/answer.go @@ -7,6 +7,7 @@ import ( "sync" "capnproto.org/go/capnp/v3/exc" + "capnproto.org/go/capnp/v3/internal/str" "capnproto.org/go/capnp/v3/internal/syncutil" ) @@ -592,7 +593,7 @@ func Transform(p Ptr, transform []PipelineOp) (Ptr, error) { } func newTransformError(index int, field uint16, err error, withDefault bool) error { - msg := "transform: op " + fmtIdecimal(index) + ": pointer field " + fmtUdecimal(field) + msg := "transform: op " + str.Itod(index) + ": pointer field " + str.Utod(field) if withDefault { msg += " with default" } diff --git a/canonical.go b/canonical.go index bb47a801..85db07d8 100644 --- a/canonical.go +++ b/canonical.go @@ -1,6 +1,9 @@ package capnp -import "capnproto.org/go/capnp/v3/exc" +import ( + "capnproto.org/go/capnp/v3/exc" + "capnproto.org/go/capnp/v3/internal/str" +) // Canonicalize encodes a struct into its canonical form: a single- // segment blob without a segment table. The result will be identical @@ -57,14 +60,14 @@ func fillCanonicalStruct(dst, s Struct) error { for i := uint16(0); i < dst.size.PointerCount; i++ { p, err := s.Ptr(i) if err != nil { - return exc.WrapError("struct pointer "+fmtUdecimal(i), err) + return exc.WrapError("struct pointer "+str.Utod(i), err) } cp, err := canonicalPtr(dst.seg, p) if err != nil { - return exc.WrapError("struct pointer "+fmtUdecimal(i), err) + return exc.WrapError("struct pointer "+str.Utod(i), err) } if err := dst.SetPtr(i, cp); err != nil { - return exc.WrapError("struct pointer "+fmtUdecimal(i), err) + return exc.WrapError("struct pointer "+str.Utod(i), err) } } return nil @@ -122,14 +125,14 @@ func canonicalList(dst *Segment, l List) (List, error) { for i := 0; i < l.Len(); i++ { p, err := PointerList(l).At(i) if err != nil { - return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) + return List{}, exc.WrapError("list element "+str.Itod(i), err) } cp, err := canonicalPtr(dst, p) if err != nil { - return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) + return List{}, exc.WrapError("list element "+str.Itod(i), err) } if err := cl.Set(i, cp); err != nil { - return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) + return List{}, exc.WrapError("list element "+str.Itod(i), err) } } return List(cl), nil @@ -152,7 +155,7 @@ func canonicalList(dst *Segment, l List) (List, error) { } for i := 0; i < cl.Len(); i++ { if err := fillCanonicalStruct(cl.Struct(i), l.Struct(i)); err != nil { - return List{}, exc.WrapError("list element "+fmtIdecimal(i), err) + return List{}, exc.WrapError("list element "+str.Itod(i), err) } } return cl, nil diff --git a/capability.go b/capability.go index 460bb759..bb110124 100644 --- a/capability.go +++ b/capability.go @@ -10,6 +10,7 @@ import ( "capnproto.org/go/capnp/v3/exp/bufferpool" "capnproto.org/go/capnp/v3/flowcontrol" + "capnproto.org/go/capnp/v3/internal/str" "capnproto.org/go/capnp/v3/internal/syncutil" ) @@ -95,12 +96,12 @@ type CapabilityID uint32 // String returns the ID in the format "capability X". func (id CapabilityID) String() string { - return "capability " + fmtUdecimal(id) + return "capability " + str.Utod(id) } // GoString returns the ID as a Go expression. func (id CapabilityID) GoString() string { - return "capnp.CapabilityID(" + fmtUdecimal(id) + ")" + return "capnp.CapabilityID(" + str.Utod(id) + ")" } // A Client is a reference to a Cap'n Proto capability. @@ -656,7 +657,7 @@ func finalizeClient(c *client) { msg = "leaked client created by " + fname } else { msg = "leaked client created by " + fname + " on " + - c.creatorFile + ":" + fmtIdecimal(c.creatorLine) + c.creatorFile + ":" + str.Itod(c.creatorLine) } if c.creatorStack != "" { msg += "\nCreation stack trace:\n" + c.creatorStack + "\n" diff --git a/exc/exc.go b/exc/exc.go index c55506a3..165df3f5 100644 --- a/exc/exc.go +++ b/exc/exc.go @@ -107,14 +107,26 @@ func (f Annotator) Failed(err error) *Exception { return f.New(Failed, err) } +func (f Annotator) WrapFailed(msg string, err error) *Exception { + return f.New(Failed, WrapError(msg, err)) +} + func (f Annotator) Disconnected(err error) *Exception { return f.New(Disconnected, err) } +func (f Annotator) WrapDisconnected(msg string, err error) *Exception { + return f.New(Disconnected, WrapError(msg, err)) +} + func (f Annotator) Unimplemented(err error) *Exception { return f.New(Unimplemented, err) } +func (f Annotator) WrapUnimplemented(msg string, err error) *Exception { + return f.New(Unimplemented, WrapError(msg, err)) +} + func (f Annotator) Annotate(err error, msg string) *Exception { return Annotate(string(f), msg, err) } diff --git a/fmt.go b/fmt.go deleted file mode 100644 index dce3d87b..00000000 --- a/fmt.go +++ /dev/null @@ -1,28 +0,0 @@ -package capnp - -// Helpers for formatting strings. We avoid the use of the fmt package for the benefit -// of environments that care about minimizing executable size. - -import "strconv" - -// An address is an index inside a segment's data (in bytes). -// It is bounded to [0, maxSegmentSize). -type address uint32 - -// fmtUdecimal is a helper for formatting unsigned integers as decimals. -func fmtUdecimal[T isUint](n T) string { - return strconv.FormatUint(uint64(n), 10) -} - -// Like fmtIdecimal, but for signed integers. -func fmtIdecimal[T isInt](n T) string { - return strconv.FormatInt(int64(n), 10) -} - -type isUint interface { - ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uint -} - -type isInt interface { - ~int8 | ~int16 | ~int32 | ~int64 | ~int -} diff --git a/list.go b/list.go index 085c21ab..fe69e3f7 100644 --- a/list.go +++ b/list.go @@ -8,6 +8,7 @@ import ( "strconv" "capnproto.org/go/capnp/v3/exc" + "capnproto.org/go/capnp/v3/internal/str" "capnproto.org/go/capnp/v3/internal/strquote" ) @@ -200,7 +201,7 @@ func (p List) primitiveElem(i int, expectedSize ObjectSize) (address, error) { } addr, ok := p.off.element(int32(i), p.size.totalSize()) if !ok { - return 0, errors.New("read list element " + fmtIdecimal(i) + ": address overflow") + return 0, errors.New("read list element " + str.Itod(i) + ": address overflow") } return addr, nil } @@ -233,7 +234,7 @@ func (p List) SetStruct(i int, s Struct) error { return errors.New("SetStruct called on bit list") } if err := copyStruct(p.Struct(i), s); err != nil { - return exc.WrapError("set list element "+fmtIdecimal(i), err) + return exc.WrapError("set list element "+str.Itod(i), err) } return nil } @@ -259,7 +260,7 @@ func NewBitList(s *Segment, n int32) (BitList, error) { } s, addr, err := alloc(s, bitListSize(n)) if err != nil { - return BitList{}, exc.WrapError("new "+fmtIdecimal(n)+"-element bit list", err) + return BitList{}, exc.WrapError("new "+str.Itod(n)+"-element bit list", err) } return BitList{ seg: s, @@ -341,7 +342,7 @@ func NewPointerList(s *Segment, n int32) (PointerList, error) { } s, addr, err := alloc(s, total) if err != nil { - return PointerList{}, exc.WrapError("new "+fmtIdecimal(n)+"-element pointer list", err) + return PointerList{}, exc.WrapError("new "+str.Itod(n)+"-element pointer list", err) } return PointerList{ seg: s, diff --git a/message.go b/message.go index f76a7cd4..22124ebe 100644 --- a/message.go +++ b/message.go @@ -10,6 +10,7 @@ import ( "capnproto.org/go/capnp/v3/exc" "capnproto.org/go/capnp/v3/exp/bufferpool" + "capnproto.org/go/capnp/v3/internal/str" "capnproto.org/go/capnp/v3/packed" ) @@ -247,7 +248,7 @@ func (m *Message) NumSegments() int64 { // Segment returns the segment with the given ID. func (m *Message) Segment(id SegmentID) (*Segment, error) { if int64(id) >= m.Arena.NumSegments() { - return nil, errors.New("segment " + fmtUdecimal(id) + ": out of bounds") + return nil, errors.New("segment " + str.Utod(id) + ": out of bounds") } m.mu.Lock() seg, err := m.segment(id) @@ -265,11 +266,11 @@ func (m *Message) segment(id SegmentID) (*Segment, error) { return s, nil } if len(m.segs) == maxInt { - return nil, errors.New("segment " + fmtUdecimal(id) + ": number of loaded segments exceeds int") + return nil, errors.New("segment " + str.Utod(id) + ": number of loaded segments exceeds int") } data, err := m.Arena.Data(id) if err != nil { - return nil, exc.WrapError("load segment "+fmtUdecimal(id), err) + return nil, exc.WrapError("load segment "+str.Utod(id), err) } s := m.setSegment(id, data) return s, nil @@ -426,7 +427,7 @@ func (ssa SingleSegmentArena) NumSegments() int64 { func (ssa SingleSegmentArena) Data(id SegmentID) ([]byte, error) { if id != 0 { - return nil, errors.New("segment " + fmtUdecimal(id) + " requested in single segment arena") + return nil, errors.New("segment " + str.Utod(id) + " requested in single segment arena") } return ssa, nil } @@ -453,7 +454,7 @@ func (ssa *SingleSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (S } func (ssa SingleSegmentArena) String() string { - return "single-segment arena [len=" + fmtIdecimal(len(ssa)) + " cap=" + fmtIdecimal(cap(ssa)) + "]" + return "single-segment arena [len=" + str.Itod(len(ssa)) + " cap=" + str.Itod(cap(ssa)) + "]" } type roSingleSegment []byte @@ -464,7 +465,7 @@ func (ss roSingleSegment) NumSegments() int64 { func (ss roSingleSegment) Data(id SegmentID) ([]byte, error) { if id != 0 { - return nil, errors.New("segment " + fmtUdecimal(id) + " requested in single segment arena") + return nil, errors.New("segment " + str.Utod(id) + " requested in single segment arena") } return ss, nil } @@ -474,7 +475,7 @@ func (ss roSingleSegment) Allocate(sz Size, segs map[SegmentID]*Segment) (Segmen } func (ss roSingleSegment) String() string { - return "read-only single-segment arena [len=" + fmtIdecimal(len(ss)) + "]" + return "read-only single-segment arena [len=" + str.Itod(len(ss)) + "]" } // MultiSegment is an arena that stores object data across multiple []byte @@ -551,8 +552,8 @@ func (msa *MultiSegmentArena) NumSegments() int64 { func (msa *MultiSegmentArena) Data(id SegmentID) ([]byte, error) { if int64(id) >= int64(len(*msa)) { - return nil, errors.New("segment " + fmtUdecimal(id) + " requested (arena only has " + - fmtIdecimal(len(*msa)) + " segments)") + return nil, errors.New("segment " + str.Utod(id) + " requested (arena only has " + + str.Itod(len(*msa)) + " segments)") } return (*msa)[id], nil } @@ -574,7 +575,7 @@ func (msa *MultiSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (Se total += int64(cap(data)) if total < 0 { // Overflow. - return 0, nil, errors.New("alloc " + fmtUdecimal(sz) + " bytes: message too large") + return 0, nil, errors.New("alloc " + str.Utod(sz) + " bytes: message too large") } } n, err := nextAlloc(total, 1<<63-1, sz) @@ -588,7 +589,7 @@ func (msa *MultiSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (Se } func (msa *MultiSegmentArena) String() string { - return "multi-segment arena [" + fmtIdecimal(len(*msa)) + " segments]" + return "multi-segment arena [" + str.Itod(len(*msa)) + " segments]" } // nextAlloc computes how much more space to allocate given the number @@ -878,7 +879,7 @@ func (e *Encoder) Encode(m *Message) error { } n := len(s.data) if int64(n) > int64(maxSegmentSize) { - return errors.New("encode: segment " + fmtIdecimal(i) + " too large") + return errors.New("encode: segment " + str.Itod(i) + " too large") } e.hdrbuf = appendUint32(e.hdrbuf, uint32(Size(n)/wordSize)) e.bufs = append(e.bufs, s.data) @@ -918,11 +919,11 @@ func (m *Message) Marshal() ([]byte, error) { n := uint64(len(s.data)) if n%uint64(wordSize) != 0 { m.mu.Unlock() - return nil, errors.New("marshal: segment " + fmtIdecimal(i) + " not word-aligned") + return nil, errors.New("marshal: segment " + str.Itod(i) + " not word-aligned") } if n > uint64(maxSegmentSize) { m.mu.Unlock() - return nil, errors.New("marshal: segment " + fmtIdecimal(i) + " too large") + return nil, errors.New("marshal: segment " + str.Itod(i) + " too large") } dataSize += n if dataSize > uint64(maxInt) { @@ -947,7 +948,7 @@ func (m *Message) Marshal() ([]byte, error) { } if len(s.data)%int(wordSize) != 0 { m.mu.Unlock() - return nil, errors.New("marshal: segment " + fmtIdecimal(i) + " not word-aligned") + return nil, errors.New("marshal: segment " + str.Itod(i) + " not word-aligned") } binary.LittleEndian.PutUint32(buf[int(i+1)*4:], uint32(len(s.data)/int(wordSize))) buf = append(buf, s.data...) @@ -999,7 +1000,7 @@ func (h streamHeader) segmentSize(i SegmentID) (Size, error) { s := binary.LittleEndian.Uint32(h.b[4+i*4:]) sz, ok := wordSize.times(int32(s)) if !ok { - return 0, errors.New("segment " + fmtUdecimal(i) + ": overflow size") + return 0, errors.New("segment " + str.Utod(i) + ": overflow size") } return sz, nil } diff --git a/pointer.go b/pointer.go index 52e78133..1b0dba74 100644 --- a/pointer.go +++ b/pointer.go @@ -4,6 +4,7 @@ import ( "bytes" "capnproto.org/go/capnp/v3/exc" + "capnproto.org/go/capnp/v3/internal/str" ) // A Ptr is a reference to a Cap'n Proto struct, list, or interface. @@ -363,7 +364,7 @@ func Equal(p1, p2 Ptr) (bool, error) { for i := 0; i < l1.Len(); i++ { e1, e2 := l1.Struct(i), l2.Struct(i) if ok, err := Equal(e1.ToPtr(), e2.ToPtr()); err != nil { - return false, exc.WrapError("equal: list element "+fmtIdecimal(i), err) + return false, exc.WrapError("equal: list element "+str.Itod(i), err) } else if !ok { return false, nil } diff --git a/rpc/answer.go b/rpc/answer.go index e3b3952b..bf6777b7 100644 --- a/rpc/answer.go +++ b/rpc/answer.go @@ -2,6 +2,7 @@ package rpc import ( "context" + "errors" "fmt" "sync" @@ -101,12 +102,12 @@ func errorAnswer(c *Conn, id answerID, err error) *answer { func (c *Conn) newReturn() (_ rpccp.Return, sendMsg func(), _ *rc.Releaser, _ error) { outMsg, err := c.transport.NewMessage() if err != nil { - return rpccp.Return{}, nil, nil, rpcerr.Failedf("create return: %w", err) + return rpccp.Return{}, nil, nil, rpcerr.WrapFailed("create return", err) } ret, err := outMsg.Message.NewReturn() if err != nil { outMsg.Release() - return rpccp.Return{}, nil, nil, rpcerr.Failedf("create return: %w", err) + return rpccp.Return{}, nil, nil, rpcerr.WrapFailed("create return", err) } // Before releasing the message, we need to wait both until it is sent and @@ -146,14 +147,14 @@ func (ans *answer) AllocResults(sz capnp.ObjectSize) (capnp.Struct, error) { var err error ans.results, err = ans.ret.NewResults() if err != nil { - return capnp.Struct{}, rpcerr.Failedf("alloc results: %w", err) + return capnp.Struct{}, rpcerr.WrapFailed("alloc results", err) } s, err := capnp.NewStruct(ans.results.Segment(), sz) if err != nil { - return capnp.Struct{}, rpcerr.Failedf("alloc results: %w", err) + return capnp.Struct{}, rpcerr.WrapFailed("alloc results", err) } if err := ans.results.SetContent(s.ToPtr()); err != nil { - return capnp.Struct{}, rpcerr.Failedf("alloc results: %w", err) + return capnp.Struct{}, rpcerr.WrapFailed("alloc results", err) } return s, nil } @@ -170,11 +171,11 @@ func (ans *answer) setBootstrap(c capnp.Client) error { var err error ans.results, err = ans.ret.NewResults() if err != nil { - return rpcerr.Failedf("alloc bootstrap results: %w", err) + return rpcerr.WrapFailed("alloc bootstrap results", err) } iface := capnp.NewInterface(ans.results.Segment(), 0) if err := ans.results.SetContent(iface.ToPtr()); err != nil { - return rpcerr.Failedf("alloc bootstrap results: %w", err) + return rpcerr.WrapFailed("alloc bootstrap results", err) } return nil } @@ -249,7 +250,7 @@ func (ans *answer) sendReturn(c *lockedConn, rl *releaseList) error { // ok to return an error if the finish comes in // before the return. Possible enhancement: use // the cancel variant of return. - ans.promise.Reject(rpcerr.Failedf("received finish before return")) + ans.promise.Reject(rpcerr.Failed(errors.New("received finish before return"))) } else { ans.promise.Resolve(ans.results.Content()) } diff --git a/rpc/export.go b/rpc/export.go index ce3ad4a7..d8ac0aa8 100644 --- a/rpc/export.go +++ b/rpc/export.go @@ -2,8 +2,10 @@ package rpc import ( "context" + "errors" "capnproto.org/go/capnp/v3" + "capnproto.org/go/capnp/v3/internal/str" "capnproto.org/go/capnp/v3/internal/syncutil" rpccp "capnproto.org/go/capnp/v3/std/capnp/rpc" ) @@ -58,7 +60,7 @@ func (c *Conn) findExport(id exportID) *expent { func (c *Conn) releaseExport(id exportID, count uint32) (capnp.Client, error) { ent := c.findExport(id) if ent == nil { - return capnp.Client{}, rpcerr.Failedf("unknown export ID %d", id) + return capnp.Client{}, rpcerr.Failed(errors.New("unknown export ID " + str.Utod(id))) } switch { case count == ent.wireRefs: @@ -71,7 +73,7 @@ func (c *Conn) releaseExport(id exportID, count uint32) (capnp.Client, error) { }) return client, nil case count > ent.wireRefs: - return capnp.Client{}, rpcerr.Failedf("export ID %d released too many references", id) + return capnp.Client{}, rpcerr.Failed(errors.New("export ID " + str.Utod(id) + " released too many references")) default: ent.wireRefs -= count return capnp.Client{}, nil @@ -179,13 +181,13 @@ func (c *lockedConn) fillPayloadCapTable(payload rpccp.Payload) (map[exportID]ui } list, err := payload.NewCapTable(int32(len(clients))) if err != nil { - return nil, rpcerr.Failedf("payload capability table: %w", err) + return nil, rpcerr.WrapFailed("payload capability table", err) } var refs map[exportID]uint32 for i, client := range clients { id, isExport, err := c.sendCap(list.At(i), client) if err != nil { - return nil, rpcerr.Failedf("Serializing capability: %w", err) + return nil, rpcerr.WrapFailed("Serializing capability", err) } if !isExport { continue @@ -278,19 +280,19 @@ type senderLoopback struct { func (sl *senderLoopback) buildDisembargo(msg rpccp.Message) error { d, err := msg.NewDisembargo() if err != nil { - return rpcerr.Failedf("build disembargo: %w", err) + return rpcerr.WrapFailed("build disembargo", err) } tgt, err := d.NewTarget() if err != nil { - return rpcerr.Failedf("build disembargo: %w", err) + return rpcerr.WrapFailed("build disembargo", err) } pa, err := tgt.NewPromisedAnswer() if err != nil { - return rpcerr.Failedf("build disembargo: %w", err) + return rpcerr.WrapFailed("build disembargo", err) } oplist, err := pa.NewTransform(int32(len(sl.transform))) if err != nil { - return rpcerr.Failedf("build disembargo: %w", err) + return rpcerr.WrapFailed("build disembargo", err) } d.Context().SetSenderLoopback(uint32(sl.id)) diff --git a/rpc/import.go b/rpc/import.go index 03fc413f..49151b70 100644 --- a/rpc/import.go +++ b/rpc/import.go @@ -2,6 +2,7 @@ package rpc import ( "context" + "errors" "capnproto.org/go/capnp/v3" "capnproto.org/go/capnp/v3/internal/syncutil" @@ -91,7 +92,7 @@ func (ic *importClient) Send(ctx context.Context, s capnp.Send) (*capnp.Answer, defer c.tasks.Done() ent := c.lk.imports[ic.id] if ent == nil || ic.generation != ent.generation { - return capnp.ErrorAnswer(s.Method, rpcerr.Disconnectedf("send on closed import")), func() {} + return capnp.ErrorAnswer(s.Method, rpcerr.Disconnected(errors.New("send on closed import"))), func() {} } q := c.newQuestion(s.Method) @@ -103,7 +104,7 @@ func (ic *importClient) Send(ctx context.Context, s capnp.Send) (*capnp.Answer, syncutil.With(&ic.c.lk, func() { ic.c.lk.questions[q.id] = nil }) - q.p.Reject(rpcerr.Failedf("send message: %w", err)) + q.p.Reject(rpcerr.WrapFailed("send message", err)) syncutil.With(&ic.c.lk, func() { ic.c.lk.questionID.remove(uint32(q.id)) }) @@ -130,38 +131,38 @@ func (ic *importClient) Send(ctx context.Context, s capnp.Send) (*capnp.Answer, func (c *lockedConn) newImportCallMessage(msg rpccp.Message, imp importID, qid questionID, s capnp.Send) error { call, err := msg.NewCall() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } call.SetQuestionId(uint32(qid)) call.SetInterfaceId(s.Method.InterfaceID) call.SetMethodId(s.Method.MethodID) target, err := call.NewTarget() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } target.SetImportedCap(uint32(imp)) payload, err := call.NewParams() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } args, err := capnp.NewStruct(payload.Segment(), s.ArgsSize) if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } if err := payload.SetContent(args.ToPtr()); err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } if s.PlaceArgs == nil { return nil } if err := s.PlaceArgs(args); err != nil { - return rpcerr.Failedf("place arguments: %w", err) + return rpcerr.WrapFailed("place arguments", err) } // TODO(soon): save param refs _, err = c.fillPayloadCapTable(payload) if err != nil { - return rpcerr.Annotatef(err, "build call message") + return rpcerr.Annotate(err, "build call message") } return nil } diff --git a/rpc/question.go b/rpc/question.go index db589196..1ea07efe 100644 --- a/rpc/question.go +++ b/rpc/question.go @@ -165,7 +165,7 @@ func (q *question) PipelineSend(ctx context.Context, transform []capnp.PipelineO syncutil.With(&q.c.lk, func() { q.c.lk.questions[q2.id] = nil }) - q2.p.Reject(rpcerr.Failedf("send message: %w", err)) + q2.p.Reject(rpcerr.WrapFailed("send message", err)) syncutil.With(&q.c.lk, func() { q.c.lk.questionID.remove(uint32(q2.id)) }) @@ -192,7 +192,7 @@ func (q *question) PipelineSend(ctx context.Context, transform []capnp.PipelineO func (c *lockedConn) newPipelineCallMessage(msg rpccp.Message, tgt questionID, transform []capnp.PipelineOp, qid questionID, s capnp.Send) error { call, err := msg.NewCall() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } call.SetQuestionId(uint32(qid)) call.SetInterfaceId(s.Method.InterfaceID) @@ -200,16 +200,16 @@ func (c *lockedConn) newPipelineCallMessage(msg rpccp.Message, tgt questionID, t target, err := call.NewTarget() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } pa, err := target.NewPromisedAnswer() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } pa.SetQuestionId(uint32(tgt)) oplist, err := pa.NewTransform(int32(len(transform))) if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } for i, op := range transform { oplist.At(i).SetGetPointerField(op.Field) @@ -217,27 +217,27 @@ func (c *lockedConn) newPipelineCallMessage(msg rpccp.Message, tgt questionID, t payload, err := call.NewParams() if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } args, err := capnp.NewStruct(payload.Segment(), s.ArgsSize) if err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } if err := payload.SetContent(args.ToPtr()); err != nil { - return rpcerr.Failedf("build call message: %w", err) + return rpcerr.WrapFailed("build call message", err) } if s.PlaceArgs == nil { return nil } if err := s.PlaceArgs(args); err != nil { - return rpcerr.Failedf("place arguments: %w", err) + return rpcerr.WrapFailed("place arguments", err) } // TODO(soon): save param refs _, err = c.fillPayloadCapTable(payload) if err != nil { - return rpcerr.Annotatef(err, "build call message") + return rpcerr.Annotate(err, "build call message") } return err diff --git a/rpc/rpc.go b/rpc/rpc.go index 7c747851..3ba132dd 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -13,6 +13,7 @@ import ( "capnproto.org/go/capnp/v3" "capnproto.org/go/capnp/v3/exc" "capnproto.org/go/capnp/v3/exp/spsc" + "capnproto.org/go/capnp/v3/internal/str" "capnproto.org/go/capnp/v3/internal/syncutil" "capnproto.org/go/capnp/v3/rpc/transport" rpccp "capnproto.org/go/capnp/v3/std/capnp/rpc" @@ -285,7 +286,7 @@ func (c *Conn) Bootstrap(ctx context.Context) (bc capnp.Client) { // Start a background task to prevent the conn from shutting down // while sending the bootstrap message. if !c.startTask() { - return capnp.ErrorClient(rpcerr.Disconnectedf("connection closed")) + return capnp.ErrorClient(rpcerr.Disconnected(errors.New("connection closed"))) } defer c.tasks.Done() @@ -387,7 +388,7 @@ func (c *Conn) shutdown(abortErr error) (err error) { case <-time.After(c.abortTimeout): } if err = c.transport.Close(); err != nil { - err = rpcerr.Failedf("close transport: %w", err) + err = rpcerr.WrapFailed("close transport", err) } }() @@ -668,7 +669,7 @@ func (c *Conn) receive() error { c.sendMessage(ctx, func(m rpccp.Message) error { defer release() if err := m.SetUnimplemented(recv); err != nil { - return rpcerr.Annotatef(err, "send unimplemented") + return rpcerr.Annotate(err, "send unimplemented") } return nil }, nil) @@ -695,7 +696,7 @@ func (c *Conn) handleBootstrap(ctx context.Context, id answerID) error { c.withLocked(func(c *lockedConn) { if c.lk.answers[id] != nil { rl.Add(ans.msgReleaser.Decr) - err = rpcerr.Failedf("incoming bootstrap: answer ID %d reused", id) + err = rpcerr.Failed(errors.New("incoming bootstrap: answer ID " + str.Utod(id) + " reused")) return } @@ -752,16 +753,16 @@ func (c *Conn) handleCall(ctx context.Context, call rpccp.Call, releaseCall capn mm, err := m.NewUnimplemented() if err != nil { - return rpcerr.Annotatef(err, "incoming call: send unimplemented") + return rpcerr.Annotate(err, "incoming call: send unimplemented") } if err = mm.SetCall(call); err != nil { - return rpcerr.Annotatef(err, "incoming call: send unimplemented") + return rpcerr.Annotate(err, "incoming call: send unimplemented") } return nil }, func(err error) { - c.er.ReportError(rpcerr.Annotatef(err, "incoming call: send unimplemented")) + c.er.ReportError(rpcerr.Annotate(err, "incoming call: send unimplemented")) }) }) @@ -776,7 +777,7 @@ func (c *Conn) handleCall(ctx context.Context, call rpccp.Call, releaseCall capn syncutil.With(&c.lk, func() { if c.lk.answers[id] != nil { rl.Add(releaseCall) - err = rpcerr.Failedf("incoming call: answer ID %d reused", id) + err = rpcerr.Failed(errors.New("incoming call: answer ID " + str.Utod(id) + "reused")) return } @@ -840,7 +841,7 @@ func (c *Conn) handleCall(ctx context.Context, call rpccp.Call, releaseCall capn retReleaser.Decr() releaseCall() }) - return rpcerr.Failedf("incoming call: unknown export ID %d", id) + return rpcerr.Failed(errors.New("incoming call: unknown export ID " + str.Utod(id))) } c.tasks.Add(1) // will be finished by answer.Return var callCtx context.Context @@ -863,7 +864,11 @@ func (c *Conn) handleCall(ctx context.Context, call rpccp.Call, releaseCall capn retReleaser.Decr() releaseCall() }) - return rpcerr.Failedf("incoming call: use of unknown or finished answer ID %d for promised answer target", p.target.promisedAnswer) + return rpcerr.Failed(errors.New( + "incoming call: use of unknown or finished answer ID " + + str.Utod(p.target.promisedAnswer) + + " for promised answer target", + )) } if tgtAns.flags.Contains(resultsReady) { if tgtAns.err != nil { @@ -877,7 +882,7 @@ func (c *Conn) handleCall(ctx context.Context, call rpccp.Call, releaseCall capn // happening on the receive goroutine. content, err := tgtAns.results.Content() if err != nil { - err = rpcerr.Failedf("incoming call: read results from target answer: %w", err) + err = rpcerr.WrapFailed("incoming call: read results from target answer", err) ans.sendException(rl, err) rl.Add(releaseCall) c.er.ReportError(err) @@ -946,7 +951,7 @@ func (c *Conn) parseCall(p *parsedCall, call rpccp.Call) error { } payload, err := call.Params() if err != nil { - return rpcerr.Failedf("read params: %w", err) + return rpcerr.WrapFailed("read params", err) } ptr, _, err := c.recvPayload(payload) if err != nil { @@ -955,7 +960,7 @@ func (c *Conn) parseCall(p *parsedCall, call rpccp.Call) error { p.args = ptr.Struct() tgt, err := call.Target() if err != nil { - return rpcerr.Failedf("read target: %w", err) + return rpcerr.WrapFailed("read target", err) } if err := parseMessageTarget(&p.target, tgt); err != nil { return err @@ -970,19 +975,19 @@ func parseMessageTarget(pt *parsedMessageTarget, tgt rpccp.MessageTarget) error case rpccp.MessageTarget_Which_promisedAnswer: pa, err := tgt.PromisedAnswer() if err != nil { - return rpcerr.Failedf("read target answer: %w", err) + return rpcerr.WrapFailed("read target answer", err) } pt.promisedAnswer = answerID(pa.QuestionId()) opList, err := pa.Transform() if err != nil { - return rpcerr.Failedf("read target transform: %w", err) + return rpcerr.WrapFailed("read target transform", err) } pt.transform, err = parseTransform(opList) if err != nil { return rpcerr.Annotate(err, "read target transform") } default: - return rpcerr.Unimplementedf("unknown message target %v", pt.which) + return rpcerr.Unimplemented(errors.New("unknown message target " + pt.which.String())) } return nil @@ -998,7 +1003,10 @@ func parseTransform(list rpccp.PromisedAnswer_Op_List) ([]capnp.PipelineOp, erro case rpccp.PromisedAnswer_Op_Which_getPointerField: ops = append(ops, capnp.PipelineOp{Field: li.GetPointerField()}) default: - return nil, rpcerr.Failedf("transform element %d: unknown type %v", i, li.Which()) + return nil, rpcerr.Failed(errors.New( + "transform element " + str.Itod(i) + + ": unknown type " + li.Which().String(), + )) } } return ops, nil @@ -1013,7 +1021,9 @@ func (c *Conn) handleReturn(ctx context.Context, ret rpccp.Return, release capnp qid := questionID(ret.AnswerId()) if uint32(qid) >= uint32(len(c.lk.questions)) { rl.Add(release) - return rpcerr.Failedf("incoming return: question %d does not exist", qid) + return rpcerr.Failed(errors.New( + "incoming return: question " + str.Utod(qid) + " does not exist", + )) } // Pop the question from the table. Receiving the Return message // will always remove the question from the table, because it's the @@ -1022,7 +1032,9 @@ func (c *Conn) handleReturn(ctx context.Context, ret rpccp.Return, release capnp c.lk.questions[qid] = nil if q == nil { rl.Add(release) - return rpcerr.Failedf("incoming return: question %d does not exist", qid) + return rpcerr.Failed(errors.New( + "incoming return: question " + str.Utod(qid) + " does not exist", + )) } canceled := q.flags&finished != 0 q.flags |= finished @@ -1122,11 +1134,11 @@ func (c *Conn) parseReturn(ret rpccp.Return, called [][]capnp.PipelineOp) parsed case rpccp.Return_Which_results: r, err := ret.Results() if err != nil { - return parsedReturn{err: rpcerr.Failedf("parse return: %w", err), parseFailed: true} + return parsedReturn{err: rpcerr.WrapFailed("parse return", err), parseFailed: true} } content, locals, err := c.recvPayload(r) if err != nil { - return parsedReturn{err: rpcerr.Failedf("parse return: %w", err), parseFailed: true} + return parsedReturn{err: rpcerr.WrapFailed("parse return", err), parseFailed: true} } var embargoCaps uintSet @@ -1158,15 +1170,17 @@ func (c *Conn) parseReturn(ret rpccp.Return, called [][]capnp.PipelineOp) parsed case rpccp.Return_Which_exception: e, err := ret.Exception() if err != nil { - return parsedReturn{err: rpcerr.Failedf("parse return: %w", err), parseFailed: true} + return parsedReturn{err: rpcerr.WrapFailed("parse return", err), parseFailed: true} } reason, err := e.Reason() if err != nil { - return parsedReturn{err: rpcerr.Failedf("parse return: %w", err), parseFailed: true} + return parsedReturn{err: rpcerr.WrapFailed("parse return", err), parseFailed: true} } return parsedReturn{err: exc.New(exc.Type(e.Type()), "", reason)} default: - return parsedReturn{err: rpcerr.Failedf("parse return: unhandled type %v", w), parseFailed: true, unimplemented: true} + return parsedReturn{err: rpcerr.Failed(errors.New( + "parse return: unhandled type " + w.String(), + )), parseFailed: true, unimplemented: true} } } @@ -1186,10 +1200,14 @@ func (c *Conn) handleFinish(ctx context.Context, id answerID, releaseResultCaps ans := c.lk.answers[id] if ans == nil { - return rpcerr.Failedf("incoming finish: unknown answer ID %d", id) + return rpcerr.Failed(errors.New( + "incoming finish: unknown answer ID " + str.Utod(id), + )) } if ans.flags.Contains(finishReceived) { - return rpcerr.Failedf("incoming finish: answer ID %d already received finish", id) + return rpcerr.Failed(errors.New( + "incoming finish: answer ID " + str.Utod(id) + " already received finish", + )) } ans.flags |= finishReceived if releaseResultCaps { @@ -1240,32 +1258,38 @@ func (c *Conn) recvCap(d rpccp.CapDescriptor) (capnp.Client, error) { id := exportID(d.ReceiverHosted()) ent := c.findExport(id) if ent == nil { - return capnp.Client{}, rpcerr.Failedf("receive capability: invalid export %d", id) + return capnp.Client{}, rpcerr.Failed(errors.New( + "receive capability: invalid export " + str.Utod(id), + )) } return ent.client.AddRef(), nil case rpccp.CapDescriptor_Which_receiverAnswer: promisedAnswer, err := d.ReceiverAnswer() if err != nil { - return capnp.Client{}, rpcerr.Failedf("receive capability: reading promised answer: %v", err) + return capnp.Client{}, rpcerr.WrapFailed("receive capability: reading promised answer", err) } rawTransform, err := promisedAnswer.Transform() if err != nil { - return capnp.Client{}, rpcerr.Failedf("receive capability: reading promised answer transform: %v", err) + return capnp.Client{}, rpcerr.WrapFailed("receive capability: reading promised answer transform", err) } transform, err := parseTransform(rawTransform) if err != nil { - return capnp.Client{}, rpcerr.Failedf("read target transform: %v", err) + return capnp.Client{}, rpcerr.WrapFailed("read target transform", err) } id := answerID(promisedAnswer.QuestionId()) ans, ok := c.lk.answers[id] if !ok { - return capnp.Client{}, rpcerr.Failedf("receive capability: no such question id: %v", id) + return capnp.Client{}, rpcerr.Failed(errors.New( + "receive capability: no such question id: " + str.Utod(id), + )) } return c.recvCapReceiverAnswer(ans, transform), nil default: - return capnp.ErrorClient(rpcerr.Failedf("unknown CapDescriptor type %v", w)), nil + return capnp.ErrorClient(rpcerr.Failed(errors.New( + "unknown CapDescriptor type " + w.String(), + ))), nil } } @@ -1286,15 +1310,15 @@ func (c *Conn) recvCapReceiverAnswer(ans *answer, transform []capnp.PipelineOp) ptr, err := ans.results.Content() if err != nil { - return capnp.ErrorClient(rpcerr.Failedf("except.Failed reading results: %v", err)) + return capnp.ErrorClient(rpcerr.WrapFailed("except.Failed reading results", err)) } ptr, err = capnp.Transform(ptr, transform) if err != nil { - return capnp.ErrorClient(rpcerr.Failedf("Applying transform to results: %v", err)) + return capnp.ErrorClient(rpcerr.WrapFailed("Applying transform to results", err)) } iface := ptr.Interface() if !iface.IsValid() { - return capnp.ErrorClient(rpcerr.Failedf("Result is not a capability")) + return capnp.ErrorClient(rpcerr.Failed(errors.New("Result is not a capability"))) } return iface.Client().AddRef() @@ -1346,11 +1370,11 @@ func (c *Conn) recvPayload(payload rpccp.Payload) (_ capnp.Ptr, locals uintSet, } if payload.Message().CapTable != nil { // RecvMessage likely violated its invariant. - return capnp.Ptr{}, nil, rpcerr.Failedf("read payload: %w", ErrCapTablePopulated) + return capnp.Ptr{}, nil, rpcerr.WrapFailed("read payload", ErrCapTablePopulated) } p, err := payload.Content() if err != nil { - return capnp.Ptr{}, nil, rpcerr.Failedf("read payload: %w", err) + return capnp.Ptr{}, nil, rpcerr.WrapFailed("read payload", err) } ptab, err := payload.CapTable() if err != nil { @@ -1398,7 +1422,7 @@ func (c *Conn) handleDisembargo(ctx context.Context, d rpccp.Disembargo, release dtarget, err := d.Target() if err != nil { release() - return rpcerr.Failedf("incoming disembargo: read target: %w", err) + return rpcerr.WrapFailed("incoming disembargo: read target", err) } var tgt parsedMessageTarget @@ -1422,7 +1446,9 @@ func (c *Conn) handleDisembargo(ctx context.Context, d rpccp.Disembargo, release } }) if e == nil { - return rpcerr.Failedf("incoming disembargo: received sender loopback for unknown ID %d", id) + return rpcerr.Failed(errors.New( + "incoming disembargo: received sender loopback for unknown ID " + str.Utod(id), + )) } e.lift() @@ -1434,40 +1460,57 @@ func (c *Conn) handleDisembargo(ctx context.Context, d rpccp.Disembargo, release syncutil.With(&c.lk, func() { if tgt.which != rpccp.MessageTarget_Which_promisedAnswer { - err = rpcerr.Failedf("incoming disembargo: sender loopback: target is not a promised answer") + err = rpcerr.Failed(errors.New("incoming disembargo: sender loopback: target is not a promised answer")) return } ans := c.lk.answers[tgt.promisedAnswer] if ans == nil { - err = rpcerr.Failedf("incoming disembargo: unknown answer ID %d", tgt.promisedAnswer) + err = rpcerr.Failed(errors.New( + "incoming disembargo: unknown answer ID " + + str.Utod(tgt.promisedAnswer), + )) return } if !ans.flags.Contains(returnSent) { - err = rpcerr.Failedf("incoming disembargo: answer ID %d has not sent return", tgt.promisedAnswer) + err = rpcerr.Failed(errors.New( + "incoming disembargo: answer ID " + + str.Utod(tgt.promisedAnswer) + " has not sent return", + )) return } if ans.err != nil { - err = rpcerr.Failedf("incoming disembargo: answer ID %d returned exception", tgt.promisedAnswer) + err = rpcerr.Failed(errors.New( + "incoming disembargo: answer ID " + + str.Utod(tgt.promisedAnswer) + " returned exception", + )) return } var content capnp.Ptr if content, err = ans.results.Content(); err != nil { - err = rpcerr.Failedf("incoming disembargo: read answer ID %d: %v", tgt.promisedAnswer, err) + err = rpcerr.Failed(errors.New( + "incoming disembargo: read answer ID " + + str.Utod(tgt.promisedAnswer) + ": " + err.Error(), + )) return } var ptr capnp.Ptr if ptr, err = capnp.Transform(content, tgt.transform); err != nil { - err = rpcerr.Failedf("incoming disembargo: read answer ID %d: %v", tgt.promisedAnswer, err) + err = rpcerr.Failed(errors.New( + "incoming disembargo: read answer ID " + + str.Utod(tgt.promisedAnswer) + ": " + err.Error(), + )) return } iface := ptr.Interface() if !iface.IsValid() || int64(iface.Capability()) >= int64(len(ans.results.Message().CapTable)) { - err = rpcerr.Failedf("incoming disembargo: sender loopback requested on a capability that is not an import") + err = rpcerr.Failed(errors.New( + "incoming disembargo: sender loopback requested on a capability that is not an import", + )) return } @@ -1482,7 +1525,9 @@ func (c *Conn) handleDisembargo(ctx context.Context, d rpccp.Disembargo, release imp, ok := client.State().Brand.Value.(*importClient) if !ok || imp.c != c { client.Release() - return rpcerr.Failedf("incoming disembargo: sender loopback requested on a capability that is not an import") + return rpcerr.Failed(errors.New( + "incoming disembargo: sender loopback requested on a capability that is not an import", + )) } // TODO(maybe): check generation? @@ -1508,7 +1553,7 @@ func (c *Conn) handleDisembargo(ctx context.Context, d rpccp.Disembargo, release }, func(err error) { defer release() defer client.Release() - c.er.ReportError(rpcerr.Annotatef(err, "incoming disembargo: send receiver loopback")) + c.er.ReportError(rpcerr.Annotate(err, "incoming disembargo: send receiver loopback")) }) }) @@ -1562,11 +1607,11 @@ func (c *lockedConn) sendMessage(ctx context.Context, build func(rpccp.Message) if err != nil { release = func() {} send = func() error { - return rpcerr.Failedf("create message: %w", err) + return rpcerr.WrapFailed("create message", err) } } else if err = build(outMsg.Message); err != nil { send = func() error { - return rpcerr.Failedf("build message: %w", err) + return rpcerr.WrapFailed("build message", err) } } @@ -1632,7 +1677,7 @@ func (as asyncSend) Send() { if err := as.send(); as.onSent != nil { if err != nil { - err = rpcerr.Failedf("send message: %w", err) + err = rpcerr.WrapFailed("send message", err) } as.onSent(err) diff --git a/segment.go b/segment.go index 9d3bf72f..4313fd30 100644 --- a/segment.go +++ b/segment.go @@ -5,6 +5,7 @@ import ( "errors" "capnproto.org/go/capnp/v3/exc" + "capnproto.org/go/capnp/v3/internal/str" ) // A SegmentID is a numeric identifier for a Segment. @@ -360,7 +361,7 @@ func (s *Segment) writePtr(off address, src Ptr, forceCopy bool) error { for i := 0; i < l.Len(); i++ { err := copyStruct(dst.Struct(i), l.Struct(i)) if err != nil { - return exc.WrapError("write pointer: copy list element"+fmtIdecimal(i), err) + return exc.WrapError("write pointer: copy list element"+str.Itod(i), err) } } } diff --git a/server/answer.go b/server/answer.go index 8eeac305..9956fa57 100644 --- a/server/answer.go +++ b/server/answer.go @@ -5,6 +5,7 @@ import ( "sync" "capnproto.org/go/capnp/v3" + "capnproto.org/go/capnp/v3/exc" ) // answerQueue is a queue of method calls to make after an earlier @@ -219,7 +220,7 @@ func (sr *structReturner) AllocResults(sz capnp.ObjectSize) (capnp.Struct, error sr.alloced = true s, err := newBlankStruct(sz) if err != nil { - return capnp.Struct{}, errorf("alloc results: %v", err) + return capnp.Struct{}, exc.WrapError("alloc results", err) } sr.result = s return s, nil diff --git a/server/server.go b/server/server.go index 3662582d..b78738a9 100644 --- a/server/server.go +++ b/server/server.go @@ -327,7 +327,3 @@ type resultsAllocer interface { func newError(msg string) error { return exc.New(exc.Failed, "capnp server", msg) } - -func errorf(format string, args ...any) error { - return newError(fmt.Sprintf(format, args...)) -} diff --git a/struct.go b/struct.go index 35038f1f..201c006a 100644 --- a/struct.go +++ b/struct.go @@ -4,6 +4,7 @@ import ( "errors" "capnproto.org/go/capnp/v3/exc" + "capnproto.org/go/capnp/v3/internal/str" ) // Struct is a pointer to a struct. @@ -339,11 +340,11 @@ func copyStruct(dst, src Struct) error { dstAddr, _ := dstPtrSect.element(int32(j), wordSize) m, err := src.seg.readPtr(srcAddr, src.depthLimit) if err != nil { - return exc.WrapError("copy struct pointer "+fmtUdecimal(j), err) + return exc.WrapError("copy struct pointer "+str.Utod(j), err) } err = dst.seg.writePtr(dstAddr, m, true) if err != nil { - return exc.WrapError("copy struct pointer "+fmtUdecimal(j), err) + return exc.WrapError("copy struct pointer "+str.Utod(j), err) } } for j := numSrcPtrs; j < numDstPtrs; j++ { From 3d82b23a9c958b51cf1e940bb91d6886f13d30ee Mon Sep 17 00:00:00 2001 From: Ian Denhardt Date: Sun, 1 Jan 2023 21:07:28 -0500 Subject: [PATCH 3/4] Remove direct calls to the fmt package from rpc & transport --- rpc/answer.go | 7 +++---- rpc/rpc.go | 27 ++++++++++++++------------- rpc/transport/transport.go | 11 +++++------ 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/rpc/answer.go b/rpc/answer.go index bf6777b7..1318723c 100644 --- a/rpc/answer.go +++ b/rpc/answer.go @@ -3,7 +3,6 @@ package rpc import ( "context" "errors" - "fmt" "sync" "capnproto.org/go/capnp/v3" @@ -122,7 +121,7 @@ func (c *Conn) newReturn() (_ rpccp.Return, sendMsg func(), _ *rc.Releaser, _ er release: releaser.Decr, onSent: func(err error) { if err != nil { - c.er.ReportError(fmt.Errorf("send return: %w", err)) + c.er.ReportError(exc.WrapError("send return", err)) } }, }) @@ -287,11 +286,11 @@ func (ans *answer) sendException(rl *releaseList, ex error) { default: // Send exception. if e, err := ans.ret.NewException(); err != nil { - ans.c.er.ReportError(fmt.Errorf("send exception: %w", err)) + ans.c.er.ReportError(exc.WrapError("send exception", err)) } else { e.SetType(rpccp.Exception_Type(exc.TypeOf(ex))) if err := e.SetReason(ex.Error()); err != nil { - ans.c.er.ReportError(fmt.Errorf("send exception: %w", err)) + ans.c.er.ReportError(exc.WrapError("send exception", err)) } else { ans.sendMsg() } diff --git a/rpc/rpc.go b/rpc/rpc.go index 3ba132dd..228632cf 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -4,7 +4,6 @@ package rpc // import "capnproto.org/go/capnp/v3/rpc" import ( "context" "errors" - "fmt" "sync" "time" @@ -575,13 +574,13 @@ func (c *Conn) receive() error { e, err := recv.Abort() if err != nil { - c.er.ReportError(fmt.Errorf("read abort: %w", err)) + c.er.ReportError(exc.WrapError("read abort", err)) return nil } reason, err := e.Reason() if err != nil { - c.er.ReportError(fmt.Errorf("read abort: reason: %w", err)) + c.er.ReportError(exc.WrapError("read abort: reason", err)) return nil } @@ -592,7 +591,7 @@ func (c *Conn) receive() error { bootstrap, err := recv.Bootstrap() if err != nil { release() - c.er.ReportError(fmt.Errorf("read bootstrap: %w", err)) + c.er.ReportError(exc.WrapError("read bootstrap", err)) continue } qid := answerID(bootstrap.QuestionId()) @@ -605,7 +604,7 @@ func (c *Conn) receive() error { call, err := recv.Call() if err != nil { release() - c.er.ReportError(fmt.Errorf("read call: %w", err)) + c.er.ReportError(exc.WrapError("read call", err)) continue } if err := c.handleCall(ctx, call, release); err != nil { @@ -616,7 +615,7 @@ func (c *Conn) receive() error { ret, err := recv.Return() if err != nil { release() - c.er.ReportError(fmt.Errorf("read return: %w", err)) + c.er.ReportError(exc.WrapError("read return", err)) continue } if err := c.handleReturn(ctx, ret, release); err != nil { @@ -627,7 +626,7 @@ func (c *Conn) receive() error { fin, err := recv.Finish() if err != nil { release() - c.er.ReportError(fmt.Errorf("read finish: %w", err)) + c.er.ReportError(exc.WrapError("read finish", err)) continue } qid := answerID(fin.QuestionId()) @@ -641,7 +640,7 @@ func (c *Conn) receive() error { rel, err := recv.Release() if err != nil { release() - c.er.ReportError(fmt.Errorf("read release: %w", err)) + c.er.ReportError(exc.WrapError("read release", err)) continue } id := exportID(rel.Id()) @@ -655,7 +654,7 @@ func (c *Conn) receive() error { d, err := recv.Disembargo() if err != nil { release() - c.er.ReportError(fmt.Errorf("read disembargo: %w", err)) + c.er.ReportError(exc.WrapError("read disembargo", err)) continue } err = c.handleDisembargo(ctx, d, release) @@ -1096,7 +1095,7 @@ func (c *Conn) handleReturn(ctx context.Context, ret rpccp.Return, release capnp for _, s := range pr.disembargoes { c.sendMessage(ctx, s.buildDisembargo, func(err error) { if err != nil { - err = fmt.Errorf("incoming return: send disembargo: %w", err) + err = exc.WrapError("incoming return: send disembargo", err) c.er.ReportError(err) } }) @@ -1116,7 +1115,7 @@ func (c *Conn) handleReturn(ctx context.Context, ret rpccp.Return, release capnp defer close(q.finishMsgSend) if err != nil { - err = fmt.Errorf("incoming return: send finish: build message: %w", err) + err = exc.WrapError("incoming return: send finish: build message", err) c.er.ReportError(err) } else { q.flags |= finishSent @@ -1380,7 +1379,7 @@ func (c *Conn) recvPayload(payload rpccp.Payload) (_ capnp.Ptr, locals uintSet, if err != nil { // Don't allow unreadable capability table to stop other results, // just present an empty capability table. - c.er.ReportError(fmt.Errorf("read payload: capability table: %w", err)) + c.er.ReportError(exc.WrapError("read payload: capability table", err)) return p, nil, nil } mtab := make([]capnp.Client, ptab.Len()) @@ -1393,7 +1392,9 @@ func (c *Conn) recvPayload(payload rpccp.Payload) (_ capnp.Ptr, locals uintSet, for _, client := range mtab[:i] { client.Release() } - return capnp.Ptr{}, nil, rpcerr.Annotate(err, fmt.Sprintf("read payload: capability %d", i)) + return capnp.Ptr{}, nil, rpcerr.Annotate( + err, "read payload: capability "+str.Itod(i), + ) } if c.isLocalClient(mtab[i]) { locals.add(uint(i)) diff --git a/rpc/transport/transport.go b/rpc/transport/transport.go index 8d96d09a..9fff096c 100644 --- a/rpc/transport/transport.go +++ b/rpc/transport/transport.go @@ -8,7 +8,6 @@ package transport import ( "errors" - "fmt" "io" capnp "capnproto.org/go/capnp/v3" @@ -116,12 +115,12 @@ func (s *transport) NewMessage() (OutgoingMessage, error) { arena := capnp.MultiSegment(nil) msg, seg, err := capnp.NewMessage(arena) if err != nil { - err = transporterr.Annotate(fmt.Errorf("new message: %w", err), "stream transport") + err = transporterr.Annotate(exc.WrapError("new message", err), "stream transport") return OutgoingMessage{}, err } rmsg, err := rpccp.NewRootMessage(seg) if err != nil { - err = transporterr.Annotate(fmt.Errorf("new message: %w", err), "stream transport") + err = transporterr.Annotate(exc.WrapError("new message", err), "stream transport") return OutgoingMessage{}, err } @@ -132,7 +131,7 @@ func (s *transport) NewMessage() (OutgoingMessage, error) { panic("Tried to send() a message that was already released.") } if err = s.c.Encode(msg); err != nil { - err = transporterr.Annotate(fmt.Errorf("send: %w", err), "stream transport") + err = transporterr.Annotate(exc.WrapError("send", err), "stream transport") } return err } @@ -160,12 +159,12 @@ func (s *transport) NewMessage() (OutgoingMessage, error) { func (s *transport) RecvMessage() (IncomingMessage, error) { msg, err := s.c.Decode() if err != nil { - err = transporterr.Annotate(fmt.Errorf("receive: %w", err), "stream transport") + err = transporterr.Annotate(exc.WrapError("receive", err), "stream transport") return IncomingMessage{}, err } rmsg, err := rpccp.ReadRootMessage(msg) if err != nil { - err = transporterr.Annotate(fmt.Errorf("receive: %w", err), "stream transport") + err = transporterr.Annotate(exc.WrapError("receive", err), "stream transport") return IncomingMessage{}, err } From 0072cda04d3eb6b519ef68df302e81fe57524d0a Mon Sep 17 00:00:00 2001 From: Ian Denhardt Date: Mon, 2 Jan 2023 02:31:32 -0500 Subject: [PATCH 4/4] Remember to commit new files --- internal/str/str.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 internal/str/str.go diff --git a/internal/str/str.go b/internal/str/str.go new file mode 100644 index 00000000..34d97d3d --- /dev/null +++ b/internal/str/str.go @@ -0,0 +1,23 @@ +// Helpers for formatting strings. We avoid the use of the fmt package for the benefit +// of environments that care about minimizing executable size. +package str + +import "strconv" + +// Utod formats unsigned integers as decimals. +func Utod[T Uint](n T) string { + return strconv.FormatUint(uint64(n), 10) +} + +// Itod formats signed integers as decimals. +func Itod[T Int](n T) string { + return strconv.FormatInt(int64(n), 10) +} + +type Uint interface { + ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uint +} + +type Int interface { + ~int8 | ~int16 | ~int32 | ~int64 | ~int +}