├── .github ├── FUNDING.yml └── workflows │ └── go.yml ├── .gitignore ├── AUTHORS ├── CHANGELOG.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── address.go ├── address_test.go ├── answer.go ├── answer_test.go ├── answerqueue.go ├── arena.go ├── arena_test.go ├── canonical.go ├── canonical_test.go ├── capability.go ├── capability_test.go ├── capnpc-go ├── .gitignore ├── any_pointer.go ├── capnpc-go.go ├── capnpc-go_test.go ├── embed.go ├── fileparts.go ├── nodes.go ├── templateparams.go ├── templates │ ├── _checktag │ ├── _hasfield │ ├── _interfaceMethod │ ├── _settag │ ├── _typeid │ ├── annotation │ ├── baseStructFuncs │ ├── constants │ ├── enum │ ├── interfaceClient │ ├── interfaceList │ ├── interfaceServer │ ├── listValue │ ├── pointerValue │ ├── promise │ ├── promiseCapabilityField │ ├── promiseFieldAnyList │ ├── promiseFieldAnyPointer │ ├── promiseFieldAnyStruct │ ├── promiseFieldInterface │ ├── promiseFieldStruct │ ├── promiseGroup │ ├── schemaVar │ ├── structAnyListField │ ├── structAnyStructField │ ├── structBoolField │ ├── structCapabilityField │ ├── structDataField │ ├── structEnums │ ├── structFloatField │ ├── structFuncs │ ├── structGroup │ ├── structIntField │ ├── structInterfaceField │ ├── structList │ ├── structListField │ ├── structPointerField │ ├── structStructField │ ├── structTextField │ ├── structTypes │ ├── structUintField │ ├── structValue │ └── structVoidField └── testdata │ ├── aircraft.capnp.out │ ├── const.capnp │ ├── const.capnp.out │ ├── go.capnp │ ├── go.capnp.out │ ├── group.capnp │ ├── group.capnp.out │ ├── otherscopes.capnp │ ├── persistent-samepkg.capnp │ ├── persistent-simple-and-samepkg.capnp.out │ ├── persistent-simple.capnp │ ├── rpc.capnp.out │ ├── scopes.capnp │ ├── scopes.capnp.out │ ├── util.capnp │ └── util.capnp.out ├── captable.go ├── captable_test.go ├── codec.go ├── codec_test.go ├── doc.go ├── docs ├── Getting-Started.md ├── Installation.md ├── RPC-and-Capability-Oriented-Design.md ├── Remote-Procedure-Calls-using-Interfaces.md ├── Working-with-Capn-Proto-Types.md └── Writing-Schemas-and-Generating-Code.md ├── encoding └── text │ ├── marshal.go │ ├── marshal_test.go │ └── testdata │ ├── txt.capnp │ └── txt.capnp.out ├── error.go ├── example ├── books │ ├── README.md │ ├── books │ │ ├── .gitignore │ │ ├── books.capnp │ │ ├── go.mod │ │ └── go.sum │ ├── ex1 │ │ ├── .gitignore │ │ ├── books1.go │ │ ├── go.mod │ │ └── go.sum │ └── ex2 │ │ ├── .gitignore │ │ ├── book.txt │ │ ├── books2.go │ │ ├── go.mod │ │ └── go.sum └── hashes │ ├── .gitignore │ ├── README.md │ ├── cmd │ └── hashesserver.go │ ├── go.mod │ ├── go.sum │ └── hashes.capnp ├── example_test.go ├── exc ├── exc.go ├── exc_test.go ├── types.go └── types_test.go ├── exp ├── bufferpool │ ├── pool.go │ ├── pool_internal_test.go │ └── pool_test.go ├── clock │ ├── clock.go │ └── clock_test.go ├── mpsc │ ├── mpsc.go │ └── mpsc_test.go └── spsc │ ├── spsc.go │ └── spsc_test.go ├── flowcontrol ├── bbr │ ├── doc.go │ ├── filter.go │ ├── filter_test.go │ ├── limiter.go │ ├── limiter_test.go │ ├── queue.go │ ├── queue_test.go │ ├── snapshot.go │ ├── state.go │ ├── trace_test.go │ └── util_test.go ├── fixed.go ├── fixed_test.go ├── flowlimiter.go ├── internal │ └── test-tool │ │ ├── .gitignore │ │ ├── main.go │ │ ├── plot.py │ │ ├── writer.capnp │ │ └── writer.capnp.go ├── nop.go └── tracing │ └── tracelimiter.go ├── gen.go ├── go.mod ├── go.sum ├── integration_test.go ├── integrationutil_test.go ├── internal ├── aircraftlib │ ├── aircraft.capnp │ ├── aircraft.capnp.go │ ├── aircraft_test.go │ └── generate.go ├── capnptool │ └── capnptool.go ├── demo │ ├── book_test.go │ ├── books │ │ ├── books.capnp │ │ ├── books.capnp.go │ │ └── gen.go │ └── doc.go ├── fuzztest │ ├── corpus │ │ ├── blob │ │ ├── bool │ │ ├── boolf │ │ ├── boolvec │ │ ├── datavec │ │ ├── f32 │ │ ├── f32vec │ │ ├── f64 │ │ ├── f64vec │ │ ├── i16 │ │ ├── i16vec │ │ ├── i32 │ │ ├── i32vec │ │ ├── i64 │ │ ├── i64vec │ │ ├── i8 │ │ ├── i8vec │ │ ├── text │ │ ├── textvec │ │ ├── u16 │ │ ├── u16vec │ │ ├── u32 │ │ ├── u32vec │ │ ├── u64 │ │ ├── u64vec │ │ ├── u8 │ │ ├── u8vec │ │ ├── void │ │ ├── zvec │ │ └── zz │ └── fuzztest.go ├── gen │ └── gen.go ├── nodemap │ └── nodemap.go ├── rc │ └── rc.go ├── schema │ └── schema.capnp.go ├── str │ └── str.go ├── strquote │ ├── strquote.go │ └── strquote_test.go └── syncutil │ └── with.go ├── list-gen.go ├── list.go ├── list_test.go ├── localpromise.go ├── message.go ├── message_test.go ├── metadata.go ├── msgp_bench_gen_test.go ├── msgp_bench_test.go ├── packed ├── fuzz.go ├── packed.go ├── packed_test.go └── testdata │ └── corpus │ ├── 03bf57ea7bdec71698adeae87a862cec7164d40b-3 │ ├── 0d25a482d77c50ce11c4e19513273663cb7f2760-3 │ ├── 0ea83e2670b5efd4c1488e7d3b8d47444edcc0de-2 │ ├── 0ebc873dc501946f782b80d9ac30e458e3f4c3bb-1 │ ├── 11e6f1cf4e924b97fa749270ef3776ca2f622e4a-1 │ ├── 1a03558a19149e8a602fa447a75203e565c5bd24-6 │ ├── 1cbdf332ea41a8607123a73db6848cb1e08a88e8-7 │ ├── 1f0255d132314eceb4cb53f8b7edba2b5acca411-5 │ ├── 25e8b15e130bd6fbac4db86667c6585220e649fd-6 │ ├── 27602d7c8f418c1648200cbbb80652dfa1d32ec3-5 │ ├── 309aa6808b3440a343e40d2539c4a8f32a7d22c6-7 │ ├── 32126b1f830c3950f99f7d6c9d4fd501c50fc95a-3 │ ├── 36c4bfda59a7f22e1d84c2559edf589e1591ab4e-5 │ ├── 386c071e2d382223ad16da6458ac74ab61995d22-1 │ ├── 397ed0b402b6615879ec32c874ad2ce1189a5566 │ ├── 3dedffca29bf233d7aab140d1ebe4f7a393aaf25-1 │ ├── 43275097819916f57c004e3d2d7292cd4c494103-2 │ ├── 46758529a6f8bdab34817ecc3b60bdeb8fc7d1df-2 │ ├── 46d2e26b94a1e361bbb9561f6bf82f709952079a-1 │ ├── 4fbde0ace86d682a07e20940ff917f4c6243c848-1 │ ├── 53ff759b338edc76af39e47cba96506942f8fcf4-1 │ ├── 5b7a93398637f041f5b2aa27f5506e070c4ab93a-10 │ ├── 62b362cbeb20f5993b35fa4104af45169e729567-5 │ ├── 6545fc074cb9ff87f00d92da2bb0e43fb655a1a0-8 │ ├── 6648f3613b1f7b827c1f8e57dfd6d98297afc120-3 │ ├── 672e3be57da3f6260bd1224ab122c43db7531c68-1 │ ├── 685749732874f297765f6aa9cb2149ae8a760a30-2 │ ├── 6b71d6625925391d3e5d5df85ed6520b3bdff697-5 │ ├── 6d7d243c36e5b97f088139d68d659a76d8f1a85e-3 │ ├── 733795216f2196269dfb2230101048a8d1a900ef-4 │ ├── 79e96c2c0556cf294cb5ccc84fbe6f688b722a4b-5 │ ├── 82449e84f12b12e2e78cd1837063af01f771a8c3-4 │ ├── 85c90a0758e25bc682dc2f169cc60394b1aef429-1 │ ├── 86bb19d6ebfdcb2986e2f5f19e8b1d064bb143b6 │ ├── 8f842db7cc972786baa4aef0bb39cedd92313ff0-6 │ ├── 928cde18fcd017b7d9f617e6efb1ad3d335106d9-9 │ ├── 950b28b26d2efaa8ca8b77551c9cc4fb9bfa73c7-1 │ ├── 95fb90dbbc7bd9d57a1c3c43fa2d9c0370f26f36-6 │ ├── 96e65fffcc37d53d86825858d055dac113e71283-1 │ ├── 97fdb523adca4cf3c5d71dbab9de9507b480152b-2 │ ├── 9a7501617cc3b5f64b6940734fe49de0458fa866-8 │ ├── a188a06676ab0e07a1922dee6ecc00957e9222cf-3 │ ├── a2df0bcacc5318483ed62a88b7b27986e4e101a9-3 │ ├── b4ec4c0d6ffdae8d6ca8a15b03e2ce94ffda6185-7 │ ├── b51b0adced7f2ed05a9fb588dddac5573d13fffb-10 │ ├── bf380df4c460fd5a77baea70c5d2eabed6a955a4-4 │ ├── c0901a103fd4ab6c7679be138aac5f02d006b382-3 │ ├── c7d6fe913d12c724c7f902cdf1a330c66a08c5ef-5 │ ├── dae7780e3c39c06f06b99d50e5f03f9eb8419867-9 │ ├── dc3509027712532d00a7e0f4ba924d7f75281019-2 │ ├── df61e7c056c523a702b8098125570d13cf47ea06-6 │ ├── e02bb49673980eb7f449d5281b965cbc1301e549-4 │ ├── e636ce666b77bbc7ddfab8f464be687aa5daff48-2 │ ├── ef6fb041e0fda30b19f739195125a612ccd325d8-1 │ ├── empty.dat │ ├── f0e5464685f75b1a98506138e11267a49bf18a2a-2 │ ├── f5869ee9335f12414c447da8226e95ce31b0d333-1 │ ├── f74fb617fe3663c2f51026f6b3bc2f78f6a13b30-4 │ ├── mixed.dat │ ├── mixed_mixed.dat │ ├── mixed_nozero.dat │ ├── mixed_zero_zero_zero_mixed.dat │ ├── nozero.dat │ ├── nozero_nozero.dat │ ├── nozero_nozero_mixed_nozero_mixed.dat │ ├── nozero_nozero_nozero_nozero.dat │ ├── nozero_nozero_nozero_nozero_mixed.dat │ ├── realworld.dat │ ├── shortbenchmark.dat │ ├── zero.dat │ ├── zero_nozero.dat │ └── zero_zero_zero_zero.dat ├── pogs ├── bench_test.go ├── doc.go ├── embed_test.go ├── example_test.go ├── extract.go ├── fields.go ├── insert.go └── pogs_test.go ├── pointer.go ├── pointer_test.go ├── rawpointer.go ├── rawpointer_test.go ├── readlimit_test.go ├── regen.sh ├── request.go ├── resolver.go ├── rpc ├── answer.go ├── bench_test.go ├── errors.go ├── export.go ├── flow_test.go ├── idgen.go ├── idgen_test.go ├── import.go ├── internal │ ├── testcapnp │ │ ├── generate.go │ │ ├── test.capnp │ │ └── test.capnp.go │ └── testnetwork │ │ ├── generate.go │ │ ├── testnetwork.capnp │ │ ├── testnetwork.capnp.go │ │ └── testnetwork.go ├── level0_test.go ├── level1_test.go ├── localpromise_test.go ├── network.go ├── pipeline_chain_test.go ├── question.go ├── receiveranswer_test.go ├── rpc.go ├── rpc_test.go ├── senderpromise_test.go ├── serve.go ├── serve_test.go ├── shutdown_test.go ├── streaming_test.go ├── transport.go └── transport │ ├── errors.go │ ├── pipe.go │ ├── pipe_test.go │ ├── transport.go │ └── transport_test.go ├── schemas ├── schemas.go └── schemas_test.go ├── segment.go ├── segment_test.go ├── server ├── example_test.go ├── server.go └── server_test.go ├── std ├── capnp │ ├── README.md │ ├── c++.capnp │ ├── compat │ │ ├── byte-stream.capnp │ │ ├── bytestream │ │ │ └── byte-stream.capnp.go │ │ ├── http-over-capnp.capnp │ │ ├── httpovercapnp │ │ │ └── http-over-capnp.capnp.go │ │ ├── json-rpc.capnp │ │ ├── json.capnp │ │ ├── json │ │ │ └── json.capnp.go │ │ └── jsonrpc │ │ │ └── json-rpc.capnp.go │ ├── compiler │ │ ├── grammar.capnp │ │ ├── grammar │ │ │ └── grammar.capnp.go │ │ ├── lexer.capnp │ │ └── lexer │ │ │ └── lexer.capnp.go │ ├── cxx │ │ └── c++.capnp.go │ ├── persistent.capnp │ ├── persistent │ │ └── persistent.capnp.go │ ├── rpc-twoparty.capnp │ ├── rpc.capnp │ ├── rpc │ │ ├── exception.go │ │ └── rpc.capnp.go │ ├── rpctwoparty │ │ └── rpc-twoparty.capnp.go │ ├── schema.capnp │ ├── schema │ │ └── schema.capnp.go │ ├── stream.capnp │ └── stream │ │ └── stream.capnp.go ├── gen.sh ├── go.capnp └── go │ └── go.capnp.go ├── struct.go ├── typeparam.go └── util ├── README.md ├── chkfatal.go ├── chkfatal_test.go ├── debugging └── debugging.go ├── deferred └── queue.go ├── exn ├── exn.go ├── exn_test.go └── wrap.go ├── idempotent.go ├── license.txt ├── maps └── maps.go ├── maybe └── maybe.go ├── orerr └── orerr.go ├── rc ├── ref.go └── ref_test.go ├── slices ├── poolslice │ └── poolslice.go └── slices.go ├── sync └── mutex │ └── mutex.go └── thunk ├── thunk.go └── thunk_test.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 2 | - lthibault 3 | - zenhack 4 | - zombiezen 5 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | go: [ '1.22', '1.23' ] 15 | arch: [ 'amd64', '386' ] 16 | name: Go ${{ matrix.go }} GOARCH=${{ matrix.arch }} 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Setup go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version: ${{ matrix.go }} 23 | - name: Set GOARCH 24 | run: | 25 | echo "GOARCH=${{ matrix.arch }}" >> $GITHUB_ENV 26 | - name: Run tests with race detector 27 | if: env.GOARCH != '386' 28 | run: go test -v -race -short ./... 29 | - name: Run all tests 30 | run: go test -v ./... 31 | - name: Run RPC tests repeatedly 32 | run: | 33 | cd rpc 34 | go test -c 35 | for i in `seq 50`; do ./rpc.test; done 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /capnpc-go/capnpc-go 2 | /internal/cmd/mktemplates/mktemplates 3 | TAGS 4 | *~ 5 | *.swp 6 | 7 | # Common extensions for coverage/profling outputs: 8 | *.out 9 | *.cpu 10 | *.mem 11 | 12 | # Test executables: 13 | *.test 14 | 15 | # Bazel 16 | /bazel-bin 17 | /bazel-capnproto2 18 | /bazel-genfiles 19 | /bazel-out 20 | /bazel-testlogs 21 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of go-capnproto authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as one of 6 | # Organization's name 7 | # Individual's name 8 | # Individual's name 9 | # See CONTRIBUTORS for the meaning of multiple email addresses. 10 | 11 | # Please keep the list sorted. 12 | 13 | CloudFlare Inc. 14 | Daniel Darabos 15 | Eran Duchan 16 | Evan Shaw 17 | Google Inc. 18 | Ian Denhardt 19 | James McKaskill 20 | Jason E. Aten 21 | Johan Hernandez 22 | Joonsung Lee 23 | Lev Radomislensky 24 | Louis Thibault 25 | Peter Waldschmidt 26 | Tom Thorogood 27 | TJ Holowaychuk 28 | William Laffin 29 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the go-capnproto repository. 3 | # The AUTHORS file lists the copyright holders; this file 4 | # lists people. For example, Google employees are listed here 5 | # but not in AUTHORS, because Google holds the copyright. 6 | # 7 | # When adding J Random Contributor's name to this file, 8 | # either J's name or J's organization's name should be 9 | # added to the AUTHORS file, depending on whether the 10 | # copyright belongs to the individual or the corporation. 11 | 12 | # Names should be added to this file like so: 13 | # Individual's name 14 | # Individual's name 15 | 16 | # Please keep the list sorted. 17 | 18 | Alan Braithwaite 19 | Albert Strasheim 20 | Daniel Darabos 21 | Eran Duchan 22 | Evan Shaw 23 | Ian Denhardt 24 | James McKaskill 25 | Jason E. Aten 26 | Johan Hernandez 27 | Joonsung Lee 28 | Lev Radomislensky 29 | Louis Thibault 30 | Peter Waldschmidt 31 | Ross Light 32 | Tom Thorogood 33 | TJ Holowaychuk 34 | William Laffin 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /capnpc-go/.gitignore: -------------------------------------------------------------------------------- 1 | /capnpc-go 2 | -------------------------------------------------------------------------------- /capnpc-go/embed.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "strings" 6 | "text/template" 7 | ) 8 | 9 | var ( 10 | //go:embed templates/* 11 | templateFS embed.FS 12 | 13 | templates = template.Must(template.New("").Funcs(template.FuncMap{ 14 | "title": strings.Title, 15 | }).ParseFS(templateFS, "templates/*")) 16 | ) 17 | -------------------------------------------------------------------------------- /capnpc-go/templates/_checktag: -------------------------------------------------------------------------------- 1 | {{if .Field.HasDiscriminant -}} 2 | if capnp.Struct(s).Uint16({{.Node.DiscriminantOffset}}) != {{.Field.DiscriminantValue}} { 3 | panic({{printf "Which() != %s" .Field.Name | printf "%q"}}) 4 | } 5 | {{end -}} 6 | -------------------------------------------------------------------------------- /capnpc-go/templates/_hasfield: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) Has{{.Field.Name|title}}() bool { 2 | {{if .Field.HasDiscriminant -}} 3 | if capnp.Struct(s).Uint16({{.Node.DiscriminantOffset}}) != {{.Field.DiscriminantValue}} { 4 | return false 5 | } 6 | {{end -}} 7 | return capnp.Struct(s).HasPtr({{.Field.Slot.Offset}}) 8 | } 9 | -------------------------------------------------------------------------------- /capnpc-go/templates/_interfaceMethod: -------------------------------------------------------------------------------- 1 | InterfaceID: {{.Interface.Id|printf "%#x"}}, 2 | MethodID: {{.ID}}, 3 | InterfaceName: {{.Interface.DisplayName|printf "%q"}}, 4 | MethodName: {{.OriginalName|printf "%q"}}, 5 | -------------------------------------------------------------------------------- /capnpc-go/templates/_settag: -------------------------------------------------------------------------------- 1 | {{if .Field.HasDiscriminant -}} 2 | capnp.Struct(s).SetUint16({{.Node.DiscriminantOffset}}, {{.Field.DiscriminantValue}}) 3 | {{end -}} 4 | -------------------------------------------------------------------------------- /capnpc-go/templates/_typeid: -------------------------------------------------------------------------------- 1 | // {{.Name}}_TypeID is the unique identifier for the type {{.Name}}. 2 | const {{.Name}}_TypeID = {{.Id|printf "%#x"}} 3 | -------------------------------------------------------------------------------- /capnpc-go/templates/annotation: -------------------------------------------------------------------------------- 1 | const {{.Node.Name}} = uint64({{.Node.Id|printf "%#x"}}) 2 | -------------------------------------------------------------------------------- /capnpc-go/templates/baseStructFuncs: -------------------------------------------------------------------------------- 1 | {{ template "_typeid" .Node }} 2 | 3 | func New{{.Node.Name}}(s *capnp.Segment) ({{.Node.Name}}, error) { 4 | st, err := capnp.NewStruct(s, {{.G.ObjectSize .Node}}) 5 | return {{.Node.Name}}(st), err 6 | } 7 | 8 | func NewRoot{{.Node.Name}}(s *capnp.Segment) ({{.Node.Name}}, error) { 9 | st, err := capnp.NewRootStruct(s, {{.G.ObjectSize .Node}}) 10 | return {{.Node.Name}}(st), err 11 | } 12 | 13 | func ReadRoot{{.Node.Name}}(msg *capnp.Message) ({{.Node.Name}}, error) { 14 | root, err := msg.Root() 15 | return {{.Node.Name}}(root.Struct()), err 16 | } 17 | {{if .StringMethod}} 18 | func (s {{.Node.Name}}) String() string { 19 | str, _ := {{.G.Imports.Text}}.Marshal({{.Node.Id|printf "%#x"}}, capnp.Struct(s)) 20 | return str 21 | } 22 | {{end}} 23 | 24 | func (s {{.Node.Name}}) EncodeAsPtr(seg *capnp.Segment) capnp.Ptr { 25 | return capnp.Struct(s).EncodeAsPtr(seg) 26 | } 27 | 28 | func ({{.Node.Name}}) DecodeFromPtr(p capnp.Ptr) {{.Node.Name}} { 29 | return {{.Node.Name}}(capnp.Struct{}.DecodeFromPtr(p)) 30 | } 31 | 32 | func (s {{.Node.Name}}) ToPtr() capnp.Ptr { 33 | return capnp.Struct(s).ToPtr() 34 | } 35 | -------------------------------------------------------------------------------- /capnpc-go/templates/constants: -------------------------------------------------------------------------------- 1 | {{with .Consts -}} 2 | // Constants defined in {{$.G.Basename}}. 3 | const ( 4 | {{range .}} {{.Name}} = {{$.G.Value . .Const.Type .Const.Value}} 5 | {{end}} 6 | ) 7 | {{end}} 8 | {{with .Vars -}} 9 | // Constants defined in {{$.G.Basename}}. 10 | var ( 11 | {{range .}} {{.Name}} = {{$.G.Value . .Const.Type .Const.Value}} 12 | {{end}} 13 | ) 14 | {{end}} 15 | -------------------------------------------------------------------------------- /capnpc-go/templates/enum: -------------------------------------------------------------------------------- 1 | {{with .Annotations.Doc -}} 2 | // {{.}} 3 | {{end -}} 4 | type {{.Node.Name}} uint16 5 | 6 | {{ template "_typeid" .Node }} 7 | 8 | {{with .EnumValues -}} 9 | // Values of {{$.Node.Name}}. 10 | const ( 11 | {{range . -}} 12 | {{.FullName}} {{$.Node.Name}} = {{.Val}} 13 | {{end}} 14 | ) 15 | 16 | // String returns the enum's constant name. 17 | func (c {{$.Node.Name}}) String() string { 18 | switch c { 19 | {{range . -}} 20 | {{if .Tag}}case {{.FullName}}: return {{printf "%q" .Tag}} 21 | {{end}} 22 | {{- end}} 23 | default: return "" 24 | } 25 | } 26 | 27 | // {{$.Node.Name}}FromString returns the enum value with a name, 28 | // or the zero value if there's no such value. 29 | func {{$.Node.Name}}FromString(c string) {{$.Node.Name}} { 30 | switch c { 31 | {{range . -}} 32 | {{if .Tag}}case {{printf "%q" .Tag}}: return {{.FullName}} 33 | {{end}} 34 | {{- end}} 35 | default: return 0 36 | } 37 | } 38 | {{end}} 39 | 40 | type {{.Node.Name}}_List = capnp.EnumList[{{.Node.Name}}] 41 | 42 | func New{{.Node.Name}}_List(s *capnp.Segment, sz int32) ({{.Node.Name}}_List, error) { 43 | return capnp.NewEnumList[{{.Node.Name}}](s, sz) 44 | } 45 | -------------------------------------------------------------------------------- /capnpc-go/templates/interfaceList: -------------------------------------------------------------------------------- 1 | 2 | // {{.Node.Name}}_List is a list of {{.Node.Name}}. 3 | type {{.Node.Name}}_List = capnp.CapList[{{.Node.Name}}] 4 | 5 | // New{{.Node.Name}}_List creates a new list of {{.Node.Name}}. 6 | func New{{.Node.Name}}_List(s *capnp.Segment, sz int32) ({{.Node.Name}}_List, error) { 7 | l, err := capnp.NewPointerList(s, sz) 8 | return capnp.CapList[{{.Node.Name}}](l), err 9 | } 10 | -------------------------------------------------------------------------------- /capnpc-go/templates/interfaceServer: -------------------------------------------------------------------------------- 1 | // A {{.Node.Name}}_Server is a {{.Node.Name}} with a local implementation. 2 | type {{.Node.Name}}_Server interface { 3 | {{range .Methods}} 4 | {{.Name|title}}({{$.G.Imports.Context}}.Context, {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}) error 5 | {{end}} 6 | } 7 | 8 | // {{.Node.Name}}_NewServer creates a new Server from an implementation of {{.Node.Name}}_Server. 9 | func {{.Node.Name}}_NewServer(s {{.Node.Name}}_Server) *{{.G.Imports.Server}}.Server { 10 | c, _ := s.({{.G.Imports.Server}}.Shutdowner) 11 | return {{.G.Imports.Server}}.New({{.Node.Name}}_Methods(nil, s), s, c) 12 | } 13 | 14 | // {{.Node.Name}}_ServerToClient creates a new Client from an implementation of {{.Node.Name}}_Server. 15 | // The caller is responsible for calling Release on the returned Client. 16 | func {{.Node.Name}}_ServerToClient(s {{.Node.Name}}_Server) {{.Node.Name}} { 17 | return {{.Node.Name}}(capnp.NewClient({{.Node.Name}}_NewServer(s))) 18 | } 19 | 20 | // {{.Node.Name}}_Methods appends Methods to a slice that invoke the methods on s. 21 | // This can be used to create a more complicated Server. 22 | func {{.Node.Name}}_Methods(methods []{{.G.Imports.Server}}.Method, s {{.Node.Name}}_Server) []{{.G.Imports.Server}}.Method { 23 | if cap(methods) == 0 { 24 | methods = make([]{{.G.Imports.Server}}.Method, 0, {{len .Methods}}) 25 | } 26 | {{range .Methods}} 27 | methods = append(methods, {{$.G.Imports.Server}}.Method{ 28 | Method: capnp.Method{ 29 | {{template "_interfaceMethod" .}} 30 | }, 31 | Impl: func(ctx {{$.G.Imports.Context}}.Context, call *{{$.G.Imports.Server}}.Call) error { 32 | return s.{{.Name|title}}(ctx, {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}{call}) 33 | }, 34 | }) 35 | {{end}} 36 | return methods 37 | } 38 | {{range .Methods -}} 39 | {{if eq .Interface.Id $.Node.Id}} 40 | // {{$.Node.Name}}_{{.Name}} holds the state for a server call to {{$.Node.Name}}.{{.Name}}. 41 | // See server.Call for documentation. 42 | type {{$.Node.Name}}_{{.Name}} struct { 43 | *{{$.G.Imports.Server}}.Call 44 | } 45 | 46 | // Args returns the call's arguments. 47 | func (c {{$.Node.Name}}_{{.Name}}) Args() {{$.G.RemoteNodeName .Params $.Node}} { 48 | return {{$.G.RemoteNodeName .Params $.Node}}(c.Call.Args()) 49 | } 50 | 51 | // AllocResults allocates the results struct. 52 | func (c {{$.Node.Name}}_{{.Name}}) AllocResults() ({{$.G.RemoteNodeName .Results $.Node}}, error) { 53 | r, err := c.Call.AllocResults({{$.G.ObjectSize .Results}}) 54 | return {{$.G.RemoteNodeName .Results $.Node}}(r), err 55 | } 56 | {{end}} 57 | {{- end}} 58 | -------------------------------------------------------------------------------- /capnpc-go/templates/listValue: -------------------------------------------------------------------------------- 1 | {{.Typ}}(capnp.MustUnmarshalRoot({{.Value}}).List()) 2 | {{- /* no EOL */ -}} 3 | -------------------------------------------------------------------------------- /capnpc-go/templates/pointerValue: -------------------------------------------------------------------------------- 1 | capnp.MustUnmarshalRoot({{.Value}}) 2 | {{- /* no EOL */ -}} 3 | -------------------------------------------------------------------------------- /capnpc-go/templates/promise: -------------------------------------------------------------------------------- 1 | // {{.Node.Name}}_Future is a wrapper for a {{.Node.Name}} promised by a client call. 2 | type {{.Node.Name}}_Future struct { *capnp.Future } 3 | 4 | func (f {{.Node.Name}}_Future) Struct() ({{.Node.Name}}, error) { 5 | p, err := f.Future.Ptr() 6 | return {{.Node.Name}}(p.Struct()), err 7 | } 8 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseCapabilityField: -------------------------------------------------------------------------------- 1 | func (p {{.Node.Name}}_Future) {{.Field.Name|title}}() capnp.Client { 2 | return p.Future.Field({{.Field.Slot.Offset}}, nil).Client() 3 | } 4 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseFieldAnyList: -------------------------------------------------------------------------------- 1 | func (p {{ .Node.Name }}_Future) {{ .Field.Name|title }}() *capnp.Future { 2 | return p.Future.Field({{ .Field.Slot.Offset }}, nil) 3 | } 4 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseFieldAnyPointer: -------------------------------------------------------------------------------- 1 | func (p {{.Node.Name}}_Future) {{.Field.Name|title}}() *capnp.Future { 2 | return p.Future.Field({{.Field.Slot.Offset}}, nil) 3 | } 4 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseFieldAnyStruct: -------------------------------------------------------------------------------- 1 | func (p {{ .Node.Name }}_Future) {{ .Field.Name|title }}() *capnp.Future { 2 | return p.Future.Field({{ .Field.Slot.Offset }}, nil) 3 | } 4 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseFieldInterface: -------------------------------------------------------------------------------- 1 | func (p {{.Node.Name}}_Future) {{.Field.Name|title}}() {{.G.RemoteNodeName .Interface .Node}} { 2 | return {{.G.RemoteNodeName .Interface .Node}}(p.Future.Field({{.Field.Slot.Offset}}, nil).Client()) 3 | } 4 | 5 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseFieldStruct: -------------------------------------------------------------------------------- 1 | func (p {{.Node.Name}}_Future) {{.Field.Name|title}}() {{.G.RemoteNodeName .Struct .Node}}_Future { 2 | return {{.G.RemoteNodeName .Struct .Node}}_Future{Future: p.Future.Field( 3 | {{- .Field.Slot.Offset}}, {{if .Default.IsValid}}{{.Default}}{{else}}nil{{end}})} 4 | } 5 | -------------------------------------------------------------------------------- /capnpc-go/templates/promiseGroup: -------------------------------------------------------------------------------- 1 | func (p {{.Node.Name}}_Future) {{.Field.Name|title}}() {{.Group.Name}}_Future { return {{.Group.Name}}_Future{p.Future} } 2 | -------------------------------------------------------------------------------- /capnpc-go/templates/schemaVar: -------------------------------------------------------------------------------- 1 | const schema_{{.FileID|printf "%x"}} = {{.SchemaLiteral}} 2 | 3 | func RegisterSchema(reg *{{.G.Imports.Schemas}}.Registry) { 4 | reg.Register(&{{.G.Imports.Schemas}}.Schema{ 5 | String: schema_{{.FileID|printf "%x"}}, 6 | Nodes: []uint64{ 7 | {{- range .NodeIDs}} 8 | {{.|printf "%#x"}}, 9 | {{- end}} 10 | }, 11 | Compressed: true, 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /capnpc-go/templates/structAnyListField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() (capnp.List, error) { 2 | {{template "_checktag" . -}} 3 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | {{if .Default.IsValid -}} 5 | if err != nil { 6 | return capnp.List{}, err 7 | } 8 | return p.ListDefault({{.Default}}) 9 | {{- else -}} 10 | return p.List(), err 11 | {{- end}} 12 | } 13 | 14 | {{template "_hasfield" .}} 15 | 16 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v capnp.List) error { 17 | {{template "_settag" . -}} 18 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, v.ToPtr()) 19 | } 20 | -------------------------------------------------------------------------------- /capnpc-go/templates/structAnyStructField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() (capnp.Struct, error) { 2 | {{template "_checktag" . -}} 3 | {{if .Default.IsValid -}} 4 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 5 | if err != nil { 6 | return capnp.Ptr{}, err 7 | } 8 | return p.StructDefault({{.Default}}) 9 | {{- else -}} 10 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 11 | return p.Struct(), err 12 | {{- end}} 13 | } 14 | 15 | {{template "_hasfield" .}} 16 | 17 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v capnp.Struct) error { 18 | {{template "_settag" . -}} 19 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, v.ToPtr()) 20 | } 21 | -------------------------------------------------------------------------------- /capnpc-go/templates/structBoolField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() bool { 2 | {{template "_checktag" . -}} 3 | return {{if .Default}}!{{end}}capnp.Struct(s).Bit({{.Field.Slot.Offset}}) 4 | } 5 | 6 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v bool) { 7 | {{template "_settag" . -}} 8 | capnp.Struct(s).SetBit({{.Field.Slot.Offset}}, {{if .Default}}!{{end}}v) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /capnpc-go/templates/structCapabilityField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.FieldType}} { 2 | {{template "_checktag" . -}} 3 | p, _ := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | return p.Interface().Client() 5 | } 6 | 7 | {{template "_hasfield" .}} 8 | 9 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(c {{.FieldType}}) error { 10 | {{template "_settag" . -}} 11 | if !c.IsValid() { 12 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, capnp.Ptr{}) 13 | } 14 | seg := s.Segment() 15 | in := capnp.NewInterface(seg, seg.Message().CapTable().Add(c)) 16 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, in.ToPtr()) 17 | } 18 | -------------------------------------------------------------------------------- /capnpc-go/templates/structDataField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.FieldType}}, error) { 2 | {{template "_checktag" . -}} 3 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | {{with .Default -}} 5 | return {{$.FieldType}}(p.DataDefault({{printf "%#v" .}})), err 6 | {{- else -}} 7 | return {{.FieldType}}(p.Data()), err 8 | {{- end}} 9 | } 10 | 11 | {{template "_hasfield" .}} 12 | 13 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error { 14 | {{template "_settag" . -}} 15 | {{if .Default -}} 16 | if v == nil { 17 | v = []byte{} 18 | } 19 | {{end -}} 20 | return capnp.Struct(s).SetData({{.Field.Slot.Offset}}, v) 21 | } 22 | 23 | -------------------------------------------------------------------------------- /capnpc-go/templates/structEnums: -------------------------------------------------------------------------------- 1 | type {{.Node.Name}}_Which uint16 2 | 3 | const ( 4 | {{range .Fields}} {{$.Node.Name}}_Which_{{.Name}} {{$.Node.Name}}_Which = {{.DiscriminantValue}} 5 | {{end}} 6 | ) 7 | 8 | func (w {{.Node.Name}}_Which) String() string { 9 | const s = {{.EnumString.ValueString|printf "%q"}} 10 | switch w { 11 | {{range $i, $f := .Fields}}case {{$.Node.Name}}_Which_{{.Name}}: 12 | return s{{$.EnumString.SliceFor $i}} 13 | {{end}} 14 | } 15 | return "{{.Node.Name}}_Which(" + {{.G.Imports.Strconv}}.FormatUint(uint64(w), 10) + ")" 16 | } 17 | 18 | -------------------------------------------------------------------------------- /capnpc-go/templates/structFloatField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() float{{.Bits}} { 2 | {{template "_checktag" . -}} 3 | return {{.G.Imports.Math}}.Float{{.Bits}}frombits(capnp.Struct(s).Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{printf "%#x" .}}{{end}}) 4 | } 5 | 6 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v float{{.Bits}}) { 7 | {{template "_settag" . -}} 8 | capnp.Struct(s).SetUint{{.Bits}}({{.Offset}}, {{.G.Imports.Math}}.Float{{.Bits}}bits(v){{with .Default}}^{{printf "%#x" .}}{{end}}) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /capnpc-go/templates/structFuncs: -------------------------------------------------------------------------------- 1 | {{if gt .Node.StructNode.DiscriminantCount 0}} 2 | func (s {{.Node.Name}}) Which() {{.Node.Name}}_Which { 3 | return {{.Node.Name}}_Which(capnp.Struct(s).Uint16({{.Node.DiscriminantOffset}})) 4 | } 5 | {{end -}} 6 | 7 | func (s {{.Node.Name}}) IsValid() bool { 8 | return capnp.Struct(s).IsValid() 9 | } 10 | 11 | func (s {{.Node.Name}}) Message() *capnp.Message { 12 | return capnp.Struct(s).Message() 13 | } 14 | 15 | func (s {{.Node.Name}}) Segment() *capnp.Segment { 16 | return capnp.Struct(s).Segment() 17 | } 18 | -------------------------------------------------------------------------------- /capnpc-go/templates/structGroup: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.Group.Name}} { return {{.Group.Name}}(s) } 2 | {{if .Field.HasDiscriminant}} 3 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}() { {{template "_settag" .}} } 4 | {{end}} 5 | -------------------------------------------------------------------------------- /capnpc-go/templates/structIntField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.ReturnType}} { 2 | {{template "_checktag" . -}} 3 | return {{.ReturnType}}(capnp.Struct(s).Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}}) 4 | } 5 | 6 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.ReturnType}}) { 7 | {{template "_settag" . -}} 8 | capnp.Struct(s).SetUint{{.Bits}}({{.Offset}}, uint{{.Bits}}(v){{with .Default}}^{{.}}{{end}}) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /capnpc-go/templates/structInterfaceField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.FieldType}} { 2 | {{template "_checktag" . -}} 3 | p, _ := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | return {{.FieldType}}(p.Interface().Client()) 5 | } 6 | 7 | {{template "_hasfield" .}} 8 | 9 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error { 10 | {{template "_settag" . -}} 11 | if !v.IsValid() { 12 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, capnp.Ptr{}) 13 | } 14 | seg := s.Segment() 15 | in := capnp.NewInterface(seg, seg.Message().CapTable().Add(capnp.Client(v))) 16 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, in.ToPtr()) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /capnpc-go/templates/structList: -------------------------------------------------------------------------------- 1 | // {{.Node.Name}}_List is a list of {{.Node.Name}}. 2 | type {{.Node.Name}}_List = capnp.StructList[{{.Node.Name}}] 3 | 4 | // New{{.Node.Name}} creates a new list of {{.Node.Name}}. 5 | func New{{.Node.Name}}_List(s *capnp.Segment, sz int32) ({{.Node.Name}}_List, error) { 6 | l, err := capnp.NewCompositeList(s, {{.G.ObjectSize .Node}}, sz) 7 | return capnp.StructList[{{.Node.Name}}](l), err 8 | } 9 | -------------------------------------------------------------------------------- /capnpc-go/templates/structListField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.FieldType}}, error) { 2 | {{template "_checktag" . -}} 3 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | {{if .Default.IsValid -}} 5 | if err != nil { 6 | return {{.FieldType}}{}, err 7 | } 8 | l, err := p.ListDefault({{.Default}}) 9 | return {{.FieldType}}(l), err 10 | {{- else -}} 11 | return {{.FieldType}}(p.List()), err 12 | {{- end}} 13 | } 14 | 15 | {{template "_hasfield" .}} 16 | 17 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error { 18 | {{template "_settag" . -}} 19 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, v.ToPtr()) 20 | } 21 | 22 | // New{{.Field.Name|title}} sets the {{.Field.Name}} field to a newly 23 | // allocated {{.FieldType}}, preferring placement in s's segment. 24 | func (s {{.Node.Name}}) New{{.Field.Name|title}}(n int32) ({{.FieldType}}, error) { 25 | {{template "_settag" . -}} 26 | l, err := {{.G.RemoteTypeNew .Field.Slot.Type .Node}}(capnp.Struct(s).Segment(), n) 27 | if err != nil { 28 | return {{.FieldType}}{}, err 29 | } 30 | err = capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, l.ToPtr()) 31 | return l, err 32 | } 33 | -------------------------------------------------------------------------------- /capnpc-go/templates/structPointerField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() (capnp.Ptr, error) { 2 | {{template "_checktag" . -}} 3 | {{if .Default.IsValid -}} 4 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 5 | if err != nil { 6 | return capnp.Ptr{}, err 7 | } 8 | return p.Default({{.Default}}) 9 | {{- else -}} 10 | return capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 11 | {{- end}} 12 | } 13 | 14 | {{template "_hasfield" .}} 15 | 16 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v capnp.Ptr) error { 17 | {{template "_settag" . -}} 18 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, v) 19 | } 20 | -------------------------------------------------------------------------------- /capnpc-go/templates/structStructField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.FieldType}}, error) { 2 | {{template "_checktag" . -}} 3 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | {{if .Default.IsValid -}} 5 | if err != nil { 6 | return {{.FieldType}}{}, err 7 | } 8 | ss, err := p.StructDefault({{.Default}}) 9 | return {{.FieldType}}(ss), err 10 | {{- else -}} 11 | return {{.FieldType}}(p.Struct()), err 12 | {{- end}} 13 | } 14 | 15 | {{template "_hasfield" .}} 16 | 17 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error { 18 | {{template "_settag" . -}} 19 | return capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, capnp.Struct(v).ToPtr()) 20 | } 21 | 22 | // New{{.Field.Name|title}} sets the {{.Field.Name}} field to a newly 23 | // allocated {{.FieldType}} struct, preferring placement in s's segment. 24 | func (s {{.Node.Name}}) New{{.Field.Name|title}}() ({{.FieldType}}, error) { 25 | {{template "_settag" . -}} 26 | ss, err := {{.G.RemoteNodeNew .TypeNode .Node}}(capnp.Struct(s).Segment()) 27 | if err != nil { 28 | return {{.FieldType}}{}, err 29 | } 30 | err = capnp.Struct(s).SetPtr({{.Field.Slot.Offset}}, capnp.Struct(ss).ToPtr()) 31 | return ss, err 32 | } 33 | 34 | -------------------------------------------------------------------------------- /capnpc-go/templates/structTextField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() (string, error) { 2 | {{template "_checktag" . -}} 3 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 4 | {{with .Default -}} 5 | return p.TextDefault({{printf "%q" .}}), err 6 | {{- else -}} 7 | return p.Text(), err 8 | {{- end}} 9 | } 10 | 11 | {{template "_hasfield" .}} 12 | 13 | func (s {{.Node.Name}}) {{.Field.Name|title}}Bytes() ([]byte, error) { 14 | p, err := capnp.Struct(s).Ptr({{.Field.Slot.Offset}}) 15 | {{with .Default -}} 16 | return p.TextBytesDefault({{printf "%q" .}}), err 17 | {{- else -}} 18 | return p.TextBytes(), err 19 | {{- end}} 20 | } 21 | 22 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v string) error { 23 | {{template "_settag" . -}} 24 | {{if .Default -}} 25 | return capnp.Struct(s).SetNewText({{.Field.Slot.Offset}}, v) 26 | {{- else -}} 27 | return capnp.Struct(s).SetText({{.Field.Slot.Offset}}, v) 28 | {{- end}} 29 | } 30 | 31 | -------------------------------------------------------------------------------- /capnpc-go/templates/structTypes: -------------------------------------------------------------------------------- 1 | {{with .Annotations.Doc -}} 2 | // {{.}} 3 | {{end -}} 4 | type {{.Node.Name}} {{if .IsBase -}} 5 | capnp.Struct 6 | {{- else -}} 7 | {{.BaseNode.Name}} 8 | {{- end}} 9 | -------------------------------------------------------------------------------- /capnpc-go/templates/structUintField: -------------------------------------------------------------------------------- 1 | func (s {{.Node.Name}}) {{.Field.Name|title}}() uint{{.Bits}} { 2 | {{template "_checktag" . -}} 3 | return capnp.Struct(s).Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}} 4 | } 5 | 6 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v uint{{.Bits}}) { 7 | {{template "_settag" . -}} 8 | capnp.Struct(s).SetUint{{.Bits}}({{.Offset}}, v{{with .Default}}^{{.}}{{end}}) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /capnpc-go/templates/structValue: -------------------------------------------------------------------------------- 1 | {{.G.RemoteNodeName .Typ .Node}}(capnp.MustUnmarshalRoot({{.Value}}).Struct()) 2 | {{- /* no EOL */ -}} 3 | -------------------------------------------------------------------------------- /capnpc-go/templates/structVoidField: -------------------------------------------------------------------------------- 1 | {{if .Field.HasDiscriminant -}} 2 | func (s {{.Node.Name}}) Set{{.Field.Name|title}}() { 3 | {{template "_settag" .}} 4 | } 5 | 6 | {{end -}} 7 | -------------------------------------------------------------------------------- /capnpc-go/testdata/aircraft.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/aircraft.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/const.capnp: -------------------------------------------------------------------------------- 1 | # Generate scopes.capnp.out with: 2 | # capnp compile -o- scopes.capnp > scopes.capnp.out 3 | # Must run inside this directory to preserve paths. 4 | 5 | using Go = import "go.capnp"; 6 | 7 | @0xc260cb50ae622e10; 8 | 9 | $Go.package("const"); 10 | $Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/const"); 11 | 12 | const answer @0xda96e2255811b258 :Int64 = 42; 13 | const blob @0xe0a385c7be1fea4d :Data = "\x01\x02\x03"; 14 | -------------------------------------------------------------------------------- /capnpc-go/testdata/const.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/const.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/go.capnp: -------------------------------------------------------------------------------- 1 | # Generate go.capnp.out with: 2 | # capnp compile -o- go.capnp > go.capnp.out 3 | # Must run inside this directory to preserve paths. 4 | 5 | @0xd12a1c51fedd6c88; 6 | 7 | annotation package(file) :Text; 8 | annotation import(file) :Text; 9 | annotation doc(struct, field, enum) :Text; 10 | annotation tag(enumerant) :Text; 11 | annotation notag(enumerant) :Void; 12 | annotation customtype(field) :Text; 13 | annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text; 14 | 15 | $package("capnp"); 16 | -------------------------------------------------------------------------------- /capnpc-go/testdata/go.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/go.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/group.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "go.capnp"; 2 | @0x83c2b5818e83ab19; 3 | 4 | $Go.package("template_fix"); 5 | $Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/group"); 6 | 7 | struct SomeMisguidedStruct { 8 | someGroup :group { 9 | someGroupField @0 :UInt64; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /capnpc-go/testdata/group.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/group.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/otherscopes.capnp: -------------------------------------------------------------------------------- 1 | # File to be imported from scopes.capnp. 2 | 3 | using Go = import "go.capnp"; 4 | 5 | @0x9c339b0568fe60ba; 6 | 7 | $Go.package("otherscopes"); 8 | $Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/otherscopes"); 9 | 10 | struct Foo @0xd127518fcfe6191d { 11 | } 12 | -------------------------------------------------------------------------------- /capnpc-go/testdata/persistent-samepkg.capnp: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Sandstorm Development Group, Inc. and contributors 2 | # Licensed under the MIT License: 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | using Go = import "/go.capnp"; 23 | 24 | @0x941fad1fe66eeb50; 25 | 26 | $Go.package("persistent_simple"); 27 | $Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/persistent-simple"); 28 | 29 | interface SomeOtherFile { 30 | } 31 | 32 | struct ThisIsInTheSamePackageAsTheOtherfile { 33 | string @0 :Text; 34 | } 35 | -------------------------------------------------------------------------------- /capnpc-go/testdata/persistent-simple-and-samepkg.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/persistent-simple-and-samepkg.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/persistent-simple.capnp: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Sandstorm Development Group, Inc. and contributors 2 | # Licensed under the MIT License: 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | using Go = import "/go.capnp"; 23 | 24 | @0xbcfe42e3392b05a8; 25 | 26 | $Go.package("persistent_simple"); 27 | $Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/persistent-simple"); 28 | 29 | interface Persistent { 30 | } 31 | 32 | annotation persistent(interface, field) :Void; 33 | 34 | struct FieldNameThatIsInvalidInGolang { 35 | string @0 :Text; 36 | } 37 | -------------------------------------------------------------------------------- /capnpc-go/testdata/rpc.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/rpc.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/scopes.capnp: -------------------------------------------------------------------------------- 1 | # Generate scopes.capnp.out with: 2 | # capnp compile -o- scopes.capnp > scopes.capnp.out 3 | # Must run inside this directory to preserve paths. 4 | 5 | using Go = import "go.capnp"; 6 | using Other = import "otherscopes.capnp"; 7 | 8 | @0xd68755941d99d05e; 9 | 10 | $Go.package("scopes"); 11 | $Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/scopes"); 12 | 13 | struct Foo @0xc8d7b3b4e07f8bd9 { 14 | } 15 | 16 | const fooVar @0x84efedc75e99768d :Foo = (); 17 | const otherFooVar @0x836faf1834d91729 :Other.Foo = (); 18 | const fooListVar @0xcda2680ec5c921e0 :List(Foo) = []; 19 | const otherFooListVar @0x83e7e1b3cd1be338 :List(Other.Foo) = []; 20 | const intList @0xacf3d9917d0bb0f0 :List(Int32) = []; 21 | -------------------------------------------------------------------------------- /capnpc-go/testdata/scopes.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/scopes.capnp.out -------------------------------------------------------------------------------- /capnpc-go/testdata/util.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/capnpc-go/testdata/util.capnp.out -------------------------------------------------------------------------------- /captable.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // CapTable is the indexed list of the clients referenced in the 4 | // message. Capability pointers inside the message will use this 5 | // table to map pointers to Clients. The table is populated by 6 | // the RPC system. 7 | // 8 | // https://capnproto.org/encoding.html#capabilities-interfaces 9 | type CapTable struct { 10 | cs []Client 11 | } 12 | 13 | // Reset the cap table, releasing all capabilities and setting 14 | // the length to zero. Clients passed as arguments are added 15 | // to the table after zeroing, such that ct.Len() == len(cs). 16 | func (ct *CapTable) Reset(cs ...Client) { 17 | for _, c := range ct.cs { 18 | c.Release() 19 | } 20 | 21 | ct.cs = append(ct.cs[:0], cs...) 22 | } 23 | 24 | // Len returns the number of capabilities in the table. 25 | func (ct CapTable) Len() int { 26 | return len(ct.cs) 27 | } 28 | 29 | // At returns the capability at the given index of the table. 30 | func (ct CapTable) At(i int) Client { 31 | return ct.cs[i] 32 | } 33 | 34 | // Contains returns true if the supplied interface corresponds 35 | // to a client already present in the table. 36 | func (ct CapTable) Contains(ifc Interface) bool { 37 | return ifc.IsValid() && ifc.Capability() < CapabilityID(ct.Len()) 38 | } 39 | 40 | // Get the client corresponding to the supplied interface. It 41 | // returns a null client if the interface's CapabilityID isn't 42 | // in the table. 43 | func (ct CapTable) Get(ifc Interface) (c Client) { 44 | if ct.Contains(ifc) { 45 | c = ct.cs[ifc.Capability()] 46 | } 47 | 48 | return 49 | } 50 | 51 | // Set the client for the supplied capability ID. If a client 52 | // for the given ID already exists, it will be replaced without 53 | // releasing. 54 | func (ct CapTable) Set(id CapabilityID, c Client) { 55 | ct.cs[id] = c 56 | } 57 | 58 | // Add appends a capability to the message's capability table and 59 | // returns its ID. It "steals" c's reference: the Message will release 60 | // the client when calling Reset. 61 | func (ct *CapTable) Add(c Client) CapabilityID { 62 | ct.cs = append(ct.cs, c) 63 | return CapabilityID(ct.Len() - 1) 64 | } 65 | -------------------------------------------------------------------------------- /captable_test.go: -------------------------------------------------------------------------------- 1 | package capnp_test 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "capnproto.org/go/capnp/v3" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCapTable(t *testing.T) { 12 | t.Parallel() 13 | 14 | var ct capnp.CapTable 15 | 16 | assert.Zero(t, ct.Len(), 17 | "zero-value CapTable should be empty") 18 | assert.Zero(t, ct.Add(capnp.Client{}), 19 | "first entry should have CapabilityID(0)") 20 | assert.Equal(t, 1, ct.Len(), 21 | "should increase length after adding capability") 22 | 23 | ct.Reset() 24 | assert.Zero(t, ct.Len(), 25 | "zero-value CapTable should be empty after Reset()") 26 | ct.Reset(capnp.Client{}, capnp.Client{}) 27 | assert.Equal(t, 2, ct.Len(), 28 | "zero-value CapTable should be empty after Reset(c, c)") 29 | 30 | errTest := errors.New("test") 31 | ct.Set(capnp.CapabilityID(0), capnp.ErrorClient(errTest)) 32 | snapshot := ct.At(0).Snapshot() 33 | defer snapshot.Release() 34 | err := snapshot.Brand().Value.(error) 35 | assert.ErrorIs(t, errTest, err, "should update client at index 0") 36 | } 37 | -------------------------------------------------------------------------------- /docs/Getting-Started.md: -------------------------------------------------------------------------------- 1 | # First Steps with Cap'n Proto 2 | 3 | ### Installation 4 | 5 | Start by [installing and configuring the toolchain](Installation.md). 6 | 7 | ### Tutorials 8 | 9 | After installing, we recommend going through the tutorials in the following order: 10 | 11 | 1. [Writing Schemas and Generating Code](Writing-Schemas-and-Generating-Code.md) 12 | 2. [Working with Generated Types](Working-with-Capn-Proto-Types.md) 13 | 3. [RPC and Object Capabilities](Remote-Procedure-Calls-using-Interfaces.md) 14 | 15 | ## Getting Help 16 | 17 | As always, our [friendly community](https://matrix.to/#/#go-capnp:matrix.org) is available to help. 18 | 19 | Give us a shout if you're stuck, or just pop in to say hello! 👋 20 | -------------------------------------------------------------------------------- /docs/Installation.md: -------------------------------------------------------------------------------- 1 | # Toolchain & Configuration 2 | 3 | First, [install the Cap'n Proto tools](https://capnproto.org/install.html). 4 | 5 | Then, run the following command to install the compiler plugin for the 6 | Go language: 7 | 8 | ```bash 9 | go install capnproto.org/go/capnp/v3/capnpc-go@latest 10 | ``` 11 | 12 | This will install a `capnpc-go` executable under `$(go env GOPATH)/bin`, 13 | which you should make sure has been added to your shell's `$PATH` variable. 14 | 15 | You will also need a checkout of of the `go-capnp` repository, for the 16 | included schema: 17 | 18 | ``` 19 | git clone https://github.com/capnproto/go-capnp 20 | ``` 21 | 22 | If you get stuck at any point, please [ask us for help](https://matrix.to/#/#go-capnp:matrix.org)! 23 | 24 | # Next 25 | 26 | Once you have installed `capnp` and the Go plugin, you should [write and 27 | compile your first schema](Writing-Schemas-and-Generating-Code.md). 28 | -------------------------------------------------------------------------------- /docs/RPC-and-Capability-Oriented-Design.md: -------------------------------------------------------------------------------- 1 | // TODO -------------------------------------------------------------------------------- /docs/Writing-Schemas-and-Generating-Code.md: -------------------------------------------------------------------------------- 1 | # Code Generation 2 | 3 | Cap'n Proto works by generating code. First, you create a schema using [Cap'n Proto's interface-definition language (IDL)](https://capnproto.org/language.html). Then, you feed this schema into the Cap'n Proto compiler, which generates the corresponding code in your native language. 4 | 5 | There is support for [other languages](https://capnproto.org/otherlang.html), too. 6 | 7 | ## Before you Begin 8 | 9 | Ensure that you have installed the `capnp` compiler and the Go language plugin, and that your shell's `$PATH` variable includes `$GOPATH/bin`. 10 | 11 | Refer to the [previous section](Getting-Started.md) for instructions. 12 | 13 | ## Example Schema File 14 | 15 | Consider the following schema, stored in `foo/books.capnp`: 16 | 17 | ```capnp 18 | using Go = import "/go.capnp"; 19 | @0x85d3acc39d94e0f8; 20 | $Go.package("books"); 21 | $Go.import("foo/books"); 22 | 23 | struct Book { 24 | title @0 :Text; 25 | # Title of the book. 26 | 27 | pageCount @1 :Int32; 28 | # Number of pages in the book. 29 | } 30 | ``` 31 | 32 | capnpc-go requires that two [annotations](https://capnproto.org/language.html#annotations) be present in your schema: 33 | 34 | 1. `$Go.package("books")`: tells the compiler to place `package books` at the top of the generated Go files. 35 | 2. `$Go.import("foo/books")`: declares the full import path within your project. The compiler uses this to generate the import statement in the auto-generated code, when one of your schemas imports a type from another. 36 | 37 | Compilation will fail unless these annotations are present. 38 | 39 | ## Compiling the Schema 40 | 41 | To compile this schema into Go code, run the following command. Note that the source path `/foo/books.capnp` must correspond to the import path declared in your annotations. 42 | 43 | ```bash 44 | capnp compile -I /path/to/go-capnp/std -ogo foo/books.capnp 45 | ``` 46 | 47 | > **Tip** 👉 For more compilation options, see `capnp compile --help`. 48 | 49 | This will output the `foo/books.capnp.go` file, containing Go structs that can be imported into your programs. These are ordinary Go types that represent the schema you declared in `books.capnp`. Each has accessor methods corresponding to the fields declared in the schema. For example, the `Book` struct will have the methods `Title() (string, error)` and `SetTitle(string) error`. 50 | 51 | If you have go-capnp as a dependency and you are in go module folder, you can use `go list` and backticks like so 52 | 53 | ```bash 54 | capnp compile -I `go list -m -f '{{.Dir}}' capnproto.org/go/capnp/v3`/std -ogo foo/books.capnp 55 | ``` 56 | 57 | In the next section, we will show how you can write these structs to a file or transmit them over the network. 58 | 59 | # Next 60 | 61 | Now that you have generated code from your schema, you should [learn how to work with Cap'n Proto types](Working-with-Capn-Proto-Types.md). 62 | -------------------------------------------------------------------------------- /encoding/text/testdata/txt.capnp.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/encoding/text/testdata/txt.capnp.out -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "capnproto.org/go/capnp/v3/exc" 5 | ) 6 | 7 | // TODO(someday): progressively remove exported functions and instead 8 | // rely on package 'exc'. 9 | 10 | // Unimplemented returns an error that formats as the given text and 11 | // will report true when passed to IsUnimplemented. 12 | func Unimplemented(s string) error { 13 | return exc.New(exc.Unimplemented, "", s) 14 | } 15 | 16 | // IsUnimplemented reports whether e indicates that functionality is unimplemented. 17 | func IsUnimplemented(e error) bool { 18 | return exc.TypeOf(e) == exc.Unimplemented 19 | } 20 | 21 | // Disconnected returns an error that formats as the given text and 22 | // will report true when passed to IsDisconnected. 23 | func Disconnected(s string) error { 24 | return exc.New(exc.Disconnected, "", s) 25 | } 26 | 27 | // IsDisconnected reports whether e indicates a failure due to loss of a necessary capability. 28 | func IsDisconnected(e error) bool { 29 | return exc.TypeOf(e) == exc.Disconnected 30 | } 31 | -------------------------------------------------------------------------------- /example/books/README.md: -------------------------------------------------------------------------------- 1 | Change directory into the example directory, and compile the example 2 | capnproto schema: 3 | 4 | ``` 5 | cd $GOPATH/src/go-capnproto2/example/books 6 | capnp compile -I ../../std/ -ogo books/books.capnp 7 | ``` 8 | 9 | Then build and run each example: 10 | ``` 11 | cd ex1 && go build . && ./bookstest1 12 | 13 | cd ex2 && go build . && capnp encode ../books/books.capnp Book < ./book.txt | ./bookstest2 && cd .. 14 | ``` 15 | -------------------------------------------------------------------------------- /example/books/books/.gitignore: -------------------------------------------------------------------------------- 1 | books.capnp.go 2 | -------------------------------------------------------------------------------- /example/books/books/books.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "/go.capnp"; 2 | @0x85d3acc39d94e0f8; 3 | $Go.package("books"); 4 | $Go.import("foo/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 | -------------------------------------------------------------------------------- /example/books/books/go.mod: -------------------------------------------------------------------------------- 1 | module books 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /example/books/books/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/example/books/books/go.sum -------------------------------------------------------------------------------- /example/books/ex1/.gitignore: -------------------------------------------------------------------------------- 1 | /bookstest1 2 | -------------------------------------------------------------------------------- /example/books/ex1/books1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "books" 7 | "capnproto.org/go/capnp/v3" 8 | ) 9 | 10 | func main() { 11 | // Make a brand new empty message. A Message allocates Cap'n Proto structs. 12 | msg, seg := capnp.NewSingleSegmentMessage(nil) 13 | 14 | // Create a new Book struct. Every message must have a root struct. 15 | book, err := books.NewRootBook(seg) 16 | if err != nil { 17 | panic(err) 18 | } 19 | _ = book.SetTitle("War and Peace") 20 | book.SetPageCount(1440) 21 | 22 | // Write the message to stdout. 23 | err = capnp.NewEncoder(os.Stdout).Encode(msg) 24 | if err != nil { 25 | panic(err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/books/ex1/go.mod: -------------------------------------------------------------------------------- 1 | module bookstest1 2 | 3 | go 1.17 4 | 5 | require capnproto.org/go/capnp/v3 v3.0.0-alpha.1 6 | 7 | require books v1.0.0 // indirect 8 | 9 | replace books v1.0.0 => ../books 10 | -------------------------------------------------------------------------------- /example/books/ex1/go.sum: -------------------------------------------------------------------------------- 1 | capnproto.org/go/capnp/v3 v3.0.0-alpha.1 h1:lA/zWC1XgaFez/UdqcWi5Bz1grsqPQDY5Ki4lgYrQ1o= 2 | capnproto.org/go/capnp/v3 v3.0.0-alpha.1/go.mod h1:izbWjXlvObqHwvhxwDBFLpY9lXSXIxDKRtnuktKHVsU= 3 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 4 | github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= 5 | github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 6 | github.com/tinylib/msgp v1.1.5 h1:2gXmtWueD2HefZHQe1QOy9HVzmFrLOVvsXwXBQ0ayy0= 7 | github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= 8 | github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= 9 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 10 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 11 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 12 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 13 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 14 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 18 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 20 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 23 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 24 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 25 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 26 | golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 27 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 29 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 30 | -------------------------------------------------------------------------------- /example/books/ex2/.gitignore: -------------------------------------------------------------------------------- 1 | /bookstest2 2 | -------------------------------------------------------------------------------- /example/books/ex2/book.txt: -------------------------------------------------------------------------------- 1 | (title = "War And Peace", pageCount = 1440) 2 | -------------------------------------------------------------------------------- /example/books/ex2/books2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "books" 8 | "capnproto.org/go/capnp/v3" 9 | ) 10 | 11 | func main() { 12 | // Read the message from stdin. 13 | msg, err := capnp.NewDecoder(os.Stdin).Decode() 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | // Extract the root struct from the message. 19 | book, err := books.ReadRootBook(msg) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | // Access fields from the struct. 25 | title, err := book.Title() 26 | if err != nil { 27 | panic(err) 28 | } 29 | pageCount := book.PageCount() 30 | fmt.Printf("%q has %d pages\n", title, pageCount) 31 | } 32 | -------------------------------------------------------------------------------- /example/books/ex2/go.mod: -------------------------------------------------------------------------------- 1 | module bookstest2 2 | 3 | go 1.17 4 | 5 | require ( 6 | books v1.0.0 7 | capnproto.org/go/capnp/v3 v3.0.0-alpha.1 8 | ) 9 | 10 | replace books v1.0.0 => ../books 11 | -------------------------------------------------------------------------------- /example/books/ex2/go.sum: -------------------------------------------------------------------------------- 1 | capnproto.org/go/capnp/v3 v3.0.0-alpha.1 h1:lA/zWC1XgaFez/UdqcWi5Bz1grsqPQDY5Ki4lgYrQ1o= 2 | capnproto.org/go/capnp/v3 v3.0.0-alpha.1/go.mod h1:izbWjXlvObqHwvhxwDBFLpY9lXSXIxDKRtnuktKHVsU= 3 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 4 | github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= 5 | github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 6 | github.com/tinylib/msgp v1.1.5 h1:2gXmtWueD2HefZHQe1QOy9HVzmFrLOVvsXwXBQ0ayy0= 7 | github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= 8 | github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= 9 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 10 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 11 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 12 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 13 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 14 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 18 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 20 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 23 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 24 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 25 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 26 | golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 27 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 29 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 30 | -------------------------------------------------------------------------------- /example/hashes/.gitignore: -------------------------------------------------------------------------------- 1 | /hashtest 2 | hashes.capnp.go 3 | -------------------------------------------------------------------------------- /example/hashes/README.md: -------------------------------------------------------------------------------- 1 | # RPC Example: Hashses 2 | 3 | ## Running the example 4 | 5 | Navigate to the example directory: 6 | 7 | ```bash 8 | cd /path/to/go-capnp/example/hashes` 9 | ```` 10 | 11 | Compile the capnp file: 12 | 13 | `capnp compile -I ../../std/ -ogo hashes.capnp` 14 | 15 | Build your code, and run it: 16 | 17 | ``` 18 | go run cmd/hashesserver.go 19 | ``` 20 | 21 | The output of the example should be the following 22 | 23 | ```bash 24 | sha1: 0a0a9f2a6772942557ab5355d76af442f8f65e01 25 | ``` -------------------------------------------------------------------------------- /example/hashes/go.mod: -------------------------------------------------------------------------------- 1 | module hashtest 2 | 3 | go 1.18 4 | 5 | require capnproto.org/go/capnp/v3 v3.0.0-alpha.30 6 | 7 | require ( 8 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect 9 | zenhack.net/go/util v0.0.0-20230414204917-531d38494cf5 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /example/hashes/go.sum: -------------------------------------------------------------------------------- 1 | capnproto.org/go/capnp/v3 v3.0.0-alpha.30 h1:iABQan/YiHFCgSXym5aNj27osapnEgAk4WaWYqb4sQM= 2 | capnproto.org/go/capnp/v3 v3.0.0-alpha.30/go.mod h1:+ysMHvOh1EWNOyorxJWs1omhRFiDoKxKkWQACp54jKM= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 7 | github.com/tinylib/msgp v1.1.5 h1:2gXmtWueD2HefZHQe1QOy9HVzmFrLOVvsXwXBQ0ayy0= 8 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= 9 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 10 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 11 | zenhack.net/go/util v0.0.0-20230414204917-531d38494cf5 h1:yksDCGMVzyn3vlyf0GZ3huiF5FFaMGQpQ3UJvR0EoGA= 12 | zenhack.net/go/util v0.0.0-20230414204917-531d38494cf5/go.mod h1:1LtNdPAs8WH+BTcQiZAOo2MIKD/5jyK/u7sZ9ZPe5SE= 13 | -------------------------------------------------------------------------------- /example/hashes/hashes.capnp: -------------------------------------------------------------------------------- 1 | @0xdb8274f9144abc7e; 2 | 3 | using Go = import "/go.capnp"; 4 | 5 | $Go.package("hashes"); 6 | $Go.import("foo/hashes"); 7 | 8 | interface HashFactory { 9 | newSha1 @0 () -> (hash :Hash); 10 | } 11 | 12 | interface Hash { 13 | write @0 (data :Data) -> (); 14 | sum @1 () -> (hash :Data); 15 | } 16 | -------------------------------------------------------------------------------- /exc/types.go: -------------------------------------------------------------------------------- 1 | package exc 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | ) 7 | 8 | // Type indicates the type of error, mirroring those in rpc.capnp. 9 | type Type int 10 | 11 | // Error types. 12 | const ( 13 | Failed Type = 0 14 | Overloaded Type = 1 15 | Disconnected Type = 2 16 | Unimplemented Type = 3 17 | ) 18 | 19 | // String returns the lowercased Go constant name, or a string in the 20 | // form "type(X)" where X is the value of typ for any unrecognized type. 21 | func (typ Type) String() string { 22 | switch typ { 23 | case Failed: 24 | return "failed" 25 | case Overloaded: 26 | return "overloaded" 27 | case Disconnected: 28 | return "disconnected" 29 | case Unimplemented: 30 | return "unimplemented" 31 | default: 32 | var buf [26]byte 33 | s := append(buf[:0], "type("...) 34 | s = strconv.AppendInt(s, int64(typ), 10) 35 | s = append(s, ')') 36 | return string(s) 37 | } 38 | } 39 | 40 | // GoString returns the Go constant name, or a string in the form 41 | // "Type(X)" where X is the value of typ for any unrecognized type. 42 | func (typ Type) GoString() string { 43 | switch typ { 44 | case Failed: 45 | return "Failed" 46 | case Overloaded: 47 | return "Overloaded" 48 | case Disconnected: 49 | return "Disconnected" 50 | case Unimplemented: 51 | return "Unimplemented" 52 | default: 53 | var buf [26]byte 54 | s := append(buf[:0], "Type("...) 55 | s = strconv.AppendInt(s, int64(typ), 10) 56 | s = append(s, ')') 57 | return string(s) 58 | } 59 | } 60 | 61 | // TypeOf returns err's type if err was created by this package or 62 | // Failed if it was not. 63 | func TypeOf(err error) Type { 64 | ce, ok := err.(*Exception) 65 | if !ok { 66 | return Failed 67 | } 68 | return ce.Type 69 | } 70 | 71 | // IsType reports whether any error in err's is an Exception 72 | // whose type matches 't'. 73 | // 74 | // The chain consists of err itself followed by the sequence of 75 | // errors obtained by repeatedly calling Unwrap. 76 | func IsType(err error, t Type) bool { 77 | var exc = &Exception{} 78 | if errors.As(err, &exc) { 79 | return exc.Type == t 80 | } 81 | 82 | return false 83 | } 84 | -------------------------------------------------------------------------------- /exc/types_test.go: -------------------------------------------------------------------------------- 1 | package exc_test 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | 8 | "capnproto.org/go/capnp/v3/exc" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestTypeOf(t *testing.T) { 13 | t.Parallel() 14 | 15 | tests := []struct { 16 | err error 17 | want exc.Type 18 | }{ 19 | {nil, exc.Failed}, 20 | {errors.New("generic"), exc.Failed}, 21 | {exc.New(exc.Failed, "capnp", "failed error"), exc.Failed}, 22 | {exc.New(exc.Overloaded, "capnp", "overloaded error"), exc.Overloaded}, 23 | {exc.New(exc.Disconnected, "capnp", "disconnected error"), exc.Disconnected}, 24 | {exc.New(exc.Unimplemented, "capnp", "unimplemented error"), exc.Unimplemented}, 25 | } 26 | for _, test := range tests { 27 | assert.Equal(t, test.want, exc.TypeOf(test.err)) 28 | } 29 | } 30 | 31 | func TestIsType(t *testing.T) { 32 | t.Parallel() 33 | 34 | tests := []struct { 35 | err error 36 | tpe exc.Type 37 | }{ 38 | { 39 | err: exc.New(exc.Failed, "capnp", "failed error"), 40 | tpe: exc.Failed, 41 | }, 42 | { 43 | err: exc.New(exc.Overloaded, "capnp", "overloaded error"), 44 | tpe: exc.Overloaded, 45 | }, 46 | { 47 | err: exc.New(exc.Disconnected, "capnp", "disconnected error"), 48 | tpe: exc.Disconnected, 49 | }, 50 | { 51 | err: exc.New(exc.Unimplemented, "capnp", "unimplemented error"), 52 | tpe: exc.Unimplemented, 53 | }, 54 | } 55 | for i, test := range tests { 56 | err := fmt.Errorf("test: %w", test.err) 57 | assert.True(t, exc.IsType(err, test.tpe), 58 | "case %d should match exception type '%s'", i, test.tpe) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /exp/bufferpool/pool_internal_test.go: -------------------------------------------------------------------------------- 1 | package bufferpool 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestBucketIndex(t *testing.T) { 11 | tests := []struct { 12 | size int 13 | get int 14 | put int 15 | }{ 16 | // Only sizes that are powers of two are obtained and returned 17 | // to the same bucket. 18 | // 19 | // Sizes that are not a power of two must be fetched by the next 20 | // higher power of two, but are returned to the lower one. 21 | {size: 0, get: 0, put: -1}, 22 | {size: 1, get: 0, put: 0}, 23 | {size: 26, get: 5, put: 4}, // 26 == 0b00011010 24 | {size: 32, get: 5, put: 5}, 25 | {size: 1024, get: 10, put: 10}, 26 | {size: 1025, get: 11, put: 10}, 27 | } 28 | 29 | for i := range tests { 30 | tc := tests[i] 31 | t.Run(fmt.Sprintf("%d", tc.size), func(t *testing.T) { 32 | get := bucketToGet(tc.size) 33 | require.Equal(t, tc.get, get) 34 | put := bucketToPut(tc.size) 35 | require.Equal(t, tc.put, put) 36 | }) 37 | } 38 | } 39 | 40 | func TestBucketSlice(t *testing.T) { 41 | const minAlloc = 8 42 | const bucketCount = 10 43 | const sizeLastBucket = 1 << (bucketCount - 1) 44 | 45 | tests := []struct { 46 | size int 47 | wantLen int 48 | wantCap int 49 | }{{ 50 | size: -1, // Negative values are skipped. 51 | wantLen: 0, 52 | wantCap: 0, 53 | }, { 54 | size: 0, 55 | wantLen: minAlloc, 56 | wantCap: minAlloc, 57 | }, { 58 | size: 1, 59 | wantLen: minAlloc, 60 | wantCap: minAlloc, 61 | }, { 62 | size: minAlloc, 63 | wantLen: minAlloc, 64 | wantCap: minAlloc, 65 | }, { 66 | size: minAlloc + 1, // Goes to next bucket. 67 | wantLen: minAlloc * 2, 68 | wantCap: minAlloc * 2, 69 | }, { 70 | size: minAlloc*2 + 1, 71 | wantLen: minAlloc * 4, 72 | wantCap: minAlloc * 4, 73 | }, { 74 | size: sizeLastBucket - 1, 75 | wantLen: sizeLastBucket, 76 | wantCap: sizeLastBucket, 77 | }, { 78 | size: sizeLastBucket, 79 | wantLen: sizeLastBucket, 80 | wantCap: sizeLastBucket, 81 | }, { 82 | size: sizeLastBucket + 1, // Anything > last bucket size is not allocated. 83 | wantLen: 0, 84 | wantCap: 0, 85 | }} 86 | 87 | for _, tc := range tests { 88 | t.Run(fmt.Sprintf("%d", tc.size), func(t *testing.T) { 89 | bs := makeBucketSlice(minAlloc, bucketCount) 90 | require.Len(t, bs, bucketCount) 91 | buf := bs.Get(tc.size) 92 | require.Len(t, buf, tc.wantLen) 93 | require.Equal(t, tc.wantCap, cap(buf)) 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /exp/bufferpool/pool_test.go: -------------------------------------------------------------------------------- 1 | package bufferpool_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "capnproto.org/go/capnp/v3/exp/bufferpool" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestInvariants(t *testing.T) { 11 | t.Parallel() 12 | 13 | assert.Panics(t, func() { 14 | bufferpool.NewPool(1024, 1) 15 | }, "should panic when BucketCount cannot satisfy MinAlloc.") 16 | } 17 | 18 | func TestGet(t *testing.T) { 19 | t.Parallel() 20 | t.Helper() 21 | 22 | minAlloc, bucketCount := 64, 8 23 | pool := bufferpool.NewPool(minAlloc, bucketCount) 24 | 25 | t.Run("SizeMinAlloc", func(t *testing.T) { 37 | size := minAlloc + 1 38 | nextAlloc := minAlloc << 1 39 | assert.Len(t, pool.Get(size), size, "should return buffer with len=%d", size) 40 | assert.Equal(t, nextAlloc, cap(pool.Get(size)), "should return buffer with cap=%d", nextAlloc) 41 | }) 42 | 43 | t.Run("Size>MaxSize", func(t *testing.T) { 44 | size := 1<<(bucketCount+1) + 1 45 | assert.Len(t, pool.Get(size), size, "should return buffer with len=%d", size) 46 | assert.GreaterOrEqual(t, size, cap(pool.Get(size)), "should return buffer with cap>=%d", size) 47 | }) 48 | } 49 | 50 | func TestPut(t *testing.T) { 51 | t.Parallel() 52 | 53 | pool := bufferpool.NewPool(1024, 20) 54 | 55 | buf := make([]byte, 1024, 1024) 56 | for i := range buf { 57 | buf[i] = byte(i) 58 | } 59 | buf = buf[:8] 60 | 61 | pool.Put(buf) 62 | buf = pool.Get(8) 63 | fullBuf := buf[:cap(buf)] 64 | 65 | assert.Len(t, fullBuf, 1024, "buf should have len and cap 1024") 66 | assert.Equal(t, make([]byte, 8), buf, "should zero first 8 bytes") 67 | assert.NotEqual(t, 0, fullBuf[9], "first byte after clearing should not be zero") 68 | } 69 | 70 | func BenchmarkPool(b *testing.B) { 71 | pool := bufferpool.NewPool(0, 0) 72 | const size = 32 73 | 74 | // Make cache hot. 75 | buf := pool.Get(size) 76 | pool.Put(buf) 77 | 78 | b.ResetTimer() 79 | b.ReportAllocs() 80 | for i := 0; i < b.N; i++ { 81 | buf := pool.Get(size) 82 | pool.Put(buf) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /exp/clock/clock_test.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestTimers(t *testing.T) { 11 | // arbitrary initial now: 12 | init := time.Unix(1e9, 0) 13 | 14 | clock := NewManual(init) 15 | 16 | timer := clock.NewTimer(5 * time.Second) 17 | 18 | select { 19 | case <-timer.Chan(): 20 | t.Fatal("Timer went off too early.") 21 | default: 22 | } 23 | 24 | clock.Advance(1 * time.Second) 25 | 26 | select { 27 | case <-timer.Chan(): 28 | t.Fatal("Timer went off too early.") 29 | default: 30 | } 31 | 32 | clock.Advance(10 * time.Second) 33 | 34 | select { 35 | case <-timer.Chan(): 36 | default: 37 | t.Fatal("Timer didn't go off.") 38 | } 39 | 40 | assert.False(t, timer.Stop(), "Timer should have already been stopped.") 41 | 42 | assert.Equal(t, init.Add(11*time.Second), clock.Now(), "Time should be 11 seconds later.") 43 | 44 | timer.Reset(1 * time.Second) 45 | 46 | select { 47 | case <-timer.Chan(): 48 | t.Fatal("Timer went off again too early.") 49 | default: 50 | } 51 | 52 | clock.Advance(2 * time.Second) 53 | 54 | select { 55 | case <-timer.Chan(): 56 | default: 57 | t.Fatal("Timer didn't go off.") 58 | } 59 | } 60 | 61 | func TestImmediateTimer(t *testing.T) { 62 | clock := NewManual(time.Unix(1e9, 0)) 63 | 64 | timer := clock.NewTimer(0) 65 | select { 66 | case <-timer.Chan(): 67 | default: 68 | t.Fatal("Timer with duration of zero should go off immediately.") 69 | } 70 | 71 | timer = clock.NewTimer(-1) 72 | select { 73 | case <-timer.Chan(): 74 | default: 75 | t.Fatal("Timer with negative duration should go off immediately.") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /exp/mpsc/mpsc.go: -------------------------------------------------------------------------------- 1 | // Package mpsc implements a multiple-producer, single-consumer queue. 2 | package mpsc 3 | 4 | // N.B. this is a trivial wrapper around the spsc package, which just adds 5 | // a lock to the Tx end to make it multiple-producer. 6 | 7 | import ( 8 | "context" 9 | "sync" 10 | 11 | "capnproto.org/go/capnp/v3/exp/spsc" 12 | ) 13 | 14 | // A multiple-producer, single-consumer queue. Create one with New(), 15 | // and send from many gorotuines with Tx.Send(). Only one gorotuine may 16 | // call Rx.Recv(). 17 | type Queue[T any] struct { 18 | Tx[T] 19 | Rx[T] 20 | } 21 | 22 | // The receive end of a Queue. 23 | type Rx[T any] struct { 24 | rx spsc.Rx[T] 25 | } 26 | 27 | // The send/transmit end of a Queue. 28 | type Tx[T any] struct { 29 | mu sync.Mutex 30 | tx spsc.Tx[T] 31 | } 32 | 33 | // Create a new, initially empty Queue. 34 | func New[T any]() *Queue[T] { 35 | q := spsc.New[T]() 36 | return &Queue[T]{ 37 | Tx: Tx[T]{tx: q.Tx}, 38 | Rx: Rx[T]{rx: q.Rx}, 39 | } 40 | } 41 | 42 | // Send a message on the queue. 43 | func (tx *Tx[T]) Send(v T) { 44 | tx.mu.Lock() 45 | defer tx.mu.Unlock() 46 | tx.tx.Send(v) 47 | } 48 | 49 | // Close the queue. Calls to Recv on the other end will return io.EOF. 50 | func (tx *Tx[T]) Close() error { 51 | tx.mu.Lock() 52 | defer tx.mu.Unlock() 53 | return tx.tx.Close() 54 | } 55 | 56 | // Receive a message from the queue. Blocks if the queue is empty. 57 | // If the context ends before the receive happens, this returns 58 | // ctx.Err(). If Close is called on the corresponding Tx, this 59 | // returns io.EOF 60 | func (rx *Rx[T]) Recv(ctx context.Context) (T, error) { 61 | return rx.rx.Recv(ctx) 62 | } 63 | 64 | // Try to receive a message from the queue. If successful, ok will be true. 65 | // If the queue is empty, this will return immediately with ok = false. 66 | func (rx *Rx[T]) TryRecv() (v T, ok bool) { 67 | return rx.rx.TryRecv() 68 | } 69 | -------------------------------------------------------------------------------- /exp/spsc/spsc_test.go: -------------------------------------------------------------------------------- 1 | package spsc 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // Test that filling/overflowing the internal items queue doesn't block the sender. 13 | func TestFillItemsNonBlock(t *testing.T) { 14 | t.Parallel() 15 | 16 | q := New[int]() 17 | 18 | for i := 0; i < itemsBuffer+1; i++ { 19 | q.Send(i) 20 | } 21 | } 22 | 23 | // Try filling the queue, then draining it with TryRecv(). 24 | func TestFillThenTryDrain(t *testing.T) { 25 | t.Parallel() 26 | 27 | q := New[int]() 28 | 29 | limit := itemsBuffer + 1 30 | 31 | for i := 0; i < limit; i++ { 32 | q.Send(i) 33 | } 34 | 35 | for i := 0; i < limit; i++ { 36 | v, ok := q.TryRecv() 37 | assert.True(t, ok) 38 | assert.Equal(t, i, v) 39 | } 40 | _, ok := q.TryRecv() 41 | assert.False(t, ok) 42 | } 43 | 44 | // Try filling the queue, then draining it with Recv(). 45 | func TestFillThenDrain(t *testing.T) { 46 | t.Parallel() 47 | 48 | q := New[int]() 49 | 50 | limit := itemsBuffer + 1 51 | 52 | for i := 0; i < limit; i++ { 53 | q.Send(i) 54 | } 55 | 56 | ctx := context.Background() 57 | for i := 0; i < limit; i++ { 58 | v, err := q.Recv(ctx) 59 | assert.Nil(t, err) 60 | assert.Equal(t, i, v) 61 | } 62 | ctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond) 63 | defer cancel() 64 | _, err := q.Recv(ctx) 65 | assert.NotNil(t, err) 66 | assert.ErrorIs(t, err, ctx.Err()) 67 | } 68 | 69 | // Test that we get io.EOF after closing the tx. 70 | func TestClose(t *testing.T) { 71 | t.Parallel() 72 | 73 | // try it with various numbers of items in the queue: 74 | cases := []struct { 75 | size int 76 | desc string 77 | }{ 78 | {0, "empty"}, 79 | {1, "one element"}, 80 | {itemsBuffer, "full buffer"}, 81 | {itemsBuffer + 1, "overfull buffer"}, 82 | } 83 | 84 | for _, c := range cases { 85 | t.Run("size: "+c.desc, func(t *testing.T) { 86 | q := New[int]() 87 | 88 | for i := 0; i < c.size; i++ { 89 | q.Send(i) 90 | } 91 | q.Close() 92 | 93 | ctx := context.Background() 94 | for i := 0; i < c.size; i++ { 95 | v, err := q.Recv(ctx) 96 | assert.NoError(t, err) 97 | assert.Equal(t, i, v) 98 | } 99 | 100 | _, err := q.Recv(ctx) 101 | assert.Equal(t, err, io.EOF) 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /flowcontrol/bbr/doc.go: -------------------------------------------------------------------------------- 1 | // package bbr implements the BBR congestion control algorithm, as 2 | // described in: 3 | // 4 | // https://queue.acm.org/detail.cfm?id=3022184 5 | package bbr 6 | -------------------------------------------------------------------------------- /flowcontrol/bbr/filter_test.go: -------------------------------------------------------------------------------- 1 | package bbr 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestBtlBwFilter(t *testing.T) { 11 | f := newBtlBwFilter() 12 | 13 | assert.Equal(t, bytesPerNs(1e-10), f.Estimate, "Initial bandwidth estimate is 1 byte per 10s.") 14 | f.AddSample(4) 15 | assert.Equal(t, bytesPerNs(4), f.Estimate, "After adding one sample, estimate is that sample.") 16 | f.AddSample(2) 17 | assert.Equal(t, bytesPerNs(4), f.Estimate, "After adding a smaller sample, estimate is unchanged.") 18 | f.AddSample(6) 19 | assert.Equal(t, bytesPerNs(6), f.Estimate, "After adding a larger sample, estimate equals new sample.") 20 | 21 | for i := 0; i < btlBwFilterSize; i++ { 22 | f.AddSample(1) 23 | } 24 | assert.Equal(t, bytesPerNs(1), f.Estimate, "Older samples slide out of the window correctly.") 25 | } 26 | 27 | func TestRtPropFilter(t *testing.T) { 28 | f := newRtPropFilter() 29 | 30 | now := sampleStartTime 31 | 32 | addSample := func(rtt time.Duration) { 33 | f.AddSample(rtPropSample{ 34 | now: now, 35 | rtt: rtt, 36 | }) 37 | } 38 | 39 | addSample(4) 40 | assert.Equal(t, time.Duration(4), f.Estimate, "After adding one sample, estimate is that sample.") 41 | assert.Equal(t, 0, f.q.Len(), "Queue is empty after the first sample.") 42 | now = now.Add(time.Second / 10) 43 | addSample(6) 44 | assert.Equal(t, time.Duration(4), f.Estimate, "After adding a larger sample, estimate is unchanged.") 45 | assert.Equal(t, 0, f.q.Len(), "Queue is empty before 1 second has passed.") 46 | 47 | now = now.Add(1 * time.Second) 48 | addSample(2) 49 | assert.Equal(t, time.Duration(2), f.Estimate, "After adding a smaller sample, estimate equals new sample.") 50 | assert.Equal(t, 1, f.q.Len(), "After one second, old sample was added to the queue.") 51 | 52 | now = now.Add(15 * time.Second) 53 | addSample(8) 54 | assert.Equal(t, time.Duration(2), f.Estimate) 55 | now = now.Add(16 * time.Second) 56 | addSample(12) 57 | assert.Equal(t, time.Duration(8), f.Estimate, "After 30 seconds, old entries are removed from the queue.") 58 | 59 | } 60 | -------------------------------------------------------------------------------- /flowcontrol/bbr/limiter_test.go: -------------------------------------------------------------------------------- 1 | package bbr 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "capnproto.org/go/capnp/v3/exp/clock" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestReleaseLimiter(t *testing.T) { 14 | lim := NewLimiter(clock.System) 15 | lim.Release() 16 | select { 17 | case <-lim.ctx.Done(): 18 | case <-time.NewTimer(10 * time.Millisecond).C: 19 | t.Fatal("lim.Release did not cancel the context.") 20 | } 21 | 22 | _, err := lim.StartMessage(context.Background(), 1) 23 | assert.NotNil(t, err, "Error should be non-nil if the limiter has shut down.") 24 | assert.Equal(t, lim.ctx.Err(), err, "Error should be that of the limiter's context.") 25 | } 26 | 27 | func TestLimiterOneShot(t *testing.T) { 28 | lim := NewLimiter(clock.System) 29 | defer lim.Release() 30 | 31 | lim.whilePaused(func() { 32 | assert.Equal(t, lim.inflight(), uint64(0), 33 | "Limiter should start off with nothing in-flight.") 34 | }) 35 | 36 | got1, err := lim.StartMessage(context.Background(), 7) 37 | assert.Nil(t, err, "StartMessage() failed.") 38 | 39 | lim.whilePaused(func() { 40 | assert.Equal(t, lim.inflight(), uint64(7), 41 | "Once we send the message, limiter should have that much data in-flight.") 42 | }) 43 | 44 | got1() 45 | 46 | lim.whilePaused(func() { 47 | assert.Equal(t, lim.inflight(), uint64(0), 48 | "Once we receive the ack, in-flight data should be zero again.") 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /flowcontrol/bbr/queue_test.go: -------------------------------------------------------------------------------- 1 | package bbr 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestQueueEmpty(t *testing.T) { 10 | q := &queue[int]{} 11 | assert.True(t, q.Empty(), "The zero value is an empty queue") 12 | q.Push(0) 13 | assert.False(t, q.Empty(), "After adding a value, Empty() reports false") 14 | q.Pop() 15 | assert.True(t, q.Empty(), "After removing it, Empty() reports true again") 16 | } 17 | 18 | func TestQueuePeek(t *testing.T) { 19 | q := &queue[int]{} 20 | 21 | assert.Panics(t, func() { q.Peek() }, "Peek() panics on an empty queue.") 22 | 23 | q.Push(1) 24 | q.Push(2) 25 | 26 | assert.Equal(t, 2, q.Len(), "Len() returns the correct length") 27 | 28 | assert.Equal(t, 1, q.Peek(), "Peek() returns the first element in the queue") 29 | assert.Equal(t, 2, q.Len(), "Peek() does not change the length") 30 | assert.Equal(t, 1, q.Peek(), "Peek() returns the same element when called twice.") 31 | } 32 | 33 | func TestQueuePop(t *testing.T) { 34 | q := &queue[int]{} 35 | q.Push(1) 36 | q.Push(2) 37 | 38 | assert.Equal(t, 2, q.Len(), "initial length is correct") 39 | assert.Equal(t, 1, q.Pop(), "Pop() returns the first element") 40 | assert.Equal(t, 1, q.Len(), "length is updated") 41 | assert.Equal(t, 2, q.Pop(), "Pop() returns the next element") 42 | assert.Equal(t, 0, q.Len(), "length is updated") 43 | assert.True(t, q.Empty(), "Final queue is empty.") 44 | 45 | assert.Panics(t, func() { q.Pop() }, "Pop() panics on an emopty queue.") 46 | } 47 | 48 | func TestQueueItems(t *testing.T) { 49 | q := &queue[int]{} 50 | 51 | q.Push(1) 52 | q.Push(2) 53 | q.Push(3) 54 | 55 | concat := func(head, tail []int) []int { 56 | ret := make([]int, 0, len(head)+len(tail)) 57 | return append(append(ret, head...), tail...) 58 | } 59 | 60 | assert.Equal(t, []int{1, 2, 3}, concat(q.Items())) 61 | 62 | q.Pop() 63 | q.Push(4) 64 | 65 | assert.Equal(t, []int{2, 3, 4}, concat(q.Items())) 66 | } 67 | 68 | func TestQueueFold(t *testing.T) { 69 | q := &queue[int]{} 70 | 71 | q.Push(1) 72 | q.Push(2) 73 | q.Push(3) 74 | 75 | assert.Equal(t, 6, q.Fold(0, func(x, y int) int { 76 | return x + y 77 | })) 78 | 79 | q.Pop() 80 | q.Push(4) 81 | 82 | assert.Equal(t, 9, q.Fold(0, func(x, y int) int { 83 | return x + y 84 | })) 85 | } 86 | -------------------------------------------------------------------------------- /flowcontrol/fixed.go: -------------------------------------------------------------------------------- 1 | package flowcontrol 2 | 3 | import ( 4 | "context" 5 | 6 | "capnproto.org/go/capnp/v3/internal/str" 7 | "golang.org/x/sync/semaphore" 8 | ) 9 | 10 | // Returns a FlowLimiter that enforces a fixed limit on the total size of 11 | // outstanding messages. 12 | func NewFixedLimiter(size int64) FlowLimiter { 13 | return &fixedLimiter{ 14 | size: size, 15 | sem: semaphore.NewWeighted(size), 16 | } 17 | } 18 | 19 | type fixedLimiter struct { 20 | size int64 21 | sem *semaphore.Weighted 22 | } 23 | 24 | func (fl *fixedLimiter) StartMessage(ctx context.Context, size uint64) (gotResponse func(), err error) { 25 | // HACK: avoid dead-locking if the size of the message exceeds the maximum 26 | // reservation on the semaphore. We can't return an error because it 27 | // is currently ignored by the caller. 28 | if int64(size) > fl.size { 29 | panic("StartMessage(): message size " + 30 | str.Utod(size) + 31 | " is too large (max " + str.Itod(fl.size) + ")") 32 | } 33 | 34 | if err = fl.sem.Acquire(ctx, int64(size)); err == nil { 35 | gotResponse = func() { fl.sem.Release(int64(size)) } 36 | } 37 | 38 | return 39 | } 40 | 41 | func (fixedLimiter) Release() {} 42 | -------------------------------------------------------------------------------- /flowcontrol/fixed_test.go: -------------------------------------------------------------------------------- 1 | package flowcontrol 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestFixed(t *testing.T) { 13 | t.Parallel() 14 | 15 | ctx := context.Background() 16 | lim := NewFixedLimiter(10) 17 | 18 | // Start a couple messages: 19 | got4, err := lim.StartMessage(ctx, 4) 20 | require.NoError(t, err, "Limiter returned an error") 21 | got6, err := lim.StartMessage(ctx, 6) 22 | require.NoError(t, err, "Limiter returned an error") 23 | 24 | // We're now exactly at the limit, so if we try again it should block: 25 | func() { 26 | ctxTimeout, cancel := context.WithTimeout(ctx, 10*time.Millisecond) 27 | defer cancel() 28 | 29 | _, err = lim.StartMessage(ctxTimeout, 1) 30 | assert.ErrorIs(t, err, context.DeadlineExceeded, "should return context error") 31 | }() 32 | 33 | // Ok, finish one of them and then it should go through again: 34 | got4() 35 | got1, err := lim.StartMessage(ctx, 1) 36 | require.NoError(t, err, "Limiter returned an error") 37 | 38 | // There are 10 - (6 + 1) = 3 bytes remaining. It should therefore block 39 | // if we ask for four: 40 | func() { 41 | ctxTimeout, cancel := context.WithTimeout(ctx, 10*time.Millisecond) 42 | defer cancel() 43 | 44 | _, err = lim.StartMessage(ctxTimeout, 4) 45 | assert.ErrorIs(t, err, context.DeadlineExceeded, "should return context error") 46 | }() 47 | got6() 48 | got1() 49 | } 50 | 51 | func TestFixeLimiterPanics(t *testing.T) { 52 | t.Parallel() 53 | 54 | assert.Panics(t, func() { 55 | NewFixedLimiter(1024).StartMessage(context.Background(), 1025) 56 | }, "should panic if reservation would cause deadlock") 57 | } 58 | -------------------------------------------------------------------------------- /flowcontrol/flowlimiter.go: -------------------------------------------------------------------------------- 1 | // Package flowcontrol provides support code for per-object flow control. 2 | // 3 | // This is most important for dealing with streaming interfaces; see 4 | // https://capnproto.org/news/2020-04-23-capnproto-0.8.html#multi-stream-flow-control 5 | // for a description of the general problem. 6 | // 7 | // The Go implementation's approach differs from that of the C++ implementation in that 8 | // we don't treat the `stream` annotation specially; we instead do flow control on all 9 | // objects. Calls to methods will transparently block for the appropriate amount of 10 | // time, so it is safe to simply call rpc methods in a loop. 11 | // 12 | // To change the default flow control policy on a Client, call Client.SetFlowLimiter 13 | // with the desired FlowLimiter. 14 | package flowcontrol 15 | 16 | import ( 17 | "context" 18 | ) 19 | 20 | // A `FlowLimiter` is used to manage flow control for a stream of messages. 21 | type FlowLimiter interface { 22 | // StartMessage informs the flow limiter that the caller wants to 23 | // send a message of the specified size. It blocks until an appropriate 24 | // time to do so, or until the context is canceled. If the returned 25 | // error is nil, the caller should then proceed in sending the message 26 | // immediately, and it should arrange to call gotResponse() as soon as 27 | // a response is received. 28 | // 29 | // StartMessage must be safe to call from multiple goroutines. 30 | StartMessage(ctx context.Context, size uint64) (gotResponse func(), err error) 31 | 32 | // Release releases any resources used by the FlowLimiter. 33 | Release() 34 | } 35 | -------------------------------------------------------------------------------- /flowcontrol/internal/test-tool/.gitignore: -------------------------------------------------------------------------------- 1 | /test-tool 2 | *.json 3 | -------------------------------------------------------------------------------- /flowcontrol/internal/test-tool/plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Plot in-flight data over time, given the output of test-tool 4 | 5 | import sys 6 | import json 7 | import arrow 8 | from pprint import pprint 9 | import matplotlib.pyplot as plt 10 | 11 | raw = json.loads(open(sys.argv[1]).read()) 12 | 13 | events = [] 14 | i = 0 15 | for record in raw['Records']: 16 | events.append({ 17 | 'packet_id': i, 18 | 'type': 'request', 19 | 'time': arrow.get(record['RequestAt']), 20 | }) 21 | events.append({ 22 | 'packet_id': i, 23 | 'type': 'proceed', 24 | 'time': arrow.get(record['ProceedAt']), 25 | }) 26 | events.append({ 27 | 'packet_id': i, 28 | 'type': 'response', 29 | 'time': arrow.get(record['ResponseAt']), 30 | }) 31 | i += 1 32 | 33 | events.sort(key=lambda e: e['time']) 34 | 35 | start_time = events[0]['time'] 36 | end_time = events[-1]['time'] 37 | 38 | packet_size = raw['Records'][0]['Size'] 39 | bandwidth = raw['Bandwidth'] 40 | # Work out rtProp: 41 | rtProp = 1e100 42 | for record in raw['Records']: 43 | rtt = (arrow.get(record['ResponseAt']) - arrow.get(record['ProceedAt'])).total_seconds() 44 | if rtt < rtProp: 45 | rtProp = rtt 46 | 47 | bdp_in_bytes = bandwidth * rtProp 48 | bdp_in_packets = bdp_in_bytes / packet_size 49 | 50 | pprint({ 51 | "RtProp (seconds)": rtProp, 52 | "Bandwidth (bytes)": bandwidth, 53 | "BDP (bytes)": bdp_in_bytes, 54 | "BDP (packets)": bdp_in_packets, 55 | }) 56 | 57 | # plot current packets on wire over time. 58 | on_wire = 0 59 | x = [] 60 | y = [] 61 | for e in events: 62 | if e['type'] == 'proceed': 63 | on_wire += 1 64 | elif e['type'] == 'response': 65 | on_wire -= 1 66 | x.append((e['time'] - start_time).total_seconds()) 67 | y.append(on_wire) 68 | 69 | plt.title("packets on wire over time") 70 | plt.xlabel("time (seconds)") 71 | plt.ylabel("packets") 72 | plt.plot(x, y) 73 | 74 | plt.show(); 75 | -------------------------------------------------------------------------------- /flowcontrol/internal/test-tool/writer.capnp: -------------------------------------------------------------------------------- 1 | @0xaca73f831c7ebfdd; 2 | 3 | using Go = import "/go.capnp"; 4 | $Go.package("main"); 5 | $Go.import("capnproto.org/go/capnp/v3/flowcontrol/internal/test-tool"); 6 | 7 | interface Writer { 8 | write @0 (data :Data); 9 | } 10 | -------------------------------------------------------------------------------- /flowcontrol/nop.go: -------------------------------------------------------------------------------- 1 | package flowcontrol 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | var ( 8 | // A flow limiter which does not actually limit anything; messages will be 9 | // sent as fast as possible. 10 | NopLimiter FlowLimiter = nopLimiter{} 11 | ) 12 | 13 | type nopLimiter struct{} 14 | 15 | func (nopLimiter) StartMessage(context.Context, uint64) (func(), error) { 16 | return func() {}, nil 17 | } 18 | 19 | func (nopLimiter) Release() {} 20 | -------------------------------------------------------------------------------- /flowcontrol/tracing/tracelimiter.go: -------------------------------------------------------------------------------- 1 | // Package tracing implements support for tracing the behavior of a FlowLimiter. 2 | package tracing 3 | 4 | import ( 5 | context "context" 6 | "time" 7 | 8 | "capnproto.org/go/capnp/v3/flowcontrol" 9 | ) 10 | 11 | var _ flowcontrol.FlowLimiter = &TraceLimiter{} 12 | 13 | // A TraceLimiter wraps an underlying FlowLimiter, and records data about messages. 14 | type TraceLimiter struct { 15 | // The underlying FlowLimiter 16 | underlying flowcontrol.FlowLimiter 17 | emitRecord func(TraceRecord) 18 | } 19 | 20 | // Return a new TraceLimiter, wrapping underlying. Each time one of the limiter's gotResponse() 21 | // callbacks is invoked, emitRecord is called with data about the call. 22 | func New(underlying flowcontrol.FlowLimiter, emitRecord func(TraceRecord)) *TraceLimiter { 23 | return &TraceLimiter{ 24 | underlying: underlying, 25 | emitRecord: emitRecord, 26 | } 27 | } 28 | 29 | // A TraceRecord records information about a message sent through the limiter. 30 | type TraceRecord struct { 31 | Size uint64 // The size of the message 32 | RequestAt time.Time // The time at which StartMessage() was called. 33 | ProceedAt time.Time // The time at which StartMessage() returned. 34 | ResponseAt time.Time // The time at which gotResponse() was called. 35 | } 36 | 37 | // StartMessage implements FlowLimiter.StartMessage for TraceLimiter. 38 | func (l *TraceLimiter) StartMessage(ctx context.Context, size uint64) (gotResponse func(), err error) { 39 | record := TraceRecord{ 40 | Size: size, 41 | RequestAt: time.Now(), 42 | } 43 | r, err := l.underlying.StartMessage(ctx, size) 44 | if err != nil { 45 | return r, err 46 | } 47 | record.ProceedAt = time.Now() 48 | return func() { 49 | now := time.Now() 50 | r() 51 | record.ResponseAt = now 52 | l.emitRecord(record) 53 | }, nil 54 | } 55 | 56 | // Release releases the underlying flow limiter. 57 | func (l *TraceLimiter) Release() { 58 | l.underlying.Release() 59 | } 60 | -------------------------------------------------------------------------------- /gen.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | //go:generate go run internal/gen/gen.go 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module capnproto.org/go/capnp/v3 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 7 | github.com/kylelemons/godebug v1.1.0 8 | github.com/stretchr/testify v1.9.0 9 | github.com/tinylib/msgp v1.1.9 10 | github.com/tj/assert v0.0.3 11 | golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 12 | golang.org/x/sync v0.7.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/philhofer/fwd v1.1.2 // indirect 18 | github.com/pmezard/go-difflib v1.0.0 // indirect 19 | gopkg.in/yaml.v3 v3.0.1 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 h1:d5EKgQfRQvO97jnISfR89AiCCCJMwMFoSxUiU0OGCRU= 2 | github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381/go.mod h1:OU76gHeRo8xrzGJU3F3I1CqX1ekM8dfJw0+wPeMwnp0= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 7 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 8 | github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= 9 | github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= 10 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 15 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 16 | github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU= 17 | github.com/tinylib/msgp v1.1.9/go.mod h1:BCXGB54lDD8qUEPmiG0cQQUANC4IUQyB2ItS2UDlO/k= 18 | github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= 19 | github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= 20 | golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= 21 | golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= 22 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 23 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 24 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 25 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 26 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 27 | gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 28 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 29 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 30 | -------------------------------------------------------------------------------- /internal/aircraftlib/aircraft_test.go: -------------------------------------------------------------------------------- 1 | package aircraftlib 2 | 3 | import ( 4 | "capnproto.org/go/capnp/v3" 5 | ) 6 | 7 | var ( 8 | // Make sure interface types satisfy TypeParam: 9 | _ capnp.TypeParam[Echo] = Echo{} 10 | 11 | // ...and structs: 12 | _ capnp.TypeParam[Zdate] = Zdate{} 13 | 14 | // ...and lists: 15 | _ capnp.TypeParam[Echo_List] = Echo_List{} 16 | _ capnp.TypeParam[Zdate_List] = Zdate_List{} 17 | ) 18 | -------------------------------------------------------------------------------- /internal/aircraftlib/generate.go: -------------------------------------------------------------------------------- 1 | package aircraftlib 2 | 3 | //go:generate capnp compile -I ../../std -ogo aircraft.capnp 4 | -------------------------------------------------------------------------------- /internal/capnptool/capnptool.go: -------------------------------------------------------------------------------- 1 | // Package capnptool provides an API for calling the capnp tool in tests. 2 | package capnptool 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "os/exec" 9 | "strings" 10 | "sync" 11 | ) 12 | 13 | // Tool is the path to the capnp command-line tool. 14 | // It can be used from multiple goroutines. 15 | type Tool string 16 | 17 | var cache struct { 18 | init sync.Once 19 | tool Tool 20 | err error 21 | } 22 | 23 | // Find searches PATH for the capnp tool. 24 | func Find() (Tool, error) { 25 | cache.init.Do(func() { 26 | path, err := exec.LookPath("capnp") 27 | if err != nil { 28 | cache.err = err 29 | return 30 | } 31 | cache.tool = Tool(path) 32 | }) 33 | return cache.tool, cache.err 34 | } 35 | 36 | // Run executes the tool with the given stdin and arguments returns the stdout. 37 | func (tool Tool) Run(stdin io.Reader, args ...string) ([]byte, error) { 38 | c := exec.Command(string(tool), args...) 39 | c.Stdin = stdin 40 | stderr := new(bytes.Buffer) 41 | c.Stderr = stderr 42 | out, err := c.Output() 43 | if err != nil { 44 | return nil, fmt.Errorf("run `%s`: %v; stderr:\n%s", strings.Join(c.Args, " "), err, stderr) 45 | } 46 | return out, nil 47 | } 48 | 49 | // Encode encodes Cap'n Proto text into the binary representation. 50 | func (tool Tool) Encode(typ Type, text string) ([]byte, error) { 51 | return tool.Run(strings.NewReader(text), "encode", typ.SchemaPath, typ.Name) 52 | } 53 | 54 | // Decode decodes a Cap'n Proto message into text. 55 | func (tool Tool) Decode(typ Type, r io.Reader) (string, error) { 56 | out, err := tool.Run(r, "decode", "--short", typ.SchemaPath, typ.Name) 57 | if err != nil { 58 | return "", err 59 | } 60 | return string(out), nil 61 | } 62 | 63 | // DecodePacked decodes a packed Cap'n Proto message into text. 64 | func (tool Tool) DecodePacked(typ Type, r io.Reader) (string, error) { 65 | out, err := tool.Run(r, "decode", "--short", "--packed", typ.SchemaPath, typ.Name) 66 | if err != nil { 67 | return "", err 68 | } 69 | return string(out), nil 70 | } 71 | 72 | // Type is a reference to a Cap'n Proto type in a schema. 73 | type Type struct { 74 | SchemaPath string 75 | Name string 76 | } 77 | -------------------------------------------------------------------------------- /internal/demo/book_test.go: -------------------------------------------------------------------------------- 1 | package demo_test 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "capnproto.org/go/capnp/v3" 8 | "capnproto.org/go/capnp/v3/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 := capnp.NewSingleSegmentMessage(nil) 22 | 23 | // Create a new Book struct. Every message must have a root struct. 24 | book, err := books.NewRootBook(seg) 25 | if err != nil { 26 | panic(err) 27 | } 28 | book.SetTitle("War and Peace") 29 | book.SetPageCount(1440) 30 | 31 | // Write the message to stdout. 32 | err = capnp.NewEncoder(out).Encode(msg) 33 | if err != nil { 34 | panic(err) 35 | } 36 | } 37 | 38 | func reader(in io.Reader) { 39 | // Read the message from stdin. 40 | msg, err := capnp.NewDecoder(in).Decode() 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | // Extract the root struct from the message. 46 | book, err := books.ReadRootBook(msg) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | // Access fields from the struct. 52 | title, err := book.Title() 53 | if err != nil { 54 | panic(err) 55 | } 56 | pageCount := book.PageCount() 57 | fmt.Printf("%q has %d pages\n", title, pageCount) 58 | } 59 | -------------------------------------------------------------------------------- /internal/demo/books/books.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "/go.capnp"; 2 | @0x85d3acc39d94e0f8; 3 | $Go.package("books"); 4 | $Go.import("capnproto.org/go/capnp/v3/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/gen.go: -------------------------------------------------------------------------------- 1 | //go:generate capnp compile -I ../../../std -ogo books.capnp 2 | 3 | package books 4 | -------------------------------------------------------------------------------- /internal/demo/doc.go: -------------------------------------------------------------------------------- 1 | // Package demo contains example tests. 2 | package demo 3 | -------------------------------------------------------------------------------- /internal/fuzztest/corpus/blob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/blob -------------------------------------------------------------------------------- /internal/fuzztest/corpus/bool: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /internal/fuzztest/corpus/boolf: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /internal/fuzztest/corpus/boolvec: -------------------------------------------------------------------------------- 1 | 'Ir -------------------------------------------------------------------------------- /internal/fuzztest/corpus/datavec: -------------------------------------------------------------------------------- 1 | (*BHelloGood bye -------------------------------------------------------------------------------- /internal/fuzztest/corpus/f32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/f32 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/f32vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/f32vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/f64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/f64 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/f64vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/f64vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i16: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/i16 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i16vec: -------------------------------------------------------------------------------- 1 | ;c -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i32: -------------------------------------------------------------------------------- 1 | ᆳ -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i32vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/i32vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/i64 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i64vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/i64vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i8: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /internal/fuzztest/corpus/i8vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/i8vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/text: -------------------------------------------------------------------------------- 1 |  rHello, World! -------------------------------------------------------------------------------- /internal/fuzztest/corpus/textvec: -------------------------------------------------------------------------------- 1 | )Bsnarkle -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u16: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u16 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u16vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u16vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u32 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u32vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u32vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u64 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u64vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u64vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u8 -------------------------------------------------------------------------------- /internal/fuzztest/corpus/u8vec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/u8vec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/void: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /internal/fuzztest/corpus/zvec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/internal/fuzztest/corpus/zvec -------------------------------------------------------------------------------- /internal/fuzztest/corpus/zz: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /internal/gen/gen.go: -------------------------------------------------------------------------------- 1 | // This program generates some repetative method implementations 2 | // for the many types of list capnp provides. 3 | package main 4 | 5 | import ( 6 | "bufio" 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "text/template" 11 | ) 12 | 13 | func chkfatal(err error) { 14 | if err != nil { 15 | panic(err) 16 | } 17 | } 18 | 19 | var tpl = template.Must(template.New("list-gen").Parse(` 20 | func (l {{.}}) IsValid() bool { 21 | return List(l).IsValid() 22 | } 23 | 24 | func (l {{.}}) Len() int { 25 | return List(l).Len() 26 | } 27 | 28 | func (l {{.}}) EncodeAsPtr(seg *Segment) Ptr { 29 | return List(l).EncodeAsPtr(seg) 30 | } 31 | 32 | func ({{.}}) DecodeFromPtr(p Ptr) {{.}} { 33 | return {{.}}(List{}.DecodeFromPtr(p)) 34 | } 35 | 36 | func (l {{.}}) Message() *Message { 37 | return List(l).Message() 38 | } 39 | 40 | func (l {{.}}) Segment() *Segment { 41 | return List(l).Segment() 42 | } 43 | 44 | func (l {{.}}) ToPtr() Ptr { 45 | return List(l).ToPtr() 46 | } 47 | 48 | func (l {{.}}) primitiveElem(i int, expectedSize ObjectSize) (address, error) { 49 | return List(l).primitiveElem(i, expectedSize) 50 | } 51 | `)) 52 | 53 | func main() { 54 | listTypes := []string{ 55 | "VoidList", 56 | "BitList", 57 | "Float32List", 58 | "Float64List", 59 | "TextList", 60 | "DataList", 61 | "PointerList", 62 | "EnumList[T]", 63 | "StructList[T]", 64 | "CapList[T]", 65 | } 66 | for _, bits := range []int{8, 16, 32, 64} { 67 | listTypes = append(listTypes, []string{ 68 | fmt.Sprintf("Int%vList", bits), 69 | fmt.Sprintf("UInt%vList", bits), 70 | }...) 71 | } 72 | 73 | f, err := os.Create("list-gen.go") 74 | chkfatal(err) 75 | 76 | bw := bufio.NewWriter(f) 77 | bw.WriteString(` 78 | // Code generated by ./internal/gen/gen.go. DO NOT EDIT. 79 | 80 | package capnp 81 | `) 82 | for _, typ := range listTypes { 83 | chkfatal(tpl.Execute(bw, typ)) 84 | } 85 | chkfatal(bw.Flush()) 86 | chkfatal(f.Close()) 87 | chkfatal(exec.Command("gofmt", "-w", "list-gen.go").Run()) 88 | } 89 | -------------------------------------------------------------------------------- /internal/nodemap/nodemap.go: -------------------------------------------------------------------------------- 1 | // Package nodemap provides a schema registry index type. 2 | package nodemap 3 | 4 | import ( 5 | "capnproto.org/go/capnp/v3" 6 | "capnproto.org/go/capnp/v3/internal/schema" 7 | "capnproto.org/go/capnp/v3/schemas" 8 | ) 9 | 10 | // Map is a lazy index of a registry. 11 | // The zero value is an index of the default registry. 12 | type Map struct { 13 | reg *schemas.Registry 14 | nodes map[uint64]schema.Node 15 | } 16 | 17 | func (m *Map) registry() *schemas.Registry { 18 | if m.reg != nil { 19 | return m.reg 20 | } 21 | return schemas.DefaultRegistry 22 | } 23 | 24 | // UseRegistry assigns 'reg' to 'm' and initializes the nodes map. 25 | func (m *Map) UseRegistry(reg *schemas.Registry) { 26 | m.reg = reg 27 | m.nodes = make(map[uint64]schema.Node) 28 | } 29 | 30 | // Find returns the node for the given ID. 31 | func (m *Map) Find(id uint64) (schema.Node, error) { 32 | if n := m.nodes[id]; n.IsValid() { 33 | return n, nil 34 | } 35 | data, err := m.registry().Find(id) 36 | if err != nil { 37 | return schema.Node{}, err 38 | } 39 | msg, err := capnp.Unmarshal(data) 40 | if err != nil { 41 | return schema.Node{}, err 42 | } 43 | req, err := schema.ReadRootCodeGeneratorRequest(msg) 44 | if err != nil { 45 | return schema.Node{}, err 46 | } 47 | nodes, err := req.Nodes() 48 | if err != nil { 49 | return schema.Node{}, err 50 | } 51 | if m.nodes == nil { 52 | m.nodes = make(map[uint64]schema.Node) 53 | } 54 | for i := 0; i < nodes.Len(); i++ { 55 | n := nodes.At(i) 56 | m.nodes[n.Id()] = n 57 | } 58 | return m.nodes[id], nil 59 | } 60 | -------------------------------------------------------------------------------- /internal/rc/rc.go: -------------------------------------------------------------------------------- 1 | package rc 2 | 3 | import "sync/atomic" 4 | 5 | type Releaser struct { 6 | release func() 7 | refcount int32 8 | } 9 | 10 | func NewReleaser(init int32, release func()) *Releaser { 11 | return &Releaser{ 12 | release: release, 13 | refcount: init, 14 | } 15 | } 16 | 17 | func (rc *Releaser) Decr() { 18 | newCount := atomic.AddInt32(&rc.refcount, -1) 19 | if newCount == 0 { 20 | rc.release() 21 | rc.release = nil 22 | } else if newCount < 0 { 23 | panic("Decremented an already-zero refcount") 24 | } 25 | } 26 | 27 | func (rc *Releaser) Incr() { 28 | newCount := atomic.AddInt32(&rc.refcount, 1) 29 | if newCount == 1 { 30 | panic("Incremented an already-zero refcount") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /internal/str/str.go: -------------------------------------------------------------------------------- 1 | // Helpers for formatting strings. We avoid the use of the fmt package for the benefit 2 | // of environments that care about minimizing executable size. 3 | package str 4 | 5 | import ( 6 | "strconv" 7 | "strings" 8 | 9 | "unsafe" // Only for formatting pointers as integers; we don't actually do anything unsafe. 10 | ) 11 | 12 | // Utod formats unsigned integers as decimals. 13 | func Utod[T Uint](n T) string { 14 | return strconv.FormatUint(uint64(n), 10) 15 | } 16 | 17 | // Itod formats signed integers as decimals. 18 | func Itod[T Int](n T) string { 19 | return strconv.FormatInt(int64(n), 10) 20 | } 21 | 22 | // UToHex returns n formatted in hexidecimal. 23 | func UToHex[T Uint](n T) string { 24 | return strconv.FormatUint(uint64(n), 16) 25 | } 26 | 27 | func PtrToHex[T any](p *T) string { 28 | return UToHex(uintptr(unsafe.Pointer(p))) 29 | } 30 | 31 | // ZeroPad pads value to the left with zeros, making the resulting string 32 | // count bytes long. 33 | func ZeroPad(count int, value string) string { 34 | pad := count - len(value) 35 | if pad < 0 { 36 | panic("ZeroPad: count is less than len(value)") 37 | } 38 | buf := make([]byte, count) 39 | for i := 0; i < pad; i++ { 40 | buf[i] = '0' 41 | } 42 | copy(buf[:pad], value[:]) 43 | return string(buf) 44 | } 45 | 46 | // Slice formats a slice of values which themselves implement Stringer. 47 | func Slice[T Stringer](s []T) string { 48 | var b strings.Builder 49 | b.WriteRune('{') 50 | for i, v := range s { 51 | if i > 0 { 52 | b.WriteString(", ") 53 | } 54 | b.WriteString(v.String()) 55 | } 56 | b.WriteRune('}') 57 | return b.String() 58 | } 59 | 60 | // Stringer is equivalent to fmt.Stringer 61 | type Stringer interface { 62 | String() string 63 | } 64 | 65 | type Uint interface { 66 | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uint | ~uintptr 67 | } 68 | 69 | type Int interface { 70 | ~int8 | ~int16 | ~int32 | ~int64 | ~int 71 | } 72 | -------------------------------------------------------------------------------- /internal/strquote/strquote.go: -------------------------------------------------------------------------------- 1 | // Package strquote provides a function for formatting a string as a 2 | // Cap'n Proto string literal. 3 | package strquote 4 | 5 | // Append appends a Cap'n Proto string literal of s to buf. 6 | func Append(buf []byte, s []byte) []byte { 7 | buf = append(buf, '"') 8 | last := 0 9 | for i, b := range s { 10 | if !needsEscape(b) { 11 | continue 12 | } 13 | buf = append(buf, s[last:i]...) 14 | switch b { 15 | case '\a': 16 | buf = append(buf, '\\', 'a') 17 | case '\b': 18 | buf = append(buf, '\\', 'b') 19 | case '\f': 20 | buf = append(buf, '\\', 'f') 21 | case '\n': 22 | buf = append(buf, '\\', 'n') 23 | case '\r': 24 | buf = append(buf, '\\', 'r') 25 | case '\t': 26 | buf = append(buf, '\\', 't') 27 | case '\v': 28 | buf = append(buf, '\\', 'v') 29 | case '\'': 30 | buf = append(buf, '\\', '\'') 31 | case '"': 32 | buf = append(buf, '\\', '"') 33 | case '\\': 34 | buf = append(buf, '\\', '\\') 35 | default: 36 | buf = append(buf, '\\', 'x', hexDigit(b/16), hexDigit(b%16)) 37 | } 38 | last = i + 1 39 | } 40 | buf = append(buf, s[last:]...) 41 | buf = append(buf, '"') 42 | return buf 43 | } 44 | 45 | func needsEscape(b byte) bool { 46 | return b < 0x20 || b >= 0x7f || b == '\\' || b == '"' || b == '\'' 47 | } 48 | 49 | func hexDigit(b byte) byte { 50 | const digits = "0123456789abcdef" 51 | return digits[b] 52 | } 53 | -------------------------------------------------------------------------------- /internal/strquote/strquote_test.go: -------------------------------------------------------------------------------- 1 | package strquote 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestAppend(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | in string 12 | expect string 13 | }{ 14 | {"printable", "Hello! No escaping.", `"Hello! No escaping."`}, 15 | {"controls", "\a\b\f\n\r\t\v", `"\a\b\f\n\r\t\v"`}, 16 | {"quotes", "\"'", `"\"\'"`}, 17 | {"backslash", "\\", `"\\"`}, 18 | {"binary", "\x00\x1f\x7f\xff", `"\x00\x1f\x7f\xff"`}, 19 | } 20 | 21 | for _, tc := range tests { 22 | t.Run(tc.name, func(t *testing.T) { 23 | var buf []byte 24 | out := Append(buf, []byte(tc.in)) 25 | if !bytes.Equal(out, []byte(tc.expect)) { 26 | t.Errorf("Append(%q) = %q; want %q", tc.in, out, tc.expect) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /internal/syncutil/with.go: -------------------------------------------------------------------------------- 1 | // misc. utilities for synchronization. 2 | package syncutil 3 | 4 | import ( 5 | "sync" 6 | ) 7 | 8 | // Runs f while holding the lock 9 | func With(l sync.Locker, f func()) { 10 | l.Lock() 11 | defer l.Unlock() 12 | f() 13 | } 14 | 15 | // Runs f while not holding the lock 16 | func Without(l sync.Locker, f func()) { 17 | l.Unlock() 18 | defer l.Lock() 19 | f() 20 | } 21 | -------------------------------------------------------------------------------- /localpromise.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // ClientHook for a promise that will be resolved to some other capability 4 | // at some point. Buffers calls in a queue until the promsie is fulfilled, 5 | // then forwards them. 6 | type localPromise struct { 7 | aq *AnswerQueue 8 | } 9 | 10 | // NewLocalPromise returns a client that will eventually resolve to a capability, 11 | // supplied via the resolver. 12 | func NewLocalPromise[C ~ClientKind]() (C, Resolver[C]) { 13 | aq := NewAnswerQueue(Method{}) 14 | f := NewPromise(Method{}, aq, aq) 15 | p := f.Answer().Client().AddRef() 16 | 17 | c := C(p) 18 | r := localResolver[C]{ 19 | p: f, 20 | c: c, 21 | } 22 | return c, r 23 | } 24 | 25 | type localResolver[C ~ClientKind] struct { 26 | p *Promise 27 | c C 28 | } 29 | 30 | func (lf localResolver[C]) Fulfill(c C) { 31 | msg, seg := NewSingleSegmentMessage(nil) 32 | Client(lf.c).AttachReleaser(lf.p.ReleaseClients) 33 | Client(lf.c).AttachReleaser(msg.Release) 34 | capID := msg.CapTable().Add(Client(c)) 35 | iface := NewInterface(seg, capID) 36 | lf.p.Fulfill(iface.ToPtr()) 37 | } 38 | 39 | func (lf localResolver[C]) Reject(err error) { 40 | Client(lf.c).AttachReleaser(lf.p.ReleaseClients) 41 | lf.p.Reject(err) 42 | } 43 | -------------------------------------------------------------------------------- /metadata.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Metadata is a morally a map[any]any which implements 8 | // sync.Locker; it is used by the rpc system to attach bookkeeping 9 | // information to various objects. 10 | // 11 | // The zero value is not meaningful, and the Metadata must not be copied 12 | // after its first use. 13 | type Metadata struct { 14 | mu sync.Mutex 15 | values map[any]any 16 | } 17 | 18 | // Lock the metadata map. 19 | func (m *Metadata) Lock() { 20 | m.mu.Lock() 21 | } 22 | 23 | // Unlock the metadata map. 24 | func (m *Metadata) Unlock() { 25 | m.mu.Unlock() 26 | } 27 | 28 | // Look up key in the map. Returns the value, and a boolean which is 29 | // false if the key was not present. 30 | func (m *Metadata) Get(key any) (value any, ok bool) { 31 | value, ok = m.values[key] 32 | return 33 | } 34 | 35 | // Insert the key, value pair into the map. 36 | func (m *Metadata) Put(key, value any) { 37 | m.values[key] = value 38 | } 39 | 40 | // Delete the key from the map. 41 | func (m *Metadata) Delete(key any) { 42 | delete(m.values, key) 43 | } 44 | 45 | // Allocate and return a freshly initialized Metadata. 46 | func NewMetadata() *Metadata { 47 | return &Metadata{ 48 | values: make(map[any]any), 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /msgp_bench_test.go: -------------------------------------------------------------------------------- 1 | //go:build msgpbench 2 | // +build msgpbench 3 | 4 | //go:generate msgp -tests=false -o msgp_bench_gen_test.go 5 | //msgp:Tuple Event 6 | 7 | package capnp_test 8 | 9 | import ( 10 | "bytes" 11 | "io" 12 | "math/rand" 13 | "testing" 14 | "time" 15 | 16 | "github.com/tinylib/msgp/msgp" 17 | ) 18 | 19 | type Event struct { 20 | Name string 21 | BirthDay time.Time 22 | Phone string 23 | Siblings int 24 | Spouse bool 25 | Money float64 26 | } 27 | 28 | func BenchmarkUnmarshalMsgp(b *testing.B) { 29 | r := rand.New(rand.NewSource(12345)) 30 | data := make([][]byte, 1000) 31 | for i := range data { 32 | msg, _ := (*Event)(generateA(r)).MarshalMsg(nil) 33 | data[i] = msg 34 | } 35 | 36 | b.ReportAllocs() 37 | b.ResetTimer() 38 | 39 | for i := 0; i < b.N; i++ { 40 | var e Event 41 | msg := data[r.Intn(len(data))] 42 | _, err := e.UnmarshalMsg(msg) 43 | if err != nil { 44 | b.Fatal(err) 45 | } 46 | } 47 | } 48 | 49 | func BenchmarkUnmarshalMsgpReader(b *testing.B) { 50 | var buf bytes.Buffer 51 | 52 | r := rand.New(rand.NewSource(12345)) 53 | w := msgp.NewWriter(&buf) 54 | count := 10000 55 | 56 | for i := 0; i < count; i++ { 57 | event := (*Event)(generateA(r)) 58 | err := event.EncodeMsg(w) 59 | if err != nil { 60 | b.Fatal(err) 61 | } 62 | } 63 | 64 | w.Flush() 65 | blob := buf.Bytes() 66 | 67 | b.ReportAllocs() 68 | b.SetBytes(int64(buf.Len())) 69 | b.ResetTimer() 70 | 71 | for i := 0; i < b.N; i++ { 72 | r := msgp.NewReader(bytes.NewReader(blob)) 73 | 74 | for { 75 | var e Event 76 | err := e.DecodeMsg(r) 77 | 78 | if err == io.EOF { 79 | break 80 | } 81 | 82 | if err != nil { 83 | b.Fatal(err) 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packed/fuzz.go: -------------------------------------------------------------------------------- 1 | //go:build gofuzz 2 | // +build gofuzz 3 | 4 | // Fuzz test harness. To run: 5 | // go-fuzz-build capnproto.org/go/capnp/v3/internal/packed 6 | // go-fuzz -bin=packed-fuzz.zip -workdir=internal/packed/testdata 7 | 8 | package packed 9 | 10 | import ( 11 | "bufio" 12 | "bytes" 13 | "io" 14 | ) 15 | 16 | func Fuzz(data []byte) int { 17 | result := 0 18 | 19 | // Unpacked 20 | if unpacked, err := Unpack(nil, data); err == nil { 21 | checkRepack(unpacked) 22 | result = 1 23 | } 24 | 25 | // Read 26 | { 27 | r := NewReader(bufio.NewReader(bytes.NewReader(data))) 28 | if unpacked, err := io.ReadAll(r); err == nil { 29 | checkRepack(unpacked) 30 | result = 1 31 | } 32 | } 33 | 34 | // ReadWord 35 | { 36 | r := NewReader(bufio.NewReader(bytes.NewReader(data))) 37 | var unpacked []byte 38 | var err error 39 | for { 40 | n := len(unpacked) 41 | unpacked = append(unpacked, 0, 0, 0, 0, 0, 0, 0, 0) 42 | if err = r.ReadWord(unpacked[n:]); err != nil { 43 | unpacked = unpacked[:n] 44 | break 45 | } 46 | } 47 | if err == io.EOF { 48 | checkRepack(unpacked) 49 | result = 1 50 | } 51 | } 52 | 53 | return result 54 | } 55 | 56 | func checkRepack(unpacked []byte) { 57 | packed := Pack(nil, unpacked) 58 | unpacked2, err := Unpack(nil, packed) 59 | if err != nil { 60 | panic("correctness: unpack, pack, unpack gives error: " + err.Error()) 61 | } 62 | if !bytes.Equal(unpacked, unpacked2) { 63 | panic("correctness: unpack, pack, unpack gives different results") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packed/testdata/corpus/03bf57ea7bdec71698adeae87a862cec7164d40b-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/03bf57ea7bdec71698adeae87a862cec7164d40b-3 -------------------------------------------------------------------------------- /packed/testdata/corpus/0d25a482d77c50ce11c4e19513273663cb7f2760-3: -------------------------------------------------------------------------------- 1 | 1#8188940354856483008125� -------------------------------------------------------------------------------- /packed/testdata/corpus/0ea83e2670b5efd4c1488e7d3b8d47444edcc0de-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/0ea83e2670b5efd4c1488e7d3b8d47444edcc0de-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/0ebc873dc501946f782b80d9ac30e458e3f4c3bb-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/0ebc873dc501946f782b80d9ac30e458e3f4c3bb-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/11e6f1cf4e924b97fa749270ef3776ca2f622e4a-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/11e6f1cf4e924b97fa749270ef3776ca2f622e4a-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/1a03558a19149e8a602fa447a75203e565c5bd24-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/1a03558a19149e8a602fa447a75203e565c5bd24-6 -------------------------------------------------------------------------------- /packed/testdata/corpus/1cbdf332ea41a8607123a73db6848cb1e08a88e8-7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/1cbdf332ea41a8607123a73db6848cb1e08a88e8-7 -------------------------------------------------------------------------------- /packed/testdata/corpus/1f0255d132314eceb4cb53f8b7edba2b5acca411-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/1f0255d132314eceb4cb53f8b7edba2b5acca411-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/25e8b15e130bd6fbac4db86667c6585220e649fd-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/25e8b15e130bd6fbac4db86667c6585220e649fd-6 -------------------------------------------------------------------------------- /packed/testdata/corpus/27602d7c8f418c1648200cbbb80652dfa1d32ec3-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/27602d7c8f418c1648200cbbb80652dfa1d32ec3-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/309aa6808b3440a343e40d2539c4a8f32a7d22c6-7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/309aa6808b3440a343e40d2539c4a8f32a7d22c6-7 -------------------------------------------------------------------------------- /packed/testdata/corpus/32126b1f830c3950f99f7d6c9d4fd501c50fc95a-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/32126b1f830c3950f99f7d6c9d4fd501c50fc95a-3 -------------------------------------------------------------------------------- /packed/testdata/corpus/36c4bfda59a7f22e1d84c2559edf589e1591ab4e-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/36c4bfda59a7f22e1d84c2559edf589e1591ab4e-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/386c071e2d382223ad16da6458ac74ab61995d22-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/386c071e2d382223ad16da6458ac74ab61995d22-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/397ed0b402b6615879ec32c874ad2ce1189a5566: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /packed/testdata/corpus/3dedffca29bf233d7aab140d1ebe4f7a393aaf25-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/3dedffca29bf233d7aab140d1ebe4f7a393aaf25-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/43275097819916f57c004e3d2d7292cd4c494103-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/43275097819916f57c004e3d2d7292cd4c494103-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/46758529a6f8bdab34817ecc3b60bdeb8fc7d1df-2: -------------------------------------------------------------------------------- 1 | d� -------------------------------------------------------------------------------- /packed/testdata/corpus/46d2e26b94a1e361bbb9561f6bf82f709952079a-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/46d2e26b94a1e361bbb9561f6bf82f709952079a-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/4fbde0ace86d682a07e20940ff917f4c6243c848-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/4fbde0ace86d682a07e20940ff917f4c6243c848-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/53ff759b338edc76af39e47cba96506942f8fcf4-1: -------------------------------------------------------------------------------- 1 | t. -------------------------------------------------------------------------------- /packed/testdata/corpus/5b7a93398637f041f5b2aa27f5506e070c4ab93a-10: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/5b7a93398637f041f5b2aa27f5506e070c4ab93a-10 -------------------------------------------------------------------------------- /packed/testdata/corpus/62b362cbeb20f5993b35fa4104af45169e729567-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/62b362cbeb20f5993b35fa4104af45169e729567-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/6545fc074cb9ff87f00d92da2bb0e43fb655a1a0-8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/6545fc074cb9ff87f00d92da2bb0e43fb655a1a0-8 -------------------------------------------------------------------------------- /packed/testdata/corpus/6648f3613b1f7b827c1f8e57dfd6d98297afc120-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/6648f3613b1f7b827c1f8e57dfd6d98297afc120-3 -------------------------------------------------------------------------------- /packed/testdata/corpus/672e3be57da3f6260bd1224ab122c43db7531c68-1: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /packed/testdata/corpus/685749732874f297765f6aa9cb2149ae8a760a30-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/685749732874f297765f6aa9cb2149ae8a760a30-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/6b71d6625925391d3e5d5df85ed6520b3bdff697-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/6b71d6625925391d3e5d5df85ed6520b3bdff697-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/6d7d243c36e5b97f088139d68d659a76d8f1a85e-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/6d7d243c36e5b97f088139d68d659a76d8f1a85e-3 -------------------------------------------------------------------------------- /packed/testdata/corpus/733795216f2196269dfb2230101048a8d1a900ef-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/733795216f2196269dfb2230101048a8d1a900ef-4 -------------------------------------------------------------------------------- /packed/testdata/corpus/79e96c2c0556cf294cb5ccc84fbe6f688b722a4b-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/79e96c2c0556cf294cb5ccc84fbe6f688b722a4b-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/82449e84f12b12e2e78cd1837063af01f771a8c3-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/82449e84f12b12e2e78cd1837063af01f771a8c3-4 -------------------------------------------------------------------------------- /packed/testdata/corpus/85c90a0758e25bc682dc2f169cc60394b1aef429-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/85c90a0758e25bc682dc2f169cc60394b1aef429-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/86bb19d6ebfdcb2986e2f5f19e8b1d064bb143b6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/86bb19d6ebfdcb2986e2f5f19e8b1d064bb143b6 -------------------------------------------------------------------------------- /packed/testdata/corpus/8f842db7cc972786baa4aef0bb39cedd92313ff0-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/8f842db7cc972786baa4aef0bb39cedd92313ff0-6 -------------------------------------------------------------------------------- /packed/testdata/corpus/928cde18fcd017b7d9f617e6efb1ad3d335106d9-9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/928cde18fcd017b7d9f617e6efb1ad3d335106d9-9 -------------------------------------------------------------------------------- /packed/testdata/corpus/950b28b26d2efaa8ca8b77551c9cc4fb9bfa73c7-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/950b28b26d2efaa8ca8b77551c9cc4fb9bfa73c7-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/95fb90dbbc7bd9d57a1c3c43fa2d9c0370f26f36-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/95fb90dbbc7bd9d57a1c3c43fa2d9c0370f26f36-6 -------------------------------------------------------------------------------- /packed/testdata/corpus/96e65fffcc37d53d86825858d055dac113e71283-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/96e65fffcc37d53d86825858d055dac113e71283-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/97fdb523adca4cf3c5d71dbab9de9507b480152b-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/97fdb523adca4cf3c5d71dbab9de9507b480152b-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/9a7501617cc3b5f64b6940734fe49de0458fa866-8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/9a7501617cc3b5f64b6940734fe49de0458fa866-8 -------------------------------------------------------------------------------- /packed/testdata/corpus/a188a06676ab0e07a1922dee6ecc00957e9222cf-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/a188a06676ab0e07a1922dee6ecc00957e9222cf-3 -------------------------------------------------------------------------------- /packed/testdata/corpus/a2df0bcacc5318483ed62a88b7b27986e4e101a9-3: -------------------------------------------------------------------------------- 1 | ! P -------------------------------------------------------------------------------- /packed/testdata/corpus/b4ec4c0d6ffdae8d6ca8a15b03e2ce94ffda6185-7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/b4ec4c0d6ffdae8d6ca8a15b03e2ce94ffda6185-7 -------------------------------------------------------------------------------- /packed/testdata/corpus/b51b0adced7f2ed05a9fb588dddac5573d13fffb-10: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/b51b0adced7f2ed05a9fb588dddac5573d13fffb-10 -------------------------------------------------------------------------------- /packed/testdata/corpus/bf380df4c460fd5a77baea70c5d2eabed6a955a4-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/bf380df4c460fd5a77baea70c5d2eabed6a955a4-4 -------------------------------------------------------------------------------- /packed/testdata/corpus/c0901a103fd4ab6c7679be138aac5f02d006b382-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/c0901a103fd4ab6c7679be138aac5f02d006b382-3 -------------------------------------------------------------------------------- /packed/testdata/corpus/c7d6fe913d12c724c7f902cdf1a330c66a08c5ef-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/c7d6fe913d12c724c7f902cdf1a330c66a08c5ef-5 -------------------------------------------------------------------------------- /packed/testdata/corpus/dae7780e3c39c06f06b99d50e5f03f9eb8419867-9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/dae7780e3c39c06f06b99d50e5f03f9eb8419867-9 -------------------------------------------------------------------------------- /packed/testdata/corpus/dc3509027712532d00a7e0f4ba924d7f75281019-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/dc3509027712532d00a7e0f4ba924d7f75281019-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/df61e7c056c523a702b8098125570d13cf47ea06-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/df61e7c056c523a702b8098125570d13cf47ea06-6 -------------------------------------------------------------------------------- /packed/testdata/corpus/e02bb49673980eb7f449d5281b965cbc1301e549-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/e02bb49673980eb7f449d5281b965cbc1301e549-4 -------------------------------------------------------------------------------- /packed/testdata/corpus/e636ce666b77bbc7ddfab8f464be687aa5daff48-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/e636ce666b77bbc7ddfab8f464be687aa5daff48-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/ef6fb041e0fda30b19f739195125a612ccd325d8-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/ef6fb041e0fda30b19f739195125a612ccd325d8-1 -------------------------------------------------------------------------------- /packed/testdata/corpus/empty.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/empty.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/f0e5464685f75b1a98506138e11267a49bf18a2a-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/f0e5464685f75b1a98506138e11267a49bf18a2a-2 -------------------------------------------------------------------------------- /packed/testdata/corpus/f5869ee9335f12414c447da8226e95ce31b0d333-1: -------------------------------------------------------------------------------- 1 | d� -------------------------------------------------------------------------------- /packed/testdata/corpus/f74fb617fe3663c2f51026f6b3bc2f78f6a13b30-4: -------------------------------------------------------------------------------- 1 | 1#8188940354856483008125 -------------------------------------------------------------------------------- /packed/testdata/corpus/mixed.dat: -------------------------------------------------------------------------------- 1 | $ " -------------------------------------------------------------------------------- /packed/testdata/corpus/mixed_mixed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/mixed_mixed.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/mixed_nozero.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/mixed_nozero.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/mixed_zero_zero_zero_mixed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/mixed_zero_zero_zero_mixed.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/nozero.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/nozero.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/nozero_nozero.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/nozero_nozero.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/nozero_nozero_mixed_nozero_mixed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/nozero_nozero_mixed_nozero_mixed.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/nozero_nozero_nozero_nozero.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/nozero_nozero_nozero_nozero.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/nozero_nozero_nozero_nozero_mixed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/nozero_nozero_nozero_nozero_mixed.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/realworld.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/realworld.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/shortbenchmark.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/shortbenchmark.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/zero.dat: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packed/testdata/corpus/zero_nozero.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capnproto/go-capnp/0b34935959cb2d6d2e99b5814e9dd733d2c6b105/packed/testdata/corpus/zero_nozero.dat -------------------------------------------------------------------------------- /packed/testdata/corpus/zero_zero_zero_zero.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /pogs/bench_test.go: -------------------------------------------------------------------------------- 1 | package pogs 2 | 3 | import ( 4 | "encoding/hex" 5 | "math/rand" 6 | "testing" 7 | 8 | "capnproto.org/go/capnp/v3" 9 | air "capnproto.org/go/capnp/v3/internal/aircraftlib" 10 | ) 11 | 12 | type A struct { 13 | Name string 14 | BirthDay int64 15 | Phone string 16 | Siblings int32 17 | Spouse bool 18 | Money float64 19 | } 20 | 21 | func generateA(r *rand.Rand) *A { 22 | return &A{ 23 | Name: randString(r, 16), 24 | BirthDay: r.Int63(), 25 | Phone: randString(r, 10), 26 | Siblings: r.Int31n(5), 27 | Spouse: r.Intn(2) == 1, 28 | Money: r.Float64(), 29 | } 30 | } 31 | 32 | func BenchmarkExtract(b *testing.B) { 33 | r := rand.New(rand.NewSource(12345)) 34 | data := make([][]byte, 1000) 35 | for i := range data { 36 | a := generateA(r) 37 | msg, seg := capnp.NewSingleSegmentMessage(nil) 38 | root, _ := air.NewRootBenchmarkA(seg) 39 | Insert(air.BenchmarkA_TypeID, capnp.Struct(root), a) 40 | data[i], _ = msg.Marshal() 41 | } 42 | b.ReportAllocs() 43 | b.ResetTimer() 44 | for i := 0; i < b.N; i++ { 45 | msg, _ := capnp.Unmarshal(data[r.Intn(len(data))]) 46 | root, _ := msg.Root() 47 | var a A 48 | Extract(&a, air.BenchmarkA_TypeID, root.Struct()) 49 | } 50 | } 51 | 52 | func BenchmarkInsert(b *testing.B) { 53 | r := rand.New(rand.NewSource(12345)) 54 | data := make([]*A, 1000) 55 | for i := range data { 56 | data[i] = generateA(r) 57 | } 58 | arena := make([]byte, 0, 512) 59 | b.ReportAllocs() 60 | b.ResetTimer() 61 | for i := 0; i < b.N; i++ { 62 | a := data[r.Intn(len(data))] 63 | msg, seg := capnp.NewSingleSegmentMessage(arena[:0]) 64 | root, _ := air.NewRootBenchmarkA(seg) 65 | Insert(air.BenchmarkA_TypeID, capnp.Struct(root), a) 66 | msg.Marshal() 67 | } 68 | } 69 | 70 | func randString(r *rand.Rand, n int) string { 71 | b := make([]byte, (n+1)/2) 72 | // Go 1.6 adds a Rand.Read method, but since we want to be compatible with Go 1.4... 73 | for i := range b { 74 | b[i] = byte(r.Intn(255)) 75 | } 76 | return hex.EncodeToString(b)[:n] 77 | } 78 | -------------------------------------------------------------------------------- /pogs/example_test.go: -------------------------------------------------------------------------------- 1 | package pogs_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "capnproto.org/go/capnp/v3" 7 | "capnproto.org/go/capnp/v3/internal/demo/books" 8 | "capnproto.org/go/capnp/v3/pogs" 9 | "capnproto.org/go/capnp/v3/schemas" 10 | ) 11 | 12 | func init() { 13 | books.RegisterSchema(schemas.DefaultRegistry) 14 | } 15 | 16 | var bookData = []byte{ 17 | 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 19 | 0xa0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x01, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 21 | 0x57, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 22 | 0x50, 0x65, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, 23 | } 24 | 25 | func ExampleExtract() { 26 | // books.capnp: 27 | // struct Book { 28 | // title @0 :Text; 29 | // pageCount @1 :Int32; 30 | // } 31 | 32 | type Book struct { 33 | Title string 34 | PageCount int32 35 | } 36 | 37 | // Read the message from bytes. 38 | msg, err := capnp.Unmarshal(bookData) 39 | if err != nil { 40 | panic(err) 41 | } 42 | root, err := msg.Root() 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | // Extract the book from the root struct. 48 | b := new(Book) 49 | if err := pogs.Extract(b, books.Book_TypeID, root.Struct()); err != nil { 50 | panic(err) 51 | } 52 | fmt.Printf("%q has %d pages\n", b.Title, b.PageCount) 53 | 54 | // Output: 55 | // "War and Peace" has 1440 pages 56 | } 57 | 58 | func ExampleInsert() { 59 | // books.capnp: 60 | // struct Book { 61 | // title @0 :Text; 62 | // pageCount @1 :Int32; 63 | // } 64 | 65 | type Book struct { 66 | Title string 67 | PageCount int32 68 | } 69 | 70 | // Allocate a new Cap'n Proto Book struct. 71 | _, seg := capnp.NewSingleSegmentMessage(nil) 72 | root, err := books.NewRootBook(seg) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | // Insert the book struct into the Cap'n Proto struct. 78 | b := &Book{ 79 | Title: "War and Peace", 80 | PageCount: 1440, 81 | } 82 | if err := pogs.Insert(books.Book_TypeID, capnp.Struct(root), b); err != nil { 83 | panic(err) 84 | } 85 | fmt.Println(root) 86 | 87 | // Output: 88 | // (title = "War and Peace", pageCount = 1440) 89 | } 90 | -------------------------------------------------------------------------------- /regen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # regen.sh - update capnpc-go and regenerate schemas 3 | set -euo pipefail 4 | 5 | cd "$(dirname "$0")" 6 | 7 | echo "** go generate" 8 | go generate 9 | 10 | echo "** capnpc-go" 11 | # Run tests so that we don't install a broken capnpc-go. 12 | (cd capnpc-go && go generate && go test && go install) 13 | 14 | echo "** schemas" 15 | (cd std/capnp; ../gen.sh compile) 16 | (cd std/capnp; capnp compile --no-standard-import -I.. -o- schema.capnp) | (cd internal/schema && capnpc-go -promises=0 -schemas=0 -structstrings=0) 17 | ( 18 | top="$PWD" 19 | cd flowcontrol/internal/test-tool 20 | capnp compile --no-standard-import -I "$top/std" -ogo *.capnp 21 | ) 22 | go generate ./... 23 | -------------------------------------------------------------------------------- /request.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | // A Request is a method call to be sent. Create one with NewReqeust, and send it with 9 | // Request.Send(). 10 | type Request struct { 11 | method Method 12 | args Struct 13 | client Client 14 | releaseResponse ReleaseFunc 15 | future *Future 16 | } 17 | 18 | // NewRequest creates a new request calling the specified method on the specified client. 19 | // argsSize is the size of the arguments struct. 20 | func NewRequest(client Client, method Method, argsSize ObjectSize) (*Request, error) { 21 | _, seg := NewMultiSegmentMessage(nil) 22 | args, err := NewStruct(seg, argsSize) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return &Request{ 27 | method: method, 28 | args: args, 29 | client: client, 30 | }, nil 31 | } 32 | 33 | // Args returns the arguments struct for this request. The arguments must not 34 | // be accessed after the request is sent. 35 | func (r *Request) Args() Struct { 36 | return r.args 37 | } 38 | 39 | func (r *Request) getSend() Send { 40 | return Send{ 41 | Method: r.method, 42 | PlaceArgs: func(args Struct) error { 43 | err := args.CopyFrom(r.args) 44 | r.releaseArgs() 45 | return err 46 | }, 47 | ArgsSize: r.args.Size(), 48 | } 49 | } 50 | 51 | // Send sends the request, returning a future for its results. 52 | func (r *Request) Send(ctx context.Context) *Future { 53 | if r.future != nil { 54 | return ErrorAnswer(r.method, errors.New("sent the same request twice")).Future() 55 | } 56 | 57 | ans, rel := r.client.SendCall(ctx, r.getSend()) 58 | r.releaseResponse = rel 59 | r.future = ans.Future() 60 | return r.future 61 | } 62 | 63 | // SendStream is to send as Client.SendStreamCall is to Client.SendCall 64 | func (r *Request) SendStream(ctx context.Context) error { 65 | if r.future != nil { 66 | return errors.New("sent the same request twice") 67 | } 68 | 69 | return r.client.SendStreamCall(ctx, r.getSend()) 70 | } 71 | 72 | // Future returns a future for the requests results. Returns nil if 73 | // called before the request is sent. 74 | func (r *Request) Future() *Future { 75 | return r.future 76 | } 77 | 78 | // Release resources associated with the request. In particular: 79 | // 80 | // - Release the arguments if they have not yet been released. 81 | // - If the request has been sent, wait for the result and release 82 | // the results. 83 | func (r *Request) Release() { 84 | r.releaseArgs() 85 | rel := r.releaseResponse 86 | if rel != nil { 87 | r.releaseResponse = nil 88 | r.future = nil 89 | rel() 90 | } 91 | } 92 | 93 | func (r *Request) releaseArgs() { 94 | if !r.args.IsValid() { 95 | msg := r.args.Message() 96 | r.args = Struct{} 97 | msg.Release() 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /resolver.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // A Resolver supplies a value for a pending promise. 4 | type Resolver[T any] interface { 5 | // Fulfill supplies the value for the corresponding 6 | // Promise 7 | Fulfill(T) 8 | 9 | // Reject rejects the corresponding promise, with 10 | // the specified error. 11 | Reject(error) 12 | } 13 | -------------------------------------------------------------------------------- /rpc/errors.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "errors" 5 | 6 | "capnproto.org/go/capnp/v3/exc" 7 | ) 8 | 9 | var ( 10 | rpcerr = exc.Annotator("rpc") 11 | 12 | // Base errors 13 | ErrConnClosed = errors.New("connection closed") 14 | ErrNotACapability = errors.New("not a capability") 15 | ErrCapTablePopulated = errors.New("capability table already populated") 16 | 17 | // RPC exceptions 18 | ExcClosed = rpcerr.Disconnected(ErrConnClosed) 19 | ) 20 | 21 | type errReporter struct { 22 | Logger Logger 23 | } 24 | 25 | func (er errReporter) Debug(msg string, args ...any) { 26 | if er.Logger != nil { 27 | er.Logger.Debug(msg, args...) 28 | } 29 | } 30 | 31 | func (er errReporter) Info(msg string, args ...any) { 32 | if er.Logger != nil { 33 | er.Logger.Info(msg, args...) 34 | } 35 | } 36 | 37 | func (er errReporter) Warn(msg string, args ...any) { 38 | if er.Logger != nil { 39 | er.Logger.Warn(msg, args...) 40 | } 41 | } 42 | 43 | func (er errReporter) Error(msg string, args ...any) { 44 | if er.Logger != nil { 45 | er.Logger.Error(msg, args...) 46 | } 47 | } 48 | 49 | func (er errReporter) ReportError(err error) { 50 | if err != nil { 51 | er.Error(err.Error()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rpc/idgen.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // idgen returns a sequence of monotonically increasing IDs with 4 | // support for replacement. The zero value is a generator that 5 | // starts at zero. 6 | type idgen[T ~uint32] struct { 7 | i uint32 8 | free uintSet 9 | } 10 | 11 | func (gen *idgen[T]) next() T { 12 | if first, ok := gen.free.min(); ok { 13 | gen.free.remove(first) 14 | return T(first) 15 | } 16 | i := gen.i 17 | if i == ^uint32(0) { 18 | // TODO(soon): make this abort the connection. 19 | // All ID generation should be under application control, not 20 | // remote, but remote could hypothetically retain 1<<32 exports 21 | // to overflow this. 22 | panic("overflow ID") 23 | } 24 | gen.i++ 25 | return T(i) 26 | } 27 | 28 | func (gen *idgen[T]) remove(i T) { 29 | gen.free.add(uint(i)) 30 | } 31 | 32 | // A uintSet is a set of unsigned integers represented by a bit set. 33 | // This data type assumes that the integers are packed closely to zero. 34 | // The zero value is the empty set. 35 | type uintSet []uint64 36 | 37 | func (s uintSet) has(i uint) bool { 38 | j := i / 64 39 | mask := uint64(1) << (i % 64) 40 | return j < uint(len(s)) && s[j]&mask != 0 41 | } 42 | 43 | func (s *uintSet) add(i uint) { 44 | j := i / 64 45 | mask := uint64(1) << (i % 64) 46 | if j >= uint(len(*s)) { 47 | s2 := make(uintSet, j+1) 48 | copy(s2, *s) 49 | *s = s2 50 | } 51 | (*s)[j] |= mask 52 | } 53 | 54 | func (s uintSet) remove(i uint) { 55 | j := i / 64 56 | mask := uint64(1) << (i % 64) 57 | if j < uint(len(s)) { 58 | s[j] &^= mask 59 | } 60 | } 61 | 62 | func (s uintSet) min() (_ uint, ok bool) { 63 | for i, x := range s { 64 | if x == 0 { 65 | continue 66 | } 67 | for j := uint(0); j < 64; j++ { 68 | if x&(1< (empty :Empty); 16 | } 17 | 18 | interface PingPong { 19 | echoNum @0 (n :Int64) -> (n :Int64); 20 | } 21 | 22 | interface StreamTest { 23 | push @0 (data :Data) -> stream; 24 | } 25 | 26 | interface CapArgsTest { 27 | call @0 (cap :Capability); 28 | self @1 () -> (self :CapArgsTest); 29 | } 30 | 31 | interface PingPongProvider { 32 | pingPong @0 () -> (pingPong :PingPong); 33 | } 34 | -------------------------------------------------------------------------------- /rpc/internal/testnetwork/generate.go: -------------------------------------------------------------------------------- 1 | package testnetwork 2 | 3 | //go:generate capnp compile -I ../../../std -ogo testnetwork.capnp 4 | -------------------------------------------------------------------------------- /rpc/internal/testnetwork/testnetwork.capnp: -------------------------------------------------------------------------------- 1 | @0xbcea0965c2a55c5b; 2 | # This schema defines the concrete format of third-party handoff 3 | # related data types used by the test network. 4 | 5 | using Go = import "/go.capnp"; 6 | $Go.package("testnetwork"); 7 | $Go.import("capnproto.org/go/capnp/v3/rpc/internal/testnetwork"); 8 | 9 | struct PeerAndNonce { 10 | # A pair of peer ID and a nonce. This is the format for all 11 | # three of ProvisionId, RecipientId, and ThirdPartyCapId, 12 | # though which peer the id refers to differs. 13 | 14 | peerId @0 :UInt64; 15 | nonce @1 :UInt64; 16 | } 17 | 18 | using ProvisionId = PeerAndNonce; 19 | # peerId is that of the peer that sends the provide. 20 | 21 | using RecipientId = PeerAndNonce; 22 | # peerId is that of the peer that sends the accept. 23 | 24 | using ThirdPartyCapId = PeerAndNonce; 25 | # peerId is that of the peer that hosts the capability. 26 | -------------------------------------------------------------------------------- /rpc/localpromise_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | 8 | "capnproto.org/go/capnp/v3" 9 | "capnproto.org/go/capnp/v3/rpc/internal/testcapnp" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | type echoNumOrderChecker struct { 14 | t *testing.T 15 | nextNum int64 16 | skipAssertNum bool 17 | } 18 | 19 | func (e *echoNumOrderChecker) EchoNum(ctx context.Context, p testcapnp.PingPong_echoNum) error { 20 | if !e.skipAssertNum { 21 | assert.Equal(e.t, e.nextNum, p.Args().N()) 22 | } 23 | e.nextNum++ 24 | results, err := p.AllocResults() 25 | if err != nil { 26 | return err 27 | } 28 | results.SetN(p.Args().N()) 29 | return nil 30 | } 31 | 32 | func TestLocalPromiseFulfill(t *testing.T) { 33 | t.Parallel() 34 | 35 | ctx := context.Background() 36 | p, r := capnp.NewLocalPromise[testcapnp.PingPong]() 37 | defer p.Release() 38 | 39 | fut1, rel1 := p.EchoNum(ctx, func(p testcapnp.PingPong_echoNum_Params) error { 40 | p.SetN(1) 41 | return nil 42 | }) 43 | defer rel1() 44 | 45 | fut2, rel2 := p.EchoNum(ctx, func(p testcapnp.PingPong_echoNum_Params) error { 46 | p.SetN(2) 47 | return nil 48 | }) 49 | defer rel2() 50 | 51 | pp := testcapnp.PingPong_ServerToClient(&echoNumOrderChecker{ 52 | t: t, 53 | nextNum: 1, 54 | }) 55 | defer pp.Release() 56 | r.Fulfill(pp) 57 | 58 | fut3, rel3 := p.EchoNum(ctx, func(p testcapnp.PingPong_echoNum_Params) error { 59 | p.SetN(3) 60 | return nil 61 | }) 62 | defer rel3() 63 | 64 | res1, err1 := fut1.Struct() 65 | res2, err2 := fut2.Struct() 66 | res3, err3 := fut3.Struct() 67 | 68 | assert.NoError(t, err1) 69 | assert.Equal(t, int64(1), res1.N()) 70 | assert.NoError(t, err2) 71 | assert.Equal(t, int64(2), res2.N()) 72 | assert.NoError(t, err3) 73 | assert.Equal(t, int64(3), res3.N()) 74 | } 75 | 76 | func echoNum(ctx context.Context, pp testcapnp.PingPong, n int64) (testcapnp.PingPong_echoNum_Results_Future, capnp.ReleaseFunc) { 77 | return pp.EchoNum(ctx, func(p testcapnp.PingPong_echoNum_Params) error { 78 | p.SetN(n) 79 | return nil 80 | }) 81 | } 82 | 83 | func TestLocalPromiseReject(t *testing.T) { 84 | t.Parallel() 85 | 86 | ctx := context.Background() 87 | p, r := capnp.NewLocalPromise[testcapnp.PingPong]() 88 | defer p.Release() 89 | 90 | fut1, rel1 := echoNum(ctx, p, 1) 91 | defer rel1() 92 | 93 | fut2, rel2 := echoNum(ctx, p, 2) 94 | defer rel2() 95 | 96 | r.Reject(errors.New("Promise rejected")) 97 | 98 | fut3, rel3 := echoNum(ctx, p, 3) 99 | defer rel3() 100 | 101 | _, err := fut1.Struct() 102 | assert.NotNil(t, err) 103 | 104 | _, err = fut2.Struct() 105 | assert.NotNil(t, err) 106 | 107 | _, err = fut3.Struct() 108 | assert.NotNil(t, err) 109 | } 110 | -------------------------------------------------------------------------------- /rpc/pipeline_chain_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "capnproto.org/go/capnp/v3" 8 | "capnproto.org/go/capnp/v3/rpc/internal/testcapnp" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestPipelineChaining(t *testing.T) { 13 | t.Parallel() 14 | 15 | bg := context.Background() 16 | 17 | np := func(cat testcapnp.CapArgsTest_self_Params) error { 18 | return nil 19 | } 20 | 21 | ctx, cancel := context.WithCancel(bg) 22 | cli := testcapnp.CapArgsTest_ServerToClient(&delayingCapReturner{ctx}) 23 | defer cli.Release() 24 | 25 | // Originally, chaining pipelines like this didn't work correctly. 26 | 27 | fut1, rel1 := cli.Self(bg, np) 28 | defer rel1() 29 | // This one was fine 30 | fut2, rel2 := fut1.Self().Self(bg, np) 31 | defer rel2() 32 | // This one would be delivered to the wrong place 33 | // (the same place as the previous one) 34 | fut3, rel3 := fut2.Self().Self(bg, np) 35 | defer rel3() 36 | // This one would segfault 37 | fut4, rel4 := fut3.Self().Self(bg, np) 38 | defer rel4() 39 | 40 | cancel() 41 | _, err := fut4.Struct() 42 | 43 | assert.Nil(t, err) 44 | } 45 | 46 | type delayingCapReturner struct { 47 | context.Context 48 | } 49 | 50 | func (c delayingCapReturner) Call(ctx context.Context, call testcapnp.CapArgsTest_call) error { 51 | return capnp.Unimplemented("yes") 52 | } 53 | 54 | func (c delayingCapReturner) Self(ctx context.Context, call testcapnp.CapArgsTest_self) error { 55 | call.Go() 56 | select { 57 | case <-ctx.Done(): 58 | case <-c.Done(): 59 | } 60 | 61 | results, err := call.AllocResults() 62 | if err != nil { 63 | return err 64 | } 65 | 66 | results.SetSelf(testcapnp.CapArgsTest_ServerToClient(selfCapReturner{})) 67 | return nil 68 | } 69 | 70 | type selfCapReturner struct{} 71 | 72 | func (c selfCapReturner) Call(ctx context.Context, call testcapnp.CapArgsTest_call) error { 73 | return capnp.Unimplemented("yes") 74 | } 75 | 76 | func (c selfCapReturner) Self(ctx context.Context, call testcapnp.CapArgsTest_self) error { 77 | results, err := call.AllocResults() 78 | if err != nil { 79 | return err 80 | } 81 | 82 | results.SetSelf(testcapnp.CapArgsTest_ServerToClient(c)) 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /rpc/shutdown_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "testing" 7 | 8 | "capnproto.org/go/capnp/v3" 9 | "capnproto.org/go/capnp/v3/rpc" 10 | "capnproto.org/go/capnp/v3/rpc/internal/testcapnp" 11 | "capnproto.org/go/capnp/v3/rpc/transport" 12 | ) 13 | 14 | // TestRejectOnDisconnect verifies that, when a connection is dropped, outstanding calls 15 | // fail with a "disconnected" exception. 16 | func TestRejectOnDisconnect(t *testing.T) { 17 | t.Parallel() 18 | 19 | ctx := context.Background() 20 | 21 | serverNetConn, clientNetConn := net.Pipe() 22 | 23 | // The remote server will close this to acknowledge that the call has reached the other 24 | // side; this makes sure we are testing the right logic, not the scenario where the 25 | // connection is dropped before the message is sent, which could fail with an IO error, 26 | // as opposed to being rejected on shutdown. 27 | readyCh := make(chan struct{}) 28 | 29 | serverRpcConn := rpc.NewConn(transport.NewStream(serverNetConn), &rpc.Options{ 30 | BootstrapClient: capnp.Client(testcapnp.PingPong_ServerToClient(dropPingServer{ 31 | readyCh: readyCh, 32 | })), 33 | }) 34 | 35 | clientRpcConn := rpc.NewConn(transport.NewStream(clientNetConn), nil) 36 | 37 | client := testcapnp.PingPong(clientRpcConn.Bootstrap(ctx)) 38 | defer client.Release() 39 | future, release := client.EchoNum(ctx, nil) 40 | defer release() 41 | <-readyCh 42 | serverNetConn.Close() 43 | _, err := future.Struct() 44 | if !capnp.IsDisconnected(err) { 45 | t.Fatalf("Wanted disconnected error but got %v", err) 46 | } 47 | <-serverRpcConn.Done() 48 | <-clientRpcConn.Done() 49 | } 50 | 51 | type dropPingServer struct { 52 | readyCh chan<- struct{} 53 | } 54 | 55 | func (s dropPingServer) EchoNum(ctx context.Context, p testcapnp.PingPong_echoNum) error { 56 | p.Go() 57 | close(s.readyCh) 58 | <-ctx.Done() 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /rpc/streaming_test.go: -------------------------------------------------------------------------------- 1 | package rpc_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "capnproto.org/go/capnp/v3/rpc/internal/testcapnp" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // TestStreamingWaitOk verifies that if no errors occur in streaming calls, 13 | // WaitStreaming retnurs nil. 14 | func TestStreamingWaitOk(t *testing.T) { 15 | ctx := context.Background() 16 | client := testcapnp.StreamTest_ServerToClient(&maxPushStream{limit: 1}) 17 | defer client.Release() 18 | assert.NoError(t, client.Push(ctx, nil)) 19 | assert.NoError(t, client.WaitStreaming()) 20 | } 21 | 22 | // TestStreamingWaitErr verifies that if an error occurs in a streaming call, 23 | // it shows up in a subsequent call to WaitStreaming(). 24 | func TestStreamingWaitErr(t *testing.T) { 25 | ctx := context.Background() 26 | client := testcapnp.StreamTest_ServerToClient(&maxPushStream{limit: 0}) 27 | defer client.Release() 28 | assert.NoError(t, client.Push(ctx, nil)) 29 | assert.NotNil(t, client.WaitStreaming()) 30 | } 31 | 32 | // A maxPushStream is an implementation of StreamTest that 33 | // starts returning errors after a specified number of calls. 34 | type maxPushStream struct { 35 | count int // How many calls have we seen? 36 | limit int // How many calls are permitted? 37 | } 38 | 39 | func (m *maxPushStream) Push(context.Context, testcapnp.StreamTest_push) error { 40 | m.count++ 41 | if m.count > m.limit { 42 | return fmt.Errorf("Exceeded limit of %v calls", m.limit) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /rpc/transport.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // Re-export things from the transport package 4 | 5 | import ( 6 | "io" 7 | 8 | "capnproto.org/go/capnp/v3/rpc/transport" 9 | ) 10 | 11 | type Codec = transport.Codec 12 | type Transport = transport.Transport 13 | type NewTransportFunc func(io.ReadWriteCloser) Transport 14 | 15 | // NewStreamTransport is an alias for as transport.NewStream 16 | func NewStreamTransport(rwc io.ReadWriteCloser) Transport { 17 | return transport.NewStream(rwc) 18 | } 19 | 20 | // NewPackedStreamTransport is an alias for as transport.NewPackedStream 21 | func NewPackedStreamTransport(rwc io.ReadWriteCloser) Transport { 22 | return transport.NewPackedStream(rwc) 23 | } 24 | 25 | // NewTransport is an alias for as transport.New 26 | func NewTransport(codec Codec) Transport { 27 | return transport.New(codec) 28 | } 29 | -------------------------------------------------------------------------------- /rpc/transport/errors.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import "capnproto.org/go/capnp/v3/exc" 4 | 5 | var transporterr = exc.Annotator("transport") 6 | -------------------------------------------------------------------------------- /rpc/transport/pipe.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "io" 5 | "sync" 6 | 7 | capnp "capnproto.org/go/capnp/v3" 8 | "capnproto.org/go/capnp/v3/internal/syncutil" 9 | ) 10 | 11 | // NewPipe returns a pair of codecs which communicate over 12 | // channels, copying messages at the channel boundary. 13 | // bufSz is the size of the channel buffers. 14 | func NewPipe(bufSz int) (c1, c2 Codec) { 15 | ch1 := make(chan *capnp.Message, bufSz) 16 | ch2 := make(chan *capnp.Message, bufSz) 17 | 18 | c1 = &pipe{ 19 | send: ch1, 20 | recv: ch2, 21 | closed: make(chan struct{}), 22 | } 23 | 24 | c2 = &pipe{ 25 | send: ch2, 26 | recv: ch1, 27 | closed: make(chan struct{}), 28 | } 29 | 30 | return 31 | } 32 | 33 | type pipe struct { 34 | // Must hold while sending or closing `send`: 35 | sendMu sync.Mutex 36 | 37 | send chan<- *capnp.Message 38 | recv <-chan *capnp.Message 39 | closed chan struct{} 40 | } 41 | 42 | func (p *pipe) Encode(m *capnp.Message) (err error) { 43 | b, err := m.Marshal() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if m, err = capnp.Unmarshal(b); err != nil { 49 | return err 50 | } 51 | 52 | p.sendMu.Lock() 53 | defer p.sendMu.Unlock() 54 | select { 55 | case p.send <- m: 56 | return nil 57 | case <-p.closed: 58 | return io.ErrClosedPipe 59 | } 60 | } 61 | 62 | func (p *pipe) Decode() (*capnp.Message, error) { 63 | select { 64 | case <-p.closed: 65 | return nil, io.ErrClosedPipe 66 | case m, ok := <-p.recv: 67 | if !ok { 68 | return nil, io.ErrClosedPipe 69 | } 70 | return m, nil 71 | } 72 | 73 | } 74 | 75 | func (*pipe) ReleaseMessage(*capnp.Message) {} 76 | 77 | func (p *pipe) Close() error { 78 | close(p.closed) 79 | syncutil.With(&p.sendMu, func() { 80 | close(p.send) 81 | p.send = nil 82 | }) 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /rpc/transport/pipe_test.go: -------------------------------------------------------------------------------- 1 | package transport_test 2 | 3 | import ( 4 | "io" 5 | "testing" 6 | 7 | "capnproto.org/go/capnp/v3" 8 | "capnproto.org/go/capnp/v3/rpc/transport" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestPipe(t *testing.T) { 13 | t.Parallel() 14 | 15 | m, _ := capnp.NewSingleSegmentMessage(nil) 16 | 17 | p1, p2 := transport.NewPipe(1) 18 | 19 | err := p1.Encode(m) 20 | require.NoError(t, err) 21 | 22 | m2, err := p2.Decode() 23 | require.NoError(t, err) 24 | require.NotEqual(t, m, m2, "message should have been copied") 25 | 26 | err = p1.Close() 27 | require.NoError(t, err) 28 | 29 | err = p1.Encode(m) 30 | require.ErrorIs(t, err, io.ErrClosedPipe) 31 | 32 | m, err = p2.Decode() 33 | require.Nil(t, m) 34 | require.ErrorIs(t, err, io.ErrClosedPipe) 35 | } 36 | -------------------------------------------------------------------------------- /schemas/schemas_test.go: -------------------------------------------------------------------------------- 1 | package schemas_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "capnproto.org/go/capnp/v3" 7 | "capnproto.org/go/capnp/v3/internal/schema" 8 | "capnproto.org/go/capnp/v3/schemas" 9 | gocp "capnproto.org/go/capnp/v3/std/go" 10 | ) 11 | 12 | func TestDefaultFind(t *testing.T) { 13 | gocp.RegisterSchema(schemas.DefaultRegistry) 14 | if s := schemas.Find(0xdeadbeef); s != nil { 15 | t.Errorf("schemas.Find(0xdeadbeef) = %d-byte slice; want nil", len(s)) 16 | } 17 | s := schemas.Find(gocp.Package) 18 | if s == nil { 19 | t.Fatalf("schemas.Find(%#x) = nil", gocp.Package) 20 | } 21 | msg, err := capnp.Unmarshal(s) 22 | if err != nil { 23 | t.Fatalf("capnp.Unmarshal(schemas.Find(%#x)) error: %v", gocp.Package, err) 24 | } 25 | req, err := schema.ReadRootCodeGeneratorRequest(msg) 26 | if err != nil { 27 | t.Fatalf("ReadRootCodeGeneratorRequest error: %v", err) 28 | } 29 | nodes, err := req.Nodes() 30 | if err != nil { 31 | t.Fatalf("req.Nodes() error: %v", err) 32 | } 33 | for i := 0; i < nodes.Len(); i++ { 34 | n := nodes.At(i) 35 | if n.Id() == gocp.Package { 36 | // Found 37 | if n.Which() != schema.Node_Which_annotation { 38 | t.Errorf("found node %#x which = %v; want annotation", gocp.Package, n.Which()) 39 | } 40 | return 41 | } 42 | } 43 | t.Fatalf("could not find node %#x in registry", gocp.Package) 44 | } 45 | 46 | func TestNotFound(t *testing.T) { 47 | reg := new(schemas.Registry) 48 | _, err := reg.Find(0) 49 | if err == nil { 50 | t.Error("new(schemas.Registry).Find(0) = nil; want not found error") 51 | } 52 | if !schemas.IsNotFound(err) { 53 | t.Errorf("new(schemas.Registry).Find(0) = %v; want not found error", err) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /server/example_test.go: -------------------------------------------------------------------------------- 1 | package server_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "capnproto.org/go/capnp/v3" 7 | "capnproto.org/go/capnp/v3/server" 8 | ) 9 | 10 | func ExampleIsServer() { 11 | x := int(42) 12 | c := capnp.NewClient(server.New([]server.Method{}, x, nil)) 13 | snapshot := c.Snapshot() 14 | brand := snapshot.Brand() 15 | snapshot.Release() 16 | if brand, ok := server.IsServer(brand); ok { 17 | fmt.Println("Client is a server, got brand:", brand) 18 | } 19 | // Output: 20 | // Client is a server, got brand: 42 21 | } 22 | -------------------------------------------------------------------------------- /std/capnp/README.md: -------------------------------------------------------------------------------- 1 | This directory contains go packages for all of the capnproto 2 | schemas that ship with capnproto itself. They are generated with 3 | the help of `./gen.sh`. Though some manual modifications have been made 4 | to the schema to correct name collisions (See below). 5 | 6 | # `./gen.sh` Usage 7 | 8 | Executing: 9 | 10 | ./gen.sh import /path/to/schemas 11 | 12 | Will copy all the `*.capnp` files from the given directory into this 13 | directory, and add annotations to them specifying package names and 14 | import paths for `capnpc-go`. 15 | 16 | ./gen.sh compile 17 | 18 | Will generate go packages for each of the schemas in the current 19 | directory. 20 | 21 | ./gen.sh clean-go 22 | 23 | Will remove all generated go source files from this directory. Finally, 24 | 25 | ./gen.sh clean-all 26 | 27 | Will remove both the go source files and the imported schemas. 28 | 29 | `gen.sh` does some name mangling to ensure that the generated packages 30 | are actually legal go. However, this is not meant to be a 31 | general-purpose solution; it is only intended to work for the base 32 | schemas. 33 | 34 | # Footnote: annotations get an underscore in certain cases 35 | 36 | Under certain circumstances, `capnpc-go` will rename the identifier when it 37 | generates go code. As an example, if two declarations `struct Foo` and 38 | `annotation foo` exist in the schema, `capnpc-go` has to capitalize 39 | `annotation foo` in the generated code. But that would not compile since 40 | there is already a `Foo` from `struct Foo`. 41 | 42 | To address this kind of issue, `capnpc-go` will change the annotation to 43 | `Foo_` (by adding the trailing "_"). This can be overridden on a 44 | case-by-case basis if you need some other name. Just add `$Go.name`: 45 | 46 | ``` 47 | annotation foo(struct, field) :Void $Go.name("fooAnnotation"); 48 | ``` 49 | 50 | # Directory Structure 51 | 52 | The directory structure of this repository is designed such that when 53 | compiling other schema, it should be sufficient to execute: 54 | 55 | capnp compile -I ${path_to_this_repository}/std -ogo ${schama_name}.capnp 56 | 57 | And have the `$import` statements in existing schema "just work." 58 | 59 | To achieve this, the base schemas themselves are stored as 60 | `/std/capnp/${schema_name}.capnp`. The generated go source files are 61 | stored in a subdirectory, to make them their own package: 62 | `/std/capnp/${mangled_schema_name}/${mangled_schema_name}.capnp.go`. 63 | 64 | In addition to the upstream base schemas, we also ship a schema 65 | `/std/go.capnp`, which contains annotations used by `go-capnpc`. Its 66 | usage is described in the top-level README. The generated source is 67 | placed in the root of the repository, making it part of the go package 68 | `capnproto.org/go/capnp/v3`. 69 | -------------------------------------------------------------------------------- /std/capnp/c++.capnp: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 | # Licensed under the MIT License: 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | @0xbdf87d7bb8304e81; 23 | $namespace("capnp::annotations"); 24 | 25 | annotation namespace(file): Text; 26 | annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text; 27 | 28 | annotation allowCancellation(interface, method, file) :Void; 29 | # Indicates that the server-side implementation of a method is allowed to be canceled when the 30 | # client requests cancellation. Without this annotation, once a method call has been delivered to 31 | # the server-side application code, any requests by the client to cancel it will be ignored, and 32 | # the method will run to completion anyway. This applies even for local in-process calls. 33 | # 34 | # This behavior applies specifically to implementations that inherit from the C++ `Foo::Server` 35 | # interface. The annotation won't affect DynamicCapability::Server implementations; they must set 36 | # the cancellation mode at runtime. 37 | # 38 | # When applied to an interface rather than an individual method, the annotation applies to all 39 | # methods in the interface. When applied to a file, it applies to all methods defined in the file. 40 | # 41 | # It's generally recommended that this annotation be applied to all methods. However, when doing 42 | # so, it is important that the server implementation use cancellation-safe code. See: 43 | # 44 | # https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#cancellation 45 | # 46 | # If your code is not cancellation-safe, then allowing cancellation might give a malicious client 47 | # an easy way to induce use-after-free or other bugs in your server, by requesting cancellation 48 | # when not expected. 49 | using Go = import "/go.capnp"; 50 | $Go.package("cxx"); 51 | $Go.import("capnproto.org/go/capnp/v3/std/capnp/cxx"); 52 | -------------------------------------------------------------------------------- /std/capnp/compat/byte-stream.capnp: -------------------------------------------------------------------------------- 1 | @0x8f5d14e1c273738d; 2 | 3 | using Cxx = import "/capnp/c++.capnp"; 4 | $Cxx.namespace("capnp"); 5 | $Cxx.allowCancellation; 6 | 7 | interface ByteStream { 8 | write @0 (bytes :Data) -> stream; 9 | # Write a chunk. 10 | 11 | end @1 (); 12 | # Signals clean EOF. (If the ByteStream is dropped without calling this, then the stream was 13 | # prematurely canceled and so the body should not be considered complete.) 14 | 15 | getSubstream @2 (callback :SubstreamCallback, 16 | limit :UInt64 = 0xffffffffffffffff) -> (substream :ByteStream); 17 | # This method is used to implement path shortening optimization. It is designed in particular 18 | # with KJ streams' pumpTo() in mind. 19 | # 20 | # getSubstream() returns a new stream object that can be used to write to the same destination 21 | # as this stream. The substream will operate until it has received `limit` bytes, or its `end()` 22 | # method has been called, whichever occurs first. At that time, it invokes one of the methods of 23 | # `callback` based on the termination condition. 24 | # 25 | # While a substream is active, it is an error to call write() on the original stream. Doing so 26 | # may throw an exception or may arbitrarily interleave bytes with the substream's writes. 27 | 28 | startTls @3 (expectedServerHostname :Text) -> stream; 29 | # Client calls this method when it wants to initiate TLS. This ByteStream is not terminated, 30 | # the caller should reuse it. 31 | 32 | interface SubstreamCallback { 33 | ended @0 (byteCount :UInt64); 34 | # `end()` was called on the substream after writing `byteCount` bytes. The `end()` call was 35 | # NOT forwarded to the underlying stream, which remains open. 36 | 37 | reachedLimit @1 () -> (next :ByteStream); 38 | # The number of bytes specified by the `limit` parameter of `getSubstream()` was reached. 39 | # The substream will "resolve itself" to `next`, so that all future calls to the substream 40 | # are forwarded to `next`. 41 | # 42 | # If the `write()` call which reached the limit included bytes past the limit, then the first 43 | # `write()` call to `next` will be for those leftover bytes. 44 | } 45 | } 46 | using Go = import "/go.capnp"; 47 | $Go.package("bytestream"); 48 | $Go.import("capnproto.org/go/capnp/v3/std/capnp/compat/bytestream"); 49 | -------------------------------------------------------------------------------- /std/capnp/compat/json-rpc.capnp: -------------------------------------------------------------------------------- 1 | @0xd04299800d6725ba; 2 | 3 | $import "/capnp/c++.capnp".namespace("capnp::json"); 4 | 5 | using Json = import "/capnp/compat/json.capnp"; 6 | 7 | struct RpcMessage { 8 | jsonrpc @0 :Text; 9 | # Must always be "2.0". 10 | 11 | id @1 :Json.Value; 12 | # Correlates a request to a response. Technically must be a string or number. Our implementation 13 | # will always use a number for calls it initiates, and will reflect IDs of any type for calls 14 | # it receives. 15 | # 16 | # May be omitted when caller doesn't care about the response. The implementation will omit `id` 17 | # and return immediately when calling methods with the annotation `@notification` (defined in 18 | # `json.capnp`). The `@notification` annotation only matters for outgoing calls; for incoming 19 | # calls, it's the client's decision whether it wants to receive the response. 20 | 21 | method @2 :Text; 22 | # Method name. Only expected when `params` is sent. 23 | 24 | union { 25 | none @3 :Void $Json.name("!missing params, result, or error"); 26 | # Dummy default value of union, to detect when none of the fields below were received. 27 | 28 | params @4 :Json.Value; 29 | # Initiates a call. 30 | 31 | result @5 :Json.Value; 32 | # Completes a call. 33 | 34 | error @6 :Error; 35 | # Completes a call throwing an exception. 36 | } 37 | 38 | struct Error { 39 | code @0 :Int32; 40 | message @1 :Text $Go.name("messageValue"); 41 | data @2 :Json.Value; 42 | } 43 | } 44 | using Go = import "/go.capnp"; 45 | $Go.package("jsonrpc"); 46 | $Go.import("capnproto.org/go/capnp/v3/std/capnp/compat/jsonrpc"); 47 | -------------------------------------------------------------------------------- /std/capnp/compiler/lexer.capnp: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 | # Licensed under the MIT License: 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | @0xa73956d2621fc3ee; 23 | 24 | using Cxx = import "/capnp/c++.capnp"; 25 | 26 | $Cxx.namespace("capnp::compiler"); 27 | 28 | struct Token { 29 | union { 30 | identifier @0 :Text; 31 | stringLiteral @1 :Text; 32 | binaryLiteral @9 :Data; 33 | integerLiteral @2 :UInt64; 34 | floatLiteral @3 :Float64; 35 | operator @4 :Text; 36 | parenthesizedList @5 :List(List(Token)); 37 | bracketedList @6 :List(List(Token)); 38 | } 39 | 40 | startByte @7 :UInt32; 41 | endByte @8 :UInt32; 42 | } 43 | 44 | struct Statement { 45 | tokens @0 :List(Token); 46 | union { 47 | line @1 :Void; 48 | block @2 :List(Statement); 49 | } 50 | 51 | docComment @3 :Text; 52 | 53 | startByte @4 :UInt32; 54 | endByte @5 :UInt32; 55 | } 56 | 57 | struct LexedTokens { 58 | # Lexer output when asked to parse tokens that don't form statements. 59 | 60 | tokens @0 :List(Token); 61 | } 62 | 63 | struct LexedStatements { 64 | # Lexer output when asked to parse statements. 65 | 66 | statements @0 :List(Statement); 67 | } 68 | using Go = import "/go.capnp"; 69 | $Go.package("lexer"); 70 | $Go.import("capnproto.org/go/capnp/v3/std/capnp/lexer"); 71 | -------------------------------------------------------------------------------- /std/capnp/cxx/c++.capnp.go: -------------------------------------------------------------------------------- 1 | // Code generated by capnpc-go. DO NOT EDIT. 2 | 3 | package cxx 4 | 5 | import ( 6 | schemas "capnproto.org/go/capnp/v3/schemas" 7 | ) 8 | 9 | const Namespace = uint64(0xb9c6f99ebf805f2c) 10 | const Name = uint64(0xf264a779fef191ce) 11 | const AllowCancellation = uint64(0xac7096ff8cfc9dce) 12 | const schema_bdf87d7bb8304e81 = "x\xda\x12\xc8v`1\xe4\xcdgb`\x0a\x94ae" + 13 | "\xfb\x7fn\xee\x9f\x9e\xff\xd3\x0a\xd60\\\xe4bed" + 14 | "\xfe\xdf\xe8g\xb0\xa3\xba\xf6\xc7^\x06\x06FaY\xc6" + 15 | "G\xc2\x9a\x8c\xec\x0c\x0c\xc1*\x8c\xcc\x8c\x0c\x8c\xffu" + 16 | "\xe2\x1b\xf6\xcf\xfbyl'C \x17+#\x8aRQ" + 17 | "\xc6E\xc2\xb2`\xa5\x12\x10\xa5\xe7&~\xfcW\xb9<" + 18 | "\xe5\x13\xc8\xd8?\xec(j9\x19\xab\x84y\xc1j9" + 19 | "@ju\xff'kk\xeb%'\x16\xe41\x15X%" + 20 | "\xe6\xe4\xe4\x97;'\xe6%\xa7\xe6\xe4$\x96\xb0g\xe6" + 21 | "\xe7\x050220\xc3\x950\x16X\xe5%\xe6\xa6\x16" + 22 | "\x17\xb0'&\xa7\x06022\xf200\xc1%\x19\xec" + 23 | "!\xb2Pq@\x00\x00\x00\xff\xff\x08\x03GM" 24 | 25 | func RegisterSchema(reg *schemas.Registry) { 26 | reg.Register(&schemas.Schema{ 27 | String: schema_bdf87d7bb8304e81, 28 | Nodes: []uint64{ 29 | 0xac7096ff8cfc9dce, 30 | 0xb9c6f99ebf805f2c, 31 | 0xf264a779fef191ce, 32 | }, 33 | Compressed: true, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /std/capnp/rpc/exception.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import "capnproto.org/go/capnp/v3/exc" 4 | 5 | // MarshalError fills in the fields of e according to err. Returns a non-nil 6 | // error if marshalling fails. 7 | func (e Exception) MarshalError(err error) error { 8 | e.SetType(Exception_Type(exc.TypeOf(err))) 9 | return e.SetReason(err.Error()) 10 | } 11 | 12 | // ToError converts the exception to an error. If accessing the reason field 13 | // returns an error, the exception's type field will still be returned by 14 | // exc.TypeOf, but the message will be replaced by something describing the 15 | // read erorr. 16 | func (e Exception) ToError() error { 17 | // TODO: rework this so that exc.Type and Exception_Type 18 | // are aliases somehow. For now we rely on the values being 19 | // identical: 20 | typ := exc.Type(e.Type()) 21 | 22 | reason, err := e.Reason() 23 | if err != nil { 24 | return &exc.Exception{ 25 | Type: typ, 26 | Prefix: "failed to read reason", 27 | Cause: err, 28 | } 29 | } 30 | return exc.New(exc.Type(e.Type()), "", reason) 31 | } 32 | -------------------------------------------------------------------------------- /std/capnp/stream.capnp: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 Cloudflare, Inc. and contributors 2 | # Licensed under the MIT License: 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | @0x86c366a91393f3f8; 23 | # Defines placeholder types used to provide backwards-compatibility while introducing streaming 24 | # to the language. The goal is that old code generators that don't know about streaming can still 25 | # generate code that functions, leaving it up to the application to implement flow control 26 | # manually. 27 | 28 | $import "/capnp/c++.capnp".namespace("capnp"); 29 | 30 | struct StreamResult @0x995f9a3377c0b16e { 31 | # Empty struct that serves as the return type for "streaming" methods. 32 | # 33 | # Defining a method like: 34 | # 35 | # write @0 (bytes :Data) -> stream; 36 | # 37 | # Is equivalent to: 38 | # 39 | # write @0 (bytes :Data) -> import "/capnp/stream.capnp".StreamResult; 40 | # 41 | # However, implementations that recognize streaming will elide the reference to StreamResult 42 | # and instead give write() a different signature appropriate for streaming. 43 | # 44 | # Streaming methods do not return a result -- that is, they return Promise. This promise 45 | # resolves not to indicate that the call was actually delivered, but instead to provide 46 | # backpressure. When the previous call's promise resolves, it is time to make another call. On 47 | # the client side, the RPC system will resolve promises immediately until an appropriate number 48 | # of requests are in-flight, and then will delay promise resolution to apply back-pressure. 49 | # On the server side, the RPC system will deliver one call at a time. 50 | } 51 | using Go = import "/go.capnp"; 52 | $Go.package("stream"); 53 | $Go.import("capnproto.org/go/capnp/v3/std/capnp/stream"); 54 | -------------------------------------------------------------------------------- /std/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("gocp"); 27 | $import("capnproto.org/go/capnp/v3/std/go"); 28 | -------------------------------------------------------------------------------- /std/go/go.capnp.go: -------------------------------------------------------------------------------- 1 | // Code generated by capnpc-go. DO NOT EDIT. 2 | 3 | package gocp 4 | 5 | import ( 6 | schemas "capnproto.org/go/capnp/v3/schemas" 7 | ) 8 | 9 | const Package = uint64(0xbea97f1023792be0) 10 | const Import = uint64(0xe130b601260e44b5) 11 | const Doc = uint64(0xc58ad6bd519f935e) 12 | const Tag = uint64(0xa574b41924caefc7) 13 | const Notag = uint64(0xc8768679ec52e012) 14 | const Customtype = uint64(0xfa10659ae02f2093) 15 | const Name = uint64(0xc2b96012172f8df1) 16 | const schema_d12a1c51fedd6c88 = "x\xda\x12\x98\xe8\xc0b\xc8{\x9c\x89\x81)P\x81\x95" + 17 | "\xed\xff\xf1\xf7\xa7T$\xb7\x94,e\x08\xe4d\xe5\xf8" + 18 | "\xdf\x91s\xf7_\xa0\x8c\xd6E\x06\x06FaO\xc6," + 19 | "a_Fv\x06\x86`\x0fFfF\x06\xc6\xff\x0f\xb4" + 20 | "+\x95\x05\xeaW\xee\x03)eDQj\xc9\xd8%\xec" + 21 | "\x08Vj\x03Q\xfa\xb1W_\\(a\xe7!\x86\x8b" + 22 | "\x9c\xac\xff\xf8Q\xd4\xea2\x16\x09\x1b\x82\xd5\xea@\xd4" + 23 | "\xc6M\x9e\x1f\xb8\xf7Z\xd7Q\x90\xb1&(Je\x19" + 24 | "\xb3\x84\x15\xc1Je J\x85\x1e\x04\xbd\xa9l+;" + 25 | "\x81\xe9X^\xc6*aA\xb0R\x1e\x88\xd2\xad.|" + 26 | "j\x8c\xdb\x0c\x1eb8V\xf0o\x930#He\xe0" + 27 | "\x1f\xb0\xc2\xc9\x0a\xfa\x0ff\xa5\x0a\xfc\x02)T@Q" + 28 | "\xf8v\x91\xe0W\x90\xba\x0f\xcc\x8c\x0c<\xff\xd3\xf3\xf5" + 29 | "\x92\x13\x0b\xf2\x0a\x18\xf8\xadJ\x12\xd3\x03\x18\x19\x19y" + 30 | "\x18\x98\xe0\xa2\x8cV\x05\x89\xc9\xd9\x89\xe9\xa9\x0c\x0c\xe8" + 31 | "R\x0c\xf2Vy\x89\xb9\xa9\x18\xc2\xfcV)\xf9\xc9\x18" + 32 | "\xa2\xf6Vy\xf9\x10\xf3\x19\x98\x91L\xcf\xcc-\xc8/" + 33 | "*a\xc0\xb46\xb9\xb4\xb8$?\xb7\x84\xbd\xb2\x00f" + 34 | "\x03 \x00\x00\xff\xff\xb5C\x8e\x03" 35 | 36 | func RegisterSchema(reg *schemas.Registry) { 37 | reg.Register(&schemas.Schema{ 38 | String: schema_d12a1c51fedd6c88, 39 | Nodes: []uint64{ 40 | 0xa574b41924caefc7, 41 | 0xbea97f1023792be0, 42 | 0xc2b96012172f8df1, 43 | 0xc58ad6bd519f935e, 44 | 0xc8768679ec52e012, 45 | 0xe130b601260e44b5, 46 | 0xfa10659ae02f2093, 47 | }, 48 | Compressed: true, 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /typeparam.go: -------------------------------------------------------------------------------- 1 | package capnp 2 | 3 | // The TypeParam interface must be satisified by a type to be used as a capnproto 4 | // type parameter. This is satisified by all capnproto pointer types. T should 5 | // be instantiated by the type itself; in type parameter lists you will typically 6 | // see parameter/constraints like T TypeParam[T]. 7 | type TypeParam[T any] interface { 8 | // Convert the receiver to a Ptr, storing it in seg if it is not 9 | // already associated with some message (only true for Clients and 10 | // wrappers around them. 11 | EncodeAsPtr(seg *Segment) Ptr 12 | 13 | // Decode the pointer as the type of the receiver. Generally, 14 | // the receiver will be the zero value for the type. 15 | DecodeFromPtr(p Ptr) T 16 | } 17 | -------------------------------------------------------------------------------- /util/README.md: -------------------------------------------------------------------------------- 1 | This directory contains misc. Go utility packages that Ian Denhardt wrote as he 2 | needed them for other projects. 3 | -------------------------------------------------------------------------------- /util/chkfatal.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // Chkfatal panics if err is not nil 4 | func Chkfatal(err error) { 5 | if err != nil { 6 | panic(err) 7 | } 8 | } 9 | 10 | // Must panics if err != nil, otherwise returns value. 11 | func Must[T any](value T, err error) T { 12 | Chkfatal(err) 13 | return value 14 | } 15 | -------------------------------------------------------------------------------- /util/chkfatal_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestChkfatal(t *testing.T) { 11 | assert.NotPanics(t, func() { 12 | Chkfatal(nil) 13 | }, "Chkfatal does not panic on a nil error.") 14 | 15 | assert.Panics(t, func() { 16 | Chkfatal(errors.New("Some error.")) 17 | }, "Chkfatal panics on a non-nil error.") 18 | } 19 | -------------------------------------------------------------------------------- /util/debugging/debugging.go: -------------------------------------------------------------------------------- 1 | // Package debugging exports various helpers useful when debugging. In general, 2 | // code from this package should not be included in production builds. 3 | package debugging 4 | 5 | import "runtime" 6 | 7 | // Wrapper around runtime.Stack, which allocates a buffer and returns the stack 8 | // trace as a string. Stack traces over 1MB will be truncated. 9 | func StackString(all bool) string { 10 | var buf [1e6]byte 11 | n := runtime.Stack(buf[:], all) 12 | return string(buf[:n]) 13 | } 14 | -------------------------------------------------------------------------------- /util/deferred/queue.go: -------------------------------------------------------------------------------- 1 | // Package deferred provides tools for deferring actions 2 | // to be run at a later time. These can be thought of as 3 | // an extension and generalization of the language's built-in 4 | // defer statement. 5 | package deferred 6 | 7 | // A Queue is a queue of actions to run at some later time. 8 | // The actions will be run in the order they are added with 9 | // Defer. Note that this is different from the language's 10 | // built-in defer statement, which runs actions in reverse 11 | // order (future versions of this package may add a Stack 12 | // type to support those semantics). 13 | // 14 | // The zero value is an empty queue. 15 | // 16 | // The advantage of this vs. built-in defer is that it needn't 17 | // be tied to the scope of a single function; for example, 18 | // you can write code like: 19 | // 20 | // q := &Queue{} 21 | // defer q.Run() 22 | // f(q) // pass the queue to a subroutine, which may queue 23 | // // up actions to be run after *this* function returns. 24 | type Queue []func() 25 | 26 | // Run runs all deferred actions, in the order they were added. 27 | func (q *Queue) Run() { 28 | funcs := *q 29 | for i, f := range funcs { 30 | if f != nil { 31 | f() 32 | funcs[i] = nil 33 | } 34 | } 35 | } 36 | 37 | // Defer adds f to the list of functions to call when Run() 38 | // is invoked. 39 | func (q *Queue) Defer(f func()) { 40 | *q = append(*q, f) 41 | } 42 | -------------------------------------------------------------------------------- /util/exn/exn.go: -------------------------------------------------------------------------------- 1 | // Package exn provides an exception-like mechanism. 2 | // 3 | // It is a bit easier to reason about than standard exceptions, since 4 | // not any code can throw, only code given access to a throw callback. 5 | // See the docs for Try. 6 | package exn 7 | 8 | // exn is a wrapper type used to distinguish values this package passes 9 | // to panic() from anything else that might be passed to panic by 10 | // unrelated code. 11 | type exn struct { 12 | err error 13 | } 14 | 15 | // An alias for the type of the callback provided by Try. 16 | type Thrower = func(error, ...string) 17 | 18 | // Try invokes f, which is a callback with type: 19 | // 20 | // func(throw Thrower) T 21 | // 22 | // Thrower is an alias for func(error, ...string). 23 | // 24 | // If f returns normally, Try returns the value f returned and a nil 25 | // error. 26 | // 27 | // If f invokes throw with a non-nil error, f's execution is 28 | // terminated, and Try returns the zero value for T and the 29 | // error that was passed to throw. 30 | // 31 | // If f invokes throw with a nil argument, throw returns normally 32 | // without terminating f. This is so that error handling code can 33 | // pass errors to throw unconditionally, like: 34 | // 35 | // v, err := foo() 36 | // throw(err) 37 | // 38 | // f must not store throw or otherwise cause it to be invoked after 39 | // Try returns. 40 | // 41 | // Callers may pass additonal string arguments to throw, which will 42 | // wrap the error with additional context, i.e. throw(err, "something") 43 | // is equivalent to throw(Wrap("something", err)) 44 | func Try[T any](f func(Thrower) T) (result T, err error) { 45 | throw := func(e error, context ...string) { 46 | if e == nil { 47 | return 48 | } 49 | for i := len(context) - 1; i >= 0; i-- { 50 | e = Wrap(context[i], e) 51 | } 52 | panic(exn{err: e}) 53 | } 54 | finishedCall := false 55 | defer func() { 56 | if finishedCall { 57 | return 58 | } 59 | 60 | panicVal := recover() 61 | if e, ok := panicVal.(exn); ok { 62 | err = e.err 63 | } else { 64 | panic(panicVal) 65 | } 66 | }() 67 | result = f(throw) 68 | finishedCall = true 69 | return 70 | } 71 | 72 | // Try0 is like Try, but f does not return a value, and Try0 only returns 73 | // an error. 74 | func Try0(f func(Thrower)) error { 75 | _, err := Try(func(throw Thrower) struct{} { 76 | f(throw) 77 | return struct{}{} 78 | }) 79 | return err 80 | } 81 | 82 | // Try2 is like Try, but with two values instead of 1. 83 | func Try2[A, B any](f func(Thrower) (A, B)) (A, B, error) { 84 | r, err := Try(func(throw Thrower) pair[A, B] { 85 | a, b := f(throw) 86 | return pair[A, B]{a: a, b: b} 87 | }) 88 | return r.a, r.b, err 89 | } 90 | 91 | // TODO: move into its own package. 92 | type pair[A, B any] struct { 93 | a A 94 | b B 95 | } 96 | -------------------------------------------------------------------------------- /util/exn/exn_test.go: -------------------------------------------------------------------------------- 1 | package exn 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // TestTryReturn tests what happens when Try's callback returns without 11 | // calling throw(). 12 | func TestTryReturn(t *testing.T) { 13 | v, err := Try(func(throw Thrower) int { 14 | return 1 15 | }) 16 | assert.Nil(t, err) 17 | assert.Equal(t, 1, v, "Result should be what was returned") 18 | } 19 | 20 | // TestTryThrowNil tests what happens when Try's callback calls throw(nil). 21 | func TestTryThrowNil(t *testing.T) { 22 | v, err := Try(func(throw Thrower) int { 23 | throw(nil) 24 | return 1 25 | }) 26 | assert.Nil(t, err) 27 | assert.Equal(t, 1, v, "Result should be what was returned") 28 | } 29 | 30 | // TestTryThrowErr tests what happens when Try's callback calls throw 31 | // with a non-nil error. 32 | func TestTryThrowErr(t *testing.T) { 33 | someErr := errors.New("Some error") 34 | v, err := Try(func(throw Thrower) int { 35 | throw(someErr) 36 | return 1 37 | }) 38 | assert.Equal(t, someErr, err, "Error should be what we threw") 39 | assert.Equal(t, 0, v, "Result should be the zero value.") 40 | } 41 | 42 | // TestTryOtherPanic verifies that if the callback to Try panics *directly*, 43 | // the panic bubbles up, rather than being captured by Try. 44 | func TestTryOtherPanic(t *testing.T) { 45 | assert.Panics(t, func() { 46 | Try(func(throw Thrower) int { 47 | panic("A real panic!") 48 | }) 49 | }) 50 | } 51 | 52 | // Like TestTryReturn, but for Try0. 53 | func TestTry0Return(t *testing.T) { 54 | err := Try0(func(throw Thrower) { 55 | }) 56 | assert.Nil(t, err) 57 | } 58 | 59 | // Like TestTryThrowNil, but for Try0. 60 | func TestTry0ThrowNil(t *testing.T) { 61 | err := Try0(func(throw Thrower) { 62 | throw(nil) 63 | }) 64 | assert.Nil(t, err) 65 | } 66 | 67 | // Like TestTryThrowErr, but for Try0. 68 | func TestTry0ThrowErr(t *testing.T) { 69 | someErr := errors.New("Some error") 70 | err := Try0(func(throw Thrower) { 71 | throw(someErr) 72 | }) 73 | assert.Equal(t, someErr, err, "Error should be what we threw") 74 | } 75 | -------------------------------------------------------------------------------- /util/exn/wrap.go: -------------------------------------------------------------------------------- 1 | package exn 2 | 3 | // Wrap wraps err, adding the string context to its message. If 4 | // err is nil, returns nil instead. 5 | func Wrap(context string, err error) error { 6 | if err != nil { 7 | err = wrappedErr{ 8 | context: context, 9 | err: err, 10 | } 11 | } 12 | return err 13 | } 14 | 15 | // WrapThrow(th, context, err) is equivalent to th(Wrap(context, err)), 16 | // i.e. if err != nil, it throws with an error wrapping err and supplying 17 | // the additional context. 18 | func WrapThrow(th Thrower, context string, err error) { 19 | th(Wrap(context, err)) 20 | } 21 | 22 | // wrapper error used by Wrap. 23 | type wrappedErr struct { 24 | context string 25 | err error 26 | } 27 | 28 | func (e wrappedErr) Error() string { 29 | return e.context + ": " + e.err.Error() 30 | } 31 | 32 | func (e wrappedErr) Unwrap() error { 33 | return e.err 34 | } 35 | -------------------------------------------------------------------------------- /util/idempotent.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // Idempotent returns a function which calls f() the first time it is called, 4 | // and then no-ops thereafter. 5 | func Idempotent(f func()) func() { 6 | var called bool 7 | return func() { 8 | if !called { 9 | called = true 10 | f() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /util/maps/maps.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | // A key-value pair 4 | type KV[K, V any] struct { 5 | Key K 6 | Value V 7 | } 8 | 9 | func Keys[K comparable, V any](m map[K]V) []K { 10 | items := make([]K, 0, len(m)) 11 | for k, _ := range m { 12 | items = append(items, k) 13 | } 14 | return items 15 | } 16 | 17 | func Items[K comparable, V any](m map[K]V) []KV[K, V] { 18 | items := make([]KV[K, V], 0, len(m)) 19 | for k, v := range m { 20 | items = append(items, KV[K, V]{ 21 | Key: k, 22 | Value: v, 23 | }) 24 | } 25 | return items 26 | } 27 | -------------------------------------------------------------------------------- /util/maybe/maybe.go: -------------------------------------------------------------------------------- 1 | // Package maybe provides support for working with optional values. 2 | package maybe 3 | 4 | // A Maybe[V] represents an optional value of type V. The zero value for Maybe[V] 5 | // is considered a "missing" value. 6 | type Maybe[V any] struct { 7 | value V 8 | ok bool 9 | } 10 | 11 | // Create a new, non-empty Maybe with value 'value'. 12 | func New[V any](value V) Maybe[V] { 13 | return Maybe[V]{ 14 | value: value, 15 | ok: true, 16 | } 17 | } 18 | 19 | // Get the underlying value, if any. ok is true iff the value was present. 20 | func (t Maybe[V]) Get() (value V, ok bool) { 21 | return t.value, t.ok 22 | } 23 | -------------------------------------------------------------------------------- /util/orerr/orerr.go: -------------------------------------------------------------------------------- 1 | // Package orerr provides a type OrErr for working with (value, error) 2 | // pairs. 3 | package orerr 4 | 5 | // An OrErr[V] bundles a value of type V with a possibly-nil error. This 6 | // allows the user to pass around a single value instead of dealing 7 | // with tuples. 8 | // 9 | // Values can be extracted with Get; callers should check the error 10 | // as with any other method that returns an error. 11 | type OrErr[V any] struct { 12 | value V 13 | err error 14 | } 15 | 16 | // New bundles a value and possible error in an OrErr. 17 | func New[V any](value V, err error) OrErr[V] { 18 | return OrErr[V]{ 19 | value: value, 20 | err: err, 21 | } 22 | } 23 | 24 | // Get gets the components of the T. 25 | func (t OrErr[V]) Get() (V, error) { 26 | return t.value, t.err 27 | } 28 | 29 | // Err returns the error from the OrErr. 30 | func (t OrErr[V]) Err() error { 31 | return t.err 32 | } 33 | -------------------------------------------------------------------------------- /util/rc/ref_test.go: -------------------------------------------------------------------------------- 1 | package rc 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/tj/assert" 7 | ) 8 | 9 | func TestRef(t *testing.T) { 10 | released := false 11 | release := func() { 12 | released = true 13 | } 14 | value := 4 15 | 16 | first := NewRef(value, release) 17 | assert.Equal(t, value, *first.Value(), 18 | "Ref.Value() should return the value passed") 19 | second := first.AddRef() 20 | assert.Equal(t, value, *second.Value(), 21 | "second ref should have the same value as the first") 22 | first.Release() 23 | assert.False(t, released, 24 | "Releasing the first ref should keep the value alive") 25 | assert.Equal(t, value, *second.Value(), 26 | "Value should be the same after releasing the first ref") 27 | assert.Panics(t, func() { 28 | first.Value() 29 | }, "Trying to access the value via a released ref should panic, even if the value is still live.") 30 | assert.Panics(t, func() { 31 | first.AddRef() 32 | }, "Trying to call AddRef() on a released ref should panic") 33 | assert.Panics(t, func() { 34 | first.Weak() 35 | }, "Trying to call Weak() on a released ref should panic") 36 | first.Release() 37 | assert.False(t, released, "Calling Release() twice should have no effect") 38 | second.Release() 39 | assert.True(t, released, "Releasing the second reference should drop the value") 40 | } 41 | 42 | func TestSteal(t *testing.T) { 43 | released := false 44 | release := func() { 45 | released = true 46 | } 47 | value := 4 48 | 49 | first := NewRef(value, release) 50 | second := first.Steal() 51 | assert.False(t, released, "steal should not reduce the refcount") 52 | assert.Panics(t, func() { 53 | first.Value() 54 | }, "receiver is invalid after steal") 55 | second.Release() 56 | assert.True(t, released, "releasing the new reference has an effect") 57 | } 58 | 59 | func TestWeakRef(t *testing.T) { 60 | released := false 61 | release := func() { 62 | released = true 63 | } 64 | value := 4 65 | 66 | first := NewRef(value, release) 67 | weak := first.Weak() 68 | 69 | second, ok := weak.AddRef() 70 | assert.True(t, ok, "WeakRef().AddRef() should succeed if the ref is live.") 71 | assert.Equal(t, value, *second.Value(), "The strong reference should have the correct value.") 72 | 73 | second.Release() 74 | assert.False(t, released, "Dropping the returned ref should keep the other ref alive") 75 | 76 | first.Release() 77 | assert.True(t, released, "Dropping the first ref should release the value") 78 | 79 | third, ok := weak.AddRef() 80 | assert.False(t, ok, "Creating a strong ref after the value is released should fail") 81 | assert.Nil(t, third, "The returned ref should be nil if creating a strong ref fails") 82 | } 83 | -------------------------------------------------------------------------------- /util/slices/poolslice/poolslice.go: -------------------------------------------------------------------------------- 1 | // Package poolslice supports allocating slots out of a slice. 2 | package poolslice 3 | 4 | // A PoolSlice wraps a slice, adding support for tracking free vs. in-use 5 | // slots and inserting new elements into free slots. 6 | type PoolSlice[T any] struct { 7 | Items []T // The slice 8 | idxGen IndexGenerator // Keeps track of free slots 9 | } 10 | 11 | // Add adds T to the slice, returning its index. Will use a free slot if 12 | // available, otherwise will expand the slice. 13 | func (s *PoolSlice[T]) Add(value T) int { 14 | index := s.idxGen.Alloc() 15 | if index == len(s.Items) { 16 | s.Items = append(s.Items, value) 17 | } else { 18 | s.Items[index] = value 19 | } 20 | return index 21 | } 22 | 23 | // Remove removes the item at the index, replacing it with the zero value and 24 | // marking its slot for re-use. 25 | func (s *PoolSlice[T]) Remove(index int) { 26 | var zero T 27 | s.Items[index] = zero 28 | s.idxGen.Release(index) 29 | } 30 | 31 | // An IndexGenerator allocates slice indexes, allowing re-use. This lets you insert 32 | // remove items within a slice dynamically, re-using empty slots when available. 33 | type IndexGenerator struct { 34 | free []int // list of free slots 35 | next int // next index to use when no slots are free 36 | } 37 | 38 | // Alloc allocates a free index. Will use an empty slot if available. 39 | func (g *IndexGenerator) Alloc() int { 40 | if len(g.free) == 0 { 41 | ret := g.next 42 | g.next++ 43 | return ret 44 | } 45 | ret := g.free[len(g.free)-1] 46 | g.free = g.free[:len(g.free)-1] 47 | return ret 48 | } 49 | 50 | // Release marks an index as free. 51 | func (g *IndexGenerator) Release(index int) { 52 | g.free = append(g.free, index) 53 | } 54 | -------------------------------------------------------------------------------- /util/slices/slices.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "sort" 5 | 6 | "golang.org/x/exp/constraints" 7 | ) 8 | 9 | func SortOn[K constraints.Ordered, T any](items []T, key func(T) K) { 10 | sort.Slice(items, func(i, j int) bool { 11 | return key(items[i]) < key(items[j]) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /util/thunk/thunk.go: -------------------------------------------------------------------------------- 1 | // Package thunk provides "thunks" (type T), which are wrappers around lazy or 2 | // concurrent computations. 3 | package thunk 4 | 5 | import "sync" 6 | 7 | // A Thunk[A] wraps a value A which may not be available yet; goroutines may get the 8 | // value by calling Force. 9 | type Thunk[A any] struct { 10 | once sync.Once 11 | f func() A 12 | value A 13 | } 14 | 15 | // Force waits for the result of the Thunk to be available and returns it. 16 | func (t *Thunk[A]) Force() A { 17 | t.once.Do(func() { 18 | t.value = t.f() 19 | t.f = nil 20 | }) 21 | return t.value 22 | } 23 | 24 | // Lazy returns a new Thunk which, when forced, will return the value returned by f(). 25 | // f() will be invoked lazily, i.e. not until Force() is called for the first time. 26 | func Lazy[A any](f func() A) *Thunk[A] { 27 | return &Thunk[A]{f: f} 28 | } 29 | 30 | // Go is like Lazy, except that f() is invoked immediately in a separate goroutine. 31 | // Calls to Force will block until f() returns. 32 | func Go[A any](f func() A) *Thunk[A] { 33 | ret := Lazy(f) 34 | go ret.Force() 35 | return ret 36 | } 37 | 38 | // Ready returns a new Thunk which is already ready; when forced it will return 39 | // value immediately. 40 | func Ready[A any](value A) *Thunk[A] { 41 | var ret Thunk[A] 42 | ret.once.Do(func() {}) 43 | ret.value = value 44 | return &ret 45 | } 46 | 47 | // Promise returns a pair of a Thunk and a function fulfill to supply the value of 48 | // the Thunk; calls to t.Force() will block until fulfill has been invoked, at which 49 | // point they will return the value passed to fulfill. Fulfill must not be called 50 | // more than once. 51 | func Promise[A any]() (t *Thunk[A], fulfill func(A)) { 52 | ch := make(chan A, 1) 53 | t = Lazy(func() A { 54 | return <-ch 55 | }) 56 | return t, func(val A) { 57 | ch <- val 58 | close(ch) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /util/thunk/thunk_test.go: -------------------------------------------------------------------------------- 1 | package thunk 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestLazy(t *testing.T) { 11 | called := 0 12 | 13 | th := Lazy(func() int { 14 | called++ 15 | return 7 16 | }) 17 | 18 | assert.Equal(t, 0, called, "Lazy does not call the function immediately.") 19 | assert.Equal(t, 7, th.Force(), "Force returns the expected result") 20 | assert.Equal(t, 1, called, "Force calls the function once") 21 | assert.Equal(t, 7, th.Force(), "Force returns the same result a second time") 22 | assert.Equal(t, 1, called, "Force does not call the function twice") 23 | } 24 | 25 | func TestGo(t *testing.T) { 26 | done := make(chan struct{}) 27 | 28 | th := Go(func() int { 29 | close(done) 30 | return 1 31 | }) 32 | <-done // Should call the function immediately; otherwise this will hang. 33 | 34 | assert.Equal(t, 1, th.Force(), "Force returns the expected result") 35 | } 36 | 37 | func TestPromise(t *testing.T) { 38 | th, fulfill := Promise[int]() 39 | 40 | resultChan := make(chan int) 41 | 42 | go func() { 43 | v := th.Force() 44 | resultChan <- v 45 | }() 46 | 47 | select { 48 | case <-time.NewTimer(10 * time.Millisecond).C: 49 | case <-resultChan: 50 | t.Fatal("Result should not be ready before fulfill() is called.") 51 | } 52 | fulfill(1) 53 | assert.Equal(t, 1, <-resultChan, "Result should be ready after fulfill.") 54 | assert.Equal(t, 1, th.Force(), "Force should return the result a second time.") 55 | assert.Panics(t, func() { 56 | fulfill(1) 57 | }, "Calling fulfill a second time panics.") 58 | } 59 | --------------------------------------------------------------------------------