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

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

@@ -0,0 +1,37 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"extract.go",
"fields.go",
"insert.go",
],
importpath = "zombiezen.com/go/capnproto2/pogs",
visibility = ["//visibility:public"],
deps = [
"//:go_default_library",
"//internal/nodemap:go_default_library",
"//internal/schema:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"bench_test.go",
"embed_test.go",
"example_test.go",
"interface_test.go",
"pogs_test.go",
],
embed = [":go_default_library"],
deps = [
"//:go_default_library",
"//internal/aircraftlib:go_default_library",
"//internal/demo/books:go_default_library",
"@com_github_kylelemons_godebug//pretty:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)

77
vendor/zombiezen.com/go/capnproto2/pogs/bench_test.go generated vendored Normal file
View File

@@ -0,0 +1,77 @@
package pogs
import (
"encoding/hex"
"math/rand"
"testing"
"zombiezen.com/go/capnproto2"
air "zombiezen.com/go/capnproto2/internal/aircraftlib"
)
type A struct {
Name string
BirthDay int64
Phone string
Siblings int32
Spouse bool
Money float64
}
func generateA(r *rand.Rand) *A {
return &A{
Name: randString(r, 16),
BirthDay: r.Int63(),
Phone: randString(r, 10),
Siblings: r.Int31n(5),
Spouse: r.Intn(2) == 1,
Money: r.Float64(),
}
}
func BenchmarkExtract(b *testing.B) {
r := rand.New(rand.NewSource(12345))
data := make([][]byte, 1000)
for i := range data {
a := generateA(r)
msg, seg, _ := capnp.NewMessage(capnp.SingleSegment(nil))
root, _ := air.NewRootBenchmarkA(seg)
Insert(air.BenchmarkA_TypeID, root.Struct, a)
data[i], _ = msg.Marshal()
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
msg, _ := capnp.Unmarshal(data[r.Intn(len(data))])
root, _ := msg.RootPtr()
var a A
Extract(&a, air.BenchmarkA_TypeID, root.Struct())
}
}
func BenchmarkInsert(b *testing.B) {
r := rand.New(rand.NewSource(12345))
data := make([]*A, 1000)
for i := range data {
data[i] = generateA(r)
}
arena := make([]byte, 0, 512)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
a := data[r.Intn(len(data))]
msg, seg, _ := capnp.NewMessage(capnp.SingleSegment(arena[:0]))
root, _ := air.NewRootBenchmarkA(seg)
Insert(air.BenchmarkA_TypeID, root.Struct, a)
msg.Marshal()
}
}
func randString(r *rand.Rand, n int) string {
b := make([]byte, (n+1)/2)
// Go 1.6 adds a Rand.Read method, but since we want to be compatible with Go 1.4...
for i := range b {
b[i] = byte(r.Intn(255))
}
return hex.EncodeToString(b)[:n]
}

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

@@ -0,0 +1,164 @@
/*
Package pogs provides functions to convert Cap'n Proto messages to and
from Go structs. pogs operates similarly to encoding/json: define a
struct that is optionally marked up with tags, then Insert and Extract
will copy the fields to and from the corresponding Cap'n Proto struct.
Inserting
To copy data into a Cap'n Proto struct, we use the Insert function.
Consider the following schema:
struct Message {
name @0 :Text;
body @1 :Text;
time @2 :Int64;
}
and the Go struct:
type Message struct {
Name string
Body string
Time int64
}
We can copy the Go struct into a Cap'n Proto struct like this:
_, arena, _ := capnp.NewMessage(capnp.SingleSegment(nil))
root, _ := myschema.NewRootMessage(arena)
m := &Message{"Alice", "Hello", 1294706395881547000}
err := pogs.Insert(myschema.Message_TypeID, root.Struct, m)
Note that if any field names in our Go struct don't match to a field in
the Cap'n Proto struct, Insert returns an error. We'll see how to fix
that in a moment.
Extracting
Copying data back out from a Cap'n Proto struct is quite similar: we
pass a pointer to our Go struct to Extract.
m := new(Message)
err := pogs.Extract(m, myschema.Message_TypeID, root.Struct)
Types
The mapping between Cap'n Proto types and underlying Go types is as
follows:
Bool -> bool
Int8, Int16, Int32, Int64 -> int8, int16, int32, int64
UInt8, UInt16, UInt32, UInt64 -> uint8, uint16, uint32, uint64
Float32, Float64 -> float32, float64
Text -> either []byte or string
Data -> []byte
List -> slice
enum -> uint16
struct -> a struct or pointer to struct
interface -> a capnp.Client or struct with
exactly one field, named
"Client", of type capnp.Client
Note that the unsized int and uint type can't be used: int and float
types must match in size. For Data and Text fields using []byte, the
filled-in byte slice will point to original segment.
Renaming and Omitting Fields
By default, the Go field name is the same as the Cap'n Proto schema
field name with the first letter capitalized. If we want to change this
mapping, we use the capnp field tag.
type MessageRenamed struct {
Subject string `capnp:"name"`
Body string
SentMillis int64 `capnp:"time"`
}
Using a "-" will cause the field to be ignored by the Insert and
Extract functions.
type ExtraFieldsMessage struct {
ID uint64 `capnp:"-"`
Name string
Body string
Time int64
}
Unions
Since Go does not have support for variant types, Go structs that want
to use fields inside a Cap'n Proto union must have an explicit
discriminant field called Which. The Extract function will populate the
Which field and the Insert function will read the Which field to
determine which field to set. Given this schema:
struct Shape {
area @0 :Float64;
union {
circle @1 :Float64;
square @2 :Float64;
}
}
the Go struct should look like this:
type Shape struct {
Area float64
Which myschema.Shape_Which // or any other uint16 type
Circle float64
Square float64
}
Attempting to use fields in a union without a uint16 Which field will
result in an error. There is one exception: we can declare our Which
field to be fixed to one particular union value by using a field tag.
type Square struct {
Which struct{} `capnp:",which=square"`
Area float64
Width float64 `capnp:"square"`
}
This can be useful if we want to use a different Go type depending on
which field in the union is set.
shape, err := myschema.ReadRootShape(msg)
if err != nil {
return nil, err
}
switch shape.Which() {
case myschema.Shape_Which_square:
sq := new(Square)
err = pogs.Extract(sq, myschema.Square_TypeID, shape.Struct)
return sq, err
case myschema.Shape_Which_circle:
// ...
}
Embedding
Anonymous struct fields are usually extracted or inserted as if their
inner exported fields were fields in the outer struct, subject to the
rules in the next paragraph. An anonymous struct field with a name
given in its capnp tag is treated as having that name, rather than being
anonymous. An anonymous struct field with a capnp tag of "-" will be
ignored.
The visibility rules for struct fields are amended for pogs in the same
way they are amended in encoding/json: if there are multiple fields at
the same level, and that level is the least nested, the following extra
rules apply:
1) Of those fields, if any are capnp-tagged, only tagged fields are
considered, even if there are multiple untagged fields that would
otherwise conflict.
2) If there is exactly one field (tagged or not according to the first
rule), that is selected.
3) Otherwise, there are multiple fields, and all are ignored; no error
occurs.
*/
package pogs // import "zombiezen.com/go/capnproto2/pogs"

360
vendor/zombiezen.com/go/capnproto2/pogs/embed_test.go generated vendored Normal file
View File

