TUN-528: Move cloudflared into a separate repo

This commit is contained in:
Areg Harutyunyan
2018-05-01 18:45:06 -05:00
parent e8c621a648
commit d06fc520c7
4726 changed files with 1763680 additions and 0 deletions

12
vendor/zombiezen.com/go/capnproto2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,12 @@
/capnpc-go/capnpc-go
/internal/cmd/mktemplates/mktemplates
TAGS
*~
*.swp
# Bazel
/bazel-bin
/bazel-capnproto2
/bazel-genfiles
/bazel-out
/bazel-testlogs

9
vendor/zombiezen.com/go/capnproto2/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,9 @@
language: go
go_import_path: zombiezen.com/go/capnproto2
go:
- 1.x
install: _travis/install.bash
script: _travis/build.bash
env:
- USE_BAZEL=0
- USE_BAZEL=1

27
vendor/zombiezen.com/go/capnproto2/AUTHORS generated vendored Normal file
View File

@@ -0,0 +1,27 @@
# This is the official list of go-capnproto authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
# See CONTRIBUTORS for the meaning of multiple email addresses.
# Please keep the list sorted.
CloudFlare Inc.
Daniel Darabos <darabos.daniel@gmail.com>
Eran Duchan <pavius@gmail.com>
Evan Shaw <edsrzf@gmail.com>
Google Inc.
Ian Denhardt <ian@zenhack.net>
James McKaskill <james@foobar.co.nz>
Jason E. Aten <j.e.aten@gmail.com>
Johan Hernandez <im@bithavoc.io>
Joonsung Lee <joonsung@devsisters.com>
Lev Radomislensky <lev.radomislensky@gmail.com>
Peter Waldschmidt <peterw@gnoso.com>
Tom Thorogood <me+github@tomthorogood.co.uk>
TJ Holowaychuk <tj@apex.sh>
William Laffin <william.laffin@gmail.com>

62
vendor/zombiezen.com/go/capnproto2/BUILD.bazel generated vendored Normal file
View File

@@ -0,0 +1,62 @@
load("@bazel_gazelle//:def.bzl", "gazelle")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
# gazelle:prefix zombiezen.com/go/capnproto2
gazelle(
name = "gazelle",
command = "fix",
)
go_library(
name = "go_default_library",
srcs = [
"address.go",
"canonical.go",
"capability.go",
"capn.go",
"doc.go",
"go.capnp.go",
"list.go",
"mem.go",
"mem_18.go",
"mem_other.go",
"pointer.go",
"rawpointer.go",
"readlimit.go",
"strings.go",
"struct.go",
],
importpath = "zombiezen.com/go/capnproto2",
visibility = ["//visibility:public"],
deps = [
"//internal/packed:go_default_library",
"//internal/strquote:go_default_library",
"//schemas:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"address_test.go",
"canonical_test.go",
"capability_test.go",
"capn_test.go",
"example_test.go",
"integration_test.go",
"integrationutil_test.go",
"list_test.go",
"mem_test.go",
"rawpointer_test.go",
"readlimit_test.go",
],
data = [
"//internal/aircraftlib:schema",
],
embed = [":go_default_library"],
deps = [
"//internal/aircraftlib:go_default_library",
"//internal/capnptool:go_default_library",
],
)

