diff --git a/address.go b/address.go index 4f497777..a8898313 100644 --- a/address.go +++ b/address.go @@ -1,21 +1,17 @@ package capnp import ( - "fmt" + "strconv" ) -// 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 fmt.Sprintf("%#08x", uint64(a)) + return strconv.FormatUint(uint64(a), 16) } // GoString returns the address in hex format. func (a address) GoString() string { - return fmt.Sprintf("capnp.address(%#08x)", uint64(a)) + return "capnp.address(%" + a.String() + ")" } // addSize returns the address a+sz. ok is false if the result would @@ -62,12 +58,12 @@ func (sz Size) String() string { if sz == 1 { return "1 byte" } - return fmt.Sprintf("%d bytes", sz) + return fmtUdecimal(sz) + " bytes" } // GoString returns the size as a Go expression. func (sz Size) GoString() string { - return fmt.Sprintf("capnp.Size(%d)", sz) + return "capnp.Size(" + fmtUdecimal(sz) + ")" } // times returns the size sz*n. ok is false if the result would be @@ -124,12 +120,12 @@ func (off DataOffset) String() string { if off == 1 { return "+1 byte" } - return fmt.Sprintf("+%d bytes", off) + return "+" + fmtUdecimal(off) + " bytes" } // GoString returns the offset as a Go expression. func (off DataOffset) GoString() string { - return fmt.Sprintf("capnp.DataOffset(%d)", off) + return "capnp.DataOffset(" + fmtUdecimal(off) + ")" } // ObjectSize records section sizes for a struct or list. @@ -181,12 +177,13 @@ func (sz ObjectSize) totalWordCount() int32 { // String returns a short, human readable representation of the object // size. func (sz ObjectSize) String() string { - return fmt.Sprintf("{datasz=%d ptrs=%d}", sz.DataSize, sz.PointerCount) + return "{datasz=" + fmtUdecimal(sz.DataSize) + " ptrs=" + fmtUdecimal(sz.PointerCount) + "}" } // GoString formats the ObjectSize as a keyed struct literal. func (sz ObjectSize) GoString() string { - return fmt.Sprintf("capnp.ObjectSize{DataSize: %d, PointerCount: %d}", sz.DataSize, sz.PointerCount) + return "capnp.ObjectSize{DataSize: " + fmtUdecimal(sz.DataSize) + + ", PointerCount: " + fmtUdecimal(sz.PointerCount) + "}" } // BitOffset is an offset in bits from the beginning of a struct's data @@ -205,10 +202,10 @@ func (bit BitOffset) mask() byte { // String returns the offset in the format "bit X". func (bit BitOffset) String() string { - return fmt.Sprintf("bit %d", bit) + return "bit " + fmtUdecimal(bit) } // GoString returns the offset as a Go expression. func (bit BitOffset) GoString() string { - return fmt.Sprintf("capnp.BitOffset(%d)", bit) + return "capnp.BitOffset(" + fmtUdecimal(bit) + ")" } diff --git a/answer.go b/answer.go index 3e5a1b1e..9f095049 100644 --- a/answer.go +++ b/answer.go @@ -2,7 +2,6 @@ package capnp import ( "context" - "fmt" "strconv" "sync" @@ -206,7 +205,7 @@ func (p *Promise) requireUnresolved(callerMethod string) { if p.err == nil { prevMethod = "Fulfill" } else { - prevMethod = fmt.Sprintf("Reject (error = %q)", p.err) + prevMethod = "Reject (error = " + strconv.Quote(p.err.Error()) + ")" } panic("Promise." + callerMethod + diff --git a/capability.go b/capability.go index d3d5701c..71752296 100644 --- a/capability.go +++ b/capability.go @@ -94,12 +94,12 @@ type CapabilityID uint32 // String returns the ID in the format "capability X". func (id CapabilityID) String() string { - return fmt.Sprintf("capability %d", id) + return "capability " + fmtUdecimal(id) } // GoString returns the ID as a Go expression. func (id CapabilityID) GoString() string { - return fmt.Sprintf("capnp.CapabilityID(%d)", id) + return "capnp.CapabilityID(" + fmtUdecimal(id) + ")" } // A Client is a reference to a Cap'n Proto capability. @@ -652,9 +652,10 @@ func finalizeClient(c *client) { } var msg string if c.creatorFile == "" { - msg = fmt.Sprintf("leaked client created by %s", fname) + msg = "leaked client created by " + fname } else { - msg = fmt.Sprintf("leaked client created by %s on %s:%d", fname, c.creatorFile, c.creatorLine) + msg = "leaked client created by " + fname + " on " + + c.creatorFile + ":" + fmtIdecimal(c.creatorLine) } if c.creatorStack != "" { msg += "\nCreation stack trace:\n" + c.creatorStack + "\n" diff --git a/fmt.go b/fmt.go new file mode 100644 index 00000000..dce3d87b --- /dev/null +++ b/fmt.go @@ -0,0 +1,28 @@ +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/message.go b/message.go index 6362b7b9..bd6135d1 100644 --- a/message.go +++ b/message.go @@ -452,7 +452,7 @@ func (ssa *SingleSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (S } func (ssa SingleSegmentArena) String() string { - return fmt.Sprintf("single-segment arena [len=%d cap=%d]", len(ssa), cap(ssa)) + return "single-segment arena [len=" + fmtIdecimal(len(ssa)) + " cap=" + fmtIdecimal(cap(ssa)) + "]" } type roSingleSegment []byte @@ -586,7 +586,7 @@ func (msa *MultiSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (Se } func (msa *MultiSegmentArena) String() string { - return fmt.Sprintf("multi-segment arena [%d segments]", len(*msa)) + return "multi-segment arena [" + fmtIdecimal(len(*msa)) + " segments]" } // nextAlloc computes how much more space to allocate given the number diff --git a/rpc/rpc.go b/rpc/rpc.go index 76195048..7c747851 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -663,7 +663,7 @@ func (c *Conn) receive() error { } default: - c.er.ReportError(fmt.Errorf("unknown message type %v from remote", recv.Which())) + c.er.ReportError(errors.New("unknown message type " + recv.Which().String() + " from remote")) c.withLocked(func(c *lockedConn) { c.sendMessage(ctx, func(m rpccp.Message) error { defer release() @@ -744,7 +744,7 @@ func (c *Conn) handleCall(ctx context.Context, call rpccp.Call, releaseCall capn // TODO(3rd-party handshake): support sending results to 3rd party vat if call.SendResultsTo().Which() != rpccp.Call_sendResultsTo_Which_caller { // TODO(someday): handle SendResultsTo.yourself - c.er.ReportError(fmt.Errorf("incoming call: results destination is not caller")) + c.er.ReportError(errors.New("incoming call: results destination is not caller")) c.withLocked(func(c *lockedConn) { c.sendMessage(ctx, func(m rpccp.Message) error { @@ -1513,7 +1513,7 @@ func (c *Conn) handleDisembargo(ctx context.Context, d rpccp.Disembargo, release }) default: - c.er.ReportError(fmt.Errorf("incoming disembargo: context %v not implemented", d.Context().Which())) + c.er.ReportError(errors.New("incoming disembargo: context " + d.Context().Which().String() + " not implemented")) c.withLocked(func(c *lockedConn) { c.sendMessage(ctx, func(m rpccp.Message) (err error) { if m, err = m.NewUnimplemented(); err == nil {