@@ -0,0 +1,360 @@
package pogs
import (
"reflect"
"testing"
"zombiezen.com/go/capnproto2"
air "zombiezen.com/go/capnproto2/internal/aircraftlib"
)
type VerVal struct {
Val int16
}
type VerOneData struct {
VerVal
}
type VerTwoData struct {
*VerVal
Duo int64
}
type F16 struct {
PlaneBase `capnp:"base"`
}
func TestExtract_Embed(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v1, err := air.NewRootVerOneData(seg)
if err != nil {
t.Fatalf("NewRootVerOneData: %v", err)
}
v1.SetVal(123)
out := new(VerOneData)
if err := Extract(out, air.VerOneData_TypeID, v1.Struct); err != nil {
t.Errorf("Extract error: %v", err)
}
if out.Val != 123 {
t.Errorf("Extract produced %s; want %s", zpretty.Sprint(out), zpretty.Sprint(&VerOneData{VerVal{123}}))
}
}
func TestExtract_EmbedPtr(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v2, err := air.NewRootVerTwoData(seg)
if err != nil {
t.Fatalf("NewRootVerTwoData: %v", err)
}
v2.SetVal(123)
v2.SetDuo(456)
out := new(VerTwoData)
if err := Extract(out, air.VerTwoData_TypeID, v2.Struct); err != nil {
t.Errorf("Extract error: %v", err)
}
if out.VerVal == nil || out.Val != 123 || out.Duo != 456 {
t.Errorf("Extract produced %s; want %s", zpretty.Sprint(out), zpretty.Sprint(&VerTwoData{&VerVal{123}, 456}))
}
}
func TestExtract_EmbedOmit(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v2, err := air.NewRootVerTwoData(seg)
if err != nil {
t.Fatalf("NewRootVerTwoData: %v", err)
}
v2.SetVal(123)
v2.SetDuo(456)
out := new(VerTwoDataOmit)
if err := Extract(out, air.VerTwoData_TypeID, v2.Struct); err != nil {
t.Errorf("Extract error: %v", err)
}
if out.Val != 0 || out.Duo != 456 {
t.Errorf("Extract produced %s; want %s", zpretty.Sprint(out), zpretty.Sprint(&VerTwoDataOmit{VerVal{}, 456}))
}
}
func TestExtract_EmbedName(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
f16, err := air.NewRootF16(seg)
if err != nil {
t.Fatalf("NewRootF16: %v", err)
}
base, err := f16.NewBase()
if err != nil {
t.Fatalf("F16.NewBase: %v", err)
}
if err := base.SetName("ALL YOUR BASE"); err != nil {
t.Fatalf("Planebase.SetName: %v", err)
}
base.SetRating(5)
base.SetCanFly(true)
out := new(F16)
if err := Extract(out, air.F16_TypeID, f16.Struct); err != nil {
t.Errorf("Extract error: %v", err)
}
if out.Name != "ALL YOUR BASE" || out.Rating != 5 || !out.CanFly {
t.Errorf("Extract produced %s; want %s", zpretty.Sprint(out), zpretty.Sprint(&F16{PlaneBase{Name: "ALL YOUR BASE", Rating: 5, CanFly: true}}))
}
}
func TestInsert_Embed(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v1, err := air.NewRootVerOneData(seg)
if err != nil {
t.Fatalf("NewRootVerOneData: %v", err)
}
gv1 := &VerOneData{VerVal{123}}
err = Insert(air.VerOneData_TypeID, v1.Struct, gv1)
if err != nil {
t.Errorf("Insert(%s) error: %v", zpretty.Sprint(gv1), err)
}
if v1.Val() != 123 {
t.Errorf("Insert(%s) produced %v", zpretty.Sprint(gv1), v1)
}
}
func TestInsert_EmbedPtr(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v2, err := air.NewRootVerTwoData(seg)
if err != nil {
t.Fatalf("NewRootVerTwoData: %v", err)
}
gv2 := &VerTwoData{&VerVal{123}, 456}
err = Insert(air.VerTwoData_TypeID, v2.Struct, gv2)
if err != nil {
t.Errorf("Insert(%s) error: %v", zpretty.Sprint(gv2), err)
}
if v2.Val() != 123 || v2.Duo() != 456 {
t.Errorf("Insert(%s) produced %v", zpretty.Sprint(gv2), v2)
}
}
func TestInsert_EmbedNilPtr(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v2, err := air.NewRootVerTwoData(seg)
if err != nil {
t.Fatalf("NewRootVerTwoData: %v", err)
}
gv2 := &VerTwoData{nil, 456}
err = Insert(air.VerTwoData_TypeID, v2.Struct, gv2)
if err != nil {
t.Errorf("Insert(%s) error: %v", zpretty.Sprint(gv2), err)
}
if v2.Val() != 0 || v2.Duo() != 456 {
t.Errorf("Insert(%s) produced %v", zpretty.Sprint(gv2), v2)
}
}
func TestInsert_EmbedOmit(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v2, err := air.NewRootVerTwoData(seg)
if err != nil {
t.Fatalf("NewRootVerTwoData: %v", err)
}
in := &VerTwoDataOmit{VerVal{123}, 456}
err = Insert(air.VerTwoData_TypeID, v2.Struct, in)
if err != nil {
t.Errorf("Insert(%s) error: %v", zpretty.Sprint(in), err)
}
if v2.Val() != 0 || v2.Duo() != 456 {
t.Errorf("Insert(%s) produced %v", zpretty.Sprint(in), v2)
}
}
func TestInsert_EmbedNamed(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
f16, err := air.NewRootF16(seg)
if err != nil {
t.Fatalf("NewRootF16: %v", err)
}
in := &F16{PlaneBase{Name: "ALL YOUR BASE", Rating: 5, CanFly: true}}
err = Insert(air.F16_TypeID, f16.Struct, in)
if err != nil {
t.Errorf("Insert(%s) error: %v", zpretty.Sprint(in), err)
}
base, err := f16.Base()
if err != nil {
t.Errorf("f16.base: %v", err)
}
name, err := base.Name()
if err != nil {
t.Errorf("f16.base.name: %v", err)
}
if name != "ALL YOUR BASE" || base.Rating() != 5 || !base.CanFly() {
t.Errorf("Insert(%s) produced %v", zpretty.Sprint(in), f16)
}
}
type VerValNoTag struct {
Val int16
}
type VerValTag1 struct {
Val int16 `capnp:"val"`
}
type VerValTag2 struct {
Val int16 `capnp:"val"`
}
type VerValTag3 struct {
Val int16 `capnp:"val"`
}
type VerTwoDataOmit struct {
VerVal `capnp:"-"`
Duo int64
}
type VerOneDataTop struct {
Val int16
VerVal
}
type VerOneDataTopWithLowerCollision struct {
Val int16
VerVal
VerValNoTag
}
type VerOneDataNoTags struct {
VerVal
VerValNoTag
}
type VerOneData1Tag struct {
VerVal
VerValTag1
}
type VerOneData1TagWithUntagged struct {
VerVal
VerValTag1
VerValNoTag
}
type VerOneData2Tags struct {
VerValTag1
VerValTag2
}
type VerOneData3Tags struct {
VerValTag1
VerValTag2
VerValTag3
}
func TestExtract_EmbedCollide(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatalf("NewMessage: %v", err)
}
v1, err := air.NewRootVerOneData(seg)
if err != nil {
t.Fatalf("NewRootVerOneData: %v", err)
}
v1.SetVal(123)
tests := []struct {
name string
want interface{}
}{
{"top", &VerOneDataTop{Val: 123}},
{"no tags", &VerOneDataNoTags{}},
{"1 tag", &VerOneData1Tag{VerValTag1: VerValTag1{123}}},
{"1 tag with untagged", &VerOneData1TagWithUntagged{VerValTag1: VerValTag1{123}}},
{"2 tags", &VerOneData2Tags{}},
{"3 tags", &VerOneData3Tags{}},
{"top with lower collision", &VerOneDataTopWithLowerCollision{Val: 123}},
}
for _, test := range tests {
out := reflect.New(reflect.TypeOf(test.want).Elem()).Interface()
if err := Extract(out, air.VerOneData_TypeID, v1.Struct); err != nil {
t.Errorf("%s: Extract error: %v", test.name, err)
}
if !reflect.DeepEqual(out, test.want) {
t.Errorf("%s: Extract produced %s; want %s", test.name, zpretty.Sprint(out), zpretty.Sprint(test.want))
}
}
}
func TestInsert_EmbedCollide(t *testing.T) {
tests := []struct {
name string
in interface{}
val int16
}{
{"top", &VerOneDataTop{Val: 123, VerVal: VerVal{456}}, 123},
{"no tags", &VerOneDataNoTags{VerVal: VerVal{123}, VerValNoTag: VerValNoTag{456}}, 0},
{"1 tag", &VerOneData1Tag{VerVal: VerVal{123}, VerValTag1: VerValTag1{456}}, 456},
{
"1 tag with untagged",
&VerOneData1TagWithUntagged{
VerVal: VerVal{123},
VerValTag1: VerValTag1{456},
VerValNoTag: VerValNoTag{789},
},
456,
},
{"2 tags", &VerOneData2Tags{VerValTag1: VerValTag1{123}, VerValTag2: VerValTag2{456}}, 0},
{"3 tags", &VerOneData3Tags{VerValTag1: VerValTag1{123}, VerValTag2: VerValTag2{456}, VerValTag3: VerValTag3{789}}, 0},
{
"top with lower collision",
&VerOneDataTopWithLowerCollision{
Val: 123,
VerVal: VerVal{456},
VerValNoTag: VerValNoTag{789},
},
123,
},
}
for _, test := range tests {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Errorf("%s: NewMessage: %v", test.name, err)
continue
}
v1, err := air.NewRootVerOneData(seg)
if err != nil {
t.Errorf("%s: NewRootVerOneData: %v", test.name, err)
continue
}
err = Insert(air.VerOneData_TypeID, v1.Struct, test.in)
if err != nil {
t.Errorf("%s: Insert(..., %s): %v", test.name, zpretty.Sprint(test.in), err)
}
if v1.Val() != test.val {
t.Errorf("%s: Insert(..., %s) produced %v; want (val = %d)", test.name, zpretty.Sprint(test.in), v1, test.val)
}
}
}

