-
-
Notifications
You must be signed in to change notification settings - Fork 112
Working with Cap'n Proto Types
The Go types you generated in the previous section are actually around a []byte
buffer, with getters and setters operate by indexing the buffer at specific offsets. So technically speaking, there there is no (un)marshal step in Cap'n Proto. Converting a generated type to and from []byte
is a simple matter of wrapping and unwrapping the buffer: a constant-time operation on the order of nanoseconds.
In this sense, operations like Marshal
and Unmarshal
are misnomers. Nevertheless, we use this terminology for two reasons:
- it is familiar to most Go developers and it conveys a pattern for effective use; and,
- it provides an obvious place for hooking in Cap'n Proto's packed encoding functionality.
Packed encoding is a simple, blisteringly fast compression method similar to the one used by Protocol Buffers.
In this section, we will show you how to "marshal" and "unmarshal" generated types to and from bytes. This is suitable for such things as writing capnp types to a file, or sending a single objet over the network, e.g. in an HTTP request. If you want to stream multiple objects over the network, you'll need a way of "framing" the stream, i.e. separating the byte stream into different objects. For this, we use the Encoder
and Decoder
types, which we will introduce further below.
// TODO
The Encoder
and Decoder
types allow you to stream generated types to and from any io.Writer
or io.Reader
, respectively.
Here's
package main
import (
"os"
"foo/books"
"capnproto.org/go/capnp/v3"
)
func main() {
// Create a new Arena for a books.Book type. The Arena wraps the underlying
// buffer, providing a low-level access API. You probably won't ever need to
// interact with it directly.
arena := capnp.SingleSegment(nil)
// Make a brand new empty message. A Message allocates Cap'n Proto structs within
// its arena.
msg, seg, err := capnp.NewMessage(arena)
if err != nil {
panic(err)
}
// Create a new Book struct. Every message must have a root struct.
book, err := books.NewRootBook(seg)
if err != nil {
panic(err)
}
book.SetTitle("War and Peace")
book.SetPageCount(1440)
// Create a new encoder that streams messages to stdout.
// You can also use NewPackedEncoder if you want to compress
// the data.
encoder := capnp.NewEncoder(os.Stdout)
// Send the book's underlying *capnp.Message
err = encoder.Encode(msg)
if err != nil {
panic(err)
}
}
These datatypes can also be read from byte-streams, as follows:
package main
import (
"fmt"
"os"
"foo/books"
"capnproto.org/go/capnp/v3"
)
func main() {
// Read the message from stdin.
msg, err := capnp.NewDecoder(os.Stdin).Decode()
if err != nil {
panic(err)
}
// Extract the root struct from the message.
book, err := books.ReadRootBook(msg)
if err != nil {
panic(err)
}
// Access fields from the struct.
title, err := book.Title()
if err != nil {
panic(err)
}
pageCount := book.PageCount()
fmt.Printf("%q has %d pages\n", title, pageCount)
}
In addition, each type has a .Message()
method that returns a capnp.Message
, which can be directly marshaled into []byte
s using Message.Marshal
, and unmarshaled with a corresponding call to Message.Unmarshal
.
Lastly, packed encodings are supported via the following methods: