package capnp_test

import (
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"testing"
	"time"
	"unsafe"

	"capnproto.org/go/capnp/v3"
	air "capnproto.org/go/capnp/v3/internal/aircraftlib"
	"capnproto.org/go/capnp/v3/internal/capnptool"
)

// A marshalTest tests whether a message can be encoded then read by the
// reference capnp implementation.
type marshalTest struct {
	name string

	msg *capnp.Message
	typ string

	text string
	data []byte
}

func makeMarshalTests(t *testing.T) []marshalTest {
	tests := []marshalTest{
		{
			name: "zdateFilledMessage(1)",
			msg:  zdateFilledMessage(t, 1),
			typ:  "Z",
			text: "(zdatevec = [(year = 2004, month = 12, day = 7)])\n",
		},
		{
			name: "zdateFilledMessage(10)",
			msg:  zdateFilledMessage(t, 10),
			typ:  "Z",
			text: "(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)])\n",
		},
		{
			name: "zdataFilledMessage(20)",
			msg:  zdataFilledMessage(t, 20),
			typ:  "Z",
			text: `(zdata = (data = "\000\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023"))` + "\n",
			data: []byte{
				0, 0, 0, 0, 9, 0, 0, 0,
				0, 0, 0, 0, 3, 0, 1, 0,
				28, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 162, 0, 0, 0,
				0, 1, 2, 3, 4, 5, 6, 7,
				8, 9, 10, 11, 12, 13, 14, 15,
				16, 17, 18, 19, 0, 0, 0, 0,
			},
		},
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		if _, err := air.NewRootZjob(seg); err != nil {
			t.Fatal(err)
		}
		tests = append(tests, marshalTest{
			name: "empty Zjob",
			msg:  msg,
			typ:  "Zjob",
			text: "()\n",
			data: []byte{
				0, 0, 0, 0, 3, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 2, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		zjob, err := air.NewRootZjob(seg)
		if err != nil {
			t.Fatal(err)
		}
		if err := zjob.SetCmd("abc"); err != nil {
			t.Fatal(err)
		}
		tests = append(tests, marshalTest{
			name: "Zjob with text",
			msg:  msg,
			typ:  "Zjob",
			text: "(cmd = \"abc\")\n",
			data: []byte{
				0, 0, 0, 0, 4, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 2, 0,
				5, 0, 0, 0, 34, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				97, 98, 99, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		zjob, err := air.NewRootZjob(seg)
		if err != nil {
			t.Fatal(err)
		}
		args, err := zjob.NewArgs(1)
		if err != nil {
			t.Fatal(err)
		}
		if err := args.Set(0, "xyz"); err != nil {
			t.Fatal(err)
		}
		tests = append(tests, marshalTest{
			name: "Zjob with text list",
			msg:  msg,
			typ:  "Zjob",
			text: "(args = [\"xyz\"])\n",
			data: []byte{
				0, 0, 0, 0, 5, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 2, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				1, 0, 0, 0, 14, 0, 0, 0,
				1, 0, 0, 0, 34, 0, 0, 0,
				120, 121, 122, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		zjob, err := air.NewRootZjob(seg)
		if err != nil {
			t.Fatal(err)
		}
		if err := zjob.SetCmd("abc"); err != nil {
			t.Fatal(err)
		}
		args, err := zjob.NewArgs(1)
		if err != nil {
			t.Fatal(err)
		}
		if err := args.Set(0, "xyz"); err != nil {
			t.Fatal(err)
		}
		tests = append(tests, marshalTest{
			name: "Zjob with text and text list",
			msg:  msg,
			typ:  "Zjob",
			text: "(cmd = \"abc\", args = [\"xyz\"])\n",
			data: []byte{
				0, 0, 0, 0, 6, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 2, 0,
				5, 0, 0, 0, 34, 0, 0, 0,
				5, 0, 0, 0, 14, 0, 0, 0,
				97, 98, 99, 0, 0, 0, 0, 0,
				1, 0, 0, 0, 34, 0, 0, 0,
				120, 121, 122, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		server, err := air.NewRootZserver(seg)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := server.NewWaitingjobs(1); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "Zserver with one empty job",
			msg:  msg,
			typ:  "Zserver",
			text: "(waitingjobs = [()])\n",
			data: []byte{
				0, 0, 0, 0, 5, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 23, 0, 0, 0,
				4, 0, 0, 0, 0, 0, 2, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		server, err := air.NewRootZserver(seg)
		if err != nil {
			t.Fatal(err)
		}
		joblist, err := server.NewWaitingjobs(1)
		if err != nil {
			t.Fatal(err)
		}
		if err := joblist.At(0).SetCmd("abc"); err != nil {
			t.Fatal(err)
		}
		args, err := joblist.At(0).NewArgs(1)
		if err != nil {
			t.Fatal(err)
		}
		if err := args.Set(0, "xyz"); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "Zserver with one full job",
			msg:  msg,
			typ:  "Zserver",
			text: "(waitingjobs = [(cmd = \"abc\", args = [\"xyz\"])])\n",
			data: []byte{
				0, 0, 0, 0, 8, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 23, 0, 0, 0,
				4, 0, 0, 0, 0, 0, 2, 0,
				5, 0, 0, 0, 34, 0, 0, 0,
				5, 0, 0, 0, 14, 0, 0, 0,
				97, 98, 99, 0, 0, 0, 0, 0,
				1, 0, 0, 0, 34, 0, 0, 0,
				120, 121, 122, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		server, err := air.NewRootZserver(seg)
		if err != nil {
			t.Fatal(err)
		}
		joblist, err := server.NewWaitingjobs(2)
		if err != nil {
			t.Fatal(err)
		}
		if err := joblist.At(0).SetCmd("abc"); err != nil {
			t.Fatal(err)
		}
		if err := joblist.At(1).SetCmd("xyz"); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "Zserver with two jobs",
			msg:  msg,
			typ:  "Zserver",
			text: "(waitingjobs = [(cmd = \"abc\"), (cmd = \"xyz\")])\n",
			data: []byte{
				0, 0, 0, 0, 9, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 39, 0, 0, 0,
				8, 0, 0, 0, 0, 0, 2, 0,
				13, 0, 0, 0, 34, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				9, 0, 0, 0, 34, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				'a', 'b', 'c', 0, 0, 0, 0, 0,
				'x', 'y', 'z', 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		_, scratch := capnp.NewSingleSegmentMessage(nil)

		// in seg
		segbag, err := air.NewRootBag(seg)
		if err != nil {
			t.Fatal(err)
		}

		// in scratch
		xc, err := air.NewRootCounter(scratch)
		if err != nil {
			t.Fatal(err)
		}
		xc.SetSize(9)

		// copy from scratch to seg
		if err = segbag.SetCounter(xc); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "copy struct between messages",
			msg:  msg,
			typ:  "Bag",
			text: "(counter = (size = 9))\n",
			data: []byte{
				0, 0, 0, 0, 6, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				0, 0, 0, 0, 1, 0, 3, 0,
				9, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		_, scratch := capnp.NewSingleSegmentMessage(nil)

		// in seg
		segbag, err := air.NewRootBag(seg)
		if err != nil {
			t.Fatal(err)
		}

		// in scratch
		xc, err := air.NewRootCounter(scratch)
		if err != nil {
			t.Fatal(err)
		}
		xc.SetSize(9)
		if err := xc.SetWords("hello"); err != nil {
			t.Fatal(err)
		}

		// copy from scratch to seg
		if err = segbag.SetCounter(xc); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "copy struct with text between messages",
			msg:  msg,
			typ:  "Bag",
			text: "(counter = (size = 9, words = \"hello\"))\n",
			data: []byte{
				0, 0, 0, 0, 7, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				0, 0, 0, 0, 1, 0, 3, 0,
				9, 0, 0, 0, 0, 0, 0, 0,
				9, 0, 0, 0, 50, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				'h', 'e', 'l', 'l', 'o', 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		_, scratch := capnp.NewSingleSegmentMessage(nil)

		// in seg
		segbag, err := air.NewRootBag(seg)
		if err != nil {
			t.Fatal(err)
		}

		// in scratch
		xc, err := air.NewRootCounter(scratch)
		if err != nil {
			t.Fatal(err)
		}
		xc.SetSize(9)
		wl, err := xc.NewWordlist(2)
		if err != nil {
			t.Fatal(err)
		}
		if err := wl.Set(0, "hello"); err != nil {
			t.Fatal(err)
		}
		if err := wl.Set(1, "bye"); err != nil {
			t.Fatal(err)
		}

		// copy from scratch to seg
		if err = segbag.SetCounter(xc); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "copy struct with list of text between messages",
			msg:  msg,
			typ:  "Bag",
			text: "(counter = (size = 9, wordlist = [\"hello\", \"bye\"]))\n",
			data: []byte{
				0, 0, 0, 0, 10, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				0, 0, 0, 0, 1, 0, 3, 0,
				9, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				5, 0, 0, 0, 22, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				5, 0, 0, 0, 50, 0, 0, 0,
				5, 0, 0, 0, 34, 0, 0, 0,
				'h', 'e', 'l', 'l', 'o', 0, 0, 0,
				'b', 'y', 'e', 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		_, scratch := capnp.NewSingleSegmentMessage(nil)

		// in seg
		segbag, err := air.NewRootBag(seg)
		if err != nil {
			t.Fatal(err)
		}

		// in scratch
		xc, err := air.NewRootCounter(scratch)
		if err != nil {
			t.Fatal(err)
		}
		xc.SetSize(9)
		if err := xc.SetWords("abc"); err != nil {
			t.Fatal(err)
		}
		wl, err := xc.NewWordlist(2)
		if err != nil {
			t.Fatal(err)
		}
		if err := wl.Set(0, "hello"); err != nil {
			t.Fatal(err)
		}
		if err := wl.Set(1, "byenow"); err != nil {
			t.Fatal(err)
		}

		// copy from scratch to seg
		if err = segbag.SetCounter(xc); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "copy struct with data, text, and list of text between messages",
			msg:  msg,
			typ:  "Bag",
			text: "(counter = (size = 9, words = \"abc\", wordlist = [\"hello\", \"byenow\"]))\n",
			data: []byte{
				0, 0, 0, 0, 11, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				0, 0, 0, 0, 1, 0, 3, 0,
				9, 0, 0, 0, 0, 0, 0, 0,
				9, 0, 0, 0, 34, 0, 0, 0,
				9, 0, 0, 0, 22, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				97, 98, 99, 0, 0, 0, 0, 0,
				5, 0, 0, 0, 50, 0, 0, 0,
				5, 0, 0, 0, 58, 0, 0, 0,
				'h', 'e', 'l', 'l', 'o', 0, 0, 0,
				'b', 'y', 'e', 'n', 'o', 'w', 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		_, scratch := capnp.NewSingleSegmentMessage(nil)

		// in seg
		segbag, err := air.NewRootBag(seg)
		if err != nil {
			t.Fatal(err)
		}

		// in scratch
		xc, err := air.NewRootCounter(scratch)
		if err != nil {
			t.Fatal(err)
		}
		bl, err := xc.NewBitlist(3)
		if err != nil {
			t.Fatal(err)
		}
		bl.Set(0, true)
		bl.Set(1, false)
		bl.Set(2, true)

		// copy from scratch to seg
		if err = segbag.SetCounter(xc); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "copy struct with bit list between messages",
			msg:  msg,
			typ:  "Bag",
			text: "(counter = (size = 0, bitlist = [true, false, true]))\n",
			data: []byte{
				0, 0, 0, 0, 7, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				0, 0, 0, 0, 1, 0, 3, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0,
				1, 0, 0, 0, 25, 0, 0, 0,
				5, 0, 0, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		holder, err := air.NewRootHoldsVerEmptyList(seg)
		if err != nil {
			t.Fatal(err)
		}
		elist, err := air.NewVerEmpty_List(seg, 2)
		if err != nil {
			t.Fatal(err)
		}
		if err := holder.SetMylist(elist); err != nil {
			t.Fatal(err)
		}

		tests = append(tests, marshalTest{
			name: "V0 list of empty",
			msg:  msg,
			typ:  "HoldsVerEmptyList",
			text: "(mylist = [(), ()])\n",
			data: []byte{
				0, 0, 0, 0, 3, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 7, 0, 0, 0,
				8, 0, 0, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		holder, err := air.NewRootNester1Capn(seg)
		if err != nil {
			t.Fatal(err)
		}
		initNester(t, holder, "furiosa", "max")

		tests = append(tests, marshalTest{
			name: "list inside a struct",
			msg:  msg,
			typ:  "Nester1Capn",
			text: "(strs = [\"furiosa\", \"max\"])\n",
			data: []byte{
				0, 0, 0, 0, 6, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 22, 0, 0, 0,
				5, 0, 0, 0, 66, 0, 0, 0,
				5, 0, 0, 0, 34, 0, 0, 0,
				102, 117, 114, 105, 111, 115, 97, 0,
				109, 97, 120, 0, 0, 0, 0, 0,
			},
		})
	}

	{
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		holder, err := air.NewRootRWTestCapn(seg)
		if err != nil {
			t.Fatal(err)
		}
		mat, err := capnp.NewPointerList(seg, 2)
		if err != nil {
			t.Fatal(err)
		}
		if err := holder.SetNestMatrix(mat); err != nil {
			t.Fatal(err)
		}

		row0, err := air.NewNester1Capn_List(seg, 2)
		if err != nil {
			t.Fatal(err)
		}
		if err := mat.Set(0, row0.ToPtr()); err != nil {
			t.Fatal(err)
		}
		initNester(t, row0.At(0), "z", "w")
		initNester(t, row0.At(1), "q", "r")

		row1, err := air.NewNester1Capn_List(seg, 2)
		if err != nil {
			t.Fatal(err)
		}
		if err := mat.Set(1, row1.ToPtr()); err != nil {
			t.Fatal(err)
		}
		initNester(t, row1.At(0), "zebra", "wally")
		initNester(t, row1.At(1), "qubert", "rocks")

		tests = append(tests, marshalTest{
			name: "doubly-nested list of struct that has a list field",
			msg:  msg,
			typ:  "RWTestCapn",
			text: `(nestMatrix = [[(strs = ["z", "w"]), (strs = ["q", "r"])], [(strs = ["zebra", "wally"]), (strs = ["qubert", "rocks"])]])` + "\n",
			data: []byte{
				0, 0, 0, 0, 26, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 1, 0,
				1, 0, 0, 0, 22, 0, 0, 0,
				5, 0, 0, 0, 23, 0, 0, 0,
				45, 0, 0, 0, 23, 0, 0, 0,
				8, 0, 0, 0, 0, 0, 1, 0,
				5, 0, 0, 0, 22, 0, 0, 0,
				17, 0, 0, 0, 22, 0, 0, 0,
				5, 0, 0, 0, 18, 0, 0, 0,
				5, 0, 0, 0, 18, 0, 0, 0,
				122, 0, 0, 0, 0, 0, 0, 0,
				119, 0, 0, 0, 0, 0, 0, 0,
				5, 0, 0, 0, 18, 0, 0, 0,
				5, 0, 0, 0, 18, 0, 0, 0,
				113, 0, 0, 0, 0, 0, 0, 0,
				114, 0, 0, 0, 0, 0, 0, 0,
				8, 0, 0, 0, 0, 0, 1, 0,
				5, 0, 0, 0, 22, 0, 0, 0,
				17, 0, 0, 0, 22, 0, 0, 0,
				5, 0, 0, 0, 50, 0, 0, 0,
				5, 0, 0, 0, 50, 0, 0, 0,
				122, 101, 98, 114, 97, 0, 0, 0,
				119, 97, 108, 108, 121, 0, 0, 0,
				5, 0, 0, 0, 58, 0, 0, 0,
				5, 0, 0, 0, 50, 0, 0, 0,
				113, 117, 98, 101, 114, 116, 0, 0,
				114, 111, 99, 107, 115, 0, 0, 0,
			},
		})
	}

	return tests
}

func TestMarshalShouldMatchData(t *testing.T) {
	t.Parallel()
	for _, test := range makeMarshalTests(t) {
		if test.data == nil {
			// TODO(light): backfill all data
			continue
		}
		data, err := test.msg.Marshal()
		if err != nil {
			t.Errorf("%s: marshal error: %v", test.name, err)
			continue
		}
		want, err := encodeTestMessage(t, test.typ, test.text, test.data)
		if err != nil {
			t.Errorf("%s: %v", test.name, err)
			continue
		}
		if !bytes.Equal(data, want) {
			t.Errorf("%s: Marshal returned:\n%s\nwant:\n%s", test.name, hex.Dump(data), hex.Dump(want))
		}
	}
}

func TestMarshalShouldMatchTextWhenDecoded(t *testing.T) {
	t.Parallel()
	tool, err := capnptool.Find()
	if err != nil {
		t.Skip("capnp tool not found:", err)
	}
	for _, test := range makeMarshalTests(t) {
		data, err := test.msg.Marshal()
		if err != nil {
			t.Errorf("%s: marshal error: %v", test.name, err)
			continue
		}
		text, err := tool.Decode(capnptool.Type{SchemaPath: schemaPath, Name: test.typ}, bytes.NewReader(data))
		if err != nil {
			t.Errorf("%s: capnp decode: %v", test.name, err)
			continue
		}
		if text != test.text {
			t.Errorf("%s: decoded to:\n%q; want:\n%q", test.name, text, test.text)
		}
	}
}

func TestMarshalPackedShouldMatchTextWhenDecoded(t *testing.T) {
	t.Parallel()
	tool, err := capnptool.Find()
	if err != nil {
		t.Skip("capnp tool not found:", err)
	}
	for _, test := range makeMarshalTests(t) {
		data, err := test.msg.MarshalPacked()
		if err != nil {
			t.Errorf("%s: marshal error: %v", test.name, err)
			continue
		}
		text, err := tool.DecodePacked(capnptool.Type{SchemaPath: schemaPath, Name: test.typ}, bytes.NewReader(data))
		if err != nil {
			t.Errorf("%s: capnp decode: %v", test.name, err)
			continue
		}
		if text != test.text {
			t.Errorf("%s: decoded to:\n%q; want:\n%q", test.name, text, test.text)
		}
	}
}

type bitListTest struct {
	list []bool
	text string
}

var bitListTests = []bitListTest{
	{
		[]bool{true, false, true},
		"(boolvec = [true, false, true])\n",
	},
	{
		[]bool{false},
		"(boolvec = [false])\n",
	},
	{
		[]bool{true},
		"(boolvec = [true])\n",
	},
	{
		[]bool{false, true},
		"(boolvec = [false, true])\n",
	},
	{
		[]bool{true, true},
		"(boolvec = [true, true])\n",
	},
	{
		[]bool{false, false, true},
		"(boolvec = [false, false, true])\n",
	},
	{
		[]bool{true, false, true, false, true},
		"(boolvec = [true, false, true, false, true])\n",
	},
	{
		[]bool{
			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,
		},
		"(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])\n",
	},
}

func (blt bitListTest) makeMessage() (*capnp.Message, error) {
	msg, seg := capnp.NewSingleSegmentMessage(nil)
	z, err := air.NewRootZ(seg)
	if err != nil {
		return nil, err
	}
	list, err := capnp.NewBitList(seg, int32(len(blt.list)))
	if err != nil {
		return nil, err
	}
	for i := range blt.list {
		list.Set(i, blt.list[i])
	}
	if err := z.SetBoolvec(list); err != nil {
		return nil, err
	}
	return msg, nil
}

func TestBitList(t *testing.T) {
	t.Parallel()
	for _, test := range bitListTests {
		msg, err := test.makeMessage()
		if err != nil {
			t.Errorf("%v: make message: %v", test.list, err)
			continue
		}

		z, err := air.ReadRootZ(msg)
		if err != nil {
			t.Errorf("%v: read root Z: %v", test.list, err)
			continue
		}
		if w := z.Which(); w != air.Z_Which_boolvec {
			t.Errorf("%v: root.Which() = %v; want boolvec", test.list, w)
			continue
		}
		list, err := z.Boolvec()
		if err != nil {
			t.Errorf("%v: read Z.boolvec: %v", test.list, err)
			continue
		}
		if n := list.Len(); n != len(test.list) {
			t.Errorf("%v: len(Z.boolvec) = %d; want %d", test.list, n, len(test.list))
			continue
		}
		for i := range test.list {
			if li := list.At(i); li != test.list[i] {
				t.Errorf("%v: Z.boolvec[%d] = %t; want %t", test.list, i, li, test.list[i])
			}
		}
	}
}

func TestBitList_Decode(t *testing.T) {
	t.Parallel()
	tool, err := capnptool.Find()
	if err != nil {
		t.Skip("capnp tool not found:", err)
	}
	for _, test := range bitListTests {
		msg, err := test.makeMessage()
		if err != nil {
			t.Errorf("%v: make message: %v", test.list, err)
			continue
		}
		out, err := msg.Marshal()
		if err != nil {
			t.Errorf("%v: marshal: %v", test.list, err)
			continue
		}
		text, err := tool.Decode(capnptool.Type{SchemaPath: schemaPath, Name: "Z"}, bytes.NewReader(out))
		if err != nil {
			t.Errorf("%v: capnp decode: %v", test.list, err)
			continue
		}
		if text != test.text {
			t.Errorf("%v: capnp decode = %q; want %q", test.list, text, test.text)
		}
	}
}

func TestZDataAccessors(t *testing.T) {
	t.Parallel()
	data := mustEncodeTestMessage(t, "Z", `(zdata = (data = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13"))`, []byte{
		0, 0, 0, 0, 9, 0, 0, 0,
		0, 0, 0, 0, 3, 0, 1, 0,
		28, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		1, 0, 0, 0, 162, 0, 0, 0,
		0, 1, 2, 3, 4, 5, 6, 7,
		8, 9, 10, 11, 12, 13, 14, 15,
		16, 17, 18, 19, 0, 0, 0, 0,
	})

	msg, err := capnp.Unmarshal(data)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}
	z, err := air.ReadRootZ(msg)
	if err != nil {
		t.Fatal("ReadRootZ:", err)
	}

	if z.Which() != air.Z_Which_zdata {
		t.Fatalf("z.Which() = %v; want zdata", z.Which())
	}
	zdata, err := z.Zdata()
	if err != nil {
		t.Fatal("z.Zdata():", err)
	}
	d, err := zdata.Data()
	if err != nil {
		t.Fatal("z.Zdata().Data():", err)
	}
	if len(d) != 20 {
		t.Errorf("z.Zdata().Data() len = %d; want 20", len(d))
	}
	for i := range d {
		if d[i] != byte(i) {
			t.Errorf("z.Zdata().Data()[%d] = %d; want %d", i, d[i], i)
		}
	}
}

func TestReadListInStruct(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(t, "Nester1Capn", "(strs = [\"furiosa\", \"max\"])", []byte{
		0, 0, 0, 0, 6, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		1, 0, 0, 0, 22, 0, 0, 0,
		5, 0, 0, 0, 66, 0, 0, 0,
		5, 0, 0, 0, 34, 0, 0, 0,
		102, 117, 114, 105, 111, 115, 97, 0,
		109, 97, 120, 0, 0, 0, 0, 0,
	})
	msg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}
	holder, err := air.ReadRootNester1Capn(msg)
	if err != nil {
		t.Fatal("ReadRootNester1Capn:", err)
	}
	strs, err := holder.Strs()
	if err != nil {
		t.Fatal("Nester1Capn.strs:", err)
	}
	if strs.Len() == 2 {
		check := func(i int, want string) {
			s, err := strs.At(i)
			if err != nil {
				t.Errorf("Nester1Capn.strs[%d] error: %v", i, err)
			}
			if s != want {
				t.Errorf("Nester1Capn.strs[%d] = %q; want %q", i, s, want)
			}
		}
		check(0, "furiosa")
		check(1, "max")
	} else {
		t.Errorf("len(Nester1Capn.strs) = %d; want 2", strs.Len())
	}
}

func TestReadNestedListOfStructWithList(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(
		t,
		"RWTestCapn",
		`(nestMatrix = [[(strs = ["z", "w"]), (strs = ["q", "r"])], [(strs = ["zebra", "wally"]), (strs = ["qubert", "rocks"])]])`,
		[]byte{
			0, 0, 0, 0, 26, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 1, 0,
			1, 0, 0, 0, 22, 0, 0, 0,
			5, 0, 0, 0, 23, 0, 0, 0,
			45, 0, 0, 0, 23, 0, 0, 0,
			8, 0, 0, 0, 0, 0, 1, 0,
			5, 0, 0, 0, 22, 0, 0, 0,
			17, 0, 0, 0, 22, 0, 0, 0,
			5, 0, 0, 0, 18, 0, 0, 0,
			5, 0, 0, 0, 18, 0, 0, 0,
			122, 0, 0, 0, 0, 0, 0, 0,
			119, 0, 0, 0, 0, 0, 0, 0,
			5, 0, 0, 0, 18, 0, 0, 0,
			5, 0, 0, 0, 18, 0, 0, 0,
			113, 0, 0, 0, 0, 0, 0, 0,
			114, 0, 0, 0, 0, 0, 0, 0,
			8, 0, 0, 0, 0, 0, 1, 0,
			5, 0, 0, 0, 22, 0, 0, 0,
			17, 0, 0, 0, 22, 0, 0, 0,
			5, 0, 0, 0, 50, 0, 0, 0,
			5, 0, 0, 0, 50, 0, 0, 0,
			122, 101, 98, 114, 97, 0, 0, 0,
			119, 97, 108, 108, 121, 0, 0, 0,
			5, 0, 0, 0, 58, 0, 0, 0,
			5, 0, 0, 0, 50, 0, 0, 0,
			113, 117, 98, 101, 114, 116, 0, 0,
			114, 111, 99, 107, 115, 0, 0, 0,
		})
	msg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}
	holder, err := air.ReadRootRWTestCapn(msg)
	if err != nil {
		t.Fatal("ReadRootRWTestCapn:", err)
	}
	mat, err := holder.NestMatrix()
	if err != nil {
		t.Fatal("RWTestCapn:", err)
	}
	check := func(i, j int, want ...string) {
		if i >= mat.Len() {
			t.Errorf("len(RWTestCapn.nestMatrix) = %d; tried to index %d", mat.Len(), i)
			return
		}
		row, err := mat.At(i)
		if err != nil {
			t.Errorf("RWTestCapn.nestMatrix[%d]: %v", i, err)
			return
		}
		rowList := capnp.StructList[air.Nester1Capn](row.List())
		if j >= rowList.Len() {
			t.Errorf("len(RWTestCapn.nestMatrix[%d]) = %d; tried to index %d", i, rowList.Len(), j)
			return
		}
		strs, err := rowList.At(j).Strs()
		if err != nil {
			t.Errorf("RWTestCapn.nestMatrix[%d][%d].strs: %v", i, j, err)
			return
		}
		if strs.Len() != len(want) {
			t.Errorf("len(RWTestCapn.nestMatrix[%d][%d].strs) = %d; want %d", i, j, strs.Len(), len(want))
			return
		}
		for k := 0; k < strs.Len(); k++ {
			s, err := strs.At(k)
			if err != nil {
				t.Errorf("RWTestCapn.nestMatrix[%d][%d].strs[%d]: %v", i, j, k, err)
				continue
			}
			if s != want[k] {
				t.Errorf("RWTestCapn.nestMatrix[%d][%d].strs[%d] = %q; want %q", i, j, k, s, want[k])
			}
		}
	}
	check(0, 0, "z", "w")
	check(0, 1, "q", "r")
	check(1, 0, "zebra", "wally")
	check(1, 1, "qubert", "rocks")
}

func TestDataVersioningAvoidsUnnecessaryTruncation(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(t, "VerTwoDataTwoPtr", "(val = 9, duo = 8, ptr1 = (val = 77), ptr2 = (val = 55))", []byte{
		0, 0, 0, 0, 7, 0, 0, 0,
		0, 0, 0, 0, 2, 0, 2, 0,
		9, 0, 0, 0, 0, 0, 0, 0,
		8, 0, 0, 0, 0, 0, 0, 0,
		4, 0, 0, 0, 1, 0, 0, 0,
		4, 0, 0, 0, 1, 0, 0, 0,
		77, 0, 0, 0, 0, 0, 0, 0,
		55, 0, 0, 0, 0, 0, 0, 0,
	})
	want := mustEncodeTestMessage(t, "Wrap2x2", "(mightNotBeReallyEmpty = (val = 9, duo = 8, ptr1 = (val = 77), ptr2 = (val = 55)))", []byte{
		0, 0, 0, 0, 8, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		0, 0, 0, 0, 2, 0, 2, 0,
		9, 0, 0, 0, 0, 0, 0, 0,
		8, 0, 0, 0, 0, 0, 0, 0,
		4, 0, 0, 0, 1, 0, 0, 0,
		4, 0, 0, 0, 1, 0, 0, 0,
		77, 0, 0, 0, 0, 0, 0, 0,
		55, 0, 0, 0, 0, 0, 0, 0,
	})

	msg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}

	// Read in the message as if it's an old client (less fields in schema).
	oldRoot, err := air.ReadRootVerEmpty(msg)
	if err != nil {
		t.Fatal("ReadRootVerEmpty:", err)
	}

	// Store the larger message into another segment.
	freshMsg, freshSeg := capnp.NewSingleSegmentMessage(nil)
	wrapEmpty, err := air.NewRootWrapEmpty(freshSeg)
	if err != nil {
		t.Fatal("NewRootWrapEmpty:", err)
	}
	if err := wrapEmpty.SetMightNotBeReallyEmpty(oldRoot); err != nil {
		t.Fatal("SetMightNotBeReallyEmpty:", err)
	}

	// Verify that it matches the expected serialization.
	out, err := freshMsg.Marshal()
	if err != nil {
		t.Fatal("Marshal:", err)
	}
	if !bytes.Equal(out, want) {
		t.Errorf("After copy, data is:\n%s\nwant:\n%s", hex.Dump(out), hex.Dump(want))
	}
}

func TestZserverAccessors(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(t, "Zserver", `(waitingjobs = [(cmd = "abc"), (cmd = "xyz")])`, []byte{
		0, 0, 0, 0, 9, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		1, 0, 0, 0, 39, 0, 0, 0,
		8, 0, 0, 0, 0, 0, 2, 0,
		13, 0, 0, 0, 34, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		9, 0, 0, 0, 34, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		97, 98, 99, 0, 0, 0, 0, 0,
		120, 121, 122, 0, 0, 0, 0, 0,
	})

	msg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}

	zserver, err := air.ReadRootZserver(msg)
	if err != nil {
		t.Fatal("ReadRootZserver:", err)
	}
	joblist, err := zserver.Waitingjobs()
	if err != nil {
		t.Fatal("Zserver.waitingjobs:", err)
	}
	if joblist.Len() != 2 {
		t.Fatalf("len(Zserver.waitingjobs) = %d; want 2", joblist.Len())
	}
	checkCmd := func(i int, want string) {
		cmd, err := joblist.At(i).Cmd()
		if err != nil {
			t.Errorf("Zserver.waitingjobs[%d].cmd error: %v", i, err)
			return
		}
		if cmd != want {
			t.Errorf("Zserver.waitingjobs[%d].cmd = %q; want %q", i, cmd, want)
		}
	}
	checkCmd(0, "abc")
	checkCmd(1, "xyz")
}

func TestEnumFromString(t *testing.T) {
	t.Parallel()
	tests := []struct {
		s  string
		ap air.Airport
	}{
		{"jfk", air.Airport_jfk},
		{"notEverMatching", 0},
	}
	for _, test := range tests {
		if ap := air.AirportFromString(test.s); ap != test.ap {
			t.Errorf("air.AirportFromString(%q) = %v; want %v", test.s, ap, test.ap)
		}
	}
}

func TestDefaultStructField(t *testing.T) {
	t.Parallel()
	_, seg := capnp.NewSingleSegmentMessage(nil)
	root, err := air.NewRootStackingRoot(seg)
	if err != nil {
		t.Fatal(err)
	}

	a, err := root.AWithDefault()

	if err != nil {
		t.Error("StackingRoot.aWithDefault error:", err)
	}
	if a.Num() != 42 {
		t.Errorf("StackingRoot.aWithDefault = %d; want 42", a.Num())
	}
}

func TestDataTextCopyOptimization(t *testing.T) {
	t.Parallel()
	_, seg1 := capnp.NewSingleSegmentMessage(nil)
	root, err := air.NewRootNester1Capn(seg1)
	if err != nil {
		t.Fatal(err)
	}
	_, seg2 := capnp.NewSingleSegmentMessage(nil)
	strsl, err := capnp.NewTextList(seg2, 256)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < strsl.Len(); i++ {
		strsl.Set(i, "testess")
	}

	err = root.SetStrs(strsl)

	if err != nil {
		t.Fatal(err)
	}
	strsl, err = root.Strs()
	if err != nil {
		t.Fatal(err)
	}
	if strsl.Len() != 256 {
		t.Errorf("strsl.Len() = %d; want 256", strsl.Len())
	}
	for i := 0; i < strsl.Len(); i++ {
		s, err := strsl.At(i)
		if err != nil {
			t.Errorf("strsl.At(%d) error: %v", i, err)
			continue
		}
		if s != "testess" {
			t.Errorf("strsl.At(%d) = %q; want \"testess\"", i, s)
		}
	}
}

// highlight how much faster text movement between segments
// is when special casing Text and Data
//
// run this test with capnp.go:1334-1341 commented in/out to compare.
func BenchmarkTextMovementBetweenSegments(b *testing.B) {
	buf := make([]byte, 1<<21)
	buf2 := make([]byte, 1<<21)

	text := make([]byte, 1<<20)
	for i := range text {
		text[i] = byte(65 + rand.Int()%26)
	}

	astr := make([]string, 1000)
	for i := range astr {
		astr[i] = string(text[i*1000 : (i+1)*1000])
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, seg := capnp.NewSingleSegmentMessage(buf[:0])
		_, scratch := capnp.NewSingleSegmentMessage(buf2[:0])

		ht, _ := air.NewRootHoldsText(seg)
		// Purposefully created in another segment.
		tl, _ := capnp.NewTextList(scratch, 1000)
		for j := 0; j < 1000; j++ {
			tl.Set(j, astr[j])
		}

		ht.SetLst(tl)
	}
}

func TestV1DataVersioningBiggerToEmpty(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(t, "HoldsVerTwoDataList", "(mylist = [(val = 27, duo = 26), (val = 42, duo = 41)])", []byte{
		0, 0, 0, 0, 7, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		1, 0, 0, 0, 39, 0, 0, 0,
		8, 0, 0, 0, 2, 0, 0, 0,
		27, 0, 0, 0, 0, 0, 0, 0,
		26, 0, 0, 0, 0, 0, 0, 0,
		42, 0, 0, 0, 0, 0, 0, 0,
		41, 0, 0, 0, 0, 0, 0, 0,
	})

	remsg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}

	// 0 data
	func() {
		reHolder0, err := air.ReadRootHoldsVerEmptyList(remsg)
		if err != nil {
			t.Error("ReadRootHoldsVerEmptyList:", err)
			return
		}
		list0, err := reHolder0.Mylist()
		if err != nil {
			t.Error("HoldsVerEmptyList.mylist:", err)
			return
		}
		if list0.Len() != 2 {
			t.Errorf("len(HoldsVerEmptyList.mylist) = %d; want 2", list0.Len())
		}
	}()

	// 1 datum
	func() {
		reHolder1, err := air.ReadRootHoldsVerOneDataList(remsg)
		if err != nil {
			t.Error("ReadRootHoldsVerOneDataList:", err)
			return
		}
		list1, err := reHolder1.Mylist()
		if err != nil {
			t.Error("HoldsVerOneDataList.mylist:", err)
			return
		}
		if list1.Len() == 2 {
			if v := list1.At(0).Val(); v != 27 {
				t.Errorf("HoldsVerOneDataList.mylist[0].val = %d; want 27", v)
			}
			if v := list1.At(1).Val(); v != 42 {
				t.Errorf("HoldsVerOneDataList.mylist[1].val = %d; want 42", v)
			}
		} else {
			t.Errorf("len(HoldsVerOneDataList.mylist) = %d; want 2", list1.Len())
		}
	}()

	// 2 data
	func() {
		reHolder2, err := air.ReadRootHoldsVerTwoDataList(remsg)
		if err != nil {
			t.Error("ReadRootHoldsVerTwoDataList:", err)
			return
		}
		list2, err := reHolder2.Mylist()
		if err != nil {
			t.Error("HoldsVerTwoDataList.mylist:", err)
			return
		}
		if list2.Len() == 2 {
			if v := list2.At(0).Val(); v != 27 {
				t.Errorf("HoldsVerTwoDataList.mylist[0].val = %d; want 27", v)
			}
			if v := list2.At(0).Duo(); v != 26 {
				t.Errorf("HoldsVerTwoDataList.mylist[0].duo = %d; want 26", v)
			}
			if v := list2.At(1).Val(); v != 42 {
				t.Errorf("HoldsVerTwoDataList.mylist[1].val = %d; want 42", v)
			}
			if v := list2.At(1).Duo(); v != 41 {
				t.Errorf("HoldsVerTwoDataList.mylist[1].duo = %d; want 41", v)
			}
		} else {
			t.Errorf("len(HoldsVerTwoDataList.mylist) = %d; want 2", list2.Len())
		}
	}()
}

func TestV1DataVersioningEmptyToBigger(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(t, "HoldsVerEmptyList", "(mylist = [(),()])", []byte{
		0, 0, 0, 0, 3, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		1, 0, 0, 0, 7, 0, 0, 0,
		8, 0, 0, 0, 0, 0, 0, 0,
	})

	remsg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}

	reHolder1, err := air.ReadRootHoldsVerOneDataList(remsg)
	if err != nil {
		t.Fatal("ReadRootHoldsVerOneDataList:", err)
	}
	list1, err := reHolder1.Mylist()
	if err != nil {
		t.Fatal("HoldsVerOneDataList.mylist:", err)
	}
	if list1.Len() == 2 {
		if v := list1.At(0).Val(); v != 0 {
			t.Errorf("HoldsVerOneDataList.mylist[0].val = %d; want 0", v)
		}
		if v := list1.At(1).Val(); v != 0 {
			t.Errorf("HoldsVerOneDataList.mylist[1].val = %d; want 0", v)
		}
	} else {
		t.Errorf("len(HoldsVerOneDataList.mylist) = %d; want 2", list1.Len())
	}

	reHolder2, err := air.ReadRootHoldsVerTwoDataList(remsg)
	if err != nil {
		t.Fatal("ReadRootHoldsVerOneDataList:", err)
	}
	list2, err := reHolder2.Mylist()
	if err != nil {
		t.Fatal("HoldsVerOneDataList.mylist:", err)
	}
	if list2.Len() == 2 {
		if v := list2.At(0).Val(); v != 0 {
			t.Errorf("HoldsVerTwoDataList.mylist[0].val = %d; want 0", v)
		}
		if v := list2.At(0).Duo(); v != 0 {
			t.Errorf("HoldsVerTwoDataList.mylist[0].duo = %d; want 0", v)
		}
		if v := list2.At(1).Val(); v != 0 {
			t.Errorf("HoldsVerTwoDataList.mylist[1].val = %d; want 0", v)
		}
		if v := list2.At(1).Duo(); v != 0 {
			t.Errorf("HoldsVerTwoDataList.mylist[1].duo = %d; want 0", v)
		}
	} else {
		t.Errorf("len(HoldsVerTwoDataList.mylist) = %d; want 2", list2.Len())
	}
}

func TestDataVersioningZeroPointersToMore(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(t, "HoldsVerEmptyList", "(mylist = [(),()])", []byte{
		0, 0, 0, 0, 3, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 1, 0,
		1, 0, 0, 0, 7, 0, 0, 0,
		8, 0, 0, 0, 0, 0, 0, 0,
	})

	remsg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}
	reHolder, err := air.ReadRootHoldsVerTwoTwoList(remsg)
	if err != nil {
		t.Fatal("ReadRootHoldsVerTwoTwoList:", err)
	}
	list22, err := reHolder.Mylist()
	if err != nil {
		t.Fatal("HoldsVerTwoTwoList.mylist:", err)
	}
	if list22.Len() != 2 {
		t.Errorf("len(HoldsVerTwoTwoList.mylist) = %d; want 2", list22.Len())
	}
	for i := 0; i < list22.Len(); i++ {
		ele := list22.At(i)
		if val := ele.Val(); val != 0 {
			t.Errorf("HoldsVerTwoTwoList.mylist[%d].val = %d; want 0", i, val)
		}
		if duo := ele.Duo(); duo != 0 {
			t.Errorf("HoldsVerTwoTwoList.mylist[%d].duo = %d; want 0", i, duo)
		}
		if ptr1, err := ele.Ptr1(); err != nil {
			t.Errorf("HoldsVerTwoTwoList.mylist[%d].ptr1: %v", i, err)
		} else if ptr1.IsValid() {
			t.Errorf("HoldsVerTwoTwoList.mylist[%d].ptr1 = %#v; want invalid (nil)", i, ptr1)
		}
		if ptr2, err := ele.Ptr2(); err != nil {
			t.Errorf("HoldsVerTwoTwoList.mylist[%d].ptr2: %v", i, err)
		} else if ptr2.IsValid() {
			t.Errorf("HoldsVerTwoTwoList.mylist[%d].ptr2 = %#v; want invalid (nil)", i, ptr2)
		}
	}
}

func TestDataVersioningZeroPointersToTwo(t *testing.T) {
	t.Parallel()
	in := mustEncodeTestMessage(
		t,
		"HoldsVerTwoTwoList",
		`(mylist = [
			(val = 27, duo = 26, ptr1 = (val = 25), ptr2 = (val = 23)),
			(val = 42, duo = 41, ptr1 = (val = 40), ptr2 = (val = 38))])`,
		[]byte{
			0, 0, 0, 0, 15, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 1, 0,
			1, 0, 0, 0, 71, 0, 0, 0,
			8, 0, 0, 0, 2, 0, 2, 0,
			27, 0, 0, 0, 0, 0, 0, 0,
			26, 0, 0, 0, 0, 0, 0, 0,
			20, 0, 0, 0, 1, 0, 0, 0,
			20, 0, 0, 0, 1, 0, 0, 0,
			42, 0, 0, 0, 0, 0, 0, 0,
			41, 0, 0, 0, 0, 0, 0, 0,
			12, 0, 0, 0, 1, 0, 0, 0,
			12, 0, 0, 0, 1, 0, 0, 0,
			25, 0, 0, 0, 0, 0, 0, 0,
			23, 0, 0, 0, 0, 0, 0, 0,
			40, 0, 0, 0, 0, 0, 0, 0,
			38, 0, 0, 0, 0, 0, 0, 0,
		})

	remsg, err := capnp.Unmarshal(in)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}

	// 0 pointers
	func() {
		reHolder, err := air.ReadRootHoldsVerEmptyList(remsg)
		if err != nil {
			t.Error("ReadRootHoldsVerEmptyList:", err)
			return
		}
		list, err := reHolder.Mylist()
		if err != nil {
			t.Error("HoldsVerEmptyList.mylist:", err)
			return
		}
		if list.Len() != 2 {
			t.Errorf("len(HoldsVerEmptyList.mylist) = %d; want 2", list.Len())
		}
	}()

	// 1 pointer
	func() {
		holder, err := air.ReadRootHoldsVerOnePtrList(remsg)
		if err != nil {
			t.Error("ReadRootHoldsVerOnePtrList:", err)
			return
		}
		list, err := holder.Mylist()
		if err != nil {
			t.Error("HoldsVerOnePtrList.mylist:", err)
			return
		}
		if list.Len() != 2 {
			t.Errorf("len(HoldsVerOnePtrList.mylist) = %d; want 2", list.Len())
			return
		}
		check := func(i int, val int16) {
			p, err := list.At(i).Ptr()
			if err != nil {
				t.Errorf("HoldsVerOnePtrList.mylist[%d].ptr: %v", i, err)
				return
			}
			if p.Val() != val {
				t.Errorf("HoldsVerOnePtrList.mylist[%d].ptr.val = %d; want %d", i, p.Val(), val)
			}
		}
		check(0, 25)
		check(1, 40)
	}()

	// 2 pointers
	func() {
		holder, err := air.ReadRootHoldsVerTwoTwoPlus(remsg)
		if err != nil {
			t.Error("ReadRootHoldsVerTwoTwoPlus:", err)
			return
		}
		list, err := holder.Mylist()
		if err != nil {
			t.Error("HoldsVerTwoTwoPlus.mylist:", err)
			return
		}
		if list.Len() != 2 {
			t.Errorf("len(HoldsVerTwoTwoPlus.mylist) = %d; want 2", list.Len())
			return
		}
		check := func(i int, val1, val2 int16) {
			if p, err := list.At(i).Ptr1(); err != nil {
				t.Errorf("HoldsVerTwoTwoPlus.mylist[%d].ptr1: %v", i, err)
			} else if p.Val() != val1 {
				t.Errorf("HoldsVerTwoTwoPlus.mylist[%d].ptr1.val = %d; want %d", i, p.Val(), val1)
			}
			if p, err := list.At(i).Ptr2(); err != nil {
				t.Errorf("HoldsVerTwoTwoPlus.mylist[%d].ptr2: %v", i, err)
			} else if p.Val() != val2 {
				t.Errorf("HoldsVerTwoTwoPlus.mylist[%d].ptr2.val = %d; want %d", i, p.Val(), val2)
			}
		}
		check(0, 25, 23)
		check(1, 40, 38)
	}()
}

func TestVoidUnionSetters(t *testing.T) {
	t.Parallel()
	want := mustEncodeTestMessage(t, "VoidUnion", "(b = void)", []byte{
		0, 0, 0, 0, 2, 0, 0, 0,
		0, 0, 0, 0, 1, 0, 0, 0,
		1, 0, 0, 0, 0, 0, 0, 0,
	})

	msg, seg := capnp.NewSingleSegmentMessage(nil)
	voidUnion, err := air.NewRootVoidUnion(seg)
	if err != nil {
		t.Fatal(err)
	}
	voidUnion.SetB()

	act, err := msg.Marshal()
	if err != nil {
		t.Fatal("msg.Marshal():", err)
	}
	if !bytes.Equal(act, want) {
		t.Errorf("msg.Marshal() =\n%s\n; want:\n%s", hex.Dump(act), hex.Dump(want))
	}
}

func TestReadDefaults(t *testing.T) {
	t.Parallel()
	data := mustEncodeTestMessage(t, "Defaults", "()", []byte{
		0, 0, 0, 0, 5, 0, 0, 0,
		0, 0, 0, 0, 2, 0, 2, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
	})

	msg, err := capnp.Unmarshal(data)
	if err != nil {
		t.Fatal("Unmarshal:", err)
	}
	d, err := air.ReadRootDefaults(msg)
	if err != nil {
		t.Fatal("ReadRootDefaults:", err)
	}

	if s, err := d.Text(); err != nil {
		t.Errorf("d.Text() error: %v", err)
	} else if s != "foo" {
		t.Errorf("d.Text() = %q; want \"foo\"", s)
	}
	if b, err := d.TextBytes(); err != nil {
		t.Errorf("d.TextBytes() error: %v", err)
	} else if !bytes.Equal(b, []byte("foo")) {
		t.Errorf("d.TextBytes() = %q; want \"foo\"", b)
	}
	if b, err := d.Data(); err != nil {
		t.Errorf("d.Data() error: %v", err)
	} else if !bytes.Equal(b, []byte("bar")) {
		t.Errorf("d.Data() = %q; want \"bar\"", b)
	}
	if f := d.Float(); f != 3.14 {
		t.Errorf("d.Float() = %g; want 3.14", f)
	}
	if i := d.Int(); i != -123 {
		t.Errorf("d.Int() = %d; want -123", i)
	}
	if i := d.Uint(); i != 42 {
		t.Errorf("d.Uint() = %d; want 42", i)
	}
}

type A struct {
	Name     string
	BirthDay time.Time
	Phone    string
	Siblings int
	Spouse   bool
	Money    float64
}

func generateA(r *rand.Rand) *A {
	return &A{
		Name:     randString(r, 16),
		BirthDay: time.Unix(r.Int63(), 0),
		Phone:    randString(r, 10),
		Siblings: r.Intn(5),
		Spouse:   r.Intn(2) == 1,
		Money:    r.Float64(),
	}
}

func unmarshalA(aa air.BenchmarkA) A {
	name, _ := aa.NameBytes()
	phone, _ := aa.PhoneBytes()
	return A{
		Name:     unsafeBytesToString(name),
		BirthDay: time.Unix(aa.BirthDay(), 0),
		Phone:    unsafeBytesToString(phone),
		Siblings: int(aa.Siblings()),
		Spouse:   aa.Spouse(),
		Money:    aa.Money(),
	}
}

func (a *A) fill(aa air.BenchmarkA) {
	aa.SetName(a.Name)
	aa.SetBirthDay(a.BirthDay.Unix())
	aa.SetPhone(a.Phone)
	aa.SetSiblings(int32(a.Siblings))
	aa.SetSpouse(a.Spouse)
	aa.SetMoney(a.Money)
}

func randString(r *rand.Rand, n int) string {
	b := make([]byte, (n+1)/2)
	// Go 1.6 adds a Rand.Read method, but since we want to be compatible with Go 1.4...
	for i := range b {
		b[i] = byte(r.Intn(255))
	}
	return hex.EncodeToString(b)[:n]
}

func unsafeBytesToString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// BenchmarkMarshal benchmarks marshalling a message using the simplest API
// available to go-capnp users.
func BenchmarkMarshal(b *testing.B) {
	r := rand.New(rand.NewSource(12345))
	data := make([]*A, 1000)
	for i := range data {
		data[i] = generateA(r)
	}
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		a := data[r.Intn(len(data))]
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		root, err := air.NewRootBenchmarkA(seg)
		if err != nil {
			b.Fatal(err)
		}
		a.fill(root)
		_, err = msg.Marshal()
		if err != nil {
			b.Fatal(err)
		}
	}
}

// BenchmarkMarshal_ReuseMsg benchmarks marshalling while reusing the message
// object.
func BenchmarkMarshal_ReuseMsg(b *testing.B) {
	r := rand.New(rand.NewSource(12345))
	data := make([]*A, 1000)
	for i := range data {
		data[i] = generateA(r)
	}
	msg, _ := capnp.NewSingleSegmentMessage(nil)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		a := data[r.Intn(len(data))]
		seg, err := msg.Reset(capnp.SingleSegment(nil))
		if err != nil {
			b.Fatal(err)
		}
		root, err := air.NewRootBenchmarkA(seg)
		if err != nil {
			b.Fatal(err)
		}
		a.fill(root)
		_, err = msg.Marshal()
		if err != nil {
			b.Fatal(err)
		}
	}
}

// BenchmarkUnmarshal benchmarks unmarshalling using the simplest API possible
// available to go-capnp users.
func BenchmarkUnmarshal(b *testing.B) {
	r := rand.New(rand.NewSource(12345))
	type testCase struct {
		a    A
		data []byte
	}

	data := make([]testCase, 1000)
	for i := range data {
		a := generateA(r)
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		root, _ := air.NewRootBenchmarkA(seg)
		a.fill(root)
		buf, err := msg.Marshal()
		if err != nil {
			b.Fatal(err)
		}
		data[i].data, data[i].a = buf, *a
	}

	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		testIdx := r.Intn(len(data))
		msg, err := capnp.Unmarshal(data[testIdx].data)
		if err != nil {
			b.Fatal(err)
		}
		a, err := air.ReadRootBenchmarkA(msg)
		if err != nil {
			b.Fatal(err)
		}
		gotA := unmarshalA(a)
		if gotA != data[testIdx].a {
			b.Fatal("unexpected unmarshalled data")
		}
	}
}

// BenchmarkUnmarshal_Reuse benchmarks unmarshalling by reusing the msg object
// and directly using the test data buffer as an arena.
//
// NOTE: this bypasses framing done by capnp marshalling, thus expects the
// caller to frame the message appropriately.
func BenchmarkUnmarshal_Reuse(b *testing.B) {
	type testCase struct {
		a    A
		data []byte
	}
	r := rand.New(rand.NewSource(12345))
	data := make([]testCase, 1000)
	for i := range data {
		a := generateA(r)
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		root, _ := air.NewRootBenchmarkA(seg)
		a.fill(root)
		buf, err := msg.Marshal()
		if err != nil {
			b.Fatal(err)
		}
		data[i].data, data[i].a = buf, *a
	}
	msg := new(capnp.Message)
	ta := capnp.NewReadOnlySingleSegment(nil)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		testIdx := r.Intn(len(data))
		msg.Release()
		ta.ReplaceData(data[testIdx].data[8:]) // [8:] to skip header
		msg.Arena = ta
		a, err := air.ReadRootBenchmarkA(msg)
		if err != nil {
			b.Fatal(err)
		}
		gotA := unmarshalA(a)
		if gotA != data[testIdx].a {
			b.Fatal("unexpected unmarshalled data")
		}
	}
}

func BenchmarkDecode(b *testing.B) {
	var buf bytes.Buffer

	r := rand.New(rand.NewSource(12345))
	enc := capnp.NewEncoder(&buf)
	count := 10000

	for i := 0; i < count; i++ {
		a := generateA(r)
		msg, seg := capnp.NewSingleSegmentMessage(nil)
		root, _ := air.NewRootBenchmarkA(seg)
		a.fill(root)
		enc.Encode(msg)
	}

	blob := buf.Bytes()

	b.ReportAllocs()
	b.SetBytes(int64(buf.Len()))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		dec := capnp.NewDecoder(bytes.NewReader(blob))

		for {
			msg, err := dec.Decode()

			if err == io.EOF {
				break
			}

			if err != nil {
				b.Fatal(err)
			}

			_, err = air.ReadRootBenchmarkA(msg)
			if err != nil {
				b.Fatal(err)
			}

			msg.Release()
		}
	}
}

func TestPointerTraverseDefense(t *testing.T) {
	t.Parallel()
	const limit = 128
	msg, _ := capnp.NewSingleSegmentMessage([]byte{
		0, 0, 0, 0, 1, 0, 0, 0, // root 1-word struct pointer to next word
		0, 0, 0, 0, 0, 0, 0, 0, // struct's data
	})
	msg.TraverseLimit = limit * 8

	for i := 0; i < limit; i++ {
		_, err := msg.Root()
		if err != nil {
			t.Fatalf("iteration %d RootPtr: %v", i, err)
		}
	}

	if _, err := msg.Root(); err == nil {
		t.Fatalf("deref %d did not fail as expected", limit+1)
	}
}

func TestPointerDepthDefense(t *testing.T) {
	t.Parallel()
	const limit = 64
	msg, _ := capnp.NewSingleSegmentMessage([]byte{
		0, 0, 0, 0, 0, 0, 1, 0, // root 1-pointer struct pointer to next word
		0xfc, 0xff, 0xff, 0xff, 0, 0, 1, 0, // root struct pointer that points back to itself
	})
	msg.DepthLimit = limit
	root, err := msg.Root()
	if err != nil {
		t.Fatal("Root:", err)
	}

	curr := root.Struct()
	if !curr.IsValid() {
		t.Fatal("Root is not a struct")
	}
	for i := 0; i < limit-1; i++ {
		p, err := curr.Ptr(0)
		if err != nil {
			t.Fatalf("deref %d fail: %v", i+1, err)
		}
		if !p.IsValid() {
			t.Fatalf("deref %d is invalid", i+1)
		}
		curr = p.Struct()
		if !curr.IsValid() {
			t.Fatalf("deref %d is not a struct", i+1)
		}
	}

	_, err = curr.Ptr(0)
	if err == nil {
		t.Fatalf("deref %d did not fail as expected", limit)
	}
}

func TestPointerDepthDefenseAcrossStructsAndLists(t *testing.T) {
	t.Parallel()
	const limit = 63
	msg, _ := capnp.NewSingleSegmentMessage([]byte{
		0, 0, 0, 0, 0, 0, 1, 0, // root 1-pointer struct pointer to next word
		0x01, 0, 0, 0, 0x0e, 0, 0, 0, // list pointer to 1-element list of pointer (next word)
		0xf8, 0xff, 0xff, 0xff, 0, 0, 1, 0, // struct pointer to previous word
	})
	msg.DepthLimit = limit

	toStruct := func(p capnp.Ptr, err error) (capnp.Struct, error) {
		if err != nil {
			return capnp.Struct{}, err
		}
		if !p.IsValid() {
			return capnp.Struct{}, errors.New("invalid pointer")
		}
		s := p.Struct()
		if !s.IsValid() {
			return capnp.Struct{}, errors.New("not a struct")
		}
		return s, nil
	}
	toList := func(p capnp.Ptr, err error) (capnp.List, error) {
		if err != nil {
			return capnp.List{}, err
		}
		if !p.IsValid() {
			return capnp.List{}, errors.New("invalid pointer")
		}
		l := p.List()
		if !l.IsValid() {
			return capnp.List{}, errors.New("not a list")
		}
		return l, nil
	}
	curr, err := toStruct(msg.Root())
	if err != nil {
		t.Fatal("Root:", err)
	}
	for i := limit; i > 2; {
		l, err := toList(curr.Ptr(0))
		if err != nil {
			t.Fatalf("deref %d (for list): %v", limit-i+1, err)
		}
		i--
		curr, err = toStruct(capnp.PointerList(l).At(0))
		if err != nil {
			t.Fatalf("deref %d (for struct): %v", limit-i+1, err)
		}
		i--
	}

	_, err = curr.Ptr(0)
	if err == nil {
		t.Fatalf("deref %d did not fail as expected", limit)
	}
}

func TestHasPointerInUnion(t *testing.T) {
	t.Parallel()
	_, seg := capnp.NewSingleSegmentMessage(nil)
	craft, err := air.NewRootAircraft(seg)
	if err != nil {
		t.Fatal("NewRootAircraft:", err)
	}
	t.Log("NewB737")
	_, err = craft.NewB737()
	if err != nil {
		t.Fatal("NewB737:", err)
	}

	// These pointers are at the same offset.
	if !craft.HasB737() {
		t.Errorf("HasB737 = false; want true")
	}
	if craft.HasA320() {
		t.Errorf("HasA320 = true; want false")
	}
}

func TestSetNilBlob(t *testing.T) {
	t.Parallel()
	_, seg := capnp.NewSingleSegmentMessage(nil)
	z, err := air.NewRootZ(seg)
	if err != nil {
		t.Fatal("NewRootZ:", err)
	}
	if err := z.SetBlob(nil); err != nil {
		t.Fatal("z.SetBlob(nil):", err)
	}

	if z.HasBlob() {
		t.Error("z.HasBlob() = true; want false")
	}
	blob, err := z.Blob()
	if err != nil {
		t.Errorf("z.Blob(): %v", err)
	}
	if blob != nil {
		t.Errorf("z.Blob() = %v; want nil", blob)
	}
}

func TestSetEmptyText(t *testing.T) {
	t.Parallel()
	_, seg := capnp.NewSingleSegmentMessage(nil)
	z, err := air.NewRootZ(seg)
	if err != nil {
		t.Fatal("NewRootZ:", err)
	}
	if err := z.SetText(""); err != nil {
		t.Fatal("z.SetText(\"\"):", err)
	}

	if z.HasText() {
		t.Error("z.HasText() = true; want false")
	}
	text, err := z.Text()
	if err != nil {
		t.Errorf("z.Text(): %v", err)
	}
	if text != "" {
		t.Errorf("z.Text() = %q; want \"\"", text)
	}
	b, err := z.TextBytes()
	if err != nil {
		t.Errorf("z.TextBytes(): %v", err)
	}
	if b != nil {
		t.Errorf("z.TextBytes() = %v; want nil", b)
	}
}

func TestSetNilBlobWithDefault(t *testing.T) {
	t.Parallel()
	_, seg := capnp.NewSingleSegmentMessage(nil)
	d, err := air.NewRootDefaults(seg)
	if err != nil {
		t.Fatal("NewRootDefaults:", err)
	}
	if err := d.SetData(nil); err != nil {
		t.Fatal("d.SetData(nil):", err)
	}

	if !d.HasData() {
		t.Error("d.HasData() = false; want true")
	}
	blob, err := d.Data()
	if err != nil {
		t.Errorf("d.Data(): %v", err)
	}
	if len(blob) != 0 {
		t.Errorf("d.Data() = %v; want zero length", blob)
	}
	// Specifically not checking for nil.  Anything zero-length is appropriate here.
}

func TestSetEmptyTextWithDefault(t *testing.T) {
	t.Parallel()
	_, seg := capnp.NewSingleSegmentMessage(nil)
	d, err := air.NewRootDefaults(seg)
	if err != nil {
		t.Fatal("NewRootDefaults:", err)
	}
	if err := d.SetText(""); err != nil {
		t.Fatal("d.SetData(\"\"):", err)
	}

	if !d.HasText() {
		t.Error("d.HasText() = false; want true")
	}
	text, err := d.Text()
	if err != nil {
		t.Errorf("d.Text(): %v", err)
	}
	if text != "" {
		t.Errorf("d.Text() = %v; want zero length", text)
	}
	b, err := d.TextBytes()
	if err != nil {
		t.Errorf("d.TextBytes(): %v", err)
	}
	if len(b) != 0 {
		t.Errorf("d.TextBytes() = %v; want zero length", b)
	}
}

func TestFuzzedListOutOfBounds(t *testing.T) {
	t.Parallel()
	msg, _ := capnp.NewSingleSegmentMessage([]byte(
		"\x00\x00\x00\x00\x03\x00\x01\x00\x0f\x000000000000" +
			"000000000000\x01\x00\x00\x00\x13\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00"))

	z, err := air.ReadRootZ(msg)
	if err != nil {
		t.Fatal("ReadRootZ:", err)
	}
	if z.Which() != air.Z_Which_f64vec {
		t.Fatalf("z.Which() = %v; want Z_Which_f64vec", z.Which())
	}
	v, err := z.F64vec()
	if err != nil {
		t.Fatal("z.F64vec:", err)
	}
	for i := 0; i < v.Len(); i++ {
		// This should not crash.
		t.Logf("v.At(%d); v.Len() = %d", i, v.Len())
		v.At(i)
	}
}

func benchmarkGrowth(b *testing.B, newArena func() capnp.Arena, release bool) {
	const (
		fieldValue = "1234567" // carefully chosen to be word-padded

		rootMessageOverhead = 8 * 3 // root pointer, Document struct, composite list tag
		perFieldOverhead    = 8 * 2 // Field struct, fieldValue + "\0"
		numElements         = 64 * 1024
		totalSize           = rootMessageOverhead + perFieldOverhead*numElements
	)
	b.SetBytes(totalSize)
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		msg, seg, err := capnp.NewMessage(newArena())
		if err != nil {
			b.Fatal(err)
		}
		doc, err := air.NewRootAllocBenchmark(seg)
		if err != nil {
			b.Fatal(err)
		}
		d, err := doc.NewFields(numElements)
		if err != nil {
			b.Fatal(err)
		}
		for j := 0; j < numElements; j++ {
			if err := d.At(j).SetStringValue(fieldValue); err != nil {
				b.Fatal(err)
			}
		}

		if release {
			msg.Release()
		}
	}
}

func newSingleSegmentArena() capnp.Arena { return capnp.SingleSegment(nil) }
func newMultiSegmentArena() capnp.Arena  { return capnp.MultiSegment(nil) }

func BenchmarkGrowth(b *testing.B) {
	tests := []struct {
		name     string
		newArena func() capnp.Arena
	}{{
		name:     "SingleSegment",
		newArena: newSingleSegmentArena,
	}, {
		name:     "MultiSegment",
		newArena: newMultiSegmentArena,
	}}

	for _, release := range []bool{false, true} {
		release := release
		for i := range tests {
			tc := tests[i]
			name := fmt.Sprintf("%s/release=%v", tc.name, release)
			b.Run(name, func(b *testing.B) {
				benchmarkGrowth(b, tc.newArena, release)
			})
		}
	}
}

func benchmarkSmallMessage(b *testing.B, newArena func() capnp.Arena, release bool) {
	const fieldValue = "1234567" // carefully chosen to be word-padded
	b.SetBytes(8 * 9)
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		msg, seg, err := capnp.NewMessage(newArena())
		if err != nil {
			b.Fatal(err)
		}
		root, err := capnp.NewRootStruct(seg, capnp.ObjectSize{PointerCount: 2})
		if err != nil {
			b.Fatal(err)
		}

		sub1, err := capnp.NewStruct(root.Segment(), capnp.ObjectSize{PointerCount: 1})
		if err != nil {
			b.Fatal(err)
		}
		if err := root.SetPtr(0, sub1.ToPtr()); err != nil {
			b.Fatal(err)
		}
		text, err := capnp.NewText(sub1.Segment(), fieldValue)
		if err != nil {
			b.Fatal(err)
		}
		if err := sub1.SetPtr(0, text.ToPtr()); err != nil {
			b.Fatal(err)
		}

		sub2, err := capnp.NewStruct(root.Segment(), capnp.ObjectSize{DataSize: 32})
		if err != nil {
			b.Fatal(err)
		}
		if err := root.SetPtr(0, sub2.ToPtr()); err != nil {
			b.Fatal(err)
		}

		if release {
			msg.Release()
		}
	}
}

func BenchmarkSmallMessage(b *testing.B) {
	tests := []struct {
		name     string
		newArena func() capnp.Arena
	}{{
		name:     "SingleSegment",
		newArena: newSingleSegmentArena,
	}, {
		name:     "MultiSegment",
		newArena: newMultiSegmentArena,
	}}

	for _, release := range []bool{false, true} {
		release := release
		for i := range tests {
			tc := tests[i]
			name := fmt.Sprintf("%s/release=%v", tc.name, release)
			b.Run(name, func(b *testing.B) {
				benchmarkSmallMessage(b, tc.newArena, release)
			})
		}
	}
}