View File

@@ -0,0 +1,87 @@
package pogs_test
import (
"fmt"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/demo/books"
"zombiezen.com/go/capnproto2/pogs"
)
var bookData = []byte{
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
0xa0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
0x57, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20,
0x50, 0x65, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,
}
func ExampleExtract() {
// books.capnp:
// struct Book {
// title @0 :Text;
// pageCount @1 :Int32;
// }
type Book struct {
Title string
PageCount int32
}
// Read the message from bytes.
msg, err := capnp.Unmarshal(bookData)
if err != nil {
panic(err)
}
root, err := msg.RootPtr()
if err != nil {
panic(err)
}
// Extract the book from the root struct.
b := new(Book)
if err := pogs.Extract(b, books.Book_TypeID, root.Struct()); err != nil {
panic(err)
}
fmt.Printf("%q has %d pages\n", b.Title, b.PageCount)
// Output:
// "War and Peace" has 1440 pages
}
func ExampleInsert() {
// books.capnp:
// struct Book {
// title @0 :Text;
// pageCount @1 :Int32;
// }
type Book struct {
Title string
PageCount int32
}
// Allocate a new Cap'n Proto Book struct.
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
panic(err)
}
root, err := books.NewRootBook(seg)
if err != nil {
panic(err)
}
// Insert the book struct into the Cap'n Proto struct.
b := &Book{
Title: "War and Peace",
PageCount: 1440,
}
if err := pogs.Insert(books.Book_TypeID, root.Struct, b); err != nil {
panic(err)
}
fmt.Println(root)
// Output:
// (title = "War and Peace", pageCount = 1440)
}

418
vendor/zombiezen.com/go/capnproto2/pogs/extract.go generated vendored Normal file
View File