194
vendor/zombiezen.com/go/capnproto2/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,194 @@
# Go Cap'n Proto Release Notes
## 2.17.0
- Add `capnp.Canonicalize` function that implements the
[canonicalization](https://capnproto.org/encoding.html#canonicalization)
algorithm. ([#92](https://github.com/capnproto/go-capnproto2/issues/92))
- Zero-sized struct pointers are now written with an offset of
-1 to distinguish them from a null pointer.
([#92](https://github.com/capnproto/go-capnproto2/issues/92))
- Better support for alternate `Arena` implementations
- [Document `Arena` contract](https://godoc.org/zombiezen.com/go/capnproto2#Arena)
in more detail
- Permit an `Arena` to have a single empty segment in `NewMessage`
- `Arena` allocation optimizations: both `SingleSegment` and
`MultiSegment` now gradually ramp up the amount of space allocated in
a single allocation as the message grows. This is similar to how
built-in Go `append` function works. Workloads with medium to large
messages should expect a decrease in number of allocations, while
small message workloads should remain about the same. Please file an
issue if you encounter any performance regressions.
([#96](https://github.com/capnproto/go-capnproto2/issues/96))
- Fix double-far pointer logic. ([#97](https://github.com/capnproto/go-capnproto2/issues/97))
This is a long-standing bug with reading and writing multi-segment
messages. I've added broader test coverage for multi-segment messages
and far pointers, so it's unlikely that such a failure will persist in
the future.
- Accessing a field in a union when that field is not the one set now
results in a panic. ([#56](https://github.com/capnproto/go-capnproto2/issues/56))
This is intended to help uncover programming mistakes where a union
field is accessed without checking `Which()`. Prior to this change,
unset union field accessors would silently return garbage.
- `Struct.Address()` and `List.Address()` are now deprecated.
Especially for `List`, where the address is at the beginning of the
data, not the composite literal, the return value is not well-defined
and its not clear how to use it. Use `capnp.SamePtr` if you need to
check for pointer reference equality. File an issue if you're using
`Address()` for something else.
## 2.16.0
- Add BUILD.bazel files ([#88](https://github.com/capnproto/go-capnproto2/issues/88))
## 2.15.0
- capnpc-go now fails when a file does not include an import annotation.
([#41](https://github.com/capnproto/go-capnproto2/issues/41))
- Remove rbtree dependency ([#80](https://github.com/capnproto/go-capnproto2/issues/80))
- Add option to reduce allocations in `capnp.Decoder`
([#79](https://github.com/capnproto/go-capnproto2/issues/79))
- Add `String()` methods for lists
([#85](https://github.com/capnproto/go-capnproto2/issues/85))
- Add `String()` methods to schema.capnp.go
([#83](https://github.com/capnproto/go-capnproto2/issues/83))
## 2.14.1
- Use [new Go generated code convention](https://golang.org/s/generatedcode) in
capnpc-go output ([#78](https://github.com/capnproto/go-capnproto2/issues/78))
## Retroactive Releases
go-capnproto2 was originally a "build from HEAD" sort of library, as was
convention for most Go libraries at the time. Before 2.14.1, Semantic
Versioning tags were retroactively added so that it would be clear what the
differences were since original release, since marking it as "2.0.0" would seem
awkward.
The general process was: any significant new feature was given a minor release,
and then any bugfixes before the next minor release were given a "2.X.1"
release.
<table>
<thead>
<tr>
<th scope="col">Version</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.14.0">2.14.0</a></th>
<td>Add support to <code>pogs</code> for interface types (<a href="https://github.com/capnproto/go-capnproto2/issues/66">#66</a> and <a href="https://github.com/capnproto/go-capnproto2/issues/74">#74</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.13.1">2.13.1</a></th>
<td>Fix bug with far far pointers (<a href="https://github.com/capnproto/go-capnproto2/issues/71">#71</a>), use <code>writev</code> system call to encode multi-segment messages efficiently in Go 1.8+ (<a href="https://github.com/capnproto/go-capnproto2/issues/70">#70</a>), and add GitHub-Linguist-compatible code generation comment</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.13.0">2.13.0</a></th>
<td>Add <code>Conn.Done</code> and <code>Conn.Err</code> methods</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.12.4">2.12.4</a></th>
<td>Fix size of created <code>List(Float32)</code></td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.12.3">2.12.3</a></th>
<td>Fix bugs from fuzz tests: mismatched element size on list access causing crashes (<a href="https://github.com/capnproto/go-capnproto2/issues/59">#59</a>) and miscellaneous packed reader issues</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.12.2">2.12.2</a></th>
<td>Fix another shutdown race condition (<a href="https://github.com/capnproto/go-capnproto2/issues/54">#54</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.12.1">2.12.1</a></th>
<td>Fix ownership bug with receiver-hosted capabilities, add discriminant check to <code>HasField</code> (<a href="https://github.com/capnproto/go-capnproto2/issues/55">#55</a>), fix multi-segment bug for data/text lists, and use nulls for setting empty data/text</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.12.0">2.12.0</a></th>
<td>Add <code>rpc.ConnLog</code> option and fix race conditions and edge cases in RPC implementation</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.11.1">2.11.1</a></th>
<td>Fix packed reader behavior on certain readers (<a href="https://github.com/capnproto/go-capnproto2/issues/49">#49</a>), add <code>capnp.UnmarshalPacked</code> function that performs faster, and reduce locking overhead of segment maps</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.11.0">2.11.0</a></th>
<td>Fix shutdown deadlock in RPC shutdown (<a href="https://github.com/capnproto/go-capnproto2/issues/45">#45</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.10.1">2.10.1</a></th>
<td>Work around lack of support for RPC-level promise capabilities (<a href="https://github.com/capnproto/go-capnproto2/issues/2">#2</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.10.0">2.10.0</a></th>
<td>Add <code>pogs</code> package (<a href="https://github.com/capnproto/go-capnproto2/issues/33">#33</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.9.1">2.9.1</a></th>
<td>Fix not-found behavior in schemas and add missing group IDs in generated embedded schemas</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.9.0">2.9.0</a></th>
<td>Add <code>encoding/text</code> package (<a href="https://github.com/capnproto/go-capnproto2/issues/20">#20</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.8.0">2.8.0</a></th>
<td>Reduce generated code size for text fields and correct NUL check</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.7.0">2.7.0</a></th>
<td>Insert compressed schema data into generated code</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.6.1">2.6.1</a></th>
<td>Strip NUL byte from <code>TextList.BytesAt</code> and fix capnpc-go output for struct groups</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.6.0">2.6.0</a></th>
<td>Add packages for predefined Cap'n Proto schemas</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.5.1">2.5.1</a></th>
<td>Fix capnpc-go regression (<a href="https://github.com/capnproto/go-capnproto2/issues/29">#29</a>) and strip trailing NUL byte in TextBytes accessor</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.5.0">2.5.0</a></th>
<td>Add <code>NewFoo</code> method for list fields in generated structs (<a href="https://github.com/capnproto/go-capnproto2/issues/7">#7</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.4.0">2.4.0</a></th>
<td>Add maximum segment limit (<a href="https://github.com/capnproto/go-capnproto2/issues/25">#25</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.3.0">2.3.0</a></th>
<td>Add depth and traversal limit security checks</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.2.1">2.2.1</a></th>
<td>Fix data race in reading <code>Message</code> from multiple goroutines</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.2.0">2.2.0</a></th>
<td>Add <code>HasFoo</code> pointer field methods to generated code (<a href="https://github.com/capnproto/go-capnproto2/issues/24">#24</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.1.0">2.1.0</a></th>
<td><a href="https://github.com/capnproto/go-capnproto2/wiki/New-Ptr-Type">Introduce <code>Ptr</code> type</a> and reduce allocations in single-segment cases</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.0.2">2.0.2</a></th>
<td>Allow allocation-less string field access via <code>TextList.BytesAt()</code> and <code>StringBytes()</code> (<a href="https://github.com/capnproto/go-capnproto2/issues/17">#17</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.0.1">2.0.1</a></th>
<td>Allow nil params in client wrappers (<a href="https://github.com/capnproto/go-capnproto2/issues/9">#9</a>) and fix integer underflow on compare function (<a href="https://github.com/capnproto/go-capnproto2/issues/12">#12</a>)</td>
</tr>
<tr>
<th scope="row"><a href="https://github.com/capnproto/go-capnproto2/releases/tag/v2.0.0">2.0.0</a></th>
<td>First release under <code>zombiezen.com/go/capnproto2</code></td>
</tr>
</tbody>
</table>

22
vendor/zombiezen.com/go/capnproto2/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# How to contribute
We'd love to accept your patches and contributions to this project. There are a
just a few small guidelines you need to follow.
## Code contributions
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult [GitHub Help][] for more
information on using pull requests.
When you make your first submission to this repository, please add your name to
the AUTHORS and CONTRIBUTORS file as part of your first pull request.
As a policy, go-capnproto2 should always build cleanly and pass tests on every
commit. We run a [Travis build][] that checks before and after merges to
enforce this policy. However, as a courtesy to other contributors, please run
`go test ./...` before sending a pull request (this is what the Travis
build does).
[GitHub Help]: https://help.github.com/articles/about-pull-requests/
[Travis build]: https://travis-ci.org/capnproto/go-capnproto2

33
vendor/zombiezen.com/go/capnproto2/CONTRIBUTORS generated vendored Normal file
View File

@@ -0,0 +1,33 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the go-capnproto repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# copyright belongs to the individual or the corporation.
# Names should be added to this file like so:
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
# Please keep the list sorted.
Alan Braithwaite <alan@cloudflare.com>
Albert Strasheim <albert@cloudflare.com>
Daniel Darabos <darabos.daniel@gmail.com>
Eran Duchan <pavius@gmail.com>
Evan Shaw <edsrzf@gmail.com>
Ian Denhardt <ian@zenhack.net>
James McKaskill <james@foobar.co.nz>
Jason E. Aten <j.e.aten@gmail.com>
Johan Hernandez <im@bithavoc.io>
Joonsung Lee <joonsung@devsisters.com>
Lev Radomislensky <lev.radomislensky@gmail.com>
Peter Waldschmidt <peterw@gnoso.com>
Ross Light <light@google.com> <ross@zombiezen.com>
Tom Thorogood <me+github@tomthorogood.co.uk>
TJ Holowaychuk <tj@apex.sh>
William Laffin <william.laffin@gmail.com>

25
vendor/zombiezen.com/go/capnproto2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,25 @@
go-capnproto is licensed under the terms of the MIT license reproduced below.
===============================================================================
Copyright (C) 2014 the go-capnproto authors and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================================================

68
vendor/zombiezen.com/go/capnproto2/README.md generated vendored Normal file
View File

@@ -0,0 +1,68 @@
# Cap'n Proto bindings for Go
[![GoDoc](https://godoc.org/zombiezen.com/go/capnproto2?status.svg)][godoc]
[![Build Status](https://travis-ci.org/capnproto/go-capnproto2.svg?branch=master)][travis]
go-capnproto consists of:
- a Go code generator for [Cap'n Proto](https://capnproto.org/)
- a Go package that provides runtime support
- a Go package that implements Level 1 of the RPC protocol
[godoc]: https://godoc.org/zombiezen.com/go/capnproto2
[travis]: https://travis-ci.org/capnproto/go-capnproto2
## Getting started
You will need the `capnp` tool to compile schemas into Go.
This package has been tested with Cap'n Proto 0.5.0.
```
$ go get -u -t zombiezen.com/go/capnproto2/...
$ go test -v zombiezen.com/go/capnproto2/...
```
This library uses [SemVer tags][] to indicate stable releases.
While the goal is that master should always be passing all known tests, tagged releases are vetted more.
When possible, use the [latest release tag](https://github.com/capnproto/go-capnproto2/releases).
```
$ cd $GOPATH/src/zombiezen.com/go/capnproto2
$ git fetch
$ git checkout v2.16.0 # check the releases page for the latest
```
Then read the [Getting Started guide][].
[SemVer tags]: http://semver.org/
[Getting Started guide]: https://github.com/capnproto/go-capnproto2/wiki/Getting-Started
## API Compatibility
Consider this package's API as beta software, since the Cap'n Proto spec is not final.
In the spirit of the [Go 1 compatibility guarantee][gocompat], I will make every effort to avoid making breaking API changes.
The major cases where I reserve the right to make breaking changes are:
- Security.
- Changes in the Cap'n Proto specification.
- Bugs.
The `pogs` package is relatively new and may change over time.
However, its functionality has been well-tested and will probably only relax restrictions.
[gocompat]: https://golang.org/doc/go1compat
## Documentation
See the docs on [godoc.org][godoc].
## What is Cap'n Proto?
The best cerealization...
https://capnproto.org/
## License
MIT - see [LICENSE][] file
[LICENSE]: https://github.com/capnproto/go-capnproto2/blob/master/LICENSE

45
vendor/zombiezen.com/go/capnproto2/WORKSPACE generated vendored Normal file
View File

@@ -0,0 +1,45 @@
workspace(name = "com_zombiezen_go_capnproto2")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "io_bazel_rules_go",
sha256 = "8b68d0630d63d95dacc0016c3bb4b76154fe34fca93efd65d1c366de3fcb4294",
urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.12.1/rules_go-0.12.1.tar.gz"],
)
http_archive(
name = "bazel_gazelle",
sha256 = "ddedc7aaeb61f2654d7d7d4fd7940052ea992ccdb031b8f9797ed143ac7e8d43",
urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/0.12.0/bazel-gazelle-0.12.0.tar.gz"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_register_toolchains", "go_rules_dependencies")
go_rules_dependencies()
go_register_toolchains()
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
gazelle_dependencies()
go_repository(
name = "com_github_kylelemons_godebug",
importpath = "github.com/kylelemons/godebug",
sha256 = "4415b09bae90e41695bc17e4d00d0708e1f6bbb6e21cc22ce0146a26ddc243a7",
strip_prefix = "godebug-a616ab194758ae0a11290d87ca46ee8c440117b0",
urls = [
"https://github.com/kylelemons/godebug/archive/a616ab194758ae0a11290d87ca46ee8c440117b0.zip",
],
)
go_repository(
name = "org_golang_x_net",
importpath = "golang.org/x/net",
sha256 = "880dc04d0af397dce6875ee2349bbb4295fe5a47352f7a4da4270456f726edd4",
strip_prefix = "net-f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f",
urls = [
"https://github.com/golang/net/archive/f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f.zip",
],
)

3
vendor/zombiezen.com/go/capnproto2/_travis/bazelrc generated vendored Normal file
View File

@@ -0,0 +1,3 @@
startup --batch --host_jvm_args=-Xms2500m --host_jvm_args=-Xmx2500m
build --noshow_progress --spawn_strategy=standalone --genrule_strategy=standalone --local_resources=1536,1.5,0.5
test --noshow_progress --test_output=errors --spawn_strategy=standalone --genrule_strategy=standalone --test_strategy=standalone --local_resources=1536,1.5,0.5

19
vendor/zombiezen.com/go/capnproto2/_travis/build.bash generated vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
must() {
echo "$@" 1>&2
"$@" || die "FAIL"
}
die() {
echo "$@" 1>&2
exit 1
}
if [[ -z "$USE_BAZEL" || "$USE_BAZEL" -eq "0" ]]; then
must go test -v ./...
else
# On Travis, this will use "$HOME/bin/bazel", but don't assume this
# for local testing of the CI script.
must bazel --bazelrc=_travis/bazelrc test //...
fi

39
vendor/zombiezen.com/go/capnproto2/_travis/install.bash generated vendored Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
must() {
echo "$@" 1>&2
"$@" || die "FAIL"
}
die() {
echo "$@" 1>&2
exit 1
}
if [[ -z "$USE_BAZEL" || "$USE_BAZEL" -eq "0" ]]; then
must go get -t ./...
else
BAZEL_VERSION="${BAZEL_VERSION:-0.14.1}"
case "$TRAVIS_OS_NAME" in
linux)
BAZEL_INSTALLER_URL="https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh"
SEDI="sed -i"
;;
osx)
BAZEL_INSTALLER_URL="https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-darwin-x86_64.sh"
SEDI="sed -i ''"
;;
*)
die "unknown OS $TRAVIS_OS_NAME"
;;
esac
must curl -fsSLo /tmp/bazel.sh "$BAZEL_INSTALLER_URL"
must chmod +x /tmp/bazel.sh
must /tmp/bazel.sh --user
rm -f /tmp/bazel.sh
if [[ ! -z "$TRAVIS_GO_VERSION" ]]; then
must $SEDI -e 's/^go_register_toolchains()/go_register_toolchains(go_version="host")/' WORKSPACE
fi
must "$HOME/bin/bazel" --bazelrc=_travis/bazelrc version
must "$HOME/bin/bazel" --bazelrc=_travis/bazelrc fetch //...
fi

116
vendor/zombiezen.com/go/capnproto2/address.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
package capnp
// An Address is an index inside a segment's data (in bytes).
type Address uint32
// addSize returns the address a+sz.
func (a Address) addSize(sz Size) (b Address, ok bool) {
x := int64(a) + int64(sz)
if x > int64(maxSize) {
return 0, false
}
return Address(x), true
}
// element returns the address a+i*sz.
func (a Address) element(i int32, sz Size) (b Address, ok bool) {
x := int64(i) * int64(sz)
if x > int64(maxSize) {
return 0, false
}
x += int64(a)
if x > int64(maxSize) {
return 0, false
}
return Address(x), true
}
// addOffset returns the address a+o.
func (a Address) addOffset(o DataOffset) Address {
return a + Address(o)
}
// A Size is a size (in bytes).
type Size uint32
// wordSize is the number of bytes in a Cap'n Proto word.
const wordSize Size = 8
// maxSize is the maximum representable size.
const maxSize Size = 1<<32 - 1
// times returns the size sz*n.
func (sz Size) times(n int32) (ns Size, ok bool) {
x := int64(sz) * int64(n)
if x > int64(maxSize) {
return 0, false
}
return Size(x), true
}
// padToWord adds padding to sz to make it divisible by wordSize.
func (sz Size) padToWord() Size {
n := Size(wordSize - 1)
return (sz + n) &^ n
}
// DataOffset is an offset in bytes from the beginning of a struct's data section.
type DataOffset uint32
// ObjectSize records section sizes for a struct or list.
type ObjectSize struct {
DataSize Size
PointerCount uint16
}
// isZero reports whether sz is the zero size.
func (sz ObjectSize) isZero() bool {
return sz.DataSize == 0 && sz.PointerCount == 0
}
// isOneByte reports whether the object size is one byte (for Text/Data element sizes).
func (sz ObjectSize) isOneByte() bool {
return sz.DataSize == 1 && sz.PointerCount == 0
}
// isValid reports whether sz's fields are in range.
func (sz ObjectSize) isValid() bool {
return sz.DataSize <= 0xffff*wordSize
}
// pointerSize returns the number of bytes the pointer section occupies.
func (sz ObjectSize) pointerSize() Size {
// Guaranteed not to overflow
return wordSize * Size(sz.PointerCount)
}
// totalSize returns the number of bytes that the object occupies.
func (sz ObjectSize) totalSize() Size {
return sz.DataSize + sz.pointerSize()
}
// dataWordCount returns the number of words in the data section.
func (sz ObjectSize) dataWordCount() int32 {
if sz.DataSize%wordSize != 0 {
panic("data size not aligned by word")
}
return int32(sz.DataSize / wordSize)
}
// totalWordCount returns the number of words that the object occupies.
func (sz ObjectSize) totalWordCount() int32 {
return sz.dataWordCount() + int32(sz.PointerCount)
}
// BitOffset is an offset in bits from the beginning of a struct's data section.
type BitOffset uint32
// offset returns the equivalent byte offset.
func (bit BitOffset) offset() DataOffset {
return DataOffset(bit / 8)
}
// mask returns the bitmask for the bit.
func (bit BitOffset) mask() byte {
return byte(1 << (bit % 8))
}

31
vendor/zombiezen.com/go/capnproto2/address_test.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package capnp
import (
"testing"
)
func TestAddressElement(t *testing.T) {
tests := []struct {
a Address
i int32
sz Size
out Address
ok bool
}{
{0, 0, 0, 0, true},
{0, 1, 0, 0, true},
{0, 1, 8, 8, true},
{0, 2, 8, 16, true},
{24, 1, 0, 24, true},
{24, 1, 8, 32, true},
{24, 2, 8, 40, true},
{0, 0x7fffffff, 3, 0, false},
{0xffffffff, 0x7fffffff, 0xffffffff, 0, false},
}
for _, test := range tests {
out, ok := test.a.element(test.i, test.sz)
if ok != test.ok || (ok && out != test.out) {
t.Errorf("%#v.element(%d, %d) = %#v, %t; want %#v, %t", test.a, test.i, test.sz, out, ok, test.out, test.ok)
}
}
}

161
vendor/zombiezen.com/go/capnproto2/canonical.go generated vendored Normal file
View File

@@ -0,0 +1,161 @@
package capnp
import (
"errors"
"fmt"
)
// Canonicalize encodes a struct into its canonical form: a single-
// segment blob without a segment table. The result will be identical
// for equivalent structs, even as the schema evolves. The blob is
// suitable for hashing or signing.
func Canonicalize(s Struct) ([]byte, error) {
msg, seg, _ := NewMessage(SingleSegment(nil))
if !s.IsValid() {
return seg.Data(), nil
}
root, err := NewRootStruct(seg, canonicalStructSize(s))
if err != nil {
return nil, fmt.Errorf("canonicalize: %v", err)
}
if err := msg.SetRootPtr(root.ToPtr()); err != nil {
return nil, fmt.Errorf("canonicalize: %v", err)
}
if err := fillCanonicalStruct(root, s); err != nil {
return nil, fmt.Errorf("canonicalize: %v", err)
}
return seg.Data(), nil
}
func canonicalPtr(dst *Segment, p Ptr) (Ptr, error) {
if !p.IsValid() {
return Ptr{}, nil
}
switch p.flags.ptrType() {
case structPtrType:
ss, err := NewStruct(dst, canonicalStructSize(p.Struct()))
if err != nil {
return Ptr{}, err
}
if err := fillCanonicalStruct(ss, p.Struct()); err != nil {
return Ptr{}, err
}
return ss.ToPtr(), nil
case listPtrType:
ll, err := canonicalList(dst, p.List())
if err != nil {
return Ptr{}, err
}
return ll.ToPtr(), nil
case interfacePtrType:
return Ptr{}, errors.New("cannot canonicalize interface")
default:
panic("unreachable")
}
}
func fillCanonicalStruct(dst, s Struct) error {
copy(dst.seg.slice(dst.off, dst.size.DataSize), s.seg.slice(s.off, s.size.DataSize))
for i := uint16(0); i < dst.size.PointerCount; i++ {
p, err := s.Ptr(i)
if err != nil {
return fmt.Errorf("pointer %d: %v", i, err)
}
cp, err := canonicalPtr(dst.seg, p)
if err != nil {
return fmt.Errorf("pointer %d: %v", i, err)
}
if err := dst.SetPtr(i, cp); err != nil {
return fmt.Errorf("pointer %d: %v", i, err)
}
}
return nil
}
func canonicalStructSize(s Struct) ObjectSize {
if !s.IsValid() {
return ObjectSize{}
}
var sz ObjectSize
// int32 will not overflow because max struct data size is 2^16 words.
for off := int32(s.size.DataSize &^ (wordSize - 1)); off >= 0; off -= int32(wordSize) {
if s.Uint64(DataOffset(off)) != 0 {
sz.DataSize = Size(off) + wordSize
break
}
}
for i := int32(s.size.PointerCount) - 1; i >= 0; i-- {
if s.seg.readRawPointer(s.pointerAddress(uint16(i))) != 0 {
sz.PointerCount = uint16(i + 1)
break
}
}
return sz
}
func canonicalList(dst *Segment, l List) (List, error) {
if !l.IsValid() {
return List{}, nil
}
if l.size.PointerCount == 0 {
// Data only, just copy over.
sz := l.allocSize()
_, newAddr, err := alloc(dst, sz)
if err != nil {
return List{}, err
}
cl := List{
seg: dst,
off: newAddr,
length: l.length,
size: l.size,
flags: l.flags,
depthLimit: maxDepth,
}
end, _ := l.off.addSize(sz) // list was already validated
copy(dst.data[newAddr:], l.seg.data[l.off:end])
return cl, nil
}
if l.flags&isCompositeList == 0 {
cl, err := NewPointerList(dst, l.length)
if err != nil {
return List{}, err
}
for i := 0; i < l.Len(); i++ {
p, err := PointerList{l}.PtrAt(i)
if err != nil {
return List{}, fmt.Errorf("element %d: %v", i, err)
}
cp, err := canonicalPtr(dst, p)
if err != nil {
return List{}, fmt.Errorf("element %d: %v", i, err)
}
if err := cl.SetPtr(i, cp); err != nil {
return List{}, fmt.Errorf("element %d: %v", i, err)
}
}
return cl.List, nil
}
// Struct/composite list
var elemSize ObjectSize
for i := 0; i < l.Len(); i++ {
sz := canonicalStructSize(l.Struct(i))
if sz.DataSize > elemSize.DataSize {
elemSize.DataSize = sz.DataSize
}
if sz.PointerCount > elemSize.PointerCount {
elemSize.PointerCount = sz.PointerCount
}
}
cl, err := NewCompositeList(dst, elemSize, l.length)
if err != nil {
return List{}, err
}
for i := 0; i < cl.Len(); i++ {
if err := fillCanonicalStruct(cl.Struct(i), l.Struct(i)); err != nil {
return List{}, fmt.Errorf("element %d: %v", i, err)
}
}
return cl, nil
}

192
vendor/zombiezen.com/go/capnproto2/canonical_test.go generated vendored Normal file
View File

@@ -0,0 +1,192 @@
package capnp
import (
"bytes"
"encoding/hex"
"testing"
)
func TestCanonicalize(t *testing.T) {
{
// null
b, err := Canonicalize(Struct{})
if err != nil {
t.Fatal("Canonicalize(Struct{}):", err)
}
if want := ([]byte{0, 0, 0, 0, 0, 0, 0, 0}); !bytes.Equal(b, want) {
t.Errorf("Canonicalize(Struct{}) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// empty struct
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{})
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(empty struct):", err)
}
want := ([]byte{0xfc, 0xff, 0xff, 0xff, 0, 0, 0, 0})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(empty struct) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// zero data, zero pointer struct
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{DataSize: 8, PointerCount: 1})
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(zero data, zero pointer struct):", err)
}
want := ([]byte{0xfc, 0xff, 0xff, 0xff, 0, 0, 0, 0})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(zero data, zero pointer struct) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// one word data struct
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{DataSize: 8, PointerCount: 1})
s.SetUint16(0, 0xbeef)
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(one word data struct):", err)
}
want := ([]byte{
0, 0, 0, 0, 1, 0, 0, 0,
0xef, 0xbe, 0, 0, 0, 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(one word data struct) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// two pointers to zero structs
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{PointerCount: 2})
e1, _ := NewStruct(seg, ObjectSize{DataSize: 8})
e2, _ := NewStruct(seg, ObjectSize{DataSize: 8})
s.SetPtr(0, e1.ToPtr())
s.SetPtr(1, e2.ToPtr())
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(two pointers to zero structs):", err)
}
want := ([]byte{
0, 0, 0, 0, 0, 0, 2, 0,
0xfc, 0xff, 0xff, 0xff, 0, 0, 0, 0,
0xfc, 0xff, 0xff, 0xff, 0, 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(two pointers to zero structs) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// int list
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{PointerCount: 1})
l, _ := NewInt8List(seg, 5)
s.SetPtr(0, l.ToPtr())
l.Set(0, 1)
l.Set(1, 2)
l.Set(2, 3)
l.Set(3, 4)
l.Set(4, 5)
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(int list):", err)
}
want := ([]byte{
0, 0, 0, 0, 0, 0, 1, 0,
0x01, 0, 0, 0, 0x2a, 0, 0, 0,
1, 2, 3, 4, 5, 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(int list) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// zero int list
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{PointerCount: 1})
l, _ := NewInt8List(seg, 5)
s.SetPtr(0, l.ToPtr())
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(zero int list):", err)
}
want := ([]byte{
0, 0, 0, 0, 0, 0, 1, 0,
0x01, 0, 0, 0, 0x2a, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(zero int list) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// struct list
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{PointerCount: 1})
l, _ := NewCompositeList(seg, ObjectSize{DataSize: 8, PointerCount: 1}, 2)
s.SetPtr(0, l.ToPtr())
l.Struct(0).SetUint64(0, 0xdeadbeef)
txt, _ := NewText(seg, "xyzzy")
l.Struct(1).SetPtr(0, txt.ToPtr())
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(struct list):", err)
}
want := ([]byte{
0, 0, 0, 0, 0, 0, 1, 0,
0x01, 0, 0, 0, 0x27, 0, 0, 0,
0x08, 0, 0, 0, 1, 0, 1, 0,
0xef, 0xbe, 0xad, 0xde, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0x01, 0, 0, 0, 0x32, 0, 0, 0,
'x', 'y', 'z', 'z', 'y', 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(struct list) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// zero struct list
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{PointerCount: 1})
l, _ := NewCompositeList(seg, ObjectSize{DataSize: 16, PointerCount: 2}, 3)
s.SetPtr(0, l.ToPtr())
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(zero struct list):", err)
}
want := ([]byte{
0, 0, 0, 0, 0, 0, 1, 0,
0x01, 0, 0, 0, 0x07, 0, 0, 0,
0x0c, 0, 0, 0, 0, 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(zero struct list) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
{
// zero-length struct list
_, seg, _ := NewMessage(SingleSegment(nil))
s, _ := NewStruct(seg, ObjectSize{PointerCount: 1})
l, _ := NewCompositeList(seg, ObjectSize{DataSize: 16, PointerCount: 2}, 0)
s.SetPtr(0, l.ToPtr())
b, err := Canonicalize(s)
if err != nil {
t.Fatal("Canonicalize(zero-length struct list):", err)
}
want := ([]byte{
0, 0, 0, 0, 0, 0, 1, 0,
0x01, 0, 0, 0, 0x07, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
})
if !bytes.Equal(b, want) {
t.Errorf("Canonicalize(zero-length struct list) =\n%s\n; want\n%s", hex.Dump(b), hex.Dump(want))
}
}
}

541
vendor/zombiezen.com/go/capnproto2/capability.go generated vendored Normal file
View File

@@ -0,0 +1,541 @@
package capnp
import (
"errors"
"strconv"
"golang.org/x/net/context"
)
// An Interface is a reference to a client in a message's capability table.
type Interface struct {
seg *Segment
cap CapabilityID
}
// NewInterface creates a new interface pointer. No allocation is
// performed; s is only used for Segment()'s return value.
func NewInterface(s *Segment, cap CapabilityID) Interface {
return Interface{
seg: s,
cap: cap,
}
}
// ToInterface converts p to an Interface.
//
// Deprecated: Use Ptr.Interface.
func ToInterface(p Pointer) Interface {
if !IsValid(p) {
return Interface{}
}
i, ok := p.underlying().(Interface)
if !ok {
return Interface{}
}
return i
}
// ToPtr converts the interface to a generic pointer.
func (p Interface) ToPtr() Ptr {
return Ptr{
seg: p.seg,
lenOrCap: uint32(p.cap),
flags: interfacePtrFlag,
}
}
// Segment returns the segment this pointer came from.
func (i Interface) Segment() *Segment {
return i.seg
}
// IsValid returns whether the interface is valid.
func (i Interface) IsValid() bool {
return i.seg != nil
}
// HasData is always true.
func (i Interface) HasData() bool {
return true
}
// Capability returns the capability ID of the interface.
func (i Interface) Capability() CapabilityID {
return i.cap
}
// value returns a raw interface pointer with the capability ID.
func (i Interface) value(paddr Address) rawPointer {
if i.seg == nil {
return 0
}
return rawInterfacePointer(i.cap)
}
func (i Interface) underlying() Pointer {
return i
}
// Client returns the client stored in the message's capability table
// or nil if the pointer is invalid.
func (i Interface) Client() Client {
if i.seg == nil {
return nil
}
tab := i.seg.msg.CapTable
if int64(i.cap) >= int64(len(tab)) {
return nil
}
return tab[i.cap]
}
// ErrNullClient is returned from a call made on a null client pointer.
var ErrNullClient = errors.New("capnp: call on null client")
// A CapabilityID is an index into a message's capability table.
type CapabilityID uint32
// A Client represents an Cap'n Proto interface type. It is safe to use
// from multiple goroutines.
//
// Generally, only RPC protocol implementers should provide types that
// implement Client: call ordering guarantees, promises, and
// synchronization are tricky to get right. Prefer creating a server
// that wraps another interface than trying to implement Client.
type Client interface {
// Call starts executing a method and returns an answer that will hold
// the resulting struct. The call's parameters must be placed before
// Call() returns.
//
// Calls are delivered to the capability in the order they are made.
// This guarantee is based on the concept of a capability
// acknowledging delivery of a call: this is specific to an
// implementation of Client. A type that implements Client must
// guarantee that if foo() then bar() is called on a client, that
// acknowledging foo() happens before acknowledging bar().
Call(call *Call) Answer
// Close releases any resources associated with this client.
// No further calls to the client should be made after calling Close.
Close() error
}
// The Call type holds the record for an outgoing interface call.
type Call struct {
// Ctx is the context of the call.
Ctx context.Context
// Method is the interface ID and method ID, along with the optional name,
// of the method to call.
Method Method
// Params is a struct containing parameters for the call.
// This should be set when the RPC system receives a call for an
// exported interface. It is mutually exclusive with ParamsFunc
// and ParamsSize.
Params Struct
// ParamsFunc is a function that populates an allocated struct with
// the parameters for the call. ParamsSize determines the size of the
// struct to allocate. This is used when application code is using a
// client. These settings should be set together; they are mutually
// exclusive with Params.
ParamsFunc func(Struct) error
ParamsSize ObjectSize
// Options passes RPC-specific options for the call.
Options CallOptions
}
// Copy clones a call, ensuring that its Params are placed.
// If Call.ParamsFunc is nil, then the same Call will be returned.
func (call *Call) Copy(s *Segment) (*Call, error) {
if call.ParamsFunc == nil {
return call, nil
}
p, err := call.PlaceParams(s)
if err != nil {
return nil, err
}
return &Call{
Ctx: call.Ctx,
Method: call.Method,
Params: p,
Options: call.Options,
}, nil
}
// PlaceParams returns the parameters struct, allocating it inside
// segment s as necessary. If s is nil, a new single-segment message
// is allocated.
func (call *Call) PlaceParams(s *Segment) (Struct, error) {
if call.ParamsFunc == nil {
return call.Params, nil
}
if s == nil {
var err error
_, s, err = NewMessage(SingleSegment(nil))
if err != nil {
return Struct{}, err
}
}
p, err := NewStruct(s, call.ParamsSize)
if err != nil {
return Struct{}, nil
}
err = call.ParamsFunc(p)
return p, err
}
// CallOptions holds RPC-specific options for an interface call.
// Its usage is similar to the values in context.Context, but is only
// used for a single call: its values are not intended to propagate to
// other callees. An example of an option would be the
// Call.sendResultsTo field in rpc.capnp.
type CallOptions struct {
m map[interface{}]interface{}
}
// NewCallOptions builds a CallOptions value from a list of individual options.
func NewCallOptions(opts []CallOption) CallOptions {
co := CallOptions{make(map[interface{}]interface{})}
for _, o := range opts {
o.f(co)
}
return co
}
// Value retrieves the value associated with the options for this key,
// or nil if no value is associated with this key.
func (co CallOptions) Value(key interface{}) interface{} {
return co.m[key]
}
// With creates a copy of the CallOptions value with other options applied.
func (co CallOptions) With(opts []CallOption) CallOptions {
newopts := CallOptions{make(map[interface{}]interface{})}
for k, v := range co.m {
newopts.m[k] = v
}
for _, o := range opts {
o.f(newopts)
}
return newopts
}
// A CallOption is a function that modifies options on an interface call.
type CallOption struct {
f func(CallOptions)
}
// SetOptionValue returns a call option that associates a value to an
// option key. This can be retrieved later with CallOptions.Value.
func SetOptionValue(key, value interface{}) CallOption {
return CallOption{func(co CallOptions) {
co.m[key] = value
}}
}
// An Answer is the deferred result of a client call, which is usually wrapped by a Pipeline.
type Answer interface {
// Struct waits until the call is finished and returns the result.
Struct() (Struct, error)
// The following methods are the same as in Client except with
// an added transform parameter -- a path to the interface to use.
PipelineCall(transform []PipelineOp, call *Call) Answer
PipelineClose(transform []PipelineOp) error
}
// A Pipeline is a generic wrapper for an answer.
type Pipeline struct {
answer Answer
parent *Pipeline
op PipelineOp
}
// NewPipeline returns a new pipeline based on an answer.
func NewPipeline(ans Answer) *Pipeline {
return &Pipeline{answer: ans}
}
// Answer returns the answer the pipeline is derived from.
func (p *Pipeline) Answer() Answer {
return p.answer
}
// Transform returns the operations needed to transform the root answer
// into the value p represents.
func (p *Pipeline) Transform() []PipelineOp {
n := 0
for q := p; q.parent != nil; q = q.parent {
n++
}
xform := make([]PipelineOp, n)
for i, q := n-1, p; q.parent != nil; i, q = i-1, q.parent {
xform[i] = q.op
}
return xform
}
// Struct waits until the answer is resolved and returns the struct
// this pipeline represents.
func (p *Pipeline) Struct() (Struct, error) {
s, err := p.answer.Struct()
if err != nil {
return Struct{}, err
}
ptr, err := TransformPtr(s.ToPtr(), p.Transform())
if err != nil {
return Struct{}, err
}
return ptr.Struct(), nil
}
// Client returns the client version of p.
func (p *Pipeline) Client() *PipelineClient {
return (*PipelineClient)(p)
}
// GetPipeline returns a derived pipeline which yields the pointer field given.
func (p *Pipeline) GetPipeline(off uint16) *Pipeline {
return p.GetPipelineDefault(off, nil)
}
// GetPipelineDefault returns a derived pipeline which yields the pointer field given,
// defaulting to the value given.
func (p *Pipeline) GetPipelineDefault(off uint16, def []byte) *Pipeline {
return &Pipeline{
answer: p.answer,
parent: p,
op: PipelineOp{
Field: off,
DefaultValue: def,
},
}
}
// PipelineClient implements Client by calling to the pipeline's answer.
type PipelineClient Pipeline
func (pc *PipelineClient) transform() []PipelineOp {
return (*Pipeline)(pc).Transform()
}
// Call calls Answer.PipelineCall with the pipeline's transform.
func (pc *PipelineClient) Call(call *Call) Answer {
return pc.answer.PipelineCall(pc.transform(), call)
}
// Close calls Answer.PipelineClose with the pipeline's transform.
func (pc *PipelineClient) Close() error {
return pc.answer.PipelineClose(pc.transform())
}
// A PipelineOp describes a step in transforming a pipeline.
// It maps closely with the PromisedAnswer.Op struct in rpc.capnp.
type PipelineOp struct {
Field uint16
DefaultValue []byte
}
// String returns a human-readable description of op.
func (op PipelineOp) String() string {
s := make([]byte, 0, 32)
s = append(s, "get field "...)
s = strconv.AppendInt(s, int64(op.Field), 10)
if op.DefaultValue == nil {
return string(s)
}
s = append(s, " with default"...)
return string(s)
}
// A Method identifies a method along with an optional human-readable
// description of the method.
type Method struct {
InterfaceID uint64
MethodID uint16
// Canonical name of the interface. May be empty.
InterfaceName string
// Method name as it appears in the schema. May be empty.
MethodName string
}
// String returns a formatted string containing the interface name or
// the method name if present, otherwise it uses the raw IDs.
// This is suitable for use in error messages and logs.
func (m *Method) String() string {
buf := make([]byte, 0, 128)
if m.InterfaceName == "" {
buf = append(buf, '@', '0', 'x')
buf = strconv.AppendUint(buf, m.InterfaceID, 16)
} else {
buf = append(buf, m.InterfaceName...)
}
buf = append(buf, '.')
if m.MethodName == "" {
buf = append(buf, '@')
buf = strconv.AppendUint(buf, uint64(m.MethodID), 10)
} else {
buf = append(buf, m.MethodName...)
}
return string(buf)
}
// Transform applies a sequence of pipeline operations to a pointer
// and returns the result.
//
// Deprecated: Use TransformPtr.
func Transform(p Pointer, transform []PipelineOp) (Pointer, error) {
pp, err := TransformPtr(toPtr(p), transform)
return pp.toPointer(), err
}
// TransformPtr applies a sequence of pipeline operations to a pointer
// and returns the result.
func TransformPtr(p Ptr, transform []PipelineOp) (Ptr, error) {
n := len(transform)
if n == 0 {
return p, nil
}
s := p.Struct()
for _, op := range transform[:n-1] {
field, err := s.Ptr(op.Field)
if err != nil {
return Ptr{}, err
}
s, err = field.StructDefault(op.DefaultValue)
if err != nil {
return Ptr{}, err
}
}
op := transform[n-1]
p, err := s.Ptr(op.Field)
if err != nil {
return Ptr{}, err
}
if op.DefaultValue != nil {
p, err = p.Default(op.DefaultValue)
}
return p, err
}
type immediateAnswer struct {
s Struct
}
// ImmediateAnswer returns an Answer that accesses s.
func ImmediateAnswer(s Struct) Answer {
return immediateAnswer{s}
}
func (ans immediateAnswer) Struct() (Struct, error) {
return ans.s, nil
}
func (ans immediateAnswer) findClient(transform []PipelineOp) Client {
p, err := TransformPtr(ans.s.ToPtr(), transform)
if err != nil {
return ErrorClient(err)
}
return p.Interface().Client()
}
func (ans immediateAnswer) PipelineCall(transform []PipelineOp, call *Call) Answer {
c := ans.findClient(transform)
if c == nil {
return ErrorAnswer(ErrNullClient)
}
return c.Call(call)
}
func (ans immediateAnswer) PipelineClose(transform []PipelineOp) error {
c := ans.findClient(transform)
if c == nil {
return ErrNullClient
}
return c.Close()
}
type errorAnswer struct {
e error
}
// ErrorAnswer returns a Answer that always returns error e.
func ErrorAnswer(e error) Answer {
return errorAnswer{e}
}
func (ans errorAnswer) Struct() (Struct, error) {
return Struct{}, ans.e
}
func (ans errorAnswer) PipelineCall([]PipelineOp, *Call) Answer {
return ans
}
func (ans errorAnswer) PipelineClose([]PipelineOp) error {
return ans.e
}
// IsFixedAnswer reports whether an answer was created by
// ImmediateAnswer or ErrorAnswer.
func IsFixedAnswer(ans Answer) bool {
switch ans.(type) {
case immediateAnswer:
return true
case errorAnswer:
return true
default:
return false
}
}
type errorClient struct {
e error
}
// ErrorClient returns a Client that always returns error e.
func ErrorClient(e error) Client {
return errorClient{e}
}
func (ec errorClient) Call(*Call) Answer {
return ErrorAnswer(ec.e)
}
func (ec errorClient) Close() error {
return nil
}
// IsErrorClient reports whether c was created with ErrorClient.
func IsErrorClient(c Client) bool {
_, ok := c.(errorClient)
return ok
}
// MethodError is an error on an associated method.
type MethodError struct {
Method *Method
Err error
}
// Error returns the method name concatenated with the error string.
func (e *MethodError) Error() string {
return e.Method.String() + ": " + e.Err.Error()
}
// ErrUnimplemented is the error returned when a method is called on
// a server that does not implement the method.
var ErrUnimplemented = errors.New("capnp: method not implemented")
// IsUnimplemented reports whether e indicates an unimplemented method error.
func IsUnimplemented(e error) bool {
if me, ok := e.(*MethodError); ok {
e = me.Err
}
return e == ErrUnimplemented
}

280
vendor/zombiezen.com/go/capnproto2/capability_test.go generated vendored Normal file
View File

@@ -0,0 +1,280 @@
package capnp
import (
"bytes"
"errors"
"testing"
)
func TestToInterface(t *testing.T) {
_, seg, err := NewMessage(SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
tests := []struct {
ptr Pointer
in Interface
}{
{nil, Interface{}},
{Struct{}, Interface{}},
{Struct{seg: seg, off: 0, depthLimit: maxDepth}, Interface{}},
{Interface{}, Interface{}},
{Interface{seg, 42}, Interface{seg, 42}},
}
for _, test := range tests {
if in := ToInterface(test.ptr); in != test.in {
t.Errorf("ToInterface(%#v) = %#v; want %#v", test.ptr, in, test.in)
}
}
}
func TestInterface_value(t *testing.T) {
_, seg, err := NewMessage(SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
tests := []struct {
in Interface
val rawPointer
}{
{Interface{}, 0},
{NewInterface(seg, 0), 0x0000000000000003},
{NewInterface(seg, 0xdeadbeef), 0xdeadbeef00000003},
}
for _, test := range tests {
for paddr := Address(0); paddr < 16; paddr++ {
if val := test.in.value(paddr); val != test.val {
t.Errorf("Interface{seg: %p, cap: %d}.value(%v) = %v; want %v", test.in.seg, test.in.cap, paddr, val, test.val)
}
}
}
}
func TestTransform(t *testing.T) {
_, s, err := NewMessage(SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
root, err := NewStruct(s, ObjectSize{PointerCount: 2})
if err != nil {
t.Fatal(err)
}
a, err := NewStruct(s, ObjectSize{DataSize: 8, PointerCount: 1})
if err != nil {
t.Fatal(err)
}
root.SetPointer(1, a)
a.SetUint64(0, 1)
b, err := NewStruct(s, ObjectSize{DataSize: 8})
if err != nil {
t.Fatal(err)
}
b.SetUint64(0, 2)
a.SetPointer(0, b)
dmsg, d, err := NewMessage(SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
da, err := NewStruct(d, ObjectSize{DataSize: 8, PointerCount: 1})
if err != nil {
t.Fatal(err)
}
if err := dmsg.SetRoot(da); err != nil {
t.Fatal(err)
}
da.SetUint64(0, 56)
db, err := NewStruct(d, ObjectSize{DataSize: 8})
if err != nil {
t.Fatal(err)
}
db.SetUint64(0, 78)
da.SetPointer(0, db)
tests := []struct {
p Pointer
transform []PipelineOp
out Pointer
}{
{
root,
nil,
root,
},
{
root,
[]PipelineOp{},
root,
},
{
root,
[]PipelineOp{
{Field: 0},
},
nil,
},
{
root,
[]PipelineOp{
{Field: 0, DefaultValue: mustMarshal(t, dmsg)},
},
da,
},
{
root,
[]PipelineOp{
{Field: 1},
},
a,
},
{
root,
[]PipelineOp{
{Field: 1, DefaultValue: mustMarshal(t, dmsg)},
},
a,
},
{
root,
[]PipelineOp{
{Field: 1},
{Field: 0},
},
b,
},
{
root,
[]PipelineOp{
{Field: 0},
{Field: 0},
},
nil,
},
{
root,
[]PipelineOp{
{Field: 0, DefaultValue: mustMarshal(t, dmsg)},
{Field: 0},
},
db,
},
{
root,
[]PipelineOp{
{Field: 0},
{Field: 0, DefaultValue: mustMarshal(t, dmsg)},
},
da,
},
{
root,
[]PipelineOp{
{Field: 0, DefaultValue: mustMarshal(t, dmsg)},
{Field: 1, DefaultValue: mustMarshal(t, dmsg)},
},
da,
},
}
for _, test := range tests {
out, err := Transform(test.p, test.transform)
if !deepPointerEqual(out, test.out) {
t.Errorf("Transform(%+v, %v) = %+v; want %+v", test.p, test.transform, out, test.out)
}
if err != nil {
t.Errorf("Transform(%+v, %v) error: %v", test.p, test.transform, err)
}
}
}
func TestMethodString(t *testing.T) {
tests := []struct {
m *Method
s string
}{
{
&Method{
InterfaceID: 0x8e5322c1e9282534,
MethodID: 1,
},
"@0x8e5322c1e9282534.@1",
},
{
&Method{
InterfaceID: 0x8e5322c1e9282534,
MethodID: 1,
InterfaceName: "aircraftlib:Echo",
MethodName: "foo",
},
"aircraftlib:Echo.foo",
},
}
for _, test := range tests {
if s := test.m.String(); s != test.s {
t.Errorf("%#v.String() = %q; want %q", test.m, s, test.s)
}
}
}
func TestPipelineOpString(t *testing.T) {
tests := []struct {
op PipelineOp
s string
}{
{
PipelineOp{Field: 4},
"get field 4",
},
{
PipelineOp{Field: 4, DefaultValue: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
"get field 4 with default",
},
}
for _, test := range tests {
if s := test.op.String(); s != test.s {
t.Errorf("%#v.String() = %q; want %q", test.op, s, test.s)
}
}
}
func TestIsUnimplemented(t *testing.T) {
tests := []struct {
e error
ok bool
}{
{nil, false},
{ErrUnimplemented, true},
{errors.New("foo"), false},
{&MethodError{Method: new(Method), Err: ErrUnimplemented}, true},
{&MethodError{Method: new(Method), Err: errors.New("foo")}, false},
}
for _, test := range tests {
if ok := IsUnimplemented(test.e); ok != test.ok {
t.Errorf("IsUnimplemented(%#v) = %t; want %t", test.e, ok, test.ok)
}
}
}
func mustMarshal(t *testing.T, msg *Message) []byte {
data, err := msg.Marshal()
if err != nil {
t.Fatal("Marshal:", err)
}
return data
}
func deepPointerEqual(a, b Pointer) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
msgA, _, _ := NewMessage(SingleSegment(nil))
msgA.SetRoot(a)
abytes, _ := msgA.Marshal()
msgB, _, _ := NewMessage(SingleSegment(nil))
msgB.SetRoot(b)
bbytes, _ := msgB.Marshal()
return bytes.Equal(abytes, bbytes)
}

427
vendor/zombiezen.com/go/capnproto2/capn.go generated vendored Normal file
View File

@@ -0,0 +1,427 @@
package capnp
import (
"encoding/binary"
"errors"
)
// A SegmentID is a numeric identifier for a Segment.
type SegmentID uint32
// A Segment is an allocation arena for Cap'n Proto objects.
// It is part of a Message, which can contain other segments that
// reference each other.
type Segment struct {
msg *Message
id SegmentID
data []byte
}
// Message returns the message that contains s.
func (s *Segment) Message() *Message {
return s.msg
}
// ID returns the segment's ID.
func (s *Segment) ID() SegmentID {
return s.id
}
// Data returns the raw byte slice for the segment.
func (s *Segment) Data() []byte {
return s.data
}
func (s *Segment) inBounds(addr Address) bool {
return addr < Address(len(s.data))
}
func (s *Segment) regionInBounds(base Address, sz Size) bool {
end, ok := base.addSize(sz)
if !ok {
return false
}
return end <= Address(len(s.data))
}
// slice returns the segment of data from base to base+sz.
func (s *Segment) slice(base Address, sz Size) []byte {
// Bounds check should have happened before calling slice.
return s.data[base : base+Address(sz)]
}
func (s *Segment) readUint8(addr Address) uint8 {
return s.slice(addr, 1)[0]
}
func (s *Segment) readUint16(addr Address) uint16 {
return binary.LittleEndian.Uint16(s.slice(addr, 2))
}
func (s *Segment) readUint32(addr Address) uint32 {
return binary.LittleEndian.Uint32(s.slice(addr, 4))
}
func (s *Segment) readUint64(addr Address) uint64 {
return binary.LittleEndian.Uint64(s.slice(addr, 8))
}
func (s *Segment) readRawPointer(addr Address) rawPointer {
return rawPointer(s.readUint64(addr))
}
func (s *Segment) writeUint8(addr Address, val uint8) {
s.slice(addr, 1)[0] = val
}
func (s *Segment) writeUint16(addr Address, val uint16) {
binary.LittleEndian.PutUint16(s.slice(addr, 2), val)
}
func (s *Segment) writeUint32(addr Address, val uint32) {
binary.LittleEndian.PutUint32(s.slice(addr, 4), val)
}
func (s *Segment) writeUint64(addr Address, val uint64) {
binary.LittleEndian.PutUint64(s.slice(addr, 8), val)
}
func (s *Segment) writeRawPointer(addr Address, val rawPointer) {
s.writeUint64(addr, uint64(val))
}
// root returns a 1-element pointer list that references the first word
// in the segment. This only makes sense to call on the first segment
// in a message.
func (s *Segment) root() PointerList {
sz := ObjectSize{PointerCount: 1}
if !s.regionInBounds(0, sz.totalSize()) {
return PointerList{}
}
return PointerList{List{
seg: s,
length: 1,
size: sz,
depthLimit: s.msg.depthLimit(),
}}
}
func (s *Segment) lookupSegment(id SegmentID) (*Segment, error) {
if s.id == id {
return s, nil
}
return s.msg.Segment(id)
}
func (s *Segment) readPtr(paddr Address, depthLimit uint) (ptr Ptr, err error) {
s, base, val, err := s.resolveFarPointer(paddr)
if err != nil {
return Ptr{}, err
}
if val == 0 {
return Ptr{}, nil
}
if depthLimit == 0 {
return Ptr{}, errDepthLimit
}
switch val.pointerType() {
case structPointer:
sp, err := s.readStructPtr(base, val)
if err != nil {
return Ptr{}, err
}
if !s.msg.ReadLimiter().canRead(sp.readSize()) {
return Ptr{}, errReadLimit
}
sp.depthLimit = depthLimit - 1
return sp.ToPtr(), nil
case listPointer:
lp, err := s.readListPtr(base, val)
if err != nil {
return Ptr{}, err
}
if !s.msg.ReadLimiter().canRead(lp.readSize()) {
return Ptr{}, errReadLimit
}
lp.depthLimit = depthLimit - 1
return lp.ToPtr(), nil
case otherPointer:
if val.otherPointerType() != 0 {
return Ptr{}, errOtherPointer
}
return Interface{
seg: s,
cap: val.capabilityIndex(),
}.ToPtr(), nil
default:
// Only other types are far pointers.
return Ptr{}, errBadLandingPad
}
}
func (s *Segment) readStructPtr(base Address, val rawPointer) (Struct, error) {
addr, ok := val.offset().resolve(base)
if !ok {
return Struct{}, errPointerAddress
}
sz := val.structSize()
if !s.regionInBounds(addr, sz.totalSize()) {
return Struct{}, errPointerAddress
}
return Struct{
seg: s,
off: addr,
size: sz,
}, nil
}
func (s *Segment) readListPtr(base Address, val rawPointer) (List, error) {
addr, ok := val.offset().resolve(base)
if !ok {
return List{}, errPointerAddress
}
lsize, ok := val.totalListSize()
if !ok {
return List{}, errOverflow
}
if !s.regionInBounds(addr, lsize) {
return List{}, errPointerAddress
}
lt := val.listType()
if lt == compositeList {
hdr := s.readRawPointer(addr)
var ok bool
addr, ok = addr.addSize(wordSize)
if !ok {
return List{}, errOverflow
}
if hdr.pointerType() != structPointer {
return List{}, errBadTag
}
sz := hdr.structSize()
n := int32(hdr.offset())
// TODO(light): check that this has the same end address
if tsize, ok := sz.totalSize().times(n); !ok {
return List{}, errOverflow
} else if !s.regionInBounds(addr, tsize) {
return List{}, errPointerAddress
}
return List{
seg: s,
size: sz,
off: addr,
length: n,
flags: isCompositeList,
}, nil
}
if lt == bit1List {
return List{
seg: s,
off: addr,
length: val.numListElements(),
flags: isBitList,
}, nil
}
return List{
seg: s,
size: val.elementSize(),
off: addr,
length: val.numListElements(),
}, nil
}
func (s *Segment) resolveFarPointer(paddr Address) (dst *Segment, base Address, resolved rawPointer, err error) {
// Encoding details at https://capnproto.org/encoding.html#inter-segment-pointers
val := s.readRawPointer(paddr)
switch val.pointerType() {
case doubleFarPointer:
padSeg, err := s.lookupSegment(val.farSegment())
if err != nil {
return nil, 0, 0, err
}
padAddr := val.farAddress()
if !padSeg.regionInBounds(padAddr, wordSize*2) {
return nil, 0, 0, errPointerAddress
}
far := padSeg.readRawPointer(padAddr)
if far.pointerType() != farPointer {
return nil, 0, 0, errBadLandingPad
}
tagAddr, ok := padAddr.addSize(wordSize)
if !ok {
return nil, 0, 0, errOverflow
}
tag := padSeg.readRawPointer(tagAddr)
if pt := tag.pointerType(); (pt != structPointer && pt != listPointer) || tag.offset() != 0 {
return nil, 0, 0, errBadLandingPad
}
if dst, err = s.lookupSegment(far.farSegment()); err != nil {
return nil, 0, 0, err
}
return dst, 0, landingPadNearPointer(far, tag), nil
case farPointer:
var err error
dst, err = s.lookupSegment(val.farSegment())
if err != nil {
return nil, 0, 0, err
}
padAddr := val.farAddress()
if !dst.regionInBounds(padAddr, wordSize) {
return nil, 0, 0, errPointerAddress
}
var ok bool
base, ok = padAddr.addSize(wordSize)
if !ok {
return nil, 0, 0, errOverflow
}
return dst, base, dst.readRawPointer(padAddr), nil
default:
var ok bool
base, ok = paddr.addSize(wordSize)
if !ok {
return nil, 0, 0, errOverflow
}
return s, base, val, nil
}
}
func (s *Segment) writePtr(off Address, src Ptr, forceCopy bool) error {
if !src.IsValid() {
s.writeRawPointer(off, 0)
return nil
}
// Copy src, if needed, and process pointers where placement is
// irrelevant (capabilities and zero-sized structs).
var srcAddr Address
var srcRaw rawPointer
switch src.flags.ptrType() {
case structPtrType:
st := src.Struct()
if st.size.isZero() {
// Zero-sized structs should always be encoded with offset -1 in
// order to avoid conflating with null. No allocation needed.
s.writeRawPointer(off, rawStructPointer(-1, ObjectSize{}))
return nil
}
if forceCopy || src.seg.msg != s.msg || st.flags&isListMember != 0 {
newSeg, newAddr, err := alloc(s, st.size.totalSize())
if err != nil {
return err
}
dst := Struct{
seg: newSeg,
off: newAddr,
size: st.size,
depthLimit: maxDepth,
// clear flags
}
if err := copyStruct(dst, st); err != nil {
return err
}
st = dst
src = dst.ToPtr()
}
srcAddr = st.off
srcRaw = rawStructPointer(0, st.size)
case listPtrType:
l := src.List()
if forceCopy || src.seg.msg != s.msg {
sz := l.allocSize()
newSeg, newAddr, err := alloc(s, sz)
if err != nil {
return err
}
dst := List{
seg: newSeg,
off: newAddr,
length: l.length,
size: l.size,
flags: l.flags,
depthLimit: maxDepth,
}
if dst.flags&isCompositeList != 0 {
// Copy tag word
newSeg.writeRawPointer(newAddr, l.seg.readRawPointer(l.off-Address(wordSize)))
var ok bool
dst.off, ok = dst.off.addSize(wordSize)
if !ok {
return errOverflow
}
sz -= wordSize
}
if dst.flags&isBitList != 0 || dst.size.PointerCount == 0 {
end, _ := l.off.addSize(sz) // list was already validated
copy(newSeg.data[dst.off:], l.seg.data[l.off:end])
} else {
for i := 0; i < l.Len(); i++ {
err := copyStruct(dst.Struct(i), l.Struct(i))
if err != nil {
return err
}
}
}
l = dst
src = dst.ToPtr()
}
srcAddr = l.off
if l.flags&isCompositeList != 0 {
srcAddr -= Address(wordSize)
}
srcRaw = l.raw()
case interfacePtrType:
i := src.Interface()
if src.seg.msg != s.msg {
c := s.msg.AddCap(i.Client())
i = NewInterface(s, c)
}
s.writeRawPointer(off, i.value(off))
return nil
default:
panic("unreachable")
}
switch {
case src.seg == s:
// Common case: src is in same segment as pointer.
// Use a near pointer.
s.writeRawPointer(off, srcRaw.withOffset(nearPointerOffset(off, srcAddr)))
return nil
case hasCapacity(src.seg.data, wordSize):
// Enough room adjacent to src to write a far pointer landing pad.
_, padAddr, _ := alloc(src.seg, wordSize)
src.seg.writeRawPointer(padAddr, srcRaw.withOffset(nearPointerOffset(padAddr, srcAddr)))
s.writeRawPointer(off, rawFarPointer(src.seg.id, padAddr))
return nil
default:
// Not enough room for a landing pad, need to use a double-far pointer.
padSeg, padAddr, err := alloc(s, wordSize*2)
if err != nil {
return err
}
padSeg.writeRawPointer(padAddr, rawFarPointer(src.seg.id, srcAddr))
padSeg.writeRawPointer(padAddr+Address(wordSize), srcRaw)
s.writeRawPointer(off, rawDoubleFarPointer(padSeg.id, padAddr))
return nil
}
}
var (
errPointerAddress = errors.New("capnp: invalid pointer address")
errBadLandingPad = errors.New("capnp: invalid far pointer landing pad")
errBadTag = errors.New("capnp: invalid tag word")
errOtherPointer = errors.New("capnp: unknown pointer type")
errObjectSize = errors.New("capnp: invalid object size")
errElementSize = errors.New("capnp: mismatched list element size")
errReadLimit = errors.New("capnp: read traversal limit reached")
errDepthLimit = errors.New("capnp: depth limit reached")
)
var (
errOverflow = errors.New("capnp: address or size overflow")
errOutOfBounds = errors.New("capnp: address out of bounds")
errCopyDepth = errors.New("capnp: copy depth too large")
errOverlap = errors.New("capnp: overlapping data on copy")
errListSize = errors.New("capnp: invalid list size")
)

742
vendor/zombiezen.com/go/capnproto2/capn_test.go generated vendored Normal file
View File

@@ -0,0 +1,742 @@
package capnp
import (
"bytes"
"encoding/binary"
"fmt"
"testing"
)
func TestSegmentInBounds(t *testing.T) {
tests := []struct {
n int
addr Address
ok bool
}{
{0, 0, false},
{0, 1, false},
{0, 2, false},
{1, 0, true},
{1, 1, false},
{1, 2, false},
{2, 0, true},
{2, 1, true},
{2, 2, false},
}
for _, test := range tests {
seg := &Segment{data: make([]byte, test.n)}
if ok := seg.inBounds(test.addr); ok != test.ok {
t.Errorf("&Segment{data: make([]byte, %d)}.inBounds(%#v) = %t; want %t", test.n, test.addr, ok, test.ok)
}
}
}
func TestSegmentRegionInBounds(t *testing.T) {
tests := []struct {
n int
addr Address
sz Size
ok bool
}{
{0, 0, 0, true}, // zero-sized region <= len is okay
{0, 0, 1, false},
{0, 1, 0, false},
{0, 1, 1, false},
{1, 0, 0, true},
{1, 0, 1, true},
{1, 1, 0, true},
{1, 1, 1, false},
{2, 0, 0, true},
{2, 0, 1, true},
{2, 0, 2, true},
{2, 0, 3, false},
{2, 1, 0, true},
{2, 1, 1, true},
{2, 1, 2, false},
{2, 1, 3, false},
{2, 2, 0, true},
{2, 2, 1, false},
}
for _, test := range tests {
seg := &Segment{data: make([]byte, test.n)}
if ok := seg.regionInBounds(test.addr, test.sz); ok != test.ok {
t.Errorf("&Segment{data: make([]byte, %d)}.regionInBounds(%#v, %#v) = %t; want %t", test.n, test.addr, test.sz, ok, test.ok)
}
}
}
func TestSegmentReadUint8(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint8
panics bool
}{
{data: []byte{}, addr: 0, panics: true},
{data: []byte{42}, addr: 0, val: 42},
{data: []byte{42}, addr: 1, panics: true},
{data: []byte{1, 42, 2}, addr: 0, val: 1},
{data: []byte{1, 42, 2}, addr: 1, val: 42},
{data: []byte{1, 42, 2}, addr: 2, val: 2},
{data: []byte{1, 42, 2}, addr: 3, panics: true},
}
for _, test := range tests {
seg := &Segment{data: test.data}
var val uint8
err := catchPanic(func() {
val = seg.readUint8(test.addr)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.readUint8(%v) unexpected panic: %v", test.data, test.addr, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.readUint8(%v) did not panic as expected", test.data, test.addr)
continue
}
if val != test.val {
t.Errorf("&Segment{data: % x}.readUint8(%v) = %#x; want %#x", test.data, test.addr, val, test.val)
}
}
}
func TestSegmentReadUint16(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint16
panics bool
}{
{data: []byte{}, addr: 0, panics: true},
{data: []byte{0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00}, addr: 0, val: 0},
{data: []byte{0x01, 0x00}, addr: 0, val: 1},
{data: []byte{0x34, 0x12}, addr: 0, val: 0x1234},
{data: []byte{0x34, 0x12, 0x56}, addr: 0, val: 0x1234},
{data: []byte{0x34, 0x12, 0x56}, addr: 1, val: 0x5612},
{data: []byte{0x34, 0x12, 0x56}, addr: 2, panics: true},
}
for _, test := range tests {
seg := &Segment{data: test.data}
var val uint16
err := catchPanic(func() {
val = seg.readUint16(test.addr)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.readUint16(%v) unexpected panic: %v", test.data, test.addr, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.readUint16(%v) did not panic as expected", test.data, test.addr)
continue
}
if val != test.val {
t.Errorf("&Segment{data: % x}.readUint16(%v) = %#x; want %#x", test.data, test.addr, val, test.val)
}
}
}
func TestSegmentReadUint32(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint32
panics bool
}{
{data: []byte{}, addr: 0, panics: true},
{data: []byte{0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00, 0x00}, addr: 0, val: 0},
{data: []byte{0x78, 0x56, 0x34, 0x12}, addr: 0, val: 0x12345678},
{data: []byte{0xff, 0x78, 0x56, 0x34, 0x12, 0xff}, addr: 1, val: 0x12345678},
{data: []byte{0xff, 0x78, 0x56, 0x34, 0x12, 0xff}, addr: 2, val: 0xff123456},
{data: []byte{0xff, 0x78, 0x56, 0x34, 0x12, 0xff}, addr: 3, panics: true},
}
for _, test := range tests {
seg := &Segment{data: test.data}
var val uint32
err := catchPanic(func() {
val = seg.readUint32(test.addr)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.readUint32(%v) unexpected panic: %v", test.data, test.addr, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.readUint32(%v) did not panic as expected", test.data, test.addr)
continue
}
if val != test.val {
t.Errorf("&Segment{data: % x}.readUint32(%v) = %#x; want %#x", test.data, test.addr, val, test.val)
}
}
}
func TestSegmentReadUint64(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint64
panics bool
}{
{data: []byte{}, addr: 0, panics: true},
{data: []byte{0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00, 0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, addr: 0, panics: true},
{data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, addr: 0, val: 0},
{data: []byte{0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01}, addr: 0, val: 0x0123456789abcdef},
{data: []byte{0xff, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0xff}, addr: 0, val: 0x23456789abcdefff},
{data: []byte{0xff, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0xff}, addr: 1, val: 0x0123456789abcdef},
{data: []byte{0xff, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0xff}, addr: 2, val: 0xff0123456789abcd},
{data: []byte{0xff, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0xff}, addr: 3, panics: true},
}
for _, test := range tests {
seg := &Segment{data: test.data}
var val uint64
err := catchPanic(func() {
val = seg.readUint64(test.addr)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.readUint64(%v) unexpected panic: %v", test.data, test.addr, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.readUint64(%v) did not panic as expected", test.data, test.addr)
continue
}
if val != test.val {
t.Errorf("&Segment{data: % x}.readUint64(%v) = %#x; want %#x", test.data, test.addr, val, test.val)
}
}
}
func TestSegmentWriteUint8(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint8
out []byte
panics bool
}{
{
data: []byte{},
addr: 0,
val: 0,
panics: true,
},
{
data: []byte{1},
addr: 0,
val: 42,
out: []byte{42},
},
{
data: []byte{42},
addr: 1,
val: 1,
panics: true,
},
{
data: []byte{1, 2, 3},
addr: 0,
val: 0xff,
out: []byte{0xff, 2, 3},
},
{
data: []byte{1, 2, 3},
addr: 1,
val: 0xff,
out: []byte{1, 0xff, 3},
},
{
data: []byte{1, 2, 3},
addr: 2,
val: 0xff,
out: []byte{1, 2, 0xff},
},
{
data: []byte{1, 2, 3},
addr: 3,
val: 0xff,
panics: true,
},
}
for _, test := range tests {
out := make([]byte, len(test.data))
copy(out, test.data)
seg := &Segment{data: out}
err := catchPanic(func() {
seg.writeUint8(test.addr, test.val)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.writeUint8(%v, %#x) unexpected panic: %v", test.data, test.addr, test.val, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.writeUint8(%v, %#x) did not panic as expected", test.data, test.addr, test.val)
continue
}
if !bytes.Equal(out, test.out) {
t.Errorf("data after &Segment{data: % x}.writeUint8(%v, %#x) = % x; want % x", test.data, test.addr, test.val, out, test.out)
}
}
}
func TestSegmentWriteUint16(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint16
out []byte
panics bool
}{
{
data: []byte{},
addr: 0,
val: 0,
panics: true,
},
{
data: []byte{1, 2, 3, 4},
addr: 1,
val: 0x1234,
out: []byte{1, 0x34, 0x12, 4},
},
}
for _, test := range tests {
out := make([]byte, len(test.data))
copy(out, test.data)
seg := &Segment{data: out}
err := catchPanic(func() {
seg.writeUint16(test.addr, test.val)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.writeUint16(%v, %#x) unexpected panic: %v", test.data, test.addr, test.val, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.writeUint16(%v, %#x) did not panic as expected", test.data, test.addr, test.val)
continue
}
if !bytes.Equal(out, test.out) {
t.Errorf("data after &Segment{data: % x}.writeUint16(%v, %#x) = % x; want % x", test.data, test.addr, test.val, out, test.out)
}
}
}
func TestSegmentWriteUint32(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint32
out []byte
panics bool
}{
{
data: []byte{},
addr: 0,
val: 0,
panics: true,
},
{
data: []byte{1, 2, 3, 4, 5, 6},
addr: 1,
val: 0x01234567,
out: []byte{1, 0x67, 0x45, 0x23, 0x01, 6},
},
}
for _, test := range tests {
out := make([]byte, len(test.data))
copy(out, test.data)
seg := &Segment{data: out}
err := catchPanic(func() {
seg.writeUint32(test.addr, test.val)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.writeUint32(%v, %#x) unexpected panic: %v", test.data, test.addr, test.val, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.writeUint32(%v, %#x) did not panic as expected", test.data, test.addr, test.val)
continue
}
if !bytes.Equal(out, test.out) {
t.Errorf("data after &Segment{data: % x}.writeUint32(%v, %#x) = % x; want % x", test.data, test.addr, test.val, out, test.out)
}
}
}
func TestSegmentWriteUint64(t *testing.T) {
tests := []struct {
data []byte
addr Address
val uint64
out []byte
panics bool
}{
{
data: []byte{},
addr: 0,
val: 0,
panics: true,
},
{
data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
addr: 1,
val: 0x0123456789abcdef,
out: []byte{1, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 10},
},
}
for _, test := range tests {
out := make([]byte, len(test.data))
copy(out, test.data)
seg := &Segment{data: out}
err := catchPanic(func() {
seg.writeUint64(test.addr, test.val)
})
if err != nil {
if !test.panics {
t.Errorf("&Segment{data: % x}.writeUint64(%v, %#x) unexpected panic: %v", test.data, test.addr, test.val, err)
}
continue
}
if test.panics {
t.Errorf("&Segment{data: % x}.writeUint64(%v, %#x) did not panic as expected", test.data, test.addr, test.val)
continue
}
if !bytes.Equal(out, test.out) {
t.Errorf("data after &Segment{data: % x}.writeUint64(%v, %#x) = % x; want % x", test.data, test.addr, test.val, out, test.out)
}
}
}
func TestSetPtrCopyListMember(t *testing.T) {
_, seg, err := NewMessage(SingleSegment(nil))
if err != nil {
t.Fatal("NewMessage:", err)
}
root, err := NewRootStruct(seg, ObjectSize{PointerCount: 2})
if err != nil {
t.Fatal("NewRootStruct:", err)
}
plist, err := NewCompositeList(seg, ObjectSize{PointerCount: 1}, 1)
if err != nil {
t.Fatal("NewCompositeList:", err)
}
if err := root.SetPtr(0, plist.ToPtr()); err != nil {
t.Fatal("root.SetPtr(0, plist):", err)
}
sub, err := NewStruct(seg, ObjectSize{DataSize: 8})
if err != nil {
t.Fatal("NewStruct:", err)
}
sub.SetUint64(0, 42)
pl0 := plist.Struct(0)
if err := pl0.SetPtr(0, sub.ToPtr()); err != nil {
t.Fatal("pl0.SetPtr(0, sub.ToPtr()):", err)
}
if err := root.SetPtr(1, pl0.ToPtr()); err != nil {
t.Error("root.SetPtr(1, pl0):", err)
}
p1, err := root.Ptr(1)
if err != nil {
t.Error("root.Ptr(1):", err)
}
s1 := p1.Struct()
if !s1.IsValid() {
t.Error("root.Ptr(1) is not a valid struct")
}
if SamePtr(s1.ToPtr(), pl0.ToPtr()) {
t.Error("list member not copied; points to same object")
}
s1p0, err := s1.Ptr(0)
if err != nil {
t.Error("root.Ptr(1).Struct().Ptr(0):", err)
}
s1s0 := s1p0.Struct()
if !s1s0.IsValid() {
t.Error("root.Ptr(1).Struct().Ptr(0) is not a valid struct")
}
if SamePtr(s1s0.ToPtr(), sub.ToPtr()) {
t.Error("sub-object not copied; points to same object")
}
if got := s1s0.Uint64(0); got != 42 {
t.Errorf("sub-object data = %d; want 42", got)
}
}
func TestSetPtrToZeroSizeStruct(t *testing.T) {
_, seg, err := NewMessage(SingleSegment(nil))
if err != nil {
t.Fatal("NewMessage:", err)
}
root, err := NewRootStruct(seg, ObjectSize{PointerCount: 1})
if err != nil {
t.Fatal("NewRootStruct:", err)
}
sub, err := NewStruct(seg, ObjectSize{})
if err != nil {
t.Fatal("NewStruct:", err)
}
if err := root.SetPtr(0, sub.ToPtr()); err != nil {
t.Fatal("root.SetPtr(0, sub.ToPtr()):", err)
}
ptrSlice := seg.Data()[root.off : root.off+8]
want := []byte{0xfc, 0xff, 0xff, 0xff, 0, 0, 0, 0}
if !bytes.Equal(ptrSlice, want) {
t.Errorf("SetPtr wrote % 02x; want % 02x", ptrSlice, want)
}
}
func TestReadFarPointers(t *testing.T) {
msg := &Message{
// an rpc.capnp Message
Arena: MultiSegment([][]byte{
// Segment 0
{
// Double-far pointer: segment 2, offset 0
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
},
// Segment 1
{
// (Root) Struct data section
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Struct pointer section
// Double-far pointer: segment 4, offset 0
0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
},
// Segment 2
{
// Far pointer landing pad: segment 1, offset 0
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
// Far pointer landing pad tag word: struct with 1 word data and 1 pointer
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
},
// Segment 3
{
// (Root>0) Struct data section
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0xaa, 0x70, 0x65, 0x21, 0xd7, 0x7b, 0x31, 0xa7,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Struct pointer section
// Far pointer: segment 4, offset 4
0x22, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
// Far pointer: segment 4, offset 7
0x3a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
// Null
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
// Segment 4
{
// Far pointer landing pad: segment 3, offset 0
0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
// Far pointer landing pad tag word: struct with 3 word data and 3 pointer
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
// (Root>0>0) Struct data section
0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Struct pointer section
// Null
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Far pointer landing pad: struct pointer: offset -3, 1 word data, 1 pointer
0xf4, 0xff, 0xff, 0xff, 0x01, 0x00, 0x01, 0x00,
// (Root>0>1) Struct pointer section
// Struct pointer: offset 2, 1 word data
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
// Null
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Far pointer landing pad: struct pointer: offset -3, 2 pointers
0xf4, 0xff, 0xff, 0xff, 0x00, 0x00, 0x02, 0x00,
// (Root>0>1>0) Struct data section
0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
}),
}
rootp, err := msg.RootPtr()
if err != nil {
t.Error("RootPtr:", err)
}
root := rootp.Struct()
if root.Uint16(0) != 2 {
t.Errorf("root.Uint16(0) = %d; want 2", root.Uint16(0))
}
callp, err := root.Ptr(0)
if err != nil {
t.Error("root.Ptr(0):", err)
}
call := callp.Struct()
targetp, err := call.Ptr(0)
if err != nil {
t.Error("root.Ptr(0).Ptr(0):", err)
}
if got := targetp.Struct().Uint32(0); got != 84 {
t.Errorf("root.Ptr(0).Ptr(0).Uint32(0) = %d; want 84", got)
}
paramsp, err := call.Ptr(1)
if err != nil {
t.Error("root.Ptr(0).Ptr(1):", err)
}
contentp, err := paramsp.Struct().Ptr(0)
if err != nil {
t.Error("root.Ptr(0).Ptr(1).Ptr(0):", err)
}
if got := contentp.Struct().Uint64(0); got != 42 {
t.Errorf("root.Ptr(0).Ptr(1).Ptr(0).Uint64(0) = %d; want 42", got)
}
}
func TestWriteFarPointer(t *testing.T) {
// TODO(someday): run same test with a two-word list
msg := &Message{
Arena: MultiSegment([][]byte{
make([]byte, 8),
make([]byte, 0, 24),
}),
}
seg1, err := msg.Segment(1)
if err != nil {
t.Fatal("msg.Segment(1):", err)
}
s, err := NewStruct(seg1, ObjectSize{DataSize: 8, PointerCount: 1})
if err != nil {
t.Fatal("NewStruct(msg.Segment(1), ObjectSize{8, 1}):", err)
}
if s.Segment() != seg1 {
t.Fatalf("struct allocated in segment %d", s.Segment().ID())
}
if err := msg.SetRootPtr(s.ToPtr()); err != nil {
t.Error("msg.SetRootPtr(...):", err)
}
seg0, err := msg.Segment(0)
if err != nil {
t.Fatal("msg.Segment(0):", err)
}
root := rawPointer(binary.LittleEndian.Uint64(seg0.Data()))
if root.pointerType() != farPointer {
t.Fatalf("root (%#016x) type = %v; want %v (farPointer)", root, root.pointerType(), farPointer)
}
if root.farSegment() != 1 {
t.Fatalf("root points to segment %d; want 1", root.farSegment())
}
padAddr := root.farAddress()
if padAddr > Address(len(seg1.Data())-8) {
t.Fatalf("root points to out of bounds address %v; size of segment is %d", padAddr, len(seg1.Data()))
}
pad := rawPointer(binary.LittleEndian.Uint64(seg1.Data()[padAddr:]))
if pad.pointerType() != structPointer {
t.Errorf("landing pad (%#016x) type = %v; want %v (structPointer)", pad, pad.pointerType(), structPointer)
}
if got, ok := pad.offset().resolve(padAddr + 8); !ok || got != s.off {
t.Errorf("landing pad (%#016x @ %v) resolved address = %v, %t; want %v, true", pad, padAddr, got, ok, s.off)
}
if got, want := pad.structSize(), (ObjectSize{DataSize: 8, PointerCount: 1}); got != want {
t.Errorf("landing pad (%#016x) struct size = %v; want %v", pad, got, want)
}
}
func TestWriteDoubleFarPointer(t *testing.T) {
// TODO(someday): run same test with a two-word list
msg := &Message{
Arena: MultiSegment([][]byte{
make([]byte, 8),
make([]byte, 0, 16),
}),
}
seg1, err := msg.Segment(1)
if err != nil {
t.Fatal("msg.Segment(1):", err)
}
s, err := NewStruct(seg1, ObjectSize{DataSize: 8, PointerCount: 1})
if err != nil {
t.Fatal("NewStruct(msg.Segment(1), ObjectSize{8, 1}):", err)
}
if s.Segment() != seg1 {
t.Fatalf("struct allocated in segment %d", s.Segment().ID())
}
if err := msg.SetRootPtr(s.ToPtr()); err != nil {
t.Error("msg.SetRootPtr(...):", err)
}
seg0, err := msg.Segment(0)
if err != nil {
t.Fatal("msg.Segment(0):", err)
}
root := rawPointer(binary.LittleEndian.Uint64(seg0.Data()))
if root.pointerType() != doubleFarPointer {
t.Fatalf("root (%#016x) type = %v; want %v (doubleFarPointer)", root, root.pointerType(), doubleFarPointer)
}
if root.farSegment() == 0 || root.farSegment() == 1 {
t.Fatalf("root points to segment %d; want !=0,1", root.farSegment())
}
padSeg, err := msg.Segment(root.farSegment())
if err != nil {
t.Fatalf("msg.Segment(%d): %v", root.farSegment(), err)
}
padAddr := root.farAddress()
if padAddr > Address(len(padSeg.Data())-16) {
t.Fatalf("root points to out of bounds address %v; size of segment is %d", padAddr, len(padSeg.Data()))
}
pad1 := rawPointer(binary.LittleEndian.Uint64(padSeg.Data()[padAddr:]))
if pad1.pointerType() != farPointer {
t.Errorf("landing pad pointer 1 (%#016x) type = %v; want %v (farPointer)", pad1, pad1.pointerType(), farPointer)
}
if pad1.farSegment() != 1 {
t.Fatalf("landing pad pointer 1 (%#016x) points to segment %d; want 1", pad1, pad1.farSegment())
}
if pad1.farAddress() != s.off {
t.Fatalf("landing pad pointer 1 (%#016x) points to address %v; want %v", pad1, pad1.farAddress(), s.off)
}
pad2 := rawPointer(binary.LittleEndian.Uint64(padSeg.Data()[padAddr+8:]))
if pad2.pointerType() != structPointer {
t.Errorf("landing pad pointer 2 (%#016x) type = %v; want %v (structPointer)", pad2, pad2.pointerType(), structPointer)
}
if pad2.offset() != 0 {
t.Errorf("landing pad pointer 2 (%#016x) offset = %d; want 0", pad2, pad2.offset())
}
if got, want := pad2.structSize(), (ObjectSize{DataSize: 8, PointerCount: 1}); got != want {
t.Errorf("landing pad pointer 2 (%#016x) struct size = %v; want %v", pad2, got, want)
}
}
func catchPanic(f func()) (err error) {
defer func() {
pval := recover()
if pval == nil {
return
}
e, ok := pval.(error)
if !ok {
err = fmt.Errorf("non-error panic: %#v", pval)
return
}
err = e
}()
f()
return nil
}

View File

@@ -0,0 +1 @@
/capnpc-go

View File

@@ -0,0 +1,36 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"capnpc-go.go",
"fileparts.go",
"nodes.go",
"templateparams.go",
"templates.go",
],
importpath = "zombiezen.com/go/capnproto2/capnpc-go",
visibility = ["//visibility:private"],
deps = [
"//:go_default_library",
"//internal/schema:go_default_library",
],
)
go_binary(
name = "capnpc-go",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["capnpc-go_test.go"],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//:go_default_library",
"//encoding/text:go_default_library",
"//internal/schema:go_default_library",
],
)

1208
vendor/zombiezen.com/go/capnproto2/capnpc-go/capnpc-go.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,471 @@
package main
import (
"bytes"
"fmt"
"go/parser"
"go/token"
"io/ioutil"
"path/filepath"
"sort"
"strconv"
"strings"
"testing"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/encoding/text"
"zombiezen.com/go/capnproto2/internal/schema"
)
func readTestFile(name string) ([]byte, error) {
path := filepath.Join("testdata", name)
return ioutil.ReadFile(path)
}
func mustReadTestFile(t *testing.T, name string) []byte {
data, err := readTestFile(name)
if err != nil {
t.Fatal(err)
}
return data
}
func mustReadGeneratorRequest(t *testing.T, name string) schema.CodeGeneratorRequest {
data := mustReadTestFile(t, name)
msg, err := capnp.Unmarshal(data)
if err != nil {
t.Fatalf("Unmarshaling %s: %v", name, err)
}
req, err := schema.ReadRootCodeGeneratorRequest(msg)
if err != nil {
t.Fatalf("Reading code generator request %s: %v", name, err)
}
return req
}
func TestBuildNodeMap(t *testing.T) {
tests := []struct {
name string
fileID uint64
fileNodes []uint64
}{
{
name: "go.capnp.out",
fileID: 0xd12a1c51fedd6c88,
fileNodes: []uint64{
0xbea97f1023792be0,
0xe130b601260e44b5,
0xc58ad6bd519f935e,
0xa574b41924caefc7,
0xc8768679ec52e012,
0xfa10659ae02f2093,
0xc2b96012172f8df1,
},
},
{
name: "group.capnp.out",
fileID: 0x83c2b5818e83ab19,
fileNodes: []uint64{
0xd119fd352d8ea888, // the struct
0x822357857e5925d4, // the group
},
},
}
for _, test := range tests {
data, err := readTestFile(test.name)
if err != nil {
t.Errorf("readTestFile(%q): %v", test.name, err)
continue
}
msg, err := capnp.Unmarshal(data)
if err != nil {
t.Errorf("Unmarshaling %s: %v", test.name, err)
continue
}
req, err := schema.ReadRootCodeGeneratorRequest(msg)
if err != nil {
t.Errorf("Reading code generator request %s: %v", test.name, err)
continue
}
nodes, err := buildNodeMap(req)
if err != nil {
t.Errorf("%s: buildNodeMap: %v", test.name, err)
}
f := nodes[test.fileID]
if f == nil {
t.Errorf("%s: node map is missing file node @%#x", test.name, test.fileID)
continue
}
if f.Id() != test.fileID {
t.Errorf("%s: node map has ID @%#x for lookup of @%#x", test.name, f.Id(), test.fileID)
}
// Test node.nodes collection
for _, id := range test.fileNodes {
found := false
for _, fn := range f.nodes {
if fn.Id() == id {
found = true
break
}
}
if !found {
t.Errorf("%s: missing @%#x from file nodes", test.name, id)
}
}
// Test map lookup
for _, k := range test.fileNodes {
n := nodes[k]
if n == nil {
t.Errorf("%s: missing @%#x from node map", test.name, k)
}
if n.Id() != k {
t.Errorf("%s: node map has ID @%#x for lookup of @%#x", test.name, n.Id(), k)
}
}
}
}
func TestRemoteScope(t *testing.T) {
type scopeTest struct {
name string
constID uint64
initImports []importSpec
remoteName string
remoteNew string
imports []importSpec
}
tests := []scopeTest{
{
name: "same-file struct",
constID: 0x84efedc75e99768d, // scopes.fooVar
remoteName: "Foo",
remoteNew: "NewFoo",
},
{
name: "different file struct",
constID: 0x836faf1834d91729, // scopes.otherFooVar
remoteName: "otherscopes.Foo",
remoteNew: "otherscopes.NewFoo",
imports: []importSpec{
{name: "otherscopes", path: "zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes"},
},
},
{
name: "same-file struct list",
constID: 0xcda2680ec5c921e0, // scopes.fooListVar
remoteName: "Foo_List",
remoteNew: "NewFoo_List",
},
{
name: "different file struct list",
constID: 0x83e7e1b3cd1be338, // scopes.otherFooListVar
remoteName: "otherscopes.Foo_List",
remoteNew: "otherscopes.NewFoo_List",
imports: []importSpec{
{name: "otherscopes", path: "zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes"},
},
},
{
name: "built-in Int32 list",
constID: 0xacf3d9917d0bb0f0, // scopes.intList
remoteName: "capnp.Int32List",
remoteNew: "capnp.NewInt32List",
imports: []importSpec{
{name: "capnp", path: "zombiezen.com/go/capnproto2"},
},
},
}
req := mustReadGeneratorRequest(t, "scopes.capnp.out")
nodes, err := buildNodeMap(req)
if err != nil {
t.Fatal("buildNodeMap:", err)
}
collect := func(test scopeTest) (g *generator, typ schema.Type, from *node, ok bool) {
g = newGenerator(0xd68755941d99d05e, nodes, genoptions{})
v := nodes[test.constID]
if v == nil {
t.Errorf("Can't find const @%#x for %s test", test.constID, test.name)
return nil, schema.Type{}, nil, false
}
if v.Which() != schema.Node_Which_const {
t.Errorf("Type of node @%#x in %s test is a %v node; want const. Check the test.", test.constID, test.name, v.Which())
return nil, schema.Type{}, nil, false
}
constType, _ := v.Const().Type()
for _, i := range test.initImports {
g.imports.add(i)
}
return g, constType, v, true
}
for _, test := range tests {
g, typ, from, ok := collect(test)
if !ok {
continue
}
rn, err := g.RemoteTypeName(typ, from)
if err != nil {
t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]) error: %v", test.name, test.constID, test.constID, err)
continue
}
if rn != test.remoteName {
t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]) = %q; want %q", test.name, test.constID, test.constID, rn, test.remoteName)
continue
}
if !hasExactImports(test.imports, g.imports) {
t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]); g.imports = %s; want %s", test.name, test.constID, test.constID, formatImportSpecs(g.imports.usedImports()), formatImportSpecs(test.imports))
continue
}
}
for _, test := range tests {
g, typ, from, ok := collect(test)
if !ok {
continue
}
rn, err := g.RemoteTypeNew(typ, from)
if err != nil {
t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]) error: %v", test.name, test.constID, test.constID, err)
continue
}
if rn != test.remoteNew {
t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]) = %q; want %q", test.name, test.constID, test.constID, rn, test.remoteNew)
continue
}
if !hasExactImports(test.imports, g.imports) {
t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]); g.imports = %s; want %s", test.name, test.constID, test.constID, formatImportSpecs(g.imports.usedImports()), formatImportSpecs(test.imports))
continue
}
}
}
func hasExactImports(specs []importSpec, imp imports) bool {
used := imp.usedImports()
if len(used) != len(specs) {
return false
}
outer:
for i := range specs {
for j := range used {
if specs[i] == used[j] {
continue outer
}
}
return false
}
return true
}
func formatImportSpecs(specs []importSpec) string {
var buf bytes.Buffer
for i, s := range specs {
if i > 0 {
buf.WriteString("; ")
}
buf.WriteString(s.String())
}
return buf.String()
}
func TestDefineConstNodes(t *testing.T) {
req := mustReadGeneratorRequest(t, "const.capnp.out")
nodes, err := buildNodeMap(req)
if err != nil {
t.Fatal("buildNodeMap:", err)
}
g := newGenerator(0xc260cb50ae622e10, nodes, genoptions{})
getCalls := traceGenerator(g)
err = g.defineConstNodes(nodes[0xc260cb50ae622e10].nodes)
if err != nil {
t.Fatal("defineConstNodes:", err)
}
calls := getCalls()
if len(calls) != 1 {
t.Fatalf("defineConstNodes called %d templates; want 1", len(calls))
}
p, ok := calls[0].params.(constantsParams)
if calls[0].name != "constants" || !ok {
t.Fatalf("defineConstNodes rendered %v; want render of constants template", calls[0])
}
if !containsExactlyIDs(p.Consts, 0xda96e2255811b258) {
t.Errorf("defineConstNodes rendered Consts %s", nodeListString(p.Consts))
}
if !containsExactlyIDs(p.Vars, 0xe0a385c7be1fea4d) {
t.Errorf("defineConstNodes rendered Vars %s", nodeListString(p.Vars))
}
}
func TestDefineFile(t *testing.T) {
// Sanity check to make sure codegen produces parseable Go.
const iterations = 3
defaultOptions := genoptions{
promises: true,
schemas: true,
structStrings: true,
}
tests := []struct {
fileID uint64
fname string
opts genoptions
}{
{0x832bcc6686a26d56, "aircraft.capnp.out", defaultOptions},
{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{
promises: false,
schemas: false,
structStrings: false,
}},
{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{
promises: true,
schemas: false,
structStrings: false,
}},
{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{
promises: false,
schemas: true,
structStrings: false,
}},
{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{
promises: true,
schemas: true,
structStrings: false,
}},
{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{
promises: false,
schemas: true,
structStrings: true,
}},
{0x83c2b5818e83ab19, "group.capnp.out", defaultOptions},
{0xb312981b2552a250, "rpc.capnp.out", defaultOptions},
{0xd68755941d99d05e, "scopes.capnp.out", defaultOptions},
{0xecd50d792c3d9992, "util.capnp.out", defaultOptions},
}
for _, test := range tests {
data, err := readTestFile(test.fname)
if err != nil {
t.Errorf("reading %s: %v", test.fname, err)
continue
}
msg, err := capnp.Unmarshal(data)
if err != nil {
t.Errorf("Unmarshaling %s: %v", test.fname, err)
continue
}
req, err := schema.ReadRootCodeGeneratorRequest(msg)
if err != nil {
t.Errorf("Reading code generator request %s: %v", test.fname, err)
continue
}
nodes, err := buildNodeMap(req)
if err != nil {
t.Errorf("buildNodeMap %s: %v", test.fname, err)
continue
}
g := newGenerator(test.fileID, nodes, test.opts)
if err := g.defineFile(); err != nil {
t.Errorf("defineFile %s %+v: %v", test.fname, test.opts, err)
continue
}
src := g.generate()
if _, err := parser.ParseFile(token.NewFileSet(), test.fname+".go", src, 0); err != nil {
// TODO(light): log src
t.Errorf("generate %s %+v failed to parse: %v", test.fname, test.opts, err)
}
// Generation should be deterministic between runs.
for i := 0; i < iterations-1; i++ {
g := newGenerator(test.fileID, nodes, test.opts)
if err := g.defineFile(); err != nil {
t.Errorf("defineFile %s %+v [iteration %d]: %v", test.fname, test.opts, i+2, err)
continue
}
src2 := g.generate()
if !bytes.Equal(src, src2) {
t.Errorf("defineFile %s %+v [iteration %d] did not match iteration 1: non-deterministic", test.fname, test.opts, i+2)
}
}
}
}
func TestSchemaVarLiteral(t *testing.T) {
tests := []string{
"",
"foo",
"deadbeefdeadbeef",
"deadbeefdeadbeefdeadbeef",
"\x00\x00",
"\xff\xff",
"\n",
" ~\"\\",
"\xff\xff\x27\xa1\xe3\xf1",
}
for _, test := range tests {
got := schemaVarParams{schema: []byte(test)}.SchemaLiteral()
u, err := strconv.Unquote(strings.Replace(got, "\" +\n\t\"", "", -1))
if err != nil {
t.Errorf("schema literal of %q does not parse: %v\n\tproduced: %s", test, err, got)
} else if u != test {
t.Errorf("schema literal of %q != %s", test, got)
}
}
}
type traceRenderer struct {
renderer
calls []renderCall
}
func traceGenerator(g *generator) (getCalls func() []renderCall) {
tr := &traceRenderer{renderer: g.r}
g.r = tr
return func() []renderCall { return tr.calls }
}
func (tr *traceRenderer) Render(name string, params interface{}) error {
tr.calls = append(tr.calls, renderCall{name, params})
return tr.renderer.Render(name, params)
}
type renderCall struct {
name string
params interface{}
}
func (rc renderCall) String() string {
return fmt.Sprintf("{%q %#v}", rc.name, rc.params)
}
func containsExactlyIDs(nodes []*node, ids ...uint64) bool {
if len(nodes) != len(ids) {
return false
}
sorted := make([]uint64, len(ids))
copy(sorted, ids)
sort.Sort(uint64Slice(sorted))
actual := make([]uint64, len(nodes))
for i := range nodes {
actual[i] = nodes[i].Id()
}
sort.Sort(uint64Slice(actual))
for i := range sorted {
if actual[i] != sorted[i] {
return false
}
}
return true
}
func nodeListString(n []*node) string {
b := new(bytes.Buffer)
e := text.NewEncoder(b)
b.WriteByte('[')
for i, nn := range n {
if i > 0 {
b.WriteByte(' ')
}
e.Encode(0xe682ab4cf923a417, nn.Struct)
}
b.WriteByte(']')
return b.String()
}

View File

@@ -0,0 +1,194 @@
package main
import (
"fmt"
"strconv"
"strings"
"unicode"
"zombiezen.com/go/capnproto2"
)
type staticData struct {
name string
buf []byte
}
func (sd *staticData) init(fileID uint64) {
sd.name = fmt.Sprintf("x_%x", fileID)
sd.buf = make([]byte, 0, 4096)
}
func (sd *staticData) copyData(obj capnp.Ptr) (staticDataRef, error) {
m, _, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
return staticDataRef{}, err
}
err = m.SetRootPtr(obj)
if err != nil {
return staticDataRef{}, err
}
data, err := m.Marshal()
if err != nil {
return staticDataRef{}, err
}
ref := staticDataRef{data: sd}
ref.Start = len(sd.buf)
sd.buf = append(sd.buf, data...)
ref.End = len(sd.buf)
return ref, nil
}
type staticDataRef struct {
data *staticData
Start, End int
}
func (ref staticDataRef) IsValid() bool {
return ref.Start < ref.End
}
func (ref staticDataRef) String() string {
return fmt.Sprintf("%s[%d:%d]", ref.data.name, ref.Start, ref.End)
}
type imports struct {
specs []importSpec
used map[string]bool // keyed on import path
}
var capnpImportSpec = importSpec{path: capnpImport, name: "capnp"}
func (i *imports) init() {
i.specs = nil
i.used = make(map[string]bool)
i.reserve(capnpImportSpec)
i.reserve(importSpec{path: schemasImport, name: "schemas"})
i.reserve(importSpec{path: serverImport, name: "server"})
i.reserve(importSpec{path: textImport, name: "text"})
i.reserve(importSpec{path: contextImport, name: "context"})
i.reserve(importSpec{path: "math", name: "math"})
i.reserve(importSpec{path: "strconv", name: "strconv"})
}
func (i *imports) Capnp() string {
return i.add(importSpec{path: capnpImport, name: "capnp"})
}
func (i *imports) Schemas() string {
return i.add(importSpec{path: schemasImport, name: "schemas"})
}
func (i *imports) Server() string {
return i.add(importSpec{path: serverImport, name: "server"})
}
func (i *imports) Text() string {
return i.add(importSpec{path: textImport, name: "text"})
}
func (i *imports) Context() string {
return i.add(importSpec{path: contextImport, name: "context"})
}
func (i *imports) Math() string {
return i.add(importSpec{path: "math", name: "math"})
}
func (i *imports) Strconv() string {
return i.add(importSpec{path: "strconv", name: "strconv"})
}
func (i *imports) usedImports() []importSpec {
specs := make([]importSpec, 0, len(i.specs))
for _, s := range i.specs {
if i.used[s.path] {
specs = append(specs, s)
}
}
return specs
}
func (i *imports) byPath(path string) (spec importSpec, ok bool) {
for _, spec = range i.specs {
if spec.path == path {
return spec, true
}
}
return importSpec{}, false
}
func (i *imports) byName(name string) (spec importSpec, ok bool) {
for _, spec = range i.specs {
if spec.name == name {
return spec, true
}
}
return importSpec{}, false
}
func (i *imports) add(spec importSpec) (name string) {
name = i.reserve(spec)
i.used[spec.path] = true
return name
}
// reserve adds an import spec without marking it as used.
func (i *imports) reserve(spec importSpec) (name string) {
if ispec, ok := i.byPath(spec.path); ok {
return ispec.name
}
if spec.name == "" {
spec.name = pkgFromImport(spec.path)
}
if _, found := i.byName(spec.name); found {
for base, n := spec.name, uint64(2); ; n++ {
spec.name = base + strconv.FormatUint(n, 10)
if _, found = i.byName(spec.name); !found {
break
}
}
}
i.specs = append(i.specs, spec)
return spec.name
}
func pkgFromImport(path string) string {
if i := strings.LastIndex(path, "/"); i != -1 {
path = path[i+1:]
}
p := []rune(path)
n := 0
for _, r := range p {
if isIdent(r) {
p[n] = r
n++
}
}
if n == 0 || !isLower(p[0]) {
return "pkg" + string(p[:n])
}
return string(p[:n])
}
func isLower(r rune) bool {
return 'a' <= r && r <= 'z' || r == '_'
}
func isIdent(r rune) bool {
return isLower(r) || 'A' <= r && r <= 'Z' || r >= 0x80 && unicode.IsLetter(r)
}
type importSpec struct {
path string
name string
}
func (spec importSpec) String() string {
if spec.name == "" {
return strconv.Quote(spec.path)
}
return spec.name + " " + strconv.Quote(spec.path)
}

336
vendor/zombiezen.com/go/capnproto2/capnpc-go/nodes.go generated vendored Normal file
View File

@@ -0,0 +1,336 @@
package main
import (
"errors"
"fmt"
"strings"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/schema"
)
type node struct {
schema.Node
pkg string
imp string
nodes []*node // only for file nodes
Name string
}
func (n *node) codeOrderFields() []field {
fields, _ := n.StructNode().Fields()
numFields := fields.Len()
mbrs := make([]field, numFields)
for i := 0; i < numFields; i++ {
f := fields.At(i)
fann, _ := f.Annotations()
fname, _ := f.Name()
fname = parseAnnotations(fann).Rename(fname)
mbrs[f.CodeOrder()] = field{Field: f, Name: fname}
}
return mbrs
}
// DiscriminantOffset returns the byte offset of the struct union discriminant.
func (n *node) DiscriminantOffset() (uint32, error) {
if n == nil {
return 0, errors.New("discriminant offset called on nil node")
}
if n.Which() != schema.Node_Which_structNode {
return 0, fmt.Errorf("discriminant offset called on %v node", n.Which())
}
return n.StructNode().DiscriminantOffset() * 2, nil
}
func (n *node) shortDisplayName() string {
dn, _ := n.DisplayName()
return dn[n.DisplayNamePrefixLength():]
}
// String returns the node's display name.
func (n *node) String() string {
return displayName(n)
}
func displayName(n interface {
DisplayName() (string, error)
}) string {
dn, _ := n.DisplayName()
return dn
}
type field struct {
schema.Field
Name string
}
// HasDiscriminant reports whether the field is in a union.
func (f field) HasDiscriminant() bool {
return f.DiscriminantValue() != schema.Field_noDiscriminant
}
type enumval struct {
schema.Enumerant
Name string
Val int
Tag string
parent *node
}
func makeEnumval(enum *node, i int, e schema.Enumerant) enumval {
eann, _ := e.Annotations()
ann := parseAnnotations(eann)
name, _ := e.Name()
name = ann.Rename(name)
t := ann.Tag(name)
return enumval{e, name, i, t, enum}
}
func (e *enumval) FullName() string {
return e.parent.Name + "_" + e.Name
}
type interfaceMethod struct {
schema.Method
Interface *node
ID int
Name string
OriginalName string
Params *node
Results *node
}
func methodSet(methods []interfaceMethod, n *node, nodes nodeMap) ([]interfaceMethod, error) {
ms, _ := n.Interface().Methods()
for i := 0; i < ms.Len(); i++ {
m := ms.At(i)
mname, _ := m.Name()
mann, _ := m.Annotations()
pn, err := nodes.mustFind(m.ParamStructType())
if err != nil {
return methods, fmt.Errorf("could not find param type for %s.%s", n.shortDisplayName(), mname)
}
rn, err := nodes.mustFind(m.ResultStructType())
if err != nil {
return methods, fmt.Errorf("could not find result type for %s.%s", n.shortDisplayName(), mname)
}
methods = append(methods, interfaceMethod{
Method: m,
Interface: n,
ID: i,
OriginalName: mname,
Name: parseAnnotations(mann).Rename(mname),
Params: pn,
Results: rn,
})
}
// TODO(light): sort added methods by code order
supers, _ := n.Interface().Superclasses()
for i := 0; i < supers.Len(); i++ {
s := supers.At(i)
sn, err := nodes.mustFind(s.Id())
if err != nil {
return methods, fmt.Errorf("could not find superclass %#x of %s", s.Id(), n)
}
methods, err = methodSet(methods, sn, nodes)
if err != nil {
return methods, err
}
}
return methods, nil
}
// Tag types
const (
defaultTag = iota
noTag
customTag
)
type annotations struct {
Doc string
Package string
Import string
TagType int
CustomTag string
Name string
}
func parseAnnotations(list schema.Annotation_List) *annotations {
ann := new(annotations)
for i, n := 0, list.Len(); i < n; i++ {
a := list.At(i)
val, _ := a.Value()
text, _ := val.Text()
switch a.Id() {
case capnp.Doc:
ann.Doc = text
case capnp.Package:
ann.Package = text
case capnp.Import:
ann.Import = text
case capnp.Tag:
ann.TagType = customTag
ann.CustomTag = text
case capnp.Notag:
ann.TagType = noTag
case capnp.Name:
ann.Name = text
}
}
return ann
}
// Tag returns the string value that an enumerant value called name should have.
// An empty string indicates that this enumerant value has no tag.
func (ann *annotations) Tag(name string) string {
switch ann.TagType {
case noTag:
return ""
case customTag:
return ann.CustomTag
case defaultTag:
fallthrough
default:
return name
}
}
// Rename returns the overridden name from the annotations or the given name
// if no annotation was found.
func (ann *annotations) Rename(given string) string {
if ann.Name == "" {
return given
}
return ann.Name
}
type nodeMap map[uint64]*node
func buildNodeMap(req schema.CodeGeneratorRequest) (nodeMap, error) {
rnodes, err := req.Nodes()
if err != nil {
return nil, err
}
nodes := make(nodeMap, rnodes.Len())
var allfiles []*node
for i := 0; i < rnodes.Len(); i++ {
ni := rnodes.At(i)
n := &node{Node: ni}
nodes[n.Id()] = n
if n.Which() == schema.Node_Which_file {
allfiles = append(allfiles, n)
}
}
for _, f := range allfiles {
fann, err := f.Annotations()
if err != nil {
return nil, fmt.Errorf("reading annotations for %v: %v", f, err)
}
ann := parseAnnotations(fann)
f.pkg = ann.Package
f.imp = ann.Import
nnodes, _ := f.NestedNodes()
for i := 0; i < nnodes.Len(); i++ {
nn := nnodes.At(i)
if ni := nodes[nn.Id()]; ni != nil {
nname, _ := nn.Name()
if err := resolveName(nodes, ni, "", nname, f); err != nil {
return nil, err
}
}
}
}
return nodes, nil
}
// resolveName is called as part of building up a node map to populate the name field of n.
func resolveName(nodes nodeMap, n *node, base, name string, file *node) error {
na, err := n.Annotations()
if err != nil {
return fmt.Errorf("reading annotations for %s: %v", n, err)
}
name = parseAnnotations(na).Rename(name)
if base == "" {
n.Name = strings.Title(name)
} else {
n.Name = base + "_" + name
}
n.pkg = file.pkg
n.imp = file.imp
file.nodes = append(file.nodes, n)
nnodes, err := n.NestedNodes()
if err != nil {
return fmt.Errorf("listing nested nodes for %s: %v", n, err)
}
for i := 0; i < nnodes.Len(); i++ {
nn := nnodes.At(i)
ni := nodes[nn.Id()]
if ni == nil {
continue
}
nname, err := nn.Name()
if err != nil {
return fmt.Errorf("reading name of nested node %d in %s: %v", i+1, n, err)
}
if err := resolveName(nodes, ni, n.Name, nname, file); err != nil {
return err
}
}
switch n.Which() {
case schema.Node_Which_structNode:
fields, _ := n.StructNode().Fields()
for i := 0; i < fields.Len(); i++ {
f := fields.At(i)
if f.Which() != schema.Field_Which_group {
continue
}
fa, _ := f.Annotations()
fname, _ := f.Name()
grp := nodes[f.Group().TypeId()]
if grp == nil {
return fmt.Errorf("could not find type information for group %s in %s", fname, n)
}
fname = parseAnnotations(fa).Rename(fname)
if err := resolveName(nodes, grp, n.Name, fname, file); err != nil {
return err
}
}
case schema.Node_Which_interface:
m, _ := n.Interface().Methods()
methodResolve := func(id uint64, mname string, base string, name string) error {
x := nodes[id]
if x == nil {
return fmt.Errorf("could not find type %#x for %s.%s", id, n, mname)
}
if x.ScopeId() != 0 {
return nil
}
return resolveName(nodes, x, base, name, file)
}
for i := 0; i < m.Len(); i++ {
mm := m.At(i)
mname, _ := mm.Name()
mann, _ := mm.Annotations()
base := n.Name + "_" + parseAnnotations(mann).Rename(mname)
if err := methodResolve(mm.ParamStructType(), mname, base, "Params"); err != nil {
return err
}
if err := methodResolve(mm.ResultStructType(), mname, base, "Results"); err != nil {
return err
}
}
}
return nil
}
func (nm nodeMap) mustFind(id uint64) (*node, error) {
n := nm[id]
if n == nil {
return nil, fmt.Errorf("could not find node %#x in schema", id)
}
return n, nil
}

View File

@@ -0,0 +1,236 @@
package main
import (
"bytes"
"fmt"
)
type annotationParams struct {
G *generator
Node *node
}
type constantsParams struct {
G *generator
Consts []*node
Vars []*node
}
type enumParams struct {
G *generator
Node *node
Annotations *annotations
EnumValues []enumval
}
type structTypesParams struct {
G *generator
Node *node
Annotations *annotations
BaseNode *node
}
func (p structTypesParams) IsBase() bool {
return p.Node == p.BaseNode
}
type baseStructFuncsParams struct {
G *generator
Node *node
StringMethod bool
}
type structFuncsParams struct {
G *generator
Node *node
}
type structGroupParams struct {
G *generator
Node *node
Group *node
Field field
}
type structFieldParams struct {
G *generator
Node *node
Field field
Annotations *annotations
FieldType string
}
type (
structFloatFieldParams structUintFieldParams
structInterfaceFieldParams structFieldParams
structVoidFieldParams structFieldParams
structListFieldParams structObjectFieldParams
structPointerFieldParams structObjectFieldParams
structStructFieldParams structObjectFieldParams
)
type structBoolFieldParams struct {
structFieldParams
Default bool
}
type structUintFieldParams struct {
structFieldParams
Bits uint
Default uint64
}
func (p structUintFieldParams) Offset() uint32 {
return p.Field.Slot().Offset() * uint32(p.Bits/8)
}
func (p structFloatFieldParams) Offset() uint32 {
return structUintFieldParams(p).Offset()
}
type structIntFieldParams struct {
structUintFieldParams
EnumName string
}
func (p structIntFieldParams) ReturnType() string {
if p.EnumName != "" {
return p.EnumName
}
return fmt.Sprintf("int%d", p.Bits)
}
type structTextFieldParams struct {
structFieldParams
Default string
}
type structDataFieldParams struct {
structFieldParams
Default []byte
}
type structObjectFieldParams struct {
structFieldParams
TypeNode *node
Default staticDataRef
}
type structListParams struct {
G *generator
Node *node
StringMethod bool
}
type structEnumsParams struct {
G *generator
Node *node
Fields []field
EnumString enumString
}
type promiseParams struct {
G *generator
Node *node
Fields []field
}
type promiseGroupParams struct {
G *generator
Node *node
Field field
Group *node
}
type promiseFieldStructParams struct {
G *generator
Node *node
Field field
Struct *node
Default staticDataRef
}
type promiseFieldAnyPointerParams struct {
G *generator
Node *node
Field field
}
type promiseFieldInterfaceParams struct {
G *generator
Node *node
Field field
Interface *node
}
type interfaceClientParams struct {
G *generator
Node *node
Annotations *annotations
Methods []interfaceMethod
}
type interfaceServerParams struct {
G *generator
Node *node
Annotations *annotations
Methods []interfaceMethod
}
type structValueParams struct {
G *generator
Node *node
Typ *node
Value staticDataRef
}
type pointerValueParams struct {
G *generator
Value staticDataRef
}
type listValueParams struct {
G *generator
Typ string
Value staticDataRef
}
type schemaVarParams struct {
G *generator
FileID uint64
NodeIDs []uint64
schema []byte
}
func (p schemaVarParams) SchemaLiteral() string {
const width = 16
var out bytes.Buffer
out.WriteByte('"')
for i, b := range p.schema {
if i > 0 && i%width == 0 {
out.WriteString("\" +\n\t\"")
}
switch {
case b < ' ' || b > '~':
// unprintable
out.WriteString("\\x")
out.WriteByte(hexdigit(b >> 4))
out.WriteByte(hexdigit(b & 0xf))
case b == '"':
out.WriteString("\\\"")
case b == '\\':
out.WriteString("\\\\")
default:
out.WriteByte(b)
}
}
out.WriteByte('"')
return out.String()
}
func hexdigit(b byte) byte {
if b < 10 {
return b + '0'
}
return (b - 10) + 'a'
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
{{if .Field.HasDiscriminant -}}
if s.Struct.Uint16({{.Node.DiscriminantOffset}}) != {{.Field.DiscriminantValue}} {
panic({{printf "Which() != %s" .Field.Name | printf "%q"}})
}
{{end -}}

View File

@@ -0,0 +1,9 @@
func (s {{.Node.Name}}) Has{{.Field.Name|title}}() bool {
{{if .Field.HasDiscriminant -}}
if s.Struct.Uint16({{.Node.DiscriminantOffset}}) != {{.Field.DiscriminantValue}} {
return false
}
{{end -}}
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
return p.IsValid() || err != nil
}

View File

@@ -0,0 +1,4 @@
InterfaceID: {{.Interface.Id|printf "%#x"}},
MethodID: {{.ID}},
InterfaceName: {{.Interface.DisplayName|printf "%q"}},
MethodName: {{.OriginalName|printf "%q"}},

View File

@@ -0,0 +1,3 @@
{{if .Field.HasDiscriminant -}}
s.Struct.SetUint16({{.Node.DiscriminantOffset}}, {{.Field.DiscriminantValue}})
{{end -}}

View File

@@ -0,0 +1,2 @@
// {{.Name}}_TypeID is the unique identifier for the type {{.Name}}.
const {{.Name}}_TypeID = {{.Id|printf "%#x"}}

View File

@@ -0,0 +1 @@
const {{.Node.Name}} = uint64({{.Node.Id|printf "%#x"}})

View File

@@ -0,0 +1,23 @@
{{ template "_typeid" .Node }}
func New{{.Node.Name}}(s *{{.G.Capnp}}.Segment) ({{.Node.Name}}, error) {
st, err := {{$.G.Capnp}}.NewStruct(s, {{.G.ObjectSize .Node}})
return {{.Node.Name}}{st}, err
}
func NewRoot{{.Node.Name}}(s *{{.G.Capnp}}.Segment) ({{.Node.Name}}, error) {
st, err := {{.G.Capnp}}.NewRootStruct(s, {{.G.ObjectSize .Node}})
return {{.Node.Name}}{st}, err
}
func ReadRoot{{.Node.Name}}(msg *{{.G.Capnp}}.Message) ({{.Node.Name}}, error) {
root, err := msg.RootPtr()
return {{.Node.Name}}{root.Struct()}, err
}
{{if .StringMethod}}
func (s {{.Node.Name}}) String() string {
str, _ := {{.G.Imports.Text}}.Marshal({{.Node.Id|printf "%#x"}}, s.Struct)
return str
}
{{end}}

View File

@@ -0,0 +1,14 @@
{{with .Consts -}}
// Constants defined in {{$.G.Basename}}.
const (
{{range .}} {{.Name}} = {{$.G.Value . .Const.Type .Const.Value}}
{{end}}
)
{{end}}
{{with .Vars -}}
// Constants defined in {{$.G.Basename}}.
var (
{{range .}} {{.Name}} = {{$.G.Value . .Const.Type .Const.Value}}
{{end}}
)
{{end}}

View File

@@ -0,0 +1,55 @@
{{with .Annotations.Doc -}}
// {{.}}
{{end -}}
type {{.Node.Name}} uint16
{{ template "_typeid" .Node }}
{{with .EnumValues -}}
// Values of {{$.Node.Name}}.
const (
{{range . -}}
{{.FullName}} {{$.Node.Name}} = {{.Val}}
{{end}}
)
// String returns the enum's constant name.
func (c {{$.Node.Name}}) String() string {
switch c {
{{range . -}}
{{if .Tag}}case {{.FullName}}: return {{printf "%q" .Tag}}
{{end}}
{{- end}}
default: return ""
}
}
// {{$.Node.Name}}FromString returns the enum value with a name,
// or the zero value if there's no such value.
func {{$.Node.Name}}FromString(c string) {{$.Node.Name}} {
switch c {
{{range . -}}
{{if .Tag}}case {{printf "%q" .Tag}}: return {{.FullName}}
{{end}}
{{- end}}
default: return 0
}
}
{{end}}
type {{.Node.Name}}_List struct { {{$.G.Capnp}}.List }
func New{{.Node.Name}}_List(s *{{$.G.Capnp}}.Segment, sz int32) ({{.Node.Name}}_List, error) {
l, err := {{.G.Capnp}}.NewUInt16List(s, sz)
return {{.Node.Name}}_List{l.List}, err
}
func (l {{.Node.Name}}_List) At(i int) {{.Node.Name}} {
ul := {{.G.Capnp}}.UInt16List{List: l.List}
return {{.Node.Name}}(ul.At(i))
}
func (l {{.Node.Name}}_List) Set(i int, v {{.Node.Name}}) {
ul := {{.G.Capnp}}.UInt16List{List: l.List}
ul.Set(i, uint16(v))
}

View File

@@ -0,0 +1,26 @@
{{with .Annotations.Doc -}}
// {{.}}
{{end -}}
type {{.Node.Name}} struct { Client {{.G.Capnp}}.Client }
{{ template "_typeid" .Node }}
{{range .Methods -}}
func (c {{$.Node.Name}}) {{.Name|title}}(ctx {{$.G.Imports.Context}}.Context, params func({{$.G.RemoteNodeName .Params $.Node}}) error, opts ...{{$.G.Capnp}}.CallOption) {{$.G.RemoteNodeName .Results $.Node}}_Promise {
if c.Client == nil {
return {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline({{$.G.Capnp}}.ErrorAnswer({{$.G.Capnp}}.ErrNullClient))}
}
call := &{{$.G.Capnp}}.Call{
Ctx: ctx,
Method: {{$.G.Capnp}}.Method{
{{template "_interfaceMethod" .}}
},
Options: {{$.G.Capnp}}.NewCallOptions(opts),
}
if params != nil {
call.ParamsSize = {{$.G.ObjectSize .Params}}
call.ParamsFunc = func(s {{$.G.Capnp}}.Struct) error { return params({{$.G.RemoteNodeName .Params $.Node}}{Struct: s}) }
}
return {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline(c.Client.Call(call))}
}
{{end}}

View File

@@ -0,0 +1,40 @@
type {{.Node.Name}}_Server interface {
{{range .Methods}}
{{.Name|title}}({{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}) error
{{end}}
}
func {{.Node.Name}}_ServerToClient(s {{.Node.Name}}_Server) {{.Node.Name}} {
c, _ := s.({{.G.Imports.Server}}.Closer)
return {{.Node.Name}}{Client: {{.G.Imports.Server}}.New({{.Node.Name}}_Methods(nil, s), c)}
}
func {{.Node.Name}}_Methods(methods []{{.G.Imports.Server}}.Method, s {{.Node.Name}}_Server) []{{.G.Imports.Server}}.Method {
if cap(methods) == 0 {
methods = make([]{{.G.Imports.Server}}.Method, 0, {{len .Methods}})
}
{{range .Methods}}
methods = append(methods, {{$.G.Imports.Server}}.Method{
Method: {{$.G.Capnp}}.Method{
{{template "_interfaceMethod" .}}
},
Impl: func(c {{$.G.Imports.Context}}.Context, opts {{$.G.Capnp}}.CallOptions, p, r {{$.G.Capnp}}.Struct) error {
call := {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}{c, opts, {{$.G.RemoteNodeName .Params $.Node}}{Struct: p}, {{$.G.RemoteNodeName .Results $.Node}}{Struct: r} }
return s.{{.Name|title}}(call)
},
ResultsSize: {{$.G.ObjectSize .Results}},
})
{{end}}
return methods
}
{{range .Methods -}}
{{if eq .Interface.Id $.Node.Id}}
// {{$.Node.Name}}_{{.Name}} holds the arguments for a server call to {{$.Node.Name}}.{{.Name}}.
type {{$.Node.Name}}_{{.Name}} struct {
Ctx {{$.G.Imports.Context}}.Context
Options {{$.G.Capnp}}.CallOptions
Params {{$.G.RemoteNodeName .Params $.Node}}
Results {{$.G.RemoteNodeName .Results $.Node}}
}
{{end}}
{{- end}}

View File

@@ -0,0 +1,2 @@
{{.Typ}}{List: {{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}).List()}
{{- /* no EOL */ -}}

View File

@@ -0,0 +1,2 @@
{{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}})
{{- /* no EOL */ -}}

View File

@@ -0,0 +1,8 @@
// {{.Node.Name}}_Promise is a wrapper for a {{.Node.Name}} promised by a client call.
type {{.Node.Name}}_Promise struct { *{{.G.Capnp}}.Pipeline }
func (p {{.Node.Name}}_Promise) Struct() ({{.Node.Name}}, error) {
s, err := p.Pipeline.Struct()
return {{.Node.Name}}{s}, err
}

View File

@@ -0,0 +1,4 @@
func (p {{.Node.Name}}_Promise) {{.Field.Name|title}}() *{{.G.Capnp}}.Pipeline {
return p.Pipeline.GetPipeline({{.Field.Slot.Offset}})
}

View File

@@ -0,0 +1,4 @@
func (p {{.Node.Name}}_Promise) {{.Field.Name|title}}() {{.G.RemoteNodeName .Interface .Node}} {
return {{.G.RemoteNodeName .Interface .Node}}{Client: p.Pipeline.GetPipeline({{.Field.Slot.Offset}}).Client()}
}

View File

@@ -0,0 +1,4 @@
func (p {{.Node.Name}}_Promise) {{.Field.Name|title}}() {{.G.RemoteNodeName .Struct .Node}}_Promise {
return {{.G.RemoteNodeName .Struct .Node}}_Promise{Pipeline: p.Pipeline.{{if .Default.IsValid}}GetPipelineDefault({{.Field.Slot.Offset}}, {{.Default}}){{else}}GetPipeline({{.Field.Slot.Offset}}){{end}} }
}

View File

@@ -0,0 +1 @@
func (p {{.Node.Name}}_Promise) {{.Field.Name|title}}() {{.Group.Name}}_Promise { return {{.Group.Name}}_Promise{p.Pipeline} }

View File

@@ -0,0 +1,8 @@
const schema_{{.FileID|printf "%x"}} = {{.SchemaLiteral}}
func init() {
{{.G.Imports.Schemas}}.Register(schema_{{.FileID|printf "%x"}},
{{- range .NodeIDs}}
{{.|printf "%#x"}},
{{- end}})
}

View File

@@ -0,0 +1,10 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() bool {
{{template "_checktag" . -}}
return {{if .Default}}!{{end}}s.Struct.Bit({{.Field.Slot.Offset}})
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v bool) {
{{template "_settag" . -}}
s.Struct.SetBit({{.Field.Slot.Offset}}, {{if .Default}}!{{end}}v)
}

View File

@@ -0,0 +1,22 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.FieldType}}, error) {
{{template "_checktag" . -}}
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
{{with .Default -}}
return {{$.FieldType}}(p.DataDefault({{printf "%#v" .}})), err
{{- else -}}
return {{.FieldType}}(p.Data()), err
{{- end}}
}
{{template "_hasfield" .}}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error {
{{template "_settag" . -}}
{{if .Default -}}
if v == nil {
v = []byte{}
}
{{end -}}
return s.Struct.SetData({{.Field.Slot.Offset}}, v)
}

View File

@@ -0,0 +1,17 @@
type {{.Node.Name}}_Which uint16
const (
{{range .Fields}} {{$.Node.Name}}_Which_{{.Name}} {{$.Node.Name}}_Which = {{.DiscriminantValue}}
{{end}}
)
func (w {{.Node.Name}}_Which) String() string {
const s = {{.EnumString.ValueString|printf "%q"}}
switch w {
{{range $i, $f := .Fields}}case {{$.Node.Name}}_Which_{{.Name}}:
return s{{$.EnumString.SliceFor $i}}
{{end}}
}
return "{{.Node.Name}}_Which(" + {{.G.Imports.Strconv}}.FormatUint(uint64(w), 10) + ")"
}

View File

@@ -0,0 +1,10 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() float{{.Bits}} {
{{template "_checktag" . -}}
return {{.G.Imports.Math}}.Float{{.Bits}}frombits(s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{printf "%#x" .}}{{end}})
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v float{{.Bits}}) {
{{template "_settag" . -}}
s.Struct.SetUint{{.Bits}}({{.Offset}}, {{.G.Imports.Math}}.Float{{.Bits}}bits(v){{with .Default}}^{{printf "%#x" .}}{{end}})
}

View File

@@ -0,0 +1,5 @@
{{if gt .Node.StructNode.DiscriminantCount 0}}
func (s {{.Node.Name}}) Which() {{.Node.Name}}_Which {
return {{.Node.Name}}_Which(s.Struct.Uint16({{.Node.DiscriminantOffset}}))
}
{{end -}}

View File

@@ -0,0 +1,4 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.Group.Name}} { return {{.Group.Name}}(s) }
{{if .Field.HasDiscriminant}}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}() { {{template "_settag" .}} }
{{end}}

View File

@@ -0,0 +1,10 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.ReturnType}} {
{{template "_checktag" . -}}
return {{.ReturnType}}(s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}})
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.ReturnType}}) {
{{template "_settag" . -}}
s.Struct.SetUint{{.Bits}}({{.Offset}}, uint{{.Bits}}(v){{with .Default}}^{{.}}{{end}})
}

View File

@@ -0,0 +1,18 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() {{.FieldType}} {
{{template "_checktag" . -}}
p, _ := s.Struct.Ptr({{.Field.Slot.Offset}})
return {{.FieldType}}{Client: p.Interface().Client()}
}
{{template "_hasfield" .}}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error {
{{template "_settag" . -}}
if v.Client == nil {
return s.Struct.SetPtr({{.Field.Slot.Offset}}, capnp.Ptr{})
}
seg := s.Segment()
in := {{.G.Capnp}}.NewInterface(seg, seg.Message().AddCap(v.Client))
return s.Struct.SetPtr({{.Field.Slot.Offset}}, in.ToPtr())
}

View File

@@ -0,0 +1,19 @@
// {{.Node.Name}}_List is a list of {{.Node.Name}}.
type {{.Node.Name}}_List struct{ {{.G.Capnp}}.List }
// New{{.Node.Name}} creates a new list of {{.Node.Name}}.
func New{{.Node.Name}}_List(s *{{.G.Capnp}}.Segment, sz int32) ({{.Node.Name}}_List, error) {
l, err := {{.G.Capnp}}.NewCompositeList(s, {{.G.ObjectSize .Node}}, sz)
return {{.Node.Name}}_List{l}, err
}
func (s {{.Node.Name}}_List) At(i int) {{.Node.Name}} { return {{.Node.Name}}{ s.List.Struct(i) } }
func (s {{.Node.Name}}_List) Set(i int, v {{.Node.Name}}) error { return s.List.SetStruct(i, v.Struct) }
{{if .StringMethod}}
func (s {{.Node.Name}}_List) String() string {
str, _ := {{.G.Imports.Text}}.MarshalList({{.Node.Id|printf "%#x"}}, s.List)
return str
}
{{end}}

View File

@@ -0,0 +1,33 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.FieldType}}, error) {
{{template "_checktag" . -}}
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
{{if .Default.IsValid -}}
if err != nil {
return {{.FieldType}}{}, err
}
l, err := p.ListDefault({{.Default}})
return {{.FieldType}}{List: l}, err
{{- else -}}
return {{.FieldType}}{List: p.List()}, err
{{- end}}
}
{{template "_hasfield" .}}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error {
{{template "_settag" . -}}
return s.Struct.SetPtr({{.Field.Slot.Offset}}, v.List.ToPtr())
}
// New{{.Field.Name|title}} sets the {{.Field.Name}} field to a newly
// allocated {{.FieldType}}, preferring placement in s's segment.
func (s {{.Node.Name}}) New{{.Field.Name|title}}(n int32) ({{.FieldType}}, error) {
{{template "_settag" . -}}
l, err := {{.G.RemoteTypeNew .Field.Slot.Type .Node}}(s.Struct.Segment(), n)
if err != nil {
return {{.FieldType}}{}, err
}
err = s.Struct.SetPtr({{.Field.Slot.Offset}}, l.List.ToPtr())
return l, err
}

View File

@@ -0,0 +1,37 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.G.Capnp}}.Pointer, error) {
{{template "_checktag" . -}}
{{if .Default.IsValid -}}
p, err := s.Struct.Pointer({{.Field.Slot.Offset}})
if err != nil {
return nil, err
}
return {{.G.Capnp}}.PointerDefault(p, {{.Default}})
{{- else -}}
return s.Struct.Pointer({{.Field.Slot.Offset}})
{{- end}}
}
{{template "_hasfield" .}}
func (s {{.Node.Name}}) {{.Field.Name|title}}Ptr() ({{.G.Capnp}}.Ptr, error) {
{{if .Default.IsValid -}}
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
if err != nil {
return nil, err
}
return p.Default({{.Default}})
{{- else -}}
return s.Struct.Ptr({{.Field.Slot.Offset}})
{{- end}}
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.G.Capnp}}.Pointer) error {
{{template "_settag" . -}}
return s.Struct.SetPointer({{.Field.Slot.Offset}}, v)
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}Ptr(v {{.G.Capnp}}.Ptr) error {
{{template "_settag" . -}}
return s.Struct.SetPtr({{.Field.Slot.Offset}}, v)
}

View File

@@ -0,0 +1,33 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() ({{.FieldType}}, error) {
{{template "_checktag" . -}}
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
{{if .Default.IsValid -}}
if err != nil {
return {{.FieldType}}{}, err
}
ss, err := p.StructDefault({{.Default}})
return {{.FieldType}}{Struct: ss}, err
{{- else -}}
return {{.FieldType}}{Struct: p.Struct()}, err
{{- end}}
}
{{template "_hasfield" .}}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v {{.FieldType}}) error {
{{template "_settag" . -}}
return s.Struct.SetPtr({{.Field.Slot.Offset}}, v.Struct.ToPtr())
}
// New{{.Field.Name|title}} sets the {{.Field.Name}} field to a newly
// allocated {{.FieldType}} struct, preferring placement in s's segment.
func (s {{.Node.Name}}) New{{.Field.Name|title}}() ({{.FieldType}}, error) {
{{template "_settag" . -}}
ss, err := {{.G.RemoteNodeNew .TypeNode .Node}}(s.Struct.Segment())
if err != nil {
return {{.FieldType}}{}, err
}
err = s.Struct.SetPtr({{.Field.Slot.Offset}}, ss.Struct.ToPtr())
return ss, err
}

View File

@@ -0,0 +1,30 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() (string, error) {
{{template "_checktag" . -}}
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
{{with .Default -}}
return p.TextDefault({{printf "%q" .}}), err
{{- else -}}
return p.Text(), err
{{- end}}
}
{{template "_hasfield" .}}
func (s {{.Node.Name}}) {{.Field.Name|title}}Bytes() ([]byte, error) {
p, err := s.Struct.Ptr({{.Field.Slot.Offset}})
{{with .Default -}}
return p.TextBytesDefault({{printf "%q" .}}), err
{{- else -}}
return p.TextBytes(), err
{{- end}}
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v string) error {
{{template "_settag" . -}}
{{if .Default -}}
return s.Struct.SetNewText({{.Field.Slot.Offset}}, v)
{{- else -}}
return s.Struct.SetText({{.Field.Slot.Offset}}, v)
{{- end}}
}

View File

@@ -0,0 +1,8 @@
{{with .Annotations.Doc -}}
// {{.}}
{{end -}}
type {{.Node.Name}} {{if .IsBase -}}
struct{ {{.G.Capnp}}.Struct }
{{- else -}}
{{.BaseNode.Name}}
{{- end}}

View File

@@ -0,0 +1,10 @@
func (s {{.Node.Name}}) {{.Field.Name|title}}() uint{{.Bits}} {
{{template "_checktag" . -}}
return s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}}
}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}(v uint{{.Bits}}) {
{{template "_settag" . -}}
s.Struct.SetUint{{.Bits}}({{.Offset}}, v{{with .Default}}^{{.}}{{end}})
}

View File

@@ -0,0 +1,2 @@
{{.G.RemoteNodeName .Typ .Node}}{Struct: {{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}).Struct()}
{{- /* no EOL */ -}}

View File

@@ -0,0 +1,6 @@
{{if .Field.HasDiscriminant -}}
func (s {{.Node.Name}}) Set{{.Field.Name|title}}() {
{{template "_settag" .}}
}
{{end -}}

Binary file not shown.

View File

@@ -0,0 +1,13 @@
# Generate scopes.capnp.out with:
# capnp compile -o- scopes.capnp > scopes.capnp.out
# Must run inside this directory to preserve paths.
using Go = import "go.capnp";
@0xc260cb50ae622e10;
$Go.package("const");
$Go.import("zombiezen.com/go/capnproto2/capnpc-go/testdata/const");
const answer @0xda96e2255811b258 :Int64 = 42;
const blob @0xe0a385c7be1fea4d :Data = "\x01\x02\x03";

Binary file not shown.

View File

@@ -0,0 +1,15 @@
# Generate go.capnp.out with:
# capnp compile -o- go.capnp > go.capnp.out
# Must run inside this directory to preserve paths.
@0xd12a1c51fedd6c88;
annotation package(file) :Text;
annotation import(file) :Text;
annotation doc(struct, field, enum) :Text;
annotation tag(enumerant) :Text;
annotation notag(enumerant) :Void;
annotation customtype(field) :Text;
annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text;
$package("capnp");

Binary file not shown.

View File

@@ -0,0 +1,11 @@
using Go = import "go.capnp";
@0x83c2b5818e83ab19;
$Go.package("template_fix");
$Go.import("zombiezen.com/go/capnproto2/capnpc-go/testdata/group");
struct SomeMisguidedStruct {
someGroup :group {
someGroupField @0 :UInt64;
}
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
# File to be imported from scopes.capnp.
using Go = import "go.capnp";
@0x9c339b0568fe60ba;
$Go.package("otherscopes");
$Go.import("zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes");
struct Foo @0xd127518fcfe6191d {
}

Binary file not shown.

View File

@@ -0,0 +1,20 @@
# Generate scopes.capnp.out with:
# capnp compile -o- scopes.capnp > scopes.capnp.out
# Must run inside this directory to preserve paths.
using Go = import "go.capnp";
using Other = import "otherscopes.capnp";
@0xd68755941d99d05e;
$Go.package("scopes");
$Go.import("zombiezen.com/go/capnproto2/capnpc-go/testdata/scopes");
struct Foo @0xc8d7b3b4e07f8bd9 {
}
const fooVar @0x84efedc75e99768d :Foo = ();
const otherFooVar @0x836faf1834d91729 :Other.Foo = ();
const fooListVar @0xcda2680ec5c921e0 :List(Foo) = [];
const otherFooListVar @0x83e7e1b3cd1be338 :List(Other.Foo) = [];
const intList @0xacf3d9917d0bb0f0 :List(Int32) = [];

Binary file not shown.

View File

@@ -0,0 +1,173 @@
# Sandstorm - Personal Cloud Sandbox
# Copyright (c) 2014 Sandstorm Development Group, Inc. and contributors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Derived from sandstorm at 08fafe55995edfe188777eb00b5a2d1826032c59.
# Using Go annotations.
@0xecd50d792c3d9992;
using Go = import "go.capnp";
$Go.package("util");
$Go.import("zombiezen.com/go/capnproto2/capnpc-go/testdata/util");
using DateInNs = Int64;
using DurationInNs = UInt64;
struct KeyValue {
key @0 :Text;
value @1 :Text;
}
struct LocalizedText {
# Text intended to be displayed to a user. May be localized to multiple languages.
#
# TODO(soon): Maybe instead of packing all translations in here, we should have a message code
# and parameter substitutions, with the (message code, locale) -> text map stored elsewhere?
defaultText @0 :Text;
# What to display if no localization matching the user's preferences is available.
localizations @1 :List(Localization);
# Localized versions of the text.
struct Localization {
locale @0 :Text; # IETF BCP 47 locale, e.g. "en" or "en-US".
text @1 :Text; # Localized text.
}
}
interface Handle {
# Arbitrary handle to some resource provided by the platform. May or may not be persistent,
# depending on the use case.
#
# To "drop" a handle means to discard any references. The purpose of a handle is to detect when
# it has been dropped and to free the underlying resource and cancel any ongoing operation at
# that time.
#
# A handle can be persistent. Once you have called `save()` on it to obtain a SturdyRef, dropping
# the live reference will not cancel the operation. You must drop all live references *and*
# explicitly drop any SturdyRef. Every interface which supports restoring SturdyRefs also
# has a corresponding `drop()` method for this purpose.
#
# Unfortunately, there is no way to ensure that a SturdyRef will eventually be deleted. A grain
# could, for example, call `save()` and then simply fail to store the SturdyRef anywhere, causing
# it to be "leaked" until such a time as the grain itself is destroyed. Or worse, a whole server
# could be destroyed in a fire, leaking all SturdyRefs stored therein forever. Apps implementing
# persistent handles must be designed to account for this, probably by giving the owning user
# a way to inspect incoming references and remove them manually. Sandstorm automatically provides
# such an interface for all apps it hosts.
}
interface ByteStream {
# Represents a destination for a stream of bytes. The bytes are ordered, but boundaries between
# messages are not semantically important.
#
# Streams are push-oriented (traditionally, "output streams") rather than pull-oriented ("input
# streams") because this most easily allows multiple packets to be in-flight at once while
# allowing flow control at either end. If we tried to design a pull-oriented stream, it would
# suffer from problems:
# * If we used a naive read() method that returns a simple data blob, you would need to make
# multiple simultaneous calls to deal with network latency. However, those calls could
# potentially return in the wrong order. Although you could restore order by keeping track of
# the order in which the calls were made, this would be a lot of work, and most callers would
# probably fail to do it.
# * We could instead have a read() method that returns a blob as well as a capability to read the
# next blob. You would then make multiple calls using pipelining. Even in this case, though,
# an unpredictable event loop could schedule a pipelined call's return before the parent call.
# Moreover, the interface would be awkward to use and implement. E.g. what happens if you call
# read() twice on the same capability?
write @0 (data :Data);
# Add bytes.
#
# It's safe to make overlapping calls to `write()`, since Cap'n Proto enforces E-Order and so
# the calls will be delivered in order. However, it is a good idea to limit how much data is
# in-flight at a time, so that it doesn't fill up buffers and block other traffic. On the other
# hand, having only one `write()` in flight at a time will not fully utilize the available
# bandwidth if the connection has any significant latency, so parallelizing a few `write()`s is
# a good idea.
#
# Similarly, the implementation of `ByteStream` can delay returning from `write()` as a way to
# hint to the caller that it should hold off on further writes.
done @1 ();
# Call after the last write to indicate that there is no more data. If the `ByteStream` is
# discarded without a call to `done()`, the callee must assume that an error occurred and that
# the data is incomplete.
#
# This will not return until all bytes are successfully written to their final destination.
# It will throw an exception if any error occurs, including if the total number of bytes written
# did not match `expectSize()`.
expectSize @2 (size :UInt64);
# Optionally called to let the receiver know exactly how much data will be written. This should
# normally be called before the first write(), but if called later, `size` indicates how many
# more bytes to expect _after_ the call. It is an error by the caller if more or fewer bytes are
# actually written before `done()`; this also implies that all calls to `expectSize()` must be
# consistent. The caller will ignore any exceptions thrown from this method, therefore it
# is not necessary for the callee to actually implement it.
}
interface Blob @0xe53527a75d90198f {
# Represents a large byte blob.
getSize @0 () -> (size :UInt64);
# Get the total size of the blob. May block if the blob is still being uploaded and the size is
# not yet known.
writeTo @1 (stream :ByteStream, startAtOffset :UInt64 = 0) -> (handle :Handle);
# Write the contents of the blob to `stream`.
getSlice @2 (offset :UInt64, size :UInt32) -> (data :Data);
# Read a slice of the blob starting at the given offset. `size` cannot be greater than Cap'n
# Proto's limit of 2^29-1, and reasonable servers will likely impose far lower limits. If the
# slice would cross past the end of the blob, it is truncated. Otherwise, `data` is always
# exactly `size` bytes (though the caller should check for security purposes).
#
# One technique that makes a lot of sense is to start off by calling e.g. `getSlice(0, 65536)`.
# If the returned data is less than 65536 bytes then you know you got the whole blob, otherwise
# you may want to switch to `writeTo`.
}
interface Assignable(T) {
# An "assignable" -- a mutable memory cell. Supports subscribing to updates.
get @0 () -> (value :T, setter :Setter);
# The returned setter functions the same as you'd get from `asSetter()` except that it will
# become disconnected the next time the Assignable is set by someone else. Thus, you may use this
# to implement optimistic concurrency control.
asGetter @1 () -> (getter :Getter);
# Return a read-only capability for this assignable, co-hosted with the assignable itself for
# performance. If the assignable is persistent, the getter is as well.
asSetter @2 () -> (setter :Setter);
# Return a write-only capability for this assignable, co-hosted with the assignable itself for
# performance. If the assignable is persistent, the setter is as well.
interface Getter {
get @0 () -> (value :T);
subscribe @1 (setter :Setter) -> (handle :Handle);
# Subscribe to updates. Calls the given setter any time the assignable's value changes. Drop
# the returned handle to stop receiving updates. If `setter` is persistent, `handle` will also
# be persistent.
}
interface Setter {
set @0 (value :T) -> ();
}
}

Binary file not shown.

384
vendor/zombiezen.com/go/capnproto2/doc.go generated vendored Normal file
View File

@@ -0,0 +1,384 @@
/*
Package capnp is a Cap'n Proto library for Go.
https://capnproto.org/
Read the Getting Started guide for a tutorial on how to use this
package. https://github.com/capnproto/go-capnproto2/wiki/Getting-Started
Generating code
capnpc-go provides the compiler backend for capnp.
# First, install capnpc-go to $PATH.
go install zombiezen.com/go/capnproto2/capnpc-go
# Then, generate Go files.
capnp compile -I$GOPATH/src/zombiezen.com/go/capnproto2/std -ogo *.capnp
capnpc-go requires two annotations for all files: package and import.
package is needed to know what package to place at the head of the
generated file and what identifier to use when referring to the type
from another package. import should be the fully qualified import path
and is used to generate import statement from other packages and to
detect when two types are in the same package. For example:
using Go = import "/go.capnp";
$Go.package("main");
$Go.import("zombiezen.com/go/capnproto2/example");
For adding documentation comments to the generated code, there's the doc
annotation. This annotation adds the comment to a struct, enum or field so
that godoc will pick it up. For example:
struct Zdate $Go.doc("Zdate represents a calendar date") {
year @0 :Int16;
month @1 :UInt8;
day @2 :UInt8 ;
}
Messages and Segments
In Cap'n Proto, the unit of communication is a message. A message
consists of one or more segments -- contiguous blocks of memory. This
allows large messages to be split up and loaded independently or lazily.
Typically you will use one segment per message. Logically, a message is
organized in a tree of objects, with the root always being a struct (as
opposed to a list or primitive). Messages can be read from and written
to a stream.
The Message and Segment types are the main types that application code
will use from this package. The Message type has methods for marshaling
and unmarshaling its segments to the wire format. If the application
needs to read or write from a stream, it should use the Encoder and
Decoder types.
Pointers
The type for a generic reference to a Cap'n Proto object is Ptr. A Ptr
can refer to a struct, a list, or an interface. Ptr, Struct, List, and
Interface (the pointer types) have value semantics and refer to data in
a single segment. All of the pointer types have a notion of "valid".
An invalid pointer will return the default value from any accessor and
panic when any setter is called.
In previous versions of this package, the Pointer interface was used
instead of the Ptr struct. This interface and functions that use it are
now deprecated. See https://github.com/capnproto/go-capnproto2/wiki/New-Ptr-Type
for details about this API change.
Data accessors and setters (i.e. struct primitive fields and list
elements) do not return errors, but pointer accessors and setters do.
There are a few reasons that a read or write of a pointer can fail, but
the most common are bad pointers or allocation failures. For accessors,
an invalid object will be returned in case of an error.
Since Go doesn't have generics, wrapper types provide type safety on
lists. This package provides lists of basic types, and capnpc-go
generates list wrappers for named types. However, if you need to use
deeper nesting of lists (e.g. List(List(UInt8))), you will need to use a
PointerList and wrap the elements.
Structs
For the following schema:
struct Foo @0x8423424e9b01c0af {
num @0 :UInt32;
bar @1 :Foo;
}
capnpc-go will generate:
// Foo is a pointer to a Foo struct in a segment.
// Member functions are provided to get/set members in the
// struct.
type Foo struct{ capnp.Struct }
// Foo_TypeID is the unique identifier for the type Foo.
// It remains the same across languages and schema changes.
const Foo_TypeID = 0x8423424e9b01c0af
// NewFoo creates a new orphaned Foo struct, preferring placement in
// s. If there isn't enough space, then another segment in the
// message will be used or allocated. You can set a field of type Foo
// to this new message, but usually you will want to use the
// NewBar()-style method shown below.
func NewFoo(s *capnp.Segment) (Foo, error)
// NewRootFoo creates a new Foo struct and sets the message's root to
// it.
func NewRootFoo(s *capnp.Segment) (Foo, error)
// ReadRootFoo reads the message's root pointer and converts it to a
// Foo struct.
func ReadRootFoo(msg *capnp.Message) (Foo, error)
// Num returns the value of the num field.
func (s Foo) Num() uint32
// SetNum sets the value of the num field to v.
func (s Foo) SetNum(v uint32)
// Bar returns the value of the bar field. This can return an error
// if the pointer goes beyond the segment's range, the segment fails
// to load, or the pointer recursion limit has been reached.
func (s Foo) Bar() (Foo, error)
// HasBar reports whether the bar field was initialized (non-null).
func (s Foo) HasBar() bool
// SetBar sets the value of the bar field to v.
func (s Foo) SetBar(v Foo) error
// NewBar sets the bar field to a newly allocated Foo struct,
// preferring placement in s's segment.
func (s Foo) NewBar() (Foo, error)
// Foo_List is a value with pointer semantics. It is created for all
// structs, and is used for List(Foo) in the capnp file.
type Foo_List struct{ capnp.List }
// NewFoo_List creates a new orphaned List(Foo), preferring placement
// in s. This can then be added to a message by using a Set function
// which takes a Foo_List. sz specifies the number of elements in the
// list. The list's size cannot be changed after creation.
func NewFoo_List(s *capnp.Segment, sz int32) Foo_List
// Len returns the number of elements in the list.
func (s Foo_List) Len() int
// At returns a pointer to the i'th element. If i is an invalid index,
// this will return an invalid Foo (all getters will return default
// values, setters will fail).
func (s Foo_List) At(i int) Foo
// Foo_Promise is a promise for a Foo. Methods are provided to get
// promises of struct and interface fields.
type Foo_Promise struct{ *capnp.Pipeline }
// Get waits until the promise is resolved and returns the result.
func (p Foo_Promise) Get() (Foo, error)
// Bar returns a promise for that bar field.
func (p Foo_Promise) Bar() Foo_Promise
Groups
For each group a typedef is created with a different method set for just the
groups fields:
struct Foo {
group :Group {
field @0 :Bool;
}
}
generates the following:
type Foo struct{ capnp.Struct }
type Foo_group Foo
func (s Foo) Group() Foo_group
func (s Foo_group) Field() bool
That way the following may be used to access a field in a group:
var f Foo
value := f.Group().Field()
Note that group accessors just convert the type and so have no overhead.
Unions
Named unions are treated as a group with an inner unnamed union. Unnamed
unions generate an enum Type_Which and a corresponding Which() function:
struct Foo {
union {
a @0 :Bool;
b @1 :Bool;
}
}
generates the following:
type Foo_Which uint16
const (
Foo_Which_a Foo_Which = 0
Foo_Which_b Foo_Which = 1
)
func (s Foo) A() bool
func (s Foo) B() bool
func (s Foo) SetA(v bool)
func (s Foo) SetB(v bool)
func (s Foo) Which() Foo_Which
Which() should be checked before using the getters, and the default case must
always be handled.
Setters for single values will set the union discriminator as well as set the
value.
For voids in unions, there is a void setter that just sets the discriminator.
For example:
struct Foo {
union {
a @0 :Void;
b @1 :Void;
}
}
generates the following:
func (s Foo) SetA() // Set that we are using A
func (s Foo) SetB() // Set that we are using B
Similarly, for groups in unions, there is a group setter that just sets
the discriminator. This must be called before the group getter can be
used to set values. For example:
struct Foo {
union {
a :group {
v :Bool
}
b :group {
v :Bool
}
}
}
and in usage:
f.SetA() // Set that we are using group A
f.A().SetV(true) // then we can use the group A getter to set the inner values
Enums
capnpc-go generates enum values as constants. For example in the capnp file:
enum ElementSize {
empty @0;
bit @1;
byte @2;
twoBytes @3;
fourBytes @4;
eightBytes @5;
pointer @6;
inlineComposite @7;
}
In the generated capnp.go file:
type ElementSize uint16
const (
ElementSize_empty ElementSize = 0
ElementSize_bit ElementSize = 1
ElementSize_byte ElementSize = 2
ElementSize_twoBytes ElementSize = 3
ElementSize_fourBytes ElementSize = 4
ElementSize_eightBytes ElementSize = 5
ElementSize_pointer ElementSize = 6
ElementSize_inlineComposite ElementSize = 7
)
In addition an enum.String() function is generated that will convert the constants to a string
for debugging or logging purposes. By default, the enum name is used as the tag value,
but the tags can be customized with a $Go.tag or $Go.notag annotation.
For example:
enum ElementSize {
empty @0 $Go.tag("void");
bit @1 $Go.tag("1 bit");
byte @2 $Go.tag("8 bits");
inlineComposite @7 $Go.notag;
}
In the generated go file:
func (c ElementSize) String() string {
switch c {
case ElementSize_empty:
return "void"
case ElementSize_bit:
return "1 bit"
case ElementSize_byte:
return "8 bits"
default:
return ""
}
}
Interfaces
capnpc-go generates type-safe Client wrappers for interfaces. For parameter
lists and result lists, structs are generated as described above with the names
Interface_method_Params and Interface_method_Results, unless a single struct
type is used. For example, for this interface:
interface Calculator {
evaluate @0 (expression :Expression) -> (value :Value);
}
capnpc-go generates the following Go code (along with the structs
Calculator_evaluate_Params and Calculator_evaluate_Results):
// Calculator is a client to a Calculator interface.
type Calculator struct{ Client capnp.Client }
// Evaluate calls `evaluate` on the client. params is called on a newly
// allocated Calculator_evaluate_Params struct to fill in the parameters.
func (c Calculator) Evaluate(
ctx context.Context,
params func(Calculator_evaluate_Params) error,
opts ...capnp.CallOption) *Calculator_evaluate_Results_Promise
capnpc-go also generates code to implement the interface:
// A Calculator_Server implements the Calculator interface.
type Calculator_Server interface {
Evaluate(Calculator_evaluate_Call) error
}
// Calculator_evaluate_Call holds the arguments for a Calculator.evaluate server call.
type Calculator_evaluate_Call struct {
Ctx context.Context
Options capnp.CallOptions
Params Calculator_evaluate_Params
Results Calculator_evaluate_Results
}
// Calculator_ServerToClient is equivalent to calling:
// NewCalculator(capnp.NewServer(Calculator_Methods(nil, s), s))
// If s does not implement the Close method, then nil is used.
func Calculator_ServerToClient(s Calculator_Server) Calculator
// Calculator_Methods appends methods from Calculator that call to server and
// returns the methods. If methods is nil or the capacity of the underlying
// slice is too small, a new slice is returned.
func Calculator_Methods(methods []server.Method, s Calculator_Server) []server.Method
Since a single capability may want to implement many interfaces, you can
use multiple *_Methods functions to build a single slice to send to
NewServer.
An example of combining the client/server code to communicate with a locally
implemented Calculator:
var srv Calculator_Server
calc := Calculator_ServerToClient(srv)
result := calc.Evaluate(ctx, func(params Calculator_evaluate_Params) {
params.SetExpression(expr)
})
val := result.Value().Get()
A note about message ordering: when implementing a server method, you
are responsible for acknowledging delivery of a method call. Failure to
do so can cause deadlocks. See the server.Ack function for more details.
*/
package capnp // import "zombiezen.com/go/capnproto2"

View File

@@ -0,0 +1,27 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["marshal.go"],
importpath = "zombiezen.com/go/capnproto2/encoding/text",
visibility = ["//visibility:public"],
deps = [
"//:go_default_library",
"//internal/nodemap:go_default_library",
"//internal/schema:go_default_library",
"//internal/strquote:go_default_library",
"//schemas:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["marshal_test.go"],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//:go_default_library",
"//internal/schema:go_default_library",
"//schemas:go_default_library",
],
)

View File

@@ -0,0 +1,455 @@
// Package text supports marshaling Cap'n Proto messages as text based on a schema.
package text
import (
"bytes"
"fmt"
"io"
"math"
"strconv"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/nodemap"
"zombiezen.com/go/capnproto2/internal/schema"
"zombiezen.com/go/capnproto2/internal/strquote"
"zombiezen.com/go/capnproto2/schemas"
)
// Marker strings.
const (
voidMarker = "void"
interfaceMarker = "<external capability>"
anyPointerMarker = "<opaque pointer>"
)
// Marshal returns the text representation of a struct.
func Marshal(typeID uint64, s capnp.Struct) (string, error) {
buf := new(bytes.Buffer)
if err := NewEncoder(buf).Encode(typeID, s); err != nil {
return "", err
}
return buf.String(), nil
}
// MarshalList returns the text representation of a struct list.
func MarshalList(typeID uint64, l capnp.List) (string, error) {
buf := new(bytes.Buffer)
if err := NewEncoder(buf).EncodeList(typeID, l); err != nil {
return "", err
}
return buf.String(), nil
}
// An Encoder writes the text format of Cap'n Proto messages to an output stream.
type Encoder struct {
w errWriter
tmp []byte
nodes nodemap.Map
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: errWriter{w: w}}
}
// UseRegistry changes the registry that the encoder consults for
// schemas from the default registry.
func (enc *Encoder) UseRegistry(reg *schemas.Registry) {
enc.nodes.UseRegistry(reg)
}
// Encode writes the text representation of s to the stream.
func (enc *Encoder) Encode(typeID uint64, s capnp.Struct) error {
if enc.w.err != nil {
return enc.w.err
}
err := enc.marshalStruct(typeID, s)
if err != nil {
return err
}
return enc.w.err
}
// EncodeList writes the text representation of struct list l to the stream.
func (enc *Encoder) EncodeList(typeID uint64, l capnp.List) error {
_, seg, _ := capnp.NewMessage(capnp.SingleSegment(nil))
typ, _ := schema.NewRootType(seg)
typ.SetStructType()
typ.StructType().SetTypeId(typeID)
return enc.marshalList(typ, l)
}
func (enc *Encoder) marshalBool(v bool) {
if v {
enc.w.WriteString("true")
} else {
enc.w.WriteString("false")
}
}
func (enc *Encoder) marshalInt(i int64) {
enc.tmp = strconv.AppendInt(enc.tmp[:0], i, 10)
enc.w.Write(enc.tmp)
}
func (enc *Encoder) marshalUint(i uint64) {
enc.tmp = strconv.AppendUint(enc.tmp[:0], i, 10)
enc.w.Write(enc.tmp)
}
func (enc *Encoder) marshalFloat32(f float32) {
enc.tmp = strconv.AppendFloat(enc.tmp[:0], float64(f), 'g', -1, 32)
enc.w.Write(enc.tmp)
}
func (enc *Encoder) marshalFloat64(f float64) {
enc.tmp = strconv.AppendFloat(enc.tmp[:0], f, 'g', -1, 64)
enc.w.Write(enc.tmp)
}
func (enc *Encoder) marshalText(t []byte) {
enc.tmp = strquote.Append(enc.tmp[:0], t)
enc.w.Write(enc.tmp)
}
func needsEscape(b byte) bool {
return b < 0x20 || b >= 0x7f
}
func hexDigit(b byte) byte {
const digits = "0123456789abcdef"
return digits[b]
}
func (enc *Encoder) marshalStruct(typeID uint64, s capnp.Struct) error {
n, err := enc.nodes.Find(typeID)
if err != nil {
return err
}
if !n.IsValid() || n.Which() != schema.Node_Which_structNode {
return fmt.Errorf("cannot find struct type %#x", typeID)
}
var discriminant uint16
if n.StructNode().DiscriminantCount() > 0 {
discriminant = s.Uint16(capnp.DataOffset(n.StructNode().DiscriminantOffset() * 2))
}
enc.w.WriteByte('(')
fields := codeOrderFields(n.StructNode())
first := true
for _, f := range fields {
if !(f.Which() == schema.Field_Which_slot || f.Which() == schema.Field_Which_group) {
continue
}
if dv := f.DiscriminantValue(); !(dv == schema.Field_noDiscriminant || dv == discriminant) {
continue
}
if !first {
enc.w.WriteString(", ")
}
first = false
name, err := f.NameBytes()
if err != nil {
return err
}
enc.w.Write(name)
enc.w.WriteString(" = ")
switch f.Which() {
case schema.Field_Which_slot:
if err := enc.marshalFieldValue(s, f); err != nil {
return err
}
case schema.Field_Which_group:
if err := enc.marshalStruct(f.Group().TypeId(), s); err != nil {
return err
}
}
}
enc.w.WriteByte(')')
return nil
}
func (enc *Encoder) marshalFieldValue(s capnp.Struct, f schema.Field) error {
typ, err := f.Slot().Type()
if err != nil {
return err
}
dv, err := f.Slot().DefaultValue()
if err != nil {
return err
}
if dv.IsValid() && int(typ.Which()) != int(dv.Which()) {
name, _ := f.Name()
return fmt.Errorf("marshal field %s: default value is a %v, want %v", name, dv.Which(), typ.Which())
}
switch typ.Which() {
case schema.Type_Which_void:
enc.w.WriteString(voidMarker)
case schema.Type_Which_bool:
v := s.Bit(capnp.BitOffset(f.Slot().Offset()))
d := dv.Bool()
enc.marshalBool(!d && v || d && !v)
case schema.Type_Which_int8:
v := s.Uint8(capnp.DataOffset(f.Slot().Offset()))
d := uint8(dv.Int8())
enc.marshalInt(int64(int8(v ^ d)))
case schema.Type_Which_int16:
v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
d := uint16(dv.Int16())
enc.marshalInt(int64(int16(v ^ d)))
case schema.Type_Which_int32:
v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
d := uint32(dv.Int32())
enc.marshalInt(int64(int32(v ^ d)))
case schema.Type_Which_int64:
v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
d := uint64(dv.Int64())
enc.marshalInt(int64(v ^ d))
case schema.Type_Which_uint8:
v := s.Uint8(capnp.DataOffset(f.Slot().Offset()))
d := dv.Uint8()
enc.marshalUint(uint64(v ^ d))
case schema.Type_Which_uint16:
v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
d := dv.Uint16()
enc.marshalUint(uint64(v ^ d))
case schema.Type_Which_uint32:
v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
d := dv.Uint32()
enc.marshalUint(uint64(v ^ d))
case schema.Type_Which_uint64:
v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
d := dv.Uint64()
enc.marshalUint(v ^ d)
case schema.Type_Which_float32:
v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
d := math.Float32bits(dv.Float32())
enc.marshalFloat32(math.Float32frombits(v ^ d))
case schema.Type_Which_float64:
v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
d := math.Float64bits(dv.Float64())
enc.marshalFloat64(math.Float64frombits(v ^ d))
case schema.Type_Which_structType:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
if !p.IsValid() {
p, _ = dv.StructValuePtr()
}
return enc.marshalStruct(typ.StructType().TypeId(), p.Struct())
case schema.Type_Which_data:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
if !p.IsValid() {
b, _ := dv.Data()
enc.marshalText(b)
return nil
}
enc.marshalText(p.Data())
case schema.Type_Which_text:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
if !p.IsValid() {
b, _ := dv.TextBytes()
enc.marshalText(b)
return nil
}
enc.marshalText(p.TextBytes())
case schema.Type_Which_list:
elem, err := typ.List().ElementType()
if err != nil {
return err
}
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
if !p.IsValid() {
p, _ = dv.ListPtr()
}
return enc.marshalList(elem, p.List())
case schema.Type_Which_enum:
v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
d := dv.Uint16()
return enc.marshalEnum(typ.Enum().TypeId(), v^d)
case schema.Type_Which_interface:
enc.w.WriteString(interfaceMarker)
case schema.Type_Which_anyPointer:
enc.w.WriteString(anyPointerMarker)
default:
return fmt.Errorf("unknown field type %v", typ.Which())
}
return nil
}
func codeOrderFields(s schema.Node_structNode) []schema.Field {
list, _ := s.Fields()
n := list.Len()
fields := make([]schema.Field, n)
for i := 0; i < n; i++ {
f := list.At(i)
fields[f.CodeOrder()] = f
}
return fields
}
func (enc *Encoder) marshalList(elem schema.Type, l capnp.List) error {
switch elem.Which() {
case schema.Type_Which_void:
enc.w.WriteString(capnp.VoidList{List: l}.String())
case schema.Type_Which_bool:
enc.w.WriteString(capnp.BitList{List: l}.String())
case schema.Type_Which_int8:
enc.w.WriteString(capnp.Int8List{List: l}.String())
case schema.Type_Which_int16:
enc.w.WriteString(capnp.Int16List{List: l}.String())
case schema.Type_Which_int32:
enc.w.WriteString(capnp.Int32List{List: l}.String())
case schema.Type_Which_int64:
enc.w.WriteString(capnp.Int64List{List: l}.String())
case schema.Type_Which_uint8:
enc.w.WriteString(capnp.UInt8List{List: l}.String())
case schema.Type_Which_uint16:
enc.w.WriteString(capnp.UInt16List{List: l}.String())
case schema.Type_Which_uint32:
enc.w.WriteString(capnp.UInt32List{List: l}.String())
case schema.Type_Which_uint64:
enc.w.WriteString(capnp.UInt64List{List: l}.String())
case schema.Type_Which_float32:
enc.w.WriteString(capnp.Float32List{List: l}.String())
case schema.Type_Which_float64:
enc.w.WriteString(capnp.Float64List{List: l}.String())
case schema.Type_Which_data:
enc.w.WriteString(capnp.DataList{List: l}.String())
case schema.Type_Which_text:
enc.w.WriteString(capnp.TextList{List: l}.String())
case schema.Type_Which_structType:
enc.w.WriteByte('[')
for i := 0; i < l.Len(); i++ {
if i > 0 {
enc.w.WriteString(", ")
}
err := enc.marshalStruct(elem.StructType().TypeId(), l.Struct(i))
if err != nil {
return err
}
}
enc.w.WriteByte(']')
case schema.Type_Which_list:
enc.w.WriteByte('[')
ee, err := elem.List().ElementType()
if err != nil {
return err
}
for i := 0; i < l.Len(); i++ {
if i > 0 {
enc.w.WriteString(", ")
}
p, err := capnp.PointerList{List: l}.PtrAt(i)
if err != nil {
return err
}
err = enc.marshalList(ee, p.List())
if err != nil {
return err
}
}
enc.w.WriteByte(']')
case schema.Type_Which_enum:
enc.w.WriteByte('[')
il := capnp.UInt16List{List: l}
typ := elem.Enum().TypeId()
// TODO(light): only search for node once
for i := 0; i < il.Len(); i++ {
if i > 0 {
enc.w.WriteString(", ")
}
enc.marshalEnum(typ, il.At(i))
}
enc.w.WriteByte(']')
case schema.Type_Which_interface:
enc.w.WriteByte('[')
for i := 0; i < l.Len(); i++ {
if i > 0 {
enc.w.WriteString(", ")
}
enc.w.WriteString(interfaceMarker)
}
enc.w.WriteByte(']')
case schema.Type_Which_anyPointer:
enc.w.WriteByte('[')
for i := 0; i < l.Len(); i++ {
if i > 0 {
enc.w.WriteString(", ")
}
enc.w.WriteString(anyPointerMarker)
}
enc.w.WriteByte(']')
default:
return fmt.Errorf("unknown list type %v", elem.Which())
}
return nil
}
func (enc *Encoder) marshalEnum(typ uint64, val uint16) error {
n, err := enc.nodes.Find(typ)
if err != nil {
return err
}
if n.Which() != schema.Node_Which_enum {
return fmt.Errorf("marshaling enum of type @%#x: type is not an enum", typ)
}
enums, err := n.Enum().Enumerants()
if err != nil {
return err
}
if int(val) >= enums.Len() {
enc.marshalUint(uint64(val))
return nil
}
name, err := enums.At(int(val)).NameBytes()
if err != nil {
return err
}
enc.w.Write(name)
return nil
}
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) Write(p []byte) (int, error) {
if ew.err != nil {
return 0, ew.err
}
var n int
n, ew.err = ew.w.Write(p)
return n, ew.err
}
func (ew *errWriter) WriteString(s string) (int, error) {
if ew.err != nil {
return 0, ew.err
}
var n int
n, ew.err = io.WriteString(ew.w, s)
return n, ew.err
}
func (ew *errWriter) WriteByte(b byte) error {
if ew.err != nil {
return ew.err
}
if bw, ok := ew.w.(io.ByteWriter); ok {
ew.err = bw.WriteByte(b)
} else {
_, ew.err = ew.w.Write([]byte{b})
}
return ew.err
}

View File

@@ -0,0 +1,229 @@
package text
import (
"bytes"
"io/ioutil"
"path/filepath"
"testing"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/schema"
"zombiezen.com/go/capnproto2/schemas"
)
func readTestFile(name string) ([]byte, error) {
path := filepath.Join("testdata", name)
return ioutil.ReadFile(path)
}
func TestEncode(t *testing.T) {
tests := []struct {
constID uint64
text string
}{
{0xc0b634e19e5a9a4e, `(key = "42", value = (int32 = -123))`},
{0x967c8fe21790b0fb, `(key = "float", value = (float64 = 3.14))`},
{0xdf35cb2e1f5ea087, `(key = "bool", value = (bool = false))`},
{0xb167974479102805, `(map = [(key = "foo", value = (void = void)), (key = "bar", value = (void = void))])`},
{0x81fdbfdc91779421, `(map = [])`},
{0x8e85252144f61858, `(data = "Hi\xde\xad\xbe\xef\xca\xfe")`},
{0xc21398a8474837ba, `(voidList = [void, void])`},
{0xde82c2eeb3a4b07c, `(boolList = [true, false, true, false])`},
{0xf9e3ffc179272aa2, `(int8List = [1, -2, 3])`},
{0xfc421b96ec6ad2b6, `(int64List = [1, -2, 3])`},
{0xb3034b89d02775a5, `(uint8List = [255, 0, 1])`},
{0x9246c307e46ad03b, `(uint64List = [1, 2, 3])`},
{0xd012128a1a9cb7fc, `(float32List = [0.5, 3.14, -2])`},
{0xf16c386c66d492e2, `(textList = ["foo", "bar", "baz"])`},
{0xe14f4d42aa55de8c, `(dataList = ["\xde\xad\xbe\xef", "\xca\xfe"])`},
{0xe88c91698f7f0b73, `(cheese = gouda)`},
{0x9c51b843b337490b, `(cheeseList = [gouda, cheddar])`},
{0x81e2aadb8bfb237b, `(matrix = [[1, 2, 3], [4, 5, 6]])`},
}
data, err := readTestFile("txt.capnp.out")
if err != nil {
t.Fatal(err)
}
reg := new(schemas.Registry)
err = reg.Register(&schemas.Schema{
Bytes: data,
Nodes: []uint64{
0x8df8bc5abdc060a6,
0xd3602730c572a43b,
},
})
if err != nil {
t.Fatalf("Adding to registry: %v", err)
}
msg, err := capnp.Unmarshal(data)
if err != nil {
t.Fatal("Unmarshaling txt.capnp.out:", err)
}
req, err := schema.ReadRootCodeGeneratorRequest(msg)
if err != nil {
t.Fatal("Reading code generator request txt.capnp.out:", err)
}
nodes, err := req.Nodes()
if err != nil {
t.Fatal(err)
}
nodeMap := make(map[uint64]schema.Node, nodes.Len())
for i := 0; i < nodes.Len(); i++ {
n := nodes.At(i)
nodeMap[n.Id()] = n
}
for _, test := range tests {
c := nodeMap[test.constID]
if !c.IsValid() {
t.Errorf("Can't find node %#x; skipping", test.constID)
continue
}
dn, _ := c.DisplayName()
if c.Which() != schema.Node_Which_const {
t.Errorf("%s @%#x is a %v, not const; skipping", dn, test.constID, c.Which())
continue
}
typ, err := c.Const().Type()
if err != nil {
t.Errorf("(%s @%#x).const.type: %v", dn, test.constID, err)
continue
}
if typ.Which() != schema.Type_Which_structType {
t.Errorf("(%s @%#x).const.type is a %v; want struct", dn, test.constID, typ.Which())
continue
}
tid := typ.StructType().TypeId()
v, err := c.Const().Value()
if err != nil {
t.Errorf("(%s @%#x).const.value: %v", dn, test.constID, err)
continue
}
if v.Which() != schema.Value_Which_structValue {
t.Errorf("(%s @%#x).const.value is a %v; want struct", dn, test.constID, v.Which())
continue
}
sv, err := v.StructValuePtr()
if err != nil {
t.Errorf("(%s @%#x).const.value.struct: %v", dn, test.constID, err)
continue
}
buf := new(bytes.Buffer)
enc := NewEncoder(buf)
enc.UseRegistry(reg)
if err := enc.Encode(tid, sv.Struct()); err != nil {
t.Errorf("Encode(%#x, (%s @%#x).const.value.struct): %v", tid, dn, test.constID, err)
continue
}
if text := buf.String(); text != test.text {
t.Errorf("Encode(%#x, (%s @%#x).const.value.struct) = %q; want %q", tid, dn, test.constID, text, test.text)
continue
}
}
}
func TestEncodeList(t *testing.T) {
tests := []struct {
constID uint64
text string
}{
{0x90c9e81e6418df8e, `[(key = "foo", value = (void = void)), (key = "bar", value = (void = void))]`},
}
data, err := readTestFile("txt.capnp.out")
if err != nil {
t.Fatal(err)
}
reg := new(schemas.Registry)
err = reg.Register(&schemas.Schema{
Bytes: data,
Nodes: []uint64{
0x8df8bc5abdc060a6,
0xd3602730c572a43b,
},
})
if err != nil {
t.Fatalf("Adding to registry: %v", err)
}
msg, err := capnp.Unmarshal(data)
if err != nil {
t.Fatal("Unmarshaling txt.capnp.out:", err)
}
req, err := schema.ReadRootCodeGeneratorRequest(msg)
if err != nil {
t.Fatal("Reading code generator request txt.capnp.out:", err)
}
nodes, err := req.Nodes()
if err != nil {
t.Fatal(err)
}
nodeMap := make(map[uint64]schema.Node, nodes.Len())
for i := 0; i < nodes.Len(); i++ {
n := nodes.At(i)
nodeMap[n.Id()] = n
}
for _, test := range tests {
c := nodeMap[test.constID]
if !c.IsValid() {
t.Errorf("Can't find node %#x; skipping", test.constID)
continue
}
dn, _ := c.DisplayName()
if c.Which() != schema.Node_Which_const {
t.Errorf("%s @%#x is a %v, not const; skipping", dn, test.constID, c.Which())
continue
}
typ, err := c.Const().Type()
if err != nil {
t.Errorf("(%s @%#x).const.type: %v", dn, test.constID, err)
continue
}
if typ.Which() != schema.Type_Which_list {
t.Errorf("(%s @%#x).const.type is a %v; want list", dn, test.constID, typ.Which())
continue
}
etyp, err := typ.List().ElementType()
if err != nil {
t.Errorf("(%s @%#x).const.type.list.element_type: %v", dn, test.constID, err)
continue
}
if etyp.Which() != schema.Type_Which_structType {
t.Errorf("(%s @%#x).const.type is a %v; want struct", dn, test.constID, etyp.Which())
continue
}
tid := etyp.StructType().TypeId()
v, err := c.Const().Value()
if err != nil {
t.Errorf("(%s @%#x).const.value: %v", dn, test.constID, err)
continue
}
if v.Which() != schema.Value_Which_list {
t.Errorf("(%s @%#x).const.value is a %v; want list", dn, test.constID, v.Which())
continue
}
lv, err := v.ListPtr()
if err != nil {
t.Errorf("(%s @%#x).const.value.list: %v", dn, test.constID, err)
continue
}
buf := new(bytes.Buffer)
enc := NewEncoder(buf)
enc.UseRegistry(reg)
if err := enc.EncodeList(tid, lv.List()); err != nil {
t.Errorf("Encode(%#x, (%s @%#x).const.value.list): %v", tid, dn, test.constID, err)
continue
}
if text := buf.String(); text != test.text {
t.Errorf("Encode(%#x, (%s @%#x).const.value.list) = %q; want %q", tid, dn, test.constID, text, test.text)
continue
}
}
}

View File

@@ -0,0 +1,76 @@
@0x8ae03d633330d781;
struct KeyValue @0x8df8bc5abdc060a6 {
key @0 :Text;
value @1 :Value;
}
struct Value @0xd3602730c572a43b {
union {
void @0 :Void;
bool @1 :Bool;
int8 @2 :Int8;
int16 @3 :Int16;
int32 @4 :Int32;
int64 @5 :Int64;
uint8 @6 :UInt8;
uint16 @7 :UInt16;
uint32 @8 :UInt32;
uint64 @9 :UInt64;
float32 @10 :Float32;
float64 @11 :Float64;
text @12 :Text;
data @13 :Data;
cheese @29 :Cheese;
map @14 :List(KeyValue);
voidList @15 :List(Void);
boolList @16 :List(Bool);
int8List @17 :List(Int8);
int16List @18 :List(Int16);
int32List @19 :List(Int32);
int64List @20 :List(Int64);
uint8List @21 :List(UInt8);
uint16List @22 :List(UInt16);
uint32List @23 :List(UInt32);
uint64List @24 :List(UInt64);
float32List @25 :List(Float32);
float64List @26 :List(Float64);
textList @27 :List(Text);
dataList @28 :List(Data);
cheeseList @30 :List(Cheese);
matrix @31 :List(List(Int32));
}
}
enum Cheese {
cheddar @0;
gouda @1;
}
const kv @0xc0b634e19e5a9a4e :KeyValue = (key = "42", value = (int32 = -123));
const floatKv @0x967c8fe21790b0fb :KeyValue = (key = "float", value = (float64 = 3.14));
const boolKv @0xdf35cb2e1f5ea087 :KeyValue = (key = "bool", value = (bool = false));
const mapVal @0xb167974479102805 :Value = (map = [
(key = "foo", value = (void = void)),
(key = "bar", value = (void = void)),
]);
const data @0x8e85252144f61858 :Value = (data = 0x"4869 dead beef cafe");
const emptyMap @0x81fdbfdc91779421 :Value = (map = []);
const voidList @0xc21398a8474837ba :Value = (voidList = [void, void]);
const boolList @0xde82c2eeb3a4b07c :Value = (boolList = [true, false, true, false]);
const int8List @0xf9e3ffc179272aa2 :Value = (int8List = [1, -2, 3]);
const int64List @0xfc421b96ec6ad2b6 :Value = (int64List = [1, -2, 3]);
const uint8List @0xb3034b89d02775a5 :Value = (uint8List = [255, 0, 1]);
const uint64List @0x9246c307e46ad03b :Value = (uint64List = [1, 2, 3]);
const floatList @0xd012128a1a9cb7fc :Value = (float32List = [0.5, 3.14, -2.0]);
const textList @0xf16c386c66d492e2 :Value = (textList = ["foo", "bar", "baz"]);
const dataList @0xe14f4d42aa55de8c :Value = (dataList = [0x"deadbeef", 0x"cafe"]);
const cheese @0xe88c91698f7f0b73 :Value = (cheese = gouda);
const cheeseList @0x9c51b843b337490b :Value = (cheeseList = [gouda, cheddar]);
const matrix @0x81e2aadb8bfb237b :Value = (matrix = [[1, 2, 3], [4, 5, 6]]);
const kvList @0x90c9e81e6418df8e :List(KeyValue) = [
(key = "foo", value = (void = void)),
(key = "bar", value = (void = void)),
];

Binary file not shown.

121
vendor/zombiezen.com/go/capnproto2/example_test.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
package capnp_test
import (
"fmt"
"io/ioutil"
"os"
"zombiezen.com/go/capnproto2"
air "zombiezen.com/go/capnproto2/internal/aircraftlib"
)
func Example() {
// Make a brand new empty message.
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
// If you want runtime-type identification, this is easily obtained. Just
// wrap everything in a struct that contains a single anoymous union (e.g. struct Z).
// Then always set a Z as the root object in you message/first segment.
// The cost of the extra word of storage is usually worth it, as
// then human readable output is easily obtained via a shell command such as
//
// $ cat binary.cpz | capnp decode aircraft.capnp Z
//
// If you need to conserve space, and know your content in advance, it
// isn't necessary to use an anonymous union. Just supply the type name
// in place of 'Z' in the decode command above.
// There can only be one root. Subsequent NewRoot* calls will set the root
// pointer and orphan the previous root.
z, err := air.NewRootZ(seg)
if err != nil {
panic(err)
}
// then non-root objects:
aircraft, err := z.NewAircraft()
if err != nil {
panic(err)
}
b737, err := aircraft.NewB737()
if err != nil {
panic(err)
}
planebase, err := b737.NewBase()
if err != nil {
panic(err)
}
// Set primitive fields
planebase.SetCanFly(true)
planebase.SetName("Henrietta")
planebase.SetRating(100)
planebase.SetMaxSpeed(876) // km/hr
// if we don't set capacity, it will get the default value, in this case 0.
//planebase.SetCapacity(26020) // Liters fuel
// Creating a list
homes, err := planebase.NewHomes(2)
if err != nil {
panic(err)
}
homes.Set(0, air.Airport_jfk)
homes.Set(1, air.Airport_lax)
// Ready to write!
// You can write to memory...
buf, err := msg.Marshal()
if err != nil {
panic(err)
}
_ = buf
// ... or write to an io.Writer.
file, err := ioutil.TempFile("", "go-capnproto")
if err != nil {
panic(err)
}
defer file.Close()
defer os.Remove(file.Name())
err = capnp.NewEncoder(file).Encode(msg)
if err != nil {
panic(err)
}
}
func ExampleUnmarshal() {
msg, s, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
fmt.Printf("allocation error %v\n", err)
return
}
d, err := air.NewRootZdate(s)
if err != nil {
fmt.Printf("root error %v\n", err)
return
}
d.SetYear(2004)
d.SetMonth(12)
d.SetDay(7)
data, err := msg.Marshal()
if err != nil {
fmt.Printf("marshal error %v\n", err)
return
}
// Read
msg, err = capnp.Unmarshal(data)
if err != nil {
fmt.Printf("unmarshal error %v\n", err)
return
}
d, err = air.ReadRootZdate(msg)
if err != nil {
fmt.Printf("read root error %v\n", err)
return
}
fmt.Printf("year %d, month %d, day %d\n", d.Year(), d.Month(), d.Day())
// Output:
// year 2004, month 12, day 7
}

45
vendor/zombiezen.com/go/capnproto2/go.capnp.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
// Code generated by capnpc-go. DO NOT EDIT.
package capnp
import (
schemas "zombiezen.com/go/capnproto2/schemas"
)
const Package = uint64(0xbea97f1023792be0)
const Import = uint64(0xe130b601260e44b5)
const Doc = uint64(0xc58ad6bd519f935e)
const Tag = uint64(0xa574b41924caefc7)
const Notag = uint64(0xc8768679ec52e012)
const Customtype = uint64(0xfa10659ae02f2093)
const Name = uint64(0xc2b96012172f8df1)
const schema_d12a1c51fedd6c88 = "x\xda\x12\x98\xe2\xc0d\xc8z\x9c\x89\x81!P\x81\x95" +
"\xed\xff\xf1\xf7\xa7T$\xb7\x94,e\x08\xe4e\xe5\xf8" +
"\xdf\x91s\xf7_\xa0\x8c\xd6E\x06\x06FaO\xc6." +
"\xe1@Fv\x06\x86`\x1fFfF\x06\xc6\xff\x0f\xb4" +
"+\x95\x05\xeaW\xee\x03)eDQj\xcb\xb8J\xd8" +
"\x15\xac\xd4\x01\xa2\xf4c\xaf\xbe\xb8P\xc2\xceC\x0c\x17" +
"yY\xff\xf1\xa3\xa85d\x9c$l\x09Vk\x02Q" +
"\x1b7y~\xe0\xdek]GA\xc6\x9a\xa0(Ue" +
"\xec\x12\xd6\x05+\xd5\x80(\x15z\x10\xf4\xa6\xb2\xad\xec" +
"\x04\xa6c%\x19g\x09+\x82\x95\xca@\x94nu\xe1" +
"Sc\xdcf\xf0\x10\xd3\xb1\xbc\x8c\x8b\x84E\xc1J\x05" +
" J'+\xe8?\x98\x95*\xf0\x0b\xa4T\x01E)" +
"#\xe3!aN\xb0R\x16\x90R\x9e\xff\xc5%)\xfa" +
"\xe9\xf9z\xc9\x8c\x89\x05y\x05V%\x89\xe9\x0c\x0c\x01" +
"\x8c\x8c\x8c<\x0cLhR\x05\x89\xc9\xfc\xd9\x89\xe9\xa9" +
"\xd8e\xf3\x12s\x19qH\xa5\xe4'\xe323/\xbf" +
"\x8491=\x80\x91\x91\x81\x19M&3\xb7\x80=\xbf" +
"\xa8\x04]\x1b\x13X2\xb9\xb4\xb8$?\xb7\xa4\xb2 " +
"\x15f. \x00\x00\xff\xff\x89\xff\x94\xdf"
func init() {
schemas.Register(schema_d12a1c51fedd6c88,
0xa574b41924caefc7,
0xbea97f1023792be0,
0xc2b96012172f8df1,
0xc58ad6bd519f935e,
0xc8768679ec52e012,
0xe130b601260e44b5,
0xfa10659ae02f2093)
}

6
vendor/zombiezen.com/go/capnproto2/go.mod generated vendored Normal file
View File

@@ -0,0 +1,6 @@
module "zombiezen.com/go/capnproto2"
require (
"github.com/kylelemons/godebug" v0.0.0-20170820004349-d65d576e9348
"golang.org/x/net" v0.0.0-20180218175443-cbe0f9307d01
)

2396
vendor/zombiezen.com/go/capnproto2/integration_test.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
package capnp_test
import (
"bytes"
"encoding/hex"
"fmt"
"path/filepath"
"runtime"
"testing"
"zombiezen.com/go/capnproto2"
air "zombiezen.com/go/capnproto2/internal/aircraftlib"
"zombiezen.com/go/capnproto2/internal/capnptool"
)
const schemaPath = "internal/aircraftlib/aircraft.capnp"
func initNester(t *testing.T, n air.Nester1Capn, strs ...string) {
tl, err := n.NewStrs(int32(len(strs)))
if err != nil {
t.Fatalf("initNester(..., %q): NewStrs: %v", strs, err)
}
for i, s := range strs {
if err := tl.Set(i, s); err != nil {
t.Fatalf("initNester(..., %q): set strs[%d]: %v", strs, i, err)
}
}
}
func zdateFilledMessage(t testing.TB, n int32) *capnp.Message {
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
z, err := air.NewRootZ(seg)
if err != nil {
t.Fatal(err)
}
list, err := z.NewZdatevec(n)
if err != nil {
t.Fatal(err)
}
for i := 0; i < int(n); i++ {
d, err := air.NewZdate(seg)
if err != nil {
t.Fatal(err)
}
d.SetMonth(12)
d.SetDay(7)
d.SetYear(int16(2004 + i))
list.Set(i, d)
}
return msg
}
func zdataFilledMessage(t testing.TB, n int) *capnp.Message {
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
z, err := air.NewRootZ(seg)
if err != nil {
t.Fatal(err)
}
d, err := air.NewZdata(seg)
if err != nil {
t.Fatal(err)
}
b := make([]byte, n)
for i := 0; i < len(b); i++ {
b[i] = byte(i)
}
d.SetData(b)
z.SetZdata(d)
return msg
}
// encodeTestMessage encodes the textual Cap'n Proto message to unpacked
// binary using the capnp tool, or returns the fallback if the tool fails.
func encodeTestMessage(typ string, text string, fallback []byte) ([]byte, error) {
tool, err := capnptool.Find()
if err != nil {
// TODO(light): log tool missing
return fallback, nil
}
b, err := tool.Encode(capnptool.Type{SchemaPath: schemaPath, Name: typ}, text)
if err != nil {
return nil, fmt.Errorf("%s value %q encode failed: %v", typ, text, err)
}
if !bytes.Equal(b, fallback) {
return nil, fmt.Errorf("%s value %q =\n%s; fallback is\n%s\nFallback out of date?", typ, text, hex.Dump(b), hex.Dump(fallback))
}
return b, nil
}
// mustEncodeTestMessage encodes the textual Cap'n Proto message to unpacked
// binary using the capnp tool, or returns the fallback if the tool fails.
func mustEncodeTestMessage(t testing.TB, typ string, text string, fallback []byte) []byte {
b, err := encodeTestMessage(typ, text, fallback)
if err != nil {
if _, fname, line, ok := runtime.Caller(1); ok {
t.Fatalf("%s:%d: %v", filepath.Base(fname), line, err)
} else {
t.Fatal(err)
}
}
return b
}

View File

@@ -0,0 +1,24 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"aircraft.capnp.go",
"generate.go",
],
importpath = "zombiezen.com/go/capnproto2/internal/aircraftlib",
visibility = ["//:__subpackages__"],
deps = [
"//:go_default_library",
"//encoding/text:go_default_library",
"//schemas:go_default_library",
"//server:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
filegroup(
name = "schema",
srcs = ["aircraft.capnp"],
visibility = ["//:__subpackages__"],
)

View File

@@ -0,0 +1,353 @@
using Go = import "/go.capnp";
$Go.package("aircraftlib");
$Go.import("zombiezen.com/go/capnproto2/internal/aircraftlib");
@0x832bcc6686a26d56;
const constDate :Zdate = (year = 2015, month = 8, day = 27);
const constList :List(Zdate) = [(year = 2015, month = 8, day = 27), (year = 2015, month = 8, day = 28)];
const constEnum :Airport = jfk;
struct Zdate {
year @0 :Int16;
month @1 :UInt8;
day @2 :UInt8;
}
struct Zdata {
data @0 :Data;
}
enum Airport {
none @0;
jfk @1;
lax @2;
sfo @3;
luv @4;
dfw @5;
test @6;
# test must be last because we use it to count
# the number of elements in the Airport enum.
}
struct PlaneBase {
name @0: Text;
homes @1: List(Airport);
rating @2: Int64;
canFly @3: Bool;
capacity @4: Int64;
maxSpeed @5: Float64;
}
struct B737 {
base @0: PlaneBase;
}
struct A320 {
base @0: PlaneBase;
}
struct F16 {
base @0: PlaneBase;
}
# need a struct with at least two pointers to catch certain bugs
struct Regression {
base @0: PlaneBase;
b0 @1: Float64; # intercept
beta @2: List(Float64);
planes @3: List(Aircraft);
ymu @4: Float64; # y-mean in original space
ysd @5: Float64; # y-standard deviation in original space
}
struct Aircraft {
# so we can restrict
# and specify a Plane is required in
# certain places.
union {
void @0: Void; # @0 will be the default, so always make @0 a Void.
b737 @1: B737;
a320 @2: A320;
f16 @3: F16;
}
}
struct Z {
# Z must contain all types, as this is our
# runtime type identification. It is a thin shim.
union {
void @0: Void; # always first in any union.
zz @1: Z; # any. fyi, this can't be 'z' alone.
f64 @2: Float64;
f32 @3: Float32;
i64 @4: Int64;
i32 @5: Int32;
i16 @6: Int16;
i8 @7: Int8;
u64 @8: UInt64;
u32 @9: UInt32;
u16 @10: UInt16;
u8 @11: UInt8;
bool @12: Bool;
text @13: Text;
blob @14: Data;
f64vec @15: List(Float64);
f32vec @16: List(Float32);
i64vec @17: List(Int64);
i32vec @18: List(Int32);
i16vec @19: List(Int16);
i8vec @20: List(Int8);
u64vec @21: List(UInt64);
u32vec @22: List(UInt32);
u16vec @23: List(UInt16);
u8vec @24: List(UInt8);
boolvec @39: List(Bool);
datavec @40: List(Data);
textvec @41: List(Text);
zvec @25: List(Z);
zvecvec @26: List(List(Z));
zdate @27: Zdate;
zdata @28: Zdata;
aircraftvec @29: List(Aircraft);
aircraft @30: Aircraft;
regression @31: Regression;
planebase @32: PlaneBase;
airport @33: Airport;
b737 @34: B737;
a320 @35: A320;
f16 @36: F16;
zdatevec @37: List(Zdate);
zdatavec @38: List(Zdata);
grp :group {
first @42 :UInt64;
second @43 :UInt64;
}
echo @44 :Echo;
echoBases @45 :EchoBases;
}
}
# tests for Text/List(Text) recusion handling
struct Counter {
size @0: Int64;
words @1: Text;
wordlist @2: List(Text);
bitlist @3: List(Bool);
}
struct Bag {
counter @0: Counter;
}
struct Zserver {
waitingjobs @0: List(Zjob);
}
struct Zjob {
cmd @0: Text;
args @1: List(Text);
}
# versioning test structs
struct VerEmpty {
}
struct VerOneData {
val @0: Int16;
}
struct VerTwoData {
val @0: Int16;
duo @1: Int64;
}
struct VerOnePtr {
ptr @0: VerOneData;
}
struct VerTwoPtr {
ptr1 @0: VerOneData;
ptr2 @1: VerOneData;
}
struct VerTwoDataTwoPtr {
val @0: Int16;
duo @1: Int64;
ptr1 @2: VerOneData;
ptr2 @3: VerOneData;
}
struct HoldsVerEmptyList {
mylist @0: List(VerEmpty);
}
struct HoldsVerOneDataList {
mylist @0: List(VerOneData);
}
struct HoldsVerTwoDataList {
mylist @0: List(VerTwoData);
}
struct HoldsVerOnePtrList {
mylist @0: List(VerOnePtr);
}
struct HoldsVerTwoPtrList {
mylist @0: List(VerTwoPtr);
}
struct HoldsVerTwoTwoList {
mylist @0: List(VerTwoDataTwoPtr);
}
struct HoldsVerTwoTwoPlus {
mylist @0: List(VerTwoTwoPlus);
}
struct VerTwoTwoPlus {
val @0: Int16;
duo @1: Int64;
ptr1 @2: VerTwoDataTwoPtr;
ptr2 @3: VerTwoDataTwoPtr;
tre @4: Int64;
lst3 @5: List(Int64);
}
# text handling
struct HoldsText {
txt @0: Text;
lst @1: List(Text);
lstlst @2: List(List(Text));
}
# test that we avoid unnecessary truncation
struct WrapEmpty {
mightNotBeReallyEmpty @0: VerEmpty;
}
struct Wrap2x2 {
mightNotBeReallyEmpty @0: VerTwoDataTwoPtr;
}
struct Wrap2x2plus {
mightNotBeReallyEmpty @0: VerTwoTwoPlus;
}
# test voids in a union
struct VoidUnion {
union {
a @0 :Void;
b @1 :Void;
}
}
# test List(List(Struct(List)))
struct Nester1Capn {
strs @0: List(Text);
}
struct RWTestCapn {
nestMatrix @0: List(List(Nester1Capn));
}
struct ListStructCapn {
vec @0: List(Nester1Capn);
}
# test interfaces
interface Echo {
echo @0 (in :Text) -> (out :Text);
}
struct Hoth {
base @0 :EchoBase;
}
struct EchoBase {
echo @0 :Echo;
}
# test List(Struct(Interface))
struct EchoBases {
bases @0 :List(EchoBase);
}
# test transforms
struct StackingRoot {
a @1 :StackingA;
aWithDefault @0 :StackingA = (num = 42);
}
struct StackingA {
num @0 :Int32;
b @1 :StackingB;
}
struct StackingB {
num @0 :Int32;
}
interface CallSequence {
getNumber @0 () -> (n :UInt32);
}
# test defaults
struct Defaults {
text @0 :Text = "foo";
data @1 :Data = "bar";
float @2 :Float32 = 3.14;
int @3 :Int32 = -123;
uint @4 :UInt32 = 42;
}
# benchmarks
struct BenchmarkA {
name @0 :Text;
birthDay @1 :Int64;
phone @2 :Text;
siblings @3 :Int32;
spouse @4 :Bool;
money @5 :Float64;
}
struct AllocBenchmark {
fields @0 :List(Field);
struct Field {
stringValue @0 :Text;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
package aircraftlib
//go:generate capnp compile -I ../../std -ogo aircraft.capnp

View File

@@ -0,0 +1,8 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["capnptool.go"],
importpath = "zombiezen.com/go/capnproto2/internal/capnptool",
visibility = ["//:__subpackages__"],
)

View File

@@ -0,0 +1,76 @@
// Package capnptool provides an API for calling the capnp tool in tests.
package capnptool
import (
"bytes"
"fmt"
"io"
"os/exec"
"strings"
"sync"
)
// Tool is the path to the capnp command-line tool.
// It can be used from multiple goroutines.
type Tool string
var cache struct {
init sync.Once
tool Tool
err error
}
// Find searches PATH for the capnp tool.
func Find() (Tool, error) {
cache.init.Do(func() {
path, err := exec.LookPath("capnp")
if err != nil {
cache.err = err
return
}
cache.tool = Tool(path)
})
return cache.tool, cache.err
}
// Run executes the tool with the given stdin and arguments returns the stdout.
func (tool Tool) Run(stdin io.Reader, args ...string) ([]byte, error) {
c := exec.Command(string(tool), args...)
c.Stdin = stdin
stderr := new(bytes.Buffer)
c.Stderr = stderr
out, err := c.Output()
if err != nil {
return nil, fmt.Errorf("run `%s`: %v; stderr:\n%s", strings.Join(c.Args, " "), err, stderr)
}
return out, nil
}
// Encode encodes Cap'n Proto text into the binary representation.
func (tool Tool) Encode(typ Type, text string) ([]byte, error) {
return tool.Run(strings.NewReader(text), "encode", typ.SchemaPath, typ.Name)
}
// Decode decodes a Cap'n Proto message into text.
func (tool Tool) Decode(typ Type, r io.Reader) (string, error) {
out, err := tool.Run(r, "decode", "--short", typ.SchemaPath, typ.Name)
if err != nil {
return "", err
}
return string(out), nil
}
// DecodePacked decodes a packed Cap'n Proto message into text.
func (tool Tool) DecodePacked(typ Type, r io.Reader) (string, error) {
out, err := tool.Run(r, "decode", "--short", "--packed", typ.SchemaPath, typ.Name)
if err != nil {
return "", err
}
return string(out), nil
}
// Type is a reference to a Cap'n Proto type in a schema.
type Type struct {
SchemaPath string
Name string
}

View File

@@ -0,0 +1,193 @@
// +build mktemplates
// Build tag so that users who run `go get zombiezen.com/go/capnproto2/...` don't install this command.
// cd internal/cmd/mktemplates && go build -tags=mktemplates
// mktemplates is a command to regenerate capnpc-go/templates.go.
package main
import (
"bytes"
"fmt"
"go/format"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"text/template/parse"
)
func main() {
if len(os.Args) != 3 {
fmt.Fprintln(os.Stderr, "usage: mktemplates OUT DIR")
os.Exit(64)
}
dir := os.Args[2]
names, err := listdir(dir)
if err != nil {
fatalln(err)
}
ts := make([]template, len(names))
for i, name := range names {
src, err := ioutil.ReadFile(filepath.Join(dir, name))
if err != nil {
fatalf("reading template %s: %v", name, err)
}
compiled, err := compileTemplate(name, string(src))
if err != nil {
fatalf("compiling template %s: %v", name, err)
}
ts[i] = template{
name: name,
content: compiled,
}
}
genbuf := new(bytes.Buffer)
err = generateGo(genbuf, os.Args, ts)
if err != nil {
fatalln("generating code:", err)
}
code, err := format.Source(genbuf.Bytes())
if err != nil {
fatalln("formatting code:", err)
}
outname := os.Args[1]
out, err := os.Create(outname)
if err != nil {
fatalf("opening destination %s: %v", outname, err)
}
_, err = out.Write(code)
cerr := out.Close()
if err != nil {
fatalf("write to %s: %v", outname, err)
}
if cerr != nil {
fatalln(err)
}
}
func compileTemplate(name, src string) (string, error) {
tset, err := parse.Parse(name, src, "{{", "}}", funcStubs)
if err != nil {
return "", err
}
return tset[name].Root.String(), nil
}
func generateGo(w io.Writer, args []string, ts []template) error {
src := new(bytes.Buffer)
for _, t := range ts {
fmt.Fprintf(src, "{{define %q}}", t.name)
src.WriteString(t.content)
src.WriteString("{{end}}")
}
// TODO(light): collect errors
fmt.Fprintln(w, "// Code generated from templates directory. DO NOT EDIT.")
fmt.Fprintln(w)
fmt.Fprintln(w, "//go:generate", strings.Join(args, " "))
fmt.Fprintln(w)
fmt.Fprintln(w, "package main")
fmt.Fprintln(w, "import (")
fmt.Fprintln(w, "\t\"strings\"")
fmt.Fprintln(w, "\t\"text/template\"")
fmt.Fprintln(w, ")")
fmt.Fprintln(w, "var templates = template.Must(template.New(\"\").Funcs(template.FuncMap{")
fmt.Fprintln(w, "\t\"title\": strings.Title,")
fmt.Fprintf(w, "}).Parse(\n\t%q))\n", src.Bytes())
for _, t := range ts {
if strings.HasPrefix(t.name, "_") {
continue
}
fmt.Fprintf(w, "func render%s(r renderer, p %sParams) error {\n\treturn r.Render(%[2]q, p)\n}\n", strings.Title(t.name), t.name)
}
return nil
}
type template struct {
name string
content string
}
func listdir(name string) ([]string, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
return nil, err
}
n := 0
for _, name := range names {
if !strings.HasPrefix(name, ".") {
names[n] = name
n++
}
}
names = names[:n]
sort.Strings(names)
return names, nil
}
func fatalln(args ...interface{}) {
var buf bytes.Buffer
buf.WriteString("mktemplates: ")
fmt.Fprintln(&buf, args...)
os.Stderr.Write(buf.Bytes())
os.Exit(1)
}
func fatalf(format string, args ...interface{}) {
var buf bytes.Buffer
buf.WriteString("mktemplates: ")
fmt.Fprintf(&buf, format, args...)
if !bytes.HasSuffix(buf.Bytes(), []byte{'\n'}) {
buf.Write([]byte{'\n'})
}
os.Stderr.Write(buf.Bytes())
os.Exit(1)
}
var funcStubs = map[string]interface{}{
// Built-ins
"and": variadicBoolStub,
"call": func(interface{}, ...interface{}) (interface{}, error) { return nil, nil },
"eq": func(arg0 interface{}, args ...interface{}) (bool, error) { return false, nil },
"ge": cmpStub,
"gt": cmpStub,
"html": escaperStub,
"index": func(interface{}, ...interface{}) (interface{}, error) { return nil, nil },
"js": escaperStub,
"le": cmpStub,
"len": func(interface{}) (int, error) { return 0, nil },
"lt": cmpStub,
"ne": cmpStub,
"not": func(interface{}) bool { return false },
"or": variadicBoolStub,
"print": fmt.Sprint,
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": escaperStub,
// App-specific
"title": strings.Title,
}
func variadicBoolStub(arg0 interface{}, args ...interface{}) interface{} {
return arg0
}
func cmpStub(interface{}, interface{}) (bool, error) {
return false, nil
}
func escaperStub(...interface{}) string {
return ""
}
func importStub() string {
return ""
}

View File

@@ -0,0 +1,24 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["doc.go"],
importpath = "zombiezen.com/go/capnproto2/internal/demo",
visibility = ["//:__subpackages__"],
)
go_test(
name = "go_default_test",
srcs = [
"book_test.go",
"hash_test.go",
],
embed = [":go_default_library"],
deps = [
"//:go_default_library",
"//internal/demo/books:go_default_library",
"//internal/demo/hashes:go_default_library",
"//rpc:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)

View File

@@ -0,0 +1,61 @@
package demo_test
import (
"fmt"
"io"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/demo/books"
)
func Example_book() {
r, w := io.Pipe()
go writer(w)
reader(r)
// Output:
// "War and Peace" has 1440 pages
}
func writer(out io.Writer) {
// Make a brand new empty message. A Message allocates Cap'n Proto structs.
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
panic(err)
}
// Create a new Book struct. Every message must have a root struct.
book, err := books.NewRootBook(seg)
if err != nil {
panic(err)
}
book.SetTitle("War and Peace")
book.SetPageCount(1440)
// Write the message to stdout.
err = capnp.NewEncoder(out).Encode(msg)
if err != nil {
panic(err)
}
}
func reader(in io.Reader) {
// Read the message from stdin.
msg, err := capnp.NewDecoder(in).Decode()
if err != nil {
panic(err)
}
// Extract the root struct from the message.
book, err := books.ReadRootBook(msg)
if err != nil {
panic(err)
}
// Access fields from the struct.
title, err := book.Title()
if err != nil {
panic(err)
}
pageCount := book.PageCount()
fmt.Printf("%q has %d pages\n", title, pageCount)
}

View File

@@ -0,0 +1,16 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"books.capnp.go",
"gen.go",
],
importpath = "zombiezen.com/go/capnproto2/internal/demo/books",
visibility = ["//:__subpackages__"],
deps = [
"//:go_default_library",
"//encoding/text:go_default_library",
"//schemas:go_default_library",
],
)

View File

@@ -0,0 +1,12 @@
using Go = import "/go.capnp";
@0x85d3acc39d94e0f8;
$Go.package("books");
$Go.import("zombiezen.com/go/capnproto2/internal/demo/books");
struct Book {
title @0 :Text;
# Title of the book.
pageCount @1 :Int32;
# Number of pages in the book.
}

Some files were not shown because too many files have changed in this diff Show More