├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── address.go ├── address_test.go ├── capability.go ├── capability_test.go ├── capn.go ├── capn_test.go ├── capnpc-go ├── .gitignore ├── capnpc-go.go ├── schema.capnp ├── schema.capnp.go └── templates.go ├── doc.go ├── example_test.go ├── go.capnp ├── go.capnp.go ├── integration_test.go ├── integrationutil_test.go ├── internal ├── aircraftlib │ ├── aircraft.capnp │ ├── aircraft.capnp.go │ └── generate.go ├── demo │ ├── book_test.go │ ├── books │ │ ├── books.capnp │ │ ├── books.capnp.go │ │ └── gen.go │ ├── hash_test.go │ └── hashes │ │ ├── gen.go │ │ ├── hash.capnp │ │ └── hash.capnp.go ├── fulfiller │ ├── fulfiller.go │ └── fulfiller_test.go ├── packed │ ├── packed.go │ └── packed_test.go └── queue │ ├── queue.go │ └── queue_test.go ├── list.go ├── mem.go ├── mem_test.go ├── pointer.go ├── rawpointer.go ├── rawpointer_test.go ├── rpc ├── answer.go ├── cancel_test.go ├── embargo_test.go ├── errors.go ├── example_test.go ├── internal │ ├── logtransport │ │ └── logtransport.go │ ├── logutil │ │ └── logutil.go │ ├── pipetransport │ │ └── pipetransport.go │ ├── refcount │ │ ├── refcount.go │ │ └── refcount_test.go │ └── testcapnp │ │ ├── generate.go │ │ ├── test.capnp │ │ └── test.capnp.go ├── introspect.go ├── issue3_test.go ├── manager.go ├── promise_test.go ├── question.go ├── release_test.go ├── rpc.go ├── rpc_test.go ├── rpccapnp │ ├── generate.go │ ├── rpc.capnp │ └── rpc.capnp.go ├── tables.go └── transport.go ├── server ├── server.go └── server_test.go ├── strings.go ├── struct.go └── travis-install.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /capnp-go/capnp-go 2 | /example/example 3 | /example/*.capnp.go 4 | TAGS 5 | *~ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4 5 | 6 | sudo: required 7 | 8 | before_install: 9 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 10 | - sudo apt-get update -qq 11 | 12 | install: ./travis-install.sh 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | go-capnproto is licensed under the terms of the MIT license reproduced below. 2 | 3 | =============================================================================== 4 | 5 | Copyright (C) 2014 the go-capnproto authors and contributors. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | =============================================================================== 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Repository has moved to go-capnproto2 2 | 3 | New development is taking place at [zombiezen/go-capnproto2](https://github.com/zombiezen/go-capnproto2). Please update your imports to `zombiezen.com/go/capnproto2`. 4 | -------------------------------------------------------------------------------- /address.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // An Address is an index inside a segment's data (in bytes). 4 | type Address uint32 5 | 6 | // addSize returns the address a+sz. 7 | func (a Address) addSize(sz Size) Address { 8 | return a.element(1, sz) 9 | } 10 | 11 | // element returns the address a+i*sz. 12 | func (a Address) element(i int32, sz Size) Address { 13 | return a + Address(sz.times(i)) 14 | } 15 | 16 | // addOffset returns the address a+o. 17 | func (a Address) addOffset(o DataOffset) Address { 18 | return a + Address(o) 19 | } 20 | 21 | // A Size is a size (in bytes). 22 | type Size uint32 23 | 24 | // wordSize is the number of bytes in a Cap'n Proto word. 25 | const wordSize Size = 8 26 | 27 | // maxSize is the maximum representable size. 28 | const maxSize Size = 1<<32 - 1 29 | 30 | // times returns the size sz*n. 31 | func (sz Size) times(n int32) Size { 32 | result := int64(sz) * int64(n) 33 | if result > int64(maxSize) { 34 | panic(errOverlarge) 35 | } 36 | return Size(result) 37 | } 38 | 39 | // padToWord adds padding to sz to make it divisible by wordSize. 40 | func (sz Size) padToWord() Size { 41 | n := Size(wordSize - 1) 42 | return (sz + n) &^ n 43 | } 44 | 45 | // DataOffset is an offset in bytes from the beginning of a struct's data section. 46 | type DataOffset uint32 47 | 48 | // ObjectSize records section sizes for a struct or list. 49 | type ObjectSize struct { 50 | DataSize Size 51 | PointerCount uint16 52 | } 53 | 54 | // isZero reports whether sz is the zero size. 55 | func (sz ObjectSize) isZero() bool { 56 | return sz.DataSize == 0 && sz.PointerCount == 0 57 | } 58 | 59 | // isOneByte reports whether the object size is one byte (for Text/Data element sizes). 60 | func (sz ObjectSize) isOneByte() bool { 61 | return sz.DataSize == 1 && sz.PointerCount == 0 62 | } 63 | 64 | // isValid reports whether sz's fields are in range. 65 | func (sz ObjectSize) isValid() bool { 66 | return sz.DataSize <= 0xffff*wordSize 67 | } 68 | 69 | // pointerSize returns the number of bytes the pointer section occupies. 70 | func (sz ObjectSize) pointerSize() Size { 71 | return wordSize.times(int32(sz.PointerCount)) 72 | } 73 | 74 | // totalSize returns the number of bytes that the object occupies. 75 | func (sz ObjectSize) totalSize() Size { 76 | return sz.DataSize + sz.pointerSize() 77 | } 78 | 79 | // dataWordCount returns the number of words in the data section. 80 | func (sz ObjectSize) dataWordCount() int32 { 81 | if sz.DataSize%wordSize != 0 { 82 | panic("data size not aligned by word") 83 | } 84 | return int32(sz.DataSize / wordSize) 85 | } 86 | 87 | // totalWordCount returns the number of words that the object occupies. 88 | func (sz ObjectSize) totalWordCount() int32 { 89 | return sz.dataWordCount() + int32(sz.PointerCount) 90 | } 91 | 92 | // BitOffset is an offset in bits from the beginning of a struct's data section. 93 | type BitOffset uint32 94 | 95 | // offset returns the equivalent byte offset. 96 | func (bit BitOffset) offset() DataOffset { 97 | return DataOffset(bit / 8) 98 | } 99 | 100 | // mask returns the bitmask for the bit. 101 | func (bit BitOffset) mask() byte { 102 | return byte(1 << (bit % 8)) 103 | } 104 | -------------------------------------------------------------------------------- /address_test.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAddressElement(t *testing.T) { 8 | tests := []struct { 9 | a Address 10 | i int32 11 | sz Size 12 | out Address 13 | }{ 14 | {0, 0, 0, 0}, 15 | {0, 1, 0, 0}, 16 | {0, 1, 8, 8}, 17 | {0, 2, 8, 16}, 18 | {24, 1, 0, 24}, 19 | {24, 1, 8, 32}, 20 | {24, 2, 8, 40}, 21 | } 22 | for _, test := range tests { 23 | if out := test.a.element(test.i, test.sz); out != test.out { 24 | t.Errorf("%#v.element(%d, %d) = %#v; want %#v", test.a, test.i, test.sz, out, test.out) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /capability.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | // An Interface is a reference to a client in a message's capability table. 11 | type Interface struct { 12 | seg *Segment 13 | cap CapabilityID 14 | } 15 | 16 | // NewInterface creates a new interface pointer. No allocation is 17 | // performed; s is only used for Segment()'s return value. 18 | func NewInterface(s *Segment, cap CapabilityID) Interface { 19 | return Interface{ 20 | seg: s, 21 | cap: cap, 22 | } 23 | } 24 | 25 | // ToInterface attempts to convert p into an interface. If p is not a 26 | // valid interface, then ToInterface returns an invalid Interface. 27 | func ToInterface(p Pointer) Interface { 28 | if !IsValid(p) { 29 | return Interface{} 30 | } 31 | i, ok := p.underlying().(Interface) 32 | if !ok { 33 | return Interface{} 34 | } 35 | return i 36 | } 37 | 38 | // Segment returns the segment this pointer came from. 39 | func (i Interface) Segment() *Segment { 40 | return i.seg 41 | } 42 | 43 | // HasData is always true. 44 | func (i Interface) HasData() bool { 45 | return true 46 | } 47 | 48 | // Capability returns the capability ID of the interface. 49 | func (i Interface) Capability() CapabilityID { 50 | return i.cap 51 | } 52 | 53 | // value returns a raw interface pointer with the capability ID. 54 | func (i Interface) value(paddr Address) rawPointer { 55 | if i.seg == nil { 56 | return 0 57 | } 58 | return rawInterfacePointer(i.cap) 59 | } 60 | 61 | func (i Interface) underlying() Pointer { 62 | return i 63 | } 64 | 65 | // Client returns the client stored in the message's capability table 66 | // or nil if the pointer is invalid. 67 | func (i Interface) Client() Client { 68 | if i.seg == nil { 69 | return nil 70 | } 71 | tab := i.seg.msg.CapTable 72 | if int64(i.cap) >= int64(len(tab)) { 73 | return nil 74 | } 75 | return tab[i.cap] 76 | } 77 | 78 | // ErrNullClient is returned from a call made on a null client pointer. 79 | var ErrNullClient = errors.New("capnp: call on null client") 80 | 81 | // A CapabilityID is an index into a message's capability table. 82 | type CapabilityID uint32 83 | 84 | // A Client represents an Cap'n Proto interface type. It is safe to use 85 | // from multiple goroutines. 86 | // 87 | // Generally, only RPC protocol implementers should provide types that 88 | // implement Client: call ordering guarantees, promises, and 89 | // synchronization are tricky to get right. Prefer creating a server 90 | // that wraps another interface than trying to implement Client. 91 | type Client interface { 92 | // Call starts executing a method and returns an answer that will hold 93 | // the resulting struct. The call's parameters must be placed before 94 | // Call() returns. 95 | // 96 | // Calls are delivered to the capability in the order they are made. 97 | // This guarantee is based on the concept of a capability 98 | // acknowledging delivery of a call: this is specific to an 99 | // implementation of Client. A type that implements Client must 100 | // guarantee that if foo() then bar() is called on a client, that 101 | // acknowledging foo() happens before acknowledging bar(). 102 | Call(call *Call) Answer 103 | 104 | // Close releases any resources associated with this client. 105 | // No further calls to the client should be made after calling Close. 106 | Close() error 107 | } 108 | 109 | // The Call type holds the record for an outgoing interface call. 110 | type Call struct { 111 | // Ctx is the context of the call. 112 | Ctx context.Context 113 | 114 | // Method is the interface ID and method ID, along with the optional name, 115 | // of the method to call. 116 | Method Method 117 | 118 | // Params is a struct containing parameters for the call. 119 | // This should be set when the RPC system receives a call for an 120 | // exported interface. It is mutually exclusive with ParamsFunc 121 | // and ParamsSize. 122 | Params Struct 123 | // ParamsFunc is a function that populates an allocated struct with 124 | // the parameters for the call. ParamsSize determines the size of the 125 | // struct to allocate. This is used when application code is using a 126 | // client. These settings should be set together; they are mutually 127 | // exclusive with Params. 128 | ParamsFunc func(Struct) error 129 | ParamsSize ObjectSize 130 | 131 | // Options passes RPC-specific options for the call. 132 | Options CallOptions 133 | } 134 | 135 | // Copy clones a call, ensuring that its Params are placed. 136 | // If Call.ParamsFunc is nil, then the same Call will be returned. 137 | func (call *Call) Copy(s *Segment) (*Call, error) { 138 | if call.ParamsFunc == nil { 139 | return call, nil 140 | } 141 | p, err := call.PlaceParams(s) 142 | if err != nil { 143 | return nil, err 144 | } 145 | return &Call{ 146 | Ctx: call.Ctx, 147 | Method: call.Method, 148 | Params: p, 149 | Options: call.Options, 150 | }, nil 151 | } 152 | 153 | // PlaceParams returns the parameters struct, allocating it inside 154 | // segment s as necessary. If s is nil, a new single-segment message 155 | // is allocated. 156 | func (call *Call) PlaceParams(s *Segment) (Struct, error) { 157 | if call.ParamsFunc == nil { 158 | return call.Params, nil 159 | } 160 | if s == nil { 161 | var err error 162 | _, s, err = NewMessage(SingleSegment(nil)) 163 | if err != nil { 164 | return Struct{}, err 165 | } 166 | } 167 | p, err := NewStruct(s, call.ParamsSize) 168 | if err != nil { 169 | return Struct{}, nil 170 | } 171 | err = call.ParamsFunc(p) 172 | return p, err 173 | } 174 | 175 | // CallOptions holds RPC-specific options for an interface call. 176 | // Its usage is similar to the values in context.Context, but is only 177 | // used for a single call: its values are not intended to propagate to 178 | // other callees. An example of an option would be the 179 | // Call.sendResultsTo field in rpc.capnp. 180 | type CallOptions struct { 181 | m map[interface{}]interface{} 182 | } 183 | 184 | // NewCallOptions builds a CallOptions value from a list of individual options. 185 | func NewCallOptions(opts []CallOption) CallOptions { 186 | co := CallOptions{make(map[interface{}]interface{})} 187 | for _, o := range opts { 188 | o.f(co) 189 | } 190 | return co 191 | } 192 | 193 | // Value retrieves the value associated with the options for this key, 194 | // or nil if no value is associated with this key. 195 | func (co CallOptions) Value(key interface{}) interface{} { 196 | return co.m[key] 197 | } 198 | 199 | // With creates a copy of the CallOptions value with other options applied. 200 | func (co CallOptions) With(opts []CallOption) CallOptions { 201 | newopts := CallOptions{make(map[interface{}]interface{})} 202 | for k, v := range co.m { 203 | newopts.m[k] = v 204 | } 205 | for _, o := range opts { 206 | o.f(newopts) 207 | } 208 | return newopts 209 | } 210 | 211 | // A CallOption is a function that modifies options on an interface call. 212 | type CallOption struct { 213 | f func(CallOptions) 214 | } 215 | 216 | // SetOptionValue returns a call option that associates a value to an 217 | // option key. This can be retrieved later with CallOptions.Value. 218 | func SetOptionValue(key, value interface{}) CallOption { 219 | return CallOption{func(co CallOptions) { 220 | co.m[key] = value 221 | }} 222 | } 223 | 224 | // An Answer is the deferred result of a client call, which is usually wrapped by a Pipeline. 225 | type Answer interface { 226 | // Struct waits until the call is finished and returns the result. 227 | Struct() (Struct, error) 228 | 229 | // The following methods are the same as in Client except with 230 | // an added transform parameter -- a path to the interface to use. 231 | 232 | PipelineCall(transform []PipelineOp, call *Call) Answer 233 | PipelineClose(transform []PipelineOp) error 234 | } 235 | 236 | // A Pipeline is a generic wrapper for an answer. 237 | type Pipeline struct { 238 | answer Answer 239 | parent *Pipeline 240 | op PipelineOp 241 | } 242 | 243 | // NewPipeline returns a new pipeline based on an answer. 244 | func NewPipeline(ans Answer) *Pipeline { 245 | return &Pipeline{answer: ans} 246 | } 247 | 248 | // Answer returns the answer the pipeline is derived from. 249 | func (p *Pipeline) Answer() Answer { 250 | return p.answer 251 | } 252 | 253 | // Transform returns the operations needed to transform the root answer 254 | // into the value p represents. 255 | func (p *Pipeline) Transform() []PipelineOp { 256 | n := 0 257 | for q := p; q.parent != nil; q = q.parent { 258 | n++ 259 | } 260 | xform := make([]PipelineOp, n) 261 | for i, q := n-1, p; q.parent != nil; i, q = i-1, q.parent { 262 | xform[i] = q.op 263 | } 264 | return xform 265 | } 266 | 267 | // Struct waits until the answer is resolved and returns the struct 268 | // this pipeline represents. 269 | func (p *Pipeline) Struct() (Struct, error) { 270 | s, err := p.answer.Struct() 271 | if err != nil { 272 | return Struct{}, err 273 | } 274 | ptr, err := Transform(s, p.Transform()) 275 | if err != nil { 276 | return Struct{}, err 277 | } 278 | return ToStruct(ptr), nil 279 | } 280 | 281 | // Client returns the client version of p. 282 | func (p *Pipeline) Client() *PipelineClient { 283 | return (*PipelineClient)(p) 284 | } 285 | 286 | // GetPipeline returns a derived pipeline which yields the pointer field given. 287 | func (p *Pipeline) GetPipeline(off uint16) *Pipeline { 288 | return p.GetPipelineDefault(off, nil) 289 | } 290 | 291 | // GetPipelineDefault returns a derived pipeline which yields the pointer field given, 292 | // defaulting to the value given. 293 | func (p *Pipeline) GetPipelineDefault(off uint16, def []byte) *Pipeline { 294 | return &Pipeline{ 295 | answer: p.answer, 296 | parent: p, 297 | op: PipelineOp{ 298 | Field: off, 299 | DefaultValue: def, 300 | }, 301 | } 302 | } 303 | 304 | // PipelineClient implements Client by calling to the pipeline's answer. 305 | type PipelineClient Pipeline 306 | 307 | func (pc *PipelineClient) transform() []PipelineOp { 308 | return (*Pipeline)(pc).Transform() 309 | } 310 | 311 | func (pc *PipelineClient) Call(call *Call) Answer { 312 | return pc.answer.PipelineCall(pc.transform(), call) 313 | } 314 | 315 | func (pc *PipelineClient) Close() error { 316 | return pc.answer.PipelineClose(pc.transform()) 317 | } 318 | 319 | // A PipelineOp describes a step in transforming a pipeline. 320 | // It maps closely with the PromisedAnswer.Op struct in rpc.capnp. 321 | type PipelineOp struct { 322 | Field uint16 323 | DefaultValue []byte 324 | } 325 | 326 | // String returns a human-readable description of op. 327 | func (op PipelineOp) String() string { 328 | s := make([]byte, 0, 32) 329 | s = append(s, "get field "...) 330 | s = strconv.AppendInt(s, int64(op.Field), 10) 331 | if op.DefaultValue == nil { 332 | return string(s) 333 | } 334 | s = append(s, " with default"...) 335 | return string(s) 336 | } 337 | 338 | // A Method identifies a method along with an optional human-readable 339 | // description of the method. 340 | type Method struct { 341 | InterfaceID uint64 342 | MethodID uint16 343 | 344 | // Canonical name of the interface. May be empty. 345 | InterfaceName string 346 | // Method name as it appears in the schema. May be empty. 347 | MethodName string 348 | } 349 | 350 | // String returns a formatted string containing the interface name or 351 | // the method name if present, otherwise it uses the raw IDs. 352 | // This is suitable for use in error messages and logs. 353 | func (m *Method) String() string { 354 | buf := make([]byte, 0, 128) 355 | if m.InterfaceName == "" { 356 | buf = append(buf, '@', '0', 'x') 357 | buf = strconv.AppendUint(buf, m.InterfaceID, 16) 358 | } else { 359 | buf = append(buf, m.InterfaceName...) 360 | } 361 | buf = append(buf, '.') 362 | if m.MethodName == "" { 363 | buf = append(buf, '@') 364 | buf = strconv.AppendUint(buf, uint64(m.MethodID), 10) 365 | } else { 366 | buf = append(buf, m.MethodName...) 367 | } 368 | return string(buf) 369 | } 370 | 371 | // Transform applies a sequence of pipeline operations to a pointer 372 | // and returns the result. 373 | func Transform(p Pointer, transform []PipelineOp) (Pointer, error) { 374 | n := len(transform) 375 | if n == 0 { 376 | return p, nil 377 | } 378 | s := ToStruct(p) 379 | for _, op := range transform[:n-1] { 380 | field, err := s.Pointer(op.Field) 381 | if err != nil { 382 | return nil, err 383 | } 384 | s, err = ToStructDefault(field, op.DefaultValue) 385 | } 386 | op := transform[n-1] 387 | p, err := s.Pointer(op.Field) 388 | if err != nil { 389 | return nil, err 390 | } 391 | if op.DefaultValue != nil { 392 | p, err = PointerDefault(p, op.DefaultValue) 393 | } 394 | return p, err 395 | } 396 | 397 | type immediateAnswer struct { 398 | s Struct 399 | } 400 | 401 | // ImmediateAnswer returns an Answer that accesses s. 402 | func ImmediateAnswer(s Struct) Answer { 403 | return immediateAnswer{s} 404 | } 405 | 406 | func (ans immediateAnswer) Struct() (Struct, error) { 407 | return ans.s, nil 408 | } 409 | 410 | func (ans immediateAnswer) findClient(transform []PipelineOp) Client { 411 | p, err := Transform(ans.s, transform) 412 | if err != nil { 413 | return ErrorClient(err) 414 | } 415 | return ToInterface(p).Client() 416 | } 417 | 418 | func (ans immediateAnswer) PipelineCall(transform []PipelineOp, call *Call) Answer { 419 | c := ans.findClient(transform) 420 | if c == nil { 421 | return ErrorAnswer(ErrNullClient) 422 | } 423 | return c.Call(call) 424 | } 425 | 426 | func (ans immediateAnswer) PipelineClose(transform []PipelineOp) error { 427 | c := ans.findClient(transform) 428 | if c == nil { 429 | return ErrNullClient 430 | } 431 | return c.Close() 432 | } 433 | 434 | type errorAnswer struct { 435 | e error 436 | } 437 | 438 | // ErrorAnswer returns a Answer that always returns error e. 439 | func ErrorAnswer(e error) Answer { 440 | return errorAnswer{e} 441 | } 442 | 443 | func (ans errorAnswer) Struct() (Struct, error) { 444 | return Struct{}, ans.e 445 | } 446 | 447 | func (ans errorAnswer) PipelineCall([]PipelineOp, *Call) Answer { 448 | return ans 449 | } 450 | 451 | func (ans errorAnswer) PipelineClose([]PipelineOp) error { 452 | return ans.e 453 | } 454 | 455 | // IsFixedAnswer reports whether an answer was created by 456 | // ImmediateAnswer or ErrorAnswer. 457 | func IsFixedAnswer(ans Answer) bool { 458 | switch ans.(type) { 459 | case immediateAnswer: 460 | return true 461 | case errorAnswer: 462 | return true 463 | default: 464 | return false 465 | } 466 | } 467 | 468 | type errorClient struct { 469 | e error 470 | } 471 | 472 | // ErrorClient returns a Client that always returns error e. 473 | func ErrorClient(e error) Client { 474 | return errorClient{e} 475 | } 476 | 477 | func (ec errorClient) Call(*Call) Answer { 478 | return ErrorAnswer(ec.e) 479 | } 480 | 481 | func (ec errorClient) Close() error { 482 | return nil 483 | } 484 | 485 | // IsErrorClient reports whether c was created with ErrorClient. 486 | func IsErrorClient(c Client) bool { 487 | _, ok := c.(errorClient) 488 | return ok 489 | } 490 | 491 | // MethodError is an error on an associated method. 492 | type MethodError struct { 493 | Method *Method 494 | Err error 495 | } 496 | 497 | // Error returns the method name concatenated with the error string. 498 | func (me *MethodError) Error() string { 499 | return me.Method.String() + ": " + me.Err.Error() 500 | } 501 | 502 | // ErrUnimplemented is the error returned when a method is called on 503 | // a server that does not implement the method. 504 | var ErrUnimplemented = errors.New("capnp: method not implemented") 505 | 506 | // IsUnimplemented reports whether e indicates an unimplemented method error. 507 | func IsUnimplemented(e error) bool { 508 | if me, ok := e.(*MethodError); ok { 509 | e = me.Err 510 | } 511 | return e == ErrUnimplemented 512 | } 513 | -------------------------------------------------------------------------------- /capability_test.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "testing" 7 | ) 8 | 9 | func TestToInterface(t *testing.T) { 10 | _, seg, err := NewMessage(SingleSegment(nil)) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | tests := []struct { 15 | ptr Pointer 16 | in Interface 17 | }{ 18 | {nil, Interface{}}, 19 | {Struct{}, Interface{}}, 20 | {Struct{seg: seg, off: 0}, Interface{}}, 21 | {Interface{}, Interface{}}, 22 | {Interface{seg, 42}, Interface{seg, 42}}, 23 | } 24 | for _, test := range tests { 25 | if in := ToInterface(test.ptr); in != test.in { 26 | t.Errorf("ToInterface(%#v) = %#v; want %#v", test.ptr, in, test.in) 27 | } 28 | } 29 | } 30 | 31 | func TestInterface_value(t *testing.T) { 32 | _, seg, err := NewMessage(SingleSegment(nil)) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | tests := []struct { 37 | in Interface 38 | val rawPointer 39 | }{ 40 | {Interface{}, 0}, 41 | {NewInterface(seg, 0), 0x0000000000000003}, 42 | {NewInterface(seg, 0xdeadbeef), 0xdeadbeef00000003}, 43 | } 44 | for _, test := range tests { 45 | for paddr := Address(0); paddr < 16; paddr++ { 46 | if val := test.in.value(paddr); val != test.val { 47 | t.Errorf("Interface{seg: %p, cap: %d}.value(%v) = %v; want %v", test.in.seg, test.in.cap, paddr, val, test.val) 48 | } 49 | } 50 | } 51 | } 52 | 53 | func TestTransform(t *testing.T) { 54 | _, s, err := NewMessage(SingleSegment(nil)) 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | root, err := NewStruct(s, ObjectSize{PointerCount: 2}) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | a, err := NewStruct(s, ObjectSize{DataSize: 8, PointerCount: 1}) 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | root.SetPointer(1, a) 67 | a.SetUint64(0, 1) 68 | b, err := NewStruct(s, ObjectSize{DataSize: 8}) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | b.SetUint64(0, 2) 73 | a.SetPointer(0, b) 74 | 75 | dmsg, d, err := NewMessage(SingleSegment(nil)) 76 | if err != nil { 77 | t.Fatal(err) 78 | } 79 | da, err := NewStruct(d, ObjectSize{DataSize: 8, PointerCount: 1}) 80 | if err != nil { 81 | t.Fatal(err) 82 | } 83 | if err := dmsg.SetRoot(da); err != nil { 84 | t.Fatal(err) 85 | } 86 | da.SetUint64(0, 56) 87 | db, err := NewStruct(d, ObjectSize{DataSize: 8}) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | db.SetUint64(0, 78) 92 | da.SetPointer(0, db) 93 | 94 | tests := []struct { 95 | p Pointer 96 | transform []PipelineOp 97 | out Pointer 98 | }{ 99 | { 100 | root, 101 | nil, 102 | root, 103 | }, 104 | { 105 | root, 106 | []PipelineOp{}, 107 | root, 108 | }, 109 | { 110 | root, 111 | []PipelineOp{ 112 | {Field: 0}, 113 | }, 114 | nil, 115 | }, 116 | { 117 | root, 118 | []PipelineOp{ 119 | {Field: 0, DefaultValue: mustMarshal(t, dmsg)}, 120 | }, 121 | da, 122 | }, 123 | { 124 | root, 125 | []PipelineOp{ 126 | {Field: 1}, 127 | }, 128 | a, 129 | }, 130 | { 131 | root, 132 | []PipelineOp{ 133 | {Field: 1, DefaultValue: mustMarshal(t, dmsg)}, 134 | }, 135 | a, 136 | }, 137 | { 138 | root, 139 | []PipelineOp{ 140 | {Field: 1}, 141 | {Field: 0}, 142 | }, 143 | b, 144 | }, 145 | { 146 | root, 147 | []PipelineOp{ 148 | {Field: 0}, 149 | {Field: 0}, 150 | }, 151 | nil, 152 | }, 153 | { 154 | root, 155 | []PipelineOp{ 156 | {Field: 0, DefaultValue: mustMarshal(t, dmsg)}, 157 | {Field: 0}, 158 | }, 159 | db, 160 | }, 161 | { 162 | root, 163 | []PipelineOp{ 164 | {Field: 0}, 165 | {Field: 0, DefaultValue: mustMarshal(t, dmsg)}, 166 | }, 167 | da, 168 | }, 169 | { 170 | root, 171 | []PipelineOp{ 172 | {Field: 0, DefaultValue: mustMarshal(t, dmsg)}, 173 | {Field: 1, DefaultValue: mustMarshal(t, dmsg)}, 174 | }, 175 | da, 176 | }, 177 | } 178 | 179 | for _, test := range tests { 180 | out, err := Transform(test.p, test.transform) 181 | if !deepPointerEqual(out, test.out) { 182 | t.Errorf("Transform(%+v, %v) = %+v; want %+v", test.p, test.transform, out, test.out) 183 | } 184 | if err != nil { 185 | t.Errorf("Transform(%+v, %v) error: %v", test.p, test.transform, err) 186 | } 187 | } 188 | } 189 | 190 | func TestMethodString(t *testing.T) { 191 | tests := []struct { 192 | m *Method 193 | s string 194 | }{ 195 | { 196 | &Method{ 197 | InterfaceID: 0x8e5322c1e9282534, 198 | MethodID: 1, 199 | }, 200 | "@0x8e5322c1e9282534.@1", 201 | }, 202 | { 203 | &Method{ 204 | InterfaceID: 0x8e5322c1e9282534, 205 | MethodID: 1, 206 | InterfaceName: "aircraftlib:Echo", 207 | MethodName: "foo", 208 | }, 209 | "aircraftlib:Echo.foo", 210 | }, 211 | } 212 | for _, test := range tests { 213 | if s := test.m.String(); s != test.s { 214 | t.Errorf("%#v.String() = %q; want %q", test.m, s, test.s) 215 | } 216 | } 217 | } 218 | 219 | func TestPipelineOpString(t *testing.T) { 220 | tests := []struct { 221 | op PipelineOp 222 | s string 223 | }{ 224 | { 225 | PipelineOp{Field: 4}, 226 | "get field 4", 227 | }, 228 | { 229 | PipelineOp{Field: 4, DefaultValue: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, 230 | "get field 4 with default", 231 | }, 232 | } 233 | for _, test := range tests { 234 | if s := test.op.String(); s != test.s { 235 | t.Errorf("%#v.String() = %q; want %q", test.op, s, test.s) 236 | } 237 | } 238 | } 239 | 240 | func TestIsUnimplemented(t *testing.T) { 241 | tests := []struct { 242 | e error 243 | ok bool 244 | }{ 245 | {nil, false}, 246 | {ErrUnimplemented, true}, 247 | {errors.New("foo"), false}, 248 | {&MethodError{Method: new(Method), Err: ErrUnimplemented}, true}, 249 | {&MethodError{Method: new(Method), Err: errors.New("foo")}, false}, 250 | } 251 | for _, test := range tests { 252 | if ok := IsUnimplemented(test.e); ok != test.ok { 253 | t.Errorf("IsUnimplemented(%#v) = %t; want %t", test.e, ok, test.ok) 254 | } 255 | } 256 | } 257 | 258 | func mustMarshal(t *testing.T, msg *Message) []byte { 259 | data, err := msg.Marshal() 260 | if err != nil { 261 | t.Fatal("Marshal:", err) 262 | } 263 | return data 264 | } 265 | 266 | func deepPointerEqual(a, b Pointer) bool { 267 | if a == nil && b == nil { 268 | return true 269 | } 270 | if a == nil || b == nil { 271 | return false 272 | } 273 | msgA, _, _ := NewMessage(SingleSegment(nil)) 274 | msgA.SetRoot(a) 275 | abytes, _ := msgA.Marshal() 276 | msgB, _, _ := NewMessage(SingleSegment(nil)) 277 | msgB.SetRoot(b) 278 | bbytes, _ := msgB.Marshal() 279 | return bytes.Equal(abytes, bbytes) 280 | } 281 | -------------------------------------------------------------------------------- /capn.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | 7 | "github.com/glycerine/rbtree" 8 | ) 9 | 10 | // A SegmentID is a numeric identifier for a Segment. 11 | type SegmentID uint32 12 | 13 | // A Segment is an allocation arena for Cap'n Proto objects. 14 | // It is part of a Message, which can contain other segments that 15 | // reference each other. 16 | type Segment struct { 17 | msg *Message 18 | id SegmentID 19 | data []byte 20 | } 21 | 22 | // Message returns the message that contains s. 23 | func (s *Segment) Message() *Message { 24 | return s.msg 25 | } 26 | 27 | // ID returns the segment's ID. 28 | func (s *Segment) ID() SegmentID { 29 | return s.id 30 | } 31 | 32 | // Data returns the raw byte slice for the segment. 33 | func (s *Segment) Data() []byte { 34 | return s.data 35 | } 36 | 37 | func (s *Segment) inBounds(addr Address) bool { 38 | return addr < Address(len(s.data)) 39 | } 40 | 41 | func (s *Segment) regionInBounds(base Address, sz Size) bool { 42 | return base.addSize(sz) <= Address(len(s.data)) 43 | } 44 | 45 | // slice returns the segment of data from base to base+sz. 46 | func (s *Segment) slice(base Address, sz Size) []byte { 47 | if !s.regionInBounds(base, sz) { 48 | panic(errOutOfBounds) 49 | } 50 | return s.data[base:base.addSize(sz)] 51 | } 52 | 53 | func (s *Segment) readUint8(addr Address) uint8 { 54 | return s.slice(addr, 1)[0] 55 | } 56 | 57 | func (s *Segment) readUint16(addr Address) uint16 { 58 | return binary.LittleEndian.Uint16(s.slice(addr, 2)) 59 | } 60 | 61 | func (s *Segment) readUint32(addr Address) uint32 { 62 | return binary.LittleEndian.Uint32(s.slice(addr, 4)) 63 | } 64 | 65 | func (s *Segment) readUint64(addr Address) uint64 { 66 | return binary.LittleEndian.Uint64(s.slice(addr, 8)) 67 | } 68 | 69 | func (s *Segment) readRawPointer(addr Address) rawPointer { 70 | return rawPointer(s.readUint64(addr)) 71 | } 72 | 73 | func (s *Segment) writeUint8(addr Address, val uint8) { 74 | s.slice(addr, 1)[0] = val 75 | } 76 | 77 | func (s *Segment) writeUint16(addr Address, val uint16) { 78 | binary.LittleEndian.PutUint16(s.slice(addr, 2), val) 79 | } 80 | 81 | func (s *Segment) writeUint32(addr Address, val uint32) { 82 | binary.LittleEndian.PutUint32(s.slice(addr, 4), val) 83 | } 84 | 85 | func (s *Segment) writeUint64(addr Address, val uint64) { 86 | binary.LittleEndian.PutUint64(s.slice(addr, 8), val) 87 | } 88 | 89 | func (s *Segment) writeRawPointer(addr Address, val rawPointer) { 90 | s.writeUint64(addr, uint64(val)) 91 | } 92 | 93 | // root returns a 1-element pointer list that references the first word 94 | // in the segment. This only makes sense to call on the first segment 95 | // in a message. 96 | func (s *Segment) root() PointerList { 97 | sz := ObjectSize{PointerCount: 1} 98 | if !s.regionInBounds(0, sz.totalSize()) { 99 | return PointerList{} 100 | } 101 | return PointerList{List{ 102 | seg: s, 103 | length: 1, 104 | size: sz, 105 | }} 106 | } 107 | 108 | func (s *Segment) lookupSegment(id SegmentID) (*Segment, error) { 109 | if s.id == id { 110 | return s, nil 111 | } 112 | return s.msg.Segment(id) 113 | } 114 | 115 | func (s *Segment) readPtr(off Address) (Pointer, error) { 116 | var err error 117 | val := s.readRawPointer(off) 118 | s, off, val, err = s.resolveFarPointer(off, val) 119 | if err != nil { 120 | return nil, err 121 | } 122 | if val == 0 { 123 | return nil, nil 124 | } 125 | // Be wary of overflow. Offset is 30 bits signed. List size is 29 bits 126 | // unsigned. For both of these we need to check in terms of words if 127 | // using 32 bit maths as bits or bytes will overflow. 128 | switch val.pointerType() { 129 | case structPointer: 130 | addr, ok := val.offset().resolve(off) 131 | if !ok { 132 | return nil, errPointerAddress 133 | } 134 | sz := val.structSize() 135 | if !s.regionInBounds(addr, sz.totalSize()) { 136 | return nil, errPointerAddress 137 | } 138 | return Struct{ 139 | seg: s, 140 | off: addr, 141 | size: sz, 142 | }, nil 143 | case listPointer: 144 | addr, ok := val.offset().resolve(off) 145 | if !ok { 146 | return nil, errPointerAddress 147 | } 148 | lt, lsize := val.listType(), val.totalListSize() 149 | if !s.regionInBounds(addr, lsize) { 150 | return nil, errPointerAddress 151 | } 152 | if lt == compositeList { 153 | hdr := s.readRawPointer(addr) 154 | addr = addr.addSize(wordSize) 155 | if hdr.pointerType() != structPointer { 156 | return nil, errBadTag 157 | } 158 | sz := hdr.structSize() 159 | n := int32(hdr.offset()) 160 | // TODO(light): check that this has the same end address 161 | if !s.regionInBounds(addr, sz.totalSize().times(n)) { 162 | return nil, errPointerAddress 163 | } 164 | return List{ 165 | seg: s, 166 | size: sz, 167 | off: addr, 168 | length: n, 169 | flags: isCompositeList, 170 | }, nil 171 | } 172 | if lt == bit1List { 173 | return List{ 174 | seg: s, 175 | off: addr, 176 | length: val.numListElements(), 177 | flags: isBitList, 178 | }, nil 179 | } 180 | return List{ 181 | seg: s, 182 | size: val.elementSize(), 183 | off: addr, 184 | length: val.numListElements(), 185 | }, nil 186 | case otherPointer: 187 | if val.otherPointerType() != 0 { 188 | return nil, errOtherPointer 189 | } 190 | return Interface{ 191 | seg: s, 192 | cap: val.capabilityIndex(), 193 | }, nil 194 | default: 195 | // Only other types are far pointers. 196 | return nil, errBadLandingPad 197 | } 198 | } 199 | 200 | func (s *Segment) resolveFarPointer(off Address, val rawPointer) (*Segment, Address, rawPointer, error) { 201 | switch val.pointerType() { 202 | case doubleFarPointer: 203 | // A double far pointer points to a double pointer, where the 204 | // first points to the actual data, and the second is the tag 205 | // that would normally be placed right before the data (offset 206 | // == 0). 207 | 208 | faroff, segid := val.farAddress(), val.farSegment() 209 | s, err := s.lookupSegment(segid) 210 | if err != nil { 211 | return nil, 0, 0, err 212 | } 213 | if !s.regionInBounds(faroff, wordSize.times(2)) { 214 | return nil, 0, 0, errPointerAddress 215 | } 216 | far := s.readRawPointer(faroff) 217 | tag := s.readRawPointer(faroff.addSize(wordSize)) 218 | if far.pointerType() != farPointer || tag.offset() != 0 { 219 | return nil, 0, 0, errPointerAddress 220 | } 221 | segid = far.farSegment() 222 | if s, err = s.lookupSegment(segid); err != nil { 223 | return nil, 0, 0, errBadLandingPad 224 | } 225 | return s, 0, landingPadNearPointer(far, tag), nil 226 | case farPointer: 227 | faroff, segid := val.farAddress(), val.farSegment() 228 | s, err := s.lookupSegment(segid) 229 | if err != nil { 230 | return nil, 0, 0, err 231 | } 232 | if !s.regionInBounds(faroff, wordSize) { 233 | return nil, 0, 0, errPointerAddress 234 | } 235 | val = s.readRawPointer(faroff) 236 | return s, faroff, val, nil 237 | default: 238 | return s, off, val, nil 239 | } 240 | } 241 | 242 | type offset struct { 243 | id SegmentID 244 | boff, bend int64 // in bits 245 | newval Pointer 246 | } 247 | 248 | func makeOffsetKey(p Pointer) offset { 249 | switch p := p.underlying().(type) { 250 | case Struct: 251 | return offset{ 252 | id: p.seg.id, 253 | boff: int64(p.off) * 8, 254 | bend: int64(p.off.addSize(p.size.totalSize())) * 8, 255 | } 256 | case List: 257 | key := offset{ 258 | id: p.seg.id, 259 | boff: int64(p.off) * 8, 260 | } 261 | if p.flags&isBitList != 0 { 262 | key.bend = int64(p.off)*8 + int64(p.length) 263 | } else { 264 | key.bend = int64(p.off.addSize(p.size.totalSize().times(p.length))) * 8 265 | } 266 | if p.flags&isCompositeList != 0 { 267 | // Composite lists' offsets are after the tag word. 268 | key.boff -= int64(wordSize) * 8 269 | } 270 | return key 271 | default: 272 | panic("unreachable") 273 | } 274 | } 275 | 276 | func compare(a, b rbtree.Item) int { 277 | ao := a.(offset) 278 | bo := b.(offset) 279 | if ao.id != bo.id { 280 | return int(ao.id - bo.id) 281 | } else if ao.boff > bo.boff { 282 | return 1 283 | } else if ao.boff < bo.boff { 284 | return -1 285 | } else { 286 | return 0 287 | } 288 | } 289 | 290 | func needsCopy(dest *Segment, src Pointer) bool { 291 | if src.Segment().msg != dest.msg { 292 | return true 293 | } 294 | if s := ToStruct(src); IsValid(s) { 295 | // Structs can only be referenced if they're not list members. 296 | return s.flags&isListMember != 0 297 | } 298 | return false 299 | } 300 | 301 | func (destSeg *Segment) writePtr(cc copyContext, off Address, src Pointer) error { 302 | // handle nulls 303 | if !IsValid(src) { 304 | destSeg.writeRawPointer(off, 0) 305 | return nil 306 | } 307 | srcSeg := src.Segment() 308 | 309 | if i := ToInterface(src); IsValid(i) { 310 | if destSeg.msg != srcSeg.msg { 311 | c := destSeg.msg.AddCap(i.Client()) 312 | src = Pointer(NewInterface(destSeg, c)) 313 | } 314 | destSeg.writeRawPointer(off, src.value(off)) 315 | return nil 316 | } 317 | if destSeg != srcSeg { 318 | // Different segments 319 | if needsCopy(destSeg, src) { 320 | return copyPointer(cc, destSeg, off, src) 321 | } 322 | if !hasCapacity(srcSeg.data, wordSize) { 323 | // Double far pointer needed. 324 | const landingSize = wordSize * 2 325 | t, dstAddr, err := alloc(destSeg, landingSize) 326 | if err != nil { 327 | return err 328 | } 329 | 330 | srcAddr := pointerAddress(src) 331 | t.writeRawPointer(dstAddr, rawFarPointer(srcSeg.id, srcAddr)) 332 | t.writeRawPointer(dstAddr.addSize(wordSize), src.value(srcAddr-Address(wordSize))) 333 | destSeg.writeRawPointer(off, rawDoubleFarPointer(t.id, dstAddr)) 334 | return nil 335 | } 336 | // Have room in the target for a tag 337 | _, srcAddr, _ := alloc(srcSeg, wordSize) 338 | srcSeg.writeRawPointer(srcAddr, src.value(srcAddr)) 339 | destSeg.writeRawPointer(off, rawFarPointer(srcSeg.id, srcAddr)) 340 | return nil 341 | } 342 | destSeg.writeRawPointer(off, src.value(off)) 343 | return nil 344 | } 345 | 346 | func copyPointer(cc copyContext, dstSeg *Segment, dstAddr Address, src Pointer) error { 347 | if cc.depth >= 32 { 348 | return errCopyDepth 349 | } 350 | cc = cc.init() 351 | // First, see if the ptr has already been copied. 352 | key := makeOffsetKey(src) 353 | iter := cc.copies.FindLE(key) 354 | if key.bend > key.boff { 355 | if !iter.NegativeLimit() { 356 | other := iter.Item().(offset) 357 | if key.id == other.id { 358 | if key.boff == other.boff && key.bend == other.bend { 359 | return dstSeg.writePtr(cc.incDepth(), dstAddr, other.newval) 360 | } else if other.bend >= key.bend { 361 | return errOverlap 362 | } 363 | } 364 | } 365 | 366 | iter = iter.Next() 367 | 368 | if !iter.Limit() { 369 | other := iter.Item().(offset) 370 | if key.id == other.id && other.boff < key.bend { 371 | return errOverlap 372 | } 373 | } 374 | } 375 | 376 | // No copy nor overlap found, so we need to clone the target 377 | newSeg, newAddr, err := alloc(dstSeg, Size((key.bend-key.boff)/8)) 378 | if err != nil { 379 | return err 380 | } 381 | switch src := src.underlying().(type) { 382 | case Struct: 383 | dst := Struct{ 384 | seg: newSeg, 385 | off: newAddr, 386 | size: src.size, 387 | // clear flags 388 | } 389 | key.newval = dst 390 | cc.copies.Insert(key) 391 | if err := copyStruct(cc, dst, src); err != nil { 392 | return err 393 | } 394 | case List: 395 | dst := List{ 396 | seg: newSeg, 397 | off: newAddr, 398 | length: src.length, 399 | size: src.size, 400 | flags: src.flags, 401 | } 402 | if dst.flags&isCompositeList != 0 { 403 | // Copy tag word 404 | newSeg.writeRawPointer(newAddr, src.seg.readRawPointer(src.off-Address(wordSize))) 405 | dst.off = dst.off.addSize(wordSize) 406 | } 407 | key.newval = dst 408 | cc.copies.Insert(key) 409 | // TODO(light): fast path for copying text/data 410 | if dst.flags&isBitList != 0 { 411 | copy(newSeg.data[newAddr:], src.seg.data[src.off:src.length+7/8]) 412 | } else { 413 | for i := 0; i < src.Len(); i++ { 414 | err := copyStruct(cc, dst.Struct(i), src.Struct(i)) 415 | if err != nil { 416 | return err 417 | } 418 | } 419 | } 420 | default: 421 | panic("unreachable") 422 | } 423 | return dstSeg.writePtr(cc.incDepth(), dstAddr, key.newval) 424 | } 425 | 426 | type copyContext struct { 427 | copies *rbtree.Tree 428 | depth int 429 | } 430 | 431 | func (cc copyContext) init() copyContext { 432 | if cc.copies == nil { 433 | return copyContext{ 434 | copies: rbtree.NewTree(compare), 435 | } 436 | } 437 | return cc 438 | } 439 | 440 | func (cc copyContext) incDepth() copyContext { 441 | return copyContext{ 442 | copies: cc.copies, 443 | depth: cc.depth + 1, 444 | } 445 | } 446 | 447 | var ( 448 | errPointerAddress = errors.New("capnp: invalid pointer address") 449 | errBadLandingPad = errors.New("capnp: invalid far pointer landing pad") 450 | errBadTag = errors.New("capnp: invalid tag word") 451 | errOtherPointer = errors.New("capnp: unknown pointer type") 452 | errObjectSize = errors.New("capnp: invalid object size") 453 | ) 454 | 455 | var ( 456 | errOverlarge = errors.New("capnp: overlarge struct/list") 457 | errOutOfBounds = errors.New("capnp: address out of bounds") 458 | errCopyDepth = errors.New("capnp: copy depth too large") 459 | errOverlap = errors.New("capnp: overlapping data on copy") 460 | errListSize = errors.New("capnp: invalid list size") 461 | errObjectType = errors.New("capnp: invalid object type") 462 | ) 463 | -------------------------------------------------------------------------------- /capnpc-go/.gitignore: -------------------------------------------------------------------------------- 1 | /capnpc-go 2 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package capnp is a Cap'n Proto library for Go. 3 | https://capnproto.org/ 4 | 5 | Generating code 6 | 7 | capnpc-go provides the compiler backend for capnp. 8 | 9 | # First, install capnpc-go to $PATH. 10 | go install zombiezen.com/go/capnproto/capnpc-go 11 | # Then, generate Go files. 12 | capnp compile -ogo *.capnp 13 | 14 | capnpc-go requires two annotations for all files: package and import. 15 | package is needed to know what package to place at the head of the 16 | generated file and what identifier to use when referring to the type 17 | from another package. import should be the fully qualified import path 18 | and is used to generate import statement from other packages and to 19 | detect when two types are in the same package. For example: 20 | 21 | using Go = import "zombiezen.com/go/capnproto/go.capnp"; 22 | $Go.package("main"); 23 | $Go.import("zombiezen.com/go/capnproto/example"); 24 | 25 | For adding documentation comments to the generated code, there's the doc 26 | annotation. This annotation adds the comment to a struct, enum or field so 27 | that godoc will pick it up. For example: 28 | 29 | struct Zdate $Go.doc("Zdate represents a calendar date") { 30 | year @0 :Int16; 31 | month @1 :UInt8; 32 | day @2 :UInt8 ; 33 | } 34 | 35 | Messages and Segments 36 | 37 | In Cap'n Proto, the unit of communication is a message. A message 38 | consists of one or more segments -- contiguous blocks of memory. This 39 | allows large messages to be split up and loaded independently or lazily. 40 | Typically you will use one segment per message. Logically, a message is 41 | organized in a tree of objects, with the root always being a struct (as 42 | opposed to a list or primitive). Messages can be read from and written 43 | to a stream. 44 | 45 | Pointers 46 | 47 | The interface for accessing a Cap'n Proto object is Pointer. This can 48 | refer to a struct, a list, or an interface. Pointers have value 49 | semantics and refer to data in a single segment. All of the concrete 50 | pointer types have a notion of "valid". An invalid pointer will return 51 | the default value from any accessor and panic when any setter is called. 52 | 53 | Data accessors and setters (i.e. struct primitive fields and list 54 | elements) do not return errors, but pointer accessors and setters do. 55 | There are a reasons that a read or write of a pointer can fail, but the 56 | most common are bad pointers or allocation failures. For accessors, an 57 | invalid object will be returned in case of an error. 58 | 59 | Since Go doesn't have generics, wrapper types provide type safety on 60 | lists. This package provides lists of basic types, and capnpc-go 61 | generates list wrappers for named types. However, if you need to use 62 | deeper nesting of lists (e.g. List(List(UInt8))), you will need to use a 63 | PointerList and wrap the elements. 64 | 65 | Structs 66 | 67 | For the following schema: 68 | 69 | struct Foo { 70 | num @0 :UInt32; 71 | bar @1 :Foo; 72 | } 73 | 74 | capnpc-go will generate: 75 | 76 | // Foo is a pointer to a Foo struct in a segment. 77 | // Member functions are provided to get/set members in the 78 | // struct. 79 | type Foo struct{ capnp.Struct } 80 | 81 | // NewFoo creates a new orphaned Foo struct, preferring placement in 82 | // s. If there isn't enough space, then another segment in the 83 | // message will be used or allocated. You can set a field of type Foo 84 | // to this new message, but usually you will want to use the 85 | // NewBar()-style method shown below. 86 | func NewFoo(s *capnp.Segment) (Foo, error) 87 | 88 | // NewRootFoo creates a new Foo struct and sets the message's root to 89 | // it. 90 | func NewRootFoo(s *capnp.Segment) (Foo, error) 91 | 92 | // ReadRootFoo reads the message's root pointer and converts it to a 93 | // Foo struct. 94 | func ReadRootFoo(msg *capnp.Message) (Foo, error) 95 | 96 | // Num returns the value of the num field. 97 | func (s Foo) Num() uint32 98 | 99 | // SetNum sets the value of the num field to v. 100 | func (s Foo) SetNum(v uint32) 101 | 102 | // Bar returns the value of the bar field. This can return an error 103 | // if the pointer goes beyond the segment's range, the segment fails 104 | // to load, or the pointer recursion limit has been reached. 105 | func (s Foo) Bar() (Foo, error) 106 | 107 | // SetBar sets the value of the bar field to v. 108 | func (s Foo) SetBar(v Foo) error 109 | 110 | // NewBar sets the bar field to a newly allocated Foo struct, 111 | // preferring placement in s's segment. 112 | func (s Foo) NewBar() (Foo, error) 113 | 114 | // Foo_List is a value with pointer semantics. It is created for all 115 | // structs, and is used for List(Foo) in the capnp file. 116 | type Foo_List struct{ capnp.List } 117 | 118 | // NewFoo_List creates a new orphaned List(Foo), preferring placement 119 | // in s. This can then be added to a message by using a Set function 120 | // which takes a Foo_List. sz specifies the number of elements in the 121 | // list. The list's size cannot be changed after creation. 122 | func NewFoo_List(s *capnp.Segment, sz int32) Foo_List 123 | 124 | // Len returns the number of elements in the list. 125 | func (s Foo_List) Len() int 126 | 127 | // At returns a pointer to the i'th element. If i is an invalid index, 128 | // this will return an invalid Foo (all getters will return default 129 | // values, setters will fail). 130 | func (s Foo_List) At(i int) Foo 131 | 132 | // Foo_Promise is a promise for a Foo. Methods are provided to get 133 | // promises of struct and interface fields. 134 | type Foo_Promise struct{ *capnp.Pipeline } 135 | 136 | // Get waits until the promise is resolved and returns the result. 137 | func (p Foo_Promise) Get() (Foo, error) 138 | 139 | // Bar returns a promise for that bar field. 140 | func (p Foo_Promise) Bar() Foo_Promise 141 | 142 | 143 | Groups 144 | 145 | For each group a typedef is created with a different method set for just the 146 | groups fields: 147 | 148 | struct Foo { 149 | group :Group { 150 | field @0 :Bool; 151 | } 152 | } 153 | 154 | generates the following: 155 | 156 | type Foo struct{ capnp.Struct } 157 | type Foo_group Foo 158 | 159 | func (s Foo) Group() Foo_group 160 | func (s Foo_group) Field() bool 161 | 162 | That way the following may be used to access a field in a group: 163 | 164 | var f Foo 165 | value := f.Group().Field() 166 | 167 | Note that group accessors just convert the type and so have no overhead. 168 | 169 | Unions 170 | 171 | Named unions are treated as a group with an inner unnamed union. Unnamed 172 | unions generate an enum Type_Which and a corresponding Which() function: 173 | 174 | struct Foo { 175 | union { 176 | a @0 :Bool; 177 | b @1 :Bool; 178 | } 179 | } 180 | 181 | generates the following: 182 | 183 | type Foo_Which uint16 184 | 185 | const ( 186 | Foo_Which_a Foo_Which = 0 187 | Foo_Which_b Foo_Which = 1 188 | ) 189 | 190 | func (s Foo) A() bool 191 | func (s Foo) B() bool 192 | func (s Foo) SetA(v bool) 193 | func (s Foo) SetB(v bool) 194 | func (s Foo) Which() Foo_Which 195 | 196 | Which() should be checked before using the getters, and the default case must 197 | always be handled. 198 | 199 | Setters for single values will set the union discriminator as well as set the 200 | value. 201 | 202 | For voids in unions, there is a void setter that just sets the discriminator. 203 | For example: 204 | 205 | struct Foo { 206 | union { 207 | a @0 :Void; 208 | b @1 :Void; 209 | } 210 | } 211 | 212 | generates the following: 213 | 214 | func (s Foo) SetA() // Set that we are using A 215 | func (s Foo) SetB() // Set that we are using B 216 | 217 | Similarly, for groups in unions, there is a group setter that just sets 218 | the discriminator. This must be called before the group getter can be 219 | used to set values. For example: 220 | 221 | struct Foo { 222 | union { 223 | a :group { 224 | v :Bool 225 | } 226 | b :group { 227 | v :Bool 228 | } 229 | } 230 | } 231 | 232 | and in usage: 233 | 234 | f.SetA() // Set that we are using group A 235 | f.A().SetV(true) // then we can use the group A getter to set the inner values 236 | 237 | Enums 238 | 239 | capnpc-go generates enum values as constants. For example in the capnp file: 240 | 241 | enum ElementSize { 242 | empty @0; 243 | bit @1; 244 | byte @2; 245 | twoBytes @3; 246 | fourBytes @4; 247 | eightBytes @5; 248 | pointer @6; 249 | inlineComposite @7; 250 | } 251 | 252 | In the generated capnp.go file: 253 | 254 | type ElementSize uint16 255 | 256 | const ( 257 | ElementSize_empty ElementSize = 0 258 | ElementSize_bit ElementSize = 1 259 | ElementSize_byte ElementSize = 2 260 | ElementSize_twoBytes ElementSize = 3 261 | ElementSize_fourBytes ElementSize = 4 262 | ElementSize_eightBytes ElementSize = 5 263 | ElementSize_pointer ElementSize = 6 264 | ElementSize_inlineComposite ElementSize = 7 265 | ) 266 | 267 | In addition an enum.String() function is generated that will convert the constants to a string 268 | for debugging or logging purposes. By default, the enum name is used as the tag value, 269 | but the tags can be customized with a $Go.tag or $Go.notag annotation. 270 | 271 | For example: 272 | 273 | enum ElementSize { 274 | empty @0 $Go.tag("void"); 275 | bit @1 $Go.tag("1 bit"); 276 | byte @2 $Go.tag("8 bits"); 277 | inlineComposite @7 $Go.notag; 278 | } 279 | 280 | In the generated go file: 281 | 282 | func (c ElementSize) String() string { 283 | switch c { 284 | case ElementSize_empty: 285 | return "void" 286 | case ElementSize_bit: 287 | return "1 bit" 288 | case ElementSize_byte: 289 | return "8 bits" 290 | default: 291 | return "" 292 | } 293 | } 294 | 295 | Interfaces 296 | 297 | capnpc-go generates type-safe Client wrappers for interfaces. For parameter 298 | lists and result lists, structs are generated as described above with the names 299 | Interface_method_Params and Interface_method_Results, unless a single struct 300 | type is used. For example, for this interface: 301 | 302 | interface Calculator { 303 | evaluate @0 (expression :Expression) -> (value :Value); 304 | } 305 | 306 | capnpc-go generates the following Go code (along with the structs 307 | Calculator_evaluate_Params and Calculator_evaluate_Results): 308 | 309 | // Calculator is a client to a Calculator interface. 310 | type Calculator struct{ Client capnp.Client } 311 | 312 | // Evaluate calls `evaluate` on the client. params is called on a newly 313 | // allocated Calculator_evaluate_Params struct to fill in the parameters. 314 | func (c Calculator) Evaluate( 315 | ctx context.Context, 316 | params func(Calculator_evaluate_Params) error, 317 | opts ...capnp.CallOption) *Calculator_evaluate_Results_Promise 318 | 319 | capnpc-go also generates code to implement the interface: 320 | 321 | // A Calculator_Server implements the Calculator interface. 322 | type Calculator_Server interface { 323 | Evaluate(Calculator_evaluate_Call) error 324 | } 325 | 326 | // Calculator_evaluate_Call holds the arguments for a Calculator.evaluate server call. 327 | type Calculator_evaluate_Call struct { 328 | Ctx context.Context 329 | Options capnp.CallOptions 330 | Params Calculator_evaluate_Params 331 | Results Calculator_evaluate_Results 332 | } 333 | 334 | // Calculator_ServerToClient is equivalent to calling: 335 | // NewCalculator(capnp.NewServer(Calculator_Methods(nil, s), s)) 336 | // If s does not implement the Close method, then nil is used. 337 | func Calculator_ServerToClient(s Calculator_Server) Calculator 338 | 339 | // Calculator_Methods appends methods from Calculator that call to server and 340 | // returns the methods. If methods is nil or the capacity of the underlying 341 | // slice is too small, a new slice is returned. 342 | func Calculator_Methods(methods []server.Method, s Calculator_Server) []server.Method 343 | 344 | Since a single capability may want to implement many interfaces, you can 345 | use multiple *_Methods functions to build a single slice to send to 346 | NewServer. 347 | 348 | An example of combining the client/server code to communicate with a locally 349 | implemented Calculator: 350 | 351 | var srv Calculator_Server 352 | calc := Calculator_ServerToClient(srv) 353 | result := calc.Evaluate(ctx, func(params Calculator_evaluate_Params) { 354 | params.SetExpression(expr) 355 | }) 356 | val := result.Value().Get() 357 | 358 | A note about message ordering: when implementing a server method, you 359 | are responsible for acknowledging delivery of a method call. Failure to 360 | do so can cause deadlocks. See the server.Ack function for more details. 361 | */ 362 | package capnp // import "zombiezen.com/go/capnproto" 363 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package capnp_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | 8 | "zombiezen.com/go/capnproto" 9 | air "zombiezen.com/go/capnproto/internal/aircraftlib" 10 | ) 11 | 12 | func Example() { 13 | // Make a brand new empty message. 14 | msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) 15 | 16 | // If you want runtime-type identification, this is easily obtained. Just 17 | // wrap everything in a struct that contains a single anoymous union (e.g. struct Z). 18 | // Then always set a Z as the root object in you message/first segment. 19 | // The cost of the extra word of storage is usually worth it, as 20 | // then human readable output is easily obtained via a shell command such as 21 | // 22 | // $ cat binary.cpz | capnp decode aircraft.capnp Z 23 | // 24 | // If you need to conserve space, and know your content in advance, it 25 | // isn't necessary to use an anonymous union. Just supply the type name 26 | // in place of 'Z' in the decode command above. 27 | 28 | // There can only be one root. Subsequent NewRoot* calls will set the root 29 | // pointer and orphan the previous root. 30 | z, err := air.NewRootZ(seg) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | // then non-root objects: 36 | aircraft, err := z.NewAircraft() 37 | if err != nil { 38 | panic(err) 39 | } 40 | b737, err := aircraft.NewB737() 41 | if err != nil { 42 | panic(err) 43 | } 44 | planebase, err := b737.NewBase() 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | // Set primitive fields 50 | planebase.SetCanFly(true) 51 | planebase.SetName("Henrietta") 52 | planebase.SetRating(100) 53 | planebase.SetMaxSpeed(876) // km/hr 54 | // if we don't set capacity, it will get the default value, in this case 0. 55 | //planebase.SetCapacity(26020) // Liters fuel 56 | 57 | // Creating a list 58 | homes, err := air.NewAirport_List(seg, 2) 59 | if err != nil { 60 | panic(err) 61 | } 62 | homes.Set(0, air.Airport_jfk) 63 | homes.Set(1, air.Airport_lax) 64 | // Setting a list field 65 | planebase.SetHomes(homes) 66 | 67 | // Ready to write! 68 | 69 | // You can write to memory... 70 | buf, err := msg.Marshal() 71 | if err != nil { 72 | panic(err) 73 | } 74 | _ = buf 75 | 76 | // ... or write to an io.Writer. 77 | file, err := ioutil.TempFile("", "go-capnproto") 78 | if err != nil { 79 | panic(err) 80 | } 81 | defer file.Close() 82 | defer os.Remove(file.Name()) 83 | err = capnp.NewEncoder(file).Encode(msg) 84 | if err != nil { 85 | panic(err) 86 | } 87 | 88 | // Read back and view that file in human readable format. Defined in util_test.go 89 | text, err := CapnFileToText(file.Name(), schemaPath, "") 90 | if err != nil { 91 | panic(err) 92 | } 93 | fmt.Printf("here is our aircraft:\n") 94 | fmt.Printf("%s\n", text) 95 | 96 | // Output: 97 | // here is our aircraft: 98 | // (aircraft = (b737 = (base = (name = "Henrietta", homes = [jfk, lax], rating = 100, canFly = true, capacity = 0, maxSpeed = 876)))) 99 | } 100 | 101 | func ExampleUnmarshal() { 102 | msg, s, err := capnp.NewMessage(capnp.SingleSegment(nil)) 103 | if err != nil { 104 | fmt.Printf("allocation error %v\n", err) 105 | return 106 | } 107 | d, err := air.NewRootZdate(s) 108 | if err != nil { 109 | fmt.Printf("root error %v\n", err) 110 | return 111 | } 112 | d.SetYear(2004) 113 | d.SetMonth(12) 114 | d.SetDay(7) 115 | data, err := msg.Marshal() 116 | if err != nil { 117 | fmt.Printf("marshal error %v\n", err) 118 | return 119 | } 120 | 121 | // Read 122 | msg, err = capnp.Unmarshal(data) 123 | if err != nil { 124 | fmt.Printf("unmarshal error %v\n", err) 125 | return 126 | } 127 | d, err = air.ReadRootZdate(msg) 128 | if err != nil { 129 | fmt.Printf("read root error %v\n", err) 130 | return 131 | } 132 | fmt.Printf("year %d, month %d, day %d\n", d.Year(), d.Month(), d.Day()) 133 | // Output: 134 | // year 2004, month 12, day 7 135 | } 136 | -------------------------------------------------------------------------------- /go.capnp: -------------------------------------------------------------------------------- 1 | @0xd12a1c51fedd6c88; 2 | 3 | annotation package(file) :Text; 4 | # The Go package name for the generated file. 5 | 6 | annotation import(file) :Text; 7 | # The Go import path that the generated file is accessible from. 8 | # Used to generate import statements and check if two types are in the 9 | # same package. 10 | 11 | annotation doc(struct, field, enum) :Text; 12 | # Adds a doc comment to the generated code. 13 | 14 | annotation tag(enumerant) :Text; 15 | # Changes the string representation of the enum in the generated code. 16 | 17 | annotation notag(enumerant) :Void; 18 | # Removes the string representation of the enum in the generated code. 19 | 20 | annotation customtype(field) :Text; 21 | # OBSOLETE, not used by code generator. 22 | 23 | annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text; 24 | # Used to rename the element in the generated code. 25 | 26 | $package("capnp"); 27 | -------------------------------------------------------------------------------- /go.capnp.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // go.capnp annotation values. 4 | const ( 5 | Package = uint64(0xbea97f1023792be0) 6 | Import = uint64(0xe130b601260e44b5) 7 | Doc = uint64(0xc58ad6bd519f935e) 8 | Tag = uint64(0xa574b41924caefc7) 9 | Notag = uint64(0xc8768679ec52e012) 10 | Customtype = uint64(0xfa10659ae02f2093) 11 | Name = uint64(0xc2b96012172f8df1) 12 | ) 13 | -------------------------------------------------------------------------------- /internal/aircraftlib/aircraft.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "../../go.capnp"; 2 | 3 | $Go.package("aircraftlib"); 4 | $Go.import("zombiezen.com/go/capnproto/internal/aircraftlib"); 5 | 6 | @0x832bcc6686a26d56; 7 | 8 | const constDate :Zdate = (year = 2015, month = 8, day = 27); 9 | const constList :List(Zdate) = [(year = 2015, month = 8, day = 27), (year = 2015, month = 8, day = 28)]; 10 | const constEnum :Airport = jfk; 11 | 12 | struct Zdate { 13 | year @0 :Int16; 14 | month @1 :UInt8; 15 | day @2 :UInt8; 16 | } 17 | 18 | struct Zdata { 19 | data @0 :Data; 20 | } 21 | 22 | 23 | enum Airport { 24 | none @0; 25 | jfk @1; 26 | lax @2; 27 | sfo @3; 28 | luv @4; 29 | dfw @5; 30 | test @6; 31 | # test must be last because we use it to count 32 | # the number of elements in the Airport enum. 33 | } 34 | 35 | struct PlaneBase { 36 | name @0: Text; 37 | homes @1: List(Airport); 38 | rating @2: Int64; 39 | canFly @3: Bool; 40 | capacity @4: Int64; 41 | maxSpeed @5: Float64; 42 | } 43 | 44 | struct B737 { 45 | base @0: PlaneBase; 46 | } 47 | 48 | struct A320 { 49 | base @0: PlaneBase; 50 | } 51 | 52 | struct F16 { 53 | base @0: PlaneBase; 54 | } 55 | 56 | 57 | # need a struct with at least two pointers to catch certain bugs 58 | struct Regression { 59 | base @0: PlaneBase; 60 | b0 @1: Float64; # intercept 61 | beta @2: List(Float64); 62 | planes @3: List(Aircraft); 63 | ymu @4: Float64; # y-mean in original space 64 | ysd @5: Float64; # y-standard deviation in original space 65 | } 66 | 67 | 68 | 69 | struct Aircraft { 70 | # so we can restrict 71 | # and specify a Plane is required in 72 | # certain places. 73 | 74 | union { 75 | void @0: Void; # @0 will be the default, so always make @0 a Void. 76 | b737 @1: B737; 77 | a320 @2: A320; 78 | f16 @3: F16; 79 | } 80 | } 81 | 82 | 83 | struct Z { 84 | # Z must contain all types, as this is our 85 | # runtime type identification. It is a thin shim. 86 | 87 | union { 88 | void @0: Void; # always first in any union. 89 | zz @1: Z; # any. fyi, this can't be 'z' alone. 90 | 91 | f64 @2: Float64; 92 | f32 @3: Float32; 93 | 94 | i64 @4: Int64; 95 | i32 @5: Int32; 96 | i16 @6: Int16; 97 | i8 @7: Int8; 98 | 99 | u64 @8: UInt64; 100 | u32 @9: UInt32; 101 | u16 @10: UInt16; 102 | u8 @11: UInt8; 103 | 104 | bool @12: Bool; 105 | text @13: Text; 106 | blob @14: Data; 107 | 108 | f64vec @15: List(Float64); 109 | f32vec @16: List(Float32); 110 | 111 | i64vec @17: List(Int64); 112 | i32vec @18: List(Int32); 113 | i16vec @19: List(Int16); 114 | i8vec @20: List(Int8); 115 | 116 | u64vec @21: List(UInt64); 117 | u32vec @22: List(UInt32); 118 | u16vec @23: List(UInt16); 119 | u8vec @24: List(UInt8); 120 | 121 | zvec @25: List(Z); 122 | zvecvec @26: List(List(Z)); 123 | 124 | zdate @27: Zdate; 125 | zdata @28: Zdata; 126 | 127 | aircraftvec @29: List(Aircraft); 128 | aircraft @30: Aircraft; 129 | regression @31: Regression; 130 | planebase @32: PlaneBase; 131 | airport @33: Airport; 132 | b737 @34: B737; 133 | a320 @35: A320; 134 | f16 @36: F16; 135 | zdatevec @37: List(Zdate); 136 | zdatavec @38: List(Zdata); 137 | 138 | boolvec @39: List(Bool); 139 | } 140 | } 141 | 142 | # tests for Text/List(Text) recusion handling 143 | 144 | struct Counter { 145 | size @0: Int64; 146 | words @1: Text; 147 | wordlist @2: List(Text); 148 | } 149 | 150 | struct Bag { 151 | counter @0: Counter; 152 | } 153 | 154 | struct Zserver { 155 | waitingjobs @0: List(Zjob); 156 | } 157 | 158 | struct Zjob { 159 | cmd @0: Text; 160 | args @1: List(Text); 161 | } 162 | 163 | # versioning test structs 164 | 165 | struct VerEmpty { 166 | } 167 | 168 | struct VerOneData { 169 | val @0: Int16; 170 | } 171 | 172 | struct VerTwoData { 173 | val @0: Int16; 174 | duo @1: Int64; 175 | } 176 | 177 | struct VerOnePtr { 178 | ptr @0: VerOneData; 179 | } 180 | 181 | struct VerTwoPtr { 182 | ptr1 @0: VerOneData; 183 | ptr2 @1: VerOneData; 184 | } 185 | 186 | struct VerTwoDataTwoPtr { 187 | val @0: Int16; 188 | duo @1: Int64; 189 | ptr1 @2: VerOneData; 190 | ptr2 @3: VerOneData; 191 | } 192 | 193 | struct HoldsVerEmptyList { 194 | mylist @0: List(VerEmpty); 195 | } 196 | 197 | struct HoldsVerOneDataList { 198 | mylist @0: List(VerOneData); 199 | } 200 | 201 | struct HoldsVerTwoDataList { 202 | mylist @0: List(VerTwoData); 203 | } 204 | 205 | struct HoldsVerOnePtrList { 206 | mylist @0: List(VerOnePtr); 207 | } 208 | 209 | struct HoldsVerTwoPtrList { 210 | mylist @0: List(VerTwoPtr); 211 | } 212 | 213 | struct HoldsVerTwoTwoList { 214 | mylist @0: List(VerTwoDataTwoPtr); 215 | } 216 | 217 | struct HoldsVerTwoTwoPlus { 218 | mylist @0: List(VerTwoTwoPlus); 219 | } 220 | 221 | struct VerTwoTwoPlus { 222 | val @0: Int16; 223 | duo @1: Int64; 224 | ptr1 @2: VerTwoDataTwoPtr; 225 | ptr2 @3: VerTwoDataTwoPtr; 226 | tre @4: Int64; 227 | lst3 @5: List(Int64); 228 | } 229 | 230 | # text handling 231 | 232 | struct HoldsText { 233 | txt @0: Text; 234 | lst @1: List(Text); 235 | lstlst @2: List(List(Text)); 236 | } 237 | 238 | # test that we avoid unnecessary truncation 239 | 240 | struct WrapEmpty { 241 | mightNotBeReallyEmpty @0: VerEmpty; 242 | } 243 | 244 | struct Wrap2x2 { 245 | mightNotBeReallyEmpty @0: VerTwoDataTwoPtr; 246 | } 247 | 248 | struct Wrap2x2plus { 249 | mightNotBeReallyEmpty @0: VerTwoTwoPlus; 250 | } 251 | 252 | # test voids in a union 253 | 254 | struct VoidUnion { 255 | union { 256 | a @0 :Void; 257 | b @1 :Void; 258 | } 259 | } 260 | 261 | # test List(List(Struct(List))) 262 | 263 | struct Nester1Capn { 264 | strs @0: List(Text); 265 | } 266 | 267 | struct RWTestCapn { 268 | nestMatrix @0: List(List(Nester1Capn)); 269 | } 270 | 271 | struct ListStructCapn { 272 | vec @0: List(Nester1Capn); 273 | } 274 | 275 | # test interfaces 276 | 277 | interface Echo { 278 | echo @0 (in :Text) -> (out :Text); 279 | } 280 | 281 | struct Hoth { 282 | base @0 :EchoBase; 283 | } 284 | 285 | struct EchoBase { 286 | echo @0 :Echo; 287 | } 288 | 289 | # test transforms 290 | 291 | struct StackingRoot { 292 | a @1 :StackingA; 293 | aWithDefault @0 :StackingA = (num = 42); 294 | } 295 | 296 | struct StackingA { 297 | num @0 :Int32; 298 | b @1 :StackingB; 299 | } 300 | 301 | struct StackingB { 302 | num @0 :Int32; 303 | } 304 | 305 | interface CallSequence { 306 | getNumber @0 () -> (n :UInt32); 307 | } 308 | -------------------------------------------------------------------------------- /internal/aircraftlib/generate.go: -------------------------------------------------------------------------------- 1 | package aircraftlib 2 | 3 | //go:generate capnp compile -ogo aircraft.capnp 4 | -------------------------------------------------------------------------------- /internal/demo/book_test.go: -------------------------------------------------------------------------------- 1 | package demo_test 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "zombiezen.com/go/capnproto" 8 | "zombiezen.com/go/capnproto/internal/demo/books" 9 | ) 10 | 11 | func Example_book() { 12 | r, w := io.Pipe() 13 | go writer(w) 14 | reader(r) 15 | // Output: 16 | // "War and Peace" has 1440 pages 17 | } 18 | 19 | func writer(out io.Writer) { 20 | // Make a brand new empty message. A Message allocates Cap'n Proto structs. 21 | msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | // Create a new Book struct. Every message must have a root struct. 27 | book, err := books.NewRootBook(seg) 28 | if err != nil { 29 | panic(err) 30 | } 31 | book.SetTitle("War and Peace") 32 | book.SetPageCount(1440) 33 | 34 | // Write the message to stdout. 35 | err = capnp.NewEncoder(out).Encode(msg) 36 | if err != nil { 37 | panic(err) 38 | } 39 | } 40 | 41 | func reader(in io.Reader) { 42 | // Read the message from stdin. 43 | msg, err := capnp.NewDecoder(in).Decode() 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | // Extract the root struct from the message. 49 | book, err := books.ReadRootBook(msg) 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | // Access fields from the struct. 55 | title, err := book.Title() 56 | if err != nil { 57 | panic(err) 58 | } 59 | pageCount := book.PageCount() 60 | fmt.Printf("%q has %d pages\n", title, pageCount) 61 | } 62 | -------------------------------------------------------------------------------- /internal/demo/books/books.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "../../../go.capnp"; 2 | @0x85d3acc39d94e0f8; 3 | $Go.package("books"); 4 | $Go.import("zombiezen.com/go/capnproto/internal/demo/books"); 5 | 6 | struct Book { 7 | title @0 :Text; 8 | # Title of the book. 9 | 10 | pageCount @1 :Int32; 11 | # Number of pages in the book. 12 | } 13 | -------------------------------------------------------------------------------- /internal/demo/books/books.capnp.go: -------------------------------------------------------------------------------- 1 | package books 2 | 3 | // AUTO GENERATED - DO NOT EDIT 4 | 5 | import ( 6 | capnp "zombiezen.com/go/capnproto" 7 | ) 8 | 9 | type Book struct{ capnp.Struct } 10 | 11 | func NewBook(s *capnp.Segment) (Book, error) { 12 | st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 1}) 13 | if err != nil { 14 | return Book{}, err 15 | } 16 | return Book{st}, nil 17 | } 18 | 19 | func NewRootBook(s *capnp.Segment) (Book, error) { 20 | st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 1}) 21 | if err != nil { 22 | return Book{}, err 23 | } 24 | return Book{st}, nil 25 | } 26 | 27 | func ReadRootBook(msg *capnp.Message) (Book, error) { 28 | root, err := msg.Root() 29 | if err != nil { 30 | return Book{}, err 31 | } 32 | st := capnp.ToStruct(root) 33 | return Book{st}, nil 34 | } 35 | 36 | func (s Book) Title() (string, error) { 37 | p, err := s.Struct.Pointer(0) 38 | if err != nil { 39 | return "", err 40 | } 41 | 42 | return capnp.ToText(p), nil 43 | 44 | } 45 | 46 | func (s Book) SetTitle(v string) error { 47 | 48 | t, err := capnp.NewText(s.Struct.Segment(), v) 49 | if err != nil { 50 | return err 51 | } 52 | return s.Struct.SetPointer(0, t) 53 | } 54 | 55 | func (s Book) PageCount() int32 { 56 | return int32(s.Struct.Uint32(0)) 57 | } 58 | 59 | func (s Book) SetPageCount(v int32) { 60 | 61 | s.Struct.SetUint32(0, uint32(v)) 62 | } 63 | 64 | // Book_List is a list of Book. 65 | type Book_List struct{ capnp.List } 66 | 67 | // NewBook creates a new list of Book. 68 | func NewBook_List(s *capnp.Segment, sz int32) (Book_List, error) { 69 | l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 1}, sz) 70 | if err != nil { 71 | return Book_List{}, err 72 | } 73 | return Book_List{l}, nil 74 | } 75 | 76 | func (s Book_List) At(i int) Book { return Book{s.List.Struct(i)} } 77 | func (s Book_List) Set(i int, v Book) error { return s.List.SetStruct(i, v.Struct) } 78 | 79 | // Book_Promise is a wrapper for a Book promised by a client call. 80 | type Book_Promise struct{ *capnp.Pipeline } 81 | 82 | func (p Book_Promise) Struct() (Book, error) { 83 | s, err := p.Pipeline.Struct() 84 | return Book{s}, err 85 | } 86 | -------------------------------------------------------------------------------- /internal/demo/books/gen.go: -------------------------------------------------------------------------------- 1 | //go:generate capnp compile -ogo books.capnp 2 | 3 | package books 4 | -------------------------------------------------------------------------------- /internal/demo/hash_test.go: -------------------------------------------------------------------------------- 1 | package demo_test 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "hash" 7 | "net" 8 | 9 | "golang.org/x/net/context" 10 | "zombiezen.com/go/capnproto/internal/demo/hashes" 11 | "zombiezen.com/go/capnproto/rpc" 12 | ) 13 | 14 | // hashFactory is a local implementation of HashFactory. 15 | type hashFactory struct{} 16 | 17 | func (hf hashFactory) NewSha1(call hashes.HashFactory_newSha1) error { 18 | // Create a new locally implemented Hash capability. 19 | hs := hashes.Hash_ServerToClient(hashServer{sha1.New()}) 20 | // Notice that methods can return other interfaces. 21 | return call.Results.SetHash(hs) 22 | } 23 | 24 | // hashServer is a local implementation of Hash. 25 | type hashServer struct { 26 | h hash.Hash 27 | } 28 | 29 | func (hs hashServer) Write(call hashes.Hash_write) error { 30 | data, err := call.Params.Data() 31 | if err != nil { 32 | return err 33 | } 34 | _, err = hs.h.Write(data) 35 | if err != nil { 36 | return err 37 | } 38 | return nil 39 | } 40 | 41 | func (hs hashServer) Sum(call hashes.Hash_sum) error { 42 | s := hs.h.Sum(nil) 43 | return call.Results.SetHash(s) 44 | } 45 | 46 | func server(c net.Conn) error { 47 | // Create a new locally implemented HashFactory. 48 | main := hashes.HashFactory_ServerToClient(hashFactory{}) 49 | // Listen for calls, using the HashFactory as the bootstrap interface. 50 | conn := rpc.NewConn(rpc.StreamTransport(c), rpc.MainInterface(main.Client)) 51 | // Wait for connection to abort. 52 | err := conn.Wait() 53 | return err 54 | } 55 | 56 | func client(ctx context.Context, c net.Conn) error { 57 | // Create a connection that we can use to get the HashFactory. 58 | conn := rpc.NewConn(rpc.StreamTransport(c)) 59 | defer conn.Close() 60 | // Get the "bootstrap" interface. This is the capability set with 61 | // rpc.MainInterface on the remote side. 62 | hf := hashes.HashFactory{Client: conn.Bootstrap(ctx)} 63 | 64 | // Now we can call methods on hf, and they will be sent over c. 65 | s := hf.NewSha1(ctx, func(p hashes.HashFactory_newSha1_Params) error { 66 | return nil 67 | }).Hash() 68 | // s refers to a remote Hash. Method calls are delivered in order. 69 | s.Write(ctx, func(p hashes.Hash_write_Params) error { 70 | err := p.SetData([]byte("Hello, ")) 71 | return err 72 | }) 73 | s.Write(ctx, func(p hashes.Hash_write_Params) error { 74 | err := p.SetData([]byte("World!")) 75 | return err 76 | }) 77 | result, err := s.Sum(ctx, func(p hashes.Hash_sum_Params) error { 78 | return nil 79 | }).Struct() 80 | if err != nil { 81 | return err 82 | } 83 | sha1Val, err := result.Hash() 84 | if err != nil { 85 | return err 86 | } 87 | fmt.Printf("sha1: %x\n", sha1Val) 88 | return nil 89 | } 90 | 91 | func Example_hash() { 92 | c1, c2 := net.Pipe() 93 | go server(c1) 94 | client(context.Background(), c2) 95 | // Output: 96 | // sha1: 0a0a9f2a6772942557ab5355d76af442f8f65e01 97 | } 98 | -------------------------------------------------------------------------------- /internal/demo/hashes/gen.go: -------------------------------------------------------------------------------- 1 | //go:generate capnp compile -ogo hash.capnp 2 | 3 | package hashes 4 | -------------------------------------------------------------------------------- /internal/demo/hashes/hash.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "../../../go.capnp"; 2 | @0xdb8274f9144abc7e; 3 | $Go.package("hashes"); 4 | $Go.import("zombiezen.com/go/capnproto/internal/demo/hashes"); 5 | 6 | interface HashFactory { 7 | newSha1 @0 () -> (hash :Hash); 8 | } 9 | 10 | interface Hash { 11 | write @0 (data :Data) -> (); 12 | sum @1 () -> (hash :Data); 13 | } 14 | -------------------------------------------------------------------------------- /internal/fulfiller/fulfiller.go: -------------------------------------------------------------------------------- 1 | // Package fulfiller provides a type that implements capnp.Answer that 2 | // resolves by calling setter methods. 3 | package fulfiller 4 | 5 | import ( 6 | "errors" 7 | "sync" 8 | 9 | "zombiezen.com/go/capnproto" 10 | "zombiezen.com/go/capnproto/internal/queue" 11 | ) 12 | 13 | // callQueueSize is the maximum number of pending calls. 14 | const callQueueSize = 64 15 | 16 | // Fulfiller is a promise for a Struct. The zero value is an unresolved 17 | // answer. A Fulfiller is considered to be resolved once Fulfill or 18 | // Reject is called. Calls to the Fulfiller will queue up until it is 19 | // resolved. A Fulfiller is safe to use from multiple goroutines. 20 | type Fulfiller struct { 21 | once sync.Once 22 | resolved chan struct{} // initialized by init() 23 | 24 | // Protected by mu 25 | mu sync.RWMutex 26 | answer capnp.Answer 27 | queue []pcall // initialized by init() 28 | } 29 | 30 | // init initializes the Fulfiller. It is idempotent. 31 | // Should be called for each method on Fulfiller. 32 | func (f *Fulfiller) init() { 33 | f.once.Do(func() { 34 | f.resolved = make(chan struct{}) 35 | f.queue = make([]pcall, 0, callQueueSize) 36 | }) 37 | } 38 | 39 | // Fulfill sets the fulfiller's answer to s. If there are queued 40 | // pipeline calls, the capabilities on the struct will be embargoed 41 | // until the queued calls finish. Fulfill will panic if the fulfiller 42 | // has already been resolved. 43 | func (f *Fulfiller) Fulfill(s capnp.Struct) { 44 | f.init() 45 | f.mu.Lock() 46 | if f.answer != nil { 47 | f.mu.Unlock() 48 | panic("Fulfiller.Fulfill called more than once") 49 | } 50 | f.answer = capnp.ImmediateAnswer(s) 51 | queues := f.emptyQueue(s) 52 | ctab := s.Segment().Message().CapTable 53 | for capIdx, q := range queues { 54 | ctab[capIdx] = newEmbargoClient(ctab[capIdx], q) 55 | } 56 | close(f.resolved) 57 | f.mu.Unlock() 58 | } 59 | 60 | // emptyQueue splits the queue by which capability it targets and 61 | // drops any invalid calls. Once this function returns, f.queue will 62 | // be nil. 63 | func (f *Fulfiller) emptyQueue(s capnp.Struct) map[capnp.CapabilityID][]ecall { 64 | qs := make(map[capnp.CapabilityID][]ecall, len(f.queue)) 65 | for i, pc := range f.queue { 66 | c, err := capnp.Transform(capnp.Pointer(s), pc.transform) 67 | if err != nil { 68 | pc.f.Reject(err) 69 | continue 70 | } 71 | in := capnp.ToInterface(c) 72 | if !capnp.IsValid(in) { 73 | pc.f.Reject(capnp.ErrNullClient) 74 | continue 75 | } 76 | cn := in.Capability() 77 | if qs[cn] == nil { 78 | qs[cn] = make([]ecall, 0, len(f.queue)-i) 79 | } 80 | qs[cn] = append(qs[cn], pc.ecall) 81 | } 82 | f.queue = nil 83 | return qs 84 | } 85 | 86 | // Reject sets the fulfiller's answer to err. If there are queued 87 | // pipeline calls, they will all return errors. Reject will panic if 88 | // the error is nil or the fulfiller has already been resolved. 89 | func (f *Fulfiller) Reject(err error) { 90 | if err == nil { 91 | panic("Fulfiller.Reject called with nil") 92 | } 93 | f.init() 94 | f.mu.Lock() 95 | if f.answer != nil { 96 | f.mu.Unlock() 97 | panic("Fulfiller.Reject called more than once") 98 | } 99 | f.answer = capnp.ErrorAnswer(err) 100 | for i := range f.queue { 101 | f.queue[i].f.Reject(err) 102 | f.queue[i] = pcall{} 103 | } 104 | close(f.resolved) 105 | f.mu.Unlock() 106 | } 107 | 108 | // Done returns a channel that is closed once f is resolved. 109 | func (f *Fulfiller) Done() <-chan struct{} { 110 | f.init() 111 | return f.resolved 112 | } 113 | 114 | // Peek returns f's resolved answer or nil if f has not been resolved. 115 | // The Struct method of an answer returned from Peek returns immediately. 116 | func (f *Fulfiller) Peek() capnp.Answer { 117 | f.init() 118 | f.mu.RLock() 119 | a := f.answer 120 | f.mu.RUnlock() 121 | return a 122 | } 123 | 124 | // Struct waits until f is resolved and returns its struct if fulfilled 125 | // or an error if rejected. 126 | func (f *Fulfiller) Struct() (capnp.Struct, error) { 127 | <-f.Done() 128 | return f.Peek().Struct() 129 | } 130 | 131 | // PipelineCall calls PipelineCall on the fulfilled answer or queues the 132 | // call if f has not been fulfilled. 133 | func (f *Fulfiller) PipelineCall(transform []capnp.PipelineOp, call *capnp.Call) capnp.Answer { 134 | f.init() 135 | 136 | // Fast path: pass-through after fulfilled. 137 | if a := f.Peek(); a != nil { 138 | return a.PipelineCall(transform, call) 139 | } 140 | 141 | f.mu.Lock() 142 | // Make sure that f wasn't fulfilled. 143 | if a := f.answer; a != nil { 144 | f.mu.Unlock() 145 | return a.PipelineCall(transform, call) 146 | } 147 | if len(f.queue) == cap(f.queue) { 148 | f.mu.Unlock() 149 | return capnp.ErrorAnswer(errCallQueueFull) 150 | } 151 | cc, err := call.Copy(nil) 152 | if err != nil { 153 | f.mu.Unlock() 154 | return capnp.ErrorAnswer(err) 155 | } 156 | g := new(Fulfiller) 157 | f.queue = append(f.queue, pcall{ 158 | transform: transform, 159 | ecall: ecall{ 160 | call: cc, 161 | f: g, 162 | }, 163 | }) 164 | f.mu.Unlock() 165 | return g 166 | } 167 | 168 | // PipelineClose waits until f is resolved and then calls PipelineClose 169 | // on the fulfilled answer. 170 | func (f *Fulfiller) PipelineClose(transform []capnp.PipelineOp) error { 171 | <-f.Done() 172 | return f.Peek().PipelineClose(transform) 173 | } 174 | 175 | // pcall is a queued pipeline call. 176 | type pcall struct { 177 | transform []capnp.PipelineOp 178 | ecall 179 | } 180 | 181 | // embargoClient is a client that flushes a queue of calls. 182 | type embargoClient struct { 183 | client capnp.Client 184 | 185 | mu sync.RWMutex 186 | q queue.Queue 187 | } 188 | 189 | func newEmbargoClient(client capnp.Client, queue []ecall) capnp.Client { 190 | ec := &embargoClient{client: client} 191 | qq := make(ecallList, callQueueSize) 192 | n := copy(qq, queue) 193 | ec.q.Init(qq, n) 194 | go ec.flushQueue() 195 | return ec 196 | } 197 | 198 | func (ec *embargoClient) push(cl *capnp.Call) capnp.Answer { 199 | f := new(Fulfiller) 200 | cl, err := cl.Copy(nil) 201 | if err != nil { 202 | return capnp.ErrorAnswer(err) 203 | } 204 | if ok := ec.q.Push(ecall{cl, f}); !ok { 205 | return capnp.ErrorAnswer(errCallQueueFull) 206 | } 207 | return f 208 | } 209 | 210 | func (ec *embargoClient) peek() ecall { 211 | if ec.q.Len() == 0 { 212 | return ecall{} 213 | } 214 | return ec.q.Peek().(ecall) 215 | } 216 | 217 | func (ec *embargoClient) pop() ecall { 218 | if ec.q.Len() == 0 { 219 | return ecall{} 220 | } 221 | return ec.q.Pop().(ecall) 222 | } 223 | 224 | // flushQueue is run in its own goroutine. 225 | func (ec *embargoClient) flushQueue() { 226 | ec.mu.Lock() 227 | c := ec.peek() 228 | ec.mu.Unlock() 229 | for c.call != nil { 230 | ans := ec.client.Call(c.call) 231 | go func(f *Fulfiller, ans capnp.Answer) { 232 | s, err := ans.Struct() 233 | if err == nil { 234 | f.Fulfill(s) 235 | } else { 236 | f.Reject(err) 237 | } 238 | }(c.f, ans) 239 | ec.mu.Lock() 240 | ec.pop() 241 | c = ec.peek() 242 | ec.mu.Unlock() 243 | } 244 | } 245 | 246 | func (ec *embargoClient) WrappedClient() capnp.Client { 247 | ec.mu.RLock() 248 | ok := ec.isPassthrough() 249 | ec.mu.RUnlock() 250 | if !ok { 251 | return nil 252 | } 253 | return ec.client 254 | } 255 | 256 | func (ec *embargoClient) isPassthrough() bool { 257 | return ec.q.Len() == 0 258 | } 259 | 260 | func (ec *embargoClient) Call(cl *capnp.Call) capnp.Answer { 261 | // Fast path: queue is flushed. 262 | ec.mu.RLock() 263 | ok := ec.isPassthrough() 264 | ec.mu.RUnlock() 265 | if ok { 266 | return ec.client.Call(cl) 267 | } 268 | 269 | // Add to queue. 270 | ec.mu.Lock() 271 | // Since we released the lock, check that the queue hasn't been flushed. 272 | if ec.isPassthrough() { 273 | ec.mu.Unlock() 274 | return ec.client.Call(cl) 275 | } 276 | ans := ec.push(cl) 277 | ec.mu.Unlock() 278 | return ans 279 | } 280 | 281 | func (ec *embargoClient) Close() error { 282 | ec.mu.Lock() 283 | // reject all queued calls 284 | for ec.q.Len() > 0 { 285 | c := ec.pop() 286 | c.f.Reject(errQueueCallCancel) 287 | } 288 | ec.mu.Unlock() 289 | return ec.client.Close() 290 | } 291 | 292 | // ecall is an queued embargoed call. 293 | type ecall struct { 294 | call *capnp.Call 295 | f *Fulfiller 296 | } 297 | 298 | type ecallList []ecall 299 | 300 | func (el ecallList) Len() int { 301 | return len(el) 302 | } 303 | 304 | func (el ecallList) At(i int) interface{} { 305 | return el[i] 306 | } 307 | 308 | func (el ecallList) Set(i int, x interface{}) { 309 | if x == nil { 310 | el[i] = ecall{} 311 | } else { 312 | el[i] = x.(ecall) 313 | } 314 | } 315 | 316 | var ( 317 | errCallQueueFull = errors.New("capnp: promised answer call queue full") 318 | errQueueCallCancel = errors.New("capnp: queued call canceled") 319 | ) 320 | -------------------------------------------------------------------------------- /internal/fulfiller/fulfiller_test.go: -------------------------------------------------------------------------------- 1 | package fulfiller 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "zombiezen.com/go/capnproto" 8 | ) 9 | 10 | func TestFulfiller_NewShouldBeUnresolved(t *testing.T) { 11 | f := new(Fulfiller) 12 | 13 | if a := f.Peek(); a != nil { 14 | t.Errorf("f.Peek() = %v; want nil", a) 15 | } 16 | select { 17 | case <-f.Done(): 18 | t.Error("Done closed early") 19 | default: 20 | // success 21 | } 22 | } 23 | 24 | func TestFulfiller_FulfillShouldResolve(t *testing.T) { 25 | f := new(Fulfiller) 26 | st := newStruct(t, capnp.ObjectSize{}) 27 | 28 | f.Fulfill(st) 29 | 30 | select { 31 | case <-f.Done(): 32 | default: 33 | t.Error("Done still closed after Fulfill") 34 | } 35 | ret, err := f.Struct() 36 | if err != nil { 37 | t.Errorf("f.Struct() error: %v", err) 38 | } 39 | if ret != st { 40 | t.Errorf("f.Struct() = %v; want %v", ret, st) 41 | } 42 | } 43 | 44 | func TestFulfiller_RejectShouldResolve(t *testing.T) { 45 | f := new(Fulfiller) 46 | e := errors.New("failure and rejection") 47 | 48 | f.Reject(e) 49 | 50 | select { 51 | case <-f.Done(): 52 | default: 53 | t.Error("Done still closed after Reject") 54 | } 55 | ret, err := f.Struct() 56 | if err != e { 57 | t.Errorf("f.Struct() error = %v; want %v", err, e) 58 | } 59 | if capnp.IsValid(ret) { 60 | t.Errorf("f.Struct() = %v; want null", ret) 61 | } 62 | } 63 | 64 | func TestFulfiller_QueuedCallsDeliveredInOrder(t *testing.T) { 65 | f := new(Fulfiller) 66 | oc := new(orderClient) 67 | result := newStruct(t, capnp.ObjectSize{PointerCount: 1}) 68 | in := result.Segment().Message().AddCap(oc) 69 | result.SetPointer(0, capnp.NewInterface(result.Segment(), in)) 70 | 71 | ans1 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call)) 72 | ans2 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call)) 73 | f.Fulfill(result) 74 | ans3 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call)) 75 | ans3.Struct() 76 | ans4 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call)) 77 | 78 | check := func(a capnp.Answer, n uint64) { 79 | r, err := a.Struct() 80 | if r.Uint64(0) != n { 81 | t.Errorf("r%d = %d; want %d", n+1, r.Uint64(0), n) 82 | } 83 | if err != nil { 84 | t.Errorf("err%d = %v", n+1, err) 85 | } 86 | } 87 | check(ans1, 0) 88 | check(ans2, 1) 89 | check(ans3, 2) 90 | check(ans4, 3) 91 | } 92 | 93 | func newStruct(t *testing.T, sz capnp.ObjectSize) capnp.Struct { 94 | _, s, err := capnp.NewMessage(capnp.SingleSegment(nil)) 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | st, err := capnp.NewStruct(s, sz) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | return st 103 | } 104 | 105 | type orderClient int 106 | 107 | func (oc *orderClient) Call(cl *capnp.Call) capnp.Answer { 108 | _, s, err := capnp.NewMessage(capnp.SingleSegment(nil)) 109 | if err != nil { 110 | return capnp.ErrorAnswer(err) 111 | } 112 | st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8}) 113 | if err != nil { 114 | return capnp.ErrorAnswer(err) 115 | } 116 | st.SetUint64(0, uint64(*oc)) 117 | *oc++ 118 | return capnp.ImmediateAnswer(st) 119 | } 120 | 121 | func (oc *orderClient) Close() error { 122 | return nil 123 | } 124 | -------------------------------------------------------------------------------- /internal/packed/packed.go: -------------------------------------------------------------------------------- 1 | // Package packed provides functions to read and write the "packed" 2 | // compression scheme described at https://capnproto.org/encoding.html#packing. 3 | package packed 4 | 5 | import "io" 6 | 7 | const wordSize = 8 8 | 9 | // Special case tags. 10 | const ( 11 | zeroTag byte = 0x00 12 | unpackedTag byte = 0xff 13 | ) 14 | 15 | // Pack appends the packed version of src to dst and returns the 16 | // resulting slice. len(src) must be a multiple of 8 or Pack panics. 17 | func Pack(dst, src []byte) []byte { 18 | if len(src)%wordSize != 0 { 19 | panic("packed.Pack len(src) must be a multiple of 8") 20 | } 21 | var buf [wordSize]byte 22 | for len(src) > 0 { 23 | var hdr byte 24 | n := 0 25 | for i := uint(0); i < wordSize; i++ { 26 | if src[i] != 0 { 27 | hdr |= 1 << i 28 | buf[n] = src[i] 29 | n++ 30 | } 31 | } 32 | dst = append(dst, hdr) 33 | dst = append(dst, buf[:n]...) 34 | src = src[wordSize:] 35 | 36 | switch hdr { 37 | case zeroTag: 38 | z := min(numZeroWords(src), 0xff) 39 | dst = append(dst, byte(z)) 40 | src = src[z*wordSize:] 41 | case unpackedTag: 42 | i := 0 43 | end := min(len(src), 0xff*wordSize) 44 | for i < end { 45 | zeros := 0 46 | for _, b := range src[i : i+wordSize] { 47 | if b == 0 { 48 | zeros++ 49 | } 50 | } 51 | 52 | if zeros > 1 { 53 | break 54 | } 55 | i += wordSize 56 | } 57 | 58 | rawWords := byte(i / wordSize) 59 | dst = append(dst, rawWords) 60 | dst = append(dst, src[:i]...) 61 | src = src[i:] 62 | } 63 | } 64 | return dst 65 | } 66 | 67 | // numZeroWords returns the number of leading zero words in b. 68 | func numZeroWords(b []byte) int { 69 | for i, bb := range b { 70 | if bb != 0 { 71 | return i / wordSize 72 | } 73 | } 74 | return len(b) / wordSize 75 | } 76 | 77 | type decompressor struct { 78 | r io.Reader 79 | buf [wordSize]byte 80 | bufsz int 81 | 82 | // track the bytes after a 0xff raw tag 83 | ffBuf [wordSize]byte 84 | ffBufLoadCount int // count of bytes loaded from r into ffBuf (max wordSize) 85 | ffBufUsedCount int // count of bytes supplied to v during Read(). 86 | 87 | zeros int 88 | raw int // number of raw bytes left to copy through 89 | state decompressorState 90 | } 91 | 92 | // NewReader returns a reader that decompresses a packed stream from r. 93 | func NewReader(r io.Reader) io.Reader { 94 | return &decompressor{r: r} 95 | } 96 | 97 | func min(a, b int) int { 98 | if b < a { 99 | return b 100 | } 101 | return a 102 | } 103 | 104 | func (c *decompressor) Read(v []byte) (n int, err error) { 105 | 106 | var b [1]byte 107 | var bytesRead int 108 | 109 | for { 110 | if len(v) == 0 { 111 | return 112 | } 113 | 114 | switch c.state { 115 | 116 | case rawState: 117 | if c.raw > 0 { 118 | bytesRead, err = c.r.Read(v[:min(len(v), c.raw)]) 119 | c.raw -= bytesRead 120 | v = v[bytesRead:] 121 | n += bytesRead 122 | 123 | if err != nil { 124 | return 125 | } 126 | } else { 127 | c.state = normalState 128 | } 129 | 130 | case postFFState: 131 | if c.ffBufUsedCount >= wordSize { 132 | c.state = readnState 133 | continue 134 | } 135 | // invar: c.ffBufUsedCount < wordSize 136 | 137 | // before reading more from r, first empty any residual in buffer. Such 138 | // bytes were already read from r, are now 139 | // waiting in c.ffBuf, and have not yet been given to v: so 140 | // these bytes are first in line to go. 141 | if c.ffBufUsedCount < c.ffBufLoadCount { 142 | br := copy(v, c.ffBuf[c.ffBufUsedCount:c.ffBufLoadCount]) 143 | c.ffBufUsedCount += br 144 | v = v[br:] 145 | n += br 146 | } 147 | if c.ffBufUsedCount >= wordSize { 148 | c.state = readnState 149 | continue 150 | } 151 | // invar: c.ffBufUsedCount < wordSize 152 | 153 | // io.ReadFull, try to read exactly (wordSize - cc.ffBufLoadCount) bytes 154 | // io.ReadFull returns EOF only if no bytes were read 155 | if c.ffBufLoadCount < wordSize { 156 | bytesRead, err = io.ReadFull(c.r, c.ffBuf[c.ffBufLoadCount:]) // read up to wordSize bytes into c.buf 157 | if bytesRead > 0 { 158 | c.ffBufLoadCount += bytesRead 159 | } else { 160 | return 161 | } 162 | if err != nil { 163 | return 164 | } 165 | } 166 | // stay in postFFState 167 | 168 | case readnState: 169 | if bytesRead, err = c.r.Read(b[:]); err != nil { 170 | return 171 | } 172 | if bytesRead == 0 { 173 | return 174 | } 175 | c.raw = int(b[0]) * wordSize 176 | c.state = rawState 177 | 178 | case normalState: 179 | 180 | if c.zeros > 0 { 181 | num0 := min(len(v), c.zeros) 182 | x := v[:num0] 183 | for i := range x { 184 | x[i] = 0 185 | } 186 | c.zeros -= num0 187 | n += num0 188 | if c.zeros > 0 { 189 | return n, nil 190 | } 191 | v = v[num0:] 192 | if len(v) == 0 { 193 | return n, nil 194 | } 195 | } 196 | // INVAR: c.zeros == 0 197 | 198 | if c.bufsz > 0 { 199 | nc := copy(v, c.buf[wordSize-c.bufsz:]) 200 | c.bufsz -= nc 201 | n += nc 202 | v = v[nc:] 203 | if c.bufsz > 0 { 204 | return n, nil 205 | } 206 | } 207 | // INVAR: c.bufz == 0 208 | 209 | for c.state == normalState && len(v) > 0 { 210 | 211 | if _, err = c.r.Read(b[:]); err != nil { 212 | return 213 | } 214 | 215 | switch b[0] { 216 | case unpackedTag: 217 | c.ffBufLoadCount = 0 218 | c.ffBufUsedCount = 0 219 | c.state = postFFState 220 | 221 | break 222 | 223 | case zeroTag: 224 | if _, err = c.r.Read(b[:]); err != nil { 225 | return 226 | } 227 | 228 | requestedZeroBytes := (int(b[0]) + 1) * wordSize 229 | zeros := min(requestedZeroBytes, len(v)) 230 | 231 | for i := 0; i < zeros; i++ { 232 | v[i] = 0 233 | } 234 | v = v[zeros:] 235 | n += zeros 236 | // remember the leftover zeros to write 237 | c.zeros = requestedZeroBytes - zeros 238 | 239 | default: 240 | ones := 0 241 | var buf [wordSize]byte 242 | for i := 0; i < wordSize; i++ { 243 | if (b[0] & (1 << uint(i))) != 0 { 244 | ones++ 245 | } 246 | } 247 | 248 | _, err = io.ReadFull(c.r, buf[:ones]) 249 | if err != nil { 250 | return 251 | } 252 | 253 | for i, j := 0, 0; i < wordSize; i++ { 254 | if (b[0] & (1 << uint(i))) != 0 { 255 | c.buf[i] = buf[j] 256 | j++ 257 | } else { 258 | c.buf[i] = 0 259 | } 260 | } 261 | 262 | use := copy(v, c.buf[:]) 263 | v = v[use:] 264 | n += use 265 | c.bufsz = wordSize - use 266 | } 267 | } 268 | } 269 | 270 | } 271 | return 272 | } 273 | 274 | // decompressorState is the state of a decompressor. 275 | type decompressorState uint8 276 | 277 | // Decompressor states 278 | const ( 279 | normalState decompressorState = iota 280 | 281 | // These states are for dealing with the 0xFF tag and the raw bytes that follow. 282 | // They tell us where to pick up if we are interrupted in the middle of anything 283 | // after the 0xFF tag, until we are done with the raw read. 284 | postFFState 285 | readnState 286 | rawState 287 | ) 288 | -------------------------------------------------------------------------------- /internal/packed/packed_test.go: -------------------------------------------------------------------------------- 1 | package packed 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "io" 7 | "testing" 8 | ) 9 | 10 | var compressionTests = []struct { 11 | original []byte 12 | compressed []byte 13 | }{ 14 | { 15 | []byte{}, 16 | []byte{}, 17 | }, 18 | { 19 | []byte{0, 0, 0, 0, 0, 0, 0, 0}, 20 | []byte{0, 0}, 21 | }, 22 | { 23 | []byte{0, 0, 12, 0, 0, 34, 0, 0}, 24 | []byte{0x24, 12, 34}, 25 | }, 26 | { 27 | []byte{ 28 | 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 29 | 0x19, 0x00, 0x00, 0x00, 0xaa, 0x01, 0x00, 0x00, 30 | }, 31 | []byte{0x51, 0x08, 0x03, 0x02, 0x31, 0x19, 0xaa, 0x01}, 32 | }, 33 | { 34 | []byte{0x8, 0, 0, 0, 0x3, 0, 0x2, 0, 0x19, 0, 0, 0, 0xaa, 0x1, 0, 0}, 35 | []byte{0x51, 0x08, 0x03, 0x02, 0x31, 0x19, 0xaa, 0x01}, 36 | }, 37 | { 38 | []byte{ 39 | 0, 0, 0, 0, 0, 0, 0, 0, 40 | 0, 0, 0, 0, 0, 0, 0, 0, 41 | 0, 0, 0, 0, 0, 0, 0, 0, 42 | 0, 0, 0, 0, 0, 0, 0, 0, 43 | }, 44 | []byte{0x00, 0x03}, 45 | }, 46 | { 47 | []byte{ 48 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 49 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 50 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 51 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 52 | }, 53 | []byte{ 54 | 0xff, 55 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 56 | 0x03, 57 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 58 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 59 | 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 60 | }, 61 | }, 62 | { 63 | []byte{1, 3, 2, 4, 5, 7, 6, 8}, 64 | []byte{0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0}, 65 | }, 66 | { 67 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8}, 68 | []byte{0, 0, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0}, 69 | }, 70 | { 71 | []byte{0, 0, 12, 0, 0, 34, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8}, 72 | []byte{0x24, 12, 34, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0}, 73 | }, 74 | { 75 | []byte{1, 3, 2, 4, 5, 7, 6, 8, 8, 6, 7, 4, 5, 2, 3, 1}, 76 | []byte{0xff, 1, 3, 2, 4, 5, 7, 6, 8, 1, 8, 6, 7, 4, 5, 2, 3, 1}, 77 | }, 78 | { 79 | []byte{ 80 | 1, 2, 3, 4, 5, 6, 7, 8, 81 | 1, 2, 3, 4, 5, 6, 7, 8, 82 | 1, 2, 3, 4, 5, 6, 7, 8, 83 | 1, 2, 3, 4, 5, 6, 7, 8, 84 | 0, 2, 4, 0, 9, 0, 5, 1, 85 | }, 86 | []byte{ 87 | 0xff, 1, 2, 3, 4, 5, 6, 7, 8, 88 | 3, 89 | 1, 2, 3, 4, 5, 6, 7, 8, 90 | 1, 2, 3, 4, 5, 6, 7, 8, 91 | 1, 2, 3, 4, 5, 6, 7, 8, 92 | 0xd6, 2, 4, 9, 5, 1, 93 | }, 94 | }, 95 | { 96 | []byte{ 97 | 1, 2, 3, 4, 5, 6, 7, 8, 98 | 1, 2, 3, 4, 5, 6, 7, 8, 99 | 6, 2, 4, 3, 9, 0, 5, 1, 100 | 1, 2, 3, 4, 5, 6, 7, 8, 101 | 0, 2, 4, 0, 9, 0, 5, 1, 102 | }, 103 | []byte{ 104 | 0xff, 1, 2, 3, 4, 5, 6, 7, 8, 105 | 3, 106 | 1, 2, 3, 4, 5, 6, 7, 8, 107 | 6, 2, 4, 3, 9, 0, 5, 1, 108 | 1, 2, 3, 4, 5, 6, 7, 8, 109 | 0xd6, 2, 4, 9, 5, 1, 110 | }, 111 | }, 112 | { 113 | []byte{ 114 | 8, 0, 100, 6, 0, 1, 1, 2, 115 | 0, 0, 0, 0, 0, 0, 0, 0, 116 | 0, 0, 0, 0, 0, 0, 0, 0, 117 | 0, 0, 0, 0, 0, 0, 0, 0, 118 | 0, 0, 1, 0, 2, 0, 3, 1, 119 | }, 120 | []byte{ 121 | 0xed, 8, 100, 6, 1, 1, 2, 122 | 0, 2, 123 | 0xd4, 1, 2, 3, 1, 124 | }, 125 | }, 126 | { 127 | []byte{ 128 | 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 129 | 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 130 | 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 131 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 132 | 0x1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 133 | 0xd4, 0x7, 0xc, 0x7, 0x0, 0x0, 0x0, 0x0, 134 | }, 135 | []byte{ 136 | 0x10, 0x5, 137 | 0x50, 0x2, 0x1, 138 | 0x1, 0x25, 139 | 0x0, 0x0, 140 | 0x11, 0x1, 0xc, 141 | 0xf, 0xd4, 0x7, 0xc, 0x7, 142 | }, 143 | }, 144 | } 145 | 146 | func TestPack(t *testing.T) { 147 | for _, test := range compressionTests { 148 | orig := make([]byte, len(test.original)) 149 | copy(orig, test.original) 150 | compressed := Pack(nil, orig) 151 | if !bytes.Equal(compressed, test.compressed) { 152 | t.Errorf("Pack(nil,\n%s\n) =\n%s\n; want\n%s", hex.Dump(test.original), hex.Dump(compressed), hex.Dump(test.compressed)) 153 | } 154 | } 155 | } 156 | 157 | func TestReader(t *testing.T) { 158 | for i, test := range compressionTests { 159 | for readSize := 1; readSize <= 8+2*len(test.original); readSize++ { 160 | r := bytes.NewReader(test.compressed) 161 | d := NewReader(r) 162 | buf := make([]byte, readSize) 163 | var actual []byte 164 | for { 165 | n, err := d.Read(buf) 166 | actual = append(actual, buf[:n]...) 167 | if err != nil { 168 | if err == io.EOF { 169 | break 170 | } 171 | t.Fatalf("Read: %v", err) 172 | } 173 | } 174 | 175 | if len(test.original) != len(actual) { 176 | t.Errorf("test:%d readSize:%d expected %d bytes, got %d", 177 | i, readSize, len(test.original), len(actual)) 178 | continue 179 | } 180 | 181 | if !bytes.Equal(test.original, actual) { 182 | t.Errorf("test:%d readSize:%d: bytes not equal", i, readSize) 183 | } 184 | } 185 | } 186 | } 187 | 188 | var result []byte 189 | 190 | func BenchmarkPack(b *testing.B) { 191 | src := bytes.Repeat([]byte{ 192 | 8, 0, 100, 6, 0, 1, 1, 2, 193 | 8, 0, 100, 6, 0, 1, 1, 2, 194 | 0, 0, 0, 0, 0, 0, 0, 0, 195 | 0, 0, 0, 0, 0, 0, 0, 0, 196 | 0, 0, 0, 0, 0, 0, 0, 0, 197 | 0, 0, 0, 0, 0, 0, 0, 0, 198 | 0, 0, 1, 0, 2, 0, 3, 1, 199 | 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 200 | 'o', 'r', 'l', 'd', '!', ' ', ' ', 'P', 201 | 'a', 'd', ' ', 't', 'e', 'x', 't', '.', 202 | }, 128) 203 | dst := make([]byte, 0, len(src)) 204 | b.SetBytes(int64(len(src))) 205 | 206 | b.ResetTimer() 207 | for i := 0; i < b.N; i++ { 208 | Pack(dst, src) 209 | } 210 | result = dst 211 | } 212 | 213 | func BenchmarkDecompressor(b *testing.B) { 214 | const multiplier = 128 215 | src := bytes.Repeat([]byte{ 216 | 0xa7, 8, 100, 6, 1, 1, 2, 217 | 0xa7, 8, 100, 6, 1, 1, 2, 218 | 0x00, 3, 219 | 0x2a, 220 | 0xff, 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 221 | 2, 222 | 'o', 'r', 'l', 'd', '!', ' ', ' ', 'P', 223 | 'a', 'd', ' ', 't', 'e', 'x', 't', '.', 224 | }, multiplier) 225 | b.SetBytes(int64(len(src))) 226 | r := bytes.NewReader(src) 227 | 228 | dst := make([]byte, 10*8*multiplier) 229 | 230 | b.ResetTimer() 231 | for i := 0; i < b.N; i++ { 232 | r.Seek(0, 0) 233 | d := NewReader(r) 234 | _, err := d.Read(dst) 235 | if err != nil { 236 | b.Fatal(err) 237 | } 238 | } 239 | result = dst 240 | } 241 | -------------------------------------------------------------------------------- /internal/queue/queue.go: -------------------------------------------------------------------------------- 1 | // Package queue implements a generic queue using a ring buffer. 2 | package queue 3 | 4 | // A Queue wraps an Interface to provide queue operations. 5 | type Queue struct { 6 | q Interface 7 | start int 8 | n int 9 | } 10 | 11 | // New creates a new queue that starts with n elements. 12 | func New(q Interface, n int) *Queue { 13 | qq := new(Queue) 14 | qq.Init(q, n) 15 | return qq 16 | } 17 | 18 | // Init initializes a queue. The old queue is untouched. 19 | func (q *Queue) Init(r Interface, n int) { 20 | q.q = r 21 | q.start, q.n = 0, n 22 | } 23 | 24 | // Len returns the length of the queue. This is different from the 25 | // underlying interface's length. 26 | func (q *Queue) Len() int { 27 | return q.n 28 | } 29 | 30 | // Push pushes an element on the queue. If the queue is full, 31 | // Push returns false. If x is nil, Push panics. 32 | func (q *Queue) Push(x interface{}) bool { 33 | n := q.q.Len() 34 | if q.n >= n { 35 | return false 36 | } 37 | i := (q.start + q.n) % n 38 | q.q.Set(i, x) 39 | q.n++ 40 | return true 41 | } 42 | 43 | // Peek returns the element at the front of the queue. 44 | // If the queue is empty, Peek panics. 45 | func (q *Queue) Peek() interface{} { 46 | if q.n == 0 { 47 | panic("Queue.Pop called on empty queue") 48 | } 49 | return q.q.At(q.start) 50 | } 51 | 52 | // Pop pops an element from the queue. 53 | // If the queue is empty, Pop panics. 54 | func (q *Queue) Pop() interface{} { 55 | x := q.Peek() 56 | q.q.Set(q.start, nil) 57 | q.start = (q.start + 1) % q.q.Len() 58 | q.n-- 59 | return x 60 | } 61 | 62 | // A type implementing Interface can be used to store elements in a Queue. 63 | type Interface interface { 64 | // Len returns the number of elements available. 65 | Len() int 66 | // At returns the element at i. 67 | At(i int) interface{} 68 | // Set sets the element at i to x. 69 | // If x is nil, that element should be cleared. 70 | Set(i int, x interface{}) 71 | } 72 | -------------------------------------------------------------------------------- /internal/queue/queue_test.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestNew(t *testing.T) { 8 | qi := make(ints, 5) 9 | 10 | q := New(qi, 0) 11 | 12 | if n := q.Len(); n != 0 { 13 | t.Errorf("New(qi, 0).Len() = %d; want 0", n) 14 | } 15 | } 16 | 17 | func TestPrepush(t *testing.T) { 18 | qi := make(ints, 5) 19 | qi[0] = 42 20 | 21 | q := New(qi, 1) 22 | 23 | if n := q.Len(); n != 1 { 24 | t.Fatalf("New(qi, 1).Len() = %d; want 1", n) 25 | } 26 | if x := q.Pop().(int); x != 42 { 27 | t.Errorf("Pop() = %d; want 42", x) 28 | } 29 | } 30 | 31 | func TestPush(t *testing.T) { 32 | qi := make(ints, 5) 33 | q := New(qi, 0) 34 | 35 | ok := q.Push(42) 36 | 37 | if !ok { 38 | t.Error("q.Push(42) returned false") 39 | } 40 | if n := q.Len(); n != 1 { 41 | t.Errorf("q.Len() after push = %d; want 1", n) 42 | } 43 | } 44 | 45 | func TestPushFull(t *testing.T) { 46 | qi := make(ints, 5) 47 | q := New(qi, 0) 48 | var ok [6]bool 49 | 50 | ok[0] = q.Push(10) 51 | ok[1] = q.Push(11) 52 | ok[2] = q.Push(12) 53 | ok[3] = q.Push(13) 54 | ok[4] = q.Push(14) 55 | ok[5] = q.Push(15) 56 | 57 | for i := 0; i < 5; i++ { 58 | if !ok[i] { 59 | t.Errorf("q.Push(%d) returned false", 10+i) 60 | } 61 | } 62 | if ok[5] { 63 | t.Error("q.Push(15) returned true") 64 | } 65 | if n := q.Len(); n != 5 { 66 | t.Errorf("q.Len() after full = %d; want 5", n) 67 | } 68 | } 69 | 70 | func TestPop(t *testing.T) { 71 | qi := make(ints, 5) 72 | q := New(qi, 0) 73 | q.Push(1) 74 | q.Push(2) 75 | q.Push(3) 76 | 77 | outs := make([]int, 0, len(qi)) 78 | outs = append(outs, q.Pop().(int)) 79 | outs = append(outs, q.Pop().(int)) 80 | outs = append(outs, q.Pop().(int)) 81 | 82 | if n := q.Len(); n != 0 { 83 | t.Errorf("q.Len() after pops = %d; want 0", n) 84 | } 85 | if outs[0] != 1 { 86 | t.Errorf("first pop = %d; want 1", outs[0]) 87 | } 88 | if outs[1] != 2 { 89 | t.Errorf("first pop = %d; want 2", outs[1]) 90 | } 91 | if outs[2] != 3 { 92 | t.Errorf("first pop = %d; want 3", outs[2]) 93 | } 94 | for i := range qi { 95 | if qi[i] != 0 { 96 | t.Errorf("qi[%d] = %d; want 0 (not cleared)", i, qi[i]) 97 | } 98 | } 99 | } 100 | 101 | func TestWrap(t *testing.T) { 102 | qi := make(ints, 5) 103 | q := New(qi, 0) 104 | var ok [7]bool 105 | 106 | ok[0] = q.Push(10) 107 | ok[1] = q.Push(11) 108 | ok[2] = q.Push(12) 109 | q.Pop() 110 | q.Pop() 111 | ok[3] = q.Push(13) 112 | ok[4] = q.Push(14) 113 | ok[5] = q.Push(15) 114 | ok[6] = q.Push(16) 115 | 116 | for i := 0; i < 6; i++ { 117 | if !ok[i] { 118 | t.Errorf("q.Push(%d) returned false", 10+i) 119 | } 120 | } 121 | if n := q.Len(); n != 5 { 122 | t.Errorf("q.Len() = %d; want 5", n) 123 | } 124 | for i := 12; q.Len() > 0; i++ { 125 | if x := q.Pop().(int); x != i { 126 | t.Errorf("q.Pop() = %d; want %d", x, i) 127 | } 128 | } 129 | } 130 | 131 | type ints []int 132 | 133 | func (is ints) Len() int { 134 | return len(is) 135 | } 136 | 137 | func (is ints) At(i int) interface{} { 138 | return is[i] 139 | } 140 | 141 | func (is ints) Set(i int, x interface{}) { 142 | if x == nil { 143 | is[i] = 0 144 | } else { 145 | is[i] = x.(int) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /pointer.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // A Pointer is a reference to a Cap'n Proto struct, list, or interface. 4 | type Pointer interface { 5 | // Segment returns the segment this pointer points into. 6 | // If nil, then this is an invalid pointer. 7 | Segment() *Segment 8 | 9 | // HasData reports whether the object referenced by the pointer has 10 | // non-zero size. 11 | HasData() bool 12 | 13 | // value converts the pointer into a raw value. 14 | value(paddr Address) rawPointer 15 | 16 | // underlying returns a Pointer that is one of a Struct, a List, or an 17 | // Interface. 18 | underlying() Pointer 19 | } 20 | 21 | // IsValid reports whether p is non-nil and valid. 22 | func IsValid(p Pointer) bool { 23 | return p != nil && p.Segment() != nil 24 | } 25 | 26 | // HasData returns true if the pointer is valid and has non-zero size. 27 | func HasData(p Pointer) bool { 28 | return IsValid(p) && p.HasData() 29 | } 30 | 31 | // PointerDefault returns p if it is valid, otherwise it unmarshals def. 32 | func PointerDefault(p Pointer, def []byte) (Pointer, error) { 33 | if !IsValid(p) { 34 | return unmarshalDefault(def) 35 | } 36 | return p, nil 37 | } 38 | 39 | func unmarshalDefault(def []byte) (Pointer, error) { 40 | msg, err := Unmarshal(def) 41 | if err != nil { 42 | return nil, err 43 | } 44 | p, err := msg.Root() 45 | if err != nil { 46 | return nil, err 47 | } 48 | return p, nil 49 | } 50 | 51 | // pointerAddress returns the pointer's address. 52 | // It panics if p's underlying pointer is not a valid Struct or List. 53 | func pointerAddress(p Pointer) Address { 54 | type addresser interface { 55 | Address() Address 56 | } 57 | a := p.underlying().(addresser) 58 | return a.Address() 59 | } 60 | -------------------------------------------------------------------------------- /rawpointer.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // pointerOffset is an address offset in multiples of word size. 4 | type pointerOffset int32 5 | 6 | // resolve returns the absolute address, given that the pointer is located at paddr. 7 | func (off pointerOffset) resolve(paddr Address) (addr Address, ok bool) { 8 | // TODO(light): verify 9 | if off < 0 && Address(-off) > paddr { 10 | return 0, false 11 | } 12 | return paddr + Address(off*8) + 8, true 13 | } 14 | 15 | // makePointerOffset computes the offset for a pointer at paddr to point to addr. 16 | func makePointerOffset(paddr, addr Address) pointerOffset { 17 | // TODO(light): verify 18 | return pointerOffset(addr/Address(wordSize) - paddr/Address(wordSize) - 1) 19 | } 20 | 21 | // rawPointer is an encoded pointer. 22 | type rawPointer uint64 23 | 24 | // rawStructPointer returns a struct pointer. The offset is from the 25 | // end of the pointer to the start of the struct. 26 | func rawStructPointer(off pointerOffset, sz ObjectSize) rawPointer { 27 | return structPointer | orable30BitOffsetPart(off) | rawPointer(sz.dataWordCount())<<32 | rawPointer(sz.PointerCount)<<48 28 | } 29 | 30 | // rawListPointer returns a list pointer. The offset is the number of 31 | // words relative to the end of the pointer that the list starts. If 32 | // listType is compositeList, then length is the number of words 33 | // that the list occupies, otherwise it is the number of elements in 34 | // the list. 35 | func rawListPointer(off pointerOffset, listType int, length int32) rawPointer { 36 | return listPointer | orable30BitOffsetPart(off) | rawPointer(listType)<<32 | rawPointer(length)<<35 37 | } 38 | 39 | // rawInterfacePointer returns an interface pointer that references 40 | // a capability number. 41 | func rawInterfacePointer(capability CapabilityID) rawPointer { 42 | return otherPointer | rawPointer(capability)<<32 43 | } 44 | 45 | // rawFarPointer returns a pointer to a pointer in another segment. 46 | func rawFarPointer(segID SegmentID, off Address) rawPointer { 47 | return farPointer | rawPointer(off&^7) | (rawPointer(segID) << 32) 48 | } 49 | 50 | // rawDoubleFarPointer returns a pointer to a pointer in another segment. 51 | func rawDoubleFarPointer(segID SegmentID, off Address) rawPointer { 52 | return doubleFarPointer | rawPointer(off&^7) | (rawPointer(segID) << 32) 53 | } 54 | 55 | // landingPadNearPointer converts a far pointer landing pad into 56 | // a near pointer in the destination segment. Its offset will be 57 | // relative to the beginning of the segment. 58 | func landingPadNearPointer(far, tag rawPointer) rawPointer { 59 | return tag | rawPointer(far.farAddress()-Address(wordSize))<<2 60 | } 61 | 62 | // Raw pointer types. 63 | const ( 64 | structPointer = 0 65 | listPointer = 1 66 | farPointer = 2 67 | doubleFarPointer = 6 68 | otherPointer = 3 69 | ) 70 | 71 | // Raw list pointer types. 72 | const ( 73 | voidList = 0 74 | bit1List = 1 75 | byte1List = 2 76 | byte2List = 3 77 | byte4List = 4 78 | byte8List = 5 79 | pointerList = 6 80 | compositeList = 7 81 | ) 82 | 83 | func (p rawPointer) pointerType() int { 84 | t := p & 3 85 | if t == farPointer { 86 | return int(p & 7) 87 | } 88 | return int(t) 89 | } 90 | 91 | func (p rawPointer) structSize() ObjectSize { 92 | c := uint16(p >> 32) 93 | d := uint16(p >> 48) 94 | return ObjectSize{ 95 | DataSize: wordSize.times(int32(c)), 96 | PointerCount: d, 97 | } 98 | } 99 | 100 | func (p rawPointer) listType() int { 101 | return int((p >> 32) & 7) 102 | } 103 | 104 | func (p rawPointer) numListElements() int32 { 105 | return int32(p >> 35) 106 | } 107 | 108 | // elementSize returns the size of an individual element in the list referenced by p. 109 | func (p rawPointer) elementSize() ObjectSize { 110 | switch p.listType() { 111 | case voidList: 112 | return ObjectSize{} 113 | case bit1List: 114 | // Size is ignored on bit lists. 115 | return ObjectSize{} 116 | case byte1List: 117 | return ObjectSize{DataSize: 1} 118 | case byte2List: 119 | return ObjectSize{DataSize: 2} 120 | case byte4List: 121 | return ObjectSize{DataSize: 4} 122 | case byte8List: 123 | return ObjectSize{DataSize: 8} 124 | case pointerList: 125 | return ObjectSize{PointerCount: 1} 126 | default: 127 | panic("elementSize not supposed to be called on composite or unknown list type") 128 | } 129 | } 130 | 131 | // totalListSize returns the total size of the list referenced by p. 132 | func (p rawPointer) totalListSize() Size { 133 | n := p.numListElements() 134 | switch p.listType() { 135 | case voidList: 136 | return 0 137 | case bit1List: 138 | return Size((n + 7) / 8) 139 | case compositeList: 140 | // For a composite list, n represents the number of words (excluding the tag word). 141 | return wordSize.times(n + 1) 142 | default: 143 | return p.elementSize().totalSize().times(n) 144 | } 145 | } 146 | 147 | // used in orable30BitOffsetPart(), rawPointer.offset(), and rawPointer.otherPointerType() 148 | const zerohi32 rawPointer = ^(^0 << 32) 149 | 150 | // orable30BitOffsetPart(): get an or-able value that handles sign 151 | // conversion. Creates part B in a struct (or list) pointer, leaving 152 | // parts A, C, and D completely zeroed in the returned uint64. 153 | // 154 | // From the spec: 155 | // 156 | // lsb struct pointer msb 157 | // +-+-----------------------------+---------------+---------------+ 158 | // |A| B | C | D | 159 | // +-+-----------------------------+---------------+---------------+ 160 | // 161 | // A (2 bits) = 0, to indicate that this is a struct pointer. 162 | // B (30 bits) = Offset, in words, from the end of the pointer to the 163 | // start of the struct's data section. Signed. 164 | // C (16 bits) = Size of the struct's data section, in words. 165 | // D (16 bits) = Size of the struct's pointer section, in words. 166 | // 167 | // (B is the same for list pointers, but C and D have different size 168 | // and meaning) 169 | // 170 | func orable30BitOffsetPart(signedOff pointerOffset) rawPointer { 171 | d32 := signedOff << 2 172 | return rawPointer(d32) & zerohi32 173 | } 174 | 175 | // and convert in the other direction, extracting the count from 176 | // the B section into an int 177 | func (p rawPointer) offset() pointerOffset { 178 | u64 := p & zerohi32 179 | u32 := uint32(u64) 180 | s32 := int32(u32) >> 2 181 | return pointerOffset(s32) 182 | } 183 | 184 | // otherPointerType returns the type of "other pointer" from p. 185 | func (p rawPointer) otherPointerType() uint32 { 186 | return uint32(p & zerohi32 >> 2) 187 | } 188 | 189 | // farAddress returns the address of the landing pad pointer. 190 | func (p rawPointer) farAddress() Address { 191 | return Address(0).element(int32(p&(1<<32-1)>>3), wordSize) 192 | } 193 | 194 | // farSegment returns the segment ID that the far pointer references. 195 | func (p rawPointer) farSegment() SegmentID { 196 | return SegmentID(p >> 32) 197 | } 198 | 199 | // capabilityIndex returns the index of the capability in the message's capability table. 200 | func (p rawPointer) capabilityIndex() CapabilityID { 201 | return CapabilityID(p >> 32) 202 | } 203 | -------------------------------------------------------------------------------- /rawpointer_test.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRawStructPointer(t *testing.T) { 8 | tests := []struct { 9 | ptr rawPointer 10 | offset pointerOffset 11 | size ObjectSize 12 | }{ 13 | {0x0000000000000000, 0, ObjectSize{}}, 14 | {0x0000000000000004, 1, ObjectSize{}}, 15 | {0x0000000000000008, 2, ObjectSize{}}, 16 | {0x000000000000000c, 3, ObjectSize{}}, 17 | {0x0000000000000010, 4, ObjectSize{}}, 18 | {0x0403020100000000, 0, ObjectSize{DataSize: 0x0201 * 8, PointerCount: 0x0403}}, 19 | {0xffffffff00000000, 0, ObjectSize{DataSize: 0xffff * 8, PointerCount: 0xffff}}, 20 | {0xfffffffffffffff8, -2, ObjectSize{DataSize: 0xffff * 8, PointerCount: 0xffff}}, 21 | {0x00000000fffffffc, -1, ObjectSize{}}, 22 | {0x04030201fffffffc, -1, ObjectSize{DataSize: 0x0201 * 8, PointerCount: 0x0403}}, 23 | {0xfffffffffffffffc, -1, ObjectSize{DataSize: 0xffff * 8, PointerCount: 0xffff}}, 24 | } 25 | for _, test := range tests { 26 | if typ := test.ptr.pointerType(); typ != structPointer { 27 | t.Errorf("rawPointer(%#016x).pointerType() = %d; want %d", uint64(test.ptr), typ, structPointer) 28 | } 29 | if offset := test.ptr.offset(); offset != test.offset { 30 | t.Errorf("rawPointer(%#016x).offset() = %d; want %d", uint64(test.ptr), offset, test.offset) 31 | } 32 | if size := test.ptr.structSize(); size != test.size { 33 | t.Errorf("rawPointer(%#016x).structSize() = %d; want %d", uint64(test.ptr), size, test.size) 34 | } 35 | } 36 | for _, test := range tests { 37 | ptr := rawStructPointer(test.offset, test.size) 38 | if ptr != test.ptr { 39 | t.Errorf("rawStructPointer(%d, %d) = rawPointer(%#016x); want rawPointer(%#016x)", test.offset, test.size, ptr, test.ptr) 40 | } 41 | } 42 | } 43 | 44 | func TestRawListPointer(t *testing.T) { 45 | tests := []struct { 46 | ptr rawPointer 47 | offset pointerOffset 48 | lt int 49 | n int32 50 | }{ 51 | {0x0000000000000001, 0, voidList, 0}, 52 | {0x0000000000000005, 1, voidList, 0}, 53 | {0x0000000000000009, 2, voidList, 0}, 54 | {0x000000000000000d, 3, voidList, 0}, 55 | {0x0000000100000001, 0, bit1List, 0}, 56 | {0x0000000200000001, 0, byte1List, 0}, 57 | {0x0000000300000001, 0, byte2List, 0}, 58 | {0x0000000400000001, 0, byte4List, 0}, 59 | {0x0000000500000001, 0, byte8List, 0}, 60 | {0x0000000600000001, 0, pointerList, 0}, 61 | {0x0000000700000001, 0, compositeList, 0}, 62 | {0x0000001500000009, 2, byte8List, 2}, 63 | {0x000000170000002d, 11, compositeList, 2}, 64 | {0x0000001700000035, 13, compositeList, 2}, 65 | {0xfffffff8fffffffd, -1, voidList, 0x1fffffff}, 66 | {0xfffffff9fffffffd, -1, bit1List, 0x1fffffff}, 67 | {0xfffffffafffffffd, -1, byte1List, 0x1fffffff}, 68 | {0xfffffffbfffffffd, -1, byte2List, 0x1fffffff}, 69 | {0xfffffffcfffffffd, -1, byte4List, 0x1fffffff}, 70 | {0xfffffffdfffffffd, -1, byte8List, 0x1fffffff}, 71 | {0xfffffffefffffffd, -1, pointerList, 0x1fffffff}, 72 | {0xfffffffffffffff9, -2, compositeList, 0x1fffffff}, 73 | {0xfffffffffffffffd, -1, compositeList, 0x1fffffff}, 74 | } 75 | for _, test := range tests { 76 | if typ := test.ptr.pointerType(); typ != listPointer { 77 | t.Errorf("rawPointer(%#016x).pointerType() = %d; want %d", uint64(test.ptr), typ, listPointer) 78 | } 79 | if offset := test.ptr.offset(); offset != test.offset { 80 | t.Errorf("rawPointer(%#016x).offset() = %d; want %d", uint64(test.ptr), offset, test.offset) 81 | } 82 | if lt := test.ptr.listType(); lt != test.lt { 83 | t.Errorf("rawPointer(%#016x).listType() = %d; want %d", uint64(test.ptr), lt, test.lt) 84 | } 85 | if n := test.ptr.numListElements(); n != test.n { 86 | t.Errorf("rawPointer(%#016x).numListElements() = %d; want %d", uint64(test.ptr), n, test.n) 87 | } 88 | } 89 | for _, test := range tests { 90 | ptr := rawListPointer(test.offset, test.lt, test.n) 91 | if ptr != test.ptr { 92 | t.Errorf("rawListPointer(%d, %d, %d) = rawPointer(%#016x); want rawPointer(%#016x)", test.offset, test.lt, test.n, ptr, test.ptr) 93 | } 94 | } 95 | } 96 | 97 | func TestRawOtherPointer(t *testing.T) { 98 | tests := []struct { 99 | ptr rawPointer 100 | typ uint32 101 | cap CapabilityID 102 | }{ 103 | {0x0000000000000003, 0, 0}, 104 | {0x0000000000000007, 1, 0}, 105 | {0x000000000000000b, 2, 0}, 106 | {0x000000000000000f, 3, 0}, 107 | {0xffffffff00000003, 0, 0xffffffff}, 108 | {0xfffffffffffffffb, 0x3ffffffe, 0xffffffff}, 109 | {0xffffffffffffffff, 0x3fffffff, 0xffffffff}, 110 | } 111 | for _, test := range tests { 112 | if typ := test.ptr.pointerType(); typ != otherPointer { 113 | t.Errorf("rawPointer(%#016x).pointerType() = %d; want %d", uint64(test.ptr), typ, otherPointer) 114 | } 115 | if typ := test.ptr.otherPointerType(); typ != test.typ { 116 | t.Errorf("rawPointer(%#016x).otherPointerType() = %d; want %d", uint64(test.ptr), typ, test.typ) 117 | } 118 | if cap := test.ptr.capabilityIndex(); cap != test.cap { 119 | t.Errorf("rawPointer(%#016x).capabilityIndex() = %d; want %d", uint64(test.ptr), cap, test.cap) 120 | } 121 | } 122 | for _, test := range tests { 123 | if test.typ != 0 { 124 | continue 125 | } 126 | ptr := rawInterfacePointer(test.cap) 127 | if ptr != test.ptr { 128 | t.Errorf("rawInterfacePointer(%d) = rawPointer(%#016x); want rawPointer(%#016x)", test.cap, ptr, test.ptr) 129 | } 130 | } 131 | } 132 | 133 | func TestRawFarPointer(t *testing.T) { 134 | tests := []struct { 135 | ptr rawPointer 136 | typ int 137 | addr Address 138 | seg SegmentID 139 | }{ 140 | {0x0000000000000002, farPointer, 0, 0}, 141 | {0x0000000000000006, doubleFarPointer, 0, 0}, 142 | {0x000000000000000a, farPointer, 8, 0}, 143 | {0x000000000000000e, doubleFarPointer, 8, 0}, 144 | {0xfffffffffffffffa, farPointer, 0xfffffff8, 0xffffffff}, 145 | {0xfffffffffffffffe, doubleFarPointer, 0xfffffff8, 0xffffffff}, 146 | } 147 | for _, test := range tests { 148 | if typ := test.ptr.pointerType(); typ != test.typ { 149 | t.Errorf("rawPointer(%#016x).pointerType() = %d; want %d", uint64(test.ptr), typ, test.typ) 150 | } 151 | if addr := test.ptr.farAddress(); addr != test.addr { 152 | t.Errorf("rawPointer(%#016x).farAddress() = %v; want %v", uint64(test.ptr), addr, test.addr) 153 | } 154 | if seg := test.ptr.farSegment(); seg != test.seg { 155 | t.Errorf("rawPointer(%#016x).farSegment() = %d; want %d", uint64(test.ptr), seg, test.seg) 156 | } 157 | } 158 | for _, test := range tests { 159 | if test.typ == farPointer { 160 | ptr := rawFarPointer(test.seg, test.addr) 161 | if ptr != test.ptr { 162 | t.Errorf("rawFarPointer(%d, %v) = rawPointer(%#016x); want rawPointer(%#016x)", test.seg, test.addr, ptr, test.ptr) 163 | } 164 | } else { 165 | ptr := rawDoubleFarPointer(test.seg, test.addr) 166 | if ptr != test.ptr { 167 | t.Errorf("rawDoubleFarPointer(%d, %v) = rawPointer(%#016x); want rawPointer(%#016x)", test.seg, test.addr, ptr, test.ptr) 168 | } 169 | } 170 | } 171 | } 172 | 173 | func TestRawPointerElementSize(t *testing.T) { 174 | tests := []struct { 175 | typ int 176 | sz ObjectSize 177 | }{ 178 | {voidList, ObjectSize{}}, 179 | {byte1List, ObjectSize{DataSize: 1}}, 180 | {byte2List, ObjectSize{DataSize: 2}}, 181 | {byte4List, ObjectSize{DataSize: 4}}, 182 | {byte8List, ObjectSize{DataSize: 8}}, 183 | {pointerList, ObjectSize{PointerCount: 1}}, 184 | } 185 | for _, test := range tests { 186 | rp := rawListPointer(0, test.typ, 0) 187 | if sz := rp.elementSize(); sz != test.sz { 188 | t.Errorf("rawListPointer(0, %d, 0).elementSize() = %v; want %v", test.typ, sz, test.sz) 189 | } 190 | } 191 | } 192 | 193 | func TestRawPointerTotalListSize(t *testing.T) { 194 | tests := []struct { 195 | typ int 196 | n int32 197 | sz Size 198 | }{ 199 | {voidList, 0, 0}, 200 | {voidList, 5, 0}, 201 | {bit1List, 0, 0}, 202 | {bit1List, 1, 1}, 203 | {bit1List, 2, 1}, 204 | {bit1List, 7, 1}, 205 | {bit1List, 8, 1}, 206 | {bit1List, 9, 2}, 207 | {compositeList, 0, 8}, 208 | {compositeList, 1, 16}, 209 | {compositeList, 2, 24}, 210 | {byte1List, 0, 0}, 211 | {byte1List, 1, 1}, 212 | {byte1List, 2, 2}, 213 | {byte2List, 0, 0}, 214 | {byte2List, 1, 2}, 215 | {byte2List, 2, 4}, 216 | {byte4List, 0, 0}, 217 | {byte4List, 1, 4}, 218 | {byte4List, 2, 8}, 219 | {byte8List, 0, 0}, 220 | {byte8List, 1, 8}, 221 | {byte8List, 2, 16}, 222 | {pointerList, 0, 0}, 223 | {pointerList, 1, 8}, 224 | {pointerList, 2, 16}, 225 | } 226 | for _, test := range tests { 227 | p := rawListPointer(0, test.typ, test.n) 228 | if sz := p.totalListSize(); sz != test.sz { 229 | t.Errorf("rawListPointer(0, %d, %d).totalListSize() = %d; want %d", test.typ, test.n, sz, test.sz) 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /rpc/answer.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | 7 | "golang.org/x/net/context" 8 | "zombiezen.com/go/capnproto" 9 | "zombiezen.com/go/capnproto/internal/fulfiller" 10 | "zombiezen.com/go/capnproto/internal/queue" 11 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 12 | ) 13 | 14 | // callQueueSize is the maximum number of calls that can be queued per answer or client. 15 | // TODO(light): make this a ConnOption 16 | const callQueueSize = 64 17 | 18 | type answerTable struct { 19 | tab map[answerID]*answer 20 | manager *manager 21 | out chan<- rpccapnp.Message 22 | returns chan<- *outgoingReturn 23 | queueCloses chan<- queueClientClose 24 | } 25 | 26 | func (at *answerTable) get(id answerID) *answer { 27 | var a *answer 28 | if at.tab != nil { 29 | a = at.tab[id] 30 | } 31 | return a 32 | } 33 | 34 | // insert creates a new question with the given ID, returning nil 35 | // if the ID is already in use. 36 | func (at *answerTable) insert(id answerID, cancel context.CancelFunc) *answer { 37 | if at.tab == nil { 38 | at.tab = make(map[answerID]*answer) 39 | } 40 | var a *answer 41 | if _, ok := at.tab[id]; !ok { 42 | a = &answer{ 43 | id: id, 44 | cancel: cancel, 45 | manager: at.manager, 46 | out: at.out, 47 | returns: at.returns, 48 | queueCloses: at.queueCloses, 49 | resolved: make(chan struct{}), 50 | queue: make([]pcall, 0, callQueueSize), 51 | } 52 | at.tab[id] = a 53 | } 54 | return a 55 | } 56 | 57 | func (at *answerTable) pop(id answerID) *answer { 58 | var a *answer 59 | if at.tab != nil { 60 | a = at.tab[id] 61 | delete(at.tab, id) 62 | } 63 | return a 64 | } 65 | 66 | type answer struct { 67 | id answerID 68 | cancel context.CancelFunc 69 | resultCaps []exportID 70 | manager *manager 71 | out chan<- rpccapnp.Message 72 | returns chan<- *outgoingReturn 73 | queueCloses chan<- queueClientClose 74 | resolved chan struct{} 75 | 76 | mu sync.RWMutex 77 | obj capnp.Pointer 78 | err error 79 | done bool 80 | queue []pcall 81 | } 82 | 83 | // fulfill is called to resolve an answer succesfully and returns a list 84 | // of return messages to send. 85 | // It must be called from the coordinate goroutine. 86 | func (a *answer) fulfill(msgs []rpccapnp.Message, obj capnp.Pointer, makeCapTable capTableMaker) []rpccapnp.Message { 87 | a.mu.Lock() 88 | defer a.mu.Unlock() 89 | if a.done { 90 | panic("answer.fulfill called more than once") 91 | } 92 | a.obj, a.done = obj, true 93 | // TODO(light): populate resultCaps 94 | 95 | retmsg := newReturnMessage(nil, a.id) 96 | ret, _ := retmsg.Return() 97 | payload, _ := ret.NewResults() 98 | payload.SetContent(obj) 99 | payloadTab, err := makeCapTable(ret.Segment()) 100 | if err != nil { 101 | // TODO(light): handle this more gracefully 102 | panic(err) 103 | } 104 | payload.SetCapTable(payloadTab) 105 | msgs = append(msgs, retmsg) 106 | 107 | queues, msgs := a.emptyQueue(msgs, obj) 108 | ctab := obj.Segment().Message().CapTable 109 | for capIdx, q := range queues { 110 | ctab[capIdx] = newQueueClient(a.manager, ctab[capIdx], q, a.out, a.queueCloses) 111 | } 112 | close(a.resolved) 113 | return msgs 114 | } 115 | 116 | // reject is called to resolve an answer with failure and returns a list 117 | // of return messages to send. 118 | // It must be called from the coordinate goroutine. 119 | func (a *answer) reject(msgs []rpccapnp.Message, err error) []rpccapnp.Message { 120 | if err == nil { 121 | panic("answer.reject called with nil") 122 | } 123 | a.mu.Lock() 124 | defer a.mu.Unlock() 125 | if a.done { 126 | panic("answer.reject called more than once") 127 | } 128 | a.err, a.done = err, true 129 | m := newReturnMessage(nil, a.id) 130 | mret, _ := m.Return() 131 | setReturnException(mret, err) 132 | msgs = append(msgs, m) 133 | for i := range a.queue { 134 | msgs = a.queue[i].a.reject(msgs, err) 135 | a.queue[i] = pcall{} 136 | } 137 | close(a.resolved) 138 | return msgs 139 | } 140 | 141 | // emptyQueue splits the queue by which capability it targets 142 | // and drops any invalid calls. Once this function returns, a.queue 143 | // will be nil. 144 | func (a *answer) emptyQueue(msgs []rpccapnp.Message, obj capnp.Pointer) (map[capnp.CapabilityID][]qcall, []rpccapnp.Message) { 145 | qs := make(map[capnp.CapabilityID][]qcall, len(a.queue)) 146 | for i, pc := range a.queue { 147 | c, err := capnp.Transform(obj, pc.transform) 148 | if err != nil { 149 | msgs = pc.a.reject(msgs, err) 150 | continue 151 | } 152 | ci := capnp.ToInterface(c) 153 | if !capnp.IsValid(ci) { 154 | msgs = pc.a.reject(msgs, capnp.ErrNullClient) 155 | continue 156 | } 157 | cn := ci.Capability() 158 | if qs[cn] == nil { 159 | qs[cn] = make([]qcall, 0, len(a.queue)-i) 160 | } 161 | qs[cn] = append(qs[cn], pc.qcall) 162 | } 163 | a.queue = nil 164 | return qs, msgs 165 | } 166 | 167 | func (a *answer) peek() (obj capnp.Pointer, err error, ok bool) { 168 | a.mu.RLock() 169 | obj, err, ok = a.obj, a.err, a.done 170 | a.mu.RUnlock() 171 | return 172 | } 173 | 174 | // queueCall is called from the coordinate goroutine to add a call to 175 | // the queue. 176 | func (a *answer) queueCall(result *answer, transform []capnp.PipelineOp, call *capnp.Call) error { 177 | a.mu.Lock() 178 | defer a.mu.Unlock() 179 | if a.done { 180 | panic("answer.queueCall called on resolved answer") 181 | } 182 | if len(a.queue) == cap(a.queue) { 183 | return errQueueFull 184 | } 185 | cc, err := call.Copy(nil) 186 | if err != nil { 187 | return err 188 | } 189 | a.queue = append(a.queue, pcall{ 190 | transform: transform, 191 | qcall: qcall{ 192 | a: result, 193 | call: cc, 194 | }, 195 | }) 196 | return nil 197 | } 198 | 199 | // queueDisembargo is called from the coordinate goroutine to add a 200 | // disembargo message to the queue. 201 | func (a *answer) queueDisembargo(transform []capnp.PipelineOp, id embargoID, target rpccapnp.MessageTarget) (queued bool, err error) { 202 | a.mu.Lock() 203 | defer a.mu.Unlock() 204 | if !a.done { 205 | return false, errDisembargoOngoingAnswer 206 | } 207 | if a.err != nil { 208 | return false, errDisembargoNonImport 209 | } 210 | targetPtr, err := capnp.Transform(a.obj, transform) 211 | if err != nil { 212 | return false, err 213 | } 214 | client := capnp.ToInterface(targetPtr).Client() 215 | qc, ok := client.(*queueClient) 216 | if !ok { 217 | // No need to embargo, disembargo immediately. 218 | return false, nil 219 | } 220 | if ic, ok := extractRPCClient(qc.client).(*importClient); !(ok && a.manager == ic.manager) { 221 | return false, errDisembargoNonImport 222 | } 223 | qc.mu.Lock() 224 | if !qc.isPassthrough() { 225 | err = qc.pushEmbargo(id, target) 226 | if err == nil { 227 | queued = true 228 | } 229 | } 230 | qc.mu.Unlock() 231 | return queued, err 232 | } 233 | 234 | func (a *answer) pipelineClient(transform []capnp.PipelineOp) capnp.Client { 235 | return &localAnswerClient{a: a, transform: transform} 236 | } 237 | 238 | // joinAnswer resolves an RPC answer by waiting on a generic answer. 239 | // It waits until the generic answer is finished, so it should be run 240 | // in its own goroutine. 241 | func joinAnswer(a *answer, ca capnp.Answer) { 242 | s, err := ca.Struct() 243 | r := &outgoingReturn{ 244 | a: a, 245 | obj: capnp.Pointer(s), 246 | err: err, 247 | } 248 | select { 249 | case a.returns <- r: 250 | case <-a.manager.finish: 251 | } 252 | } 253 | 254 | // joinFulfiller resolves a fulfiller by waiting on a generic answer. 255 | // It waits until the generic answer is finished, so it should be run 256 | // in its own goroutine. 257 | func joinFulfiller(f *fulfiller.Fulfiller, ca capnp.Answer) { 258 | s, err := ca.Struct() 259 | if err != nil { 260 | f.Reject(err) 261 | } else { 262 | f.Fulfill(s) 263 | } 264 | } 265 | 266 | // outgoingReturn is a message sent to the coordinate goroutine to 267 | // indicate that a call started by an answer has completed. A simple 268 | // message is insufficient, since the connection needs to populate the 269 | // return message's capability table. 270 | type outgoingReturn struct { 271 | a *answer 272 | obj capnp.Pointer 273 | err error 274 | } 275 | 276 | type queueClient struct { 277 | manager *manager 278 | client capnp.Client 279 | out chan<- rpccapnp.Message 280 | closes chan<- queueClientClose 281 | 282 | mu sync.RWMutex 283 | q queue.Queue 284 | } 285 | 286 | func newQueueClient(m *manager, client capnp.Client, queue []qcall, out chan<- rpccapnp.Message, closes chan<- queueClientClose) *queueClient { 287 | qc := &queueClient{ 288 | manager: m, 289 | client: client, 290 | out: out, 291 | closes: closes, 292 | } 293 | qq := make(qcallList, callQueueSize) 294 | n := copy(qq, queue) 295 | qc.q.Init(qq, n) 296 | go qc.flushQueue() 297 | return qc 298 | } 299 | 300 | func (qc *queueClient) pushCall(cl *capnp.Call) capnp.Answer { 301 | f := new(fulfiller.Fulfiller) 302 | cl, err := cl.Copy(nil) 303 | if err != nil { 304 | return capnp.ErrorAnswer(err) 305 | } 306 | if ok := qc.q.Push(qcall{call: cl, f: f}); !ok { 307 | return capnp.ErrorAnswer(errQueueFull) 308 | } 309 | return f 310 | } 311 | 312 | func (qc *queueClient) pushEmbargo(id embargoID, tgt rpccapnp.MessageTarget) error { 313 | ok := qc.q.Push(qcall{embargoID: id, embargoTarget: tgt}) 314 | if !ok { 315 | return errQueueFull 316 | } 317 | return nil 318 | } 319 | 320 | func (qc *queueClient) peek() qcall { 321 | if qc.q.Len() == 0 { 322 | return qcall{} 323 | } 324 | return qc.q.Peek().(qcall) 325 | } 326 | 327 | func (qc *queueClient) pop() qcall { 328 | if qc.q.Len() == 0 { 329 | return qcall{} 330 | } 331 | return qc.q.Pop().(qcall) 332 | } 333 | 334 | // flushQueue is run in its own goroutine. 335 | func (qc *queueClient) flushQueue() { 336 | qc.mu.RLock() 337 | c := qc.peek() 338 | qc.mu.RUnlock() 339 | for c.which() != qcallInvalid { 340 | qc.handle(&c) 341 | 342 | qc.mu.Lock() 343 | qc.pop() 344 | c = qc.peek() 345 | qc.mu.Unlock() 346 | } 347 | } 348 | 349 | func (qc *queueClient) handle(c *qcall) { 350 | switch c.which() { 351 | case qcallRemoteCall: 352 | answer := qc.client.Call(c.call) 353 | go joinAnswer(c.a, answer) 354 | case qcallLocalCall: 355 | answer := qc.client.Call(c.call) 356 | go joinFulfiller(c.f, answer) 357 | case qcallDisembargo: 358 | msg := newDisembargoMessage(nil, rpccapnp.Disembargo_context_Which_receiverLoopback, c.embargoID) 359 | d, _ := msg.Disembargo() 360 | d.SetTarget(c.embargoTarget) 361 | sendMessage(qc.manager, qc.out, msg) 362 | } 363 | } 364 | 365 | func (qc *queueClient) isPassthrough() bool { 366 | return qc.q.Len() == 0 367 | } 368 | 369 | func (qc *queueClient) Call(cl *capnp.Call) capnp.Answer { 370 | // Fast path: queue is flushed. 371 | qc.mu.RLock() 372 | ok := qc.isPassthrough() 373 | qc.mu.RUnlock() 374 | if ok { 375 | return qc.client.Call(cl) 376 | } 377 | 378 | // Add to queue. 379 | qc.mu.Lock() 380 | // Since we released the lock, check that the queue hasn't been flushed. 381 | if qc.isPassthrough() { 382 | qc.mu.Unlock() 383 | return qc.client.Call(cl) 384 | } 385 | ans := qc.pushCall(cl) 386 | qc.mu.Unlock() 387 | return ans 388 | } 389 | 390 | func (qc *queueClient) WrappedClient() capnp.Client { 391 | qc.mu.RLock() 392 | ok := qc.isPassthrough() 393 | qc.mu.RUnlock() 394 | if !ok { 395 | return nil 396 | } 397 | return qc.client 398 | } 399 | 400 | func (qc *queueClient) Close() error { 401 | done := make(chan struct{}) 402 | select { 403 | case qc.closes <- queueClientClose{qc, done}: 404 | case <-qc.manager.finish: 405 | return qc.manager.err() 406 | } 407 | select { 408 | case <-done: 409 | case <-qc.manager.finish: 410 | return qc.manager.err() 411 | } 412 | return qc.client.Close() 413 | } 414 | 415 | // rejectQueue is called from the coordinate goroutine to close out a queueClient. 416 | func (qc *queueClient) rejectQueue(msgs []rpccapnp.Message) []rpccapnp.Message { 417 | qc.mu.Lock() 418 | for { 419 | c := qc.pop() 420 | if w := c.which(); w == qcallRemoteCall { 421 | msgs = c.a.reject(msgs, errQueueCallCancel) 422 | } else if w == qcallLocalCall { 423 | c.f.Reject(errQueueCallCancel) 424 | } else if w == qcallDisembargo { 425 | m := newDisembargoMessage(nil, rpccapnp.Disembargo_context_Which_receiverLoopback, c.embargoID) 426 | d, _ := m.Disembargo() 427 | d.SetTarget(c.embargoTarget) 428 | msgs = append(msgs, m) 429 | } else { 430 | break 431 | } 432 | } 433 | qc.mu.Unlock() 434 | return msgs 435 | } 436 | 437 | // queueClientClose is a message sent to the coordinate goroutine to 438 | // handle rejecting a queue. 439 | type queueClientClose struct { 440 | qc *queueClient 441 | done chan<- struct{} 442 | } 443 | 444 | // pcall is a queued pipeline call. 445 | type pcall struct { 446 | transform []capnp.PipelineOp 447 | qcall 448 | } 449 | 450 | // qcall is a queued call. 451 | type qcall struct { 452 | // Calls 453 | a *answer // non-nil if remote call 454 | f *fulfiller.Fulfiller // non-nil if local call 455 | call *capnp.Call 456 | 457 | // Disembargo 458 | embargoID embargoID 459 | embargoTarget rpccapnp.MessageTarget 460 | } 461 | 462 | // Queued call types. 463 | const ( 464 | qcallInvalid = iota 465 | qcallRemoteCall 466 | qcallLocalCall 467 | qcallDisembargo 468 | ) 469 | 470 | func (c *qcall) which() int { 471 | if c.a != nil { 472 | return qcallRemoteCall 473 | } else if c.f != nil { 474 | return qcallLocalCall 475 | } else if capnp.IsValid(c.embargoTarget) { 476 | return qcallDisembargo 477 | } else { 478 | return qcallInvalid 479 | } 480 | } 481 | 482 | type qcallList []qcall 483 | 484 | func (ql qcallList) Len() int { 485 | return len(ql) 486 | } 487 | 488 | func (ql qcallList) At(i int) interface{} { 489 | return ql[i] 490 | } 491 | 492 | func (ql qcallList) Set(i int, x interface{}) { 493 | if x == nil { 494 | ql[i] = qcall{} 495 | } else { 496 | ql[i] = x.(qcall) 497 | } 498 | } 499 | 500 | // A localAnswerClient is used to provide a pipelined client of an answer. 501 | type localAnswerClient struct { 502 | a *answer 503 | transform []capnp.PipelineOp 504 | } 505 | 506 | func (lac *localAnswerClient) Call(call *capnp.Call) capnp.Answer { 507 | lac.a.mu.Lock() 508 | if lac.a.done { 509 | obj, err := lac.a.obj, lac.a.err 510 | lac.a.mu.Unlock() 511 | return clientFromResolution(lac.transform, obj, err).Call(call) 512 | } 513 | defer lac.a.mu.Unlock() 514 | if len(lac.a.queue) == cap(lac.a.queue) { 515 | return capnp.ErrorAnswer(errQueueFull) 516 | } 517 | f := new(fulfiller.Fulfiller) 518 | cc, err := call.Copy(nil) 519 | if err != nil { 520 | return capnp.ErrorAnswer(err) 521 | } 522 | lac.a.queue = append(lac.a.queue, pcall{ 523 | transform: lac.transform, 524 | qcall: qcall{ 525 | f: f, 526 | call: cc, 527 | }, 528 | }) 529 | return f 530 | } 531 | 532 | func (lac *localAnswerClient) WrappedClient() capnp.Client { 533 | obj, err, ok := lac.a.peek() 534 | if !ok { 535 | return nil 536 | } 537 | return clientFromResolution(lac.transform, obj, err) 538 | } 539 | 540 | func (lac *localAnswerClient) Close() error { 541 | obj, err, ok := lac.a.peek() 542 | if !ok { 543 | return nil 544 | } 545 | client := clientFromResolution(lac.transform, obj, err) 546 | return client.Close() 547 | } 548 | 549 | // A capTableMaker converts the clients in a segment's message into capability descriptors. 550 | type capTableMaker func(*capnp.Segment) (rpccapnp.CapDescriptor_List, error) 551 | 552 | var ( 553 | errQueueFull = errors.New("rpc: pipeline queue full") 554 | errQueueCallCancel = errors.New("rpc: queued call canceled") 555 | 556 | errDisembargoOngoingAnswer = errors.New("rpc: disembargo attempted on in-progress answer") 557 | errDisembargoNonImport = errors.New("rpc: disembargo attempted on non-import capability") 558 | errDisembargoMissingAnswer = errors.New("rpc: disembargo attempted on missing answer (finished too early?)") 559 | ) 560 | -------------------------------------------------------------------------------- /rpc/cancel_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/net/context" 7 | "zombiezen.com/go/capnproto/rpc" 8 | "zombiezen.com/go/capnproto/rpc/internal/logtransport" 9 | "zombiezen.com/go/capnproto/rpc/internal/pipetransport" 10 | "zombiezen.com/go/capnproto/rpc/internal/testcapnp" 11 | "zombiezen.com/go/capnproto/server" 12 | ) 13 | 14 | func TestCancel(t *testing.T) { 15 | ctx, cancel := context.WithCancel(context.Background()) 16 | defer cancel() 17 | p, q := pipetransport.New() 18 | if *logMessages { 19 | p = logtransport.New(nil, p) 20 | } 21 | c := rpc.NewConn(p) 22 | notify := make(chan struct{}) 23 | hanger := testcapnp.Hanger_ServerToClient(Hanger{notify: notify}) 24 | d := rpc.NewConn(q, rpc.MainInterface(hanger.Client)) 25 | defer d.Wait() 26 | defer c.Close() 27 | client := testcapnp.Hanger{Client: c.Bootstrap(ctx)} 28 | 29 | subctx, subcancel := context.WithCancel(ctx) 30 | promise := client.Hang(subctx, func(r testcapnp.Hanger_hang_Params) error { return nil }) 31 | <-notify 32 | subcancel() 33 | _, err := promise.Struct() 34 | <-notify // test will deadlock if cancel not delivered 35 | 36 | if err != context.Canceled { 37 | t.Errorf("promise.Get() error: %v; want %v", err, context.Canceled) 38 | } 39 | } 40 | 41 | type Hanger struct { 42 | notify chan struct{} 43 | } 44 | 45 | func (h Hanger) Hang(call testcapnp.Hanger_hang) error { 46 | server.Ack(call.Options) 47 | h.notify <- struct{}{} 48 | <-call.Ctx.Done() 49 | close(h.notify) 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /rpc/embargo_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/net/context" 7 | "zombiezen.com/go/capnproto" 8 | "zombiezen.com/go/capnproto/rpc" 9 | "zombiezen.com/go/capnproto/rpc/internal/logtransport" 10 | "zombiezen.com/go/capnproto/rpc/internal/pipetransport" 11 | "zombiezen.com/go/capnproto/rpc/internal/testcapnp" 12 | ) 13 | 14 | func TestEmbargo(t *testing.T) { 15 | ctx, cancel := context.WithCancel(context.Background()) 16 | defer cancel() 17 | p, q := pipetransport.New() 18 | if *logMessages { 19 | p = logtransport.New(nil, p) 20 | } 21 | c := rpc.NewConn(p) 22 | echoSrv := testcapnp.Echoer_ServerToClient(new(Echoer)) 23 | d := rpc.NewConn(q, rpc.MainInterface(echoSrv.Client)) 24 | defer d.Wait() 25 | defer c.Close() 26 | client := testcapnp.Echoer{Client: c.Bootstrap(ctx)} 27 | localCap := testcapnp.CallOrder_ServerToClient(new(CallOrder)) 28 | 29 | earlyCall := callseq(ctx, client.Client, 0) 30 | echo := client.Echo(ctx, func(p testcapnp.Echoer_echo_Params) error { 31 | return p.SetCap(localCap) 32 | }) 33 | pipeline := echo.Cap() 34 | call0 := callseq(ctx, pipeline.Client, 0) 35 | call1 := callseq(ctx, pipeline.Client, 1) 36 | _, err := earlyCall.Struct() 37 | if err != nil { 38 | t.Errorf("earlyCall error: %v", err) 39 | } 40 | call2 := callseq(ctx, pipeline.Client, 2) 41 | _, err = echo.Struct() 42 | if err != nil { 43 | t.Errorf("echo.Get() error: %v", err) 44 | } 45 | call3 := callseq(ctx, pipeline.Client, 3) 46 | call4 := callseq(ctx, pipeline.Client, 4) 47 | call5 := callseq(ctx, pipeline.Client, 5) 48 | 49 | check := func(promise testcapnp.CallOrder_getCallSequence_Results_Promise, n uint32) { 50 | r, err := promise.Struct() 51 | if err != nil { 52 | t.Errorf("call%d error: %v", n, err) 53 | } 54 | if r.N() != n { 55 | t.Errorf("call%d = %d; want %d", n, r.N(), n) 56 | } 57 | } 58 | check(call0, 0) 59 | check(call1, 1) 60 | check(call2, 2) 61 | check(call3, 3) 62 | check(call4, 4) 63 | check(call5, 5) 64 | } 65 | 66 | func callseq(c context.Context, client capnp.Client, n uint32) testcapnp.CallOrder_getCallSequence_Results_Promise { 67 | return testcapnp.CallOrder{Client: client}.GetCallSequence(c, func(p testcapnp.CallOrder_getCallSequence_Params) error { 68 | p.SetExpected(n) 69 | return nil 70 | }) 71 | } 72 | 73 | type CallOrder struct { 74 | n uint32 75 | } 76 | 77 | func (co *CallOrder) GetCallSequence(call testcapnp.CallOrder_getCallSequence) error { 78 | call.Results.SetN(co.n) 79 | co.n++ 80 | return nil 81 | } 82 | 83 | type Echoer struct { 84 | CallOrder 85 | } 86 | 87 | func (*Echoer) Echo(call testcapnp.Echoer_echo) error { 88 | call.Results.SetCap(call.Params.Cap()) 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /rpc/errors.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "zombiezen.com/go/capnproto" 8 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 9 | ) 10 | 11 | // An Exception is a Cap'n Proto RPC error. 12 | type Exception struct { 13 | rpccapnp.Exception 14 | } 15 | 16 | // Error returns the exception's reason. 17 | func (e Exception) Error() string { 18 | r, err := e.Reason() 19 | if err != nil { 20 | return "rpc exception" 21 | } 22 | return "rpc exception: " + r 23 | } 24 | 25 | // An Abort is a hang-up by a remote vat. 26 | type Abort Exception 27 | 28 | // Error returns the exception's reason. 29 | func (a Abort) Error() string { 30 | r, err := a.Reason() 31 | if err != nil { 32 | return "rpc: aborted by remote" 33 | } 34 | return "rpc: aborted by remote: " + r 35 | } 36 | 37 | // toException sets fields on exc to match err. 38 | func toException(exc rpccapnp.Exception, err error) { 39 | if ee, ok := err.(Exception); ok { 40 | // TODO(light): copy struct 41 | r, err := ee.Reason() 42 | if err == nil { 43 | exc.SetReason(r) 44 | } 45 | exc.SetType(ee.Type()) 46 | return 47 | } 48 | 49 | exc.SetReason(err.Error()) 50 | exc.SetType(rpccapnp.Exception_Type_failed) 51 | } 52 | 53 | // Errors 54 | var ( 55 | ErrConnClosed = errors.New("rpc: connection closed") 56 | ) 57 | 58 | // Internal errors 59 | var ( 60 | errQuestionReused = errors.New("rpc: question ID reused") 61 | errNoMainInterface = errors.New("rpc: no bootstrap interface") 62 | errBadTarget = errors.New("rpc: target not found") 63 | errShutdown = errors.New("rpc: shutdown") 64 | errCallCanceled = errors.New("rpc: call canceled") 65 | errUnimplemented = errors.New("rpc: remote used unimplemented protocol feature") 66 | ) 67 | 68 | type bootstrapError struct { 69 | err error 70 | } 71 | 72 | func (e bootstrapError) Error() string { 73 | return "rpc bootstrap:" + e.err.Error() 74 | } 75 | 76 | type questionError struct { 77 | id questionID 78 | method *capnp.Method // nil if this is bootstrap 79 | err error 80 | } 81 | 82 | func (qe *questionError) Error() string { 83 | if qe.method == nil { 84 | return fmt.Sprintf("bootstrap call id=%d: %v", qe.id, qe.err) 85 | } 86 | return fmt.Sprintf("%v call id=%d: %v", qe.method, qe.id, qe.err) 87 | } 88 | -------------------------------------------------------------------------------- /rpc/example_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "golang.org/x/net/context" 8 | "zombiezen.com/go/capnproto/rpc" 9 | "zombiezen.com/go/capnproto/rpc/internal/testcapnp" 10 | "zombiezen.com/go/capnproto/server" 11 | ) 12 | 13 | func Example() { 14 | // Create an in-memory transport. In a real application, you would probably 15 | // use a net.TCPConn (for RPC) or an os.Pipe (for IPC). 16 | p1, p2 := net.Pipe() 17 | t1, t2 := rpc.StreamTransport(p1), rpc.StreamTransport(p2) 18 | 19 | // Server-side 20 | srv := testcapnp.Adder_ServerToClient(AdderServer{}) 21 | serverConn := rpc.NewConn(t1, rpc.MainInterface(srv.Client)) 22 | defer serverConn.Wait() 23 | 24 | // Client-side 25 | ctx := context.Background() 26 | clientConn := rpc.NewConn(t2) 27 | defer clientConn.Close() 28 | adderClient := testcapnp.Adder{Client: clientConn.Bootstrap(ctx)} 29 | // Every client call returns a promise. You can make multiple calls 30 | // concurrently. 31 | call1 := adderClient.Add(ctx, func(p testcapnp.Adder_add_Params) error { 32 | p.SetA(5) 33 | p.SetB(2) 34 | return nil 35 | }) 36 | call2 := adderClient.Add(ctx, func(p testcapnp.Adder_add_Params) error { 37 | p.SetA(10) 38 | p.SetB(20) 39 | return nil 40 | }) 41 | // Calling Struct() on a promise waits until it returns. 42 | result1, err := call1.Struct() 43 | if err != nil { 44 | fmt.Println("Add #1 failed:", err) 45 | return 46 | } 47 | result2, err := call2.Struct() 48 | if err != nil { 49 | fmt.Println("Add #2 failed:", err) 50 | return 51 | } 52 | 53 | fmt.Println("Results:", result1.Result(), result2.Result()) 54 | // Output: 55 | // Results: 7 30 56 | } 57 | 58 | // An AdderServer is a local implementation of the Adder interface. 59 | type AdderServer struct{} 60 | 61 | // Add implements a method 62 | func (AdderServer) Add(call testcapnp.Adder_add) error { 63 | // Acknowledging the call allows other calls to be made (it returns the Answer 64 | // to the caller). 65 | server.Ack(call.Options) 66 | 67 | // Parameters are accessed with call.Params. 68 | a := call.Params.A() 69 | b := call.Params.B() 70 | 71 | // A result struct is allocated for you at call.Results. 72 | call.Results.SetResult(a + b) 73 | 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /rpc/internal/logtransport/logtransport.go: -------------------------------------------------------------------------------- 1 | // Package logtransport provides a transport that logs all of its messages. 2 | package logtransport 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "log" 9 | 10 | "golang.org/x/net/context" 11 | "zombiezen.com/go/capnproto/rpc" 12 | "zombiezen.com/go/capnproto/rpc/internal/logutil" 13 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 14 | ) 15 | 16 | type transport struct { 17 | rpc.Transport 18 | l *log.Logger 19 | sendBuf bytes.Buffer 20 | recvBuf bytes.Buffer 21 | } 22 | 23 | // New creates a new logger that proxies messages to and from t and 24 | // logs them to l. If l is nil, then the log package's default 25 | // logger is used. 26 | func New(l *log.Logger, t rpc.Transport) rpc.Transport { 27 | return &transport{Transport: t, l: l} 28 | } 29 | 30 | func (t *transport) SendMessage(ctx context.Context, msg rpccapnp.Message) error { 31 | t.sendBuf.Reset() 32 | t.sendBuf.WriteString("<- ") 33 | formatMsg(&t.sendBuf, msg) 34 | logutil.Print(t.l, t.sendBuf.String()) 35 | return t.Transport.SendMessage(ctx, msg) 36 | } 37 | 38 | func (t *transport) RecvMessage(ctx context.Context) (rpccapnp.Message, error) { 39 | msg, err := t.Transport.RecvMessage(ctx) 40 | if err != nil { 41 | return msg, err 42 | } 43 | t.recvBuf.Reset() 44 | t.recvBuf.WriteString("-> ") 45 | formatMsg(&t.recvBuf, msg) 46 | logutil.Print(t.l, t.recvBuf.String()) 47 | return msg, nil 48 | } 49 | 50 | func formatMsg(w io.Writer, m rpccapnp.Message) { 51 | switch m.Which() { 52 | case rpccapnp.Message_Which_unimplemented: 53 | fmt.Fprint(w, "unimplemented") 54 | case rpccapnp.Message_Which_abort: 55 | mabort, _ := m.Abort() 56 | reason, _ := mabort.Reason() 57 | fmt.Fprintf(w, "abort type=%v: %s", mabort.Type(), reason) 58 | case rpccapnp.Message_Which_bootstrap: 59 | mboot, _ := m.Bootstrap() 60 | fmt.Fprintf(w, "bootstrap id=%d", mboot.QuestionId()) 61 | case rpccapnp.Message_Which_call: 62 | c, _ := m.Call() 63 | fmt.Fprintf(w, "call id=%d target=<", c.QuestionId()) 64 | tgt, _ := c.Target() 65 | formatMessageTarget(w, tgt) 66 | fmt.Fprintf(w, "> @%#x/@%d", c.InterfaceId(), c.MethodId()) 67 | case rpccapnp.Message_Which_return: 68 | r, _ := m.Return() 69 | fmt.Fprintf(w, "return id=%d", r.AnswerId()) 70 | if r.ReleaseParamCaps() { 71 | fmt.Fprint(w, " releaseParamCaps") 72 | } 73 | switch r.Which() { 74 | case rpccapnp.Return_Which_results: 75 | case rpccapnp.Return_Which_exception: 76 | exc, _ := r.Exception() 77 | reason, _ := exc.Reason() 78 | fmt.Fprintf(w, ", exception type=%v: %s", exc.Type(), reason) 79 | case rpccapnp.Return_Which_canceled: 80 | fmt.Fprint(w, ", canceled") 81 | case rpccapnp.Return_Which_resultsSentElsewhere: 82 | fmt.Fprint(w, ", results sent elsewhere") 83 | case rpccapnp.Return_Which_takeFromOtherQuestion: 84 | fmt.Fprint(w, ", results sent elsewhere") 85 | case rpccapnp.Return_Which_acceptFromThirdParty: 86 | fmt.Fprint(w, ", accept from third party") 87 | default: 88 | fmt.Fprintf(w, ", UNKNOWN RESULT which=%v", r.Which()) 89 | } 90 | case rpccapnp.Message_Which_finish: 91 | fin, _ := m.Finish() 92 | fmt.Fprintf(w, "finish id=%d", fin.QuestionId()) 93 | if fin.ReleaseResultCaps() { 94 | fmt.Fprint(w, " releaseResultCaps") 95 | } 96 | case rpccapnp.Message_Which_resolve: 97 | r, _ := m.Resolve() 98 | fmt.Fprintf(w, "resolve id=%d ", r.PromiseId()) 99 | switch r.Which() { 100 | case rpccapnp.Resolve_Which_cap: 101 | fmt.Fprint(w, "capability=") 102 | c, _ := r.Cap() 103 | formatCapDescriptor(w, c) 104 | case rpccapnp.Resolve_Which_exception: 105 | exc, _ := r.Exception() 106 | reason, _ := exc.Reason() 107 | fmt.Fprintf(w, "exception type=%v: %s", exc.Type(), reason) 108 | default: 109 | fmt.Fprintf(w, "UNKNOWN RESOLUTION which=%v", r.Which()) 110 | } 111 | case rpccapnp.Message_Which_release: 112 | rel, _ := m.Release() 113 | fmt.Fprintf(w, "release id=%d by %d", rel.Id(), rel.ReferenceCount()) 114 | case rpccapnp.Message_Which_disembargo: 115 | de, _ := m.Disembargo() 116 | tgt, _ := de.Target() 117 | fmt.Fprint(w, "disembargo <") 118 | formatMessageTarget(w, tgt) 119 | fmt.Fprint(w, "> ") 120 | dc := de.Context() 121 | switch dc.Which() { 122 | case rpccapnp.Disembargo_context_Which_senderLoopback: 123 | fmt.Fprintf(w, "sender loopback id=%d", dc.SenderLoopback()) 124 | case rpccapnp.Disembargo_context_Which_receiverLoopback: 125 | fmt.Fprintf(w, "receiver loopback id=%d", dc.ReceiverLoopback()) 126 | case rpccapnp.Disembargo_context_Which_accept: 127 | fmt.Fprint(w, "accept") 128 | case rpccapnp.Disembargo_context_Which_provide: 129 | fmt.Fprintf(w, "provide id=%d", dc.Provide()) 130 | default: 131 | fmt.Fprintf(w, "UNKNOWN CONTEXT which=%v", dc.Which()) 132 | } 133 | case rpccapnp.Message_Which_obsoleteSave: 134 | fmt.Fprint(w, "save") 135 | case rpccapnp.Message_Which_obsoleteDelete: 136 | fmt.Fprint(w, "delete") 137 | case rpccapnp.Message_Which_provide: 138 | prov, _ := m.Provide() 139 | tgt, _ := prov.Target() 140 | fmt.Fprintf(w, "provide id=%d <", prov.QuestionId()) 141 | formatMessageTarget(w, tgt) 142 | fmt.Fprint(w, ">") 143 | case rpccapnp.Message_Which_accept: 144 | acc, _ := m.Accept() 145 | fmt.Fprintf(w, "accept id=%d", acc.QuestionId()) 146 | if acc.Embargo() { 147 | fmt.Fprint(w, " with embargo") 148 | } 149 | case rpccapnp.Message_Which_join: 150 | join, _ := m.Join() 151 | tgt, _ := join.Target() 152 | fmt.Fprintf(w, "join id=%d <", join.QuestionId()) 153 | formatMessageTarget(w, tgt) 154 | fmt.Fprint(w, ">") 155 | default: 156 | fmt.Fprintf(w, "UNKNOWN MESSAGE which=%v", m.Which()) 157 | } 158 | } 159 | 160 | func formatMessageTarget(w io.Writer, t rpccapnp.MessageTarget) { 161 | switch t.Which() { 162 | case rpccapnp.MessageTarget_Which_importedCap: 163 | fmt.Fprintf(w, "import %d", t.ImportedCap()) 164 | case rpccapnp.MessageTarget_Which_promisedAnswer: 165 | fmt.Fprint(w, "promise ") 166 | pa, _ := t.PromisedAnswer() 167 | formatPromisedAnswer(w, pa) 168 | default: 169 | fmt.Fprintf(w, "UNKNOWN TARGET which=%v", t.Which()) 170 | } 171 | } 172 | 173 | func formatPromisedAnswer(w io.Writer, a rpccapnp.PromisedAnswer) { 174 | fmt.Fprintf(w, "(question %d)", a.QuestionId()) 175 | trans, _ := a.Transform() 176 | for i := 0; i < trans.Len(); i++ { 177 | t := trans.At(i) 178 | switch t.Which() { 179 | case rpccapnp.PromisedAnswer_Op_Which_noop: 180 | case rpccapnp.PromisedAnswer_Op_Which_getPointerField: 181 | fmt.Fprintf(w, ".getPointerField(%d)", t.GetPointerField()) 182 | default: 183 | fmt.Fprintf(w, ".UNKNOWN(%v)", t.Which()) 184 | } 185 | } 186 | } 187 | 188 | func formatCapDescriptor(w io.Writer, c rpccapnp.CapDescriptor) { 189 | switch c.Which() { 190 | case rpccapnp.CapDescriptor_Which_none: 191 | fmt.Fprint(w, "none") 192 | case rpccapnp.CapDescriptor_Which_senderHosted: 193 | fmt.Fprintf(w, "sender-hosted %d", c.SenderHosted()) 194 | case rpccapnp.CapDescriptor_Which_senderPromise: 195 | fmt.Fprintf(w, "sender promise %d", c.SenderPromise()) 196 | case rpccapnp.CapDescriptor_Which_receiverHosted: 197 | fmt.Fprintf(w, "receiver-hosted %d", c.ReceiverHosted()) 198 | case rpccapnp.CapDescriptor_Which_receiverAnswer: 199 | ans, _ := c.ReceiverAnswer() 200 | fmt.Fprint(w, "receiver answer ") 201 | formatPromisedAnswer(w, ans) 202 | case rpccapnp.CapDescriptor_Which_thirdPartyHosted: 203 | fmt.Fprint(w, "third-party hosted") 204 | default: 205 | fmt.Fprintf(w, "UNKNOWN CAPABILITY which=%v", c.Which()) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /rpc/internal/logutil/logutil.go: -------------------------------------------------------------------------------- 1 | // Package logutil provides functions that can print to a logger. 2 | // Any function in this package that takes in a *log.Logger can be 3 | // passed nil to use the log package's default logger. 4 | package logutil 5 | 6 | import "log" 7 | 8 | // Print calls Print on a logger or the default logger. 9 | // Arguments are handled in the manner of fmt.Print. 10 | func Print(l *log.Logger, v ...interface{}) { 11 | if l == nil { 12 | log.Print(v...) 13 | } else { 14 | l.Print(v...) 15 | } 16 | } 17 | 18 | // Printf calls Printf on a logger or the default logger. 19 | // Arguments are handled in the manner of fmt.Printf. 20 | func Printf(l *log.Logger, format string, v ...interface{}) { 21 | if l == nil { 22 | log.Printf(format, v...) 23 | } else { 24 | l.Printf(format, v...) 25 | } 26 | } 27 | 28 | // Println calls Println on a logger or the default logger. 29 | // Arguments are handled in the manner of fmt.Println. 30 | func Println(l *log.Logger, v ...interface{}) { 31 | if l == nil { 32 | log.Println(v...) 33 | } else { 34 | l.Println(v...) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /rpc/internal/pipetransport/pipetransport.go: -------------------------------------------------------------------------------- 1 | // Package pipetransport provides in-memory implementations of rpc.Transport for testing. 2 | package pipetransport 3 | 4 | import ( 5 | "bytes" 6 | "errors" 7 | "sync" 8 | 9 | "golang.org/x/net/context" 10 | "zombiezen.com/go/capnproto" 11 | "zombiezen.com/go/capnproto/rpc" 12 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 13 | ) 14 | 15 | type pipeTransport struct { 16 | r <-chan rpccapnp.Message 17 | w chan<- rpccapnp.Message 18 | finish chan struct{} 19 | otherFin chan struct{} 20 | 21 | rbuf bytes.Buffer 22 | 23 | mu sync.Mutex 24 | inflight int 25 | done bool 26 | } 27 | 28 | // New creates a synchronous in-memory pipe transport. 29 | func New() (p, q rpc.Transport) { 30 | a, b := make(chan rpccapnp.Message), make(chan rpccapnp.Message) 31 | afin, bfin := make(chan struct{}), make(chan struct{}) 32 | p = &pipeTransport{ 33 | r: a, 34 | w: b, 35 | finish: afin, 36 | otherFin: bfin, 37 | } 38 | q = &pipeTransport{ 39 | r: b, 40 | w: a, 41 | finish: bfin, 42 | otherFin: afin, 43 | } 44 | return 45 | } 46 | 47 | func (p *pipeTransport) SendMessage(ctx context.Context, msg rpccapnp.Message) error { 48 | if !p.startSend() { 49 | return errClosed 50 | } 51 | defer p.finishSend() 52 | 53 | buf, err := msg.Segment().Message().Marshal() 54 | if err != nil { 55 | return err 56 | } 57 | mm, err := capnp.Unmarshal(buf) 58 | if err != nil { 59 | return err 60 | } 61 | msg, err = rpccapnp.ReadRootMessage(mm) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | select { 67 | case p.w <- msg: 68 | return nil 69 | case <-ctx.Done(): 70 | return ctx.Err() 71 | case <-p.finish: 72 | return errClosed 73 | case <-p.otherFin: 74 | return errBrokenPipe 75 | } 76 | } 77 | 78 | func (p *pipeTransport) startSend() bool { 79 | p.mu.Lock() 80 | ok := !p.done 81 | if ok { 82 | p.inflight++ 83 | } 84 | p.mu.Unlock() 85 | return ok 86 | } 87 | 88 | func (p *pipeTransport) finishSend() { 89 | p.mu.Lock() 90 | p.inflight-- 91 | p.mu.Unlock() 92 | } 93 | 94 | func (p *pipeTransport) RecvMessage(ctx context.Context) (rpccapnp.Message, error) { 95 | // Scribble over shared buffer to test for race conditions. 96 | for b, i := p.rbuf.Bytes(), 0; i < len(b); i++ { 97 | b[i] = 0xff 98 | } 99 | p.rbuf.Reset() 100 | 101 | select { 102 | case msg, ok := <-p.r: 103 | if !ok { 104 | return rpccapnp.Message{}, errBrokenPipe 105 | } 106 | if err := capnp.NewEncoder(&p.rbuf).Encode(msg.Segment().Message()); err != nil { 107 | return rpccapnp.Message{}, err 108 | } 109 | m, err := capnp.Unmarshal(p.rbuf.Bytes()) 110 | if err != nil { 111 | return rpccapnp.Message{}, err 112 | } 113 | return rpccapnp.ReadRootMessage(m) 114 | case <-ctx.Done(): 115 | return rpccapnp.Message{}, ctx.Err() 116 | } 117 | } 118 | 119 | func (p *pipeTransport) Close() error { 120 | p.mu.Lock() 121 | done := p.done 122 | if !done { 123 | p.done = true 124 | close(p.finish) 125 | if p.inflight == 0 { 126 | close(p.w) 127 | } 128 | } 129 | p.mu.Unlock() 130 | if done { 131 | return errClosed 132 | } 133 | return nil 134 | } 135 | 136 | var ( 137 | errBrokenPipe = errors.New("pipetransport: broken pipe") 138 | errClosed = errors.New("pipetransport: write to broken pipe") 139 | ) 140 | -------------------------------------------------------------------------------- /rpc/internal/refcount/refcount.go: -------------------------------------------------------------------------------- 1 | // Package refcount implements a reference-counting client. 2 | package refcount 3 | 4 | import ( 5 | "errors" 6 | "runtime" 7 | "sync" 8 | 9 | "zombiezen.com/go/capnproto" 10 | ) 11 | 12 | // A RefCount will close its underlying client once all its references are closed. 13 | type RefCount struct { 14 | Client capnp.Client 15 | 16 | mu sync.Mutex 17 | refs int 18 | } 19 | 20 | // New creates a reference counter and the first client reference. 21 | func New(c capnp.Client) (rc *RefCount, ref capnp.Client) { 22 | rc = &RefCount{Client: c} 23 | ref = rc.Ref() 24 | return 25 | } 26 | 27 | // Ref makes a new client reference. 28 | func (rc *RefCount) Ref() capnp.Client { 29 | // TODO(light): what if someone calls Ref() after refs hits zero? 30 | rc.mu.Lock() 31 | rc.refs++ 32 | rc.mu.Unlock() 33 | r := &ref{rc: rc} 34 | runtime.SetFinalizer(r, (*ref).Close) 35 | return r 36 | } 37 | 38 | func (rc *RefCount) call(cl *capnp.Call) capnp.Answer { 39 | // We lock here so that we can prevent the client from being closed 40 | // while we start the call. 41 | rc.mu.Lock() 42 | defer rc.mu.Unlock() 43 | if rc.refs <= 0 { 44 | return capnp.ErrorAnswer(errClosed) 45 | } 46 | return rc.Client.Call(cl) 47 | } 48 | 49 | // decref decreases the reference count by one, closing the Client if it reaches zero. 50 | func (rc *RefCount) decref() error { 51 | shouldClose := false 52 | 53 | rc.mu.Lock() 54 | if rc.refs <= 0 { 55 | rc.mu.Unlock() 56 | return errClosed 57 | } 58 | rc.refs-- 59 | if rc.refs == 0 { 60 | shouldClose = true 61 | } 62 | rc.mu.Unlock() 63 | 64 | if shouldClose { 65 | return rc.Client.Close() 66 | } 67 | return nil 68 | } 69 | 70 | var errClosed = errors.New("rpc: Close() called on closed client") 71 | 72 | type ref struct { 73 | rc *RefCount 74 | once sync.Once 75 | } 76 | 77 | func (r *ref) Call(cl *capnp.Call) capnp.Answer { 78 | return r.rc.call(cl) 79 | } 80 | 81 | func (r *ref) WrappedClient() capnp.Client { 82 | return r.rc.Client 83 | } 84 | 85 | func (r *ref) Close() error { 86 | var err error 87 | closed := false 88 | r.once.Do(func() { 89 | err = r.rc.decref() 90 | closed = true 91 | }) 92 | if !closed { 93 | return errClosed 94 | } 95 | return err 96 | } 97 | -------------------------------------------------------------------------------- /rpc/internal/refcount/refcount_test.go: -------------------------------------------------------------------------------- 1 | package refcount 2 | 3 | import ( 4 | "testing" 5 | 6 | "zombiezen.com/go/capnproto" 7 | ) 8 | 9 | func TestSingleRefCloses(t *testing.T) { 10 | c := new(fakeClient) 11 | 12 | _, ref := New(c) 13 | err := ref.Close() 14 | 15 | if err != nil { 16 | t.Errorf("ref.Close(): %v", err) 17 | } 18 | if c.closed != 1 { 19 | t.Errorf("client Close() called %d times; want 1 time", c.closed) 20 | } 21 | } 22 | 23 | func TestCloseRefMultipleDecrefsOnce(t *testing.T) { 24 | c := new(fakeClient) 25 | 26 | rc, ref1 := New(c) 27 | ref2 := rc.Ref() 28 | err1 := ref1.Close() 29 | err2 := ref1.Close() 30 | _ = ref2 31 | 32 | if err1 != nil { 33 | t.Errorf("ref.Close() #1: %v", err1) 34 | } 35 | if err2 != errClosed { 36 | t.Errorf("ref.Close() #2: %v; want %v", err2, errClosed) 37 | } 38 | if c.closed != 0 { 39 | t.Errorf("client Close() called %d times; want 0 times", c.closed) 40 | } 41 | } 42 | 43 | func TestClosingOneOfManyRefsDoesntClose(t *testing.T) { 44 | c := new(fakeClient) 45 | 46 | rc, ref1 := New(c) 47 | ref2 := rc.Ref() 48 | err := ref1.Close() 49 | _ = ref2 50 | 51 | if err != nil { 52 | t.Errorf("ref1.Close(): %v", err) 53 | } 54 | if c.closed != 0 { 55 | t.Errorf("client Close() called %d times; want 0 times", c.closed) 56 | } 57 | } 58 | 59 | func TestClosingAllRefsCloses(t *testing.T) { 60 | c := new(fakeClient) 61 | 62 | rc, ref1 := New(c) 63 | ref2 := rc.Ref() 64 | err1 := ref1.Close() 65 | err2 := ref2.Close() 66 | 67 | if err1 != nil { 68 | t.Errorf("ref1.Close(): %v", err1) 69 | } 70 | if err2 != nil { 71 | t.Errorf("ref2.Close(): %v", err2) 72 | } 73 | if c.closed != 1 { 74 | t.Errorf("client Close() called %d times; want 1 times", c.closed) 75 | } 76 | } 77 | 78 | type fakeClient struct { 79 | closed int 80 | } 81 | 82 | func (c *fakeClient) Call(cl *capnp.Call) capnp.Answer { 83 | panic("not implemented") 84 | } 85 | 86 | func (c *fakeClient) Close() error { 87 | c.closed++ 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /rpc/internal/testcapnp/generate.go: -------------------------------------------------------------------------------- 1 | package testcapnp 2 | 3 | //go:generate capnp compile -ogo test.capnp 4 | -------------------------------------------------------------------------------- /rpc/internal/testcapnp/test.capnp: -------------------------------------------------------------------------------- 1 | # Test interfaces for RPC tests. 2 | 3 | using Go = import "../../../go.capnp"; 4 | 5 | @0xef12a34b9807e19c; 6 | $Go.package("testcapnp"); 7 | $Go.import("zombiezen.com/go/capnproto/rpc/internal/testcapnp"); 8 | 9 | interface Handle {} 10 | 11 | interface HandleFactory { 12 | newHandle @0 () -> (handle :Handle); 13 | } 14 | 15 | interface Hanger { 16 | hang @0 () -> (); 17 | # Block until context is cancelled 18 | } 19 | 20 | interface CallOrder { 21 | getCallSequence @0 (expected: UInt32) -> (n: UInt32); 22 | # First call returns 0, next returns 1, ... 23 | # 24 | # The input `expected` is ignored but useful for disambiguating debug logs. 25 | } 26 | 27 | interface Echoer extends(CallOrder) { 28 | echo @0 (cap :CallOrder) -> (cap :CallOrder); 29 | # Just returns the input cap. 30 | } 31 | 32 | # Example interfaces 33 | 34 | interface Adder { 35 | add @0 (a :Int32, b :Int32) -> (result :Int32); 36 | } 37 | -------------------------------------------------------------------------------- /rpc/introspect.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "log" 5 | 6 | "zombiezen.com/go/capnproto" 7 | "zombiezen.com/go/capnproto/internal/fulfiller" 8 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 9 | ) 10 | 11 | // nestedCall is called from the coordinate goroutine to make a client call. 12 | // Since the client may point 13 | func (c *Conn) nestedCall(client capnp.Client, cl *capnp.Call) capnp.Answer { 14 | client = extractRPCClient(client) 15 | ac := appCallFromClientCall(c, client, cl) 16 | if ac != nil { 17 | ans, err := c.handleCall(ac) 18 | if err != nil { 19 | log.Println("rpc: failed to handle call:", err) 20 | return capnp.ErrorAnswer(err) 21 | } 22 | return ans 23 | } 24 | // TODO(light): Add a CallOption that signals to bypass sync. 25 | // The above hack works in *most* cases. 26 | // 27 | // If your code is deadlocking here, you've hit the edge of the 28 | // compromise between these three goals: 29 | // 1) Package capnp is loosely coupled with package rpc 30 | // 2) Arbitrary implementations of Client may exist 31 | // 3) Local E-order must be preserved 32 | // 33 | // #3 is the one that creates a goroutine send cycle, since 34 | // application code must synchronize with the coordinate goroutine 35 | // to preserve order of delivery. You can't really overcome this 36 | // without breaking one of the first two constraints. 37 | // 38 | // To avoid #2 as much as possible, implementing Client is discouraged 39 | // by several docs. 40 | return client.Call(cl) 41 | } 42 | 43 | func (c *Conn) descriptorForClient(desc rpccapnp.CapDescriptor, client capnp.Client) error { 44 | client = extractRPCClient(client) 45 | if ic, ok := client.(*importClient); ok && isImportFromConn(ic, c) { 46 | desc.SetReceiverHosted(uint32(ic.id)) 47 | return nil 48 | } 49 | if pc, ok := client.(*capnp.PipelineClient); ok { 50 | p := (*capnp.Pipeline)(pc) 51 | if q, ok := p.Answer().(*question); ok && isQuestionFromConn(q, c) { 52 | a, err := desc.NewReceiverAnswer() 53 | if err != nil { 54 | return err 55 | } 56 | a.SetQuestionId(uint32(q.id)) 57 | err = transformToPromisedAnswer(desc.Segment(), a, p.Transform()) 58 | if err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | } 64 | id := c.exports.add(client) 65 | desc.SetSenderHosted(uint32(id)) 66 | return nil 67 | } 68 | 69 | func appCallFromClientCall(c *Conn, client capnp.Client, cl *capnp.Call) *appCall { 70 | if ic, ok := client.(*importClient); ok && isImportFromConn(ic, c) { 71 | ac, _ := newAppImportCall(ic.id, cl) 72 | return ac 73 | } 74 | if pc, ok := client.(*capnp.PipelineClient); ok { 75 | p := (*capnp.Pipeline)(pc) 76 | if q, ok := p.Answer().(*question); ok && isQuestionFromConn(q, c) { 77 | ac, _ := newAppPipelineCall(q, p.Transform(), cl) 78 | return ac 79 | } 80 | } 81 | return nil 82 | } 83 | 84 | // extractRPCClient attempts to extract the client that is the most 85 | // meaningful for further processing of RPCs. For example, instead of a 86 | // PipelineClient on a resolved answer, the client's capability. 87 | func extractRPCClient(client capnp.Client) capnp.Client { 88 | for { 89 | switch c := client.(type) { 90 | case *importClient: 91 | return c 92 | case *capnp.PipelineClient: 93 | p := (*capnp.Pipeline)(c) 94 | next := extractRPCClientFromPipeline(p.Answer(), p.Transform()) 95 | if next == nil { 96 | return client 97 | } 98 | client = next 99 | case clientWrapper: 100 | wc := c.WrappedClient() 101 | if wc == nil { 102 | return client 103 | } 104 | client = wc 105 | default: 106 | return client 107 | } 108 | } 109 | } 110 | 111 | func extractRPCClientFromPipeline(ans capnp.Answer, transform []capnp.PipelineOp) capnp.Client { 112 | if capnp.IsFixedAnswer(ans) { 113 | s, err := ans.Struct() 114 | return clientFromResolution(transform, capnp.Pointer(s), err) 115 | } 116 | switch a := ans.(type) { 117 | case *fulfiller.Fulfiller: 118 | ap := a.Peek() 119 | if ap == nil { 120 | // This can race, see TODO in nestedCall. 121 | return nil 122 | } 123 | s, err := ap.Struct() 124 | return clientFromResolution(transform, capnp.Pointer(s), err) 125 | case *question: 126 | _, obj, err, ok := a.peek() 127 | if !ok { 128 | // This can race, see TODO in nestedCall. 129 | return nil 130 | } 131 | return clientFromResolution(transform, obj, err) 132 | default: 133 | return nil 134 | } 135 | } 136 | 137 | // clientWrapper is an interface for types that wrap clients. 138 | // If WrappedClient returns a non-nil value, that means that a Call to 139 | // the wrapper passes through to the returned client. 140 | // TODO(light): this should probably be exported at some point. 141 | type clientWrapper interface { 142 | WrappedClient() capnp.Client 143 | } 144 | 145 | func isQuestionFromConn(q *question, c *Conn) bool { 146 | // TODO(light): ideally there would be better ways to check. 147 | return q.manager == &c.manager 148 | } 149 | 150 | func isImportFromConn(ic *importClient, c *Conn) bool { 151 | // TODO(light): ideally there would be better ways to check. 152 | return ic.manager == &c.manager 153 | } 154 | -------------------------------------------------------------------------------- /rpc/issue3_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/net/context" 7 | "zombiezen.com/go/capnproto/rpc" 8 | "zombiezen.com/go/capnproto/rpc/internal/logtransport" 9 | "zombiezen.com/go/capnproto/rpc/internal/pipetransport" 10 | "zombiezen.com/go/capnproto/rpc/internal/testcapnp" 11 | ) 12 | 13 | func TestIssue3(t *testing.T) { 14 | ctx, cancel := context.WithCancel(context.Background()) 15 | defer cancel() 16 | p, q := pipetransport.New() 17 | if *logMessages { 18 | p = logtransport.New(nil, p) 19 | } 20 | c := rpc.NewConn(p) 21 | echoSrv := testcapnp.Echoer_ServerToClient(new(SideEffectEchoer)) 22 | d := rpc.NewConn(q, rpc.MainInterface(echoSrv.Client)) 23 | defer d.Wait() 24 | defer c.Close() 25 | client := testcapnp.Echoer{Client: c.Bootstrap(ctx)} 26 | localCap := testcapnp.CallOrder_ServerToClient(new(CallOrder)) 27 | echo := client.Echo(ctx, func(p testcapnp.Echoer_echo_Params) error { 28 | return p.SetCap(localCap) 29 | }) 30 | 31 | // This should not deadlock. 32 | _, err := echo.Struct() 33 | if err != nil { 34 | t.Error("Echo error:", err) 35 | } 36 | } 37 | 38 | type SideEffectEchoer struct { 39 | CallOrder 40 | } 41 | 42 | func (*SideEffectEchoer) Echo(call testcapnp.Echoer_echo) error { 43 | call.Params.Cap().GetCallSequence(call.Ctx, func(p testcapnp.CallOrder_getCallSequence_Params) error { 44 | return nil 45 | }) 46 | call.Results.SetCap(call.Params.Cap()) 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /rpc/manager.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "sync" 5 | 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | // manager signals the running goroutines in a Conn. 10 | // Since there is one manager per connection, it's also a way of 11 | // identifying an object's origin. 12 | type manager struct { 13 | finish chan struct{} 14 | wg sync.WaitGroup 15 | ctx context.Context 16 | 17 | mu sync.RWMutex 18 | done bool 19 | e error 20 | } 21 | 22 | func (m *manager) init() { 23 | m.finish = make(chan struct{}) 24 | var cancel context.CancelFunc 25 | m.ctx, cancel = context.WithCancel(context.Background()) 26 | go func() { 27 | <-m.finish 28 | cancel() 29 | }() 30 | } 31 | 32 | // context returns a context that is cancelled when the manager shuts down. 33 | func (m *manager) context() context.Context { 34 | return m.ctx 35 | } 36 | 37 | // do starts a function in a new goroutine and will block shutdown 38 | // until it has returned. If the manager has already started shutdown, 39 | // then it is a no-op. 40 | func (m *manager) do(f func()) { 41 | m.mu.RLock() 42 | done := m.done 43 | if !done { 44 | m.wg.Add(1) 45 | } 46 | m.mu.RUnlock() 47 | if !done { 48 | go func() { 49 | defer m.wg.Done() 50 | f() 51 | }() 52 | } 53 | } 54 | 55 | // shutdown closes the finish channel and sets the error. 56 | // The first call to shutdown returns true; subsequent calls are no-ops 57 | // and return false. 58 | func (m *manager) shutdown(e error) bool { 59 | m.mu.Lock() 60 | ok := !m.done 61 | if ok { 62 | close(m.finish) 63 | m.done = true 64 | m.e = e 65 | } 66 | m.mu.Unlock() 67 | if ok { 68 | m.wg.Wait() 69 | } 70 | return ok 71 | } 72 | 73 | // err returns the error passed to shutdown. 74 | func (m *manager) err() error { 75 | m.mu.RLock() 76 | e := m.e 77 | m.mu.RUnlock() 78 | return e 79 | } 80 | -------------------------------------------------------------------------------- /rpc/promise_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/net/context" 7 | "zombiezen.com/go/capnproto/rpc" 8 | "zombiezen.com/go/capnproto/rpc/internal/logtransport" 9 | "zombiezen.com/go/capnproto/rpc/internal/pipetransport" 10 | "zombiezen.com/go/capnproto/rpc/internal/testcapnp" 11 | "zombiezen.com/go/capnproto/server" 12 | ) 13 | 14 | func TestPromisedCapability(t *testing.T) { 15 | ctx, cancel := context.WithCancel(context.Background()) 16 | defer cancel() 17 | p, q := pipetransport.New() 18 | if *logMessages { 19 | p = logtransport.New(nil, p) 20 | } 21 | c := rpc.NewConn(p) 22 | delay := make(chan struct{}) 23 | echoSrv := testcapnp.Echoer_ServerToClient(&DelayEchoer{delay: delay}) 24 | d := rpc.NewConn(q, rpc.MainInterface(echoSrv.Client)) 25 | defer d.Wait() 26 | defer c.Close() 27 | client := testcapnp.Echoer{Client: c.Bootstrap(ctx)} 28 | 29 | echo := client.Echo(ctx, func(p testcapnp.Echoer_echo_Params) error { 30 | return p.SetCap(testcapnp.CallOrder{Client: client.Client}) 31 | }) 32 | pipeline := echo.Cap() 33 | call0 := callseq(ctx, pipeline.Client, 0) 34 | call1 := callseq(ctx, pipeline.Client, 1) 35 | close(delay) 36 | 37 | check := func(promise testcapnp.CallOrder_getCallSequence_Results_Promise, n uint32) { 38 | r, err := promise.Struct() 39 | if err != nil { 40 | t.Errorf("call%d error: %v", n, err) 41 | } 42 | if r.N() != n { 43 | t.Errorf("call%d = %d; want %d", n, r.N(), n) 44 | } 45 | } 46 | check(call0, 0) 47 | check(call1, 1) 48 | } 49 | 50 | type DelayEchoer struct { 51 | Echoer 52 | delay chan struct{} 53 | } 54 | 55 | func (de *DelayEchoer) Echo(call testcapnp.Echoer_echo) error { 56 | server.Ack(call.Options) 57 | <-de.delay 58 | return de.Echoer.Echo(call) 59 | } 60 | -------------------------------------------------------------------------------- /rpc/question.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "sync" 5 | 6 | "golang.org/x/net/context" 7 | "zombiezen.com/go/capnproto" 8 | "zombiezen.com/go/capnproto/internal/fulfiller" 9 | "zombiezen.com/go/capnproto/internal/queue" 10 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 11 | ) 12 | 13 | type questionTable struct { 14 | tab []*question 15 | gen idgen 16 | 17 | manager *manager 18 | calls chan<- *appCall 19 | cancels chan<- *question 20 | } 21 | 22 | // new creates a new question with an unassigned ID. 23 | func (qt *questionTable) new(ctx context.Context, method *capnp.Method) *question { 24 | id := questionID(qt.gen.next()) 25 | q := &question{ 26 | ctx: ctx, 27 | method: method, 28 | manager: qt.manager, 29 | calls: qt.calls, 30 | cancels: qt.cancels, 31 | resolved: make(chan struct{}), 32 | id: id, 33 | } 34 | // TODO(light): populate paramCaps 35 | if int(id) == len(qt.tab) { 36 | qt.tab = append(qt.tab, q) 37 | } else { 38 | qt.tab[id] = q 39 | } 40 | return q 41 | } 42 | 43 | func (qt *questionTable) get(id questionID) *question { 44 | var q *question 45 | if int(id) < len(qt.tab) { 46 | q = qt.tab[id] 47 | } 48 | return q 49 | } 50 | 51 | func (qt *questionTable) pop(id questionID) *question { 52 | var q *question 53 | if int(id) < len(qt.tab) { 54 | q = qt.tab[id] 55 | qt.tab[id] = nil 56 | qt.gen.remove(uint32(id)) 57 | } 58 | return q 59 | } 60 | 61 | type question struct { 62 | ctx context.Context 63 | method *capnp.Method // nil if this is bootstrap 64 | paramCaps []exportID 65 | calls chan<- *appCall 66 | cancels chan<- *question 67 | manager *manager 68 | resolved chan struct{} 69 | 70 | // Fields below are protected by mu. 71 | mu sync.RWMutex 72 | id questionID 73 | obj capnp.Pointer 74 | err error 75 | state questionState 76 | derived [][]capnp.PipelineOp 77 | } 78 | 79 | type questionState uint8 80 | 81 | // Question states 82 | const ( 83 | questionInProgress questionState = iota 84 | questionResolved 85 | questionCanceled 86 | ) 87 | 88 | // start signals that the question has been sent. 89 | func (q *question) start() { 90 | go func() { 91 | select { 92 | case <-q.resolved: 93 | case <-q.ctx.Done(): 94 | select { 95 | case q.cancels <- q: 96 | case <-q.resolved: 97 | case <-q.manager.finish: 98 | } 99 | case <-q.manager.finish: 100 | // TODO(light): connection should reject all questions on shutdown. 101 | } 102 | }() 103 | } 104 | 105 | // fulfill is called to resolve a question succesfully and returns the disembargoes. 106 | // It must be called from the coordinate goroutine. 107 | func (q *question) fulfill(obj capnp.Pointer, makeDisembargo func() (embargoID, embargo)) []rpccapnp.Message { 108 | q.mu.Lock() 109 | if q.state != questionInProgress { 110 | q.mu.Unlock() 111 | panic("question.fulfill called more than once") 112 | } 113 | ctab := obj.Segment().Message().CapTable 114 | visited := make([]bool, len(ctab)) 115 | msgs := make([]rpccapnp.Message, 0, len(q.derived)) 116 | for _, d := range q.derived { 117 | tgt, err := capnp.Transform(obj, d) 118 | if err != nil { 119 | continue 120 | } 121 | in := capnp.ToInterface(tgt) 122 | if !capnp.IsValid(in) { 123 | continue 124 | } 125 | client := extractRPCClient(in.Client()) 126 | if ic, ok := client.(*importClient); ok && ic.manager == q.manager { 127 | // Imported from remote vat. Don't need to disembargo. 128 | continue 129 | } 130 | if cn := in.Capability(); !visited[cn] { 131 | id, e := makeDisembargo() 132 | ctab[cn] = newEmbargoClient(q.manager, ctab[cn], e) 133 | m := newDisembargoMessage(nil, rpccapnp.Disembargo_context_Which_senderLoopback, id) 134 | dis, _ := m.Disembargo() 135 | mt, _ := dis.NewTarget() 136 | pa, _ := mt.NewPromisedAnswer() 137 | pa.SetQuestionId(uint32(q.id)) 138 | transformToPromisedAnswer(m.Segment(), pa, d) 139 | mt.SetPromisedAnswer(pa) 140 | msgs = append(msgs, m) 141 | visited[cn] = true 142 | } 143 | } 144 | q.obj, q.state = obj, questionResolved 145 | close(q.resolved) 146 | q.mu.Unlock() 147 | return msgs 148 | } 149 | 150 | // reject is called to resolve a question with failure. 151 | // It must be called from the coordinate goroutine. 152 | func (q *question) reject(state questionState, err error) { 153 | if err == nil { 154 | panic("question.reject called with nil") 155 | } 156 | q.mu.Lock() 157 | if q.state != questionInProgress { 158 | q.mu.Unlock() 159 | panic("question.reject called more than once") 160 | } 161 | q.err, q.state = err, state 162 | close(q.resolved) 163 | q.mu.Unlock() 164 | } 165 | 166 | func (q *question) peek() (id questionID, obj capnp.Pointer, err error, ok bool) { 167 | q.mu.RLock() 168 | id, obj, err, ok = q.id, q.obj, q.err, q.state != questionInProgress 169 | q.mu.RUnlock() 170 | return 171 | } 172 | 173 | func (q *question) addPromise(transform []capnp.PipelineOp) { 174 | q.mu.Lock() 175 | defer q.mu.Unlock() 176 | for _, d := range q.derived { 177 | if transformsEqual(transform, d) { 178 | return 179 | } 180 | } 181 | q.derived = append(q.derived, transform) 182 | } 183 | 184 | func transformsEqual(t, u []capnp.PipelineOp) bool { 185 | if len(t) != len(u) { 186 | return false 187 | } 188 | for i := range t { 189 | if t[i].Field != u[i].Field { 190 | return false 191 | } 192 | } 193 | return true 194 | } 195 | 196 | func (q *question) Struct() (capnp.Struct, error) { 197 | <-q.resolved 198 | _, obj, err, _ := q.peek() 199 | return capnp.ToStruct(obj), err 200 | } 201 | 202 | func (q *question) PipelineCall(transform []capnp.PipelineOp, ccall *capnp.Call) capnp.Answer { 203 | ac, achan := newAppPipelineCall(q, transform, ccall) 204 | select { 205 | case q.calls <- ac: 206 | case <-ccall.Ctx.Done(): 207 | return capnp.ErrorAnswer(ccall.Ctx.Err()) 208 | case <-q.manager.finish: 209 | return capnp.ErrorAnswer(q.manager.err()) 210 | } 211 | select { 212 | case a := <-achan: 213 | return a 214 | case <-ccall.Ctx.Done(): 215 | return capnp.ErrorAnswer(ccall.Ctx.Err()) 216 | case <-q.manager.finish: 217 | return capnp.ErrorAnswer(q.manager.err()) 218 | } 219 | } 220 | 221 | func (q *question) PipelineClose(transform []capnp.PipelineOp) error { 222 | <-q.resolved 223 | _, obj, err, _ := q.peek() 224 | if err != nil { 225 | return err 226 | } 227 | x, err := capnp.Transform(obj, transform) 228 | if err != nil { 229 | return err 230 | } 231 | c := capnp.ToInterface(x).Client() 232 | if c == nil { 233 | return capnp.ErrNullClient 234 | } 235 | return c.Close() 236 | } 237 | 238 | // embargoClient is a client that waits until an embargo signal is 239 | // received to deliver calls. 240 | type embargoClient struct { 241 | manager *manager 242 | client capnp.Client 243 | embargo embargo 244 | 245 | mu sync.RWMutex 246 | q queue.Queue 247 | } 248 | 249 | func newEmbargoClient(manager *manager, client capnp.Client, e embargo) *embargoClient { 250 | ec := &embargoClient{ 251 | manager: manager, 252 | client: client, 253 | embargo: e, 254 | } 255 | ec.q.Init(make(ecallList, callQueueSize), 0) 256 | go ec.flushQueue() 257 | return ec 258 | } 259 | 260 | func (ec *embargoClient) push(cl *capnp.Call) capnp.Answer { 261 | f := new(fulfiller.Fulfiller) 262 | cl, err := cl.Copy(nil) 263 | if err != nil { 264 | return capnp.ErrorAnswer(err) 265 | } 266 | if ok := ec.q.Push(ecall{cl, f}); !ok { 267 | return capnp.ErrorAnswer(errQueueFull) 268 | } 269 | return f 270 | } 271 | 272 | func (ec *embargoClient) peek() ecall { 273 | if ec.q.Len() == 0 { 274 | return ecall{} 275 | } 276 | return ec.q.Peek().(ecall) 277 | } 278 | 279 | func (ec *embargoClient) pop() ecall { 280 | if ec.q.Len() == 0 { 281 | return ecall{} 282 | } 283 | return ec.q.Pop().(ecall) 284 | } 285 | 286 | func (ec *embargoClient) Call(cl *capnp.Call) capnp.Answer { 287 | // Fast path: queue is flushed. 288 | ec.mu.RLock() 289 | ok := ec.isPassthrough() 290 | ec.mu.RUnlock() 291 | if ok { 292 | return ec.client.Call(cl) 293 | } 294 | 295 | ec.mu.Lock() 296 | if ec.isPassthrough() { 297 | ec.mu.Unlock() 298 | return ec.client.Call(cl) 299 | } 300 | ans := ec.push(cl) 301 | ec.mu.Unlock() 302 | return ans 303 | } 304 | 305 | func (ec *embargoClient) WrappedClient() capnp.Client { 306 | ec.mu.RLock() 307 | ok := ec.isPassthrough() 308 | ec.mu.RUnlock() 309 | if !ok { 310 | return nil 311 | } 312 | return ec.client 313 | } 314 | 315 | func (ec *embargoClient) isPassthrough() bool { 316 | select { 317 | case <-ec.embargo: 318 | default: 319 | return false 320 | } 321 | return ec.q.Len() == 0 322 | } 323 | 324 | func (ec *embargoClient) Close() error { 325 | ec.mu.Lock() 326 | for { 327 | c := ec.pop() 328 | if c.call == nil { 329 | break 330 | } 331 | c.f.Reject(errQueueCallCancel) 332 | } 333 | ec.mu.Unlock() 334 | return ec.client.Close() 335 | } 336 | 337 | // flushQueue is run in its own goroutine. 338 | func (ec *embargoClient) flushQueue() { 339 | select { 340 | case <-ec.embargo: 341 | case <-ec.manager.finish: 342 | return 343 | } 344 | ec.mu.RLock() 345 | c := ec.peek() 346 | ec.mu.RUnlock() 347 | for c.call != nil { 348 | ans := ec.client.Call(c.call) 349 | go joinFulfiller(c.f, ans) 350 | ec.mu.Lock() 351 | ec.pop() 352 | c = ec.peek() 353 | ec.mu.Unlock() 354 | } 355 | } 356 | 357 | type ecall struct { 358 | call *capnp.Call 359 | f *fulfiller.Fulfiller 360 | } 361 | 362 | type ecallList []ecall 363 | 364 | func (el ecallList) Len() int { 365 | return len(el) 366 | } 367 | 368 | func (el ecallList) At(i int) interface{} { 369 | return el[i] 370 | } 371 | 372 | func (el ecallList) Set(i int, x interface{}) { 373 | if x == nil { 374 | el[i] = ecall{} 375 | } else { 376 | el[i] = x.(ecall) 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /rpc/release_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "golang.org/x/net/context" 8 | "zombiezen.com/go/capnproto" 9 | "zombiezen.com/go/capnproto/rpc" 10 | "zombiezen.com/go/capnproto/rpc/internal/logtransport" 11 | "zombiezen.com/go/capnproto/rpc/internal/pipetransport" 12 | "zombiezen.com/go/capnproto/rpc/internal/testcapnp" 13 | "zombiezen.com/go/capnproto/server" 14 | ) 15 | 16 | func TestRelease(t *testing.T) { 17 | ctx, cancel := context.WithCancel(context.Background()) 18 | defer cancel() 19 | p, q := pipetransport.New() 20 | if *logMessages { 21 | p = logtransport.New(nil, p) 22 | } 23 | c := rpc.NewConn(p) 24 | hf := new(HandleFactory) 25 | d := rpc.NewConn(q, rpc.MainInterface(testcapnp.HandleFactory_ServerToClient(hf).Client)) 26 | defer d.Wait() 27 | defer c.Close() 28 | client := testcapnp.HandleFactory{Client: c.Bootstrap(ctx)} 29 | r, err := client.NewHandle(ctx, func(r testcapnp.HandleFactory_newHandle_Params) error { return nil }).Struct() 30 | if err != nil { 31 | t.Fatal("NewHandle:", err) 32 | } 33 | handle := r.Handle() 34 | if n := hf.numHandles(); n != 1 { 35 | t.Fatalf("numHandles = %d; want 1", n) 36 | } 37 | 38 | if err := handle.Client.Close(); err != nil { 39 | t.Error("handle.Client.Close():", err) 40 | } 41 | flushConn(ctx, c) 42 | 43 | if n := hf.numHandles(); n != 0 { 44 | t.Errorf("numHandles = %d; want 0", n) 45 | } 46 | } 47 | 48 | func TestReleaseAlias(t *testing.T) { 49 | ctx, cancel := context.WithCancel(context.Background()) 50 | defer cancel() 51 | p, q := pipetransport.New() 52 | if *logMessages { 53 | p = logtransport.New(nil, p) 54 | } 55 | c := rpc.NewConn(p) 56 | hf := singletonHandleFactory() 57 | d := rpc.NewConn(q, rpc.MainInterface(testcapnp.HandleFactory_ServerToClient(hf).Client)) 58 | defer d.Wait() 59 | defer c.Close() 60 | client := testcapnp.HandleFactory{Client: c.Bootstrap(ctx)} 61 | r1, err := client.NewHandle(ctx, func(r testcapnp.HandleFactory_newHandle_Params) error { return nil }).Struct() 62 | if err != nil { 63 | t.Fatal("NewHandle #1:", err) 64 | } 65 | handle1 := r1.Handle() 66 | r2, err := client.NewHandle(ctx, func(r testcapnp.HandleFactory_newHandle_Params) error { return nil }).Struct() 67 | if err != nil { 68 | t.Fatal("NewHandle #2:", err) 69 | } 70 | handle2 := r2.Handle() 71 | if n := hf.numHandles(); n != 1 { 72 | t.Fatalf("after creation, numHandles = %d; want 1", n) 73 | } 74 | 75 | if err := handle1.Client.Close(); err != nil { 76 | t.Error("handle1.Client.Close():", err) 77 | } 78 | flushConn(ctx, c) 79 | if n := hf.numHandles(); n != 1 { 80 | t.Errorf("after handle1.Client.Close(), numHandles = %d; want 1", n) 81 | } 82 | if err := handle2.Client.Close(); err != nil { 83 | t.Error("handle2.Client.Close():", err) 84 | } 85 | flushConn(ctx, c) 86 | if n := hf.numHandles(); n != 0 { 87 | t.Errorf("after handle1.Close() and handle2.Close(), numHandles = %d; want 0", n) 88 | } 89 | } 90 | 91 | func flushConn(ctx context.Context, c *rpc.Conn) { 92 | // discard result 93 | c.Bootstrap(ctx).Call(&capnp.Call{ 94 | Ctx: ctx, 95 | Method: capnp.Method{InterfaceID: 0xdeadbeef, MethodID: 42}, 96 | ParamsFunc: func(capnp.Struct) error { return nil }, 97 | ParamsSize: capnp.ObjectSize{}, 98 | }).Struct() 99 | } 100 | 101 | type Handle struct { 102 | f *HandleFactory 103 | } 104 | 105 | func (h Handle) Close() error { 106 | h.f.mu.Lock() 107 | h.f.n-- 108 | h.f.mu.Unlock() 109 | return nil 110 | } 111 | 112 | type HandleFactory struct { 113 | n int 114 | mu sync.Mutex 115 | singleton testcapnp.Handle 116 | } 117 | 118 | func singletonHandleFactory() *HandleFactory { 119 | hf := new(HandleFactory) 120 | hf.singleton = testcapnp.Handle_ServerToClient(&Handle{f: hf}) 121 | return hf 122 | } 123 | 124 | func (hf *HandleFactory) NewHandle(call testcapnp.HandleFactory_newHandle) error { 125 | server.Ack(call.Options) 126 | if hf.singleton.Client == nil { 127 | hf.mu.Lock() 128 | hf.n++ 129 | hf.mu.Unlock() 130 | call.Results.SetHandle(testcapnp.Handle_ServerToClient(&Handle{f: hf})) 131 | } else { 132 | hf.mu.Lock() 133 | hf.n = 1 134 | hf.mu.Unlock() 135 | call.Results.SetHandle(hf.singleton) 136 | } 137 | return nil 138 | } 139 | 140 | func (hf *HandleFactory) numHandles() int { 141 | hf.mu.Lock() 142 | n := hf.n 143 | hf.mu.Unlock() 144 | return n 145 | } 146 | -------------------------------------------------------------------------------- /rpc/rpccapnp/generate.go: -------------------------------------------------------------------------------- 1 | package rpccapnp 2 | 3 | //go:generate capnp compile -ogo rpc.capnp 4 | -------------------------------------------------------------------------------- /rpc/tables.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "log" 5 | 6 | "zombiezen.com/go/capnproto" 7 | "zombiezen.com/go/capnproto/rpc/internal/refcount" 8 | ) 9 | 10 | // Table IDs 11 | type ( 12 | questionID uint32 13 | answerID uint32 14 | exportID uint32 15 | importID uint32 16 | embargoID uint32 17 | ) 18 | 19 | // impent is an entry in the import table. 20 | type impent struct { 21 | rc *refcount.RefCount 22 | refs int 23 | } 24 | 25 | type importTable struct { 26 | tab map[importID]*impent 27 | manager *manager 28 | calls chan<- *appCall 29 | releases chan<- *outgoingRelease 30 | } 31 | 32 | // addRef increases the counter of the times the import ID was sent to this vat. 33 | func (it *importTable) addRef(id importID) capnp.Client { 34 | if it.tab == nil { 35 | it.tab = make(map[importID]*impent) 36 | } 37 | ent := it.tab[id] 38 | var ref capnp.Client 39 | if ent == nil { 40 | client := &importClient{ 41 | id: id, 42 | manager: it.manager, 43 | calls: it.calls, 44 | releases: it.releases, 45 | } 46 | var rc *refcount.RefCount 47 | rc, ref = refcount.New(client) 48 | ent = &impent{rc: rc, refs: 0} 49 | it.tab[id] = ent 50 | } 51 | if ref == nil { 52 | ref = ent.rc.Ref() 53 | } 54 | ent.refs++ 55 | return ref 56 | } 57 | 58 | // pop removes the import ID and returns the number of times the import ID was sent to this vat. 59 | func (it *importTable) pop(id importID) (refs int) { 60 | if it.tab != nil { 61 | if ent := it.tab[id]; ent != nil { 62 | refs = ent.refs 63 | } 64 | delete(it.tab, id) 65 | } 66 | return 67 | } 68 | 69 | // An outgoingRelease is a message sent to the coordinate goroutine to 70 | // indicate that an import should be released. 71 | type outgoingRelease struct { 72 | id importID 73 | echan chan<- error 74 | } 75 | 76 | // An importClient implements capnp.Client for a remote capability. 77 | type importClient struct { 78 | id importID 79 | manager *manager 80 | calls chan<- *appCall 81 | releases chan<- *outgoingRelease 82 | } 83 | 84 | func (ic *importClient) Call(cl *capnp.Call) capnp.Answer { 85 | // TODO(light): don't send if closed. 86 | ac, achan := newAppImportCall(ic.id, cl) 87 | select { 88 | case ic.calls <- ac: 89 | select { 90 | case a := <-achan: 91 | return a 92 | case <-ic.manager.finish: 93 | return capnp.ErrorAnswer(ic.manager.err()) 94 | } 95 | case <-ic.manager.finish: 96 | return capnp.ErrorAnswer(ic.manager.err()) 97 | } 98 | } 99 | 100 | func (ic *importClient) Close() error { 101 | echan := make(chan error, 1) 102 | r := &outgoingRelease{ 103 | id: ic.id, 104 | echan: echan, 105 | } 106 | select { 107 | case ic.releases <- r: 108 | select { 109 | case err := <-echan: 110 | return err 111 | case <-ic.manager.finish: 112 | return ic.manager.err() 113 | } 114 | case <-ic.manager.finish: 115 | return ic.manager.err() 116 | } 117 | } 118 | 119 | type export struct { 120 | id exportID 121 | client capnp.Client 122 | 123 | // for use by the table only 124 | refs int 125 | } 126 | 127 | type exportTable struct { 128 | tab []*export 129 | gen idgen 130 | } 131 | 132 | func (et *exportTable) get(id exportID) *export { 133 | var e *export 134 | if int(id) < len(et.tab) { 135 | e = et.tab[id] 136 | } 137 | return e 138 | } 139 | 140 | // add ensures that the client is present in the table, returning its ID. 141 | // If the client is already in the table, the previous ID is returned. 142 | func (et *exportTable) add(client capnp.Client) exportID { 143 | for i, e := range et.tab { 144 | if e != nil && e.client == client { 145 | e.refs++ 146 | return exportID(i) 147 | } 148 | } 149 | id := exportID(et.gen.next()) 150 | export := &export{ 151 | id: id, 152 | client: client, 153 | refs: 1, 154 | } 155 | if int(id) == len(et.tab) { 156 | et.tab = append(et.tab, export) 157 | } else { 158 | et.tab[id] = export 159 | } 160 | return id 161 | } 162 | 163 | func (et *exportTable) release(id exportID, refs int) { 164 | if int(id) >= len(et.tab) { 165 | return 166 | } 167 | e := et.tab[id] 168 | if e == nil { 169 | return 170 | } 171 | e.refs -= refs 172 | if e.refs > 0 { 173 | return 174 | } 175 | if e.refs < 0 { 176 | log.Printf("rpc: warning: export %v has negative refcount (%d)", id, e.refs) 177 | } 178 | if err := e.client.Close(); err != nil { 179 | log.Printf("rpc: export %v close: %v", id, err) 180 | } 181 | et.tab[id] = nil 182 | et.gen.remove(uint32(id)) 183 | } 184 | 185 | // releaseList decrements the reference count of each of the given exports by 1. 186 | func (et *exportTable) releaseList(ids []exportID) { 187 | for _, id := range ids { 188 | et.release(id, 1) 189 | } 190 | } 191 | 192 | type embargoTable struct { 193 | tab []chan<- struct{} 194 | gen idgen 195 | } 196 | 197 | type embargo <-chan struct{} 198 | 199 | func (et *embargoTable) new() (embargoID, embargo) { 200 | id := embargoID(et.gen.next()) 201 | e := make(chan struct{}) 202 | if int(id) == len(et.tab) { 203 | et.tab = append(et.tab, e) 204 | } else { 205 | et.tab[id] = e 206 | } 207 | return id, e 208 | } 209 | 210 | func (et *embargoTable) disembargo(id embargoID) { 211 | if int(id) >= len(et.tab) { 212 | return 213 | } 214 | e := et.tab[id] 215 | if e == nil { 216 | return 217 | } 218 | close(e) 219 | et.tab[id] = nil 220 | et.gen.remove(uint32(id)) 221 | } 222 | 223 | // idgen returns a sequence of monotonically increasing IDs with 224 | // support for replacement. The zero value is a generator that 225 | // starts at zero. 226 | type idgen struct { 227 | i uint32 228 | free []uint32 229 | } 230 | 231 | func (gen *idgen) next() uint32 { 232 | if n := len(gen.free); n > 0 { 233 | i := gen.free[n-1] 234 | gen.free = gen.free[:n-1] 235 | return i 236 | } 237 | i := gen.i 238 | gen.i++ 239 | return i 240 | } 241 | 242 | func (gen *idgen) remove(i uint32) { 243 | gen.free = append(gen.free, i) 244 | } 245 | -------------------------------------------------------------------------------- /rpc/transport.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "log" 7 | "time" 8 | 9 | "golang.org/x/net/context" 10 | "zombiezen.com/go/capnproto" 11 | "zombiezen.com/go/capnproto/rpc/rpccapnp" 12 | ) 13 | 14 | // Transport is the interface that abstracts sending and receiving 15 | // individual messages of the Cap'n Proto RPC protocol. 16 | type Transport interface { 17 | // SendMessage sends msg. 18 | SendMessage(ctx context.Context, msg rpccapnp.Message) error 19 | 20 | // RecvMessage waits to receive a message and returns it. 21 | // Implementations may re-use buffers between calls, so the message is 22 | // only valid until the next call to RecvMessage. 23 | RecvMessage(ctx context.Context) (rpccapnp.Message, error) 24 | 25 | // Close releases any resources associated with the transport. 26 | Close() error 27 | } 28 | 29 | type streamTransport struct { 30 | rwc io.ReadWriteCloser 31 | deadline writeDeadlineSetter 32 | 33 | enc *capnp.Encoder 34 | dec *capnp.Decoder 35 | wbuf bytes.Buffer 36 | } 37 | 38 | // StreamTransport creates a transport that sends and receives messages 39 | // by serializing and deserializing unpacked Cap'n Proto messages. 40 | // Closing the transport will close the underlying ReadWriteCloser. 41 | func StreamTransport(rwc io.ReadWriteCloser) Transport { 42 | d, _ := rwc.(writeDeadlineSetter) 43 | s := &streamTransport{ 44 | rwc: rwc, 45 | deadline: d, 46 | dec: capnp.NewDecoder(rwc), 47 | } 48 | s.wbuf.Grow(4096) 49 | s.enc = capnp.NewEncoder(&s.wbuf) 50 | return s 51 | } 52 | 53 | func (s *streamTransport) SendMessage(ctx context.Context, msg rpccapnp.Message) error { 54 | s.wbuf.Reset() 55 | if err := s.enc.Encode(msg.Segment().Message()); err != nil { 56 | return err 57 | } 58 | if s.deadline != nil { 59 | // TODO(light): log errors 60 | if d, ok := ctx.Deadline(); ok { 61 | s.deadline.SetWriteDeadline(d) 62 | } else { 63 | s.deadline.SetWriteDeadline(time.Time{}) 64 | } 65 | } 66 | _, err := s.rwc.Write(s.wbuf.Bytes()) 67 | return err 68 | } 69 | 70 | func (s *streamTransport) RecvMessage(ctx context.Context) (rpccapnp.Message, error) { 71 | var ( 72 | msg *capnp.Message 73 | err error 74 | ) 75 | read := make(chan struct{}) 76 | go func() { 77 | msg, err = s.dec.Decode() 78 | close(read) 79 | }() 80 | select { 81 | case <-read: 82 | case <-ctx.Done(): 83 | return rpccapnp.Message{}, ctx.Err() 84 | } 85 | if err != nil { 86 | return rpccapnp.Message{}, err 87 | } 88 | return rpccapnp.ReadRootMessage(msg) 89 | } 90 | 91 | func (s *streamTransport) Close() error { 92 | return s.rwc.Close() 93 | } 94 | 95 | type writeDeadlineSetter interface { 96 | SetWriteDeadline(t time.Time) error 97 | } 98 | 99 | // dispatchSend runs in its own goroutine and sends messages on a transport. 100 | func dispatchSend(m *manager, transport Transport, msgs <-chan rpccapnp.Message) { 101 | for { 102 | select { 103 | case msg := <-msgs: 104 | err := transport.SendMessage(m.context(), msg) 105 | if err != nil { 106 | log.Printf("rpc: writing %v: %v", msg.Which(), err) 107 | } 108 | case <-m.finish: 109 | return 110 | } 111 | } 112 | } 113 | 114 | // sendMessage sends a message to out to be sent. It returns an error 115 | // if the manager finished. 116 | func sendMessage(m *manager, out chan<- rpccapnp.Message, msg rpccapnp.Message) error { 117 | select { 118 | case out <- msg: 119 | return nil 120 | case <-m.finish: 121 | return m.err() 122 | } 123 | } 124 | 125 | // dispatchRecv runs in its own goroutine and receives messages from a transport. 126 | func dispatchRecv(m *manager, transport Transport, msgs chan<- rpccapnp.Message) { 127 | for { 128 | msg, err := transport.RecvMessage(m.context()) 129 | if err != nil { 130 | if isTemporaryError(err) { 131 | log.Println("rpc: read temporary error:", err) 132 | continue 133 | } 134 | m.shutdown(err) 135 | return 136 | } 137 | select { 138 | case msgs <- copyRPCMessage(msg): 139 | case <-m.finish: 140 | return 141 | } 142 | } 143 | } 144 | 145 | // copyMessage clones a Cap'n Proto buffer. 146 | func copyMessage(msg *capnp.Message) *capnp.Message { 147 | n := msg.NumSegments() 148 | segments := make([][]byte, n) 149 | for i := range segments { 150 | s, err := msg.Segment(capnp.SegmentID(i)) 151 | if err != nil { 152 | panic(err) 153 | } 154 | segments[i] = make([]byte, len(s.Data())) 155 | copy(segments[i], s.Data()) 156 | } 157 | return &capnp.Message{Arena: capnp.MultiSegment(segments)} 158 | } 159 | 160 | // copyRPCMessage clones an RPC packet. 161 | func copyRPCMessage(m rpccapnp.Message) rpccapnp.Message { 162 | mm := copyMessage(m.Segment().Message()) 163 | rpcMsg, err := rpccapnp.ReadRootMessage(mm) 164 | if err != nil { 165 | panic(err) 166 | } 167 | return rpcMsg 168 | } 169 | 170 | // isTemporaryError reports whether e has a Temporary() method that 171 | // returns true. 172 | func isTemporaryError(e error) bool { 173 | type temp interface { 174 | Temporary() bool 175 | } 176 | t, ok := e.(temp) 177 | return ok && t.Temporary() 178 | } 179 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | // Package server provides runtime support for implementing Cap'n Proto 2 | // interfaces locally. 3 | package server // import "zombiezen.com/go/capnproto/server" 4 | 5 | import ( 6 | "errors" 7 | "sort" 8 | "sync" 9 | 10 | "golang.org/x/net/context" 11 | "zombiezen.com/go/capnproto" 12 | "zombiezen.com/go/capnproto/internal/fulfiller" 13 | ) 14 | 15 | // A Method describes a single method on a server object. 16 | type Method struct { 17 | capnp.Method 18 | Impl Func 19 | ResultsSize capnp.ObjectSize 20 | } 21 | 22 | // A Func is a function that implements a single method. 23 | type Func func(ctx context.Context, options capnp.CallOptions, params, results capnp.Struct) error 24 | 25 | // Closer is the interface that wraps the Close method. 26 | type Closer interface { 27 | Close() error 28 | } 29 | 30 | // queueSize is the number of calls that can be made on a server 31 | // before Call blocks returning an answer. 32 | const queueSize = 64 33 | 34 | // A server is a locally implemented interface. 35 | type server struct { 36 | methods sortedMethods 37 | closer Closer 38 | queue chan *call 39 | stop chan struct{} 40 | } 41 | 42 | // New returns a client that makes calls to a set of methods. 43 | // If closer is nil then the client's Close is a no-op. The server 44 | // guarantees message delivery order by blocking each call on the 45 | // return or acknowledgement of the previous call. See the Ack function 46 | // for more details. 47 | func New(methods []Method, closer Closer) capnp.Client { 48 | s := &server{ 49 | methods: make(sortedMethods, len(methods)), 50 | closer: closer, 51 | queue: make(chan *call, 64), 52 | stop: make(chan struct{}), 53 | } 54 | copy(s.methods, methods) 55 | sort.Sort(s.methods) 56 | go s.dispatch() 57 | return s 58 | } 59 | 60 | // dispatch runs in its own goroutine. 61 | func (s *server) dispatch() { 62 | dispatch: 63 | for { 64 | select { 65 | case cl, ok := <-s.queue: 66 | if !ok { 67 | return 68 | } 69 | err := s.startCall(cl) 70 | if err != nil { 71 | cl.ans.Reject(err) 72 | continue dispatch 73 | } 74 | case <-s.stop: 75 | break dispatch 76 | } 77 | } 78 | 79 | // Close() has been called, flush the queue. 80 | for cl := range s.queue { 81 | cl.ans.Reject(errClosed) 82 | } 83 | } 84 | 85 | // startCall runs in the dispatch goroutine to start a call. 86 | func (s *server) startCall(cl *call) error { 87 | _, out, err := capnp.NewMessage(capnp.SingleSegment(nil)) 88 | if err != nil { 89 | return err 90 | } 91 | results, err := capnp.NewRootStruct(out, cl.method.ResultsSize) 92 | if err != nil { 93 | return err 94 | } 95 | acksig := newAckSignal() 96 | opts := cl.Options.With([]capnp.CallOption{capnp.SetOptionValue(ackSignalKey, acksig)}) 97 | go func() { 98 | err := cl.method.Impl(cl.Ctx, opts, cl.Params, results) 99 | if err == nil { 100 | cl.ans.Fulfill(results) 101 | } else { 102 | cl.ans.Reject(err) 103 | } 104 | }() 105 | select { 106 | case <-acksig.c: 107 | case <-cl.ans.Done(): 108 | // Implementation functions may not call Ack, which is fine for 109 | // smaller functions. 110 | case <-cl.Ctx.Done(): 111 | // Ideally, this would reject the answer immediately, but then you 112 | // would race with the implementation function. 113 | } 114 | return nil 115 | } 116 | 117 | func (s *server) Call(cl *capnp.Call) capnp.Answer { 118 | sm := s.methods.find(&cl.Method) 119 | if sm == nil { 120 | return capnp.ErrorAnswer(&capnp.MethodError{ 121 | Method: &cl.Method, 122 | Err: capnp.ErrUnimplemented, 123 | }) 124 | } 125 | cl, err := cl.Copy(nil) 126 | if err != nil { 127 | return capnp.ErrorAnswer(err) 128 | } 129 | scall := newCall(cl, sm) 130 | select { 131 | case s.queue <- scall: 132 | return &scall.ans 133 | case <-cl.Ctx.Done(): 134 | return capnp.ErrorAnswer(cl.Ctx.Err()) 135 | } 136 | } 137 | 138 | func (s *server) Close() error { 139 | close(s.stop) 140 | close(s.queue) 141 | if s.closer == nil { 142 | return nil 143 | } 144 | return s.closer.Close() 145 | } 146 | 147 | // Ack acknowledges delivery of a server call, allowing other methods 148 | // to be called on the server. It is intended to be used inside the 149 | // implementation of a server function. Calling Ack on options that 150 | // aren't from a server method implementation is a no-op. 151 | // 152 | // Example: 153 | // 154 | // func (my *myServer) MyMethod(call schema.MyServer_myMethod) error { 155 | // server.Ack(call.Options) 156 | // // ... do long-running operation ... 157 | // return nil 158 | // } 159 | // 160 | // Ack need not be the first call in a function nor is it required. 161 | // Since the function's return is also an acknowledgement of delivery, 162 | // short functions can return without calling Ack. However, since 163 | // clients will not return an Answer until the delivery is acknowledged, 164 | // it is advisable to ack early. 165 | func Ack(opts capnp.CallOptions) { 166 | if ack, _ := opts.Value(ackSignalKey).(*ackSignal); ack != nil { 167 | ack.signal() 168 | } 169 | } 170 | 171 | type call struct { 172 | *capnp.Call 173 | ans fulfiller.Fulfiller 174 | method *Method 175 | } 176 | 177 | func newCall(cl *capnp.Call, sm *Method) *call { 178 | return &call{Call: cl, method: sm} 179 | } 180 | 181 | type sortedMethods []Method 182 | 183 | // find returns the method with the given ID or nil. 184 | func (sm sortedMethods) find(id *capnp.Method) *Method { 185 | i := sort.Search(len(sm), func(i int) bool { 186 | m := &sm[i] 187 | if m.InterfaceID != id.InterfaceID { 188 | return m.InterfaceID >= id.InterfaceID 189 | } 190 | return m.MethodID >= id.MethodID 191 | }) 192 | if i == len(sm) { 193 | return nil 194 | } 195 | m := &sm[i] 196 | if m.InterfaceID != id.InterfaceID || m.MethodID != id.MethodID { 197 | return nil 198 | } 199 | return m 200 | } 201 | 202 | func (sm sortedMethods) Len() int { 203 | return len(sm) 204 | } 205 | 206 | func (sm sortedMethods) Less(i, j int) bool { 207 | if id1, id2 := sm[i].InterfaceID, sm[j].InterfaceID; id1 != id2 { 208 | return id1 < id2 209 | } 210 | return sm[i].MethodID < sm[j].MethodID 211 | } 212 | 213 | func (sm sortedMethods) Swap(i, j int) { 214 | sm[i], sm[j] = sm[j], sm[i] 215 | } 216 | 217 | type ackSignal struct { 218 | c chan struct{} 219 | once sync.Once 220 | } 221 | 222 | func newAckSignal() *ackSignal { 223 | return &ackSignal{c: make(chan struct{})} 224 | } 225 | 226 | func (ack *ackSignal) signal() { 227 | ack.once.Do(func() { 228 | close(ack.c) 229 | }) 230 | } 231 | 232 | // callOptionKey is the unexported key type for predefined options. 233 | type callOptionKey int 234 | 235 | // Predefined call options 236 | const ( 237 | invalidOptionKey callOptionKey = iota 238 | ackSignalKey 239 | ) 240 | 241 | var errClosed = errors.New("capnp: server closed") 242 | -------------------------------------------------------------------------------- /server/server_test.go: -------------------------------------------------------------------------------- 1 | package server_test 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "golang.org/x/net/context" 8 | air "zombiezen.com/go/capnproto/internal/aircraftlib" 9 | . "zombiezen.com/go/capnproto/server" 10 | ) 11 | 12 | type echoImpl struct{} 13 | 14 | func (echoImpl) Echo(call air.Echo_echo) error { 15 | in, err := call.Params.In() 16 | if err != nil { 17 | return err 18 | } 19 | call.Results.SetOut(in + in) 20 | return nil 21 | } 22 | 23 | func TestServerCall(t *testing.T) { 24 | echo := air.Echo_ServerToClient(echoImpl{}) 25 | 26 | result, err := echo.Echo(context.Background(), func(p air.Echo_echo_Params) error { 27 | err := p.SetIn("foo") 28 | return err 29 | }).Struct() 30 | 31 | if err != nil { 32 | t.Errorf("echo.Echo() error: %v", err) 33 | } 34 | if out, err := result.Out(); err != nil { 35 | t.Errorf("echo.Echo() error: %v", err) 36 | } else if out != "foofoo" { 37 | t.Errorf("echo.Echo() = %q; want %q", out, "foofoo") 38 | } 39 | } 40 | 41 | type callSeq uint32 42 | 43 | func (seq *callSeq) GetNumber(call air.CallSequence_getNumber) error { 44 | call.Results.SetN(uint32(*seq)) 45 | *seq++ 46 | return nil 47 | } 48 | 49 | type lockCallSeq struct { 50 | n uint32 51 | mu sync.Mutex 52 | } 53 | 54 | func (seq *lockCallSeq) GetNumber(call air.CallSequence_getNumber) error { 55 | seq.mu.Lock() 56 | defer seq.mu.Unlock() 57 | Ack(call.Options) 58 | 59 | call.Results.SetN(seq.n) 60 | seq.n++ 61 | return nil 62 | } 63 | 64 | func TestServerCallOrder(t *testing.T) { 65 | testCallOrder(t, air.CallSequence_ServerToClient(new(callSeq))) 66 | } 67 | 68 | func TestServerCallOrderWithCustomLocks(t *testing.T) { 69 | testCallOrder(t, air.CallSequence_ServerToClient(new(lockCallSeq))) 70 | } 71 | 72 | func testCallOrder(t *testing.T, seq air.CallSequence) { 73 | ctx := context.Background() 74 | send := func() air.CallSequence_getNumber_Results_Promise { 75 | return seq.GetNumber(ctx, func(air.CallSequence_getNumber_Params) error { return nil }) 76 | } 77 | check := func(p air.CallSequence_getNumber_Results_Promise, n uint32) { 78 | result, err := p.Struct() 79 | if err != nil { 80 | t.Errorf("seq.getNumber() error: %v; want %d", err, n) 81 | } else if result.N() != n { 82 | t.Errorf("seq.getNumber() = %d; want %d", result.N(), n) 83 | } 84 | } 85 | 86 | call0 := send() 87 | call1 := send() 88 | call2 := send() 89 | call3 := send() 90 | call4 := send() 91 | 92 | check(call0, 0) 93 | check(call1, 1) 94 | check(call2, 2) 95 | check(call3, 3) 96 | check(call4, 4) 97 | } 98 | -------------------------------------------------------------------------------- /strings.go: -------------------------------------------------------------------------------- 1 | // +build !nocapnpstrings 2 | 3 | package capnp 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // String returns the address in hex format. 10 | func (addr Address) String() string { 11 | return fmt.Sprintf("%#016x", uint64(addr)) 12 | } 13 | 14 | // GoString returns the address in hex format. 15 | func (addr Address) GoString() string { 16 | return fmt.Sprintf("capnp.Address(%#016x)", uint64(addr)) 17 | } 18 | 19 | // String returns the size in the format "X bytes". 20 | func (sz Size) String() string { 21 | if sz == 1 { 22 | return "1 byte" 23 | } 24 | return fmt.Sprintf("%d bytes", sz) 25 | } 26 | 27 | // GoString returns the size as a Go expression. 28 | func (sz Size) GoString() string { 29 | return fmt.Sprintf("capnp.Size(%d)", sz) 30 | } 31 | 32 | // String returns the offset in the format "+X bytes". 33 | func (off DataOffset) String() string { 34 | if off == 1 { 35 | return "+1 byte" 36 | } 37 | return fmt.Sprintf("+%d bytes", off) 38 | } 39 | 40 | // GoString returns the offset as a Go expression. 41 | func (off DataOffset) GoString() string { 42 | return fmt.Sprintf("capnp.DataOffset(%d)", off) 43 | } 44 | 45 | // String returns a short, human readable representation of the object 46 | // size. 47 | func (sz ObjectSize) String() string { 48 | return fmt.Sprintf("{datasz=%d ptrs=%d}", sz.DataSize, sz.PointerCount) 49 | } 50 | 51 | // GoString formats the ObjectSize as a keyed struct literal. 52 | func (sz ObjectSize) GoString() string { 53 | return fmt.Sprintf("capnp.ObjectSize{DataSize: %d, PointerCount: %d}", sz.DataSize, sz.PointerCount) 54 | } 55 | 56 | // String returns the offset in the format "bit X". 57 | func (bit BitOffset) String() string { 58 | return fmt.Sprintf("bit %d", bit) 59 | } 60 | 61 | // GoString returns the offset as a Go expression. 62 | func (bit BitOffset) GoString() string { 63 | return fmt.Sprintf("capnp.BitOffset(%d)", bit) 64 | } 65 | 66 | // String returns the ID in the format "capability X". 67 | func (id CapabilityID) String() string { 68 | return fmt.Sprintf("capability %d", id) 69 | } 70 | 71 | // GoString returns the ID as a Go expression. 72 | func (id CapabilityID) GoString() string { 73 | return fmt.Sprintf("capnp.CapabilityID(%d)", id) 74 | } 75 | 76 | // GoString formats the pointer as a call to one of the rawPointer 77 | // construction functions. 78 | func (p rawPointer) GoString() string { 79 | if p == 0 { 80 | return "rawPointer(0)" 81 | } 82 | switch p.pointerType() { 83 | case structPointer: 84 | return fmt.Sprintf("rawStructPointer(%d, %#v)", p.offset(), p.structSize()) 85 | case listPointer: 86 | var lt string 87 | switch p.listType() { 88 | case voidList: 89 | lt = "voidList" 90 | case bit1List: 91 | lt = "bit1List" 92 | case byte1List: 93 | lt = "byte1List" 94 | case byte2List: 95 | lt = "byte2List" 96 | case byte4List: 97 | lt = "byte4List" 98 | case byte8List: 99 | lt = "byte8List" 100 | case pointerList: 101 | lt = "pointerList" 102 | case compositeList: 103 | lt = "compositeList" 104 | } 105 | return fmt.Sprintf("rawListPointer(%d, %s, %d)", p.offset(), lt, p.numListElements()) 106 | case farPointer: 107 | return fmt.Sprintf("rawFarPointer(%d, %v)", p.farSegment(), p.farAddress()) 108 | case doubleFarPointer: 109 | return fmt.Sprintf("rawDoubleFarPointer(%d, %v)", p.farSegment(), p.farAddress()) 110 | default: 111 | // other pointer 112 | if p.otherPointerType() != 0 { 113 | return fmt.Sprintf("rawPointer(%#016x)", uint64(p)) 114 | } 115 | return fmt.Sprintf("rawInterfacePointer(%d)", p.capabilityIndex()) 116 | } 117 | } 118 | 119 | func (ssa *singleSegmentArena) String() string { 120 | return fmt.Sprintf("single-segment arena [len=%d cap=%d]", len(*ssa), cap(*ssa)) 121 | } 122 | 123 | func (msa *multiSegmentArena) String() string { 124 | return fmt.Sprintf("multi-segment arena [%d segments]", len(*msa)) 125 | } 126 | -------------------------------------------------------------------------------- /struct.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // Struct is a pointer to a struct. 4 | type Struct struct { 5 | seg *Segment 6 | off Address 7 | size ObjectSize 8 | flags structFlags 9 | } 10 | 11 | // NewStruct creates a new struct, preferring placement in s. 12 | func NewStruct(s *Segment, sz ObjectSize) (Struct, error) { 13 | if !sz.isValid() { 14 | return Struct{}, errObjectSize 15 | } 16 | sz.DataSize = sz.DataSize.padToWord() 17 | seg, addr, err := alloc(s, sz.totalSize()) 18 | if err != nil { 19 | return Struct{}, err 20 | } 21 | return Struct{ 22 | seg: seg, 23 | off: addr, 24 | size: sz, 25 | }, nil 26 | } 27 | 28 | // NewRootStruct creates a new struct, preferring placement in s, then sets the 29 | // message's root to the new struct. 30 | func NewRootStruct(s *Segment, sz ObjectSize) (Struct, error) { 31 | st, err := NewStruct(s, sz) 32 | if err != nil { 33 | return st, err 34 | } 35 | if err := s.msg.SetRoot(st); err != nil { 36 | return st, err 37 | } 38 | return st, nil 39 | } 40 | 41 | // ToStruct attempts to convert p into a struct. If p is not a valid 42 | // struct, then it returns an invalid Struct. 43 | func ToStruct(p Pointer) Struct { 44 | if !IsValid(p) { 45 | return Struct{} 46 | } 47 | s, ok := p.underlying().(Struct) 48 | if !ok { 49 | return Struct{} 50 | } 51 | return s 52 | } 53 | 54 | // ToStructDefault attempts to convert p into a struct, reading the 55 | // default value from def if p is not a struct. 56 | func ToStructDefault(p Pointer, def []byte) (Struct, error) { 57 | fallback := func() (Struct, error) { 58 | if def == nil { 59 | return Struct{}, nil 60 | } 61 | defp, err := unmarshalDefault(def) 62 | if err != nil { 63 | return Struct{}, err 64 | } 65 | return ToStruct(defp), nil 66 | } 67 | if !IsValid(p) { 68 | return fallback() 69 | } 70 | s, ok := p.underlying().(Struct) 71 | if !ok { 72 | return fallback() 73 | } 74 | return s, nil 75 | } 76 | 77 | // Segment returns the segment this pointer came from. 78 | func (p Struct) Segment() *Segment { 79 | return p.seg 80 | } 81 | 82 | // Address returns the address the pointer references. 83 | func (p Struct) Address() Address { 84 | return p.off 85 | } 86 | 87 | // HasData reports whether the struct has a non-zero size. 88 | func (p Struct) HasData() bool { 89 | return !p.size.isZero() 90 | } 91 | 92 | // value returns a raw struct pointer. 93 | func (p Struct) value(paddr Address) rawPointer { 94 | off := makePointerOffset(paddr, p.off) 95 | return rawStructPointer(off, p.size) 96 | } 97 | 98 | func (p Struct) underlying() Pointer { 99 | return p 100 | } 101 | 102 | // Pointer returns the i'th pointer in the struct. 103 | func (p Struct) Pointer(i uint16) (Pointer, error) { 104 | if p.seg == nil || i >= p.size.PointerCount { 105 | return nil, nil 106 | } 107 | return p.seg.readPtr(p.pointerAddress(i)) 108 | } 109 | 110 | // SetPointer sets the i'th pointer in the struct to src. 111 | func (p Struct) SetPointer(i uint16, src Pointer) error { 112 | if p.seg == nil || i >= p.size.PointerCount { 113 | panic(errOutOfBounds) 114 | } 115 | return p.seg.writePtr(copyContext{}, p.pointerAddress(i), src) 116 | } 117 | 118 | func (p Struct) pointerAddress(i uint16) Address { 119 | ptrStart := p.off.addSize(p.size.DataSize) 120 | return ptrStart.element(int32(i), wordSize) 121 | } 122 | 123 | // bitInData reports whether bit is inside p's data section. 124 | func (p Struct) bitInData(bit BitOffset) bool { 125 | return p.seg != nil && bit < BitOffset(p.size.DataSize*8) 126 | } 127 | 128 | // Bit returns the bit that is n bits from the start of the struct. 129 | func (p Struct) Bit(n BitOffset) bool { 130 | if !p.bitInData(n) { 131 | return false 132 | } 133 | addr := p.off.addOffset(n.offset()) 134 | return p.seg.readUint8(addr)&n.mask() != 0 135 | } 136 | 137 | // SetBit sets the bit that is n bits from the start of the struct to v. 138 | func (p Struct) SetBit(n BitOffset, v bool) { 139 | if !p.bitInData(n) { 140 | panic(errOutOfBounds) 141 | } 142 | addr := p.off.addOffset(n.offset()) 143 | b := p.seg.readUint8(addr) 144 | if v { 145 | b |= n.mask() 146 | } else { 147 | b &^= n.mask() 148 | } 149 | p.seg.writeUint8(addr, b) 150 | } 151 | 152 | func (p Struct) dataAddress(off DataOffset, sz Size) (addr Address, ok bool) { 153 | if p.seg == nil || Size(off)+sz > p.size.DataSize { 154 | return 0, false 155 | } 156 | return p.off.addOffset(off), true 157 | } 158 | 159 | // Uint8 returns an 8-bit integer from the struct's data section. 160 | func (p Struct) Uint8(off DataOffset) uint8 { 161 | addr, ok := p.dataAddress(off, 1) 162 | if !ok { 163 | return 0 164 | } 165 | return p.seg.readUint8(addr) 166 | } 167 | 168 | // Uint16 returns a 16-bit integer from the struct's data section. 169 | func (p Struct) Uint16(off DataOffset) uint16 { 170 | addr, ok := p.dataAddress(off, 2) 171 | if !ok { 172 | return 0 173 | } 174 | return p.seg.readUint16(addr) 175 | } 176 | 177 | // Uint32 returns a 32-bit integer from the struct's data section. 178 | func (p Struct) Uint32(off DataOffset) uint32 { 179 | addr, ok := p.dataAddress(off, 4) 180 | if !ok { 181 | return 0 182 | } 183 | return p.seg.readUint32(addr) 184 | } 185 | 186 | // Uint64 returns a 64-bit integer from the struct's data section. 187 | func (p Struct) Uint64(off DataOffset) uint64 { 188 | addr, ok := p.dataAddress(off, 8) 189 | if !ok { 190 | return 0 191 | } 192 | return p.seg.readUint64(addr) 193 | } 194 | 195 | // SetUint8 sets the 8-bit integer that is off bytes from the start of the struct to v. 196 | func (p Struct) SetUint8(off DataOffset, v uint8) { 197 | addr, ok := p.dataAddress(off, 1) 198 | if !ok { 199 | panic(errOutOfBounds) 200 | } 201 | p.seg.writeUint8(addr, v) 202 | } 203 | 204 | // SetUint16 sets the 16-bit integer that is off bytes from the start of the struct to v. 205 | func (p Struct) SetUint16(off DataOffset, v uint16) { 206 | addr, ok := p.dataAddress(off, 2) 207 | if !ok { 208 | panic(errOutOfBounds) 209 | } 210 | p.seg.writeUint16(addr, v) 211 | } 212 | 213 | // SetUint32 sets the 32-bit integer that is off bytes from the start of the struct to v. 214 | func (p Struct) SetUint32(off DataOffset, v uint32) { 215 | addr, ok := p.dataAddress(off, 4) 216 | if !ok { 217 | panic(errOutOfBounds) 218 | } 219 | p.seg.writeUint32(addr, v) 220 | } 221 | 222 | // SetUint64 sets the 64-bit integer that is off bytes from the start of the struct to v. 223 | func (p Struct) SetUint64(off DataOffset, v uint64) { 224 | addr, ok := p.dataAddress(off, 8) 225 | if !ok { 226 | panic(errOutOfBounds) 227 | } 228 | p.seg.writeUint64(addr, v) 229 | } 230 | 231 | // structFlags is a bitmask of flags for a pointer. 232 | type structFlags uint8 233 | 234 | // Pointer flags. 235 | const ( 236 | isListMember structFlags = 1 << iota 237 | ) 238 | 239 | // copyStruct makes a deep copy of src into dst. 240 | func copyStruct(cc copyContext, dst, src Struct) error { 241 | if dst.seg == nil { 242 | return nil 243 | } 244 | 245 | // Q: how does version handling happen here, when the 246 | // destination toData[] slice can be bigger or smaller 247 | // than the source data slice, which is in 248 | // src.seg.Data[src.off:src.off+src.size.DataSize] ? 249 | // 250 | // A: Newer fields only come *after* old fields. Note that 251 | // copy only copies min(len(src), len(dst)) size, 252 | // and then we manually zero the rest in the for loop 253 | // that writes toData[j] = 0. 254 | // 255 | 256 | // data section: 257 | srcData := src.seg.slice(src.off, src.size.DataSize) 258 | dstData := dst.seg.slice(dst.off, dst.size.DataSize) 259 | copyCount := copy(dstData, srcData) 260 | dstData = dstData[copyCount:] 261 | for j := range dstData { 262 | dstData[j] = 0 263 | } 264 | 265 | // ptrs section: 266 | 267 | // version handling: we ignore any extra-newer-pointers in src, 268 | // i.e. the case when srcPtrSize > dstPtrSize, by only 269 | // running j over the size of dstPtrSize, the destination size. 270 | srcPtrSect := src.off.addSize(src.size.DataSize) 271 | dstPtrSect := dst.off.addSize(dst.size.DataSize) 272 | numSrcPtrs := src.size.PointerCount 273 | numDstPtrs := dst.size.PointerCount 274 | for j := uint16(0); j < numSrcPtrs && j < numDstPtrs; j++ { 275 | srcAddr := srcPtrSect.element(int32(j), wordSize) 276 | dstAddr := dstPtrSect.element(int32(j), wordSize) 277 | m, err := src.seg.readPtr(srcAddr) 278 | if err != nil { 279 | return err 280 | } 281 | err = dst.seg.writePtr(cc.incDepth(), dstAddr, m) 282 | if err != nil { 283 | return err 284 | } 285 | } 286 | for j := numSrcPtrs; j < numDstPtrs; j++ { 287 | // destination p is a newer version than source so these extra new pointer fields in p must be zeroed. 288 | addr := dstPtrSect.element(int32(j), wordSize) 289 | dst.seg.writeRawPointer(addr, 0) 290 | } 291 | // Nothing more here: so any other pointers in srcPtrSize beyond 292 | // those in dstPtrSize are ignored and discarded. 293 | 294 | return nil 295 | } 296 | -------------------------------------------------------------------------------- /travis-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | # Install gcc 5 | sudo apt-get install -qq g++-4.8 libstdc++-4.8-dev 6 | sudo update-alternatives --quiet --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.8 --slave /usr/bin/gcov gcov /usr/bin/gcov-4.8 7 | sudo update-alternatives --quiet --set gcc /usr/bin/gcc-4.8 8 | 9 | # Install capnp 10 | cd "$HOME" 11 | wget -O capnproto.tar.gz https://capnproto.org/capnproto-c++-0.5.1.2.tar.gz 12 | tar zxf capnproto.tar.gz 13 | cd capnproto-c++-0.5.1.2 14 | ./configure && make -j2 check 15 | sudo make install 16 | 17 | # Install go-capnproto 18 | export GOPATH="$HOME/gopath" 19 | mkdir -p "$GOPATH/src/zombiezen.com/go" 20 | mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/zombiezen.com/go/capnproto" 21 | go get -v -t -d zombiezen.com/go/capnproto 22 | --------------------------------------------------------------------------------