@@ -0,0 +1,418 @@
package pogs
import (
"errors"
"fmt"
"math"
"reflect"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/nodemap"
"zombiezen.com/go/capnproto2/internal/schema"
)
// Extract copies s into val, a pointer to a Go struct.
func Extract(val interface{}, typeID uint64, s capnp.Struct) error {
e := new(extracter)
err := e.extractStruct(reflect.ValueOf(val), typeID, s)
if err != nil {
return fmt.Errorf("pogs: extract @%#x: %v", typeID, err)
}
return nil
}
type extracter struct {
nodes nodemap.Map
}
var clientType = reflect.TypeOf((*capnp.Client)(nil)).Elem()
func (e *extracter) extractStruct(val reflect.Value, typeID uint64, s capnp.Struct) error {
if val.Kind() == reflect.Ptr {
if val.Type().Elem().Kind() != reflect.Struct {
return fmt.Errorf("can't extract struct into %v", val.Type())
}
switch {
case !val.CanSet() && val.IsNil():
// Even if the Cap'n Proto pointer isn't valid, this is probably
// the caller's fault and will be a bug at some point.
return errors.New("can't extract struct into nil")
case !s.IsValid() && val.CanSet():
val.Set(reflect.Zero(val.Type()))
return nil
case s.IsValid() && val.CanSet() && val.IsNil():
val.Set(reflect.New(val.Type().Elem()))
}
val = val.Elem()
} else if val.Kind() != reflect.Struct {
return fmt.Errorf("can't extract struct into %v", val.Type())
}
if !val.CanSet() {
return errors.New("can't modify struct, did you pass in a pointer to your struct?")
}
n, err := e.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)
}
props, err := mapStruct(val.Type(), n)
if err != nil {
return fmt.Errorf("can't extract %s: %v", val.Type(), err)
}
var discriminant uint16
hasWhich := false
if hasDiscriminant(n) {
discriminant = s.Uint16(capnp.DataOffset(n.StructNode().DiscriminantOffset() * 2))
if err := props.setWhich(val, discriminant); err == nil {
hasWhich = true
} else if !isNoWhichError(err) {
return err
}
}
fields, err := n.StructNode().Fields()
if err != nil {
return err
}
for i := 0; i < fields.Len(); i++ {
f := fields.At(i)
vf := props.makeFieldByOrdinal(val, i)
if !vf.IsValid() {
// Don't have a field for this.
continue
}
if dv := f.DiscriminantValue(); dv != schema.Field_noDiscriminant {
if !hasWhich {
return fmt.Errorf("can't extract %s into %v: has union field but no Which field", shortDisplayName(n), val.Type())
}
if dv != discriminant {
continue
}
}
switch f.Which() {
case schema.Field_Which_slot:
if err := e.extractField(vf, s, f); err != nil {
return err
}
case schema.Field_Which_group:
if err := e.extractStruct(vf, f.Group().TypeId(), s); err != nil {
return err
}
}
}
return nil
}
func (e *extracter) extractField(val reflect.Value, 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.NameBytes()
return fmt.Errorf("extract field %s: default value is a %v, want %v", name, dv.Which(), typ.Which())
}
if !isTypeMatch(val.Type(), typ) {
name, _ := f.NameBytes()
return fmt.Errorf("can't extract field %s of type %v into a Go %v", name, typ.Which(), val.Type())
}
switch typ.Which() {
case schema.Type_Which_bool:
v := s.Bit(capnp.BitOffset(f.Slot().Offset()))
d := dv.Bool()
val.SetBool(v != d) // != acts as XOR
case schema.Type_Which_int8:
v := int8(s.Uint8(capnp.DataOffset(f.Slot().Offset())))
d := dv.Int8()
val.SetInt(int64(v ^ d))
case schema.Type_Which_int16:
v := int16(s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2)))
d := dv.Int16()
val.SetInt(int64(v ^ d))
case schema.Type_Which_int32:
v := int32(s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4)))
d := dv.Int32()
val.SetInt(int64(v ^ d))
case schema.Type_Which_int64:
v := int64(s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8)))
d := dv.Int64()
val.SetInt(v ^ d)
case schema.Type_Which_uint8:
v := s.Uint8(capnp.DataOffset(f.Slot().Offset()))
d := dv.Uint8()
val.SetUint(uint64(v ^ d))
case schema.Type_Which_uint16:
v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
d := dv.Uint16()
val.SetUint(uint64(v ^ d))
case schema.Type_Which_enum:
v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
d := dv.Enum()
val.SetUint(uint64(v ^ d))
case schema.Type_Which_uint32:
v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
d := dv.Uint32()
val.SetUint(uint64(v ^ d))
case schema.Type_Which_uint64:
v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
d := dv.Uint64()
val.SetUint(v ^ d)
case schema.Type_Which_float32:
v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
d := math.Float32bits(dv.Float32())
val.SetFloat(float64(math.Float32frombits(v ^ d)))
case schema.Type_Which_float64:
v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
d := math.Float64bits(dv.Float64())
val.SetFloat(math.Float64frombits(v ^ d))
case schema.Type_Which_text:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
var b []byte
if p.IsValid() {
b = p.TextBytes()
} else {
b, _ = dv.TextBytes()
}
if val.Kind() == reflect.String {
val.SetString(string(b))
} else {
// byte slice, as guaranteed by isTypeMatch
val.SetBytes(b)
}
case schema.Type_Which_data:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
var b []byte
if p.IsValid() {
b = p.Data()
} else {
b, _ = dv.Data()
}
val.SetBytes(b)
case schema.Type_Which_structType:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
ss := p.Struct()
if !ss.IsValid() {
p, _ = dv.StructValuePtr()
ss = p.Struct()
}
return e.extractStruct(val, typ.StructType().TypeId(), ss)
case schema.Type_Which_list:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
l := p.List()
if !l.IsValid() {
p, _ = dv.ListPtr()
l = p.List()
}
return e.extractList(val, typ, l)
case schema.Type_Which_interface:
p, err := s.Ptr(uint16(f.Slot().Offset()))
if err != nil {
return err
}
if val.Type() != clientType {
// Must be a struct wrapper.
val = val.FieldByName("Client")
}
client := p.Interface().Client()
if client == nil {
val.Set(reflect.Zero(val.Type()))
} else {
val.Set(reflect.ValueOf(client))
}
default:
return fmt.Errorf("unknown field type %v", typ.Which())
}
return nil
}
func (e *extracter) extractList(val reflect.Value, typ schema.Type, l capnp.List) error {
vt := val.Type()
elem, err := typ.List().ElementType()
if err != nil {
return err
}
if !isTypeMatch(vt, typ) {
// TODO(light): the error won't be that useful for nested lists.
return fmt.Errorf("can't extract %v list into a Go %v", elem.Which(), vt)
}
if !l.IsValid() {
val.Set(reflect.Zero(vt))
return nil
}
n := l.Len()
val.Set(reflect.MakeSlice(vt, n, n))
switch elem.Which() {
case schema.Type_Which_bool:
for i := 0; i < n; i++ {
val.Index(i).SetBool(capnp.BitList{List: l}.At(i))
}
case schema.Type_Which_int8:
for i := 0; i < n; i++ {
val.Index(i).SetInt(int64(capnp.Int8List{List: l}.At(i)))
}
case schema.Type_Which_int16:
for i := 0; i < n; i++ {
val.Index(i).SetInt(int64(capnp.Int16List{List: l}.At(i)))
}
case schema.Type_Which_int32:
for i := 0; i < n; i++ {
val.Index(i).SetInt(int64(capnp.Int32List{List: l}.At(i)))
}
case schema.Type_Which_int64:
for i := 0; i < n; i++ {
val.Index(i).SetInt(capnp.Int64List{List: l}.At(i))
}
case schema.Type_Which_uint8:
for i := 0; i < n; i++ {
val.Index(i).SetUint(uint64(capnp.UInt8List{List: l}.At(i)))
}
case schema.Type_Which_uint16, schema.Type_Which_enum:
for i := 0; i < n; i++ {
val.Index(i).SetUint(uint64(capnp.UInt16List{List: l}.At(i)))
}
case schema.Type_Which_uint32:
for i := 0; i < n; i++ {
val.Index(i).SetUint(uint64(capnp.UInt32List{List: l}.At(i)))
}
case schema.Type_Which_uint64:
for i := 0; i < n; i++ {
val.Index(i).SetUint(capnp.UInt64List{List: l}.At(i))
}
case schema.Type_Which_float32:
for i := 0; i < n; i++ {
val.Index(i).SetFloat(float64(capnp.Float32List{List: l}.At(i)))
}
case schema.Type_Which_float64:
for i := 0; i < n; i++ {
val.Index(i).SetFloat(capnp.Float64List{List: l}.At(i))
}
case schema.Type_Which_text:
if val.Type().Elem().Kind() == reflect.String {
for i := 0; i < n; i++ {
s, err := capnp.TextList{List: l}.At(i)
if err != nil {
// TODO(light): collect errors and finish
return err
}
val.Index(i).SetString(s)
}
} else {
for i := 0; i < n; i++ {
b, err := capnp.TextList{List: l}.BytesAt(i)
if err != nil {
// TODO(light): collect errors and finish
return err
}
val.Index(i).SetBytes(b)
}
}
case schema.Type_Which_data:
for i := 0; i < n; i++ {
b, err := capnp.DataList{List: l}.At(i)
if err != nil {
// TODO(light): collect errors and finish
return err
}
val.Index(i).SetBytes(b)
}
case schema.Type_Which_list:
for i := 0; i < n; i++ {
p, err := capnp.PointerList{List: l}.PtrAt(i)
// TODO(light): collect errors and finish
if err != nil {
return err
}
if err := e.extractList(val.Index(i), elem, p.List()); err != nil {
return err
}
}
case schema.Type_Which_structType:
if val.Type().Elem().Kind() == reflect.Struct {
for i := 0; i < n; i++ {
err := e.extractStruct(val.Index(i), elem.StructType().TypeId(), l.Struct(i))
if err != nil {
return err
}
}
} else {
for i := 0; i < n; i++ {
newval := reflect.New(val.Type().Elem().Elem())
val.Index(i).Set(newval)
err := e.extractStruct(newval, elem.StructType().TypeId(), l.Struct(i))
if err != nil {
return err
}
}
}
default:
return fmt.Errorf("unknown list type %v", elem.Which())
}
return nil
}
var typeMap = map[schema.Type_Which]reflect.Kind{
schema.Type_Which_bool: reflect.Bool,
schema.Type_Which_int8: reflect.Int8,
schema.Type_Which_int16: reflect.Int16,
schema.Type_Which_int32: reflect.Int32,
schema.Type_Which_int64: reflect.Int64,
schema.Type_Which_uint8: reflect.Uint8,
schema.Type_Which_uint16: reflect.Uint16,
schema.Type_Which_uint32: reflect.Uint32,
schema.Type_Which_uint64: reflect.Uint64,
schema.Type_Which_float32: reflect.Float32,
schema.Type_Which_float64: reflect.Float64,
schema.Type_Which_enum: reflect.Uint16,
}
func isTypeMatch(r reflect.Type, s schema.Type) bool {
switch s.Which() {
case schema.Type_Which_text:
return r.Kind() == reflect.String || r.Kind() == reflect.Slice && r.Elem().Kind() == reflect.Uint8
case schema.Type_Which_data:
return r.Kind() == reflect.Slice && r.Elem().Kind() == reflect.Uint8
case schema.Type_Which_structType:
return isStructOrStructPtr(r)
case schema.Type_Which_list:
e, _ := s.List().ElementType()
return r.Kind() == reflect.Slice && isTypeMatch(r.Elem(), e)
case schema.Type_Which_interface:
if r == clientType {
return true
}
// Otherwise, the type must be a struct with one element named
// "Client" of type capnp.Client.
if r.Kind() != reflect.Struct {
return false
}
if r.NumField() != 1 {
return false
}
field, ok := r.FieldByName("Client")
if !ok {
return false
}
return field.Type == clientType
}
k, ok := typeMap[s.Which()]
return ok && k == r.Kind()
}

