├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── aircraftlib ├── Makefile ├── aircraft.capnp ├── aircraft.capnp.go └── go.capnp ├── bench_test.go ├── bin ├── dec ├── decp ├── enc └── encp ├── boollist_test.go ├── capn.go ├── capnpc-go ├── .gitignore ├── caplit.go ├── capnpc-go.go ├── schema.capnp └── schema.capnp.go ├── common_test.go ├── compress_test.go ├── create_test.go ├── customtype_test.go ├── data ├── bvec0.bin ├── bvec1.bin ├── bvec3.bin ├── bvec5.bin ├── capnp.packed.one.zdate.vector.dat ├── capnp.unpacked.one.zdate.vector.dat ├── check.zdate.cpz ├── gen-bvec.sh ├── packed.byteslice.dat ├── zdate2.packed.dat └── zdate2.unpacked.dat ├── doc.go ├── enum_test.go ├── example_test.go ├── exists.go ├── go.capnp ├── go.capnp.go ├── json.go ├── mem.go ├── nested_test.go ├── notrunc_test.go ├── print_test.go ├── stream.go ├── stream_test.go ├── stringbytes_test.go ├── struct_test.go ├── textmv_test.go ├── util_test.go ├── utils2_test.go ├── version_test.go └── write_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | /capnp-go/capnp-go 2 | /example/example 3 | /example/*.capnp.go 4 | TAGS 5 | *~ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | go-capnproto is licensed under the terms of the MIT license reproduced below. 2 | 3 | =============================================================================== 4 | 5 | Copyright (C) 2014 the go-capnproto authors and contributors. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | =============================================================================== 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: prepare 2 | 3 | prepare: 4 | cd capnpc-go; go install && go build 5 | go install ./capnpc-go 6 | cd aircraftlib; make 7 | which capnpc-go 8 | # if there is a diff below, adjust your PATH to use the most-recently built capnpc-go 9 | diff `which capnpc-go` ./capnpc-go/capnpc-go 10 | 11 | 12 | check: 13 | cat data/check.zdate.cpz | capnp decode aircraftlib/aircraft.capnp Zdate 14 | 15 | checkp: 16 | cat data/zdate2.packed.dat | bin/decp 17 | 18 | testbuild: 19 | go test -c -gcflags "-N -l" -v 20 | 21 | clean: 22 | rm -f go-capnproto.test *~ 23 | cd aircraftlib; make clean 24 | 25 | test: 26 | cd capnpc-go; go build; go install 27 | cd aircraftlib; make 28 | go test -v 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Version 1.0 vs 2.0 2 | ------- 3 | 4 | Update 2015 Sept 20: 5 | 6 | Big news! Version 2.0 of the go-bindings, authored by Ross Light, is now released and newly available! It features capnproto RPC and capabilities support. See https://github.com/zombiezen/go-capnproto2 for the v2 code and docs. 7 | 8 | This repository (https://github.com/glycerine/go-capnproto) is now being called version 1.0 of the go bindings for capnproto. It does not have RPC (it was created before the RPC protocol was defined). Version 1 has schema generating tools such as https://github.com/glycerine/bambam. Personally I have many projects that use v1 with mangos for network transport; https://github.com/gdamore/mangos. Here is an example of using them together: https://github.com/glycerine/goq. Nonetheless, for new projects, especially once v2 has been hardened and tested, v2 should be preferred. 9 | 10 | Version 1 will be maintained for applications that currently use it. However new users, new features and new code contributions should be directed to the version 2 code base to take advantage of the RPC and capabilities. 11 | 12 | 13 | License 14 | ------- 15 | 16 | MIT - see LICENSE file 17 | 18 | Documentation 19 | ------------- 20 | In godoc see http://godoc.org/github.com/glycerine/go-capnproto 21 | 22 | 23 | News 24 | ---- 25 | 26 | 5 April 2014: James McKaskill, the author of go-capnproto (https://github.com/jmckaskill/go-capnproto), 27 | has been super busy of late, so I agreed to take over as maintainer. This branch 28 | (https://github.com/glycerine/go-capnproto) includes my recent work to fix bugs in the 29 | creation (originating) of structs for Go, and an implementation of the packing/unpacking capnp specification. 30 | Thanks to Albert Strasheim (https://github.com/alberts/go-capnproto) of CloudFlare for a great set of packing tests. - Jason 31 | 32 | Getting started 33 | --------------- 34 | 35 | New! Visit the sibling project to this one, [bambam](https://github.com/glycerine/bambam), to automagically generate a capnproto schema from the struct definitions in your go source files. Bambam makes it easy to get starting with go-capnproto. 36 | 37 | pre-requisite: Due to the use of the customtype annotation feature, you will need a relatively recent capnproto installation. At or after 1 July 2014 (at or after b2d752beac5436bada2712f1a23185b78063e6fa) is known to work. 38 | 39 | ~~~ 40 | # first: be sure you have your GOPATH env variable setup. 41 | $ go get -u -t github.com/glycerine/go-capnproto 42 | $ cd $GOPATH/src/github.com/glycerine/go-capnproto 43 | $ make # will install capnpc-go and compile the test schema aircraftlib/aircraft.capnp, which is used in the tests. 44 | $ diff ./capnpc-go/capnpc-go `which capnpc-go` # you should verify that you are using the capnpc-go binary you just built. There should be no diff. Adjust your PATH if necessary to include the binary capnpc-go that you just built/installed from ./capnpc-go/capnpc-go. 45 | $ go test -v # confirm all tests are green 46 | ~~~ 47 | 48 | What is Cap'n Proto? 49 | -------------------- 50 | 51 | The best cerealization... 52 | 53 | http://kentonv.github.io/capnproto/ 54 | 55 | 56 | -------------------------------------------------------------------------------- /aircraftlib/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | capnp compile -ogo aircraft.capnp 3 | 4 | aircraft.capnp.go: aircraft.capnp 5 | capnp compile -ogo aircraft.capnp 6 | 7 | clean: 8 | rm -f aircraft.capnp.go *~ 9 | 10 | -------------------------------------------------------------------------------- /aircraftlib/aircraft.capnp: -------------------------------------------------------------------------------- 1 | using Go = import "go.capnp"; 2 | 3 | $Go.package("aircraftlib"); 4 | $Go.import("go-capnproto/aircraftlib"); 5 | 6 | @0x832bcc6686a26d56; 7 | 8 | struct Zdate { 9 | year @0 :Int16; 10 | month @1 :UInt8; 11 | day @2 :UInt8; 12 | } 13 | 14 | struct Zdata { 15 | data @0 :Data; 16 | } 17 | 18 | 19 | enum Airport { 20 | none @0; 21 | jfk @1; 22 | lax @2; 23 | sfo @3; 24 | luv @4; 25 | dfw @5; 26 | test @6; 27 | # test must be last because we use it to count 28 | # the number of elements in the Airport enum. 29 | } 30 | 31 | struct PlaneBase { 32 | name @0: Text; 33 | homes @1: List(Airport); 34 | rating @2: Int64; 35 | canFly @3: Bool; 36 | capacity @4: Int64; 37 | maxSpeed @5: Float64; 38 | } 39 | 40 | struct B737 { 41 | base @0: PlaneBase; 42 | } 43 | 44 | struct A320 { 45 | base @0: PlaneBase; 46 | } 47 | 48 | struct F16 { 49 | base @0: PlaneBase; 50 | } 51 | 52 | 53 | # need a struct with at least two pointers to catch certain bugs 54 | struct Regression { 55 | base @0: PlaneBase; 56 | b0 @1: Float64; # intercept 57 | beta @2: List(Float64); 58 | planes @3: List(Aircraft); 59 | ymu @4: Float64; # y-mean in original space 60 | ysd @5: Float64; # y-standard deviation in original space 61 | } 62 | 63 | 64 | 65 | struct Aircraft { 66 | # so we can restrict 67 | # and specify a Plane is required in 68 | # certain places. 69 | 70 | union { 71 | void @0: Void; # @0 will be the default, so always make @0 a Void. 72 | b737 @1: B737; 73 | a320 @2: A320; 74 | f16 @3: F16; 75 | } 76 | } 77 | 78 | 79 | struct Z { 80 | # Z must contain all types, as this is our 81 | # runtime type identification. It is a thin shim. 82 | 83 | union { 84 | void @0: Void; # always first in any union. 85 | zz @1: Z; # any. fyi, this can't be 'z' alone. 86 | 87 | f64 @2: Float64; 88 | f32 @3: Float32; 89 | 90 | i64 @4: Int64; 91 | i32 @5: Int32; 92 | i16 @6: Int16; 93 | i8 @7: Int8; 94 | 95 | u64 @8: UInt64; 96 | u32 @9: UInt32; 97 | u16 @10: UInt16; 98 | u8 @11: UInt8; 99 | 100 | bool @12: Bool; 101 | text @13: Text; 102 | blob @14: Data; 103 | 104 | f64vec @15: List(Float64); 105 | f32vec @16: List(Float32); 106 | 107 | i64vec @17: List(Int64); 108 | i32vec @18: List(Int32); 109 | i16vec @19: List(Int16); 110 | i8vec @20: List(Int8); 111 | 112 | u64vec @21: List(UInt64); 113 | u32vec @22: List(UInt32); 114 | u16vec @23: List(UInt16); 115 | u8vec @24: List(UInt8); 116 | 117 | zvec @25: List(Z); 118 | zvecvec @26: List(List(Z)); 119 | 120 | zdate @27: Zdate; 121 | zdata @28: Zdata; 122 | 123 | aircraftvec @29: List(Aircraft); 124 | aircraft @30: Aircraft; 125 | regression @31: Regression; 126 | planebase @32: PlaneBase; 127 | airport @33: Airport; 128 | b737 @34: B737; 129 | a320 @35: A320; 130 | f16 @36: F16; 131 | zdatevec @37: List(Zdate); 132 | zdatavec @38: List(Zdata); 133 | 134 | boolvec @39: List(Bool); 135 | } 136 | } 137 | 138 | # tests for Text/List(Text) recusion handling 139 | 140 | struct Counter { 141 | size @0: Int64; 142 | words @1: Text; 143 | wordlist @2: List(Text); 144 | } 145 | 146 | struct Bag { 147 | counter @0: Counter; 148 | } 149 | 150 | struct Zserver { 151 | waitingjobs @0: List(Zjob); 152 | } 153 | 154 | struct Zjob { 155 | cmd @0: Text; 156 | args @1: List(Text); 157 | } 158 | 159 | # versioning test structs 160 | 161 | struct VerEmpty { 162 | } 163 | 164 | struct VerOneData { 165 | val @0: Int16; 166 | } 167 | 168 | struct VerTwoData { 169 | val @0: Int16; 170 | duo @1: Int64; 171 | } 172 | 173 | struct VerOnePtr { 174 | ptr @0: VerOneData; 175 | } 176 | 177 | struct VerTwoPtr { 178 | ptr1 @0: VerOneData; 179 | ptr2 @1: VerOneData; 180 | } 181 | 182 | struct VerTwoDataTwoPtr { 183 | val @0: Int16; 184 | duo @1: Int64; 185 | ptr1 @2: VerOneData; 186 | ptr2 @3: VerOneData; 187 | } 188 | 189 | struct HoldsVerEmptyList { 190 | mylist @0: List(VerEmpty); 191 | } 192 | 193 | struct HoldsVerOneDataList { 194 | mylist @0: List(VerOneData); 195 | } 196 | 197 | struct HoldsVerTwoDataList { 198 | mylist @0: List(VerTwoData); 199 | } 200 | 201 | struct HoldsVerOnePtrList { 202 | mylist @0: List(VerOnePtr); 203 | } 204 | 205 | struct HoldsVerTwoPtrList { 206 | mylist @0: List(VerTwoPtr); 207 | } 208 | 209 | struct HoldsVerTwoTwoList { 210 | mylist @0: List(VerTwoDataTwoPtr); 211 | } 212 | 213 | struct HoldsVerTwoTwoPlus { 214 | mylist @0: List(VerTwoTwoPlus); 215 | } 216 | 217 | struct VerTwoTwoPlus { 218 | val @0: Int16; 219 | duo @1: Int64; 220 | ptr1 @2: VerTwoDataTwoPtr; 221 | ptr2 @3: VerTwoDataTwoPtr; 222 | tre @4: Int64; 223 | lst3 @5: List(Int64); 224 | } 225 | 226 | # text handling 227 | 228 | struct HoldsText { 229 | txt @0: Text; 230 | lst @1: List(Text); 231 | lstlst @2: List(List(Text)); 232 | } 233 | 234 | # test that we avoid unnecessary truncation 235 | 236 | struct WrapEmpty { 237 | mightNotBeReallyEmpty @0: VerEmpty; 238 | } 239 | 240 | struct Wrap2x2 { 241 | mightNotBeReallyEmpty @0: VerTwoDataTwoPtr; 242 | } 243 | 244 | struct Wrap2x2plus { 245 | mightNotBeReallyEmpty @0: VerTwoTwoPlus; 246 | } 247 | 248 | # test customtype annotation for Data 249 | 250 | struct Endpoint { 251 | ip @0: Data $Go.customtype("net.IP"); 252 | port @1: Int16; 253 | hostname @2: Text; 254 | } 255 | 256 | # test voids in a union 257 | 258 | struct VoidUnion { 259 | union { 260 | a @0 :Void; 261 | b @1 :Void; 262 | } 263 | } 264 | 265 | # test List(List(Struct(List))) 266 | 267 | struct Nester1Capn { 268 | strs @0: List(Text); 269 | } 270 | 271 | struct RWTestCapn { 272 | nestMatrix @0: List(List(Nester1Capn)); 273 | } 274 | 275 | struct ListStructCapn { 276 | vec @0: List(Nester1Capn); 277 | } 278 | 279 | # test transforms 280 | 281 | struct StackingRoot { 282 | a @1 :StackingA; 283 | aWithDefault @0 :StackingA = (num = 42); 284 | } 285 | 286 | struct StackingA { 287 | num @0 :Int32; 288 | b @1 :StackingB; 289 | } 290 | 291 | struct StackingB { 292 | num @0 :Int32; 293 | } 294 | -------------------------------------------------------------------------------- /aircraftlib/go.capnp: -------------------------------------------------------------------------------- 1 | @0xd12a1c51fedd6c88; 2 | annotation package(file) :Text; 3 | annotation import(file) :Text; 4 | annotation doc(struct, field, enum) :Text; 5 | annotation tag(enumerant) : Text; 6 | annotation notag(enumerant) : Void; 7 | annotation customtype(field) : Text; 8 | $import("github.com/glycerine/go-capnproto"); 9 | $package("capn"); 10 | -------------------------------------------------------------------------------- /bin/dec: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | capnp decode --short aircraftlib/aircraft.capnp Z 3 | -------------------------------------------------------------------------------- /bin/decp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | capnp decode --packed --short aircraftlib/aircraft.capnp Z 3 | -------------------------------------------------------------------------------- /bin/enc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | capnp encode aircraftlib/aircraft.capnp Z 3 | -------------------------------------------------------------------------------- /bin/encp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | capnp encode --packed aircraftlib/aircraft.capnp Z 3 | -------------------------------------------------------------------------------- /boollist_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | capn "github.com/glycerine/go-capnproto" 9 | air "github.com/glycerine/go-capnproto/aircraftlib" 10 | cv "github.com/glycerine/goconvey/convey" 11 | ) 12 | 13 | func ValAtBit(value int64, bitPosition uint) bool { 14 | return (int64(1)< 0 { 75 | for i := uint(0); i < elementCount; i++ { 76 | list.Set(int(i), ValAtBit(value, i)) 77 | } 78 | } 79 | z.SetBoolvec(list) 80 | 81 | buf := bytes.Buffer{} 82 | seg.WriteTo(&buf) 83 | return seg, buf.Bytes() 84 | } 85 | 86 | func TestBitList(t *testing.T) { 87 | seg, _ := zboolvec_value_FilledSegment(5, 3) 88 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z") 89 | 90 | expectedText := `(boolvec = [true, false, true])` 91 | 92 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [true, false, true]", t, func() { 93 | cv.Convey("When we decode it with capnp", func() { 94 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 95 | cv.So(text, cv.ShouldEqual, expectedText) 96 | }) 97 | cv.Convey("And our data should contain Z_BOOLVEC with contents true, false, true", func() { 98 | z := air.ReadRootZ(seg) 99 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 100 | 101 | var bitlist = z.Boolvec() 102 | cv.So(bitlist.Len(), cv.ShouldEqual, 3) 103 | cv.So(bitlist.At(0), cv.ShouldEqual, true) 104 | cv.So(bitlist.At(1), cv.ShouldEqual, false) 105 | cv.So(bitlist.At(2), cv.ShouldEqual, true) 106 | }) 107 | }) 108 | }) 109 | 110 | } 111 | 112 | func TestWriteBitList0(t *testing.T) { 113 | seg, _ := zboolvec_value_FilledSegment(0, 1) 114 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [false]", t, func() { 115 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 116 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [false])`) 117 | }) 118 | }) 119 | 120 | cv.Convey("And we should be able to read back what we wrote", t, func() { 121 | z := air.ReadRootZ(seg) 122 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 123 | 124 | var bitlist = z.Boolvec() 125 | cv.So(bitlist.Len(), cv.ShouldEqual, 1) 126 | cv.So(bitlist.At(0), cv.ShouldEqual, false) 127 | }) 128 | } 129 | 130 | func TestWriteBitList1(t *testing.T) { 131 | seg, _ := zboolvec_value_FilledSegment(1, 1) 132 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [true]", t, func() { 133 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 134 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [true])`) 135 | }) 136 | }) 137 | 138 | cv.Convey("And we should be able to read back what we wrote", t, func() { 139 | z := air.ReadRootZ(seg) 140 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 141 | 142 | var bitlist = z.Boolvec() 143 | cv.So(bitlist.Len(), cv.ShouldEqual, 1) 144 | cv.So(bitlist.At(0), cv.ShouldEqual, true) 145 | }) 146 | 147 | } 148 | 149 | func TestWriteBitList2(t *testing.T) { 150 | seg, _ := zboolvec_value_FilledSegment(2, 2) 151 | //seg, by := zboolvec_value_FilledSegment(2, 2) 152 | //ShowBytes(by, 0) 153 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [false, true]", t, func() { 154 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 155 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [false, true])`) 156 | }) 157 | }) 158 | 159 | cv.Convey("And we should be able to read back what we wrote", t, func() { 160 | z := air.ReadRootZ(seg) 161 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 162 | 163 | var bitlist = z.Boolvec() 164 | cv.So(bitlist.Len(), cv.ShouldEqual, 2) 165 | cv.So(bitlist.At(0), cv.ShouldEqual, false) 166 | cv.So(bitlist.At(1), cv.ShouldEqual, true) 167 | }) 168 | } 169 | 170 | func TestWriteBitList3(t *testing.T) { 171 | seg, _ := zboolvec_value_FilledSegment(3, 2) 172 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [true, true]", t, func() { 173 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 174 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [true, true])`) 175 | }) 176 | }) 177 | 178 | cv.Convey("And we should be able to read back what we wrote", t, func() { 179 | z := air.ReadRootZ(seg) 180 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 181 | 182 | var bitlist = z.Boolvec() 183 | cv.So(bitlist.Len(), cv.ShouldEqual, 2) 184 | cv.So(bitlist.At(0), cv.ShouldEqual, true) 185 | cv.So(bitlist.At(1), cv.ShouldEqual, true) 186 | }) 187 | 188 | } 189 | 190 | func TestWriteBitList4(t *testing.T) { 191 | seg, _ := zboolvec_value_FilledSegment(4, 3) 192 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [false, false, true]", t, func() { 193 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 194 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [false, false, true])`) 195 | }) 196 | }) 197 | 198 | cv.Convey("And we should be able to read back what we wrote", t, func() { 199 | z := air.ReadRootZ(seg) 200 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 201 | 202 | var bitlist = z.Boolvec() 203 | cv.So(bitlist.Len(), cv.ShouldEqual, 3) 204 | cv.So(bitlist.At(0), cv.ShouldEqual, false) 205 | cv.So(bitlist.At(1), cv.ShouldEqual, false) 206 | cv.So(bitlist.At(2), cv.ShouldEqual, true) 207 | }) 208 | } 209 | 210 | func TestWriteBitList21(t *testing.T) { 211 | seg, _ := zboolvec_value_FilledSegment(21, 5) 212 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [true, false, true, false, true]", t, func() { 213 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 214 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [true, false, true, false, true])`) 215 | }) 216 | }) 217 | 218 | cv.Convey("And we should be able to read back what we wrote", t, func() { 219 | z := air.ReadRootZ(seg) 220 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 221 | 222 | var bitlist = z.Boolvec() 223 | cv.So(bitlist.Len(), cv.ShouldEqual, 5) 224 | cv.So(bitlist.At(0), cv.ShouldEqual, true) 225 | cv.So(bitlist.At(1), cv.ShouldEqual, false) 226 | cv.So(bitlist.At(2), cv.ShouldEqual, true) 227 | cv.So(bitlist.At(3), cv.ShouldEqual, false) 228 | cv.So(bitlist.At(4), cv.ShouldEqual, true) 229 | }) 230 | } 231 | 232 | func TestWriteBitListTwo64BitWords(t *testing.T) { 233 | 234 | seg := capn.NewBuffer(nil) 235 | z := air.NewRootZ(seg) 236 | list := seg.NewBitList(66) 237 | list.Set(64, true) 238 | list.Set(65, true) 239 | 240 | z.SetBoolvec(list) 241 | 242 | buf := bytes.Buffer{} 243 | seg.WriteTo(&buf) 244 | 245 | cv.Convey("Given a go-capnproto created List(Bool) Z::boolvec with bool values [true (+ 64 more times)]", t, func() { 246 | cv.Convey("Decoding it with c++ capnp should yield the expected text", func() { 247 | cv.So(CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z"), cv.ShouldEqual, `(boolvec = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true])`) 248 | }) 249 | }) 250 | 251 | cv.Convey("And we should be able to read back what we wrote", t, func() { 252 | z := air.ReadRootZ(seg) 253 | cv.So(z.Which(), cv.ShouldEqual, air.Z_BOOLVEC) 254 | 255 | var bitlist = z.Boolvec() 256 | cv.So(bitlist.Len(), cv.ShouldEqual, 66) 257 | 258 | for i := 0; i < 64; i++ { 259 | cv.So(bitlist.At(i), cv.ShouldEqual, false) 260 | } 261 | cv.So(bitlist.At(64), cv.ShouldEqual, true) 262 | cv.So(bitlist.At(65), cv.ShouldEqual, true) 263 | }) 264 | } 265 | 266 | /* 267 | expected binaries: see data/bvec*.bin 268 | 269 | jaten@i7:~/cap:master$ echo "(boolvec = [true, false, true])" | capnp encode aircraftlib/aircraft.capnp Z > bvec5.bin 270 | jaten@i7:~/cap:master$ echo "(boolvec = [true, true])" | capnp encode aircraftlib/aircraft.capnp Z > bvec3.bin 271 | jaten@i7:~/cap:master$ echo "(boolvec = [true])" | capnp encode aircraftlib/aircraft.capnp Z > bvec1.bin 272 | jaten@i7:~/cap:master$ echo "(boolvec = [false])" | capnp encode aircraftlib/aircraft.capnp Z > bvec0.bin 273 | 274 | jaten@i7:~/cap:master$ xxd -g 1 -c 8 bvec0.bin 275 | 0000000: 00 00 00 00 05 00 00 00 ........ 276 | 0000008: 00 00 00 00 02 00 01 00 ........ 277 | 0000010: 27 00 00 00 00 00 00 00 '....... 278 | 0000018: 00 00 00 00 00 00 00 00 ........ 279 | 0000020: 01 00 00 00 09 00 00 00 ........ 280 | 0000028: 00 00 00 00 00 00 00 00 ........ 281 | 282 | jaten@i7:~/cap:master$ xxd -g 1 -c 8 bvec1.bin 283 | 0000000: 00 00 00 00 05 00 00 00 ........ 284 | 0000008: 00 00 00 00 02 00 01 00 ........ 285 | 0000010: 27 00 00 00 00 00 00 00 '....... 286 | 0000018: 00 00 00 00 00 00 00 00 ........ 287 | 0000020: 01 00 00 00 09 00 00 00 ........ 288 | 0000028: 01 00 00 00 00 00 00 00 ........ 289 | 290 | jaten@i7:~/cap:master$ xxd -g 1 -c 8 bvec3.bin 291 | 0000000: 00 00 00 00 05 00 00 00 ........ 292 | 0000008: 00 00 00 00 02 00 01 00 ........ 293 | 0000010: 27 00 00 00 00 00 00 00 '....... 294 | 0000018: 00 00 00 00 00 00 00 00 ........ 295 | 0000020: 01 00 00 00 11 00 00 00 ........ 296 | 0000028: 03 00 00 00 00 00 00 00 ........ 297 | 298 | jaten@i7:~/cap:master$ xxd -g 1 -c 8 bvec5.bin 299 | 0000000: 00 00 00 00 05 00 00 00 ........ 300 | 0000008: 00 00 00 00 02 00 01 00 ........ 301 | 0000010: 27 00 00 00 00 00 00 00 '....... 302 | 0000018: 00 00 00 00 00 00 00 00 ........ 303 | 0000020: 01 00 00 00 19 00 00 00 ........ 304 | 0000028: 05 00 00 00 00 00 00 00 ........ 305 | 306 | */ 307 | -------------------------------------------------------------------------------- /capnpc-go/.gitignore: -------------------------------------------------------------------------------- 1 | /capnpc-go 2 | -------------------------------------------------------------------------------- /capnpc-go/caplit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | // define the Caplit capnproto literal representation 10 | // producing functions. Initially adapted from the WriteJSON implementation. 11 | 12 | func (n *node) defineTypeCaplitFuncs(w io.Writer) { 13 | g_imported["io"] = true 14 | g_imported["bufio"] = true 15 | g_imported["bytes"] = true 16 | 17 | fprintf(w, "func (s %s) WriteCapLit(w io.Writer) error {\n", n.name) 18 | fprintf(w, "b := bufio.NewWriter(w);") 19 | fprintf(w, "var err error;") 20 | fprintf(w, "var buf []byte;") 21 | fprintf(w, "_ = buf;") 22 | 23 | switch n.Which() { 24 | case NODE_ENUM: 25 | n.caplitEnum(w) 26 | case NODE_STRUCT: 27 | n.caplitStruct(w) 28 | } 29 | 30 | fprintf(w, "err = b.Flush(); return err\n};\n") 31 | 32 | fprintf(w, "func (s %s) MarshalCapLit() ([]byte, error) {\n", n.name) 33 | fprintf(w, "b := bytes.Buffer{}; err := s.WriteCapLit(&b); return b.Bytes(), err };") 34 | } 35 | 36 | func (n *node) caplitEnum(w io.Writer) { 37 | fprintf(w, "_, err = b.WriteString(s.String());") 38 | writeErrCheck(w) 39 | } 40 | 41 | // Write statements that will write a caplit struct 42 | func (n *node) caplitStruct(w io.Writer) { 43 | fprintf(w, `err = b.WriteByte('(');`) 44 | writeErrCheck(w) 45 | for i, f := range n.codeOrderFields() { 46 | if f.DiscriminantValue() != 0xFFFF { 47 | enumname := fmt.Sprintf("%s_%s", strings.ToUpper(n.name), strings.ToUpper(f.Name())) 48 | fprintf(w, "if s.Which() == %s {", enumname) 49 | } else if i != 0 { 50 | fprintf(w, ` 51 | _, err = b.WriteString(", "); 52 | `) 53 | writeErrCheck(w) 54 | } 55 | 56 | fprintf(w, `_, err = b.WriteString("%s = ");`, f.Name()) 57 | writeErrCheck(w) 58 | f.caplit(w) 59 | if f.DiscriminantValue() != 0xFFFF { 60 | fprintf(w, "};") 61 | } 62 | } 63 | fprintf(w, `err = b.WriteByte(')');`) 64 | writeErrCheck(w) 65 | } 66 | 67 | // This function writes statements that write the field's caplit representation to the bufio. 68 | func (f *Field) caplit(w io.Writer) { 69 | 70 | switch f.Which() { 71 | case FIELD_SLOT: 72 | fs := f.Slot() 73 | // we don't generate setters for Void fields 74 | if fs.Type().Which() == TYPE_VOID { 75 | fs.Type().caplit(w) 76 | return 77 | } 78 | fprintf(w, "{ s := s.%s(); ", title(f.Name())) 79 | fs.Type().caplit(w) 80 | fprintf(w, "}; ") 81 | case FIELD_GROUP: 82 | tid := f.Group().TypeId() 83 | n := findNode(tid) 84 | fprintf(w, "{ s := s.%s();", title(f.Name())) 85 | 86 | n.caplitStruct(w) 87 | fprintf(w, "};") 88 | } 89 | } 90 | 91 | func (t Type) caplit(w io.Writer) { 92 | switch t.Which() { 93 | case TYPE_UINT8, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, 94 | TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_INT64, 95 | TYPE_FLOAT32, TYPE_FLOAT64, TYPE_BOOL, TYPE_TEXT, TYPE_DATA: 96 | g_imported["encoding/json"] = true 97 | fprintf(w, "buf, err = json.Marshal(s);") 98 | writeErrCheck(w) 99 | fprintf(w, "_, err = b.Write(buf);") 100 | writeErrCheck(w) 101 | case TYPE_ENUM, TYPE_STRUCT: 102 | // since we handle groups at the field level, only named struct types make it in here 103 | // so we can just call the named structs caplit dumper 104 | fprintf(w, "err = s.WriteCapLit(b);") 105 | writeErrCheck(w) 106 | case TYPE_LIST: 107 | typ := t.List().ElementType() 108 | which := typ.Which() 109 | if which == TYPE_LIST || which == TYPE_ANYPOINTER { 110 | // untyped list, cant do anything but report 111 | // that a field existed. 112 | // 113 | // s will be unused in this case, so ignore 114 | fprintf(w, `_ = s;`) 115 | fprintf(w, `_, err = b.WriteString("\"untyped list\"");`) 116 | writeErrCheck(w) 117 | return 118 | } 119 | fprintf(w, "{ err = b.WriteByte('[');") 120 | writeErrCheck(w) 121 | fprintf(w, "for i, s := range s.ToArray() {") 122 | fprintf(w, `if i != 0 { _, err = b.WriteString(", "); };`) 123 | writeErrCheck(w) 124 | typ.caplit(w) 125 | fprintf(w, "}; err = b.WriteByte(']'); };") 126 | writeErrCheck(w) 127 | case TYPE_VOID: 128 | fprintf(w, `_ = s;`) 129 | fprintf(w, `_, err = b.WriteString("null");`) 130 | writeErrCheck(w) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /capnpc-go/capnpc-go.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "math" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "strconv" 12 | "strings" 13 | 14 | C "github.com/glycerine/go-capnproto" 15 | ) 16 | 17 | var ( 18 | go_capnproto_import = "github.com/glycerine/go-capnproto" 19 | fprintf = fmt.Fprintf 20 | sprintf = fmt.Sprintf 21 | title = strings.Title 22 | ) 23 | 24 | var g_nodes = make(map[uint64]*node) 25 | var g_imported map[string]bool 26 | var g_segment *C.Segment 27 | var g_bufname string 28 | 29 | type node struct { 30 | Node 31 | pkg string 32 | imp string 33 | nodes []*node 34 | name string 35 | } 36 | 37 | func assert(chk bool, format string, a ...interface{}) { 38 | if !chk { 39 | panic(sprintf(format, a...)) 40 | os.Exit(1) 41 | } 42 | } 43 | 44 | func copyData(obj C.Object) int { 45 | r, off, err := g_segment.NewRoot() 46 | assert(err == nil, "%v\n", err) 47 | err = r.Set(0, obj) 48 | assert(err == nil, "%v\n", err) 49 | return off 50 | } 51 | 52 | func findNode(id uint64) *node { 53 | n := g_nodes[id] 54 | assert(n != nil, "could not find node 0x%x\n", id) 55 | return n 56 | } 57 | 58 | func (n *node) remoteScope(from *node) string { 59 | assert(n.pkg != "", "missing package declaration for %s", n.DisplayName()) 60 | assert(n.imp != "", "missing import declaration for %s", n.DisplayName()) 61 | assert(from.imp != "", "missing import declaration for %s", from.DisplayName()) 62 | 63 | if n.imp == from.imp { 64 | return "" 65 | } else { 66 | g_imported[n.imp] = true 67 | return n.pkg + "." 68 | } 69 | } 70 | 71 | func (n *node) remoteName(from *node) string { 72 | return n.remoteScope(from) + n.name 73 | } 74 | 75 | func (n *node) resolveName(base, name string, file *node) { 76 | if na := nameAnnotation(n.Annotations()); na != "" { 77 | name = na 78 | } 79 | if base != "" { 80 | n.name = base + title(name) 81 | } else { 82 | n.name = title(name) 83 | } 84 | 85 | n.pkg = file.pkg 86 | n.imp = file.imp 87 | 88 | if n.Which() != NODE_STRUCT || !n.Struct().IsGroup() { 89 | file.nodes = append(file.nodes, n) 90 | } 91 | 92 | for _, nn := range n.NestedNodes().ToArray() { 93 | if ni := g_nodes[nn.Id()]; ni != nil { 94 | ni.resolveName(n.name, nn.Name(), file) 95 | } 96 | } 97 | 98 | if n.Which() == NODE_STRUCT { 99 | for _, f := range n.Struct().Fields().ToArray() { 100 | if f.Which() == FIELD_GROUP { 101 | gname := f.Name() 102 | if na := nameAnnotation(f.Annotations()); na != "" { 103 | gname = na 104 | } 105 | findNode(f.Group().TypeId()).resolveName(n.name, gname, file) 106 | } 107 | } 108 | } 109 | } 110 | 111 | func nameAnnotation(annotations Annotation_List) string { 112 | for _, a := range annotations.ToArray() { 113 | if a.Id() == C.Name { 114 | if name := a.Value().Text(); name != "" { 115 | return name 116 | } 117 | } 118 | } 119 | return "" 120 | } 121 | 122 | type enumval struct { 123 | Enumerant 124 | val int 125 | name string 126 | tag string 127 | parent *node 128 | } 129 | 130 | func (e *enumval) fullName() string { 131 | return fmt.Sprintf("%s_%s", strings.ToUpper(e.parent.name), strings.ToUpper(e.name)) 132 | } 133 | 134 | func (n *node) defineEnum(w io.Writer) { 135 | for _, a := range n.Annotations().ToArray() { 136 | if a.Id() == C.Doc { 137 | fprintf(w, "// %s\n", a.Value().Text()) 138 | } 139 | } 140 | fprintf(w, "type %s uint16\n", n.name) 141 | 142 | if es := n.Enum().Enumerants(); es.Len() > 0 { 143 | fprintf(w, "const (\n") 144 | 145 | ev := make([]enumval, es.Len()) 146 | for i := 0; i < es.Len(); i++ { 147 | e := es.At(i) 148 | ename := e.Name() 149 | if an := nameAnnotation(e.Annotations()); an != "" { 150 | ename = an 151 | } 152 | 153 | t := ename 154 | for _, an := range e.Annotations().ToArray() { 155 | if an.Id() == C.Tag { 156 | t = an.Value().Text() 157 | } else if an.Id() == C.Notag { 158 | t = "" 159 | } 160 | } 161 | ev[e.CodeOrder()] = enumval{e, i, ename, t, n} 162 | } 163 | 164 | // not an iota, so type has to go on each line 165 | for _, e := range ev { 166 | fprintf(w, "%s %s = %d\n", e.fullName(), n.name, e.val) 167 | } 168 | 169 | fprintf(w, ")\n") 170 | 171 | fprintf(w, "func (c %s) String() string {\n", n.name) 172 | fprintf(w, "switch c {\n") 173 | for _, e := range ev { 174 | if e.tag != "" { 175 | fprintf(w, "case %s: return \"%s\"\n", e.fullName(), e.tag) 176 | } 177 | } 178 | fprintf(w, "default: return \"\"\n") 179 | fprintf(w, "}\n}\n\n") 180 | 181 | fprintf(w, "func %sFromString(c string) %s {\n", n.name, n.name) 182 | fprintf(w, "switch c {\n") 183 | for _, e := range ev { 184 | if e.tag != "" { 185 | fprintf(w, "case \"%s\": return %s\n", e.tag, e.fullName()) 186 | } 187 | } 188 | fprintf(w, "default: return 0\n") 189 | fprintf(w, "}\n}\n") 190 | } 191 | 192 | fprintf(w, "type %s_List C.PointerList\n", n.name) 193 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewUInt16List(sz)) }\n", n.name, n.name, n.name) 194 | fprintf(w, "func (s %s_List) Len() int { return C.UInt16List(s).Len() }\n", n.name) 195 | fprintf(w, "func (s %s_List) At(i int) %s { return %s(C.UInt16List(s).At(i)) }\n", n.name, n.name, n.name) 196 | fprintf(w, "func (s %s_List) ToArray() []%s {\n", n.name, n.name) 197 | fprintf(w, "\tn := s.Len()\n") 198 | fprintf(w, "\ta := make([]%s, n)\n", n.name) 199 | fprintf(w, "\tfor i := 0; i < n; i++ { a[i] = s.At(i) }\n") 200 | fprintf(w, "\treturn a\n}\n") 201 | fprintf(w, "func (s %s_List) Set(i int, item %s) { C.UInt16List(s).Set(i, uint16(item)) }\n", n.name, n.name) 202 | } 203 | 204 | func (n *node) writeValue(w io.Writer, t Type, v Value) { 205 | switch t.Which() { 206 | case TYPE_VOID, TYPE_INTERFACE: 207 | fprintf(w, "C.Void{}") 208 | 209 | case TYPE_BOOL: 210 | assert(v.Which() == VALUE_BOOL, "expected bool value") 211 | if v.Bool() { 212 | fprintf(w, "true") 213 | } else { 214 | fprintf(w, "false") 215 | } 216 | 217 | case TYPE_INT8: 218 | assert(v.Which() == VALUE_INT8, "expected int8 value") 219 | fprintf(w, "int8(%d)", v.Int8()) 220 | 221 | case TYPE_UINT8: 222 | assert(v.Which() == VALUE_UINT8, "expected uint8 value") 223 | fprintf(w, "uint8(%d)", v.Uint8()) 224 | 225 | case TYPE_INT16: 226 | assert(v.Which() == VALUE_INT16, "expected int16 value") 227 | fprintf(w, "int16(%d)", v.Int16()) 228 | 229 | case TYPE_UINT16: 230 | assert(v.Which() == VALUE_UINT16, "expected uint16 value") 231 | fprintf(w, "uint16(%d)", v.Uint16()) 232 | 233 | case TYPE_INT32: 234 | assert(v.Which() == VALUE_INT32, "expected int32 value") 235 | fprintf(w, "int32(%d)", v.Int32()) 236 | 237 | case TYPE_UINT32: 238 | assert(v.Which() == VALUE_UINT32, "expected uint32 value") 239 | fprintf(w, "uint32(%d)", v.Uint32()) 240 | 241 | case TYPE_INT64: 242 | assert(v.Which() == VALUE_INT64, "expected int64 value") 243 | fprintf(w, "int64(%d)", v.Int64()) 244 | 245 | case TYPE_UINT64: 246 | assert(v.Which() == VALUE_UINT64, "expected uint64 value") 247 | fprintf(w, "uint64(%d)", v.Uint64()) 248 | 249 | case TYPE_FLOAT32: 250 | assert(v.Which() == VALUE_FLOAT32, "expected float32 value") 251 | fprintf(w, "math.Float32frombits(0x%x)", math.Float32bits(v.Float32())) 252 | g_imported["math"] = true 253 | 254 | case TYPE_FLOAT64: 255 | assert(v.Which() == VALUE_FLOAT64, "expected float64 value") 256 | fprintf(w, "math.Float64frombits(0x%x)", math.Float64bits(v.Float64())) 257 | g_imported["math"] = true 258 | 259 | case TYPE_TEXT: 260 | assert(v.Which() == VALUE_TEXT, "expected text value") 261 | fprintf(w, "%s", strconv.Quote(v.Text())) 262 | 263 | case TYPE_DATA: 264 | assert(v.Which() == VALUE_DATA, "expected data value") 265 | fprintf(w, "[]byte{") 266 | for i, b := range v.Data() { 267 | if i > 0 { 268 | fprintf(w, ", ") 269 | } 270 | fprintf(w, "%d", b) 271 | } 272 | fprintf(w, "}") 273 | 274 | case TYPE_ENUM: 275 | assert(v.Which() == VALUE_ENUM, "expected enum value") 276 | en := findNode(t.Enum().TypeId()) 277 | assert(en.Which() == NODE_ENUM, "expected enum type ID") 278 | ev := en.Enum().Enumerants() 279 | if val := int(v.Enum()); val >= ev.Len() { 280 | fprintf(w, "%s(%d)", en.remoteName(n), val) 281 | } else { 282 | fprintf(w, "%s%s", en.remoteScope(n), ev.At(val).Name()) 283 | } 284 | 285 | case TYPE_STRUCT: 286 | fprintf(w, "%s(%s.Root(%d))", findNode(t.Struct().TypeId()).remoteName(n), g_bufname, copyData(v.Struct())) 287 | 288 | case TYPE_ANYPOINTER: 289 | fprintf(w, "%s.Root(%d)", g_bufname, copyData(v.AnyPointer())) 290 | 291 | case TYPE_LIST: 292 | assert(v.Which() == VALUE_LIST, "expected list value") 293 | 294 | switch lt := t.List().ElementType(); lt.Which() { 295 | case TYPE_VOID, TYPE_INTERFACE: 296 | fprintf(w, "make([]C.Void, %d)", v.List().ToVoidList().Len()) 297 | case TYPE_BOOL: 298 | fprintf(w, "C.BitList(%s.Root(%d))", g_bufname, copyData(v.List())) 299 | case TYPE_INT8: 300 | fprintf(w, "C.Int8List(%s.Root(%d))", g_bufname, copyData(v.List())) 301 | case TYPE_UINT8: 302 | fprintf(w, "C.UInt8List(%s.Root(%d))", g_bufname, copyData(v.List())) 303 | case TYPE_INT16: 304 | fprintf(w, "C.Int16List(%s.Root(%d))", g_bufname, copyData(v.List())) 305 | case TYPE_UINT16: 306 | fprintf(w, "C.UInt16List(%s.Root(%d))", g_bufname, copyData(v.List())) 307 | case TYPE_INT32: 308 | fprintf(w, "C.Int32List(%s.Root(%d))", g_bufname, copyData(v.List())) 309 | case TYPE_UINT32: 310 | fprintf(w, "C.UInt32List(%s.Root(%d))", g_bufname, copyData(v.List())) 311 | case TYPE_FLOAT32: 312 | fprintf(w, "C.Float32List(%s.Root(%d))", g_bufname, copyData(v.List())) 313 | case TYPE_INT64: 314 | fprintf(w, "C.Int64List(%s.Root(%d))", g_bufname, copyData(v.List())) 315 | case TYPE_UINT64: 316 | fprintf(w, "C.UInt64List(%s.Root(%d))", g_bufname, copyData(v.List())) 317 | case TYPE_FLOAT64: 318 | fprintf(w, "C.Float64List(%s.Root(%d))", g_bufname, copyData(v.List())) 319 | case TYPE_TEXT: 320 | fprintf(w, "C.TextList(%s.Root(%d))", g_bufname, copyData(v.List())) 321 | case TYPE_DATA: 322 | fprintf(w, "C.DataList(%s.Root(%d))", g_bufname, copyData(v.List())) 323 | case TYPE_ENUM: 324 | fprintf(w, "%s_List(%s.Root(%d))", findNode(lt.Enum().TypeId()).remoteName(n), g_bufname, copyData(v.List())) 325 | case TYPE_STRUCT: 326 | fprintf(w, "%s_List(%s.Root(%d))", findNode(lt.Struct().TypeId()).remoteName(n), g_bufname, copyData(v.List())) 327 | case TYPE_LIST, TYPE_ANYPOINTER: 328 | fprintf(w, "C.PointerList(%s.Root(%d))", g_bufname, copyData(v.List())) 329 | } 330 | } 331 | } 332 | 333 | func (n *node) defineAnnotation(w io.Writer) { 334 | fprintf(w, "const %s = uint64(0x%x)\n", n.name, n.Id()) 335 | } 336 | 337 | func constIsVar(n *node) bool { 338 | switch n.Const().Type().Which() { 339 | case TYPE_BOOL, TYPE_INT8, TYPE_UINT8, TYPE_INT16, 340 | TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64, 341 | TYPE_UINT64, TYPE_TEXT, TYPE_ENUM: 342 | return false 343 | default: 344 | return true 345 | } 346 | } 347 | 348 | func defineConstNodes(w io.Writer, nodes []*node) { 349 | 350 | any := false 351 | 352 | for _, n := range nodes { 353 | if n.Which() == NODE_CONST && !constIsVar(n) { 354 | if !any { 355 | fprintf(w, "const (\n") 356 | any = true 357 | } 358 | fprintf(w, "%s = ", n.name) 359 | n.writeValue(w, n.Const().Type(), n.Const().Value()) 360 | fprintf(w, "\n") 361 | } 362 | } 363 | 364 | if any { 365 | fprintf(w, ")\n") 366 | } 367 | 368 | any = false 369 | 370 | for _, n := range nodes { 371 | if n.Which() == NODE_CONST && constIsVar(n) { 372 | if !any { 373 | fprintf(w, "var (\n") 374 | any = true 375 | } 376 | fprintf(w, "%s = ", n.name) 377 | n.writeValue(w, n.Const().Type(), n.Const().Value()) 378 | fprintf(w, "\n") 379 | } 380 | } 381 | 382 | if any { 383 | fprintf(w, ")\n") 384 | } 385 | } 386 | 387 | func (n *node) defineField(w io.Writer, f Field) { 388 | t := f.Slot().Type() 389 | def := f.Slot().DefaultValue() 390 | off := f.Slot().Offset() 391 | 392 | if t.Which() == TYPE_INTERFACE { 393 | return 394 | } 395 | 396 | fname := f.Name() 397 | if an := nameAnnotation(f.Annotations()); an != "" { 398 | fname = an 399 | } 400 | fname = title(fname) 401 | 402 | var g, s bytes.Buffer 403 | 404 | settag := "" 405 | if f.DiscriminantValue() != 0xFFFF { 406 | settag = sprintf(" C.Struct(s).Set16(%d, %d);", n.Struct().DiscriminantOffset()*2, f.DiscriminantValue()) 407 | if t.Which() == TYPE_VOID { 408 | fprintf(&s, "func (s %s) Set%s() {%s }\n", n.name, fname, settag) 409 | w.Write(s.Bytes()) 410 | return 411 | } 412 | } else if t.Which() == TYPE_VOID { 413 | return 414 | } 415 | 416 | customtype := "" 417 | for _, a := range f.Annotations().ToArray() { 418 | if a.Id() == C.Doc { 419 | fprintf(&g, "// %s\n", a.Value().Text()) 420 | } 421 | if a.Id() == C.Customtype { 422 | customtype = a.Value().Text() 423 | if i := strings.LastIndex(customtype, "."); i != -1 { 424 | g_imported[customtype[:i]] = true 425 | } 426 | } 427 | } 428 | fprintf(&g, "func (s %s) %s() ", n.name, fname) 429 | fprintf(&s, "func (s %s) Set%s", n.name, fname) 430 | 431 | switch t.Which() { 432 | case TYPE_BOOL: 433 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_BOOL, "expected bool default") 434 | if def.Which() == VALUE_BOOL && def.Bool() { 435 | fprintf(&g, "bool { return !C.Struct(s).Get1(%d) }\n", off) 436 | fprintf(&s, "(v bool) {%s C.Struct(s).Set1(%d, !v) }\n", settag, off) 437 | } else { 438 | fprintf(&g, "bool { return C.Struct(s).Get1(%d) }\n", off) 439 | fprintf(&s, "(v bool) {%s C.Struct(s).Set1(%d, v) }\n", settag, off) 440 | } 441 | 442 | case TYPE_INT8: 443 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_INT8, "expected int8 default") 444 | if def.Which() == VALUE_INT8 && def.Int8() != 0 { 445 | fprintf(&g, "int8 { return int8(C.Struct(s).Get8(%d)) ^ %d }\n", off, def.Int8()) 446 | fprintf(&s, "(v int8) {%s C.Struct(s).Set8(%d, uint8(v^%d)) }\n", settag, off, def.Int8()) 447 | } else { 448 | fprintf(&g, "int8 { return int8(C.Struct(s).Get8(%d)) }\n", off) 449 | fprintf(&s, "(v int8) {%s C.Struct(s).Set8(%d, uint8(v)) }\n", settag, off) 450 | } 451 | 452 | case TYPE_UINT8: 453 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_UINT8, "expected uint8 default") 454 | if def.Which() == VALUE_UINT8 && def.Uint8() != 0 { 455 | fprintf(&g, "uint8 { return C.Struct(s).Get8(%d) ^ %d }\n", off, def.Uint8()) 456 | fprintf(&s, "(v uint8) {%s C.Struct(s).Set8(%d, v^%d) }\n", settag, off, def.Uint8()) 457 | } else { 458 | fprintf(&g, "uint8 { return C.Struct(s).Get8(%d) }\n", off) 459 | fprintf(&s, "(v uint8) {%s C.Struct(s).Set8(%d, v) }\n", settag, off) 460 | } 461 | 462 | case TYPE_INT16: 463 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_INT16, "expected int16 default") 464 | if def.Which() == VALUE_INT16 && def.Int16() != 0 { 465 | fprintf(&g, "int16 { return int16(C.Struct(s).Get16(%d)) ^ %d }\n", off*2, def.Int16()) 466 | fprintf(&s, "(v int16) {%s C.Struct(s).Set16(%d, uint16(v^%d)) }\n", settag, off*2, def.Int16()) 467 | } else { 468 | fprintf(&g, "int16 { return int16(C.Struct(s).Get16(%d)) }\n", off*2) 469 | fprintf(&s, "(v int16) {%s C.Struct(s).Set16(%d, uint16(v)) }\n", settag, off*2) 470 | } 471 | 472 | case TYPE_UINT16: 473 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_UINT16, "expected uint16 default") 474 | if def.Which() == VALUE_UINT16 && def.Uint16() != 0 { 475 | fprintf(&g, "uint16 { return C.Struct(s).Get16(%d) ^ %d }\n", off*2, def.Uint16()) 476 | fprintf(&s, "(v uint16) {%s C.Struct(s).Set16(%d, v^%d) }\n", settag, off*2, def.Uint16()) 477 | } else { 478 | fprintf(&g, "uint16 { return C.Struct(s).Get16(%d) }\n", off*2) 479 | fprintf(&s, "(v uint16) {%s C.Struct(s).Set16(%d, v) }\n", settag, off*2) 480 | } 481 | 482 | case TYPE_INT32: 483 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_INT32, "expected int32 default") 484 | if def.Which() == VALUE_INT32 && def.Int32() != 0 { 485 | fprintf(&g, "int32 { return int32(C.Struct(s).Get32(%d)) ^ %d }\n", off*4, def.Int32()) 486 | fprintf(&s, "(v int32) {%s C.Struct(s).Set32(%d, uint32(v^%d)) }\n", settag, off*4, def.Int32()) 487 | } else { 488 | fprintf(&g, "int32 { return int32(C.Struct(s).Get32(%d)) }\n", off*4) 489 | fprintf(&s, "(v int32) {%s C.Struct(s).Set32(%d, uint32(v)) }\n", settag, off*4) 490 | } 491 | 492 | case TYPE_UINT32: 493 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_UINT32, "expected uint32 default") 494 | if def.Which() == VALUE_UINT32 && def.Uint32() != 0 { 495 | fprintf(&g, "uint32 { return C.Struct(s).Get32(%d) ^ %d }\n", off*4, def.Uint32()) 496 | fprintf(&s, "(v uint32) {%s C.Struct(s).Set32(%d, v^%d) }\n", settag, off*4, def.Uint32()) 497 | } else { 498 | fprintf(&g, "uint32 { return C.Struct(s).Get32(%d) }\n", off*4) 499 | fprintf(&s, "(v uint32) {%s C.Struct(s).Set32(%d, v) }\n", settag, off*4) 500 | } 501 | 502 | case TYPE_INT64: 503 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_INT64, "expected int64 default") 504 | if def.Which() == VALUE_INT64 && def.Int64() != 0 { 505 | fprintf(&g, "int64 { return int64(C.Struct(s).Get64(%d)) ^ %d }\n", off*8, def.Int64()) 506 | fprintf(&s, "(v int64) {%s C.Struct(s).Set64(%d, uint64(v^%d)) }\n", settag, off*8, def.Int64()) 507 | } else { 508 | fprintf(&g, "int64 { return int64(C.Struct(s).Get64(%d)) }\n", off*8) 509 | fprintf(&s, "(v int64) {%s C.Struct(s).Set64(%d, uint64(v)) }\n", settag, off*8) 510 | } 511 | 512 | case TYPE_UINT64: 513 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_UINT64, "expected uint64 default") 514 | if def.Which() == VALUE_UINT64 && def.Uint64() != 0 { 515 | fprintf(&g, "uint64 { return C.Struct(s).Get64(%d) ^ %d }\n", off*8, def.Uint64()) 516 | fprintf(&s, "(v uint64) {%s C.Struct(s).Set64(%d, v^%d) }\n", settag, off*8, def.Uint64()) 517 | } else { 518 | fprintf(&g, "uint64 { return C.Struct(s).Get64(%d) }\n", off*8) 519 | fprintf(&s, "(v uint64) {%s C.Struct(s).Set64(%d, v) }\n", settag, off*8) 520 | } 521 | 522 | case TYPE_FLOAT32: 523 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_FLOAT32, "expected float32 default") 524 | if def.Which() == VALUE_FLOAT32 && def.Float32() != 0 { 525 | fprintf(&g, "float32 { return math.Float32frombits(C.Struct(s).Get32(%d) ^ 0x%x) }\n", off*4, math.Float32bits(def.Float32())) 526 | fprintf(&s, "(v float32) {%s C.Struct(s).Set32(%d, math.Float32bits(v) ^ 0x%x) }\n", settag, off*4, math.Float32bits(def.Float32())) 527 | } else { 528 | fprintf(&g, "float32 { return math.Float32frombits(C.Struct(s).Get32(%d)) }\n", off*4) 529 | fprintf(&s, "(v float32) {%s C.Struct(s).Set32(%d, math.Float32bits(v)) }\n", settag, off*4) 530 | } 531 | g_imported["math"] = true 532 | 533 | case TYPE_FLOAT64: 534 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_FLOAT64, "expected float64 default") 535 | if def.Which() == VALUE_FLOAT64 && def.Float64() != 0 { 536 | fprintf(&g, "float64 { return math.Float64frombits(C.Struct(s).Get64(%d) ^ 0x%x) }\n", off*8, math.Float64bits(def.Float64())) 537 | fprintf(&s, "(v float64) {%s C.Struct(s).Set64(%d, math.Float64bits(v) ^ 0x%x) }\n", settag, off*8, math.Float64bits(def.Float64())) 538 | } else { 539 | fprintf(&g, "float64 { return math.Float64frombits(C.Struct(s).Get64(%d)) }\n", off*8) 540 | fprintf(&s, "(v float64) {%s C.Struct(s).Set64(%d, math.Float64bits(v)) }\n", settag, off*8) 541 | } 542 | g_imported["math"] = true 543 | 544 | case TYPE_TEXT: 545 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_TEXT, "expected text default") 546 | if def.Which() == VALUE_TEXT && def.Text() != "" { 547 | fprintf(&g, "string { return C.Struct(s).GetObject(%d).ToTextDefault(%s) }\n", off, strconv.Quote(def.Text())) 548 | } else { 549 | fprintf(&g, "string { return C.Struct(s).GetObject(%d).ToText() }\n", off) 550 | } 551 | fprintf(&s, "(v string) {%s C.Struct(s).SetObject(%d, s.Segment.NewText(v)) }\n", settag, off) 552 | 553 | // add the StringBytes() accessor to avoid copying the string around. 554 | // note that the bytes would have the \x00 byte at the end of 555 | // the string included in the []byte, so we use ToDataTrimLastByte() 556 | // instead of ToData() to avoid that. 557 | fprintf(&g, "func (s %s) %sBytes() []byte { return C.Struct(s).GetObject(%d).ToDataTrimLastByte() }\n", n.name, fname, off) 558 | 559 | case TYPE_DATA: 560 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_DATA, "expected data default") 561 | if def.Which() == VALUE_DATA && len(def.Data()) > 0 { 562 | dstr := "[]byte{" 563 | for i, b := range def.Data() { 564 | if i > 0 { 565 | dstr += ", " 566 | } 567 | dstr += sprintf("%d", b) 568 | } 569 | dstr += "}" 570 | if len(customtype) != 0 { 571 | fprintf(&g, "%s { return %s(C.Struct(s).GetObject(%d).ToDataDefault(%s)) }\n", customtype, customtype, off, dstr) 572 | } else { 573 | fprintf(&g, "[]byte { return C.Struct(s).GetObject(%d).ToDataDefault(%s) }\n", off, dstr) 574 | } 575 | } else { 576 | if len(customtype) != 0 { 577 | fprintf(&g, "%s { return %s(C.Struct(s).GetObject(%d).ToData()) }\n", customtype, customtype, off) 578 | } else { 579 | fprintf(&g, "[]byte { return C.Struct(s).GetObject(%d).ToData() }\n", off) 580 | } 581 | } 582 | if len(customtype) != 0 { 583 | fprintf(&s, "(v %s) {%s C.Struct(s).SetObject(%d, s.Segment.NewData([]byte(v))) }\n", customtype, settag, off) 584 | } else { 585 | fprintf(&s, "(v []byte) {%s C.Struct(s).SetObject(%d, s.Segment.NewData(v)) }\n", settag, off) 586 | } 587 | 588 | case TYPE_ENUM: 589 | ni := findNode(t.Enum().TypeId()) 590 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_ENUM, "expected enum default") 591 | if def.Which() == VALUE_ENUM && def.Enum() != 0 { 592 | fprintf(&g, "%s { return %s(C.Struct(s).Get16(%d) ^ %d) }\n", ni.remoteName(n), ni.remoteName(n), off*2, def.Enum()) 593 | fprintf(&s, "(v %s) {%s C.Struct(s).Set16(%d, uint16(v)^%d) }\n", ni.remoteName(n), settag, off*2, def.Uint16()) 594 | } else { 595 | fprintf(&g, "%s { return %s(C.Struct(s).Get16(%d)) }\n", ni.remoteName(n), ni.remoteName(n), off*2) 596 | fprintf(&s, "(v %s) {%s C.Struct(s).Set16(%d, uint16(v)) }\n", ni.remoteName(n), settag, off*2) 597 | } 598 | 599 | case TYPE_STRUCT: 600 | ni := findNode(t.Struct().TypeId()) 601 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_STRUCT, "expected struct default") 602 | if def.Which() == VALUE_STRUCT && def.Struct().HasData() { 603 | fprintf(&g, "%s { return %s(C.Struct(s).GetObject(%d).ToStructDefault(%s, %d)) }\n", 604 | ni.remoteName(n), ni.remoteName(n), off, g_bufname, copyData(def.Struct())) 605 | } else { 606 | fprintf(&g, "%s { return %s(C.Struct(s).GetObject(%d).ToStruct()) }\n", 607 | ni.remoteName(n), ni.remoteName(n), off) 608 | } 609 | fprintf(&s, "(v %s) {%s C.Struct(s).SetObject(%d, C.Object(v)) }\n", ni.remoteName(n), settag, off) 610 | 611 | case TYPE_ANYPOINTER: 612 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_ANYPOINTER, "expected object default") 613 | if def.Which() == VALUE_ANYPOINTER && def.AnyPointer().HasData() { 614 | fprintf(&g, "C.Object { return C.Struct(s).GetObject(%d).ToObjectDefault(%s, %d) }\n", 615 | off, g_bufname, copyData(def.AnyPointer())) 616 | } else { 617 | fprintf(&g, "C.Object { return C.Struct(s).GetObject(%d) }\n", off) 618 | } 619 | fprintf(&s, "(v C.Object) {%s C.Struct(s).SetObject(%d, v) }\n", settag, off) 620 | 621 | case TYPE_LIST: 622 | assert(def.Which() == VALUE_VOID || def.Which() == VALUE_LIST, "expected list default") 623 | 624 | typ := "" 625 | 626 | switch lt := t.List().ElementType(); lt.Which() { 627 | case TYPE_VOID, TYPE_INTERFACE: 628 | typ = "C.VoidList" 629 | case TYPE_BOOL: 630 | typ = "C.BitList" 631 | case TYPE_INT8: 632 | typ = "C.Int8List" 633 | case TYPE_UINT8: 634 | typ = "C.UInt8List" 635 | case TYPE_INT16: 636 | typ = "C.Int16List" 637 | case TYPE_UINT16: 638 | typ = "C.UInt16List" 639 | case TYPE_INT32: 640 | typ = "C.Int32List" 641 | case TYPE_UINT32: 642 | typ = "C.UInt32List" 643 | case TYPE_INT64: 644 | typ = "C.Int64List" 645 | case TYPE_UINT64: 646 | typ = "C.UInt64List" 647 | case TYPE_FLOAT32: 648 | typ = "C.Float32List" 649 | case TYPE_FLOAT64: 650 | typ = "C.Float64List" 651 | case TYPE_TEXT: 652 | typ = "C.TextList" 653 | case TYPE_DATA: 654 | typ = "C.DataList" 655 | case TYPE_ENUM: 656 | ni := findNode(lt.Enum().TypeId()) 657 | typ = sprintf("%s_List", ni.remoteName(n)) 658 | case TYPE_STRUCT: 659 | ni := findNode(lt.Struct().TypeId()) 660 | typ = sprintf("%s_List", ni.remoteName(n)) 661 | case TYPE_ANYPOINTER, TYPE_LIST: 662 | typ = "C.PointerList" 663 | } 664 | 665 | ldef := C.Object{} 666 | if def.Which() == VALUE_LIST { 667 | ldef = def.List() 668 | } 669 | 670 | if ldef.HasData() { 671 | fprintf(&g, "%s { return %s(C.Struct(s).GetObject(%d).ToListDefault(%s, %d)) }\n", 672 | typ, typ, off, g_bufname, copyData(ldef)) 673 | } else { 674 | fprintf(&g, "%s { return %s(C.Struct(s).GetObject(%d)) }\n", 675 | typ, typ, off) 676 | } 677 | 678 | fprintf(&s, "(v %s) {%s C.Struct(s).SetObject(%d, C.Object(v)) }\n", typ, settag, off) 679 | } 680 | 681 | w.Write(g.Bytes()) 682 | w.Write(s.Bytes()) 683 | } 684 | 685 | func (n *node) codeOrderFields() []Field { 686 | fields := n.Struct().Fields().ToArray() 687 | mbrs := make([]Field, len(fields)) 688 | for _, f := range fields { 689 | mbrs[f.CodeOrder()] = f 690 | } 691 | return mbrs 692 | } 693 | 694 | func (n *node) defineStructTypes(w io.Writer, baseNode *node) { 695 | assert(n.Which() == NODE_STRUCT, "invalid struct node") 696 | 697 | for _, a := range n.Annotations().ToArray() { 698 | if a.Id() == C.Doc { 699 | fprintf(w, "// %s\n", a.Value().Text()) 700 | } 701 | } 702 | if baseNode != nil { 703 | fprintf(w, "type %s %s\n", n.name, baseNode.name) 704 | } else { 705 | fprintf(w, "type %s C.Struct\n", n.name) 706 | baseNode = n 707 | } 708 | 709 | for _, f := range n.codeOrderFields() { 710 | if f.Which() == FIELD_GROUP { 711 | findNode(f.Group().TypeId()).defineStructTypes(w, baseNode) 712 | } 713 | } 714 | } 715 | 716 | func (n *node) defineStructEnums(w io.Writer) { 717 | assert(n.Which() == NODE_STRUCT, "invalid struct node") 718 | 719 | if n.Struct().DiscriminantCount() > 0 { 720 | fprintf(w, "type %s_Which uint16\n", n.name) 721 | fprintf(w, "const (\n") 722 | 723 | for _, f := range n.codeOrderFields() { 724 | if f.DiscriminantValue() == 0xFFFF { 725 | // Non-union member 726 | } else { 727 | fprintf(w, "%s_%s %s_Which = %d\n", strings.ToUpper(n.name), strings.ToUpper(f.Name()), n.name, f.DiscriminantValue()) 728 | } 729 | } 730 | fprintf(w, ")\n") 731 | } 732 | 733 | for _, f := range n.codeOrderFields() { 734 | if f.Which() == FIELD_GROUP { 735 | findNode(f.Group().TypeId()).defineStructEnums(w) 736 | } 737 | } 738 | } 739 | 740 | func (n *node) defineStructFuncs(w io.Writer) { 741 | assert(n.Which() == NODE_STRUCT, "invalid struct node") 742 | 743 | if n.Struct().DiscriminantCount() > 0 { 744 | fprintf(w, "func (s %s) Which() %s_Which { return %s_Which(C.Struct(s).Get16(%d)) }\n", 745 | n.name, n.name, n.name, n.Struct().DiscriminantOffset()*2) 746 | } 747 | 748 | for _, f := range n.codeOrderFields() { 749 | switch f.Which() { 750 | case FIELD_SLOT: 751 | n.defineField(w, f) 752 | case FIELD_GROUP: 753 | g := findNode(f.Group().TypeId()) 754 | fname := f.Name() 755 | if an := nameAnnotation(f.Annotations()); an != "" { 756 | fname = an 757 | } 758 | fname = title(fname) 759 | fprintf(w, "func (s %s) %s() %s { return %s(s) }\n", n.name, fname, g.name, g.name) 760 | if f.DiscriminantValue() != 0xFFFF { 761 | fprintf(w, "func (s %s) Set%s() { C.Struct(s).Set16(%d, %d) }\n", n.name, fname, n.Struct().DiscriminantOffset()*2, f.DiscriminantValue()) 762 | } 763 | g.defineStructFuncs(w) 764 | } 765 | } 766 | } 767 | 768 | // This writes the WriteJSON function. 769 | // 770 | // This is an unusual interface, but it was chosen because the types in go-capnproto 771 | // didn't match right to use the json.Marshaler interface. 772 | // This function recurses through the type, writing statements that will dump json to a wire 773 | // For all statements, the json encoder js and the bufio writer b will be in scope. 774 | // The value will be in scope as s. Some features need to redefine s, like unions. 775 | // In that case, Make a new block and redeclare s 776 | func (n *node) defineTypeJsonFuncs(w io.Writer) { 777 | if C.JSON_enabled { 778 | g_imported["io"] = true 779 | g_imported["bufio"] = true 780 | g_imported["bytes"] = true 781 | 782 | fprintf(w, "func (s %s) WriteJSON(w io.Writer) error {\n", n.name) 783 | fprintf(w, "b := bufio.NewWriter(w);") 784 | fprintf(w, "var err error;") 785 | fprintf(w, "var buf []byte;") 786 | fprintf(w, "_ = buf;") 787 | 788 | switch n.Which() { 789 | case NODE_ENUM: 790 | n.jsonEnum(w) 791 | case NODE_STRUCT: 792 | n.jsonStruct(w) 793 | } 794 | 795 | fprintf(w, "err = b.Flush(); return err\n};\n") 796 | 797 | fprintf(w, "func (s %s) MarshalJSON() ([]byte, error) {\n", n.name) 798 | fprintf(w, "b := bytes.Buffer{}; err := s.WriteJSON(&b); return b.Bytes(), err };") 799 | 800 | } else { 801 | fprintf(w, "// capn.JSON_enabled == false so we stub MarshallJSON().") 802 | fprintf(w, "\nfunc (s %s) MarshalJSON() (bs []byte, err error) { return } \n", n.name) 803 | } 804 | } 805 | 806 | func writeErrCheck(w io.Writer) { 807 | fprintf(w, "if err != nil { return err; };") 808 | } 809 | 810 | func (n *node) jsonEnum(w io.Writer) { 811 | g_imported["encoding/json"] = true 812 | fprintf(w, `buf, err = json.Marshal(s.String());`) 813 | writeErrCheck(w) 814 | fprintf(w, "_, err = b.Write(buf);") 815 | writeErrCheck(w) 816 | } 817 | 818 | // Write statements that will write a json struct 819 | func (n *node) jsonStruct(w io.Writer) { 820 | fprintf(w, `err = b.WriteByte('{');`) 821 | writeErrCheck(w) 822 | for i, f := range n.codeOrderFields() { 823 | if f.DiscriminantValue() != 0xFFFF { 824 | enumname := fmt.Sprintf("%s_%s", strings.ToUpper(n.name), strings.ToUpper(f.Name())) 825 | fprintf(w, "if s.Which() == %s {", enumname) 826 | } else if i != 0 { 827 | fprintf(w, ` 828 | err = b.WriteByte(','); 829 | `) 830 | writeErrCheck(w) 831 | } 832 | fprintf(w, `_, err = b.WriteString("\"%s\":");`, f.Name()) 833 | writeErrCheck(w) 834 | f.json(w) 835 | if f.DiscriminantValue() != 0xFFFF { 836 | fprintf(w, "};") 837 | } 838 | } 839 | fprintf(w, `err = b.WriteByte('}');`) 840 | writeErrCheck(w) 841 | } 842 | 843 | // This function writes statements that write the fields json representation to the bufio. 844 | func (f *Field) json(w io.Writer) { 845 | 846 | switch f.Which() { 847 | case FIELD_SLOT: 848 | fs := f.Slot() 849 | // we don't generate setters for Void fields 850 | if fs.Type().Which() == TYPE_VOID { 851 | fs.Type().json(w) 852 | return 853 | } 854 | fprintf(w, "{ s := s.%s(); ", title(f.Name())) 855 | fs.Type().json(w) 856 | fprintf(w, "}; ") 857 | case FIELD_GROUP: 858 | tid := f.Group().TypeId() 859 | n := findNode(tid) 860 | fprintf(w, "{ s := s.%s();", title(f.Name())) 861 | 862 | n.jsonStruct(w) 863 | fprintf(w, "};") 864 | } 865 | } 866 | 867 | func (t Type) json(w io.Writer) { 868 | switch t.Which() { 869 | case TYPE_UINT8, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, 870 | TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_INT64, 871 | TYPE_FLOAT32, TYPE_FLOAT64, TYPE_BOOL, TYPE_TEXT, TYPE_DATA: 872 | g_imported["encoding/json"] = true 873 | fprintf(w, "buf, err = json.Marshal(s);") 874 | writeErrCheck(w) 875 | fprintf(w, "_, err = b.Write(buf);") 876 | writeErrCheck(w) 877 | case TYPE_ENUM, TYPE_STRUCT: 878 | // since we handle groups at the field level, only named struct types make it in here 879 | // so we can just call the named structs json dumper 880 | fprintf(w, "err = s.WriteJSON(b);") 881 | writeErrCheck(w) 882 | case TYPE_LIST: 883 | typ := t.List().ElementType() 884 | which := typ.Which() 885 | if which == TYPE_LIST || which == TYPE_ANYPOINTER { 886 | // untyped list, cant do anything but report 887 | // that a field existed. 888 | // 889 | // s will be unused in this case, so ignore 890 | fprintf(w, `_ = s;`) 891 | fprintf(w, `_, err = b.WriteString("\"untyped list\"");`) 892 | writeErrCheck(w) 893 | return 894 | } 895 | fprintf(w, "{ err = b.WriteByte('[');") 896 | writeErrCheck(w) 897 | fprintf(w, "for i, s := range s.ToArray() {") 898 | fprintf(w, `if i != 0 { _, err = b.WriteString(", "); };`) 899 | writeErrCheck(w) 900 | typ.json(w) 901 | fprintf(w, "}; err = b.WriteByte(']'); };") 902 | writeErrCheck(w) 903 | case TYPE_VOID: 904 | fprintf(w, `_ = s;`) 905 | fprintf(w, `_, err = b.WriteString("null");`) 906 | writeErrCheck(w) 907 | } 908 | } 909 | 910 | func (n *node) defineNewStructFunc(w io.Writer) { 911 | assert(n.Which() == NODE_STRUCT, "invalid struct node") 912 | 913 | fprintf(w, "func New%s(s *C.Segment) %s { return %s(s.NewStruct(%d, %d)) }\n", 914 | n.name, n.name, n.name, n.Struct().DataWordCount()*8, n.Struct().PointerCount()) 915 | fprintf(w, "func NewRoot%s(s *C.Segment) %s { return %s(s.NewRootStruct(%d, %d)) }\n", 916 | n.name, n.name, n.name, n.Struct().DataWordCount()*8, n.Struct().PointerCount()) 917 | fprintf(w, "func AutoNew%s(s *C.Segment) %s { return %s(s.NewStructAR(%d, %d)) }\n", 918 | n.name, n.name, n.name, n.Struct().DataWordCount()*8, n.Struct().PointerCount()) 919 | fprintf(w, "func ReadRoot%s(s *C.Segment) %s { return %s(s.Root(0).ToStruct()) }\n", 920 | n.name, n.name, n.name) 921 | } 922 | 923 | func (n *node) defineStructList(w io.Writer) { 924 | assert(n.Which() == NODE_STRUCT, "invalid struct node") 925 | 926 | fprintf(w, "type %s_List C.PointerList\n", n.name) 927 | 928 | switch n.Struct().PreferredListEncoding() { 929 | case ELEMENTSIZE_EMPTY: 930 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewVoidList(sz)) }\n", n.name, n.name, n.name) 931 | case ELEMENTSIZE_BIT: 932 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewBitList(sz)) }\n", n.name, n.name, n.name) 933 | case ELEMENTSIZE_BYTE: 934 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewUInt8List(sz)) }\n", n.name, n.name, n.name) 935 | case ELEMENTSIZE_TWOBYTES: 936 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewUInt16List(sz)) }\n", n.name, n.name, n.name) 937 | case ELEMENTSIZE_FOURBYTES: 938 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewUInt32List(sz)) }\n", n.name, n.name, n.name) 939 | case ELEMENTSIZE_EIGHTBYTES: 940 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewUInt64List(sz)) }\n", n.name, n.name, n.name) 941 | default: 942 | fprintf(w, "func New%sList(s *C.Segment, sz int) %s_List { return %s_List(s.NewCompositeList(%d, %d, sz)) }\n", 943 | n.name, n.name, n.name, n.Struct().DataWordCount()*8, n.Struct().PointerCount()) 944 | } 945 | 946 | fprintf(w, "func (s %s_List) Len() int { return C.PointerList(s).Len() }\n", n.name) 947 | fprintf(w, "func (s %s_List) At(i int) %s { return %s(C.PointerList(s).At(i).ToStruct()) }\n", n.name, n.name, n.name) 948 | fprintf(w, "func (s %s_List) ToArray() []%s {\n", n.name, n.name) 949 | fprintf(w, "\tn := s.Len()\n") 950 | fprintf(w, "\ta := make([]%s, n)\n", n.name) 951 | fprintf(w, "\tfor i := 0; i < n; i++ { a[i] = s.At(i) }\n") 952 | fprintf(w, "\treturn a\n}\n") 953 | fprintf(w, "func (s %s_List) Set(i int, item %s) { C.PointerList(s).Set(i, C.Object(item)) }\n", n.name, n.name) 954 | } 955 | 956 | func main() { 957 | s, err := C.ReadFromStream(os.Stdin, nil) 958 | assert(err == nil, "%v\n", err) 959 | 960 | req := ReadRootCodeGeneratorRequest(s) 961 | allfiles := []*node{} 962 | 963 | for _, ni := range req.Nodes().ToArray() { 964 | n := &node{Node: ni} 965 | g_nodes[n.Id()] = n 966 | 967 | if n.Which() == NODE_FILE { 968 | allfiles = append(allfiles, n) 969 | } 970 | } 971 | 972 | for _, f := range allfiles { 973 | for _, a := range f.Annotations().ToArray() { 974 | if v := a.Value(); v.Which() == VALUE_TEXT { 975 | switch a.Id() { 976 | case C.Package: 977 | f.pkg = v.Text() 978 | case C.Import: 979 | f.imp = v.Text() 980 | } 981 | } 982 | } 983 | 984 | for _, nn := range f.NestedNodes().ToArray() { 985 | if ni := g_nodes[nn.Id()]; ni != nil { 986 | ni.resolveName("", nn.Name(), f) 987 | } 988 | } 989 | } 990 | 991 | for _, reqf := range req.RequestedFiles().ToArray() { 992 | f := findNode(reqf.Id()) 993 | buf := bytes.Buffer{} 994 | g_imported = make(map[string]bool) 995 | g_segment = C.NewBuffer([]byte{}) 996 | g_bufname = sprintf("x_%x", f.Id()) 997 | 998 | for _, n := range f.nodes { 999 | if n.Which() == NODE_ANNOTATION { 1000 | n.defineAnnotation(&buf) 1001 | } 1002 | } 1003 | 1004 | defineConstNodes(&buf, f.nodes) 1005 | 1006 | for _, n := range f.nodes { 1007 | switch n.Which() { 1008 | case NODE_ANNOTATION: 1009 | case NODE_ENUM: 1010 | n.defineEnum(&buf) 1011 | n.defineTypeJsonFuncs(&buf) 1012 | n.defineTypeCaplitFuncs(&buf) 1013 | case NODE_STRUCT: 1014 | if !n.Struct().IsGroup() { 1015 | n.defineStructTypes(&buf, nil) 1016 | n.defineStructEnums(&buf) 1017 | n.defineNewStructFunc(&buf) 1018 | n.defineStructFuncs(&buf) 1019 | n.defineTypeJsonFuncs(&buf) 1020 | n.defineTypeCaplitFuncs(&buf) 1021 | n.defineStructList(&buf) 1022 | } 1023 | } 1024 | } 1025 | 1026 | assert(f.pkg != "", "missing package annotation for %s", reqf.Filename()) 1027 | 1028 | if dirPath, _ := filepath.Split(reqf.Filename()); dirPath != "" { 1029 | err := os.MkdirAll(dirPath, os.ModePerm) 1030 | assert(err == nil, "%v\n", err) 1031 | } 1032 | 1033 | file, err := os.Create(reqf.Filename() + ".go") 1034 | assert(err == nil, "%v\n", err) 1035 | fprintf(file, "package %s\n\n", f.pkg) 1036 | fprintf(file, "// AUTO GENERATED - DO NOT EDIT\n\n") 1037 | 1038 | fprintf(file, "import (\n") 1039 | fprintf(file, "C \"%s\"\n", go_capnproto_import) 1040 | for imp := range g_imported { 1041 | fprintf(file, "%s\n", strconv.Quote(imp)) 1042 | } 1043 | fprintf(file, ")\n") 1044 | 1045 | file.Write(buf.Bytes()) 1046 | 1047 | if len(g_segment.Data) > 0 { 1048 | fprintf(file, "var %s = C.NewBuffer([]byte{", g_bufname) 1049 | for i, b := range g_segment.Data { 1050 | if i%8 == 0 { 1051 | fprintf(file, "\n") 1052 | } 1053 | fprintf(file, "%d,", b) 1054 | } 1055 | fprintf(file, "\n})\n") 1056 | } 1057 | file.Close() 1058 | 1059 | cmd := exec.Command("gofmt", "-w", reqf.Filename()+".go") 1060 | cmd.Stderr = os.Stderr 1061 | err = cmd.Run() 1062 | assert(err == nil, "%v\n", err) 1063 | } 1064 | } 1065 | -------------------------------------------------------------------------------- /capnpc-go/schema.capnp: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 | # Licensed under the MIT License: 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | using Go = import "../go.capnp"; 23 | 24 | @0xa93fc509624c72d9; 25 | $Go.package("main"); 26 | $Go.import("github.com/glycerine/go-capnproto/capnpc-go"); 27 | 28 | using Id = UInt64; 29 | # The globally-unique ID of a file, type, or annotation. 30 | 31 | struct Node { 32 | id @0 :Id; 33 | 34 | displayName @1 :Text; 35 | # Name to present to humans to identify this Node. You should not attempt to parse this. Its 36 | # format could change. It is not guaranteed to be unique. 37 | # 38 | # (On Zooko's triangle, this is the node's nickname.) 39 | 40 | displayNamePrefixLength @2 :UInt32; 41 | # If you want a shorter version of `displayName` (just naming this node, without its surrounding 42 | # scope), chop off this many characters from the beginning of `displayName`. 43 | 44 | scopeId @3 :Id; 45 | # ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back 46 | # at this node, but robust code should avoid relying on this (and, in fact, group nodes are not 47 | # listed in the outer struct's nestedNodes, since they are listed in the fields). `scopeId` is 48 | # zero if the node has no parent, which is normally only the case with files, but should be 49 | # allowed for any kind of node (in order to make runtime type generation easier). 50 | 51 | parameters @32 :List(Parameter); 52 | # If this node is parameterized (generic), the list of parameters. Empty for non-generic types. 53 | 54 | isGeneric @33 :Bool; 55 | # True if this node is generic, meaning that it or one of its parent scopes has a non-empty 56 | # `parameters`. 57 | 58 | struct Parameter { 59 | # Information about one of the node's parameters. 60 | 61 | name @0 :Text; 62 | } 63 | 64 | nestedNodes @4 :List(NestedNode); 65 | # List of nodes nested within this node, along with the names under which they were declared. 66 | 67 | struct NestedNode { 68 | name @0 :Text; 69 | # Unqualified symbol name. Unlike Node.displayName, this *can* be used programmatically. 70 | # 71 | # (On Zooko's triangle, this is the node's petname according to its parent scope.) 72 | 73 | id @1 :Id; 74 | # ID of the nested node. Typically, the target node's scopeId points back to this node, but 75 | # robust code should avoid relying on this. 76 | } 77 | 78 | annotations @5 :List(Annotation); 79 | # Annotations applied to this node. 80 | 81 | union { 82 | # Info specific to each kind of node. 83 | 84 | file @6 :Void; 85 | 86 | struct :group { 87 | dataWordCount @7 :UInt16; 88 | # Size of the data section, in words. 89 | 90 | pointerCount @8 :UInt16; 91 | # Size of the pointer section, in pointers (which are one word each). 92 | 93 | preferredListEncoding @9 :ElementSize; 94 | # The preferred element size to use when encoding a list of this struct. If this is anything 95 | # other than `inlineComposite` then the struct is one word or less in size and is a candidate 96 | # for list packing optimization. 97 | 98 | isGroup @10 :Bool; 99 | # If true, then this "struct" node is actually not an independent node, but merely represents 100 | # some named union or group within a particular parent struct. This node's scopeId refers 101 | # to the parent struct, which may itself be a union/group in yet another struct. 102 | # 103 | # All group nodes share the same dataWordCount and pointerCount as the top-level 104 | # struct, and their fields live in the same ordinal and offset spaces as all other fields in 105 | # the struct. 106 | # 107 | # Note that a named union is considered a special kind of group -- in fact, a named union 108 | # is exactly equivalent to a group that contains nothing but an unnamed union. 109 | 110 | discriminantCount @11 :UInt16; 111 | # Number of fields in this struct which are members of an anonymous union, and thus may 112 | # overlap. If this is non-zero, then a 16-bit discriminant is present indicating which 113 | # of the overlapping fields is active. This can never be 1 -- if it is non-zero, it must be 114 | # two or more. 115 | # 116 | # Note that the fields of an unnamed union are considered fields of the scope containing the 117 | # union -- an unnamed union is not its own group. So, a top-level struct may contain a 118 | # non-zero discriminant count. Named unions, on the other hand, are equivalent to groups 119 | # containing unnamed unions. So, a named union has its own independent schema node, with 120 | # `isGroup` = true. 121 | 122 | discriminantOffset @12 :UInt32; 123 | # If `discriminantCount` is non-zero, this is the offset of the union discriminant, in 124 | # multiples of 16 bits. 125 | 126 | fields @13 :List(Field); 127 | # Fields defined within this scope (either the struct's top-level fields, or the fields of 128 | # a particular group; see `isGroup`). 129 | # 130 | # The fields are sorted by ordinal number, but note that because groups share the same 131 | # ordinal space, the field's index in this list is not necessarily exactly its ordinal. 132 | # On the other hand, the field's position in this list does remain the same even as the 133 | # protocol evolves, since it is not possible to insert or remove an earlier ordinal. 134 | # Therefore, for most use cases, if you want to identify a field by number, it may make the 135 | # most sense to use the field's index in this list rather than its ordinal. 136 | } 137 | 138 | enum :group { 139 | enumerants@14 :List(Enumerant); 140 | # Enumerants ordered by numeric value (ordinal). 141 | } 142 | 143 | interface :group { 144 | methods @15 :List(Method); 145 | # Methods ordered by ordinal. 146 | 147 | superclasses @31 :List(Superclass); 148 | # Superclasses of this interface. 149 | } 150 | 151 | const :group { 152 | type @16 :Type; 153 | value @17 :Value; 154 | } 155 | 156 | annotation :group { 157 | type @18 :Type; 158 | 159 | targetsFile @19 :Bool; 160 | targetsConst @20 :Bool; 161 | targetsEnum @21 :Bool; 162 | targetsEnumerant @22 :Bool; 163 | targetsStruct @23 :Bool; 164 | targetsField @24 :Bool; 165 | targetsUnion @25 :Bool; 166 | targetsGroup @26 :Bool; 167 | targetsInterface @27 :Bool; 168 | targetsMethod @28 :Bool; 169 | targetsParam @29 :Bool; 170 | targetsAnnotation @30 :Bool; 171 | } 172 | } 173 | } 174 | 175 | struct Field { 176 | # Schema for a field of a struct. 177 | 178 | name @0 :Text; 179 | 180 | codeOrder @1 :UInt16; 181 | # Indicates where this member appeared in the code, relative to other members. 182 | # Code ordering may have semantic relevance -- programmers tend to place related fields 183 | # together. So, using code ordering makes sense in human-readable formats where ordering is 184 | # otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so the maximum 185 | # value is count(members) - 1. Fields that are members of a union are only ordered relative to 186 | # the other members of that union, so the maximum value there is count(union.members). 187 | 188 | annotations @2 :List(Annotation); 189 | 190 | const noDiscriminant :UInt16 = 0xffff; 191 | 192 | discriminantValue @3 :UInt16 = Field.noDiscriminant; 193 | # If the field is in a union, this is the value which the union's discriminant should take when 194 | # the field is active. If the field is not in a union, this is 0xffff. 195 | 196 | union { 197 | slot :group { 198 | # A regular, non-group, non-fixed-list field. 199 | 200 | offset @4 :UInt32; 201 | # Offset, in units of the field's size, from the beginning of the section in which the field 202 | # resides. E.g. for a UInt32 field, multiply this by 4 to get the byte offset from the 203 | # beginning of the data section. 204 | 205 | type @5 :Type; 206 | defaultValue @6 :Value; 207 | 208 | hadExplicitDefault @10 :Bool; 209 | # Whether the default value was specified explicitly. Non-explicit default values are always 210 | # zero or empty values. Usually, whether the default value was explicit shouldn't matter. 211 | # The main use case for this flag is for structs representing method parameters: 212 | # explicitly-defaulted parameters may be allowed to be omitted when calling the method. 213 | } 214 | 215 | group :group { 216 | # A group. 217 | 218 | typeId @7 :Id; 219 | # The ID of the group's node. 220 | } 221 | } 222 | 223 | ordinal :union { 224 | implicit @8 :Void; 225 | explicit @9 :UInt16; 226 | # The original ordinal number given to the field. You probably should NOT use this; if you need 227 | # a numeric identifier for a field, use its position within the field array for its scope. 228 | # The ordinal is given here mainly just so that the original schema text can be reproduced given 229 | # the compiled version -- i.e. so that `capnp compile -ocapnp` can do its job. 230 | } 231 | } 232 | 233 | struct Enumerant { 234 | # Schema for member of an enum. 235 | 236 | name @0 :Text; 237 | 238 | codeOrder @1 :UInt16; 239 | # Specifies order in which the enumerants were declared in the code. 240 | # Like Struct.Field.codeOrder. 241 | 242 | annotations @2 :List(Annotation); 243 | } 244 | 245 | struct Superclass { 246 | id @0 :Id; 247 | brand @1 :Brand; 248 | } 249 | 250 | struct Method { 251 | # Schema for method of an interface. 252 | 253 | name @0 :Text; 254 | 255 | codeOrder @1 :UInt16; 256 | # Specifies order in which the methods were declared in the code. 257 | # Like Struct.Field.codeOrder. 258 | 259 | implicitParameters @7 :List(Node.Parameter); 260 | # The parameters listed in [] (typically, type / generic parameters), whose bindings are intended 261 | # to be inferred rather than specified explicitly, although not all languages support this. 262 | 263 | paramStructType @2 :Id; 264 | # ID of the parameter struct type. If a named parameter list was specified in the method 265 | # declaration (rather than a single struct parameter type) then a corresponding struct type is 266 | # auto-generated. Such an auto-generated type will not be listed in the interface's 267 | # `nestedNodes` and its `scopeId` will be zero -- it is completely detached from the namespace. 268 | # (Awkwardly, it does of course inherit generic parameters from the method's scope, which makes 269 | # this a situation where you can't just climb the scope chain to find where a particular 270 | # generic parameter was introduced. Making the `scopeId` zero was a mistake.) 271 | 272 | paramBrand @5 :Brand; 273 | # Brand of param struct type. 274 | 275 | resultStructType @3 :Id; 276 | # ID of the return struct type; similar to `paramStructType`. 277 | 278 | resultBrand @6 :Brand; 279 | # Brand of result struct type. 280 | 281 | annotations @4 :List(Annotation); 282 | } 283 | 284 | struct Type { 285 | # Represents a type expression. 286 | 287 | union { 288 | # The ordinals intentionally match those of Value. 289 | 290 | void @0 :Void; 291 | bool @1 :Void; 292 | int8 @2 :Void; 293 | int16 @3 :Void; 294 | int32 @4 :Void; 295 | int64 @5 :Void; 296 | uint8 @6 :Void; 297 | uint16 @7 :Void; 298 | uint32 @8 :Void; 299 | uint64 @9 :Void; 300 | float32 @10 :Void; 301 | float64 @11 :Void; 302 | text @12 :Void; 303 | data @13 :Void; 304 | 305 | list :group { 306 | elementType @14 :Type; 307 | } 308 | 309 | enum :group { 310 | typeId @15 :Id; 311 | brand @21 :Brand; 312 | } 313 | struct :group { 314 | typeId @16 :Id; 315 | brand @22 :Brand; 316 | } 317 | interface :group { 318 | typeId @17 :Id; 319 | brand @23 :Brand; 320 | } 321 | 322 | anyPointer :union { 323 | unconstrained :union { 324 | # A regular AnyPointer. 325 | # 326 | # The name "unconstained" means as opposed to constraining it to match a type parameter. 327 | # In retrospect this name is probably a poor choice given that it may still be constrained 328 | # to be a struct, list, or capability. 329 | 330 | anyKind @18 :Void; # truly AnyPointer 331 | struct @25 :Void; # AnyStruct 332 | list @26 :Void; # AnyList 333 | capability @27 :Void; # Capability 334 | } 335 | 336 | parameter :group { 337 | # This is actually a reference to a type parameter defined within this scope. 338 | 339 | scopeId @19 :Id; 340 | # ID of the generic type whose parameter we're referencing. This should be a parent of the 341 | # current scope. 342 | 343 | parameterIndex @20 :UInt16; 344 | # Index of the parameter within the generic type's parameter list. 345 | } 346 | 347 | implicitMethodParameter :group { 348 | # This is actually a reference to an implicit (generic) parameter of a method. The only 349 | # legal context for this type to appear is inside Method.paramBrand or Method.resultBrand. 350 | 351 | parameterIndex @24 :UInt16; 352 | } 353 | } 354 | } 355 | } 356 | 357 | struct Brand { 358 | # Specifies bindings for parameters of generics. Since these bindings turn a generic into a 359 | # non-generic, we call it the "brand". 360 | 361 | scopes @0 :List(Scope); 362 | # For each of the target type and each of its parent scopes, a parameterization may be included 363 | # in this list. If no parameterization is included for a particular relevant scope, then either 364 | # that scope has no parameters or all parameters should be considered to be `AnyPointer`. 365 | 366 | struct Scope { 367 | scopeId @0 :Id; 368 | # ID of the scope to which these params apply. 369 | 370 | union { 371 | bind @1 :List(Binding); 372 | # List of parameter bindings. 373 | 374 | inherit @2 :Void; 375 | # The place where this Brand appears is actually within this scope or a sub-scope, 376 | # and the bindings for this scope should be inherited from the reference point. 377 | } 378 | } 379 | 380 | struct Binding { 381 | union { 382 | unbound @0 :Void; 383 | type @1 :Type; 384 | 385 | # TODO(someday): Allow non-type parameters? Unsure if useful. 386 | } 387 | } 388 | } 389 | 390 | struct Value { 391 | # Represents a value, e.g. a field default value, constant value, or annotation value. 392 | 393 | union { 394 | # The ordinals intentionally match those of Type. 395 | 396 | void @0 :Void; 397 | bool @1 :Bool; 398 | int8 @2 :Int8; 399 | int16 @3 :Int16; 400 | int32 @4 :Int32; 401 | int64 @5 :Int64; 402 | uint8 @6 :UInt8; 403 | uint16 @7 :UInt16; 404 | uint32 @8 :UInt32; 405 | uint64 @9 :UInt64; 406 | float32 @10 :Float32; 407 | float64 @11 :Float64; 408 | text @12 :Text; 409 | data @13 :Data; 410 | 411 | list @14 :AnyPointer; 412 | 413 | enum @15 :UInt16; 414 | struct @16 :AnyPointer; 415 | 416 | interface @17 :Void; 417 | # The only interface value that can be represented statically is "null", whose methods always 418 | # throw exceptions. 419 | 420 | anyPointer @18 :AnyPointer; 421 | } 422 | } 423 | 424 | struct Annotation { 425 | # Describes an annotation applied to a declaration. Note AnnotationNode describes the 426 | # annotation's declaration, while this describes a use of the annotation. 427 | 428 | id @0 :Id; 429 | # ID of the annotation node. 430 | 431 | brand @2 :Brand; 432 | # Brand of the annotation. 433 | # 434 | # Note that the annotation itself is not allowed to be parameterized, but its scope might be. 435 | 436 | value @1 :Value; 437 | } 438 | 439 | enum ElementSize { 440 | # Possible element sizes for encoded lists. These correspond exactly to the possible values of 441 | # the 3-bit element size component of a list pointer. 442 | 443 | empty @0; # aka "void", but that's a keyword. 444 | bit @1; 445 | byte @2; 446 | twoBytes @3; 447 | fourBytes @4; 448 | eightBytes @5; 449 | pointer @6; 450 | inlineComposite @7; 451 | } 452 | 453 | struct CodeGeneratorRequest { 454 | nodes @0 :List(Node); 455 | # All nodes parsed by the compiler, including for the files on the command line and their 456 | # imports. 457 | 458 | requestedFiles @1 :List(RequestedFile); 459 | # Files which were listed on the command line. 460 | 461 | struct RequestedFile { 462 | id @0 :Id; 463 | # ID of the file. 464 | 465 | filename @1 :Text; 466 | # Name of the file as it appeared on the command-line (minus the src-prefix). You may use 467 | # this to decide where to write the output. 468 | 469 | imports @2 :List(Import); 470 | # List of all imported paths seen in this file. 471 | 472 | struct Import { 473 | id @0 :Id; 474 | # ID of the imported file. 475 | 476 | name @1 :Text; 477 | # Name which *this* file used to refer to the foreign file. This may be a relative name. 478 | # This information is provided because it might be useful for code generation, e.g. to 479 | # generate #include directives in C++. We don't put this in Node.file because this 480 | # information is only meaningful at compile time anyway. 481 | # 482 | # (On Zooko's triangle, this is the import's petname according to the importing file.) 483 | } 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /common_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | 6 | capn "github.com/glycerine/go-capnproto" 7 | air "github.com/glycerine/go-capnproto/aircraftlib" 8 | ) 9 | 10 | func zdateFilledSegment(n int, packed bool) (*capn.Segment, []byte) { 11 | seg := capn.NewBuffer(nil) 12 | z := air.NewRootZ(seg) 13 | list := air.NewZdateList(seg, n) 14 | // hand added a Set() method to messages_test.go, so plist not needed 15 | plist := capn.PointerList(list) 16 | 17 | for i := 0; i < n; i++ { 18 | d := air.NewZdate(seg) 19 | d.SetMonth(12) 20 | d.SetDay(7) 21 | d.SetYear(int16(2004 + i)) 22 | plist.Set(i, capn.Object(d)) 23 | //list.Set(i, d) 24 | } 25 | z.SetZdatevec(list) 26 | 27 | buf := bytes.Buffer{} 28 | if packed { 29 | seg.WriteToPacked(&buf) 30 | } else { 31 | seg.WriteTo(&buf) 32 | } 33 | return seg, buf.Bytes() 34 | } 35 | 36 | func zdateReader(n int, packed bool) *bytes.Reader { 37 | _, byteSlice := zdateFilledSegment(n, packed) 38 | return bytes.NewReader(byteSlice) 39 | } 40 | 41 | // actually return n segments back-to-back. 42 | // WriteTo will automatically add the stream header word with message length. 43 | // 44 | func zdateReaderNBackToBack(n int, packed bool) *bytes.Reader { 45 | 46 | buf := bytes.Buffer{} 47 | 48 | for i := 0; i < n; i++ { 49 | seg := capn.NewBuffer(nil) 50 | d := air.NewRootZdate(seg) 51 | d.SetMonth(12) 52 | d.SetDay(7) 53 | d.SetYear(int16(2004 + i)) 54 | 55 | if packed { 56 | seg.WriteToPacked(&buf) 57 | } else { 58 | seg.WriteTo(&buf) 59 | } 60 | } 61 | 62 | return bytes.NewReader(buf.Bytes()) 63 | } 64 | 65 | func zdataFilledSegment(n int) (*capn.Segment, []byte) { 66 | seg := capn.NewBuffer(nil) 67 | z := air.NewRootZ(seg) 68 | d := air.NewZdata(seg) 69 | 70 | b := make([]byte, n) 71 | for i := 0; i < len(b); i++ { 72 | b[i] = byte(i) 73 | } 74 | d.SetData(b) 75 | z.SetZdata(d) 76 | 77 | buf := bytes.Buffer{} 78 | seg.WriteTo(&buf) 79 | return seg, buf.Bytes() 80 | } 81 | 82 | func zdataReader(n int) *bytes.Reader { 83 | _, byteSlice := zdataFilledSegment(n) 84 | return bytes.NewReader(byteSlice) 85 | } 86 | -------------------------------------------------------------------------------- /compress_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "github.com/glycerine/go-capnproto" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | func TestPackingRoundtrip(t *testing.T) { 11 | var buf bytes.Buffer 12 | c := capn.NewCompressor(&buf) 13 | b := []byte{0x8, 0, 0, 0, 0x3, 0, 0x2, 0, 0x19, 0, 0, 0, 0xaa, 0x1, 0, 0} 14 | n, err := c.Write(b) 15 | if err != nil { 16 | panic(err) 17 | } 18 | var expected = len(b) 19 | if expected != n { 20 | t.Fatalf("expected %v bytes, got %v", expected, n) 21 | } 22 | exp := []byte{0x51, 0x08, 0x03, 0x02, 0x31, 0x19, 0xaa, 0x01} 23 | if !bytes.Equal(buf.Bytes(), exp) { 24 | t.Fatalf("expected %x bytes, got %x", exp, buf.Bytes()) 25 | } 26 | dc := capn.NewDecompressor(&buf) 27 | readBuf, err := ioutil.ReadAll(dc) 28 | if err != nil { 29 | panic(err) 30 | } 31 | readBuf = readBuf[:n] 32 | if !bytes.Equal(b, readBuf) { 33 | t.Fatalf("expected %x bytes, got %x", b, readBuf) 34 | } 35 | } 36 | 37 | func TestPackingTag1(t *testing.T) { 38 | var buf bytes.Buffer 39 | c := capn.NewCompressor(&buf) 40 | _, err := c.Write(make([]byte, 32)) 41 | if err != nil { 42 | panic(err) 43 | } 44 | if bytes.Compare(buf.Bytes(), []byte{0x0, 0x3}) != 0 { 45 | t.Fatalf("invalid packing") 46 | } 47 | } 48 | 49 | func TestPackingTag2(t *testing.T) { 50 | var buf bytes.Buffer 51 | c := capn.NewCompressor(&buf) 52 | b := make([]byte, 32) 53 | for i := 0; i < len(b); i++ { 54 | b[i] = 0x8a 55 | } 56 | _, err := c.Write(b) 57 | if err != nil { 58 | panic(err) 59 | } 60 | b = make([]byte, 34) 61 | for i := 0; i < len(b); i++ { 62 | b[i] = 0x8a 63 | } 64 | b[0] = 0xff 65 | b[9] = 0x3 66 | if bytes.Compare(buf.Bytes(), b) != 0 { 67 | t.Fatalf("invalid packing") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /create_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | air "github.com/glycerine/go-capnproto/aircraftlib" 8 | cv "github.com/glycerine/goconvey/convey" 9 | ) 10 | 11 | func TestCreationOfZDate(t *testing.T) { 12 | const n = 1 13 | packed := false 14 | seg, _ := zdateFilledSegment(n, packed) 15 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z") 16 | 17 | //expectedText := `(year = 2004, month = 12, day = 7)` 18 | expectedText := `(zdatevec = [(year = 2004, month = 12, day = 7)])` 19 | 20 | cv.Convey("Given a go-capnproto created Zdate", t, func() { 21 | cv.Convey("When we decode it with capnp", func() { 22 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 23 | cv.So(text, cv.ShouldEqual, expectedText) 24 | }) 25 | }) 26 | }) 27 | } 28 | 29 | func TestCreationOfManyZDate(t *testing.T) { 30 | const n = 10 31 | packed := false 32 | seg, _ := zdateFilledSegment(n, packed) 33 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z") 34 | 35 | expectedText := `(zdatevec = [(year = 2004, month = 12, day = 7), (year = 2005, month = 12, day = 7), (year = 2006, month = 12, day = 7), (year = 2007, month = 12, day = 7), (year = 2008, month = 12, day = 7), (year = 2009, month = 12, day = 7), (year = 2010, month = 12, day = 7), (year = 2011, month = 12, day = 7), (year = 2012, month = 12, day = 7), (year = 2013, month = 12, day = 7)])` 36 | 37 | cv.Convey("Given a go-capnproto created segment with 10 Zdate", t, func() { 38 | cv.Convey("When we decode it with capnp", func() { 39 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 40 | cv.So(text, cv.ShouldEqual, expectedText) 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestCreationOfManyZDatePacked(t *testing.T) { 47 | const n = 10 48 | packed := true 49 | seg, _ := zdateFilledSegment(n, packed) 50 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z") 51 | 52 | expectedText := `(zdatevec = [(year = 2004, month = 12, day = 7), (year = 2005, month = 12, day = 7), (year = 2006, month = 12, day = 7), (year = 2007, month = 12, day = 7), (year = 2008, month = 12, day = 7), (year = 2009, month = 12, day = 7), (year = 2010, month = 12, day = 7), (year = 2011, month = 12, day = 7), (year = 2012, month = 12, day = 7), (year = 2013, month = 12, day = 7)])` 53 | 54 | cv.Convey("Given a go-capnproto created a PACKED segment with 10 Zdate", t, func() { 55 | cv.Convey("When we decode it with capnp", func() { 56 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 57 | cv.So(text, cv.ShouldEqual, expectedText) 58 | }) 59 | }) 60 | }) 61 | } 62 | 63 | func TestSegmentWriteToPackedOfManyZDatePacked(t *testing.T) { 64 | const n = 10 65 | packed := true 66 | _, byteSlice := zdateFilledSegment(n, packed) 67 | 68 | // check the packing-- is it wrong? 69 | text := CapnpDecodeBuf(byteSlice, "", "", "Z", true) 70 | 71 | expectedText := `(zdatevec = [(year = 2004, month = 12, day = 7), (year = 2005, month = 12, day = 7), (year = 2006, month = 12, day = 7), (year = 2007, month = 12, day = 7), (year = 2008, month = 12, day = 7), (year = 2009, month = 12, day = 7), (year = 2010, month = 12, day = 7), (year = 2011, month = 12, day = 7), (year = 2012, month = 12, day = 7), (year = 2013, month = 12, day = 7)])` 72 | 73 | cv.Convey("Given a go-capnproto write packed with WriteToPacked() with 10 Zdate", t, func() { 74 | cv.Convey("When we decode it with capnp", func() { 75 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 76 | cv.So(text, cv.ShouldEqual, expectedText) 77 | }) 78 | }) 79 | }) 80 | } 81 | 82 | /// now for Zdata (not Zdate) 83 | 84 | func TestCreationOfZData(t *testing.T) { 85 | const n = 20 86 | seg, _ := zdataFilledSegment(n) 87 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Z") 88 | 89 | expectedText := `(zdata = (data = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13"))` 90 | 91 | cv.Convey("Given a go-capnproto created Zdata DATA element with n=20", t, func() { 92 | cv.Convey("When we decode it with capnp", func() { 93 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 94 | cv.So(text, cv.ShouldEqual, expectedText) 95 | }) 96 | cv.Convey("And our data should contain Z_ZDATA with contents 0,1,2,...,n", func() { 97 | z := air.ReadRootZ(seg) 98 | cv.So(z.Which(), cv.ShouldEqual, air.Z_ZDATA) 99 | 100 | var data []byte = z.Zdata().Data() 101 | cv.So(len(data), cv.ShouldEqual, n) 102 | for i := range data { 103 | cv.So(data[i], cv.ShouldEqual, i) 104 | } 105 | 106 | }) 107 | }) 108 | }) 109 | 110 | } 111 | -------------------------------------------------------------------------------- /customtype_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "net" 7 | "testing" 8 | 9 | capn "github.com/glycerine/go-capnproto" 10 | air "github.com/glycerine/go-capnproto/aircraftlib" 11 | cv "github.com/glycerine/goconvey/convey" 12 | ) 13 | 14 | func ExampleCreateEndpoint() (*capn.Segment, []byte) { 15 | seg := capn.NewBuffer(nil) 16 | e := air.NewRootEndpoint(seg) 17 | e.SetIp(net.ParseIP("1.2.3.4").To4()) 18 | e.SetPort(56) 19 | e.SetHostname("test.com") 20 | 21 | fmt.Printf("ip: %s\n", e.Ip().String()) 22 | fmt.Printf("port: %d\n", e.Port()) 23 | fmt.Printf("hostname: %s\n", e.Hostname()) 24 | if capn.JSON_enabled { 25 | json, err := e.MarshalJSON() 26 | if err != nil { 27 | panic(err) 28 | } 29 | fmt.Printf("%s\n", string(json)) 30 | } 31 | 32 | buf := bytes.Buffer{} 33 | seg.WriteTo(&buf) 34 | 35 | return seg, buf.Bytes() 36 | } 37 | 38 | func TestCreationOfEndpoint(t *testing.T) { 39 | seg, _ := ExampleCreateEndpoint() 40 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Endpoint") 41 | 42 | expectedText := `(ip = "\x01\x02\x03\x04", port = 56, hostname = "test.com")` 43 | expectedIP := net.IP([]byte{1,2,3,4}) 44 | const expectedPort = 56 45 | expectedHostname := "test.com" 46 | expectedJSON := `{"ip":"1.2.3.4","port":56,"hostname":"test.com"}` 47 | 48 | cv.Convey("Given a go-capnproto created Endpoint", t, func() { 49 | cv.Convey("When we decode it with capnp", func() { 50 | cv.Convey(fmt.Sprintf("Then we should get the expected text '%s'", expectedText), func() { 51 | cv.So(text, cv.ShouldEqual, expectedText) 52 | }) 53 | }) 54 | cv.Convey("When we decode it", func() { 55 | endpoint := air.ReadRootEndpoint(seg) 56 | cv.Convey(fmt.Sprintf("Then we should get the expected ip '%s'", expectedIP), func() { 57 | cv.So(endpoint.Ip(), cv.ShouldResemble, expectedIP) 58 | }) 59 | cv.Convey(fmt.Sprintf("Then we should get the expected port '%d'", expectedPort), func() { 60 | cv.So(endpoint.Port(), cv.ShouldEqual, expectedPort) 61 | }) 62 | cv.Convey(fmt.Sprintf("Then we should get the expected hostname '%s'", expectedHostname), func() { 63 | cv.So(endpoint.Hostname(), cv.ShouldEqual, expectedHostname) 64 | }) 65 | if capn.JSON_enabled { 66 | json, err := endpoint.MarshalJSON() 67 | if err != nil { 68 | panic(err) 69 | } 70 | cv.Convey(fmt.Sprintf("Then we should get the expected JSON '%s'", expectedJSON), func() { 71 | cv.So(string(json), cv.ShouldEqual, expectedJSON) 72 | }) 73 | } 74 | }) 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /data/bvec0.bin: -------------------------------------------------------------------------------- 1 | ' -------------------------------------------------------------------------------- /data/bvec1.bin: -------------------------------------------------------------------------------- 1 | '  -------------------------------------------------------------------------------- /data/bvec3.bin: -------------------------------------------------------------------------------- 1 | ' -------------------------------------------------------------------------------- /data/bvec5.bin: -------------------------------------------------------------------------------- 1 | ' -------------------------------------------------------------------------------- /data/capnp.packed.one.zdate.vector.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glycerine/go-capnproto/2d07de3aa7fc2910cdd2276e5effdd8832265d39/data/capnp.packed.one.zdate.vector.dat -------------------------------------------------------------------------------- /data/capnp.unpacked.one.zdate.vector.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glycerine/go-capnproto/2d07de3aa7fc2910cdd2276e5effdd8832265d39/data/capnp.unpacked.one.zdate.vector.dat -------------------------------------------------------------------------------- /data/check.zdate.cpz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glycerine/go-capnproto/2d07de3aa7fc2910cdd2276e5effdd8832265d39/data/check.zdate.cpz -------------------------------------------------------------------------------- /data/gen-bvec.sh: -------------------------------------------------------------------------------- 1 | echo "(boolvec = [true, false, true])" | capnp encode ../aircraftlib/aircraft.capnp Z > bvec5.bin 2 | echo "(boolvec = [true, true])" | capnp encode ../aircraftlib/aircraft.capnp Z > bvec3.bin 3 | echo "(boolvec = [true])" | capnp encode ../aircraftlib/aircraft.capnp Z > bvec1.bin 4 | echo "(boolvec = [false])" | capnp encode ../aircraftlib/aircraft.capnp Z > bvec0.bin 5 | -------------------------------------------------------------------------------- /data/packed.byteslice.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glycerine/go-capnproto/2d07de3aa7fc2910cdd2276e5effdd8832265d39/data/packed.byteslice.dat -------------------------------------------------------------------------------- /data/zdate2.packed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glycerine/go-capnproto/2d07de3aa7fc2910cdd2276e5effdd8832265d39/data/zdate2.packed.dat -------------------------------------------------------------------------------- /data/zdate2.unpacked.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glycerine/go-capnproto/2d07de3aa7fc2910cdd2276e5effdd8832265d39/data/zdate2.unpacked.dat -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package capn is a capnproto library for go 3 | 4 | see http://kentonv.github.io/capnproto/ 5 | 6 | capnpc-go provides the compiler backend for capnp 7 | after installing to $PATH capnp files can be compiled with 8 | 9 | capnp compile -ogo *.capnp 10 | 11 | capnpc-go requires two annotations for all types. This is the package and 12 | import found in go.capnp. Package is needed to know what package to place at 13 | the head of the generated file and what go name to use when referring to the 14 | type from another package. Import should be the fully qualified import path 15 | and is used to generate import statement from other packages and to detect 16 | when two types are in the same package. Typically these are added as file 17 | annotations. For example: 18 | 19 | using Go = import "github.com/glycerine/go-capnproto/go.capnp"; 20 | $Go.package("main"); 21 | $Go.import("github.com/glycerine/go-capnproto/example"); 22 | 23 | In capnproto, the unit of communication is a message. A message 24 | consists of one or more of segments to allow easier allocation, but 25 | ideally and typically you just make one segment per message. 26 | 27 | Logically, a message organized in a tree of objects, with the root 28 | always being a struct (as opposed to a list or primitive). 29 | 30 | Here is an example of writing a new message. We use the demo schema 31 | aircraft.capnp from the aircraftlib directory. You may wish to read 32 | the schema before reading this example. 33 | 34 | << Example moved to its own file: See the file, write_test.go >> 35 | 36 | In summary, when you make a new message, you should first make new segment, 37 | and then create the root struct in that segment. Then add your non-child 38 | (contained) objects. This is because, as the spec says: 39 | 40 | The first word of the first segment of the message 41 | is always a pointer pointing to the message's root 42 | struct. 43 | 44 | 45 | All objects are values with pointer semantics that point into the data 46 | in a message or segment. Messages can be read/written from a stream 47 | uncompressed or using the capnproto compression. 48 | 49 | In this library a *Segment is taken to refer to both a specific segment as 50 | well as the containing message. This is to reduce the number of types generic 51 | code needs to deal with and allows objects to be created in the same segment 52 | as their outer object (thus reducing the number of far pointers). 53 | 54 | Most getters/setters in the library don't return an error. Instead a get that 55 | fails due to an invalid pointer, out of bounds, etc will return the default 56 | value. A invalid set will be noop'ed. If you really need to know whether a set 57 | succeeds then errors are provided by the lower level Object methods. 58 | 59 | Since go doesn't have any templating, lists are created for the basic types 60 | and one level of named types. The list of basic types (e.g. List(UInt8), 61 | List(Text), etc) are all provided in this library. Lists of user named types 62 | are created with the user types (e.g. user struct Foo will create a Foo_List 63 | type). capnp schemas that use deeper lists (e.g. List(List(UInt8))) will use 64 | PointerList and the user will have to use the Object.ToList* functions to cast 65 | to the correct type. 66 | 67 | For adding documentation comments to the generated code, there's the doc 68 | annotation. This annotation adds the comment to a struct, enum or field so 69 | that godoc will pick it up. For Example: 70 | 71 | struct Zdate $Go.doc("Zdate represents an instance in time") { 72 | year @0 :Int16; 73 | month @1 :UInt8; 74 | day @2 :UInt8 ; 75 | } 76 | 77 | Structs 78 | 79 | capnpc-go will generate the following for structs: 80 | 81 | // Foo is a value with pointer semantics referencing the data in a 82 | // segment. Member functions are provided to get/set members in the 83 | // struct. Getters/setters of an outer struct will use values of type 84 | // Foo to set/get pointers. 85 | type Foo capn.Struct 86 | 87 | // NewFoo creates a new orphaned Foo struct. This can then be added to 88 | // a message by using a Set function which takes a Foo argument. 89 | func NewFoo(s *capn.Segment) Foo 90 | 91 | // NewRootFoo creates a new root of type Foo in the next unused space in the 92 | // provided segment. This is distinct from NewFoo as this always 93 | // creates a root tag. Typically the provided segment should be empty. 94 | // Remember that a message is a tree of objects with a single root, and 95 | // you usually have to create the root before any other object in a 96 | // segment. The only exception would be for a multi-segment message. 97 | func NewRootFoo(s *capn.Segment) Foo 98 | 99 | // ReadRootFoo reads the root tag at the beginning of the provided 100 | // segment and returns it as a Foo struct. 101 | func ReadRootFoo(s *capn.Segment) Foo 102 | 103 | // Foo_List is a value with pointer semantics. It is created for all 104 | // structs, and is used for List(Foo) in the capnp file. 105 | type Foo_List capn.List 106 | 107 | // NewFooList creates a new orphaned List(Foo). This can then be added 108 | // to a message by using a Set function which takes a Foo_List. sz 109 | // specifies the list size. Due to the list using memory directly in 110 | // the outgoing buffer (i.e. arena style memory management), the size 111 | // can not be changed after creation. 112 | func NewFooList(s *capn.Segment, sz int) Foo_List 113 | 114 | // Len returns the list length. For composite lists this is the number 115 | // of list elements. 116 | func (s Foo_List) Len() int 117 | 118 | // At returns a pointer to the i'th element. If i is an invalid index, 119 | // this will return a null Foo (all getters will return default 120 | // values, setters will fail). For a composite list the returned value 121 | // will be a list member. Setting another value to point to list 122 | // members forces a copy of the data. For pointer lists, the pointer 123 | // value will be auto-derefenced. 124 | func (s Foo_List) At(i int) Foo 125 | 126 | // ToArray converts the capnproto list into a go list. For large lists 127 | // this is inefficient as it has to read all elements. This can be 128 | // quite convenient especially for iterating as it lets you use a for 129 | // range clause: 130 | // for i, f := range mylist.ToArray() {} 131 | func (s Foo_List) ToArray() []Foo 132 | 133 | 134 | 135 | Groups 136 | 137 | For each group a typedef is created with a different method set for just the 138 | groups fields: 139 | 140 | struct Foo { 141 | group :Group { 142 | field @0 :Bool; 143 | } 144 | } 145 | 146 | type Foo capn.Struct 147 | type FooGroup Foo 148 | 149 | func (s Foo) Group() FooGroup 150 | func (s FooGroup) Field() bool 151 | 152 | That way the following may be used to access a field in a group: 153 | 154 | var f Foo 155 | value := f.Group().Field() 156 | 157 | Note that Group accessors just cast the type and so have no overhead 158 | 159 | func (s Foo) Group() FooGroup {return FooGroup(s)} 160 | 161 | 162 | 163 | Unions 164 | 165 | Named unions are treated as a group with an inner unnamed union. Unnamed 166 | unions generate an enum Type_Which and a corresponding Which() function: 167 | 168 | struct Foo { 169 | union { 170 | a @0 :Bool; 171 | b @1 :Bool; 172 | } 173 | } 174 | 175 | type Foo_Which uint16 176 | 177 | const ( 178 | FOO_A Foo_Which = 0 179 | FOO_B = 1 180 | ) 181 | 182 | func (s Foo) A() bool 183 | func (s Foo) B() bool 184 | func (s Foo) SetA(v bool) 185 | func (s Foo) SetB(v bool) 186 | func (s Foo) Which() Foo_Which 187 | 188 | Which() should be checked before using the getters, and the default case must 189 | always be handled. 190 | 191 | Setters for single values will set the union discriminator as well as set the 192 | value. 193 | 194 | For voids in unions, there is a void setter that just sets the discriminator. 195 | For example: 196 | 197 | struct Foo { 198 | union { 199 | a @0 :Void; 200 | b @1 :Void; 201 | } 202 | } 203 | 204 | f.SetA() // Set that we are using A 205 | f.SetB() // Set that we are using B 206 | 207 | For groups in unions, there is a group setter that just sets the 208 | discriminator. This must be called before the group getter can be used to set 209 | values. For example: 210 | 211 | struct Foo { 212 | union { 213 | a :group { 214 | v :Bool 215 | } 216 | b :group { 217 | v :Bool 218 | } 219 | } 220 | } 221 | 222 | f.SetA() // Set that we are using group A 223 | f.A().SetV(true) // then we can use the group A getter to set the inner values 224 | 225 | 226 | 227 | Enums 228 | 229 | capnpc-go generates enum values in all caps. For example in the capnp file: 230 | 231 | enum ElementSize { 232 | empty @0; 233 | bit @1; 234 | byte @2; 235 | twoBytes @3; 236 | fourBytes @4; 237 | eightBytes @5; 238 | pointer @6; 239 | inlineComposite @7; 240 | } 241 | 242 | In the generated capnp.go file: 243 | 244 | type ElementSize uint16 245 | 246 | const ( 247 | ELEMENTSIZE_EMPTY ElementSize = 0 248 | ELEMENTSIZE_BIT = 1 249 | ELEMENTSIZE_BYTE = 2 250 | ELEMENTSIZE_TWOBYTES = 3 251 | ELEMENTSIZE_FOURBYTES = 4 252 | ELEMENTSIZE_EIGHTBYTES = 5 253 | ELEMENTSIZE_POINTER = 6 254 | ELEMENTSIZE_INLINECOMPOSITE = 7 255 | ) 256 | 257 | In addition an enum.String() function is generated that will convert the constants to a string 258 | for debugging or logging purposes. By default, the enum name is used as the tag value, 259 | but the tags can be customized with a $Go.tag or $Go.notag annotation. 260 | 261 | For example: 262 | 263 | enum ElementSize { 264 | empty @0 $Go.tag("void"); 265 | bit @1 $Go.tag("1 bit"); 266 | byte @2 $Go.tag("8 bits"); 267 | inlineComposite @7 $Go.notag; 268 | } 269 | 270 | In the generated go file: 271 | 272 | func (c ElementSize) String() string { 273 | switch c { 274 | case ELEMENTSIZE_EMPTY: 275 | return "void" 276 | case ELEMENTSIZE_BIT: 277 | return "1 bit" 278 | case ELEMENTSIZE_BYTE: 279 | return "8 bits" 280 | default: 281 | return "" 282 | } 283 | } 284 | */ 285 | package capn 286 | -------------------------------------------------------------------------------- /enum_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/glycerine/go-capnproto" 7 | air "github.com/glycerine/go-capnproto/aircraftlib" 8 | cv "github.com/glycerine/goconvey/convey" 9 | ) 10 | 11 | // For enums like Airport, their lists should have a set method 12 | func TestEnumListHasSet(t *testing.T) { 13 | 14 | seg := capn.NewBuffer(nil) 15 | z := air.NewRootZ(seg) 16 | airc := air.AutoNewAircraft(seg) 17 | b737 := air.AutoNewB737(seg) 18 | base := air.AutoNewPlaneBase(seg) 19 | base.SetName("helen") 20 | homes := air.NewAirportList(seg, 2) 21 | 22 | // test is here!! 23 | // these next two lines should compile, because there should be a Set method. 24 | homes.Set(0, air.AIRPORT_JFK) 25 | homes.Set(1, air.AIRPORT_SFO) 26 | base.SetHomes(homes) 27 | b737.SetBase(base) 28 | airc.SetB737(b737) 29 | z.SetAircraft(airc) 30 | j, err := z.MarshalCapLit() 31 | panicOn(err) 32 | 33 | cv.Convey("To confirm that enum lists have a Set() method: Given the aircraftlib schema (and an Aircraft value), we should generate a MarshalCapLit() function that returns a literal representation in bytes for the given Aircraft value ", t, func() { 34 | cv.So(string(j), cv.ShouldEqual, `(aircraft = (b737 = (base = (name = "helen", homes = [jfk, sfo], rating = 0, canFly = false, capacity = 0, maxSpeed = 0))))`) 35 | }) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | 8 | capn "github.com/glycerine/go-capnproto" 9 | air "github.com/glycerine/go-capnproto/aircraftlib" 10 | ) 11 | 12 | func ExampleReadFromStream() { 13 | s := capn.NewBuffer(nil) 14 | d := air.NewRootZdate(s) 15 | d.SetYear(2004) 16 | d.SetMonth(12) 17 | d.SetDay(7) 18 | buf := bytes.Buffer{} 19 | s.WriteTo(&buf) 20 | 21 | fmt.Println(hex.EncodeToString(buf.Bytes())) 22 | 23 | // Read 24 | s, err := capn.ReadFromStream(&buf, nil) 25 | if err != nil { 26 | fmt.Printf("read error %v\n", err) 27 | return 28 | } 29 | d = air.ReadRootZdate(s) 30 | fmt.Printf("year %d, month %d, day %d\n", d.Year(), d.Month(), d.Day()) 31 | } 32 | -------------------------------------------------------------------------------- /exists.go: -------------------------------------------------------------------------------- 1 | package capn 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func fileExists(name string) bool { 8 | fi, err := os.Stat(name) 9 | if err != nil { 10 | return false 11 | } 12 | if fi.IsDir() { 13 | return false 14 | } 15 | return true 16 | } 17 | 18 | func dirExists(name string) bool { 19 | fi, err := os.Stat(name) 20 | if err != nil { 21 | return false 22 | } 23 | if fi.IsDir() { 24 | return true 25 | } 26 | return false 27 | } 28 | -------------------------------------------------------------------------------- /go.capnp: -------------------------------------------------------------------------------- 1 | @0xd12a1c51fedd6c88; 2 | annotation package(file) :Text; 3 | annotation import(file) :Text; 4 | annotation doc(struct, field, enum) :Text; 5 | annotation tag(enumerant) : Text; 6 | annotation notag(enumerant) : Void; 7 | annotation customtype(field) : Text; 8 | annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text; 9 | $package("capn"); 10 | -------------------------------------------------------------------------------- /go.capnp.go: -------------------------------------------------------------------------------- 1 | package capn 2 | 3 | const Package = uint64(0xbea97f1023792be0) 4 | const Import = uint64(0xe130b601260e44b5) 5 | const Doc = uint64(0xc58ad6bd519f935e) 6 | const Tag = uint64(0xa574b41924caefc7) 7 | const Notag = uint64(0xc8768679ec52e012) 8 | const Customtype = uint64(0xfa10659ae02f2093) 9 | const Name = uint64(0xc2b96012172f8df1) 10 | -------------------------------------------------------------------------------- /json.go: -------------------------------------------------------------------------------- 1 | package capn 2 | 3 | // If you want to omit the json support 4 | // in the generated code, to save space, 5 | // it can be disabled here. 6 | const JSON_enabled = true 7 | -------------------------------------------------------------------------------- /mem.go: -------------------------------------------------------------------------------- 1 | package capn 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "io" 8 | "math" 9 | ) 10 | 11 | var ( 12 | errBufferCall = errors.New("capn: can't call on a memory buffer") 13 | ErrInvalidSegment = errors.New("capn: invalid segment id") 14 | ErrTooMuchData = errors.New("capn: too much data in stream") 15 | ) 16 | 17 | type buffer Segment 18 | 19 | // NewBuffer creates an expanding single segment buffer. Creating new objects 20 | // will expand the buffer. Data can be nil (or length 0 with some capacity) if 21 | // creating a new session. If parsing an existing segment then data should be 22 | // the segment contents and will not be copied. 23 | func NewBuffer(data []byte) *Segment { 24 | if uint64(len(data)) > uint64(math.MaxUint32) { 25 | return nil 26 | } 27 | 28 | b := &buffer{} 29 | b.Message = b 30 | b.Data = data 31 | return (*Segment)(b) 32 | } 33 | 34 | func (b *buffer) NewSegment(minsz int) (*Segment, error) { 35 | if minsz < 4096 { 36 | minsz = 4096 37 | } 38 | if uint64(len(b.Data)) > uint64(math.MaxUint32)-uint64(minsz) { 39 | return nil, ErrOverlarge 40 | } 41 | b.Data = append(b.Data, make([]byte, minsz)...) 42 | b.Data = b.Data[:len(b.Data)-minsz] 43 | return (*Segment)(b), nil 44 | } 45 | 46 | func (b *buffer) Lookup(segid uint32) (*Segment, error) { 47 | if segid == 0 { 48 | return (*Segment)(b), nil 49 | } else { 50 | return nil, ErrInvalidSegment 51 | } 52 | } 53 | 54 | type MultiBuffer struct { 55 | Segments []*Segment 56 | } 57 | 58 | // NewMultiBuffer creates a new multi segment message. Creating new objects 59 | // will try and reuse the buffers available, but will create new ones if there 60 | // is insufficient capacity. When parsing an existing message data should be 61 | // the list of segments. The data buffers will not be copied. 62 | func NewMultiBuffer(data [][]byte) *Segment { 63 | m := &MultiBuffer{make([]*Segment, len(data))} 64 | for i, d := range data { 65 | m.Segments[i] = &Segment{m, d, uint32(i), false} 66 | } 67 | if len(data) > 0 { 68 | return m.Segments[0] 69 | } 70 | return &Segment{Message: m, Data: nil, Id: 0xFFFFFFFF, RootDone: false} 71 | } 72 | 73 | var ( 74 | MaxSegmentNumber = 1024 75 | MaxTotalSize = 1024 * 1024 * 1024 76 | ) 77 | 78 | func (m *MultiBuffer) NewSegment(minsz int) (*Segment, error) { 79 | for _, s := range m.Segments { 80 | if len(s.Data)+minsz <= cap(s.Data) { 81 | return s, nil 82 | } 83 | } 84 | 85 | if minsz < 4096 { 86 | minsz = 4096 87 | } 88 | s := &Segment{m, make([]byte, 0, minsz), uint32(len(m.Segments)), false} 89 | m.Segments = append(m.Segments, s) 90 | return s, nil 91 | } 92 | 93 | func (m *MultiBuffer) Lookup(segid uint32) (*Segment, error) { 94 | if uint(segid) < uint(len(m.Segments)) { 95 | return m.Segments[segid], nil 96 | } else { 97 | return nil, ErrInvalidSegment 98 | } 99 | } 100 | 101 | // ReadFromStream reads a non-packed serialized stream from r. buf is used to 102 | // buffer the read contents, can be nil, and is provided so that the buffer 103 | // can be reused between messages. The returned segment is the first segment 104 | // read, which contains the root pointer. 105 | // 106 | // Warning about buf reuse: It is safer to just pass nil for buf. 107 | // When making multiple calls to ReadFromStream() with the same buf argument, you 108 | // may overwrite the data in a previously returned Segment. 109 | // The re-use of buf is an optimization for when you are actually 110 | // done with any previously returned Segment which may have data still alive 111 | // in buf. 112 | // 113 | func ReadFromStream(r io.Reader, buf *bytes.Buffer) (*Segment, error) { 114 | if buf == nil { 115 | buf = new(bytes.Buffer) 116 | } else { 117 | buf.Reset() 118 | } 119 | 120 | if _, err := io.CopyN(buf, r, 4); err != nil { 121 | return nil, err 122 | } 123 | 124 | if binary.LittleEndian.Uint32(buf.Bytes()[:]) >= uint32(MaxSegmentNumber) { 125 | return nil, ErrTooMuchData 126 | } 127 | 128 | segnum := int(binary.LittleEndian.Uint32(buf.Bytes()[:]) + 1) 129 | hdrsz := 8*(segnum/2) + 4 130 | 131 | if _, err := io.CopyN(buf, r, int64(hdrsz)); err != nil { 132 | return nil, err 133 | } 134 | 135 | total := 0 136 | for i := 0; i < segnum; i++ { 137 | sz := binary.LittleEndian.Uint32(buf.Bytes()[4*i+4:]) 138 | if uint64(total)+uint64(sz)*8 > uint64(MaxTotalSize) { 139 | return nil, ErrTooMuchData 140 | } 141 | total += int(sz) * 8 142 | } 143 | 144 | if _, err := io.CopyN(buf, r, int64(total)); err != nil { 145 | return nil, err 146 | } 147 | 148 | hdrv := buf.Bytes()[4 : hdrsz+4] 149 | datav := buf.Bytes()[hdrsz+4:] 150 | 151 | if segnum == 1 { 152 | sz := int(binary.LittleEndian.Uint32(hdrv)) * 8 153 | return NewBuffer(datav[:sz]), nil 154 | } 155 | 156 | m := &MultiBuffer{make([]*Segment, segnum)} 157 | for i := 0; i < segnum; i++ { 158 | sz := int(binary.LittleEndian.Uint32(hdrv[4*i:])) * 8 159 | m.Segments[i] = &Segment{m, datav[:sz:sz], uint32(i), false} 160 | datav = datav[sz:] 161 | } 162 | 163 | return m.Segments[0], nil 164 | } 165 | 166 | // ReadFromMemoryZeroCopy: like ReadFromStream, but reads a non-packed 167 | // serialized stream that already resides in memory in the argument data. 168 | // The returned segment is the first segment read, which contains 169 | // the root pointer. The returned bytesRead says how many bytes were 170 | // consumed from data in making seg. The caller should advance the 171 | // data slice by doing data = data[bytesRead:] between successive calls 172 | // to ReadFromMemoryZeroCopy(). 173 | func ReadFromMemoryZeroCopy(data []byte) (seg *Segment, bytesRead int64, err error) { 174 | 175 | dataLen := len(data) 176 | if dataLen < 4 { 177 | return nil, 0, io.EOF 178 | } 179 | 180 | if binary.LittleEndian.Uint32(data[0:4]) >= uint32(MaxSegmentNumber) { 181 | return nil, 0, ErrTooMuchData 182 | } 183 | 184 | segnum := int(binary.LittleEndian.Uint32(data[0:4]) + 1) 185 | hdrsz := 8*(segnum/2) + 4 186 | 187 | if dataLen < hdrsz+4 { 188 | return nil, 0, io.EOF 189 | } 190 | b := data[0:(hdrsz + 4)] 191 | 192 | total := 0 193 | for i := 0; i < segnum; i++ { 194 | sz := binary.LittleEndian.Uint32(b[4*i+4:]) 195 | if uint64(total)+uint64(sz)*8 > uint64(MaxTotalSize) { 196 | return nil, 0, ErrTooMuchData 197 | } 198 | total += int(sz) * 8 199 | } 200 | if total == 0 { 201 | return nil, 0, io.EOF 202 | } 203 | 204 | hdrv := data[4:(hdrsz + 4)] 205 | datav := data[hdrsz+4:] 206 | m := &MultiBuffer{make([]*Segment, segnum)} 207 | for i := 0; i < segnum; i++ { 208 | sz := int(binary.LittleEndian.Uint32(hdrv[4*i:])) * 8 209 | if len(datav) < sz { 210 | return nil, 0, io.EOF 211 | } 212 | m.Segments[i] = &Segment{m, datav[:sz], uint32(i), false} 213 | datav = datav[sz:] 214 | } 215 | 216 | return m.Segments[0], int64(4 + hdrsz + total), nil 217 | } 218 | 219 | func NewSingleSegmentMultiBuffer() *MultiBuffer { 220 | m := &MultiBuffer{make([]*Segment, 1)} 221 | m.Segments[0] = &Segment{} 222 | return m 223 | } 224 | 225 | // ReadFromMemoryZeroCopyNoAlloc: like ReadFromMemoryZeroCopy, 226 | // but avoid all allocations so we get zero GC pressure. 227 | // 228 | // This requires some strict but easy to meet pre-requisites: 229 | // 230 | // PRE: the capnp bytes in data must come from only one segment. Else we panic. 231 | // PRE: multi must point to an existing MultiBuffer that has exactly one Segment 232 | // that will be re-used and over-written. If in doubt, 233 | // you can allocate a correct new one the first time 234 | // by calling NewSingleSegmentMultiBuffer(). 235 | // 236 | func ReadFromMemoryZeroCopyNoAlloc(data []byte, multi *MultiBuffer) (bytesRead int64, err error) { 237 | 238 | if len(data) < 4 { 239 | return 0, io.EOF 240 | } 241 | 242 | if binary.LittleEndian.Uint32(data[0:4]) >= uint32(MaxSegmentNumber) { 243 | return 0, ErrTooMuchData 244 | } 245 | 246 | segnum := int(binary.LittleEndian.Uint32(data[0:4]) + 1) 247 | if segnum != 1 { 248 | panic("only one segment allowed in data read in with ReadFromMemoryZeroCopyNoAlloc()") 249 | } 250 | if multi == nil { 251 | panic("multi must point to an existing MultiBuffer with a single Segment") 252 | } 253 | if len(multi.Segments) != 1 { 254 | panic("only one segment allowed in the multi *MultiBuffer used in ReadFromMemoryZeroCopyNoAlloc()") 255 | } 256 | if multi.Segments[0] == nil { 257 | panic("multi.Segment[0] must point to an allocated Segment{} to be resused in ReadFromMemoryZeroCopyNoAlloc()") 258 | } 259 | 260 | hdrsz := 8*(segnum/2) + 4 261 | 262 | b := data[0:(hdrsz + 4)] 263 | 264 | total := 0 265 | for i := 0; i < segnum; i++ { 266 | sz := binary.LittleEndian.Uint32(b[4*i+4:]) 267 | if uint64(total)+uint64(sz)*8 > uint64(MaxTotalSize) { 268 | return 0, ErrTooMuchData 269 | } 270 | total += int(sz) * 8 271 | } 272 | if total == 0 { 273 | return 0, io.EOF 274 | } 275 | 276 | hdrv := data[4:(hdrsz + 4)] 277 | datav := data[hdrsz+4:] 278 | sz := int(binary.LittleEndian.Uint32(hdrv)) * 8 279 | seg := multi.Segments[0] 280 | seg.Message = multi 281 | seg.Data = datav[:sz] 282 | seg.Id = 0 283 | seg.RootDone = false 284 | datav = datav[sz:] 285 | 286 | return int64(4 + hdrsz + total), nil 287 | } 288 | 289 | // WriteTo writes the message that the segment is part of to the 290 | // provided stream in serialized form. 291 | func (s *Segment) WriteTo(w io.Writer) (int64, error) { 292 | segnum := uint32(1) 293 | for { 294 | if seg, _ := s.Message.Lookup(segnum); seg == nil { 295 | break 296 | } 297 | segnum++ 298 | } 299 | 300 | hdrv := make([]uint8, 8*(segnum/2)+8) 301 | binary.LittleEndian.PutUint32(hdrv, segnum-1) 302 | for i := uint32(0); i < segnum; i++ { 303 | seg, _ := s.Message.Lookup(i) 304 | binary.LittleEndian.PutUint32(hdrv[4*i+4:], uint32(len(seg.Data)/8)) 305 | } 306 | 307 | if n, err := w.Write(hdrv); err != nil { 308 | return int64(n), err 309 | } 310 | written := int64(len(hdrv)) 311 | 312 | for i := uint32(0); i < segnum; i++ { 313 | seg, _ := s.Message.Lookup(i) 314 | if n, err := w.Write(seg.Data); err != nil { 315 | return written + int64(n), err 316 | } else { 317 | written += int64(n) 318 | } 319 | } 320 | 321 | return written, nil 322 | } 323 | -------------------------------------------------------------------------------- /nested_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "testing" 9 | 10 | capn "github.com/glycerine/go-capnproto" 11 | air "github.com/glycerine/go-capnproto/aircraftlib" 12 | cv "github.com/glycerine/goconvey/convey" 13 | ) 14 | 15 | // demonstrate and test serialization to List(List(Struct(List))), nested lists. 16 | 17 | // start with smaller Struct(List) 18 | func Test001StructList(t *testing.T) { 19 | 20 | cv.Convey("Given type Nester1 struct { Strs []string } in go, where Nester1 is a struct, and a mirror/parallel capnp struct air.Nester1Capn { strs @0: List(Text); } defined in the aircraftlib schema", t, func() { 21 | cv.Convey("When we Save() Nester to capn and then Load() it back, the data should match, so that we have working Struct(List) serialization and deserializatoin in go-capnproto", func() { 22 | 23 | // Does Nester1 alone serialization and deser okay? 24 | rw := Nester1{Strs: []string{"xenophilia", "watchowski"}} 25 | 26 | var o bytes.Buffer 27 | rw.Save(&o) 28 | 29 | seg, n, err := capn.ReadFromMemoryZeroCopy(o.Bytes()) 30 | cv.So(err, cv.ShouldEqual, nil) 31 | cv.So(n, cv.ShouldBeGreaterThan, 0) 32 | 33 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "Nester1Capn") 34 | if false { 35 | fmt.Printf("text = '%s'\n", text) 36 | } 37 | rw2 := &Nester1{} 38 | rw2.Load(&o) 39 | 40 | //fmt.Printf("rw = '%#v'\n", rw) 41 | //fmt.Printf("rw2 = '%#v'\n", rw2) 42 | 43 | same := reflect.DeepEqual(&rw, rw2) 44 | cv.So(same, cv.ShouldEqual, true) 45 | }) 46 | }) 47 | } 48 | 49 | func Test002ListListStructList(t *testing.T) { 50 | 51 | cv.Convey("Given type RWTest struct { NestMatrix [][]Nester1; } in go, where Nester1 is a struct, and a mirror/parallel capnp struct air.RWTestCapn { nestMatrix @0: List(List(Nester1Capn)); } defined in the aircraftlib schema", t, func() { 52 | cv.Convey("When we Save() RWTest to capn and then Load() it back, the data should match, so that we have working List(List(Struct)) serialization and deserializatoin in go-capnproto", func() { 53 | 54 | // full RWTest 55 | rw := RWTest{ 56 | NestMatrix: [][]Nester1{[]Nester1{Nester1{Strs: []string{"z", "w"}}, Nester1{Strs: []string{"q", "r"}}}, []Nester1{Nester1{Strs: []string{"zebra", "wally"}}, Nester1{Strs: []string{"qubert", "rocks"}}}}, 57 | } 58 | 59 | var o bytes.Buffer 60 | rw.Save(&o) 61 | 62 | seg, n, err := capn.ReadFromMemoryZeroCopy(o.Bytes()) 63 | cv.So(err, cv.ShouldEqual, nil) 64 | cv.So(n, cv.ShouldBeGreaterThan, 0) 65 | 66 | text := CapnpDecodeSegment(seg, "", "aircraftlib/aircraft.capnp", "RWTestCapn") 67 | 68 | if false { 69 | fmt.Printf("text = '%s'\n", text) 70 | } 71 | 72 | rw2 := &RWTest{} 73 | rw2.Load(&o) 74 | 75 | //fmt.Printf("rw = '%#v'\n", rw) 76 | //fmt.Printf("rw2 = '%#v'\n", rw2) 77 | 78 | same := reflect.DeepEqual(&rw, rw2) 79 | cv.So(same, cv.ShouldEqual, true) 80 | }) 81 | }) 82 | } 83 | 84 | type Nester1 struct { 85 | Strs []string 86 | } 87 | 88 | type RWTest struct { 89 | NestMatrix [][]Nester1 90 | } 91 | 92 | func (s *Nester1) Save(w io.Writer) { 93 | seg := capn.NewBuffer(nil) 94 | Nester1GoToCapn(seg, s) 95 | seg.WriteTo(w) 96 | } 97 | 98 | func Nester1GoToCapn(seg *capn.Segment, src *Nester1) air.Nester1Capn { 99 | //fmt.Printf("\n\n Nester1GoToCapn sees seg = '%#v'\n", seg) 100 | dest := air.AutoNewNester1Capn(seg) 101 | 102 | mylist1 := seg.NewTextList(len(src.Strs)) 103 | for i := range src.Strs { 104 | mylist1.Set(i, string(src.Strs[i])) 105 | } 106 | dest.SetStrs(mylist1) 107 | 108 | //fmt.Printf("after Nester1GoToCapn setting\n") 109 | return dest 110 | } 111 | 112 | func Nester1CapnToGo(src air.Nester1Capn, dest *Nester1) *Nester1 { 113 | if dest == nil { 114 | dest = &Nester1{} 115 | } 116 | dest.Strs = src.Strs().ToArray() 117 | 118 | return dest 119 | } 120 | 121 | func (s *Nester1) Load(r io.Reader) { 122 | capMsg, err := capn.ReadFromStream(r, nil) 123 | if err != nil { 124 | panic(fmt.Errorf("capn.ReadFromStream error: %s", err)) 125 | } 126 | z := air.ReadRootNester1Capn(capMsg) 127 | Nester1CapnToGo(z, s) 128 | } 129 | 130 | func (s *RWTest) Save(w io.Writer) { 131 | seg := capn.NewBuffer(nil) 132 | RWTestGoToCapn(seg, s) 133 | seg.WriteTo(w) 134 | } 135 | 136 | func (s *RWTest) Load(r io.Reader) { 137 | capMsg, err := capn.ReadFromStream(r, nil) 138 | if err != nil { 139 | panic(fmt.Errorf("capn.ReadFromStream error: %s", err)) 140 | } 141 | z := air.ReadRootRWTestCapn(capMsg) 142 | RWTestCapnToGo(z, s) 143 | } 144 | 145 | func RWTestCapnToGo(src air.RWTestCapn, dest *RWTest) *RWTest { 146 | if dest == nil { 147 | dest = &RWTest{} 148 | } 149 | var n int 150 | // NestMatrix 151 | n = src.NestMatrix().Len() 152 | dest.NestMatrix = make([][]Nester1, n) 153 | for i := 0; i < n; i++ { 154 | dest.NestMatrix[i] = Nester1CapnListToSliceNester1(air.Nester1Capn_List(src.NestMatrix().At(i))) 155 | } 156 | 157 | return dest 158 | } 159 | 160 | func RWTestGoToCapn(seg *capn.Segment, src *RWTest) air.RWTestCapn { 161 | dest := air.AutoNewRWTestCapn(seg) 162 | 163 | // NestMatrix -> Nester1Capn (go slice to capn list) 164 | if len(src.NestMatrix) > 0 { 165 | //typedList := air.NewNester1CapnList(seg, len(src.NestMatrix)) 166 | plist := seg.NewPointerList(len(src.NestMatrix)) 167 | i := 0 168 | for _, ele := range src.NestMatrix { 169 | //plist.Set(i, capn.Object(Nester1GoToCapn(seg, &ele))) 170 | r := capn.Object(SliceNester1ToNester1CapnList(seg, ele)) 171 | plist.Set(i, r) 172 | i++ 173 | } 174 | //dest.SetNestMatrix(typedList) 175 | dest.SetNestMatrix(plist) 176 | } 177 | 178 | return dest 179 | } 180 | 181 | func Nester1CapnListToSliceNester1(p air.Nester1Capn_List) []Nester1 { 182 | v := make([]Nester1, p.Len()) 183 | for i := range v { 184 | Nester1CapnToGo(p.At(i), &v[i]) 185 | } 186 | return v 187 | } 188 | 189 | func SliceNester1ToNester1CapnList(seg *capn.Segment, m []Nester1) air.Nester1Capn_List { 190 | lst := air.NewNester1CapnList(seg, len(m)) 191 | for i := range m { 192 | lst.Set(i, Nester1GoToCapn(seg, &m[i])) 193 | } 194 | return lst 195 | } 196 | 197 | func SliceStringToTextList(seg *capn.Segment, m []string) capn.TextList { 198 | lst := seg.NewTextList(len(m)) 199 | for i := range m { 200 | lst.Set(i, string(m[i])) 201 | } 202 | return lst 203 | } 204 | 205 | func TextListToSliceString(p capn.TextList) []string { 206 | v := make([]string, p.Len()) 207 | for i := range v { 208 | v[i] = string(p.At(i)) 209 | } 210 | return v 211 | } 212 | -------------------------------------------------------------------------------- /notrunc_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | capn "github.com/glycerine/go-capnproto" 8 | air "github.com/glycerine/go-capnproto/aircraftlib" 9 | cv "github.com/glycerine/goconvey/convey" 10 | ) 11 | 12 | func TestDataVersioningAvoidsUnnecessaryTruncation(t *testing.T) { 13 | 14 | expFull := CapnpEncode("(val = 9, duo = 8, ptr1 = (val = 77), ptr2 = (val = 55))", "VerTwoDataTwoPtr") 15 | //expEmpty := CapnpEncode("()", "VerEmpty") 16 | 17 | cv.Convey("Given a struct with 0 ptr fields, and a newer version of the struct with two data and two pointer fields", t, func() { 18 | cv.Convey("then old code expecting the smaller struct but reading the newer-bigger struct should not truncate it if it doesn't have to (e.g. not assigning into a composite list), and should preserve all data when re-serializing it.", func() { 19 | 20 | seg := capn.NewBuffer(nil) 21 | scratch := capn.NewBuffer(nil) 22 | 23 | big := air.NewRootVerTwoDataTwoPtr(seg) 24 | one := air.NewVerOneData(scratch) 25 | one.SetVal(77) 26 | two := air.NewVerOneData(scratch) 27 | two.SetVal(55) 28 | big.SetVal(9) 29 | big.SetDuo(8) 30 | big.SetPtr1(one) 31 | big.SetPtr2(two) 32 | 33 | bigVerBytes := ShowSeg("\n\n with our 2x2 new big struct, segment seg is:", seg) 34 | 35 | cv.So(bigVerBytes, cv.ShouldResemble, expFull) 36 | 37 | // now pretend to be an old client, reading and writing 38 | // expecting an empty struct, but full data should be preserved 39 | // and written, because we aren't writing into a cramped/ 40 | // fixed-space composite-list space. 41 | 42 | // Before test, verify that if we force reading into text-form, we get 43 | // what we expect. 44 | actEmptyCap := string(CapnpDecode(bigVerBytes, "VerEmpty")) 45 | cv.So(actEmptyCap, cv.ShouldResemble, "()\n") 46 | 47 | // okay, now the actual test: 48 | weThinkEmptyButActuallyFull := air.ReadRootVerEmpty(seg) 49 | 50 | freshSeg := capn.NewBuffer(nil) 51 | wrapEmpty := air.NewRootWrapEmpty(freshSeg) 52 | 53 | // here is the critical step, this should not truncate: 54 | wrapEmpty.SetMightNotBeReallyEmpty(weThinkEmptyButActuallyFull) 55 | 56 | // now verify: 57 | freshBytes := ShowSeg("\n\n after wrapEmpty.SetMightNotBeReallyEmpty(weThinkEmptyButActuallyFull), segment freshSeg is:", freshSeg) 58 | 59 | reseg, _, err := capn.ReadFromMemoryZeroCopy(freshBytes) 60 | if err != nil { 61 | panic(err) 62 | } 63 | ShowSeg(" after re-reading freshBytes, segment reseg is:", reseg) 64 | fmt.Printf("freshBytes decoded by capnp as Wrap2x2: '%s'\n", string(CapnpDecode(freshBytes, "Wrap2x2"))) 65 | 66 | wrap22 := air.ReadRootWrap2x2plus(reseg) 67 | notEmpty := wrap22.MightNotBeReallyEmpty() 68 | val := notEmpty.Val() 69 | cv.So(val, cv.ShouldEqual, 9) 70 | duo := notEmpty.Duo() 71 | cv.So(duo, cv.ShouldEqual, 8) 72 | ptr1 := notEmpty.Ptr1() 73 | ptr2 := notEmpty.Ptr2() 74 | cv.So(ptr1.Val(), cv.ShouldEqual, 77) 75 | cv.So(ptr2.Val(), cv.ShouldEqual, 55) 76 | // Tre should get the default, as it was never set 77 | cv.So(notEmpty.Tre(), cv.ShouldEqual, 0) 78 | // same for Lst3 79 | cv.So(notEmpty.Lst3().Len(), cv.ShouldEqual, 0) 80 | }) 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /print_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/glycerine/go-capnproto" 7 | air "github.com/glycerine/go-capnproto/aircraftlib" 8 | cv "github.com/glycerine/goconvey/convey" 9 | ) 10 | 11 | func TestPrint(t *testing.T) { 12 | 13 | seg := capn.NewBuffer(nil) 14 | z := air.NewRootZ(seg) 15 | airc := air.AutoNewAircraft(seg) 16 | b737 := air.AutoNewB737(seg) 17 | base := air.AutoNewPlaneBase(seg) 18 | base.SetName("helen") 19 | base.SetMaxSpeed(0.5) 20 | homes := air.NewAirportList(seg, 2) 21 | homes.Set(0, air.AIRPORT_JFK) 22 | homes.Set(1, air.AIRPORT_SFO) 23 | base.SetHomes(homes) 24 | b737.SetBase(base) 25 | airc.SetB737(b737) 26 | z.SetAircraft(airc) 27 | lit, err := z.MarshalCapLit() 28 | panicOn(err) 29 | json, err := z.MarshalJSON() 30 | panicOn(err) 31 | 32 | cv.Convey("Given the aircraftlib schema (and an Aircraft value), we should generate a MarshalCapLit() function that returns a literal representation in bytes for the given Aircraft value. And the MarshalJSON() should return the expected format too.", t, func() { 33 | cv.So(string(lit), cv.ShouldEqual, `(aircraft = (b737 = (base = (name = "helen", homes = [jfk, sfo], rating = 0, canFly = false, capacity = 0, maxSpeed = 0.5))))`) 34 | cv.So(string(json), cv.ShouldEqual, `{"aircraft":{"b737":{"base":{"name":"helen","homes":["jfk", "sfo"],"rating":0,"canFly":false,"capacity":0,"maxSpeed":0.5}}}}`) 35 | }) 36 | 37 | } 38 | 39 | func panicOn(err error) { 40 | if err != nil { 41 | panic(err) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /stream.go: -------------------------------------------------------------------------------- 1 | package capn 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "errors" 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | type Compressor struct { 13 | w *bufio.Writer 14 | } 15 | 16 | type DecompParseState uint8 17 | 18 | const ( 19 | S_NORMAL DecompParseState = 0 20 | 21 | // The 1-3 states are for dealing with the 0xFF tag and the raw bytes that follow. 22 | // They tell us where to pick up if we are interrupted in the middle of anything 23 | // after the 0xFF tag, until we are done with the raw read. 24 | S_POSTFF = 1 25 | S_READN = 2 26 | S_RAW = 3 27 | ) 28 | 29 | type Decompressor struct { 30 | r io.Reader 31 | buf [8]byte 32 | bufsz int 33 | 34 | // track the bytes after a 0xff raw tag 35 | ffBuf [8]byte 36 | ffBufLoadCount int // count of bytes loaded from r into ffBuf (max 8) 37 | ffBufUsedCount int // count of bytes supplied to v during Read(). 38 | 39 | zeros int 40 | raw int // number of raw bytes left to copy through 41 | state DecompParseState 42 | } 43 | 44 | // externally available flag for compiling with debug info on/off 45 | const VerboseDecomp = false 46 | const VerboseCompress = false 47 | 48 | func NewCompressor(w io.Writer) *Compressor { 49 | return &Compressor{ 50 | w: bufio.NewWriter(w), 51 | } 52 | } 53 | 54 | // WriteToPacked writes the message that the segment is part of to the 55 | // provided stream in packed form. 56 | func (s *Segment) WriteToPacked(w io.Writer) (int64, error) { 57 | c := NewCompressor(w) 58 | return s.WriteTo(c) 59 | } 60 | 61 | func NewDecompressor(r io.Reader) *Decompressor { 62 | return &Decompressor{r: r} 63 | } 64 | 65 | // ReadFromPackedStream reads a single message from the stream r in packed 66 | // form returning the first segment. buf can be specified in order to reuse 67 | // the buffer (or it is allocated each call if nil). 68 | func ReadFromPackedStream(r io.Reader, buf *bytes.Buffer) (*Segment, error) { 69 | c := Decompressor{r: r} 70 | return ReadFromStream(&c, buf) 71 | } 72 | 73 | func min(a, b int) int { 74 | if b < a { 75 | return b 76 | } 77 | return a 78 | } 79 | 80 | func (c *Decompressor) Read(v []byte) (n int, err error) { 81 | 82 | var b [1]byte 83 | var bytesRead int 84 | 85 | for { 86 | if len(v) == 0 { 87 | return 88 | } 89 | 90 | switch c.state { 91 | 92 | case S_RAW: 93 | if c.raw > 0 { 94 | bytesRead, err = c.r.Read(v[:min(len(v), c.raw)]) 95 | if VerboseDecomp { 96 | fmt.Printf("decompression copied in %d raw bytes: %v\n", bytesRead, v[:bytesRead]) 97 | } 98 | c.raw -= bytesRead 99 | v = v[bytesRead:] 100 | n += bytesRead 101 | 102 | if err != nil { 103 | return 104 | } 105 | } else { 106 | c.state = S_NORMAL 107 | } 108 | 109 | case S_POSTFF: 110 | if c.ffBufUsedCount >= 8 { 111 | c.state = S_READN 112 | continue 113 | } 114 | // invar: c.ffBufUsedCount < 8 115 | 116 | // before reading more from r, first empty any residual in buffer. Such 117 | // bytes were already read from r, are now 118 | // waiting in c.ffBuf, and have not yet been given to v: so 119 | // these bytes are first in line to go. 120 | if c.ffBufUsedCount < c.ffBufLoadCount { 121 | br := copy(v, c.ffBuf[c.ffBufUsedCount:c.ffBufLoadCount]) 122 | if VerboseDecomp { 123 | fmt.Printf("decompression copied in %d bytes: %v\n", br, v[:br]) 124 | } 125 | c.ffBufUsedCount += br 126 | v = v[br:] 127 | n += br 128 | } 129 | if c.ffBufUsedCount >= 8 { 130 | c.state = S_READN 131 | continue 132 | } 133 | // invar: c.ffBufUsedCount < 8 134 | 135 | // io.ReadFull, try to read exactly (8 - cc.ffBufLoadCount) bytes 136 | // io.ReadFull returns EOF only if no bytes were read 137 | if c.ffBufLoadCount < 8 { 138 | bytesRead, err = io.ReadFull(c.r, c.ffBuf[c.ffBufLoadCount:]) // read up to 8 bytes into c.buf 139 | if bytesRead > 0 { 140 | c.ffBufLoadCount += bytesRead 141 | } else { 142 | return 143 | } 144 | if err != nil { 145 | return 146 | } 147 | } 148 | // stay in S_POSTFF 149 | 150 | case S_READN: 151 | if bytesRead, err = c.r.Read(b[:]); err != nil { 152 | return 153 | } 154 | if bytesRead == 0 { 155 | return 156 | } 157 | c.raw = int(b[0]) * 8 158 | c.state = S_RAW 159 | 160 | case S_NORMAL: 161 | 162 | if c.zeros > 0 { 163 | num0 := min(len(v), c.zeros) 164 | x := v[:num0] 165 | for i := range x { 166 | x[i] = 0 167 | } 168 | c.zeros -= num0 169 | n += num0 170 | if c.zeros > 0 { 171 | return n, nil 172 | } 173 | v = v[num0:] 174 | if len(v) == 0 { 175 | return n, nil 176 | } 177 | } 178 | // INVAR: c.zeros == 0 179 | 180 | if c.bufsz > 0 { 181 | nc := copy(v, c.buf[8-c.bufsz:]) 182 | c.bufsz -= nc 183 | n += nc 184 | v = v[nc:] 185 | if c.bufsz > 0 { 186 | return n, nil 187 | } 188 | } 189 | // INVAR: c.bufz == 0 190 | 191 | for c.state == S_NORMAL && len(v) > 0 { 192 | 193 | if _, err = c.r.Read(b[:]); err != nil { 194 | return 195 | } 196 | if VerboseDecomp { 197 | fmt.Printf("decompression read TAG byte b: %#v\n", b) 198 | } 199 | 200 | switch b[0] { 201 | case 0xFF: 202 | c.ffBufLoadCount = 0 203 | c.ffBufUsedCount = 0 204 | c.state = S_POSTFF 205 | 206 | break 207 | 208 | case 0x00: 209 | if _, err = c.r.Read(b[:]); err != nil { 210 | return 211 | } 212 | if VerboseDecomp { 213 | fmt.Printf("decompression read byte Zero-word -1 count byte b: %#v\n", b) 214 | } 215 | 216 | requestedZeroBytes := (int(b[0]) + 1) * 8 217 | zeros := min(requestedZeroBytes, len(v)) 218 | 219 | if VerboseDecomp { 220 | fmt.Printf("decompression writing zeros to n=%d to n+zeros=%d &v[0]=%p\n", n, n+zeros, &v[0]) 221 | } // this next is obliterating out v[4] wierdly 222 | for i := 0; i < zeros; i++ { 223 | v[i] = 0 224 | } 225 | v = v[zeros:] 226 | n += zeros 227 | // remember the leftover zeros to write 228 | c.zeros = requestedZeroBytes - zeros 229 | 230 | default: 231 | ones := 0 232 | var buf [8]byte 233 | for i := 0; i < 8; i++ { 234 | if (b[0] & (1 << uint(i))) != 0 { 235 | ones++ 236 | } 237 | } 238 | 239 | _, err = io.ReadFull(c.r, buf[:ones]) 240 | if err != nil { 241 | return 242 | } 243 | 244 | for i, j := 0, 0; i < 8; i++ { 245 | if (b[0] & (1 << uint(i))) != 0 { 246 | c.buf[i] = buf[j] 247 | j++ 248 | } else { 249 | c.buf[i] = 0 250 | } 251 | } 252 | 253 | use := copy(v, c.buf[:]) 254 | if VerboseDecomp { 255 | fmt.Printf("decompression copied in %d bytes: %v\n", use, c.buf[:]) 256 | } 257 | v = v[use:] 258 | n += use 259 | c.bufsz = 8 - use 260 | } 261 | } 262 | } 263 | 264 | } 265 | return 266 | } 267 | 268 | func (c *Compressor) Write(v []byte) (n int, err error) { 269 | origVlen := len(v) 270 | if (origVlen % 8) != 0 { 271 | return 0, errors.New("capnproto: compressor relies on word aligned data") 272 | } 273 | buf := make([]byte, 0, 8) 274 | for len(v) > 0 { 275 | var hdr byte 276 | buf = buf[:0] 277 | for i, b := range v[:8] { 278 | if b != 0 { 279 | hdr |= 1 << uint(i) 280 | buf = append(buf, b) 281 | } 282 | } 283 | err = c.w.WriteByte(hdr) 284 | if err != nil { 285 | return n, err 286 | } 287 | _, err = c.w.Write(buf) 288 | if err != nil { 289 | return n, err 290 | } 291 | n += 8 292 | v = v[8:] 293 | 294 | switch hdr { 295 | case 0x00: 296 | i := 0 297 | for len(v) > 0 && binary.LittleEndian.Uint64(v) == 0 && i < 0xFF { 298 | i++ 299 | n += 8 300 | v = v[8:] 301 | } 302 | err = c.w.WriteByte(byte(i)) 303 | if err != nil { 304 | return n, err 305 | } 306 | case 0xFF: 307 | i := 0 308 | end := min(len(v), 0xFF*8) 309 | for i < end { 310 | zeros := 0 311 | for _, b := range v[i : i+8] { 312 | if b == 0 { 313 | zeros++ 314 | } 315 | } 316 | 317 | if zeros > 1 { 318 | break 319 | } 320 | i += 8 321 | } 322 | 323 | rawWords := byte(i / 8) 324 | err := c.w.WriteByte(rawWords) 325 | if err != nil { 326 | return n, err 327 | } 328 | _, err = c.w.Write(v[:i]) 329 | if err != nil { 330 | return n, err 331 | } 332 | n += i 333 | v = v[i:] 334 | } 335 | } 336 | err = c.w.Flush() 337 | return n, err 338 | } 339 | -------------------------------------------------------------------------------- /stream_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "testing" 10 | 11 | capn "github.com/glycerine/go-capnproto" 12 | air "github.com/glycerine/go-capnproto/aircraftlib" 13 | cv "github.com/glycerine/goconvey/convey" 14 | ) 15 | 16 | var benchForever bool 17 | 18 | func init() { 19 | flag.BoolVar(&benchForever, "bench.forever", false, "benchmark forever") 20 | flag.Parse() 21 | } 22 | 23 | func TestReadFromStream(t *testing.T) { 24 | const n = 10 25 | r := zdateReader(n, false) 26 | s, err := capn.ReadFromStream(r, nil) 27 | if err != nil { 28 | t.Fatalf("ReadFromStream: %v", err) 29 | } 30 | z := air.ReadRootZ(s) 31 | if z.Which() != air.Z_ZDATEVEC { 32 | panic("expected Z_ZDATEVEC in root Z of segment") 33 | } 34 | zdatelist := z.Zdatevec() 35 | 36 | if capn.JSON_enabled { 37 | for i := 0; i < n; i++ { 38 | zdate := zdatelist.At(i) 39 | js, err := zdate.MarshalJSON() 40 | if err != nil { 41 | t.Fatalf("MarshalJSON: %v", err) 42 | } 43 | t.Logf("%s", string(js)) 44 | } 45 | } 46 | } 47 | 48 | func TestReadFromStreamBackToBack(t *testing.T) { 49 | const n = 10 50 | 51 | r := zdateReaderNBackToBack(n, false) 52 | 53 | for i := 0; i < n; i++ { 54 | s, err := capn.ReadFromStream(r, nil) 55 | if err != nil { 56 | t.Fatalf("ReadFromStream: %v", err) 57 | } 58 | m := air.ReadRootZdate(s) 59 | if capn.JSON_enabled { 60 | js, err := m.MarshalJSON() 61 | if err != nil { 62 | t.Fatalf("MarshalJSON: %v", err) 63 | } 64 | t.Logf("%s", string(js)) 65 | } 66 | } 67 | 68 | } 69 | 70 | func TestDecompressorZdate1(t *testing.T) { 71 | const n = 1 72 | 73 | r := zdateReader(n, false) 74 | expected, err := ioutil.ReadAll(r) 75 | if err != nil { 76 | t.Fatalf("ReadAll: %v", err) 77 | } 78 | 79 | r = zdateReader(n, true) 80 | actual, err := ioutil.ReadAll(capn.NewDecompressor(r)) 81 | if err != nil { 82 | t.Fatalf("ReadAll: %v", err) 83 | } 84 | 85 | if !bytes.Equal(expected, actual) { 86 | fmt.Printf("expected to get: '%s'\n actually observed instead: '%s'\n", expected, actual) 87 | t.Fatal("decompressor failed") 88 | } 89 | } 90 | 91 | func TestDecompressorUNPACKZdate2(t *testing.T) { 92 | const n = 2 93 | 94 | un := zdateReader(n, false) 95 | expected, err := ioutil.ReadAll(un) 96 | fmt.Printf("expected: '%#v' of len(%d)\n", expected, len(expected)) 97 | // prints: 98 | // expected: []byte{ 99 | // 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 100 | // 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 101 | // 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 102 | // 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 103 | // 0x1, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 104 | // 0xd4, 0x7, 0xc, 0x7, 0xd5, 0x7, 0xc, 0x7, 105 | // 0xd4, 0x7, 0xc, 0x7, 0x0, 0x0, 0x0, 0x0, 106 | // 0xd5, 0x7, 0xc, 0x7, 0x0, 0x0, 0x0, 0x0} of len(64) 107 | if err != nil { 108 | t.Fatalf("ReadAll: %v", err) 109 | } 110 | 111 | //r = zdateReader(n, true) 112 | _, slicePacked := zdateFilledSegment(n, true) 113 | fmt.Printf("slicePacked = %#v\n", slicePacked) 114 | // prints slicePacked = []byte{ 115 | // tag0x10, 0x7, tag:0x50, 0x2, 0x1, tag0x1, 0x25, tag0x0, 116 | // 0x0, tag0x11, 0x1, 0x14, tag0xff, 0xd4, 0x7, 0xc, 117 | // 0x7, 0xd5, 0x7, 0xc, 0x7, N:0x2, 0xd4, 0x7, 118 | // 0xc, 0x7, 0x0, 0x0, 0x0, 0x0, 0xd5, 0x7, 119 | // 0xc, 0x7, 0x0, 0x0, 0x0, 0x0} 120 | 121 | pa := bytes.NewReader(slicePacked) 122 | 123 | actual, err := ioutil.ReadAll(capn.NewDecompressor(pa)) 124 | if err != nil { 125 | t.Fatalf("ReadAll: %v", err) 126 | } 127 | 128 | cv.Convey("Given the []byte slice from a capnp conversion from packed to unpacked form of a two Zdate vector", t, func() { 129 | cv.Convey("When we use go-capnproto NewDecompressor", func() { 130 | cv.Convey("Then we should get the same unpacked bytes as capnp provides", func() { 131 | cv.So(len(actual), cv.ShouldResemble, len(expected)) 132 | cv.So(actual, cv.ShouldResemble, expected) 133 | }) 134 | }) 135 | }) 136 | } 137 | 138 | func TestDecodeOnKnownWellPackedData(t *testing.T) { 139 | 140 | // length 17 141 | byteSliceIn := []byte{0x10, 0x5, 0x50, 0x2, 0x1, 0x1, 0x25, 0x0, 0x0, 0x11, 0x1, 0xc, 0xf, 0xd4, 0x7, 0xc, 0x7} 142 | fmt.Printf("len of byteSliceIn is %d\n", len(byteSliceIn)) 143 | // annotated: byteSliceIn := []byte{tag:0x10, 0x5, tag:0x50, 0x2, 0x1, tag:0x1, 0x25, tag:0x0, 0x0, tag:0x11, 0x1, 0xc, tag:0xf, 0xd4, 0x7, 0xc, 0x7} 144 | 145 | // length 48 146 | expectedOut := []byte{0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xd4, 0x7, 0xc, 0x7, 0x0, 0x0, 0x0, 0x0} 147 | 148 | r := bytes.NewReader(byteSliceIn) 149 | actual, err := ioutil.ReadAll(capn.NewDecompressor(r)) 150 | if err != nil { 151 | panic(err) 152 | } 153 | 154 | cv.Convey("Given a known-to-be-correctly packed 17-byte long sequence for a ZdateVector holding a single Zdate", t, func() { 155 | cv.Convey("When we use go-capnproto NewDecompressor", func() { 156 | cv.Convey("Then we should get the same unpacked bytes as capnp provides", func() { 157 | cv.So(len(actual), cv.ShouldResemble, len(expectedOut)) 158 | cv.So(actual, cv.ShouldResemble, expectedOut) 159 | }) 160 | }) 161 | }) 162 | 163 | } 164 | 165 | var compressionTests = []struct { 166 | original []byte 167 | compressed []byte 168 | }{ 169 | { 170 | []byte{}, 171 | []byte{}, 172 | }, 173 | { 174 | []byte{0, 0, 0, 0, 0, 0, 0, 0}, 175 | []byte{0, 0}, 176 | }, 177 | { 178 | []byte{0, 0, 12, 0, 0, 34, 0, 0}, 179 | []byte{0x24, 12, 34}, 180 | }, 181 | { 182 | []byte{1, 3, 2, 4, 5, 7, 6, 8}, 183 | []byte{0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0}, 184 | }, 185 | { 186 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8}, 187 | []byte{0, 0, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0}, 188 | }, 189 | { 190 | []byte{0, 0, 12, 0, 0, 34, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8}, 191 | []byte{0x24, 12, 34, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0}, 192 | }, 193 | { 194 | []byte{1, 3, 2, 4, 5, 7, 6, 8, 8, 6, 7, 4, 5, 2, 3, 1}, 195 | []byte{0xff, 1, 3, 2, 4, 5, 7, 6, 8, 1, 8, 6, 7, 4, 5, 2, 3, 1}, 196 | }, 197 | { 198 | []byte{ 199 | 1, 2, 3, 4, 5, 6, 7, 8, 200 | 1, 2, 3, 4, 5, 6, 7, 8, 201 | 1, 2, 3, 4, 5, 6, 7, 8, 202 | 1, 2, 3, 4, 5, 6, 7, 8, 203 | 0, 2, 4, 0, 9, 0, 5, 1, 204 | }, 205 | []byte{ 206 | 0xff, 1, 2, 3, 4, 5, 6, 7, 8, 207 | 3, 208 | 1, 2, 3, 4, 5, 6, 7, 8, 209 | 1, 2, 3, 4, 5, 6, 7, 8, 210 | 1, 2, 3, 4, 5, 6, 7, 8, 211 | 0xd6, 2, 4, 9, 5, 1, 212 | }, 213 | }, 214 | { 215 | []byte{ 216 | 1, 2, 3, 4, 5, 6, 7, 8, 217 | 1, 2, 3, 4, 5, 6, 7, 8, 218 | 6, 2, 4, 3, 9, 0, 5, 1, 219 | 1, 2, 3, 4, 5, 6, 7, 8, 220 | 0, 2, 4, 0, 9, 0, 5, 1, 221 | }, 222 | []byte{ 223 | 0xff, 1, 2, 3, 4, 5, 6, 7, 8, 224 | 3, 225 | 1, 2, 3, 4, 5, 6, 7, 8, 226 | 6, 2, 4, 3, 9, 0, 5, 1, 227 | 1, 2, 3, 4, 5, 6, 7, 8, 228 | 0xd6, 2, 4, 9, 5, 1, 229 | }, 230 | }, 231 | { 232 | []byte{ 233 | 8, 0, 100, 6, 0, 1, 1, 2, 234 | 0, 0, 0, 0, 0, 0, 0, 0, 235 | 0, 0, 0, 0, 0, 0, 0, 0, 236 | 0, 0, 0, 0, 0, 0, 0, 0, 237 | 0, 0, 1, 0, 2, 0, 3, 1, 238 | }, 239 | []byte{ 240 | 0xed, 8, 100, 6, 1, 1, 2, 241 | 0, 2, 242 | 0xd4, 1, 2, 3, 1, 243 | }, 244 | }, 245 | } 246 | 247 | func TestCompressor(t *testing.T) { 248 | for i, test := range compressionTests { 249 | var buf bytes.Buffer 250 | if i == 7 { 251 | fmt.Printf("at test 7\n") 252 | } 253 | c := capn.NewCompressor(&buf) 254 | c.Write(test.original) 255 | if !bytes.Equal(test.compressed, buf.Bytes()) { 256 | t.Errorf("test:%d: failed", i) 257 | fmt.Printf(" test.original = %#v\n test.compressed = %#v\n buf.Bytes() = %#v\n", test.original, test.compressed, buf.Bytes()) 258 | } 259 | } 260 | } 261 | 262 | func TestCompressor7(t *testing.T) { 263 | i := 7 264 | test := compressionTests[i] 265 | var buf bytes.Buffer 266 | c := capn.NewCompressor(&buf) 267 | 268 | fmt.Printf("compressing test.original = %#v\n", test.original) 269 | 270 | c.Write(test.original) 271 | if !bytes.Equal(test.compressed, buf.Bytes()) { 272 | t.Errorf("test:%d: failed", i) 273 | fmt.Printf(" test.original = %#v\n test.compressed = %#v\n buf.Bytes() = %#v\n", test.original, test.compressed, buf.Bytes()) 274 | } 275 | } 276 | 277 | func TestDecompressor(t *testing.T) { 278 | errCount := 0 279 | for i, test := range compressionTests { 280 | for readSize := 1; readSize <= 8+2*len(test.original); readSize++ { 281 | r := bytes.NewReader(test.compressed) 282 | d := capn.NewDecompressor(r) 283 | buf := make([]byte, readSize) 284 | var actual []byte 285 | for { 286 | n, err := d.Read(buf) 287 | actual = append(actual, buf[:n]...) 288 | if err != nil { 289 | if err == io.EOF { 290 | break 291 | } 292 | t.Fatalf("Read: %v", err) 293 | } 294 | } 295 | 296 | if len(test.original) != len(actual) { 297 | errCount++ 298 | t.Errorf("test:%d readSize:%d expected %d bytes, got %d", 299 | i, readSize, len(test.original), len(actual)) 300 | continue 301 | } 302 | 303 | if !bytes.Equal(test.original, actual) { 304 | errCount++ 305 | t.Errorf("test:%d readSize:%d: bytes not equal", i, readSize) 306 | } 307 | } 308 | } 309 | if errCount == 0 { 310 | fmt.Printf("TestDecompressor() passed. (0 errors).\n") 311 | } 312 | 313 | } 314 | 315 | func TestDecompressorVerbosely(t *testing.T) { 316 | cv.Convey("Testing the go-capnproto Decompressor.Read() function:", t, func() { 317 | for _, test := range compressionTests { 318 | 319 | fmt.Printf("\n\nGiven compressed text '%#v'\n", test.compressed) 320 | 321 | // fmt.Printf(" test.original = %#v\n test.compressed = %#v\n", test.original, test.compressed) 322 | for readSize := 1; readSize <= 8+2*len(test.original); readSize++ { 323 | fmt.Printf("\n When we use go-capnproto NewDecompressor, with readSize: %d\n Then we should get the original text back.", readSize) 324 | 325 | r := bytes.NewReader(test.compressed) 326 | d := capn.NewDecompressor(r) 327 | buf := make([]byte, readSize) 328 | var actual []byte 329 | for { 330 | n, err := d.Read(buf) 331 | actual = append(actual, buf[:n]...) 332 | if err != nil { 333 | if err == io.EOF { 334 | break 335 | } 336 | t.Fatalf("Read: %v", err) 337 | } 338 | } 339 | 340 | cv.So(len(actual), cv.ShouldResemble, len(test.original)) 341 | if len(test.original) > 0 { 342 | cv.So(actual, cv.ShouldResemble, test.original) 343 | } 344 | } // end readSize loop 345 | } 346 | fmt.Printf("\n\n") 347 | }) 348 | } 349 | 350 | func TestReadFromPackedStream(t *testing.T) { 351 | const n = 10 352 | 353 | r := zdateReaderNBackToBack(n, true) 354 | 355 | for i := 0; i < n; i++ { 356 | s, err := capn.ReadFromPackedStream(r, nil) 357 | if err != nil { 358 | t.Fatalf("ReadFromPackedStream: %v, i=%d", err, i) 359 | } 360 | m := air.ReadRootZdate(s) 361 | if capn.JSON_enabled { 362 | js, err := m.MarshalJSON() 363 | if err != nil { 364 | t.Fatalf("MarshalJSON: %v", err) 365 | } 366 | t.Logf("%s", string(js)) 367 | } 368 | } 369 | } 370 | 371 | func TestModifyMultiSegmentMessage(t *testing.T) { 372 | cv.Convey("Testing Read->Modify->Write of multi-segmented message:", t, func() { 373 | // Create multi-segment message by capnp tool 374 | cv.Convey("When we read from multi-segmented stream, we should get the original data", func() { 375 | capnp := CapnpEncode(`(size = 100, words = "AAAAAAAAA", wordlist = ["word0", "word1", "word2", "word3", "word4"])`, "Counter", "--segment-size", "10") 376 | 377 | seg, err := capn.ReadFromStream(bytes.NewReader(capnp), nil) 378 | cv.So(err, cv.ShouldBeNil) 379 | 380 | c := air.ReadRootCounter(seg) 381 | cv.So(c.Size(), cv.ShouldEqual, 100) 382 | cv.So(c.Words(), cv.ShouldEqual, "AAAAAAAAA") 383 | cv.So(c.Wordlist().Len(), cv.ShouldEqual, 5) 384 | cv.So(c.Wordlist().At(0), cv.ShouldEqual, "word0") 385 | cv.So(c.Wordlist().At(1), cv.ShouldEqual, "word1") 386 | 387 | cv.Convey("When we set new value on 'words' field, another field should not be changed", func() { 388 | c.SetWords("hello, capnp!") 389 | 390 | cv.So(c.Size(), cv.ShouldEqual, 100) 391 | cv.So(c.Words(), cv.ShouldEqual, "hello, capnp!") 392 | cv.So(c.Wordlist().Len(), cv.ShouldEqual, 5) 393 | cv.So(c.Wordlist().At(0), cv.ShouldEqual, "word0") 394 | cv.So(c.Wordlist().At(1), cv.ShouldEqual, "word1") 395 | }) 396 | }) 397 | }) 398 | } 399 | 400 | func BenchmarkCompressor(b *testing.B) { 401 | r := zdateReader(100, false) 402 | buf, err := ioutil.ReadAll(r) 403 | if err != nil { 404 | panic(err) 405 | } 406 | c := capn.NewCompressor(ioutil.Discard) 407 | b.SetBytes(int64(len(buf))) 408 | 409 | b.ResetTimer() 410 | for i := 0; i < b.N || benchForever; i++ { 411 | c.Write(buf) 412 | } 413 | } 414 | 415 | func BenchmarkDecompressor(b *testing.B) { 416 | r := zdateReader(100, true) 417 | buf, err := ioutil.ReadAll(r) 418 | if err != nil { 419 | b.Fatalf("%v", err) 420 | } 421 | b.SetBytes(int64(len(buf))) 422 | 423 | // determine buffer size to read all the data 424 | // in a single call 425 | r.Seek(0, 0) 426 | d := capn.NewDecompressor(r) 427 | buf, err = ioutil.ReadAll(d) 428 | if err != nil { 429 | b.Fatalf("%v", err) 430 | } 431 | 432 | b.ResetTimer() 433 | for i := 0; i < b.N || benchForever; i++ { 434 | r.Seek(0, 0) 435 | n, err := d.Read(buf) 436 | if err != nil { 437 | b.Fatalf("%v", err) 438 | } 439 | if n != len(buf) { 440 | b.Fatal("short read") 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /stringbytes_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | capn "github.com/glycerine/go-capnproto" 8 | air "github.com/glycerine/go-capnproto/aircraftlib" 9 | cv "github.com/glycerine/goconvey/convey" 10 | ) 11 | 12 | // StringBytes() should allow us to avoid the copying overhead of reading a string 13 | // and making a copy. 14 | /* 15 | // Confirmed: StringBytes lets us avoid all allcations: 16 | go test -v -run 500 -benchmem -bench=String 17 | PASS 18 | BenchmarkStringBytes-4 50000000 29.3 ns/op 0 B/op 0 allocs/op 19 | BenchmarkWithoutStringBytes-4 20000000 79.0 ns/op 32 B/op 1 allocs/op 20 | ok github.com/glycerine/go-capnproto 3.255s 21 | $ 22 | $ go test -v -run=BBBBBBBB -bench=. -benchmem 23 | PASS 24 | BenchmarkPopulateCapnp-4 1000000 1571 ns/op 48 B/op 1 allocs/op 25 | BenchmarkMarshalCapnp-4 20000000 118 ns/op 3927.52 MB/s 16 B/op 1 allocs/op 26 | BenchmarkUnmarshalCapnp-4 2000000 605 ns/op 766.08 MB/s 256 B/op 5 allocs/op 27 | BenchmarkUnmarshalCapnpZeroCopy-4 10000000 182 ns/op 2539.81 MB/s 88 B/op 3 allocs/op 28 | BenchmarkUnmarshalCapnpZeroCopyNoAlloc-4 100000000 24.9 ns/op 18668.12 MB/s 0 B/op 0 allocs/op 29 | BenchmarkCompressor-4 200000 6651 ns/op 247.78 MB/s 16 B/op 1 allocs/op 30 | BenchmarkDecompressor-4 100000 17557 ns/op 57.81 MB/s 3296 B/op 206 allocs/op 31 | BenchmarkStringBytes-4 50000000 29.4 ns/op 0 B/op 0 allocs/op 32 | BenchmarkWithoutStringBytes-4 20000000 78.6 ns/op 32 B/op 1 allocs/op 33 | BenchmarkTextMovementBetweenSegments-4 500 3562898 ns/op 208304 B/op 3005 allocs/op 34 | ok github.com/glycerine/go-capnproto 19.246s 35 | $ 36 | */ 37 | func Test500StringBytesWorksAndDoesNoAllocation(t *testing.T) { 38 | 39 | baseBytes := CapnpEncode(`(name = "An Airport base station")`, "PlaneBase") 40 | bagBytes := CapnpEncode(`(counter = (size = 9, wordlist = ["hello","bye"]))`, "Bag") 41 | 42 | cv.Convey("Given an capnp serialized data segment containing strings or vectors of strings", t, func() { 43 | cv.Convey("We should be able to use StringBytes() to avoid copying data, "+ 44 | "instead just getting a []byte back", func() { 45 | 46 | // Base - for standalone string field. 47 | multiBase := capn.NewSingleSegmentMultiBuffer() 48 | var err error 49 | _, err = capn.ReadFromMemoryZeroCopyNoAlloc(baseBytes, multiBase) 50 | if err != nil { 51 | panic(err) 52 | } 53 | seg := multiBase.Segments[0] 54 | 55 | base := air.ReadRootPlaneBase(seg) 56 | 57 | fmt.Printf("\n base.Name() = '%s'\n", base.Name()) 58 | fmt.Printf(" base.NameBytes() = '%s'\n", base.NameBytes()) 59 | cv.So(string(base.NameBytes()), cv.ShouldResemble, base.Name()) 60 | 61 | // Bag - for vector of string, aka TextList 62 | _, err = capn.ReadFromMemoryZeroCopyNoAlloc(bagBytes, multiBase) 63 | if err != nil { 64 | panic(err) 65 | } 66 | seg = multiBase.Segments[0] 67 | bag := air.ReadRootBag(seg) 68 | 69 | fmt.Printf("\n bag.Counter().Wordlist().AtAsBytes(0) = '%s'\n", string(bag.Counter().Wordlist().AtAsBytes(0))) 70 | fmt.Printf(" bag.Counter().Wordlist().At(0) = '%s'\n", bag.Counter().Wordlist().At(0)) 71 | cv.So(string(bag.Counter().Wordlist().AtAsBytes(0)), cv.ShouldResemble, bag.Counter().Wordlist().At(0)) 72 | }) 73 | }) 74 | } 75 | 76 | func BenchmarkStringBytes(b *testing.B) { 77 | 78 | baseBytes := CapnpEncode(`(name = "An Airport base station")`, "PlaneBase") 79 | multiBase := capn.NewSingleSegmentMultiBuffer() 80 | var err error 81 | _, err = capn.ReadFromMemoryZeroCopyNoAlloc(baseBytes, multiBase) 82 | if err != nil { 83 | panic(err) 84 | } 85 | seg := multiBase.Segments[0] 86 | base := air.ReadRootPlaneBase(seg) 87 | 88 | b.ResetTimer() 89 | for i := 0; i < b.N; i++ { 90 | name := base.NameBytes() 91 | _ = name 92 | } 93 | } 94 | 95 | func BenchmarkWithoutStringBytes(b *testing.B) { 96 | 97 | baseBytes := CapnpEncode(`(name = "An Airport base station")`, "PlaneBase") 98 | multiBase := capn.NewSingleSegmentMultiBuffer() 99 | var err error 100 | _, err = capn.ReadFromMemoryZeroCopyNoAlloc(baseBytes, multiBase) 101 | if err != nil { 102 | panic(err) 103 | } 104 | seg := multiBase.Segments[0] 105 | base := air.ReadRootPlaneBase(seg) 106 | 107 | b.ResetTimer() 108 | for i := 0; i < b.N; i++ { 109 | name := base.Name() 110 | _ = name 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /struct_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | capn "github.com/glycerine/go-capnproto" 9 | air "github.com/glycerine/go-capnproto/aircraftlib" 10 | cv "github.com/glycerine/goconvey/convey" 11 | ) 12 | 13 | func TestTextAndListTextContaintingEmptyStruct(t *testing.T) { 14 | 15 | emptyZjobBytes := CapnpEncode("()", "Zjob") 16 | 17 | cv.Convey("Given a simple struct message Zjob containing a string and a list of string (all empty)", t, func() { 18 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 19 | ShowBytes(emptyZjobBytes, 10) 20 | 21 | seg := capn.NewBuffer(nil) 22 | air.NewRootZjob(seg) 23 | 24 | buf := bytes.Buffer{} 25 | seg.WriteTo(&buf) 26 | 27 | cv.So(buf.Bytes(), cv.ShouldResemble, emptyZjobBytes) 28 | }) 29 | }) 30 | } 31 | 32 | func TestTextContaintingStruct(t *testing.T) { 33 | 34 | zjobBytes := CapnpEncode(`(cmd = "abc")`, "Zjob") 35 | 36 | cv.Convey("Given a simple struct message Zjob containing a string 'abc' and a list of string (empty)", t, func() { 37 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 38 | 39 | seg := capn.NewBuffer(nil) 40 | zjob := air.NewRootZjob(seg) 41 | zjob.SetCmd("abc") 42 | 43 | buf := bytes.Buffer{} 44 | seg.WriteTo(&buf) 45 | 46 | act := buf.Bytes() 47 | fmt.Printf(" actual:\n") 48 | ShowBytes(act, 10) 49 | 50 | fmt.Printf("\n\n expected:\n") 51 | ShowBytes(zjobBytes, 10) 52 | 53 | cv.So(act, cv.ShouldResemble, zjobBytes) 54 | }) 55 | }) 56 | } 57 | 58 | func TestTextListContaintingStruct(t *testing.T) { 59 | 60 | zjobBytes := CapnpEncode(`(args = ["xyz"])`, "Zjob") 61 | 62 | cv.Convey("Given a simple struct message Zjob containing an unset string and a list of string ('xyz' as the only element)", t, func() { 63 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 64 | 65 | seg := capn.NewBuffer(nil) 66 | zjob := air.NewRootZjob(seg) 67 | tl := seg.NewTextList(1) 68 | tl.Set(0, "xyz") 69 | zjob.SetArgs(tl) 70 | 71 | buf := bytes.Buffer{} 72 | seg.WriteTo(&buf) 73 | 74 | act := buf.Bytes() 75 | fmt.Printf(" actual:\n") 76 | ShowBytes(act, 10) 77 | 78 | fmt.Printf("expected:\n") 79 | ShowBytes(zjobBytes, 10) 80 | 81 | cv.So(act, cv.ShouldResemble, zjobBytes) 82 | }) 83 | }) 84 | } 85 | 86 | func TestTextAndTextListContaintingStruct(t *testing.T) { 87 | 88 | zjobBytes := CapnpEncode(`(cmd = "abc", args = ["xyz"])`, "Zjob") 89 | 90 | cv.Convey("Given a simple struct message Zjob containing a string (cmd='abc') and a list of string (args=['xyz'])", t, func() { 91 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 92 | 93 | seg := capn.NewBuffer(nil) 94 | zjob := air.NewRootZjob(seg) 95 | zjob.SetCmd("abc") 96 | tl := seg.NewTextList(1) 97 | tl.Set(0, "xyz") 98 | zjob.SetArgs(tl) 99 | 100 | buf := bytes.Buffer{} 101 | seg.WriteTo(&buf) 102 | 103 | act := buf.Bytes() 104 | fmt.Printf(" actual:\n") 105 | ShowBytes(act, 10) 106 | 107 | fmt.Printf("expected:\n") 108 | ShowBytes(zjobBytes, 10) 109 | 110 | cv.So(act, cv.ShouldResemble, zjobBytes) 111 | }) 112 | }) 113 | } 114 | 115 | func TestZserverWithOneFullJob(t *testing.T) { 116 | 117 | exp := CapnpEncode(`(waitingjobs = [(cmd = "abc", args = ["xyz"])])`, "Zserver") 118 | 119 | cv.Convey("Given an Zserver with one empty job", t, func() { 120 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 121 | 122 | seg := capn.NewBuffer(nil) 123 | scratch := capn.NewBuffer(nil) 124 | 125 | server := air.NewRootZserver(seg) 126 | 127 | joblist := air.NewZjobList(seg, 1) 128 | plist := capn.PointerList(joblist) 129 | 130 | zjob := air.NewZjob(scratch) 131 | zjob.SetCmd("abc") 132 | tl := scratch.NewTextList(1) 133 | tl.Set(0, "xyz") 134 | zjob.SetArgs(tl) 135 | 136 | plist.Set(0, capn.Object(zjob)) 137 | 138 | server.SetWaitingjobs(joblist) 139 | 140 | buf := bytes.Buffer{} 141 | seg.WriteTo(&buf) 142 | 143 | act := buf.Bytes() 144 | fmt.Printf(" actual:\n") 145 | ShowBytes(act, 10) 146 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Zserver"))) 147 | save(act, "myact") 148 | 149 | fmt.Printf("expected:\n") 150 | ShowBytes(exp, 10) 151 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Zserver"))) 152 | save(exp, "myexp") 153 | 154 | cv.So(act, cv.ShouldResemble, exp) 155 | }) 156 | }) 157 | } 158 | 159 | func TestZserverWithAccessors(t *testing.T) { 160 | 161 | exp := CapnpEncode(`(waitingjobs = [(cmd = "abc"), (cmd = "xyz")])`, "Zserver") 162 | 163 | cv.Convey("Given an Zserver with a custom list", t, func() { 164 | cv.Convey("then all the accessors should work as expected", func() { 165 | 166 | seg := capn.NewBuffer(nil) 167 | scratch := capn.NewBuffer(nil) 168 | 169 | server := air.NewRootZserver(seg) 170 | 171 | joblist := air.NewZjobList(seg, 2) 172 | 173 | // .Set(int, item) 174 | zjob := air.NewZjob(scratch) 175 | zjob.SetCmd("abc") 176 | joblist.Set(0, zjob) 177 | 178 | zjob = air.NewZjob(scratch) 179 | zjob.SetCmd("xyz") 180 | joblist.Set(1, zjob) 181 | 182 | // .At(int) 183 | cv.So(joblist.At(0).Cmd(), cv.ShouldEqual, "abc") 184 | cv.So(joblist.At(1).Cmd(), cv.ShouldEqual, "xyz") 185 | 186 | // .Len() 187 | cv.So(joblist.Len(), cv.ShouldEqual, 2) 188 | 189 | // .ToArray() 190 | cv.So(len(joblist.ToArray()), cv.ShouldEqual, 2) 191 | cv.So(joblist.ToArray()[0].Cmd(), cv.ShouldEqual, "abc") 192 | cv.So(joblist.ToArray()[1].Cmd(), cv.ShouldEqual, "xyz") 193 | 194 | server.SetWaitingjobs(joblist) 195 | 196 | buf := bytes.Buffer{} 197 | seg.WriteTo(&buf) 198 | 199 | act := buf.Bytes() 200 | fmt.Printf(" actual:\n") 201 | ShowBytes(act, 10) 202 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Zserver"))) 203 | save(act, "myact") 204 | 205 | fmt.Printf("expected:\n") 206 | ShowBytes(exp, 10) 207 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Zserver"))) 208 | save(exp, "myexp") 209 | 210 | cv.So(act, cv.ShouldResemble, exp) 211 | }) 212 | }) 213 | } 214 | 215 | func TestEnumFromString(t *testing.T) { 216 | cv.Convey("Given an enum tag string matching a constant", t, func() { 217 | cv.Convey("FromString should return the corresponding matching constant value", func() { 218 | cv.So(air.AirportFromString("jfk"), cv.ShouldEqual, air.AIRPORT_JFK) 219 | }) 220 | }) 221 | cv.Convey("Given an enum tag string that does not match a constant", t, func() { 222 | cv.Convey("FromString should return 0", func() { 223 | cv.So(air.AirportFromString("notEverMatching"), cv.ShouldEqual, 0) 224 | }) 225 | }) 226 | } 227 | 228 | func TestSetObjectBetweenSegments(t *testing.T) { 229 | 230 | exp := CapnpEncode(`(counter = (size = 9))`, "Bag") 231 | 232 | cv.Convey("Given an Counter in one segment and a Bag in another", t, func() { 233 | cv.Convey("we should be able to copy from one segment to the other with SetCounter() on a Bag", func() { 234 | 235 | seg := capn.NewBuffer(nil) 236 | scratch := capn.NewBuffer(nil) 237 | 238 | // in seg 239 | segbag := air.NewRootBag(seg) 240 | 241 | // in scratch 242 | xc := air.NewRootCounter(scratch) 243 | xc.SetSize(9) 244 | 245 | // copy from scratch to seg 246 | segbag.SetCounter(xc) 247 | 248 | buf := bytes.Buffer{} 249 | seg.WriteTo(&buf) 250 | 251 | act := buf.Bytes() 252 | fmt.Printf(" actual:\n") 253 | ShowBytes(act, 10) 254 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Bag"))) 255 | save(act, "myact") 256 | 257 | fmt.Printf("expected:\n") 258 | ShowBytes(exp, 10) 259 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Bag"))) 260 | save(exp, "myexp") 261 | 262 | cv.So(act, cv.ShouldResemble, exp) 263 | }) 264 | }) 265 | } 266 | 267 | func TestObjectWithTextBetweenSegments(t *testing.T) { 268 | 269 | exp := CapnpEncode(`(counter = (size = 9, words = "hello"))`, "Bag") 270 | 271 | cv.Convey("Given an Counter in one segment and a Bag with text in another", t, func() { 272 | cv.Convey("we should be able to copy from one segment to the other with SetCounter() on a Bag", func() { 273 | 274 | seg := capn.NewBuffer(nil) 275 | scratch := capn.NewBuffer(nil) 276 | 277 | // in seg 278 | segbag := air.NewRootBag(seg) 279 | 280 | // in scratch 281 | xc := air.NewRootCounter(scratch) 282 | xc.SetSize(9) 283 | xc.SetWords("hello") 284 | 285 | // copy from scratch to seg 286 | segbag.SetCounter(xc) 287 | 288 | buf := bytes.Buffer{} 289 | seg.WriteTo(&buf) 290 | 291 | act := buf.Bytes() 292 | fmt.Printf(" actual:\n") 293 | ShowBytes(act, 10) 294 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Bag"))) 295 | save(act, "myact") 296 | 297 | fmt.Printf("expected:\n") 298 | ShowBytes(exp, 10) 299 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Bag"))) 300 | save(exp, "myexp") 301 | 302 | cv.So(act, cv.ShouldResemble, exp) 303 | }) 304 | }) 305 | } 306 | 307 | func TestObjectWithListOfTextBetweenSegments(t *testing.T) { 308 | 309 | exp := CapnpEncode(`(counter = (size = 9, wordlist = ["hello","bye"]))`, "Bag") 310 | 311 | cv.Convey("Given an Counter in one segment and a Bag with text in another", t, func() { 312 | cv.Convey("we should be able to copy from one segment to the other with SetCounter() on a Bag", func() { 313 | 314 | seg := capn.NewBuffer(nil) 315 | scratch := capn.NewBuffer(nil) 316 | 317 | // in seg 318 | segbag := air.NewRootBag(seg) 319 | 320 | // in scratch 321 | xc := air.NewRootCounter(scratch) 322 | xc.SetSize(9) 323 | tl := scratch.NewTextList(2) 324 | tl.Set(0, "hello") 325 | tl.Set(1, "bye") 326 | xc.SetWordlist(tl) 327 | 328 | xbuf := bytes.Buffer{} 329 | scratch.WriteTo(&xbuf) 330 | 331 | x := xbuf.Bytes() 332 | save(x, "myscratch") 333 | fmt.Printf("scratch segment (%p):\n", scratch) 334 | ShowBytes(x, 10) 335 | fmt.Printf("scratch segment (%p) with Counter decoded by capnp: '%s'\n", scratch, string(CapnpDecode(x, "Counter"))) 336 | 337 | prebuf := bytes.Buffer{} 338 | seg.WriteTo(&prebuf) 339 | fmt.Printf("Bag only segment seg (%p), pre-transfer:\n", seg) 340 | ShowBytes(prebuf.Bytes(), 10) 341 | 342 | // now for the actual test: 343 | // copy from scratch to seg 344 | segbag.SetCounter(xc) 345 | 346 | buf := bytes.Buffer{} 347 | seg.WriteTo(&buf) 348 | 349 | act := buf.Bytes() 350 | save(act, "myact") 351 | save(exp, "myexp") 352 | 353 | fmt.Printf("expected:\n") 354 | ShowBytes(exp, 10) 355 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Bag"))) 356 | 357 | fmt.Printf(" actual:\n") 358 | ShowBytes(act, 10) 359 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Bag"))) 360 | 361 | cv.So(act, cv.ShouldResemble, exp) 362 | }) 363 | }) 364 | } 365 | 366 | func TestSetBetweenSegments(t *testing.T) { 367 | 368 | exp := CapnpEncode(`(counter = (size = 9, words = "abc", wordlist = ["hello","byenow"]))`, "Bag") 369 | 370 | cv.Convey("Given an struct with Text and List(Text) in one segment", t, func() { 371 | cv.Convey("assigning it to a struct in a different segment should recursively import", func() { 372 | 373 | seg := capn.NewBuffer(nil) 374 | scratch := capn.NewBuffer(nil) 375 | 376 | // in seg 377 | segbag := air.NewRootBag(seg) 378 | 379 | // in scratch 380 | xc := air.NewRootCounter(scratch) 381 | xc.SetSize(9) 382 | tl := scratch.NewTextList(2) 383 | tl.Set(0, "hello") 384 | tl.Set(1, "byenow") 385 | xc.SetWordlist(tl) 386 | xc.SetWords("abc") 387 | 388 | fmt.Printf("\n\n starting copy from scratch to seg \n\n") 389 | 390 | // copy from scratch to seg 391 | segbag.SetCounter(xc) 392 | 393 | buf := bytes.Buffer{} 394 | seg.WriteTo(&buf) 395 | 396 | act := buf.Bytes() 397 | fmt.Printf(" actual:\n") 398 | ShowBytes(act, 10) 399 | //fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Bag"))) 400 | save(act, "myact") 401 | 402 | fmt.Printf("expected:\n") 403 | ShowBytes(exp, 10) 404 | //fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Bag"))) 405 | save(exp, "myexp") 406 | 407 | cv.So(act, cv.ShouldResemble, exp) 408 | }) 409 | }) 410 | } 411 | 412 | func ShowSeg(msg string, seg *capn.Segment) []byte { 413 | pre := bytes.Buffer{} 414 | seg.WriteTo(&pre) 415 | 416 | fmt.Printf("%s\n", msg) 417 | by := pre.Bytes() 418 | ShowBytes(by, 10) 419 | return by 420 | } 421 | 422 | func TestZserverWithOneEmptyJob(t *testing.T) { 423 | 424 | exp := CapnpEncode(`(waitingjobs = [()])`, "Zserver") 425 | 426 | cv.Convey("Given an Zserver with one empty job", t, func() { 427 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 428 | 429 | seg := capn.NewBuffer(nil) 430 | scratch := capn.NewBuffer(nil) 431 | server := air.NewRootZserver(seg) 432 | 433 | joblist := air.NewZjobList(seg, 1) 434 | plist := capn.PointerList(joblist) 435 | 436 | ShowSeg(" pre NewZjob, segment seg is:", seg) 437 | 438 | zjob := air.NewZjob(scratch) 439 | plist.Set(0, capn.Object(zjob)) 440 | 441 | ShowSeg(" pre SetWaitingjobs, segment seg is:", seg) 442 | 443 | fmt.Printf("Then we do the SetWaitingjobs:\n") 444 | server.SetWaitingjobs(joblist) 445 | 446 | // save 447 | buf := bytes.Buffer{} 448 | seg.WriteTo(&buf) 449 | act := buf.Bytes() 450 | save(act, "my.act.zserver") 451 | 452 | // show 453 | ShowSeg(" actual:\n", seg) 454 | 455 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "Zserver"))) 456 | 457 | fmt.Printf("expected:\n") 458 | ShowBytes(exp, 10) 459 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "Zserver"))) 460 | 461 | cv.So(act, cv.ShouldResemble, exp) 462 | }) 463 | }) 464 | } 465 | 466 | func TestDefaultStructField(t *testing.T) { 467 | cv.Convey("Given a new root StackingRoot", t, func() { 468 | cv.Convey("then the aWithDefault field should have a default", func() { 469 | seg := capn.NewBuffer(nil) 470 | root := air.NewRootStackingRoot(seg) 471 | 472 | cv.So(root.AWithDefault().Num(), cv.ShouldEqual, 42) 473 | }) 474 | }) 475 | } 476 | 477 | func TestDataTextCopyOptimization(t *testing.T) { 478 | cv.Convey("Given a text list from a different segment", t, func() { 479 | cv.Convey("Adding it to a different segment shouldn't panic", func() { 480 | seg := capn.NewBuffer(nil) 481 | seg2 := capn.NewBuffer(nil) 482 | root := air.NewRootNester1Capn(seg) 483 | 484 | strsl := seg2.NewTextList(256) 485 | for i := 0; i < strsl.Len(); i++ { 486 | strsl.Set(i, "testess") 487 | } 488 | 489 | root.SetStrs(strsl) 490 | }) 491 | }) 492 | } 493 | -------------------------------------------------------------------------------- /textmv_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | capn "github.com/glycerine/go-capnproto" 8 | air "github.com/glycerine/go-capnproto/aircraftlib" 9 | ) 10 | 11 | // highlight how much faster text movement between segments 12 | // is when special casing Text and Data 13 | // 14 | // run this test with capn.go:1334-1341 commented in/out to compare. 15 | // 16 | func BenchmarkTextMovementBetweenSegments(b *testing.B) { 17 | 18 | buf := make([]byte, 1<<21) 19 | buf2 := make([]byte, 1<<21) 20 | 21 | text := make([]byte, 1<<20) 22 | for i := range text { 23 | text[i] = byte(65 + rand.Int()%26) 24 | } 25 | //stext := string(text) 26 | //fmt.Printf("text = %#v\n", stext) 27 | 28 | astr := make([]string, 1000) 29 | for i := range astr { 30 | astr[i] = string(text[i*1000 : (i+1)*1000]) 31 | } 32 | 33 | b.ResetTimer() 34 | for i := 0; i < b.N; i++ { 35 | seg := capn.NewBuffer(buf[:0]) 36 | scratch := capn.NewBuffer(buf2[:0]) 37 | 38 | ht := air.NewRootHoldsText(seg) 39 | tl := scratch.NewTextList(1000) 40 | 41 | for j := 0; j < 1000; j++ { 42 | tl.Set(j, astr[j]) 43 | } 44 | 45 | ht.SetLst(tl) 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | 13 | capn "github.com/glycerine/go-capnproto" 14 | ) 15 | 16 | // some generally useful capnp/segment utilities 17 | 18 | // shell out to display capnp bytes as human-readable text. Data flow: 19 | // in-memory capn segment -> stdin to capnp decode -> stdout human-readble string form 20 | func CapnpDecodeSegment(seg *capn.Segment, capnpExePath string, capnpSchemaFilePath string, typeName string) string { 21 | 22 | // set defaults 23 | if capnpExePath == "" { 24 | capnpExePath = CheckAndGetCapnpPath() 25 | } 26 | 27 | if capnpSchemaFilePath == "" { 28 | capnpSchemaFilePath = "aircraftlib/aircraft.capnp" 29 | } 30 | 31 | if typeName == "" { 32 | typeName = "Z" 33 | } 34 | 35 | cs := []string{"decode", "--short", capnpSchemaFilePath, typeName} 36 | cmd := exec.Command(capnpExePath, cs...) 37 | cmdline := capnpExePath + " " + strings.Join(cs, " ") 38 | 39 | buf := new(bytes.Buffer) 40 | seg.WriteTo(buf) 41 | 42 | cmd.Stdin = buf 43 | 44 | var errout bytes.Buffer 45 | cmd.Stderr = &errout 46 | 47 | bs, err := cmd.Output() 48 | if err != nil { 49 | if err.Error() == "exit status 1" { 50 | cwd, _ := os.Getwd() 51 | fmt.Fprintf(os.Stderr, "\nCall to capnp in CapnpDecodeSegment(): '%s' in dir '%s' failed with status 1\n", cmdline, cwd) 52 | fmt.Printf("stderr: '%s'\n", string(errout.Bytes())) 53 | fmt.Printf("stdout: '%s'\n", string(bs)) 54 | } 55 | panic(err) 56 | } 57 | return strings.TrimSpace(string(bs)) 58 | } 59 | 60 | // reduce boilerplate, dump this segment to disk. 61 | func SegToFile(seg *capn.Segment, filePath string) { 62 | file, err := os.Create(filePath) 63 | if err != nil { 64 | panic(err) 65 | } 66 | seg.WriteTo(file) 67 | file.Close() 68 | } 69 | 70 | // disk file of a capn segment -> in-memory capn segment -> stdin to capnp decode -> stdout human-readble string form 71 | func CapnFileToText(serializedCapnpFilePathToDisplay string, capnpSchemaFilePath string, capnpExePath string) (string, error) { 72 | 73 | // a) read file into Segment 74 | 75 | byteslice, err := ioutil.ReadFile(serializedCapnpFilePathToDisplay) 76 | if err != nil { 77 | return "", err 78 | } 79 | 80 | seg, nbytes, err := capn.ReadFromMemoryZeroCopy(byteslice) 81 | 82 | if err == io.EOF { 83 | return "", err 84 | } 85 | if err != nil { 86 | return "", err 87 | } 88 | if nbytes == 0 { 89 | return "", errors.New(fmt.Sprintf("did not expect 0 bytes back from capn.ReadFromMemoryZeroCopy() on reading file '%s'", serializedCapnpFilePathToDisplay)) 90 | } 91 | 92 | // b) tell CapnpDecodeSegment() to show the human-readable-text form of the message 93 | // warning: CapnpDecodeSegment() may panic on you. It is a testing utility so that 94 | // is desirable. For production, do something else. 95 | return CapnpDecodeSegment(seg, capnpExePath, capnpSchemaFilePath, "Z"), nil 96 | } 97 | 98 | // return path to capnp if 'which' can find it. Feel free to replace this with 99 | // a more general configuration mechanism. 100 | func CheckAndGetCapnpPath() string { 101 | 102 | path, err := exec.LookPath("capnp") 103 | if err != nil { 104 | panic(fmt.Sprintf("could not locate the capnp executable: put the capnp executable in your path: %s", err)) 105 | } 106 | 107 | cmd := exec.Command(path, "id") 108 | bs, err := cmd.Output() 109 | if err != nil || string(bs[:3]) != `@0x` { 110 | panic(fmt.Sprintf("%s id did not function: put a working capnp executable in your path. Err: %s", path, err)) 111 | } 112 | 113 | return path 114 | } 115 | 116 | // take an already (packed or unpacked, depending on the packed flag) buffer of a serialized segment, and display it 117 | func CapnpDecodeBuf(buf []byte, capnpExePath string, capnpSchemaFilePath string, typeName string, packed bool) string { 118 | 119 | // set defaults 120 | if capnpExePath == "" { 121 | capnpExePath = CheckAndGetCapnpPath() 122 | } 123 | 124 | if capnpSchemaFilePath == "" { 125 | capnpSchemaFilePath = "aircraftlib/aircraft.capnp" 126 | } 127 | 128 | if typeName == "" { 129 | typeName = "Z" 130 | } 131 | 132 | cs := []string{"decode", "--short", capnpSchemaFilePath, typeName} 133 | if packed { 134 | cs = []string{"decode", "--short", "--packed", capnpSchemaFilePath, typeName} 135 | } 136 | cmd := exec.Command(capnpExePath, cs...) 137 | cmdline := capnpExePath + " " + strings.Join(cs, " ") 138 | 139 | cmd.Stdin = bytes.NewReader(buf) 140 | 141 | var errout bytes.Buffer 142 | cmd.Stderr = &errout 143 | 144 | bs, err := cmd.Output() 145 | if err != nil { 146 | if err.Error() == "exit status 1" { 147 | cwd, _ := os.Getwd() 148 | fmt.Fprintf(os.Stderr, "\nCall to capnp in CapnpDecodeBuf(): '%s' in dir '%s' failed with status 1\n", cmdline, cwd) 149 | fmt.Printf("stderr: '%s'\n", string(errout.Bytes())) 150 | fmt.Printf("stdout: '%s'\n", string(bs)) 151 | } 152 | panic(err) 153 | } 154 | return strings.TrimSpace(string(bs)) 155 | } 156 | -------------------------------------------------------------------------------- /utils2_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | "unsafe" 12 | ) 13 | 14 | var zerohi32 uint64 15 | 16 | func init() { 17 | // initialize zerohi32 once 18 | var minus1 int32 = -1 19 | u32 := uint32(minus1) 20 | zerohi32 = uint64(u32) 21 | } 22 | 23 | func CapnpEncode(msg string, typ string, extra_args ...string) []byte { 24 | capnpPath, err := exec.LookPath("capnp") 25 | //capnpPath, err := exec.LookPath("tee") 26 | if err != nil { 27 | panic(err) 28 | } 29 | if !FileExists(capnpPath) { 30 | panic(fmt.Sprintf("could not locate capnp tool in PATH")) 31 | } 32 | 33 | schfn := "aircraftlib/aircraft.capnp" 34 | args := append([]string{"encode", schfn, typ}, extra_args...) 35 | cmdline := fmt.Sprintf("%s %s", capnpPath, strings.Join(args, " ")) 36 | //fmt.Printf("cmdline = %s\n", cmdline) 37 | c := exec.Command(capnpPath, args...) 38 | 39 | var o bytes.Buffer 40 | c.Stdout = &o 41 | 42 | var in bytes.Buffer 43 | in.Write([]byte(msg)) 44 | c.Stdin = &in 45 | 46 | err = c.Run() 47 | if err != nil { 48 | panic(fmt.Errorf("tried to run %s, got err:%s", cmdline, err)) 49 | } 50 | return o.Bytes() 51 | } 52 | 53 | func CapnpDecode(input []byte, typ string) []byte { 54 | capnpPath, err := exec.LookPath("capnp") 55 | //capnpPath, err := exec.LookPath("tee") 56 | if err != nil { 57 | panic(err) 58 | } 59 | if !FileExists(capnpPath) { 60 | panic(fmt.Sprintf("could not locate capnp tool in PATH")) 61 | } 62 | 63 | schfn := "aircraftlib/aircraft.capnp" 64 | args := []string{"decode", "--short", schfn, typ} 65 | cmdline := fmt.Sprintf("%s %s %s %s %s", capnpPath, "decode", "--short", schfn, typ) 66 | fmt.Printf("cmdline = %s\n", cmdline) 67 | c := exec.Command(capnpPath, args...) 68 | 69 | var o bytes.Buffer 70 | c.Stdout = &o 71 | 72 | var e bytes.Buffer 73 | c.Stderr = &e 74 | 75 | var in bytes.Buffer 76 | in.Write(input) 77 | c.Stdin = &in 78 | 79 | err = c.Run() 80 | if err != nil { 81 | fmt.Printf("tried to run %s, got err:%s and stderr: '%s'", cmdline, err, e.Bytes()) 82 | panic(err) 83 | } 84 | return o.Bytes() 85 | } 86 | 87 | func MakeAndMoveToTempDir() (origdir string, tmpdir string) { 88 | 89 | var err error 90 | origdir, err = os.Getwd() 91 | if err != nil { 92 | panic(err) 93 | } 94 | tmpdir, err = ioutil.TempDir(origdir, "tempgocapnpdir") 95 | if err != nil { 96 | panic(err) 97 | } 98 | err = os.Chdir(tmpdir) 99 | if err != nil { 100 | panic(err) 101 | } 102 | 103 | return origdir, tmpdir 104 | } 105 | 106 | func TempDirCleanup(origdir string, tmpdir string) { 107 | // cleanup 108 | os.Chdir(origdir) 109 | err := os.RemoveAll(tmpdir) 110 | if err != nil { 111 | panic(err) 112 | } 113 | } 114 | 115 | func ShowBytes(b []byte, indent int) { 116 | c := NewCap() 117 | k := 0 118 | ind := strings.Repeat(" ", indent) 119 | fmt.Printf("\n%s", ind) 120 | line := 0 121 | for i := 0; i < len(b)/8; i++ { 122 | for j := 0; j < 8; j++ { 123 | fmt.Printf("%02x ", b[k]) 124 | k++ 125 | if k == len(b) { 126 | break 127 | } 128 | } 129 | fmt.Printf(" ==(line %02d)> %s\n%s", line, c.Interp(line, binary.LittleEndian.Uint64(b[k-8:k]), b), ind) 130 | line++ 131 | } 132 | } 133 | 134 | type Cap struct { 135 | nextTag bool 136 | expected map[int]string 137 | } 138 | 139 | func NewCap() *Cap { 140 | return &Cap{ 141 | expected: make(map[int]string), 142 | } 143 | } 144 | 145 | func (c *Cap) Interp(line int, val uint64, b []byte) string { 146 | r := "" 147 | 148 | // allowing store of state and re-discovery 149 | if k, ok := c.expected[line]; ok { 150 | return k 151 | } 152 | 153 | if line == 0 { 154 | numSeg := val&zerohi32 + 1 155 | words := val >> 32 156 | return fmt.Sprintf("stream header: %d segment(s), this segment has %d words", numSeg, words) 157 | } else { 158 | // assume single segment for now 159 | switch A(val) { 160 | case structPointer: 161 | return c.StructPointer(val, line) 162 | case listPointer: 163 | //fmt.Printf("\ndetected List with element count = %d (unless this is a composite). ListB = %d, ListC = %d\n", ListD(val), B(val), ListC(val)) 164 | 165 | if ListC(val) == bit1List { 166 | listSize := ListD(val) 167 | bytesRequired := (listSize + 7) / 8 168 | szBytesWordBoundary := (bytesRequired + 7) &^ 7 169 | eline := line + 1 + B(val) 170 | listContent := BytesToWordString(b[eline*8 : (eline*8 + szBytesWordBoundary)]) 171 | c.expected[eline] = fmt.Sprintf("bit-list contents: %s", listContent) 172 | return fmt.Sprintf("list of %d bits (pointer to: '%s' at line %d)", listSize, listContent, eline) 173 | } 174 | 175 | if ListC(val) == byte1List { 176 | // assume it will be text 177 | eline := line + 1 + B(val) 178 | c.expected[eline] = fmt.Sprintf("text contents: %s", string(b[eline*8:(eline*8+ListD(val))])) 179 | return fmt.Sprintf("list of bytes/Text (pointer to: '%s' at line %d)", string(b[eline*8:(eline*8+ListD(val)-1)]), eline) 180 | } 181 | 182 | if ListC(val) == compositeList { 183 | c.nextTag = true 184 | tagline := line + 1 + B(val) 185 | tag := binary.LittleEndian.Uint64(b[(tagline)*8 : (tagline+1)*8]) 186 | r = fmt.Sprintf("list-of-composite, count: %d. (from tag at line %d). total-words-not-counting-tag-word: %d", B(tag), line+1+B(val), ListD(val)) 187 | c.expected[tagline] = CompositeTag(tag) 188 | return r 189 | } 190 | eline := line + 1 + B(val) 191 | return fmt.Sprintf("list, first element starts %d words from here (at line %d). Size: %s, num-elem: %d", B(val), eline, ListCString(val), ListD(val)) 192 | 193 | default: 194 | r += "other" 195 | } 196 | } 197 | return r 198 | } 199 | 200 | // lsb struct pointer msb 201 | // +-+-----------------------------+---------------+---------------+ 202 | // |A| B | C | D | 203 | // +-+-----------------------------+---------------+---------------+ 204 | // 205 | // A (2 bits) = 0, to indicate that this is a struct pointer. 206 | // B (30 bits) = Offset, in words, from the end of the pointer to the 207 | // start of the struct's data section. Signed. 208 | // C (16 bits) = Size of the struct's data section, in words. 209 | // D (16 bits) = Size of the struct's pointer section, in words. 210 | // 211 | // (B is the same for list pointers, but C and D have different size 212 | // and meaning) 213 | // 214 | // B(): extract the count from the B section of a struct pointer 215 | // a.k.a. signedOffsetFromStructPointer() 216 | func B(val uint64) int { 217 | u64 := uint64(val) & zerohi32 218 | u32 := uint32(u64) 219 | s32 := int32(u32) >> 2 220 | return int(s32) 221 | } 222 | 223 | func A(val uint64) int { 224 | return int(val & 3) 225 | } 226 | 227 | func StructC(val uint64) int { 228 | return int(uint16(val >> 32)) 229 | } 230 | 231 | func StructD(val uint64) int { 232 | return int(uint16(val >> 48)) 233 | } 234 | 235 | func ListC(val uint64) int { 236 | return int((val >> 32) & 7) 237 | } 238 | 239 | func ListCString(val uint64) string { 240 | switch ListC(val) { 241 | case voidList: 242 | return "void" 243 | case bit1List: 244 | return "1bit" 245 | case byte1List: 246 | return "1byte" 247 | case byte2List: 248 | return "2bytes" 249 | case byte4List: 250 | return "4bytes" 251 | case byte8List: 252 | return "8bytes" 253 | case pointerList: 254 | return "pointer" 255 | case compositeList: 256 | return "composite" 257 | default: 258 | panic("unknown list element size") 259 | } 260 | return "" 261 | } 262 | 263 | func ListD(val uint64) int { 264 | return int(uint32(val >> 35)) 265 | } 266 | 267 | const ( 268 | structPointer = 0 269 | listPointer = 1 270 | farPointer = 2 271 | doubleFarPointer = 6 272 | 273 | voidList = 0 274 | bit1List = 1 275 | byte1List = 2 276 | byte2List = 3 277 | byte4List = 4 278 | byte8List = 5 279 | pointerList = 6 280 | compositeList = 7 281 | ) 282 | 283 | /* 284 | lsb list pointer msb 285 | +-+-----------------------------+--+----------------------------+ 286 | |A| B |C | D | 287 | +-+-----------------------------+--+----------------------------+ 288 | 289 | A (2 bits) = 1, to indicate that this is a list pointer. 290 | B (30 bits) = Offset, in words, from the end of the pointer to the 291 | start of the first element of the list. Signed. 292 | C (3 bits) = Size of each element: 293 | 0 = 0 (e.g. List(Void)) 294 | 1 = 1 bit 295 | 2 = 1 byte 296 | 3 = 2 bytes 297 | 4 = 4 bytes 298 | 5 = 8 bytes (non-pointer) 299 | 6 = 8 bytes (pointer) 300 | 7 = composite (see below) 301 | D (29 bits) = Number of elements in the list, except when C is 7 302 | (see below). 303 | 304 | The pointed-to values are tightly-packed. In particular, Bools are packed bit-by-bit in little-endian order (the first bit is the least-significant bit of the first byte). 305 | 306 | Lists of structs use the smallest element size in which the struct can fit. So, a list of structs that each contain two UInt8 fields and nothing else could be encoded with C = 3 (2-byte elements). A list of structs that each contain a single Text field would be encoded as C = 6 (pointer elements). A list of structs that each contain a single Bool field would be encoded using C = 1 (1-bit elements). A list structs which are each more than one word in size must be be encoded using C = 7 (composite). 307 | 308 | When C = 7, the elements of the list are fixed-width composite values – usually, structs. In this case, the list content is prefixed by a "tag" word that describes each individual element. The tag has the same layout as a struct pointer, except that the pointer offset (B) instead indicates the number of elements in the list. Meanwhile, section (D) of the list pointer – which normally would store this element count – instead stores the total number of words in the list (not counting the tag word). The reason we store a word count in the pointer rather than an element count is to ensure that the extents of the list’s location can always be determined by inspecting the pointer alone, without having to look at the tag; this may allow more-efficient prefetching in some use cases. The reason we don’t store struct lists as a list of pointers is because doing so would take significantly more space (an extra pointer per element) and may be less cache-friendly. 309 | */ 310 | 311 | func CompositeTag(val uint64) string { 312 | //return fmt.Sprintf("composite-tag, num elements in list: %d. Each elem: {prim: %d words. pointers: %d words}.", B(val), StructC(val), StructD(val)) 313 | return fmt.Sprintf("composite-tag {prim: %d, pointers: %d words}.", StructC(val), StructD(val)) 314 | } 315 | 316 | func (c *Cap) StructPointer(val uint64, line int) string { 317 | if val == 0 { 318 | return "empty struct, zero valued." 319 | } 320 | eline := line + 1 + B(val) 321 | numprim := StructC(val) 322 | if numprim > 0 { 323 | for i := 0; i < numprim; i++ { 324 | c.expected[eline+i] = fmt.Sprintf("primitive data for struct on line %d", line) 325 | } 326 | } 327 | return fmt.Sprintf("struct-pointer, data starts at +%d words (line %d). {prim: %d, pointers: %d words}.", B(val), eline, StructC(val), StructD(val)) 328 | } 329 | 330 | func save(b []byte, fn string) { 331 | file, err := os.Create(fn) 332 | if err != nil { 333 | panic(err) 334 | } 335 | file.Write(b) 336 | file.Close() 337 | 338 | } 339 | 340 | func InspectSlice(slice []byte) { 341 | // Capture the address to the slice structure 342 | address := unsafe.Pointer(&slice) 343 | 344 | // Create a pointer to the underlying array 345 | addPtr := (*[8]byte)(unsafe.Pointer(*(*uintptr)(address))) 346 | 347 | fmt.Printf("underlying array Addr[%p]\n", addPtr) 348 | fmt.Printf("\n\n") 349 | } 350 | 351 | func FileExists(name string) bool { 352 | fi, err := os.Stat(name) 353 | if err != nil { 354 | return false 355 | } 356 | if fi.IsDir() { 357 | return false 358 | } 359 | return true 360 | } 361 | 362 | func DirExists(name string) bool { 363 | fi, err := os.Stat(name) 364 | if err != nil { 365 | return false 366 | } 367 | if fi.IsDir() { 368 | return true 369 | } 370 | return false 371 | } 372 | 373 | func BytesToWordString(b []byte) string { 374 | var s string 375 | k := 0 376 | for i := 0; i < len(b)/8; i++ { 377 | for j := 0; j < 8; j++ { 378 | s += fmt.Sprintf("%02x ", b[k]) 379 | k++ 380 | if k == len(b) { 381 | break 382 | } 383 | } 384 | } 385 | return s 386 | } 387 | -------------------------------------------------------------------------------- /version_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | capn "github.com/glycerine/go-capnproto" 9 | air "github.com/glycerine/go-capnproto/aircraftlib" 10 | cv "github.com/glycerine/goconvey/convey" 11 | ) 12 | 13 | func TestV0ListofEmptyShouldMatchCapnp(t *testing.T) { 14 | 15 | exp := CapnpEncode("(mylist = [(),()])", "HoldsVerEmptyList") 16 | 17 | cv.Convey("Given an empty struct with 0 data/0 ptr fields", t, func() { 18 | cv.Convey("then a list of 2 empty structs should match the capnp representation", func() { 19 | 20 | seg := capn.NewBuffer(nil) 21 | scratch := capn.NewBuffer(nil) 22 | holder := air.NewRootHoldsVerEmptyList(seg) 23 | 24 | ShowSeg(" after NewRootHoldsVerEmptyList(seg), segment seg is:", seg) 25 | 26 | elist := air.NewVerEmptyList(seg, 2) 27 | plist := capn.PointerList(elist) 28 | 29 | ShowSeg(" pre NewVerEmpty(scratch), segment seg is:", seg) 30 | 31 | e0 := air.NewVerEmpty(scratch) 32 | e1 := air.NewVerEmpty(scratch) 33 | plist.Set(0, capn.Object(e0)) 34 | plist.Set(1, capn.Object(e1)) 35 | 36 | ShowSeg(" pre SetMylist, segment seg is:", seg) 37 | 38 | fmt.Printf("Then we do the SetMylist():\n") 39 | holder.SetMylist(elist) 40 | 41 | // save 42 | buf := bytes.Buffer{} 43 | seg.WriteTo(&buf) 44 | act := buf.Bytes() 45 | save(act, "my.act.holder.elist") 46 | 47 | // show 48 | ShowSeg(" actual:\n", seg) 49 | 50 | fmt.Printf("act decoded by capnp: '%s'\n", string(CapnpDecode(act, "HoldsVerEmptyList"))) 51 | 52 | fmt.Printf("expected:\n") 53 | ShowBytes(exp, 10) 54 | fmt.Printf("exp decoded by capnp: '%s'\n", string(CapnpDecode(exp, "HoldsVerEmptyList"))) 55 | 56 | cv.So(act, cv.ShouldResemble, exp) 57 | }) 58 | }) 59 | } 60 | 61 | func TestV1DataVersioningBiggerToEmpty(t *testing.T) { 62 | 63 | //expTwoSet := CapnpEncode("(mylist = [(val = 27, duo = 26),(val = 42, duo = 41)])", "HoldsVerTwoDataList") 64 | //expOneDataOneDefault := CapnpEncode("(mylist = [(val = 27, duo = 0),(val = 42, duo = 0)])", "HoldsVerTwoDataList") 65 | //expTwoEmpty := CapnpEncode("(mylist = [(),()])", "HoldsVerTwoDataList") 66 | 67 | //expEmpty := CapnpEncode("(mylist = [(),()])", "HoldsVerEmptyList") 68 | //expOne := CapnpEncode("(mylist = [(val = 27),(val = 42)])", "HoldsVerOneDataList") 69 | 70 | cv.Convey("Given a struct with 0 data/0 ptr fields, and a newer version of the struct with 2 data fields", t, func() { 71 | cv.Convey("then reading serialized bigger-struct-list into the smaller (empty or one data-member) list should work, truncating/ignoring the new fields", func() { 72 | 73 | seg := capn.NewBuffer(nil) 74 | scratch := capn.NewBuffer(nil) 75 | holder := air.NewRootHoldsVerTwoDataList(seg) 76 | 77 | twolist := air.NewVerTwoDataList(scratch, 2) 78 | plist := capn.PointerList(twolist) 79 | 80 | d0 := air.NewVerTwoData(scratch) 81 | d0.SetVal(27) 82 | d0.SetDuo(26) 83 | d1 := air.NewVerTwoData(scratch) 84 | d1.SetVal(42) 85 | d1.SetDuo(41) 86 | plist.Set(0, capn.Object(d0)) 87 | plist.Set(1, capn.Object(d1)) 88 | 89 | holder.SetMylist(twolist) 90 | 91 | ShowSeg(" before serializing out, segment scratch is:", scratch) 92 | ShowSeg(" before serializing out, segment seg is:", seg) 93 | 94 | // serialize out 95 | buf := bytes.Buffer{} 96 | seg.WriteTo(&buf) 97 | segbytes := buf.Bytes() 98 | 99 | // and read-back in using smaller expectations 100 | reseg, _, err := capn.ReadFromMemoryZeroCopy(segbytes) 101 | if err != nil { 102 | panic(err) 103 | } 104 | ShowSeg(" after re-reading segbytes, segment reseg is:", reseg) 105 | fmt.Printf("segbytes decoded by capnp as HoldsVerEmptyList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerEmptyList"))) 106 | fmt.Printf("segbytes decoded by capnp as HoldsVerOneDataList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerOneDataList"))) 107 | fmt.Printf("segbytes decoded by capnp as HoldsVerTwoDataList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerTwoDataList"))) 108 | 109 | reHolder := air.ReadRootHoldsVerEmptyList(reseg) 110 | elist := reHolder.Mylist() 111 | lene := elist.Len() 112 | cv.So(lene, cv.ShouldEqual, 2) 113 | 114 | reHolder1 := air.ReadRootHoldsVerOneDataList(reseg) 115 | onelist := reHolder1.Mylist() 116 | lenone := onelist.Len() 117 | cv.So(lenone, cv.ShouldEqual, 2) 118 | 119 | for i := 0; i < 2; i++ { 120 | ele := onelist.At(i) 121 | val := ele.Val() 122 | cv.So(val, cv.ShouldEqual, twolist.At(i).Val()) 123 | } 124 | 125 | reHolder2 := air.ReadRootHoldsVerTwoDataList(reseg) 126 | twolist2 := reHolder2.Mylist() 127 | lentwo2 := twolist2.Len() 128 | cv.So(lentwo2, cv.ShouldEqual, 2) 129 | 130 | for i := 0; i < 2; i++ { 131 | ele := twolist2.At(i) 132 | val := ele.Val() 133 | duo := ele.Duo() 134 | cv.So(val, cv.ShouldEqual, twolist.At(i).Val()) 135 | cv.So(duo, cv.ShouldEqual, twolist.At(i).Duo()) 136 | } 137 | 138 | }) 139 | }) 140 | } 141 | 142 | func TestV1DataVersioningEmptyToBigger(t *testing.T) { 143 | 144 | //expOneSet := CapnpEncode("(mylist = [(val = 27),(val = 42)])", "HoldsVerOneDataList") 145 | //expOneZeroed := CapnpEncode("(mylist = [(val = 0),(val = 0)])", "HoldsVerOneDataList") 146 | //expOneEmpty := CapnpEncode("(mylist = [(),()])", "HoldsVerOneDataList") 147 | expEmpty := CapnpEncode("(mylist = [(),()])", "HoldsVerEmptyList") 148 | 149 | cv.Convey("Given a struct with 0 data/0 ptr fields, and a newer version of the struct with 1 data fields", t, func() { 150 | cv.Convey("then reading from serialized form the small list into the bigger (one or two data values) list should work, getting default value 0 for val/duo.", func() { 151 | 152 | seg := capn.NewBuffer(nil) 153 | scratch := capn.NewBuffer(nil) 154 | 155 | emptyholder := air.NewRootHoldsVerEmptyList(seg) 156 | elist := air.NewVerEmptyList(scratch, 2) 157 | emptyholder.SetMylist(elist) 158 | 159 | actEmpty := ShowSeg(" after NewRootHoldsVerEmptyList(seg) and SetMylist(elist), segment seg is:", seg) 160 | actEmptyCap := string(CapnpDecode(actEmpty, "HoldsVerEmptyList")) 161 | expEmptyCap := string(CapnpDecode(expEmpty, "HoldsVerEmptyList")) 162 | cv.So(actEmptyCap, cv.ShouldResemble, expEmptyCap) 163 | 164 | fmt.Printf("\n actEmpty is \n") 165 | ShowBytes(actEmpty, 10) 166 | fmt.Printf("actEmpty decoded by capnp: '%s'\n", string(CapnpDecode(actEmpty, "HoldsVerEmptyList"))) 167 | cv.So(actEmpty, cv.ShouldResemble, expEmpty) 168 | 169 | // seg is set, now read into bigger list 170 | buf := bytes.Buffer{} 171 | seg.WriteTo(&buf) 172 | segbytes := buf.Bytes() 173 | 174 | reseg, _, err := capn.ReadFromMemoryZeroCopy(segbytes) 175 | if err != nil { 176 | panic(err) 177 | } 178 | ShowSeg(" after re-reading segbytes, segment reseg is:", reseg) 179 | fmt.Printf("segbytes decoded by capnp as HoldsVerOneDataList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerOneDataList"))) 180 | 181 | reHolder := air.ReadRootHoldsVerOneDataList(reseg) 182 | onelist := reHolder.Mylist() 183 | lenone := onelist.Len() 184 | cv.So(lenone, cv.ShouldEqual, 2) 185 | for i := 0; i < 2; i++ { 186 | ele := onelist.At(i) 187 | val := ele.Val() 188 | cv.So(val, cv.ShouldEqual, 0) 189 | } 190 | 191 | reHolder2 := air.ReadRootHoldsVerTwoDataList(reseg) 192 | twolist := reHolder2.Mylist() 193 | lentwo := twolist.Len() 194 | cv.So(lentwo, cv.ShouldEqual, 2) 195 | for i := 0; i < 2; i++ { 196 | ele := twolist.At(i) 197 | val := ele.Val() 198 | cv.So(val, cv.ShouldEqual, 0) 199 | duo := ele.Duo() 200 | cv.So(duo, cv.ShouldEqual, 0) 201 | } 202 | 203 | }) 204 | }) 205 | } 206 | 207 | func TestDataVersioningZeroPointersToMore(t *testing.T) { 208 | 209 | expEmpty := CapnpEncode("(mylist = [(),()])", "HoldsVerEmptyList") 210 | 211 | cv.Convey("Given a struct with 0 ptr fields, and a newer version of the struct with 1-2 pointer fields", t, func() { 212 | cv.Convey("then serializing the empty list and reading it back into 1 or 2 pointer fields should default initialize the pointer fields", func() { 213 | 214 | seg := capn.NewBuffer(nil) 215 | scratch := capn.NewBuffer(nil) 216 | 217 | emptyholder := air.NewRootHoldsVerEmptyList(seg) 218 | elist := air.NewVerEmptyList(scratch, 2) 219 | emptyholder.SetMylist(elist) 220 | 221 | actEmpty := ShowSeg(" after NewRootHoldsVerEmptyList(seg) and SetMylist(elist), segment seg is:", seg) 222 | actEmptyCap := string(CapnpDecode(actEmpty, "HoldsVerEmptyList")) 223 | expEmptyCap := string(CapnpDecode(expEmpty, "HoldsVerEmptyList")) 224 | cv.So(actEmptyCap, cv.ShouldResemble, expEmptyCap) 225 | 226 | fmt.Printf("\n actEmpty is \n") 227 | ShowBytes(actEmpty, 10) 228 | fmt.Printf("actEmpty decoded by capnp: '%s'\n", string(CapnpDecode(actEmpty, "HoldsVerEmptyList"))) 229 | cv.So(actEmpty, cv.ShouldResemble, expEmpty) 230 | 231 | // seg is set, now read into bigger list 232 | buf := bytes.Buffer{} 233 | seg.WriteTo(&buf) 234 | segbytes := buf.Bytes() 235 | 236 | reseg, _, err := capn.ReadFromMemoryZeroCopy(segbytes) 237 | if err != nil { 238 | panic(err) 239 | } 240 | ShowSeg(" after re-reading segbytes, segment reseg is:", reseg) 241 | fmt.Printf("segbytes decoded by capnp as HoldsVerOneDataList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerOneDataList"))) 242 | 243 | reHolder := air.ReadRootHoldsVerTwoTwoList(reseg) 244 | list22 := reHolder.Mylist() 245 | len22 := list22.Len() 246 | cv.So(len22, cv.ShouldEqual, 2) 247 | for i := 0; i < 2; i++ { 248 | ele := list22.At(i) 249 | val := ele.Val() 250 | cv.So(val, cv.ShouldEqual, 0) 251 | duo := ele.Duo() 252 | cv.So(duo, cv.ShouldEqual, 0) 253 | ptr1 := ele.Ptr1() 254 | ptr2 := ele.Ptr2() 255 | fmt.Printf("ptr1 = %#v\n", ptr1) 256 | cv.So(ptr1.Segment, cv.ShouldEqual, nil) 257 | fmt.Printf("ptr2 = %#v\n", ptr2) 258 | cv.So(ptr2.Segment, cv.ShouldEqual, nil) 259 | } 260 | 261 | }) 262 | }) 263 | } 264 | 265 | func TestDataVersioningZeroPointersToTwo(t *testing.T) { 266 | cv.Convey("Given a struct with 2 ptr fields, and another version of the struct with 0 or 1 pointer fields", t, func() { 267 | cv.Convey("then reading serialized bigger-struct-list into the smaller (empty or one data-pointer) list should work, truncating/ignoring the new fields", func() { 268 | 269 | seg := capn.NewBuffer(nil) 270 | scratch := capn.NewBuffer(nil) 271 | holder := air.NewRootHoldsVerTwoTwoList(seg) 272 | 273 | twolist := air.NewVerTwoDataTwoPtrList(scratch, 2) 274 | plist := capn.PointerList(twolist) 275 | 276 | d0 := air.NewVerTwoDataTwoPtr(scratch) 277 | d0.SetVal(27) 278 | d0.SetDuo(26) 279 | 280 | v1 := air.NewVerOneData(scratch) 281 | v1.SetVal(25) 282 | v2 := air.NewVerOneData(scratch) 283 | v2.SetVal(23) 284 | 285 | d0.SetPtr1(v1) 286 | d0.SetPtr2(v2) 287 | 288 | d1 := air.NewVerTwoDataTwoPtr(scratch) 289 | d1.SetVal(42) 290 | d1.SetDuo(41) 291 | 292 | w1 := air.NewVerOneData(scratch) 293 | w1.SetVal(40) 294 | w2 := air.NewVerOneData(scratch) 295 | w2.SetVal(38) 296 | 297 | d1.SetPtr1(w1) 298 | d1.SetPtr2(w2) 299 | 300 | plist.Set(0, capn.Object(d0)) 301 | plist.Set(1, capn.Object(d1)) 302 | 303 | holder.SetMylist(twolist) 304 | 305 | ShowSeg(" before serializing out, segment scratch is:", scratch) 306 | ShowSeg(" before serializing out, segment seg is:", seg) 307 | 308 | // serialize out 309 | buf := bytes.Buffer{} 310 | seg.WriteTo(&buf) 311 | segbytes := buf.Bytes() 312 | 313 | // and read-back in using smaller expectations 314 | reseg, _, err := capn.ReadFromMemoryZeroCopy(segbytes) 315 | if err != nil { 316 | panic(err) 317 | } 318 | ShowSeg(" after re-reading segbytes, segment reseg is:", reseg) 319 | fmt.Printf("segbytes decoded by capnp as HoldsVerEmptyList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerEmptyList"))) 320 | fmt.Printf("segbytes decoded by capnp as HoldsVerOnePtrList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerOnePtrList"))) 321 | fmt.Printf("segbytes decoded by capnp as HoldsVerTwoTwoList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerTwoTwoList"))) 322 | 323 | reHolder := air.ReadRootHoldsVerEmptyList(reseg) 324 | elist := reHolder.Mylist() 325 | lene := elist.Len() 326 | cv.So(lene, cv.ShouldEqual, 2) 327 | 328 | reHolder1 := air.ReadRootHoldsVerOnePtrList(reseg) 329 | onelist := reHolder1.Mylist() 330 | lenone := onelist.Len() 331 | cv.So(lenone, cv.ShouldEqual, 2) 332 | 333 | for i := 0; i < 2; i++ { 334 | ele := onelist.At(i) 335 | ptr1 := ele.Ptr() 336 | cv.So(ptr1.Val(), cv.ShouldEqual, twolist.At(i).Ptr1().Val()) 337 | } 338 | 339 | reHolder2 := air.ReadRootHoldsVerTwoTwoPlus(reseg) 340 | twolist2 := reHolder2.Mylist() 341 | lentwo2 := twolist2.Len() 342 | cv.So(lentwo2, cv.ShouldEqual, 2) 343 | 344 | for i := 0; i < 2; i++ { 345 | ele := twolist2.At(i) 346 | ptr1 := ele.Ptr1() 347 | ptr2 := ele.Ptr2() 348 | cv.So(ptr1.Val(), cv.ShouldEqual, twolist.At(i).Ptr1().Val()) 349 | //cv.So(ptr1.Duo(), cv.ShouldEqual, twolist.At(i).Ptr1().Duo()) 350 | cv.So(ptr2.Val(), cv.ShouldEqual, twolist.At(i).Ptr2().Val()) 351 | //cv.So(ptr2.Duo(), cv.ShouldEqual, twolist.At(i).Ptr2().Duo()) 352 | cv.So(ele.Tre(), cv.ShouldEqual, 0) 353 | cv.So(ele.Lst3().Len(), cv.ShouldEqual, 0) 354 | } 355 | 356 | }) 357 | }) 358 | } 359 | -------------------------------------------------------------------------------- /write_test.go: -------------------------------------------------------------------------------- 1 | package capn_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | capn "github.com/glycerine/go-capnproto" 10 | air "github.com/glycerine/go-capnproto/aircraftlib" 11 | cv "github.com/glycerine/goconvey/convey" 12 | ) 13 | 14 | func ExampleAirplaneWrite() string { 15 | 16 | fname := "out.write_test.airplane.cpz" 17 | 18 | // make a brand new, empty segment (message) 19 | seg := capn.NewBuffer(nil) 20 | 21 | // If you want runtime-type identification, this is easily obtained. Just 22 | // wrap everything in a struct that contains a single anoymous union (e.g. struct Z). 23 | // Then always set a Z as the root object in you message/first segment. 24 | // The cost of the extra word of storage is usually worth it, as 25 | // then human readable output is easily obtained via a shell command such as 26 | // 27 | // $ cat binary.cpz | capnp decode aircraft.capnp Z 28 | // 29 | // If you need to conserve space, and know your content in advance, it 30 | // isn't necessary to use an anonymous union. Just supply the type name 31 | // in place of 'Z' in the decode command above. 32 | 33 | z := air.NewRootZ(seg) // root should be allocated first. 34 | // There must be only one root. 35 | 36 | // then non-root objects: 37 | aircraft := air.NewAircraft(seg) 38 | b737 := air.NewB737(seg) 39 | planebase := air.NewPlaneBase(seg) 40 | 41 | // how to create a list. Requires a cast at the moment. 42 | homes := air.NewAirportList(seg, 2) 43 | uint16list := capn.UInt16List(homes) // cast to the underlying type 44 | uint16list.Set(0, uint16(air.AIRPORT_JFK)) 45 | uint16list.Set(1, uint16(air.AIRPORT_LAX)) 46 | 47 | // set the primitive fields 48 | planebase.SetCanFly(true) 49 | planebase.SetName("Henrietta") 50 | planebase.SetRating(100) 51 | planebase.SetMaxSpeed(876) // km/hr 52 | // if we don't set capacity, it will get the default value, in this case 0. 53 | //planebase.SetCapacity(26020) // Liters fuel 54 | 55 | // set a list field 56 | planebase.SetHomes(homes) 57 | 58 | // wire up the pointers between objects 59 | b737.SetBase(planebase) 60 | aircraft.SetB737(b737) 61 | z.SetAircraft(aircraft) 62 | 63 | // ready to write 64 | 65 | // example of writing to memory 66 | buf := bytes.Buffer{} 67 | seg.WriteTo(&buf) 68 | 69 | // example of writing to file. Just use WriteTo(). 70 | // We could have used SegToFile(seg, fname) from 71 | // util_test.go intead, but this makes it clear how easy it is. 72 | file, err := os.Create(fname) 73 | defer file.Close() 74 | if err != nil { 75 | panic(err) 76 | } 77 | seg.WriteTo(file) 78 | 79 | // readback and view that file in human readable format. Defined in util_test.go 80 | text, err := CapnFileToText(fname, "aircraftlib/aircraft.capnp", "") 81 | if err != nil { 82 | panic(err) 83 | } 84 | fmt.Printf("here is our aircraft:\n") 85 | fmt.Printf("%s\n", text) 86 | 87 | return text 88 | } 89 | 90 | func TestAircraftWrite(t *testing.T) { 91 | 92 | observedText := ExampleAirplaneWrite() 93 | expectedText := `(aircraft = (b737 = (base = (name = "Henrietta", homes = [jfk, lax], rating = 100, canFly = true, capacity = 0, maxSpeed = 876))))` 94 | 95 | cv.Convey("When we run the ExampleAirplaneWrite() function in write_test.go", t, func() { 96 | cv.Convey("Then we should see the human readable B737 example struct we expect", func() { 97 | cv.So(observedText, cv.ShouldEqual, expectedText) 98 | }) 99 | }) 100 | 101 | } 102 | 103 | func TestVoidUnionSetters(t *testing.T) { 104 | want := CapnpEncode(`(b = void)`, "VoidUnion") 105 | 106 | cv.Convey("Given a VoidUnion set to b", t, func() { 107 | cv.Convey("then the go-capnproto serialization should match the capnp c++ serialization", func() { 108 | seg := capn.NewBuffer(nil) 109 | voidUnion := air.NewRootVoidUnion(seg) 110 | voidUnion.SetB() 111 | 112 | var buf bytes.Buffer 113 | seg.WriteTo(&buf) 114 | act := buf.Bytes() 115 | 116 | cv.So(act, cv.ShouldResemble, want) 117 | }) 118 | }) 119 | } 120 | --------------------------------------------------------------------------------