350
vendor/zombiezen.com/go/capnproto2/pogs/fields.go generated vendored Normal file
View File

@@ -0,0 +1,350 @@
package pogs
import (
"fmt"
"reflect"
"strings"
"zombiezen.com/go/capnproto2/internal/schema"
)
type fieldProps struct {
schemaName string // empty if doesn't map to schema
typ fieldType
fixedWhich string
tagged bool
}
type fieldType int
const (
mappedField fieldType = iota
whichField
embedField
)
func parseField(f reflect.StructField, hasDiscrim bool) fieldProps {
var p fieldProps
tag := f.Tag.Get("capnp")
p.tagged = tag != ""
tname, opts := nextOpt(tag)
switch tname {
case "-":
// omitted field
case "":
if f.Anonymous && isStructOrStructPtr(f.Type) {
p.typ = embedField
return p
}
if hasDiscrim && f.Name == "Which" {
p.typ = whichField
for len(opts) > 0 {
var curr string
curr, opts = nextOpt(opts)
if strings.HasPrefix(curr, "which=") {
p.fixedWhich = strings.TrimPrefix(curr, "which=")
break
}
}
return p
}
// TODO(light): check it's uppercase.
x := f.Name[0] - 'A' + 'a'
p.schemaName = string(x) + f.Name[1:]
default:
p.schemaName = tname
}
return p
}
func nextOpt(opts string) (head, tail string) {
i := strings.Index(opts, ",")
if i == -1 {
return opts, ""
}
return opts[:i], opts[i+1:]
}
type fieldLoc struct {
i int
path []int
}
func (loc fieldLoc) depth() int {
if len(loc.path) > 0 {
return len(loc.path)
}
return 1
}
func (loc fieldLoc) sub(i int) fieldLoc {
n := len(loc.path)
switch {
case !loc.isValid():
return fieldLoc{i: i}
case n > 0:
p := make([]int, n+1)
copy(p, loc.path)
p[n] = i
return fieldLoc{path: p}
default:
return fieldLoc{path: []int{loc.i, i}}
}
}
func (loc fieldLoc) isValid() bool {
return loc.i >= 0
}
type structProps struct {
fields []fieldLoc
whichLoc fieldLoc // i == -1: none; i == -2: fixed
fixedWhich uint16
}
func mapStruct(t reflect.Type, n schema.Node) (structProps, error) {
fields, err := n.StructNode().Fields()
if err != nil {
return structProps{}, err
}
sp := structProps{
fields: make([]fieldLoc, fields.Len()),
whichLoc: fieldLoc{i: -1},
}
for i := range sp.fields {
sp.fields[i] = fieldLoc{i: -1}
}
sm := structMapper{
sp: &sp,
t: t,
hasDiscrim: hasDiscriminant(n),
fields: fields,
}
if err := sm.visit(fieldLoc{i: -1}); err != nil {
return structProps{}, err
}
for len(sm.embedQueue) > 0 {
loc := sm.embedQueue[0]
copy(sm.embedQueue, sm.embedQueue[1:])
sm.embedQueue = sm.embedQueue[:len(sm.embedQueue)-1]
if err := sm.visit(loc); err != nil {
return structProps{}, err
}
}
return sp, nil
}
type structMapper struct {
sp *structProps
t reflect.Type
hasDiscrim bool
fields schema.Field_List
embedQueue []fieldLoc
}
func (sm *structMapper) visit(base fieldLoc) error {
t := sm.t
if base.isValid() {
t = typeFieldByLoc(t, base).Type
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.PkgPath != "" && !f.Anonymous {
// unexported field
continue
}
loc := base.sub(i)
p := parseField(f, sm.hasDiscrim)
if p.typ == embedField {
sm.embedQueue = append(sm.embedQueue, loc)
continue
}
if err := sm.visitField(loc, f, p); err != nil {
return err
}
}
return nil
}
func (sm *structMapper) visitField(loc fieldLoc, f reflect.StructField, p fieldProps) error {
switch p.typ {
case mappedField:
if p.schemaName == "" {
return nil
}
fi := fieldIndex(sm.fields, p.schemaName)
if fi < 0 {
return fmt.Errorf("%v has unknown field %s, maps to %s", sm.t, f.Name, p.schemaName)
}
switch oldloc := sm.sp.fields[fi]; {
case oldloc.i == -2:
// Prior tag collision, do nothing.
case oldloc.i == -3 && p.tagged && loc.depth() == len(oldloc.path):
// A tagged field wins over untagged fields.
sm.sp.fields[fi] = loc
case oldloc.isValid() && oldloc.depth() < loc.depth():
// This is deeper, don't override.
case oldloc.isValid() && oldloc.depth() == loc.depth():
oldp := parseField(typeFieldByLoc(sm.t, oldloc), sm.hasDiscrim)
if oldp.tagged && p.tagged {
// Tag collision
sm.sp.fields[fi] = fieldLoc{i: -2}
} else if p.tagged {
sm.sp.fields[fi] = loc
} else if !oldp.tagged {
// Multiple untagged fields. Keep path, because we need it for depth.
sm.sp.fields[fi].i = -3
}
default:
sm.sp.fields[fi] = loc
}
case whichField:
if sm.sp.whichLoc.i != -1 {
return fmt.Errorf("%v embeds multiple Which fields", sm.t)
}
switch {
case p.fixedWhich != "":
fi := fieldIndex(sm.fields, p.fixedWhich)
if fi < 0 {
return fmt.Errorf("%v.Which is tagged with unknown field %s", sm.t, p.fixedWhich)
}
dv := sm.fields.At(fi).DiscriminantValue()
if dv == schema.Field_noDiscriminant {
return fmt.Errorf("%v.Which is tagged with non-union field %s", sm.t, p.fixedWhich)
}
sm.sp.whichLoc = fieldLoc{i: -2}
sm.sp.fixedWhich = dv
case f.Type.Kind() != reflect.Uint16:
return fmt.Errorf("%v.Which is type %v, not uint16", sm.t, f.Type)
default:
sm.sp.whichLoc = loc
}
}
return nil
}
// fieldBySchemaName returns the field for the given name.
// Returns an invalid value if the field was not found or it is
// contained inside a nil anonymous struct pointer.
func (sp structProps) fieldByOrdinal(val reflect.Value, i int) reflect.Value {
return fieldByLoc(val, sp.fields[i], false)
}
// makeFieldBySchemaName returns the field for the given name, creating
// its parent anonymous structs if necessary. Returns an invalid value
// if the field was not found.
func (sp structProps) makeFieldByOrdinal(val reflect.Value, i int) reflect.Value {
return fieldByLoc(val, sp.fields[i], true)
}
// which returns the value of the discriminator field.
func (sp structProps) which(val reflect.Value) (discrim uint16, ok bool) {
if sp.whichLoc.i == -2 {
return sp.fixedWhich, true
}
f := fieldByLoc(val, sp.whichLoc, false)
if !f.IsValid() {
return 0, false
}
return uint16(f.Uint()), true
}
// setWhich sets the value of the discriminator field, creating its
// parent anonymous structs if necessary. Returns whether the struct
// had a field to set.
func (sp structProps) setWhich(val reflect.Value, discrim uint16) error {
if sp.whichLoc.i == -2 {
if discrim != sp.fixedWhich {
return fmt.Errorf("extract union field @%d into %v; expected @%d", discrim, val.Type(), sp.fixedWhich)
}
return nil
}
f := fieldByLoc(val, sp.whichLoc, true)
if !f.IsValid() {
return noWhichError{val.Type()}
}
f.SetUint(uint64(discrim))
return nil
}
type noWhichError struct {
t reflect.Type
}
func (e noWhichError) Error() string {
return fmt.Sprintf("%v has no field Which", e.t)
}
func isNoWhichError(e error) bool {
_, ok := e.(noWhichError)
return ok
}
func fieldByLoc(val reflect.Value, loc fieldLoc, mkparents bool) reflect.Value {
if !loc.isValid() {
return reflect.Value{}
}
if len(loc.path) > 0 {
for i, x := range loc.path {
if i > 0 {
if val.Kind() == reflect.Ptr {
if val.IsNil() {
if !mkparents {
return reflect.Value{}
}
val.Set(reflect.New(val.Type().Elem()))
}
val = val.Elem()
}
}
val = val.Field(x)
}
return val
}
return val.Field(loc.i)
}
func typeFieldByLoc(t reflect.Type, loc fieldLoc) reflect.StructField {
if len(loc.path) > 0 {
return t.FieldByIndex(loc.path)
}
return t.Field(loc.i)
}
func hasDiscriminant(n schema.Node) bool {
return n.Which() == schema.Node_Which_structNode && n.StructNode().DiscriminantCount() > 0
}
func shortDisplayName(n schema.Node) []byte {
dn, _ := n.DisplayNameBytes()
return dn[n.DisplayNamePrefixLength():]
}
func fieldIndex(fields schema.Field_List, name string) int {
for i := 0; i < fields.Len(); i++ {
b, _ := fields.At(i).NameBytes()
if bytesStrEqual(b, name) {
return i
}
}
return -1
}
func bytesStrEqual(b []byte, s string) bool {
if len(b) != len(s) {
return false
}
for i := range b {
if b[i] != s[i] {
return false
}
}
return true
}
func isStructOrStructPtr(t reflect.Type) bool {
return t.Kind() == reflect.Struct || t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}

485
vendor/zombiezen.com/go/capnproto2/pogs/insert.go generated vendored Normal file
View File

@@ -0,0 +1,485 @@
package pogs
import (
"fmt"
"math"
"reflect"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/nodemap"
"zombiezen.com/go/capnproto2/internal/schema"
)
// Insert copies val, a pointer to a Go struct, into s.
func Insert(typeID uint64, s capnp.Struct, val interface{}) error {
ins := new(inserter)
err := ins.insertStruct(typeID, s, reflect.ValueOf(val))
if err != nil {
return fmt.Errorf("pogs: insert @%#x: %v", typeID, err)
}
return nil
}
type inserter struct {
nodes nodemap.Map
}
func (ins *inserter) insertStruct(typeID uint64, s capnp.Struct, val reflect.Value) error {
if val.Kind() == reflect.Ptr {
// TODO(light): ignore if nil?
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return fmt.Errorf("can't insert %v into a struct", val.Kind())
}
n, err := ins.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)
}
props, err := mapStruct(val.Type(), n)
if err != nil {
return fmt.Errorf("can't insert into %v: %v", val.Type(), err)
}
var discriminant uint16
hasWhich := false
if hasDiscriminant(n) {
discriminant, hasWhich = props.which(val)
if hasWhich {
off := capnp.DataOffset(n.StructNode().DiscriminantOffset() * 2)
if s.Size().DataSize < capnp.Size(off+2) {
return fmt.Errorf("can't set discriminant for %s: allocated struct is too small", shortDisplayName(n))
}
s.SetUint16(off, discriminant)
}
}
fields, err := n.StructNode().Fields()
if err != nil {
return err
}
for i := 0; i < fields.Len(); i++ {
f := fields.At(i)
vf := props.fieldByOrdinal(val, i)
if !vf.IsValid() {
// Don't have a field for this.
continue
}
if dv := f.DiscriminantValue(); dv != schema.Field_noDiscriminant {
if !hasWhich {
sname, _ := f.NameBytes()
return fmt.Errorf("can't insert %s from %v: has union field %s but no Which field", shortDisplayName(n), val.Type(), sname)
}
if dv != discriminant {
continue
}
}
switch f.Which() {
case schema.Field_Which_slot:
if err := ins.insertField(s, f, vf); err != nil {
return err
}
case schema.Field_Which_group:
if err := ins.insertStruct(f.Group().TypeId(), s, vf); err != nil {
return err
}
}
}
return nil
}
func (ins *inserter) insertField(s capnp.Struct, f schema.Field, val reflect.Value) 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.NameBytes()
return fmt.Errorf("insert field %s: default value is a %v, want %v", name, dv.Which(), typ.Which())
}
if !isTypeMatch(val.Type(), typ) {
name, _ := f.NameBytes()
return fmt.Errorf("can't insert field %s of type Go %v into a %v", name, val.Type(), typ.Which())
}
if !isFieldInBounds(s.Size(), f.Slot().Offset(), typ) {
name, _ := f.NameBytes()
return fmt.Errorf("can't insert field %s: allocated struct is too small", name)
}
switch typ.Which() {
case schema.Type_Which_bool:
v := val.Bool()
d := dv.Bool()
s.SetBit(capnp.BitOffset(f.Slot().Offset()), v != d) // != acts as XOR
case schema.Type_Which_int8:
v := int8(val.Int())
d := dv.Int8()
s.SetUint8(capnp.DataOffset(f.Slot().Offset()), uint8(v^d))
case schema.Type_Which_int16:
v := int16(val.Int())
d := dv.Int16()
s.SetUint16(capnp.DataOffset(f.Slot().Offset()*2), uint16(v^d))
case schema.Type_Which_int32:
v := int32(val.Int())
d := dv.Int32()
s.SetUint32(capnp.DataOffset(f.Slot().Offset()*4), uint32(v^d))
case schema.Type_Which_int64:
v := val.Int()
d := dv.Int64()
s.SetUint64(capnp.DataOffset(f.Slot().Offset()*8), uint64(v^d))
case schema.Type_Which_uint8:
v := uint8(val.Uint())
d := dv.Uint8()
s.SetUint8(capnp.DataOffset(f.Slot().Offset()), v^d)
case schema.Type_Which_uint16:
v := uint16(val.Uint())
d := dv.Uint16()
s.SetUint16(capnp.DataOffset(f.Slot().Offset()*2), v^d)
case schema.Type_Which_enum:
v := uint16(val.Uint())
d := dv.Enum()
s.SetUint16(capnp.DataOffset(f.Slot().Offset()*2), v^d)
case schema.Type_Which_uint32:
v := uint32(val.Uint())
d := dv.Uint32()
s.SetUint32(capnp.DataOffset(f.Slot().Offset()*4), v^d)
case schema.Type_Which_uint64:
v := val.Uint()
d := dv.Uint64()
s.SetUint64(capnp.DataOffset(f.Slot().Offset()*8), v^d)
case schema.Type_Which_float32:
v := math.Float32bits(float32(val.Float()))
d := math.Float32bits(dv.Float32())
s.SetUint32(capnp.DataOffset(f.Slot().Offset()*4), v^d)
case schema.Type_Which_float64:
v := math.Float64bits(val.Float())
d := uint64(math.Float64bits(dv.Float64()))
s.SetUint64(capnp.DataOffset(f.Slot().Offset()*8), v^d)
case schema.Type_Which_text:
off := uint16(f.Slot().Offset())
if val.Len() == 0 {
if !isEmptyValue(dv) {
return s.SetNewText(off, "")
}
return s.SetText(off, "")
}
if val.Kind() == reflect.String {
return s.SetText(off, val.String())
} else {
return s.SetTextFromBytes(off, val.Bytes())
}
case schema.Type_Which_data:
b := val.Bytes()
if b == nil && !isEmptyValue(dv) {
b = []byte{}
}
off := uint16(f.Slot().Offset())
return s.SetData(off, b)
case schema.Type_Which_structType:
off := uint16(f.Slot().Offset())
sval := val
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return s.SetPtr(off, capnp.Ptr{})
}
sval = val.Elem()
}
id := typ.StructType().TypeId()
sz, err := ins.structSize(id)
if err != nil {
return err
}
ss, err := capnp.NewStruct(s.Segment(), sz)
if err != nil {
return err
}
if err := s.SetPtr(off, ss.ToPtr()); err != nil {
return err
}
return ins.insertStruct(id, ss, sval)
case schema.Type_Which_list:
off := uint16(f.Slot().Offset())
if val.IsNil() && isEmptyValue(dv) {
return s.SetPtr(off, capnp.Ptr{})
}
elem, err := typ.List().ElementType()
if err != nil {
return err
}
l, err := ins.newList(s.Segment(), elem, int32(val.Len()))
if err != nil {
return err
}
if err := s.SetPtr(off, l.ToPtr()); err != nil {
return err
}
return ins.insertList(l, typ, val)
case schema.Type_Which_interface:
off := uint16(f.Slot().Offset())
ptr := capPtr(s.Segment(), val)
if err := s.SetPtr(off, ptr); err != nil {
return err
}
default:
return fmt.Errorf("unknown field type %v", typ.Which())
}
return nil
}
func capPtr(seg *capnp.Segment, val reflect.Value) capnp.Ptr {
client, ok := val.Interface().(capnp.Client)
if !ok {
client, ok = val.FieldByName("Client").Interface().(capnp.Client)
if !ok {
// interface is nil.
return capnp.Ptr{}
}
}
cap := seg.Message().AddCap(client)
iface := capnp.NewInterface(seg, cap)
return iface.ToPtr()
}
func (ins *inserter) insertList(l capnp.List, typ schema.Type, val reflect.Value) error {
elem, err := typ.List().ElementType()
if err != nil {
return err
}
if !isTypeMatch(val.Type(), typ) {
// TODO(light): the error won't be that useful for nested lists.
return fmt.Errorf("can't insert Go %v into a %v list", val.Type(), elem.Which())
}
n := val.Len()
switch elem.Which() {
case schema.Type_Which_void:
case schema.Type_Which_bool:
for i := 0; i < n; i++ {
capnp.BitList{List: l}.Set(i, val.Index(i).Bool())
}
case schema.Type_Which_int8:
for i := 0; i < n; i++ {
capnp.Int8List{List: l}.Set(i, int8(val.Index(i).Int()))
}
case schema.Type_Which_int16:
for i := 0; i < n; i++ {
capnp.Int16List{List: l}.Set(i, int16(val.Index(i).Int()))
}
case schema.Type_Which_int32:
for i := 0; i < n; i++ {
capnp.Int32List{List: l}.Set(i, int32(val.Index(i).Int()))
}
case schema.Type_Which_int64:
for i := 0; i < n; i++ {
capnp.Int64List{List: l}.Set(i, val.Index(i).Int())
}
case schema.Type_Which_uint8:
for i := 0; i < n; i++ {
capnp.UInt8List{List: l}.Set(i, uint8(val.Index(i).Uint()))
}
case schema.Type_Which_uint16, schema.Type_Which_enum:
for i := 0; i < n; i++ {
capnp.UInt16List{List: l}.Set(i, uint16(val.Index(i).Uint()))
}
case schema.Type_Which_uint32:
for i := 0; i < n; i++ {
capnp.UInt32List{List: l}.Set(i, uint32(val.Index(i).Uint()))
}
case schema.Type_Which_uint64:
for i := 0; i < n; i++ {
capnp.UInt64List{List: l}.Set(i, val.Index(i).Uint())
}
case schema.Type_Which_float32:
for i := 0; i < n; i++ {
capnp.Float32List{List: l}.Set(i, float32(val.Index(i).Float()))
}
case schema.Type_Which_float64:
for i := 0; i < n; i++ {
capnp.Float64List{List: l}.Set(i, val.Index(i).Float())
}
case schema.Type_Which_text:
if val.Type().Elem().Kind() == reflect.String {
for i := 0; i < n; i++ {
err := capnp.TextList{List: l}.Set(i, val.Index(i).String())
if err != nil {
// TODO(light): collect errors and finish
return err
}
}
} else {
for i := 0; i < n; i++ {
b := val.Index(i).Bytes()
if len(b) == 0 {
err := capnp.PointerList{List: l}.SetPtr(i, capnp.Ptr{})
if err != nil {
// TODO(light): collect errors and finish
return err
}
}
t, err := capnp.NewTextFromBytes(l.Segment(), b)
if err != nil {
// TODO(light): collect errors and finish
return err
}
err = capnp.PointerList{List: l}.SetPtr(i, t.ToPtr())
if err != nil {
// TODO(light): collect errors and finish
return err
}
}
}
case schema.Type_Which_data:
for i := 0; i < n; i++ {
b := val.Index(i).Bytes()
if len(b) == 0 {
err := capnp.PointerList{List: l}.SetPtr(i, capnp.Ptr{})
if err != nil {
// TODO(light): collect errors and finish
return err
}
}
err := capnp.DataList{List: l}.Set(i, b)
if err != nil {
// TODO(light): collect errors and finish
return err
}
}
case schema.Type_Which_list:
pl := capnp.PointerList{List: l}
for i := 0; i < n; i++ {
vi := val.Index(i)
if vi.IsNil() {
if err := pl.SetPtr(i, capnp.Ptr{}); err != nil {
return err
}
continue
}
ee, err := elem.List().ElementType()
if err != nil {
return err
}
li, err := ins.newList(l.Segment(), ee, int32(vi.Len()))
if err != nil {
return err
}
if err := pl.SetPtr(i, li.ToPtr()); err != nil {
return err
}
if err := ins.insertList(li, elem, vi); err != nil {
return err
}
}
case schema.Type_Which_structType:
id := elem.StructType().TypeId()
for i := 0; i < n; i++ {
err := ins.insertStruct(id, l.Struct(i), val.Index(i))
if err != nil {
// TODO(light): collect errors and finish
return err
}
}
case schema.Type_Which_interface:
pl := capnp.PointerList{List: l}
for i := 0; i < n; i++ {
ptr := capPtr(l.Segment(), val.Index(i))
if err := pl.SetPtr(i, ptr); err != nil {
// TODO(zenhack): collect errors and finish
return err
}
}
default:
return fmt.Errorf("unknown list type %v", elem.Which())
}
return nil
}
func (ins *inserter) newList(s *capnp.Segment, t schema.Type, len int32) (capnp.List, error) {
switch t.Which() {
case schema.Type_Which_void:
l := capnp.NewVoidList(s, len)
return l.List, nil
case schema.Type_Which_bool:
l, err := capnp.NewBitList(s, len)
return l.List, err
case schema.Type_Which_int8, schema.Type_Which_uint8:
l, err := capnp.NewUInt8List(s, len)
return l.List, err
case schema.Type_Which_int16, schema.Type_Which_uint16, schema.Type_Which_enum:
l, err := capnp.NewUInt16List(s, len)
return l.List, err
case schema.Type_Which_int32, schema.Type_Which_uint32, schema.Type_Which_float32:
l, err := capnp.NewUInt32List(s, len)
return l.List, err
case schema.Type_Which_int64, schema.Type_Which_uint64, schema.Type_Which_float64:
l, err := capnp.NewUInt64List(s, len)
return l.List, err
case schema.Type_Which_text, schema.Type_Which_data, schema.Type_Which_list, schema.Type_Which_interface, schema.Type_Which_anyPointer:
l, err := capnp.NewPointerList(s, len)
return l.List, err
case schema.Type_Which_structType:
sz, err := ins.structSize(t.StructType().TypeId())
if err != nil {
return capnp.List{}, err
}
return capnp.NewCompositeList(s, sz, len)
default:
return capnp.List{}, fmt.Errorf("new list: unknown element type: %v", t.Which())
}
}
func (ins *inserter) structSize(id uint64) (capnp.ObjectSize, error) {
n, err := ins.nodes.Find(id)
if err != nil {
return capnp.ObjectSize{}, err
}
if n.Which() != schema.Node_Which_structNode {
return capnp.ObjectSize{}, fmt.Errorf("insert struct: sizing: node @%#x is not a struct", id)
}
return capnp.ObjectSize{
DataSize: capnp.Size(n.StructNode().DataWordCount()) * 8,
PointerCount: n.StructNode().PointerCount(),
}, nil
}
func isFieldInBounds(sz capnp.ObjectSize, off uint32, t schema.Type) bool {
switch t.Which() {
case schema.Type_Which_void:
return true
case schema.Type_Which_bool:
return sz.DataSize >= capnp.Size(off/8+1)
case schema.Type_Which_int8, schema.Type_Which_uint8:
return sz.DataSize >= capnp.Size(off+1)
case schema.Type_Which_int16, schema.Type_Which_uint16, schema.Type_Which_enum:
return sz.DataSize >= capnp.Size(off+1)*2
case schema.Type_Which_int32, schema.Type_Which_uint32, schema.Type_Which_float32:
return sz.DataSize >= capnp.Size(off+1)*4
case schema.Type_Which_int64, schema.Type_Which_uint64, schema.Type_Which_float64:
return sz.DataSize >= capnp.Size(off+1)*8
case schema.Type_Which_text, schema.Type_Which_data, schema.Type_Which_list, schema.Type_Which_structType, schema.Type_Which_interface, schema.Type_Which_anyPointer:
return sz.PointerCount >= uint16(off+1)
default:
return false
}
}
func isEmptyValue(v schema.Value) bool {
if !v.IsValid() {
return false
}
switch v.Which() {
case schema.Value_Which_text:
b, _ := v.TextBytes()
return len(b) == 0
case schema.Value_Which_data:
b, _ := v.Data()
return len(b) == 0
case schema.Value_Which_list:
p, _ := v.ListPtr()
return p.List().Len() == 0
default:
return false
}
}

View File

@@ -0,0 +1,172 @@
package pogs
import (
"testing"
"golang.org/x/net/context"
"zombiezen.com/go/capnproto2"
air "zombiezen.com/go/capnproto2/internal/aircraftlib"
)
type simpleEcho struct{}
func checkFatal(t *testing.T, name string, err error) {
if err != nil {
t.Fatalf("%s for TestInsertIFace: %v", name, err)
}
}
func (s simpleEcho) Echo(p air.Echo_echo) error {
text, err := p.Params.In()
if err != nil {
return err
}
p.Results.SetOut(text)
return nil
}
type EchoBase struct {
Echo air.Echo
}
type EchoBases struct {
Bases []EchoBase
}
type Hoth struct {
Base EchoBase
}
func TestInsertIFace(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
checkFatal(t, "NewMessage", err)
h, err := air.NewRootHoth(seg)
checkFatal(t, "NewRootHoth", err)
err = Insert(air.Hoth_TypeID, h.Struct, Hoth{
Base: EchoBase{Echo: air.Echo_ServerToClient(simpleEcho{})},
})
checkFatal(t, "Insert", err)
base, err := h.Base()
checkFatal(t, "h.Base", err)
echo := base.Echo()
testEcho(t, echo)
}
func TestInsertListIFace(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
checkFatal(t, "NewMessage", err)
wrapper, err := air.NewEchoBases(seg)
checkFatal(t, "NewEchoBases", err)
err = Insert(air.EchoBases_TypeID, wrapper.Struct, EchoBases{
Bases: []EchoBase{
{Echo: air.Echo_ServerToClient(simpleEcho{})},
{Echo: air.Echo_ServerToClient(simpleEcho{})},
},
})
checkFatal(t, "Insert", err)
bases, err := wrapper.Bases()
checkFatal(t, "Bases", err)
for i := 0; i < bases.Len(); i++ {
testEcho(t, bases.At(i).Echo())
}
}
func TestInsertNilInterface(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
checkFatal(t, "NewMessage", err)
h, err := air.NewRootHoth(seg)
checkFatal(t, "NewRootHoth", err)
err = Insert(air.Hoth_TypeID, h.Struct, Hoth{
Base: EchoBase{Echo: air.Echo{Client: nil}},
})
checkFatal(t, "Insert", err)
base, err := h.Base()
checkFatal(t, "h.Base", err)
echo := base.Echo()
if echo.Client != nil {
t.Fatalf("Expected nil client, but got %v", echo.Client)
}
}
func TestExtractIFace(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
checkFatal(t, "NewMessage", err)
h, err := air.NewRootHoth(seg)
checkFatal(t, "NewRootHoth", err)
base, err := air.NewEchoBase(seg)
checkFatal(t, "NewEchoBase", err)
h.SetBase(base)
base.SetEcho(air.Echo_ServerToClient(simpleEcho{}))
extractedHoth := Hoth{}
err = Extract(&extractedHoth, air.Hoth_TypeID, h.Struct)
checkFatal(t, "Extract", err)
testEcho(t, extractedHoth.Base.Echo)
}
func TestExtractListIFace(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
checkFatal(t, "NewMessage", err)
wrapper, err := air.NewEchoBases(seg)
checkFatal(t, "NewEchoBases", err)
length := 2
list, err := air.NewEchoBase_List(seg, int32(length))
checkFatal(t, "NewEchoBase_List", err)
for i := 0; i < length; i++ {
base, err := air.NewEchoBase(seg)
base.SetEcho(air.Echo_ServerToClient(simpleEcho{}))
checkFatal(t, "NewEchoBase", err)
list.Set(i, base)
}
wrapper.SetBases(list)
extractedBases := EchoBases{}
err = Extract(&extractedBases, air.EchoBases_TypeID, wrapper.Struct)
checkFatal(t, "Extract", err)
if extractedBases.Bases == nil {
t.Fatal("Bases is nil")
}
if len(extractedBases.Bases) != length {
t.Fatalf("Bases has Wrong length: got %d but wanted %d.",
len(extractedBases.Bases), length)
}
for _, v := range extractedBases.Bases {
testEcho(t, v.Echo)
}
}
// Make sure extract correctly handles missing interfaces.
func TestExtractMissingIFace(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
base, err := air.NewRootEchoBase(seg)
checkFatal(t, "NewRootEchoBase", err)
// Fill the client in, so we know that after extracting, if
// it's nil it's because it was *set*, not just left over:
extractedBase := EchoBase{Echo: air.Echo_ServerToClient(simpleEcho{})}
err = Extract(&extractedBase, air.EchoBase_TypeID, base.Struct)
checkFatal(t, "Extract", err)
if extractedBase.Echo.Client != nil {
t.Fatalf("Expected nil client but got %v", extractedBase.Echo.Client)
}
}
func testEcho(t *testing.T, echo air.Echo) {
expected := "Hello!"
result, err := echo.Echo(context.TODO(), func(p air.Echo_echo_Params) error {
p.SetIn(expected)
return nil
}).Struct()
checkFatal(t, "Echo", err)
actual, err := result.Out()
checkFatal(t, "result.Out", err)
if actual != expected {
t.Fatal("Echo result did not match input; "+
"wanted %q but got %q.", expected, actual)
}
}

1472
vendor/zombiezen.com/go/capnproto2/pogs/pogs_test.go generated vendored Normal file

File diff suppressed because it is too large Load Diff