openpilot v0.4.6 release

This commit is contained in:
Vehicle Researcher
2018-05-23 03:59:04 +00:00
parent 28e3543ec4
commit c6df34f55b
119 changed files with 44037 additions and 379 deletions
File diff suppressed because it is too large Load Diff
+220
View File
@@ -0,0 +1,220 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_BLOB_H_
#define CAPNP_BLOB_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include <kj/common.h>
#include <kj/string.h>
#include "common.h"
#include <string.h>
namespace capnp {
struct Data {
Data() = delete;
class Reader;
class Builder;
class Pipeline {};
};
struct Text {
Text() = delete;
class Reader;
class Builder;
class Pipeline {};
};
class Data::Reader: public kj::ArrayPtr<const byte> {
// Points to a blob of bytes. The usual Reader rules apply -- Data::Reader behaves like a simple
// pointer which does not own its target, can be passed by value, etc.
public:
typedef Data Reads;
Reader() = default;
inline Reader(decltype(nullptr)): ArrayPtr<const byte>(nullptr) {}
inline Reader(const byte* value, size_t size): ArrayPtr<const byte>(value, size) {}
inline Reader(const kj::Array<const byte>& value): ArrayPtr<const byte>(value) {}
inline Reader(const ArrayPtr<const byte>& value): ArrayPtr<const byte>(value) {}
inline Reader(const kj::Array<byte>& value): ArrayPtr<const byte>(value) {}
inline Reader(const ArrayPtr<byte>& value): ArrayPtr<const byte>(value) {}
};
class Text::Reader: public kj::StringPtr {
// Like Data::Reader, but points at NUL-terminated UTF-8 text. The NUL terminator is not counted
// in the size but must be present immediately after the last byte.
//
// Text::Reader's interface contract is that its data MUST be NUL-terminated. The producer of
// the Text::Reader must guarantee this, so that the consumer need not check. The data SHOULD
// also be valid UTF-8, but this is NOT guaranteed -- the consumer must verify if it cares.
public:
typedef Text Reads;
Reader() = default;
inline Reader(decltype(nullptr)): StringPtr(nullptr) {}
inline Reader(const char* value): StringPtr(value) {}
inline Reader(const char* value, size_t size): StringPtr(value, size) {}
inline Reader(const kj::String& value): StringPtr(value) {}
inline Reader(const StringPtr& value): StringPtr(value) {}
#if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP
template <typename T, typename = decltype(kj::instance<T>().c_str())>
inline Reader(const T& t): StringPtr(t) {}
// Allow implicit conversion from any class that has a c_str() method (namely, std::string).
// We use a template trick to detect std::string in order to avoid including the header for
// those who don't want it.
#endif
};
class Data::Builder: public kj::ArrayPtr<byte> {
// Like Data::Reader except the pointers aren't const.
public:
typedef Data Builds;
Builder() = default;
inline Builder(decltype(nullptr)): ArrayPtr<byte>(nullptr) {}
inline Builder(byte* value, size_t size): ArrayPtr<byte>(value, size) {}
inline Builder(kj::Array<byte>& value): ArrayPtr<byte>(value) {}
inline Builder(ArrayPtr<byte> value): ArrayPtr<byte>(value) {}
inline Data::Reader asReader() const { return Data::Reader(*this); }
inline operator Reader() const { return asReader(); }
};
class Text::Builder: public kj::DisallowConstCopy {
// Basically identical to kj::StringPtr, except that the contents are non-const.
public:
inline Builder(): content(nulstr, 1) {}
inline Builder(decltype(nullptr)): content(nulstr, 1) {}
inline Builder(char* value): content(value, strlen(value) + 1) {}
inline Builder(char* value, size_t size): content(value, size + 1) {
KJ_IREQUIRE(value[size] == '\0', "StringPtr must be NUL-terminated.");
}
inline Reader asReader() const { return Reader(content.begin(), content.size() - 1); }
inline operator Reader() const { return asReader(); }
inline operator kj::ArrayPtr<char>();
inline kj::ArrayPtr<char> asArray();
inline operator kj::ArrayPtr<const char>() const;
inline kj::ArrayPtr<const char> asArray() const;
inline kj::ArrayPtr<byte> asBytes() { return asArray().asBytes(); }
inline kj::ArrayPtr<const byte> asBytes() const { return asArray().asBytes(); }
// Result does not include NUL terminator.
inline operator kj::StringPtr() const;
inline kj::StringPtr asString() const;
inline const char* cStr() const { return content.begin(); }
// Returns NUL-terminated string.
inline size_t size() const { return content.size() - 1; }
// Result does not include NUL terminator.
inline char operator[](size_t index) const { return content[index]; }
inline char& operator[](size_t index) { return content[index]; }
inline char* begin() { return content.begin(); }
inline char* end() { return content.end() - 1; }
inline const char* begin() const { return content.begin(); }
inline const char* end() const { return content.end() - 1; }
inline bool operator==(decltype(nullptr)) const { return content.size() <= 1; }
inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
inline bool operator==(Builder other) const { return asString() == other.asString(); }
inline bool operator!=(Builder other) const { return asString() != other.asString(); }
inline bool operator< (Builder other) const { return asString() < other.asString(); }
inline bool operator> (Builder other) const { return asString() > other.asString(); }
inline bool operator<=(Builder other) const { return asString() <= other.asString(); }
inline bool operator>=(Builder other) const { return asString() >= other.asString(); }
inline kj::StringPtr slice(size_t start) const;
inline kj::ArrayPtr<const char> slice(size_t start, size_t end) const;
inline Builder slice(size_t start);
inline kj::ArrayPtr<char> slice(size_t start, size_t end);
// A string slice is only NUL-terminated if it is a suffix, so slice() has a one-parameter
// version that assumes end = size().
private:
inline explicit Builder(kj::ArrayPtr<char> content): content(content) {}
kj::ArrayPtr<char> content;
static char nulstr[1];
};
inline kj::StringPtr KJ_STRINGIFY(Text::Builder builder) {
return builder.asString();
}
inline bool operator==(const char* a, const Text::Builder& b) { return a == b.asString(); }
inline bool operator!=(const char* a, const Text::Builder& b) { return a != b.asString(); }
inline Text::Builder::operator kj::StringPtr() const {
return kj::StringPtr(content.begin(), content.size() - 1);
}
inline kj::StringPtr Text::Builder::asString() const {
return kj::StringPtr(content.begin(), content.size() - 1);
}
inline Text::Builder::operator kj::ArrayPtr<char>() {
return content.slice(0, content.size() - 1);
}
inline kj::ArrayPtr<char> Text::Builder::asArray() {
return content.slice(0, content.size() - 1);
}
inline Text::Builder::operator kj::ArrayPtr<const char>() const {
return content.slice(0, content.size() - 1);
}
inline kj::ArrayPtr<const char> Text::Builder::asArray() const {
return content.slice(0, content.size() - 1);
}
inline kj::StringPtr Text::Builder::slice(size_t start) const {
return asReader().slice(start);
}
inline kj::ArrayPtr<const char> Text::Builder::slice(size_t start, size_t end) const {
return content.slice(start, end);
}
inline Text::Builder Text::Builder::slice(size_t start) {
return Text::Builder(content.slice(start, content.size()));
}
inline kj::ArrayPtr<char> Text::Builder::slice(size_t start, size_t end) {
return content.slice(start, end);
}
} // namespace capnp
#endif // CAPNP_BLOB_H_
@@ -0,0 +1,26 @@
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0xbdf87d7bb8304e81;
$namespace("capnp::annotations");
annotation namespace(file): Text;
annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text;
@@ -0,0 +1,33 @@
// Generated by Cap'n Proto compiler, DO NOT EDIT
// source: c++.capnp
#ifndef CAPNP_INCLUDED_bdf87d7bb8304e81_
#define CAPNP_INCLUDED_bdf87d7bb8304e81_
#include <capnp/generated-header-support.h>
#if CAPNP_VERSION != 6001
#error "Version mismatch between generated code and library headers. You must use the same version of the Cap'n Proto compiler and library."
#endif
namespace capnp {
namespace schemas {
CAPNP_DECLARE_SCHEMA(b9c6f99ebf805f2c);
CAPNP_DECLARE_SCHEMA(f264a779fef191ce);
} // namespace schemas
} // namespace capnp
namespace capnp {
namespace annotations {
// =======================================================================================
// =======================================================================================
} // namespace
} // namespace
#endif // CAPNP_INCLUDED_bdf87d7bb8304e81_
+37
View File
@@ -0,0 +1,37 @@
# Copyright (c) 2016 NetDEF, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0xc0183dd65ffef0f3;
annotation nameinfix @0x85a8d86d736ba637 (file): Text;
# add an infix (middle insert) for output file names
#
# "make" generally has implicit rules for compiling "foo.c" => "foo". This
# is very annoying with capnp since the rule will be "foo" => "foo.c", leading
# to a loop. $nameinfix (recommended parameter: "-gen") inserts its parameter
# before the ".c", so the filename becomes "foo-gen.c"
#
# ("foo" is really "foo.capnp", so it's foo.capnp-gen.c)
annotation fieldgetset @0xf72bc690355d66de (file): Void;
# generate getter & setter functions for accessing fields
#
# allows grabbing/putting values without de-/encoding the entire struct.
@@ -0,0 +1,884 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_CAPABILITY_H_
#define CAPNP_CAPABILITY_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#if CAPNP_LITE
#error "RPC APIs, including this header, are not available in lite mode."
#endif
#include <kj/async.h>
#include <kj/vector.h>
#include "raw-schema.h"
#include "any.h"
#include "pointer-helpers.h"
namespace capnp {
template <typename Results>
class Response;
template <typename T>
class RemotePromise: public kj::Promise<Response<T>>, public T::Pipeline {
// A Promise which supports pipelined calls. T is typically a struct type. T must declare
// an inner "mix-in" type "Pipeline" which implements pipelining; RemotePromise simply
// multiply-inherits that type along with Promise<Response<T>>. T::Pipeline must be movable,
// but does not need to be copyable (i.e. just like Promise<T>).
//
// The promise is for an owned pointer so that the RPC system can allocate the MessageReader
// itself.
public:
inline RemotePromise(kj::Promise<Response<T>>&& promise, typename T::Pipeline&& pipeline)
: kj::Promise<Response<T>>(kj::mv(promise)),
T::Pipeline(kj::mv(pipeline)) {}
inline RemotePromise(decltype(nullptr))
: kj::Promise<Response<T>>(nullptr),
T::Pipeline(nullptr) {}
KJ_DISALLOW_COPY(RemotePromise);
RemotePromise(RemotePromise&& other) = default;
RemotePromise& operator=(RemotePromise&& other) = default;
};
class LocalClient;
namespace _ { // private
extern const RawSchema NULL_INTERFACE_SCHEMA; // defined in schema.c++
class CapabilityServerSetBase;
} // namespace _ (private)
struct Capability {
// A capability without type-safe methods. Typed capability clients wrap `Client` and typed
// capability servers subclass `Server` to dispatch to the regular, typed methods.
class Client;
class Server;
struct _capnpPrivate {
struct IsInterface;
static constexpr uint64_t typeId = 0x3;
static constexpr Kind kind = Kind::INTERFACE;
static constexpr _::RawSchema const* schema = &_::NULL_INTERFACE_SCHEMA;
static const _::RawBrandedSchema* brand() {
return &_::NULL_INTERFACE_SCHEMA.defaultBrand;
}
};
};
// =======================================================================================
// Capability clients
class RequestHook;
class ResponseHook;
class PipelineHook;
class ClientHook;
template <typename Params, typename Results>
class Request: public Params::Builder {
// A call that hasn't been sent yet. This class extends a Builder for the call's "Params"
// structure with a method send() that actually sends it.
//
// Given a Cap'n Proto method `foo(a :A, b :B): C`, the generated client interface will have
// a method `Request<FooParams, C> fooRequest()` (as well as a convenience method
// `RemotePromise<C> foo(A::Reader a, B::Reader b)`).
public:
inline Request(typename Params::Builder builder, kj::Own<RequestHook>&& hook)
: Params::Builder(builder), hook(kj::mv(hook)) {}
inline Request(decltype(nullptr)): Params::Builder(nullptr) {}
RemotePromise<Results> send() KJ_WARN_UNUSED_RESULT;
// Send the call and return a promise for the results.
private:
kj::Own<RequestHook> hook;
friend class Capability::Client;
friend struct DynamicCapability;
template <typename, typename>
friend class CallContext;
friend class RequestHook;
};
template <typename Results>
class Response: public Results::Reader {
// A completed call. This class extends a Reader for the call's answer structure. The Response
// is move-only -- once it goes out-of-scope, the underlying message will be freed.
public:
inline Response(typename Results::Reader reader, kj::Own<ResponseHook>&& hook)
: Results::Reader(reader), hook(kj::mv(hook)) {}
private:
kj::Own<ResponseHook> hook;
template <typename, typename>
friend class Request;
friend class ResponseHook;
};
class Capability::Client {
// Base type for capability clients.
public:
typedef Capability Reads;
typedef Capability Calls;
Client(decltype(nullptr));
// If you need to declare a Client before you have anything to assign to it (perhaps because
// the assignment is going to occur in an if/else scope), you can start by initializing it to
// `nullptr`. The resulting client is not meant to be called and throws exceptions from all
// methods.
template <typename T, typename = kj::EnableIf<kj::canConvert<T*, Capability::Server*>()>>
Client(kj::Own<T>&& server);
// Make a client capability that wraps the given server capability. The server's methods will
// only be executed in the given EventLoop, regardless of what thread calls the client's methods.
template <typename T, typename = kj::EnableIf<kj::canConvert<T*, Client*>()>>
Client(kj::Promise<T>&& promise);
// Make a client from a promise for a future client. The resulting client queues calls until the
// promise resolves.
Client(kj::Exception&& exception);
// Make a broken client that throws the given exception from all calls.
Client(Client& other);
Client& operator=(Client& other);
// Copies by reference counting. Warning: This refcounting is not thread-safe. All copies of
// the client must remain in one thread.
Client(Client&&) = default;
Client& operator=(Client&&) = default;
// Move constructor avoids reference counting.
explicit Client(kj::Own<ClientHook>&& hook);
// For use by the RPC implementation: Wrap a ClientHook.
template <typename T>
typename T::Client castAs();
// Reinterpret the capability as implementing the given interface. Note that no error will occur
// here if the capability does not actually implement this interface, but later method calls will
// fail. It's up to the application to decide how indicate that additional interfaces are
// supported.
//
// TODO(perf): GCC 4.8 / Clang 3.3: rvalue-qualified version for better performance.
template <typename T>
typename T::Client castAs(InterfaceSchema schema);
// Dynamic version. `T` must be `DynamicCapability`, and you must `#include <capnp/dynamic.h>`.
kj::Promise<void> whenResolved();
// If the capability is actually only a promise, the returned promise resolves once the
// capability itself has resolved to its final destination (or propagates the exception if
// the capability promise is rejected). This is mainly useful for error-checking in the case
// where no calls are being made. There is no reason to wait for this before making calls; if
// the capability does not resolve, the call results will propagate the error.
Request<AnyPointer, AnyPointer> typelessRequest(
uint64_t interfaceId, uint16_t methodId,
kj::Maybe<MessageSize> sizeHint);
// Make a request without knowing the types of the params or results. You specify the type ID
// and method number manually.
// TODO(someday): method(s) for Join
protected:
Client() = default;
template <typename Params, typename Results>
Request<Params, Results> newCall(uint64_t interfaceId, uint16_t methodId,
kj::Maybe<MessageSize> sizeHint);
private:
kj::Own<ClientHook> hook;
static kj::Own<ClientHook> makeLocalClient(kj::Own<Capability::Server>&& server);
template <typename, Kind>
friend struct _::PointerHelpers;
friend struct DynamicCapability;
friend class Orphanage;
friend struct DynamicStruct;
friend struct DynamicList;
template <typename, Kind>
friend struct List;
friend class _::CapabilityServerSetBase;
friend class ClientHook;
};
// =======================================================================================
// Capability servers
class CallContextHook;
template <typename Params, typename Results>
class CallContext: public kj::DisallowConstCopy {
// Wrapper around CallContextHook with a specific return type.
//
// Methods of this class may only be called from within the server's event loop, not from other
// threads.
//
// The CallContext becomes invalid as soon as the call reports completion.
public:
explicit CallContext(CallContextHook& hook);
typename Params::Reader getParams();
// Get the params payload.
void releaseParams();
// Release the params payload. getParams() will throw an exception after this is called.
// Releasing the params may allow the RPC system to free up buffer space to handle other
// requests. Long-running asynchronous methods should try to call this as early as is
// convenient.
typename Results::Builder getResults(kj::Maybe<MessageSize> sizeHint = nullptr);
typename Results::Builder initResults(kj::Maybe<MessageSize> sizeHint = nullptr);
void setResults(typename Results::Reader value);
void adoptResults(Orphan<Results>&& value);
Orphanage getResultsOrphanage(kj::Maybe<MessageSize> sizeHint = nullptr);
// Manipulate the results payload. The "Return" message (part of the RPC protocol) will
// typically be allocated the first time one of these is called. Some RPC systems may
// allocate these messages in a limited space (such as a shared memory segment), therefore the
// application should delay calling these as long as is convenient to do so (but don't delay
// if doing so would require extra copies later).
//
// `sizeHint` indicates a guess at the message size. This will usually be used to decide how
// much space to allocate for the first message segment (don't worry: only space that is actually
// used will be sent on the wire). If omitted, the system decides. The message root pointer
// should not be included in the size. So, if you are simply going to copy some existing message
// directly into the results, just call `.totalSize()` and pass that in.
template <typename SubParams>
kj::Promise<void> tailCall(Request<SubParams, Results>&& tailRequest);
// Resolve the call by making a tail call. `tailRequest` is a request that has been filled in
// but not yet sent. The context will send the call, then fill in the results with the result
// of the call. If tailCall() is used, {get,init,set,adopt}Results (above) *must not* be called.
//
// The RPC implementation may be able to optimize a tail call to another machine such that the
// results never actually pass through this machine. Even if no such optimization is possible,
// `tailCall()` may allow pipelined calls to be forwarded optimistically to the new call site.
//
// In general, this should be the last thing a method implementation calls, and the promise
// returned from `tailCall()` should then be returned by the method implementation.
void allowCancellation();
// Indicate that it is OK for the RPC system to discard its Promise for this call's result if
// the caller cancels the call, thereby transitively canceling any asynchronous operations the
// call implementation was performing. This is not done by default because it could represent a
// security risk: applications must be carefully written to ensure that they do not end up in
// a bad state if an operation is canceled at an arbitrary point. However, for long-running
// method calls that hold significant resources, prompt cancellation is often useful.
//
// Keep in mind that asynchronous cancellation cannot occur while the method is synchronously
// executing on a local thread. The method must perform an asynchronous operation or call
// `EventLoop::current().evalLater()` to yield control.
//
// Note: You might think that we should offer `onCancel()` and/or `isCanceled()` methods that
// provide notification when the caller cancels the request without forcefully killing off the
// promise chain. Unfortunately, this composes poorly with promise forking: the canceled
// path may be just one branch of a fork of the result promise. The other branches still want
// the call to continue. Promise forking is used within the Cap'n Proto implementation -- in
// particular each pipelined call forks the result promise. So, if a caller made a pipelined
// call and then dropped the original object, the call should not be canceled, but it would be
// excessively complicated for the framework to avoid notififying of cancellation as long as
// pipelined calls still exist.
private:
CallContextHook* hook;
friend class Capability::Server;
friend struct DynamicCapability;
};
class Capability::Server {
// Objects implementing a Cap'n Proto interface must subclass this. Typically, such objects
// will instead subclass a typed Server interface which will take care of implementing
// dispatchCall().
public:
typedef Capability Serves;
virtual kj::Promise<void> dispatchCall(uint64_t interfaceId, uint16_t methodId,
CallContext<AnyPointer, AnyPointer> context) = 0;
// Call the given method. `params` is the input struct, and should be released as soon as it
// is no longer needed. `context` may be used to allocate the output struct and deal with
// cancellation.
// TODO(someday): Method which can optionally be overridden to implement Join when the object is
// a proxy.
protected:
inline Capability::Client thisCap();
// Get a capability pointing to this object, much like the `this` keyword.
//
// The effect of this method is undefined if:
// - No capability client has been created pointing to this object. (This is always the case in
// the server's constructor.)
// - The capability client pointing at this object has been destroyed. (This is always the case
// in the server's destructor.)
// - Multiple capability clients have been created around the same server (possible if the server
// is refcounted, which is not recommended since the client itself provides refcounting).
template <typename Params, typename Results>
CallContext<Params, Results> internalGetTypedContext(
CallContext<AnyPointer, AnyPointer> typeless);
kj::Promise<void> internalUnimplemented(const char* actualInterfaceName,
uint64_t requestedTypeId);
kj::Promise<void> internalUnimplemented(const char* interfaceName,
uint64_t typeId, uint16_t methodId);
kj::Promise<void> internalUnimplemented(const char* interfaceName, const char* methodName,
uint64_t typeId, uint16_t methodId);
private:
ClientHook* thisHook = nullptr;
friend class LocalClient;
};
// =======================================================================================
class ReaderCapabilityTable: private _::CapTableReader {
// Class which imbues Readers with the ability to read capabilities.
//
// In Cap'n Proto format, the encoding of a capability pointer is simply an integer index into
// an external table. Since these pointers fundamentally point outside the message, a
// MessageReader by default has no idea what they point at, and therefore reading capabilities
// from such a reader will throw exceptions.
//
// In order to be able to read capabilities, you must first attach a capability table, using
// this class. By "imbuing" a Reader, you get a new Reader which will interpret capability
// pointers by treating them as indexes into the ReaderCapabilityTable.
//
// Note that when using Cap'n Proto's RPC system, this is handled automatically.
public:
explicit ReaderCapabilityTable(kj::Array<kj::Maybe<kj::Own<ClientHook>>> table);
KJ_DISALLOW_COPY(ReaderCapabilityTable);
template <typename T>
T imbue(T reader);
// Return a reader equivalent to `reader` except that when reading capability-valued fields,
// the capabilities are looked up in this table.
private:
kj::Array<kj::Maybe<kj::Own<ClientHook>>> table;
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override;
};
class BuilderCapabilityTable: private _::CapTableBuilder {
// Class which imbues Builders with the ability to read and write capabilities.
//
// This is much like ReaderCapabilityTable, except for builders. The table starts out empty,
// but capabilities can be added to it over time.
public:
BuilderCapabilityTable();
KJ_DISALLOW_COPY(BuilderCapabilityTable);
inline kj::ArrayPtr<kj::Maybe<kj::Own<ClientHook>>> getTable() { return table; }
template <typename T>
T imbue(T builder);
// Return a builder equivalent to `builder` except that when reading capability-valued fields,
// the capabilities are looked up in this table.
private:
kj::Vector<kj::Maybe<kj::Own<ClientHook>>> table;
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override;
uint injectCap(kj::Own<ClientHook>&& cap) override;
void dropCap(uint index) override;
};
// =======================================================================================
namespace _ { // private
class CapabilityServerSetBase {
public:
Capability::Client addInternal(kj::Own<Capability::Server>&& server, void* ptr);
kj::Promise<void*> getLocalServerInternal(Capability::Client& client);
};
} // namespace _ (private)
template <typename T>
class CapabilityServerSet: private _::CapabilityServerSetBase {
// Allows a server to recognize its own capabilities when passed back to it, and obtain the
// underlying Server objects associated with them.
//
// All objects in the set must have the same interface type T. The objects may implement various
// interfaces derived from T (and in fact T can be `capnp::Capability` to accept all objects),
// but note that if you compile with RTTI disabled then you will not be able to down-cast through
// virtual inheritance, and all inheritance between server interfaces is virtual. So, with RTTI
// disabled, you will likely need to set T to be the most-derived Cap'n Proto interface type,
// and you server class will need to be directly derived from that, so that you can use
// static_cast (or kj::downcast) to cast to it after calling getLocalServer(). (If you compile
// with RTTI, then you can freely dynamic_cast and ignore this issue!)
public:
CapabilityServerSet() = default;
KJ_DISALLOW_COPY(CapabilityServerSet);
typename T::Client add(kj::Own<typename T::Server>&& server);
// Create a new capability Client for the given Server and also add this server to the set.
kj::Promise<kj::Maybe<typename T::Server&>> getLocalServer(typename T::Client& client);
// Given a Client pointing to a server previously passed to add(), return the corresponding
// Server. This returns a promise because if the input client is itself a promise, this must
// wait for it to resolve. Keep in mind that the server will be deleted when all clients are
// gone, so the caller should make sure to keep the client alive (hence why this method only
// accepts an lvalue input).
};
// =======================================================================================
// Hook interfaces which must be implemented by the RPC system. Applications never call these
// directly; the RPC system implements them and the types defined earlier in this file wrap them.
class RequestHook {
// Hook interface implemented by RPC system representing a request being built.
public:
virtual RemotePromise<AnyPointer> send() = 0;
// Send the call and return a promise for the result.
virtual const void* getBrand() = 0;
// Returns a void* that identifies who made this request. This can be used by an RPC adapter to
// discover when tail call is going to be sent over its own connection and therefore can be
// optimized into a remote tail call.
template <typename T, typename U>
inline static kj::Own<RequestHook> from(Request<T, U>&& request) {
return kj::mv(request.hook);
}
};
class ResponseHook {
// Hook interface implemented by RPC system representing a response.
//
// At present this class has no methods. It exists only for garbage collection -- when the
// ResponseHook is destroyed, the results can be freed.
public:
virtual ~ResponseHook() noexcept(false);
// Just here to make sure the type is dynamic.
template <typename T>
inline static kj::Own<ResponseHook> from(Response<T>&& response) {
return kj::mv(response.hook);
}
};
// class PipelineHook is declared in any.h because it is needed there.
class ClientHook {
public:
ClientHook();
virtual Request<AnyPointer, AnyPointer> newCall(
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) = 0;
// Start a new call, allowing the client to allocate request/response objects as it sees fit.
// This version is used when calls are made from application code in the local process.
struct VoidPromiseAndPipeline {
kj::Promise<void> promise;
kj::Own<PipelineHook> pipeline;
};
virtual VoidPromiseAndPipeline call(uint64_t interfaceId, uint16_t methodId,
kj::Own<CallContextHook>&& context) = 0;
// Call the object, but the caller controls allocation of the request/response objects. If the
// callee insists on allocating these objects itself, it must make a copy. This version is used
// when calls come in over the network via an RPC system. Note that even if the returned
// `Promise<void>` is discarded, the call may continue executing if any pipelined calls are
// waiting for it.
//
// Since the caller of this method chooses the CallContext implementation, it is the caller's
// responsibility to ensure that the returned promise is not canceled unless allowed via
// the context's `allowCancellation()`.
//
// The call must not begin synchronously; the callee must arrange for the call to begin in a
// later turn of the event loop. Otherwise, application code may call back and affect the
// callee's state in an unexpected way.
virtual kj::Maybe<ClientHook&> getResolved() = 0;
// If this ClientHook is a promise that has already resolved, returns the inner, resolved version
// of the capability. The caller may permanently replace this client with the resolved one if
// desired. Returns null if the client isn't a promise or hasn't resolved yet -- use
// `whenMoreResolved()` to distinguish between them.
virtual kj::Maybe<kj::Promise<kj::Own<ClientHook>>> whenMoreResolved() = 0;
// If this client is a settled reference (not a promise), return nullptr. Otherwise, return a
// promise that eventually resolves to a new client that is closer to being the final, settled
// client (i.e. the value eventually returned by `getResolved()`). Calling this repeatedly
// should eventually produce a settled client.
kj::Promise<void> whenResolved();
// Repeatedly calls whenMoreResolved() until it returns nullptr.
virtual kj::Own<ClientHook> addRef() = 0;
// Return a new reference to the same capability.
virtual const void* getBrand() = 0;
// Returns a void* that identifies who made this client. This can be used by an RPC adapter to
// discover when a capability it needs to marshal is one that it created in the first place, and
// therefore it can transfer the capability without proxying.
static const uint NULL_CAPABILITY_BRAND;
// Value is irrelevant; used for pointer.
inline bool isNull() { return getBrand() == &NULL_CAPABILITY_BRAND; }
// Returns true if the capability was created as a result of assigning a Client to null or by
// reading a null pointer out of a Cap'n Proto message.
virtual void* getLocalServer(_::CapabilityServerSetBase& capServerSet);
// If this is a local capability created through `capServerSet`, return the underlying Server.
// Otherwise, return nullptr. Default implementation (which everyone except LocalClient should
// use) always returns nullptr.
static kj::Own<ClientHook> from(Capability::Client client) { return kj::mv(client.hook); }
};
class CallContextHook {
// Hook interface implemented by RPC system to manage a call on the server side. See
// CallContext<T>.
public:
virtual AnyPointer::Reader getParams() = 0;
virtual void releaseParams() = 0;
virtual AnyPointer::Builder getResults(kj::Maybe<MessageSize> sizeHint) = 0;
virtual kj::Promise<void> tailCall(kj::Own<RequestHook>&& request) = 0;
virtual void allowCancellation() = 0;
virtual kj::Promise<AnyPointer::Pipeline> onTailCall() = 0;
// If `tailCall()` is called, resolves to the PipelineHook from the tail call. An
// implementation of `ClientHook::call()` is allowed to call this at most once.
virtual ClientHook::VoidPromiseAndPipeline directTailCall(kj::Own<RequestHook>&& request) = 0;
// Call this when you would otherwise call onTailCall() immediately followed by tailCall().
// Implementations of tailCall() should typically call directTailCall() and then fulfill the
// promise fulfiller for onTailCall() with the returned pipeline.
virtual kj::Own<CallContextHook> addRef() = 0;
};
kj::Own<ClientHook> newLocalPromiseClient(kj::Promise<kj::Own<ClientHook>>&& promise);
// Returns a ClientHook that queues up calls until `promise` resolves, then forwards them to
// the new client. This hook's `getResolved()` and `whenMoreResolved()` methods will reflect the
// redirection to the eventual replacement client.
kj::Own<PipelineHook> newLocalPromisePipeline(kj::Promise<kj::Own<PipelineHook>>&& promise);
// Returns a PipelineHook that queues up calls until `promise` resolves, then forwards them to
// the new pipeline.
kj::Own<ClientHook> newBrokenCap(kj::StringPtr reason);
kj::Own<ClientHook> newBrokenCap(kj::Exception&& reason);
// Helper function that creates a capability which simply throws exceptions when called.
kj::Own<PipelineHook> newBrokenPipeline(kj::Exception&& reason);
// Helper function that creates a pipeline which simply throws exceptions when called.
Request<AnyPointer, AnyPointer> newBrokenRequest(
kj::Exception&& reason, kj::Maybe<MessageSize> sizeHint);
// Helper function that creates a Request object that simply throws exceptions when sent.
// =======================================================================================
// Extend PointerHelpers for interfaces
namespace _ { // private
template <typename T>
struct PointerHelpers<T, Kind::INTERFACE> {
static inline typename T::Client get(PointerReader reader) {
return typename T::Client(reader.getCapability());
}
static inline typename T::Client get(PointerBuilder builder) {
return typename T::Client(builder.getCapability());
}
static inline void set(PointerBuilder builder, typename T::Client&& value) {
builder.setCapability(kj::mv(value.Capability::Client::hook));
}
static inline void set(PointerBuilder builder, typename T::Client& value) {
builder.setCapability(value.Capability::Client::hook->addRef());
}
static inline void adopt(PointerBuilder builder, Orphan<T>&& value) {
builder.adopt(kj::mv(value.builder));
}
static inline Orphan<T> disown(PointerBuilder builder) {
return Orphan<T>(builder.disown());
}
};
} // namespace _ (private)
// =======================================================================================
// Extend List for interfaces
template <typename T>
struct List<T, Kind::INTERFACE> {
List() = delete;
class Reader {
public:
typedef List<T> Reads;
Reader() = default;
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
inline typename T::Client operator[](uint index) const {
KJ_IREQUIRE(index < size());
return typename T::Client(reader.getPointerElement(
bounded(index) * ELEMENTS).getCapability());
}
typedef _::IndexingIterator<const Reader, typename T::Client> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
_::ListReader reader;
template <typename U, Kind K>
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
public:
typedef List<T> Builds;
Builder() = delete;
inline Builder(decltype(nullptr)) {}
inline explicit Builder(_::ListBuilder builder): builder(builder) {}
inline operator Reader() const { return Reader(builder.asReader()); }
inline Reader asReader() const { return Reader(builder.asReader()); }
inline uint size() const { return unbound(builder.size() / ELEMENTS); }
inline typename T::Client operator[](uint index) {
KJ_IREQUIRE(index < size());
return typename T::Client(builder.getPointerElement(
bounded(index) * ELEMENTS).getCapability());
}
inline void set(uint index, typename T::Client value) {
KJ_IREQUIRE(index < size());
builder.getPointerElement(bounded(index) * ELEMENTS).setCapability(kj::mv(value.hook));
}
inline void adopt(uint index, Orphan<T>&& value) {
KJ_IREQUIRE(index < size());
builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(value));
}
inline Orphan<T> disown(uint index) {
KJ_IREQUIRE(index < size());
return Orphan<T>(builder.getPointerElement(bounded(index) * ELEMENTS).disown());
}
typedef _::IndexingIterator<Builder, typename T::Client> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
inline Iterator end() { return Iterator(this, size()); }
private:
_::ListBuilder builder;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
private:
inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) {
return builder.initList(ElementSize::POINTER, bounded(size) * ELEMENTS);
}
inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) {
return builder.getList(ElementSize::POINTER, defaultValue);
}
inline static _::ListReader getFromPointer(
const _::PointerReader& reader, const word* defaultValue) {
return reader.getList(ElementSize::POINTER, defaultValue);
}
template <typename U, Kind k>
friend struct List;
template <typename U, Kind K>
friend struct _::PointerHelpers;
};
// =======================================================================================
// Inline implementation details
template <typename Params, typename Results>
RemotePromise<Results> Request<Params, Results>::send() {
auto typelessPromise = hook->send();
hook = nullptr; // prevent reuse
// Convert the Promise to return the correct response type.
// Explicitly upcast to kj::Promise to make clear that calling .then() doesn't invalidate the
// Pipeline part of the RemotePromise.
auto typedPromise = kj::implicitCast<kj::Promise<Response<AnyPointer>>&>(typelessPromise)
.then([](Response<AnyPointer>&& response) -> Response<Results> {
return Response<Results>(response.getAs<Results>(), kj::mv(response.hook));
});
// Wrap the typeless pipeline in a typed wrapper.
typename Results::Pipeline typedPipeline(
kj::mv(kj::implicitCast<AnyPointer::Pipeline&>(typelessPromise)));
return RemotePromise<Results>(kj::mv(typedPromise), kj::mv(typedPipeline));
}
inline Capability::Client::Client(kj::Own<ClientHook>&& hook): hook(kj::mv(hook)) {}
template <typename T, typename>
inline Capability::Client::Client(kj::Own<T>&& server)
: hook(makeLocalClient(kj::mv(server))) {}
template <typename T, typename>
inline Capability::Client::Client(kj::Promise<T>&& promise)
: hook(newLocalPromiseClient(promise.then([](T&& t) { return kj::mv(t.hook); }))) {}
inline Capability::Client::Client(Client& other): hook(other.hook->addRef()) {}
inline Capability::Client& Capability::Client::operator=(Client& other) {
hook = other.hook->addRef();
return *this;
}
template <typename T>
inline typename T::Client Capability::Client::castAs() {
return typename T::Client(hook->addRef());
}
inline kj::Promise<void> Capability::Client::whenResolved() {
return hook->whenResolved();
}
inline Request<AnyPointer, AnyPointer> Capability::Client::typelessRequest(
uint64_t interfaceId, uint16_t methodId,
kj::Maybe<MessageSize> sizeHint) {
return newCall<AnyPointer, AnyPointer>(interfaceId, methodId, sizeHint);
}
template <typename Params, typename Results>
inline Request<Params, Results> Capability::Client::newCall(
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) {
auto typeless = hook->newCall(interfaceId, methodId, sizeHint);
return Request<Params, Results>(typeless.template getAs<Params>(), kj::mv(typeless.hook));
}
template <typename Params, typename Results>
inline CallContext<Params, Results>::CallContext(CallContextHook& hook): hook(&hook) {}
template <typename Params, typename Results>
inline typename Params::Reader CallContext<Params, Results>::getParams() {
return hook->getParams().template getAs<Params>();
}
template <typename Params, typename Results>
inline void CallContext<Params, Results>::releaseParams() {
hook->releaseParams();
}
template <typename Params, typename Results>
inline typename Results::Builder CallContext<Params, Results>::getResults(
kj::Maybe<MessageSize> sizeHint) {
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
return hook->getResults(sizeHint).template getAs<Results>();
}
template <typename Params, typename Results>
inline typename Results::Builder CallContext<Params, Results>::initResults(
kj::Maybe<MessageSize> sizeHint) {
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
return hook->getResults(sizeHint).template initAs<Results>();
}
template <typename Params, typename Results>
inline void CallContext<Params, Results>::setResults(typename Results::Reader value) {
hook->getResults(value.totalSize()).template setAs<Results>(value);
}
template <typename Params, typename Results>
inline void CallContext<Params, Results>::adoptResults(Orphan<Results>&& value) {
hook->getResults(nullptr).adopt(kj::mv(value));
}
template <typename Params, typename Results>
inline Orphanage CallContext<Params, Results>::getResultsOrphanage(
kj::Maybe<MessageSize> sizeHint) {
return Orphanage::getForMessageContaining(hook->getResults(sizeHint));
}
template <typename Params, typename Results>
template <typename SubParams>
inline kj::Promise<void> CallContext<Params, Results>::tailCall(
Request<SubParams, Results>&& tailRequest) {
return hook->tailCall(kj::mv(tailRequest.hook));
}
template <typename Params, typename Results>
inline void CallContext<Params, Results>::allowCancellation() {
hook->allowCancellation();
}
template <typename Params, typename Results>
CallContext<Params, Results> Capability::Server::internalGetTypedContext(
CallContext<AnyPointer, AnyPointer> typeless) {
return CallContext<Params, Results>(*typeless.hook);
}
Capability::Client Capability::Server::thisCap() {
return Client(thisHook->addRef());
}
template <typename T>
T ReaderCapabilityTable::imbue(T reader) {
return T(_::PointerHelpers<FromReader<T>>::getInternalReader(reader).imbue(this));
}
template <typename T>
T BuilderCapabilityTable::imbue(T builder) {
return T(_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::mv(builder)).imbue(this));
}
template <typename T>
typename T::Client CapabilityServerSet<T>::add(kj::Own<typename T::Server>&& server) {
void* ptr = reinterpret_cast<void*>(server.get());
// Clang insists that `castAs` is a template-dependent member and therefore we need the
// `template` keyword here, but AFAICT this is wrong: addImpl() is not a template.
return addInternal(kj::mv(server), ptr).template castAs<T>();
}
template <typename T>
kj::Promise<kj::Maybe<typename T::Server&>> CapabilityServerSet<T>::getLocalServer(
typename T::Client& client) {
return getLocalServerInternal(client)
.then([](void* server) -> kj::Maybe<typename T::Server&> {
if (server == nullptr) {
return nullptr;
} else {
return *reinterpret_cast<typename T::Server*>(server);
}
});
}
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::INTERFACE> {
static inline kj::Own<ClientHook> apply(typename T::Client t) {
return ClientHook::from(kj::mv(t));
}
};
} // namespace capnp
#endif // CAPNP_CAPABILITY_H_
+723
View File
@@ -0,0 +1,723 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This file contains types which are intended to help detect incorrect usage at compile
// time, but should then be optimized down to basic primitives (usually, integers) by the
// compiler.
#ifndef CAPNP_COMMON_H_
#define CAPNP_COMMON_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include <inttypes.h>
#include <kj/string.h>
#include <kj/memory.h>
#if CAPNP_DEBUG_TYPES
#include <kj/units.h>
#endif
namespace capnp {
#define CAPNP_VERSION_MAJOR 0
#define CAPNP_VERSION_MINOR 6
#define CAPNP_VERSION_MICRO 1
#define CAPNP_VERSION \
(CAPNP_VERSION_MAJOR * 1000000 + CAPNP_VERSION_MINOR * 1000 + CAPNP_VERSION_MICRO)
#ifndef CAPNP_LITE
#define CAPNP_LITE 0
#endif
typedef unsigned int uint;
struct Void {
// Type used for Void fields. Using C++'s "void" type creates a bunch of issues since it behaves
// differently from other types.
inline constexpr bool operator==(Void other) const { return true; }
inline constexpr bool operator!=(Void other) const { return false; }
};
static constexpr Void VOID = Void();
// Constant value for `Void`, which is an empty struct.
inline kj::StringPtr KJ_STRINGIFY(Void) { return "void"; }
struct Text;
struct Data;
enum class Kind: uint8_t {
PRIMITIVE,
BLOB,
ENUM,
STRUCT,
UNION,
INTERFACE,
LIST,
OTHER
// Some other type which is often a type parameter to Cap'n Proto templates, but which needs
// special handling. This includes types like AnyPointer, Dynamic*, etc.
};
enum class Style: uint8_t {
PRIMITIVE,
POINTER, // other than struct
STRUCT,
CAPABILITY
};
enum class ElementSize: uint8_t {
// Size of a list element.
VOID = 0,
BIT = 1,
BYTE = 2,
TWO_BYTES = 3,
FOUR_BYTES = 4,
EIGHT_BYTES = 5,
POINTER = 6,
INLINE_COMPOSITE = 7
};
enum class PointerType {
// Various wire types a pointer field can take
NULL_,
// Should be NULL, but that's #defined in stddef.h
STRUCT,
LIST,
CAPABILITY
};
namespace schemas {
template <typename T>
struct EnumInfo;
} // namespace schemas
namespace _ { // private
template <typename T, typename = void> struct Kind_;
template <> struct Kind_<Void> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<bool> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int8_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int16_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int32_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int64_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint8_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint16_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint32_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint64_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<float> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<double> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<Text> { static constexpr Kind kind = Kind::BLOB; };
template <> struct Kind_<Data> { static constexpr Kind kind = Kind::BLOB; };
template <typename T> struct Kind_<T, kj::VoidSfinae<typename T::_capnpPrivate::IsStruct>> {
static constexpr Kind kind = Kind::STRUCT;
};
template <typename T> struct Kind_<T, kj::VoidSfinae<typename T::_capnpPrivate::IsInterface>> {
static constexpr Kind kind = Kind::INTERFACE;
};
template <typename T> struct Kind_<T, kj::VoidSfinae<typename schemas::EnumInfo<T>::IsEnum>> {
static constexpr Kind kind = Kind::ENUM;
};
} // namespace _ (private)
template <typename T, Kind k = _::Kind_<T>::kind>
inline constexpr Kind kind() {
// This overload of kind() matches types which have a Kind_ specialization.
return k;
}
#if _MSC_VER
#define CAPNP_KIND(T) ::capnp::_::Kind_<T>::kind
// Avoid constexpr methods in MSVC (it remains buggy in many situations).
#else // _MSC_VER
#define CAPNP_KIND(T) ::capnp::kind<T>()
// Use this macro rather than kind<T>() in any code which must work in MSVC.
#endif // _MSC_VER, else
#if !CAPNP_LITE
template <typename T, Kind k = kind<T>()>
inline constexpr Style style() {
return k == Kind::PRIMITIVE || k == Kind::ENUM ? Style::PRIMITIVE
: k == Kind::STRUCT ? Style::STRUCT
: k == Kind::INTERFACE ? Style::CAPABILITY : Style::POINTER;
}
#endif // !CAPNP_LITE
template <typename T, Kind k = CAPNP_KIND(T)>
struct List;
#if _MSC_VER
template <typename T, Kind k>
struct List {};
// For some reason, without this declaration, MSVC will error out on some uses of List
// claiming that "T" -- as used in the default initializer for the second template param, "k" --
// is not defined. I do not understand this error, but adding this empty default declaration fixes
// it.
#endif
template <typename T> struct ListElementType_;
template <typename T> struct ListElementType_<List<T>> { typedef T Type; };
template <typename T> using ListElementType = typename ListElementType_<T>::Type;
namespace _ { // private
template <typename T, Kind k> struct Kind_<List<T, k>> {
static constexpr Kind kind = Kind::LIST;
};
} // namespace _ (private)
template <typename T, Kind k = CAPNP_KIND(T)> struct ReaderFor_ { typedef typename T::Reader Type; };
template <typename T> struct ReaderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct ReaderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> struct ReaderFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
template <typename T> using ReaderFor = typename ReaderFor_<T>::Type;
// The type returned by List<T>::Reader::operator[].
template <typename T, Kind k = CAPNP_KIND(T)> struct BuilderFor_ { typedef typename T::Builder Type; };
template <typename T> struct BuilderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct BuilderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> struct BuilderFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
template <typename T> using BuilderFor = typename BuilderFor_<T>::Type;
// The type returned by List<T>::Builder::operator[].
template <typename T, Kind k = CAPNP_KIND(T)> struct PipelineFor_ { typedef typename T::Pipeline Type;};
template <typename T> struct PipelineFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
template <typename T> using PipelineFor = typename PipelineFor_<T>::Type;
template <typename T, Kind k = CAPNP_KIND(T)> struct TypeIfEnum_;
template <typename T> struct TypeIfEnum_<T, Kind::ENUM> { typedef T Type; };
template <typename T>
using TypeIfEnum = typename TypeIfEnum_<kj::Decay<T>>::Type;
template <typename T>
using FromReader = typename kj::Decay<T>::Reads;
// FromReader<MyType::Reader> = MyType (for any Cap'n Proto type).
template <typename T>
using FromBuilder = typename kj::Decay<T>::Builds;
// FromBuilder<MyType::Builder> = MyType (for any Cap'n Proto type).
template <typename T>
using FromPipeline = typename kj::Decay<T>::Pipelines;
// FromBuilder<MyType::Pipeline> = MyType (for any Cap'n Proto type).
template <typename T>
using FromClient = typename kj::Decay<T>::Calls;
// FromReader<MyType::Client> = MyType (for any Cap'n Proto interface type).
template <typename T>
using FromServer = typename kj::Decay<T>::Serves;
// FromBuilder<MyType::Server> = MyType (for any Cap'n Proto interface type).
template <typename T, typename = void>
struct FromAny_;
template <typename T>
struct FromAny_<T, kj::VoidSfinae<FromReader<T>>> {
using Type = FromReader<T>;
};
template <typename T>
struct FromAny_<T, kj::VoidSfinae<FromBuilder<T>>> {
using Type = FromBuilder<T>;
};
template <typename T>
struct FromAny_<T, kj::VoidSfinae<FromPipeline<T>>> {
using Type = FromPipeline<T>;
};
// Note that T::Client is covered by FromReader
template <typename T>
struct FromAny_<kj::Own<T>, kj::VoidSfinae<FromServer<T>>> {
using Type = FromServer<T>;
};
template <typename T>
struct FromAny_<T,
kj::EnableIf<_::Kind_<T>::kind == Kind::PRIMITIVE || _::Kind_<T>::kind == Kind::ENUM>> {
// TODO(msvc): Ideally the EnableIf condition would be `style<T>() == Style::PRIMITIVE`, but MSVC
// cannot yet use style<T>() in this constexpr context.
using Type = kj::Decay<T>;
};
template <typename T>
using FromAny = typename FromAny_<T>::Type;
// Given any Cap'n Proto value type as an input, return the Cap'n Proto base type. That is:
//
// Foo::Reader -> Foo
// Foo::Builder -> Foo
// Foo::Pipeline -> Foo
// Foo::Client -> Foo
// Own<Foo::Server> -> Foo
// uint32_t -> uint32_t
namespace _ { // private
template <typename T, Kind k = CAPNP_KIND(T)>
struct PointerHelpers;
#if _MSC_VER
template <typename T, Kind k>
struct PointerHelpers {};
// For some reason, without this declaration, MSVC will error out on some uses of PointerHelpers
// claiming that "T" -- as used in the default initializer for the second template param, "k" --
// is not defined. I do not understand this error, but adding this empty default declaration fixes
// it.
#endif
} // namespace _ (private)
struct MessageSize {
// Size of a message. Every struct type has a method `.totalSize()` that returns this.
uint64_t wordCount;
uint capCount;
};
// =======================================================================================
// Raw memory types and measures
using kj::byte;
class word { uint64_t content KJ_UNUSED_MEMBER; KJ_DISALLOW_COPY(word); public: word() = default; };
// word is an opaque type with size of 64 bits. This type is useful only to make pointer
// arithmetic clearer. Since the contents are private, the only way to access them is to first
// reinterpret_cast to some other pointer type.
//
// Copying is disallowed because you should always use memcpy(). Otherwise, you may run afoul of
// aliasing rules.
//
// A pointer of type word* should always be word-aligned even if won't actually be dereferenced as
// that type.
static_assert(sizeof(byte) == 1, "uint8_t is not one byte?");
static_assert(sizeof(word) == 8, "uint64_t is not 8 bytes?");
#if CAPNP_DEBUG_TYPES
// Set CAPNP_DEBUG_TYPES to 1 to use kj::Quantity for "count" types. Otherwise, plain integers are
// used. All the code should still operate exactly the same, we just lose compile-time checking.
// Note that this will also change symbol names, so it's important that the library and any clients
// be compiled with the same setting here.
//
// We disable this by default to reduce symbol name size and avoid any possibility of the compiler
// failing to fully-optimize the types, but anyone modifying Cap'n Proto itself should enable this
// during development and testing.
namespace _ { class BitLabel; class ElementLabel; struct WirePointer; }
template <uint width, typename T = uint>
using BitCountN = kj::Quantity<kj::Bounded<kj::maxValueForBits<width>(), T>, _::BitLabel>;
template <uint width, typename T = uint>
using ByteCountN = kj::Quantity<kj::Bounded<kj::maxValueForBits<width>(), T>, byte>;
template <uint width, typename T = uint>
using WordCountN = kj::Quantity<kj::Bounded<kj::maxValueForBits<width>(), T>, word>;
template <uint width, typename T = uint>
using ElementCountN = kj::Quantity<kj::Bounded<kj::maxValueForBits<width>(), T>, _::ElementLabel>;
template <uint width, typename T = uint>
using WirePointerCountN = kj::Quantity<kj::Bounded<kj::maxValueForBits<width>(), T>, _::WirePointer>;
typedef BitCountN<8, uint8_t> BitCount8;
typedef BitCountN<16, uint16_t> BitCount16;
typedef BitCountN<32, uint32_t> BitCount32;
typedef BitCountN<64, uint64_t> BitCount64;
typedef BitCountN<sizeof(uint) * 8, uint> BitCount;
typedef ByteCountN<8, uint8_t> ByteCount8;
typedef ByteCountN<16, uint16_t> ByteCount16;
typedef ByteCountN<32, uint32_t> ByteCount32;
typedef ByteCountN<64, uint64_t> ByteCount64;
typedef ByteCountN<sizeof(uint) * 8, uint> ByteCount;
typedef WordCountN<8, uint8_t> WordCount8;
typedef WordCountN<16, uint16_t> WordCount16;
typedef WordCountN<32, uint32_t> WordCount32;
typedef WordCountN<64, uint64_t> WordCount64;
typedef WordCountN<sizeof(uint) * 8, uint> WordCount;
typedef ElementCountN<8, uint8_t> ElementCount8;
typedef ElementCountN<16, uint16_t> ElementCount16;
typedef ElementCountN<32, uint32_t> ElementCount32;
typedef ElementCountN<64, uint64_t> ElementCount64;
typedef ElementCountN<sizeof(uint) * 8, uint> ElementCount;
typedef WirePointerCountN<8, uint8_t> WirePointerCount8;
typedef WirePointerCountN<16, uint16_t> WirePointerCount16;
typedef WirePointerCountN<32, uint32_t> WirePointerCount32;
typedef WirePointerCountN<64, uint64_t> WirePointerCount64;
typedef WirePointerCountN<sizeof(uint) * 8, uint> WirePointerCount;
template <uint width>
using BitsPerElementN = decltype(BitCountN<width>() / ElementCountN<width>());
template <uint width>
using BytesPerElementN = decltype(ByteCountN<width>() / ElementCountN<width>());
template <uint width>
using WordsPerElementN = decltype(WordCountN<width>() / ElementCountN<width>());
template <uint width>
using PointersPerElementN = decltype(WirePointerCountN<width>() / ElementCountN<width>());
using kj::bounded;
using kj::unbound;
using kj::unboundAs;
using kj::unboundMax;
using kj::unboundMaxBits;
using kj::assertMax;
using kj::assertMaxBits;
using kj::upgradeBound;
using kj::ThrowOverflow;
using kj::assumeBits;
using kj::assumeMax;
using kj::subtractChecked;
using kj::trySubtract;
template <typename T, typename U>
inline constexpr U* operator+(U* ptr, kj::Quantity<T, U> offset) {
return ptr + unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr const U* operator+(const U* ptr, kj::Quantity<T, U> offset) {
return ptr + unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr U* operator+=(U*& ptr, kj::Quantity<T, U> offset) {
return ptr = ptr + unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr const U* operator+=(const U*& ptr, kj::Quantity<T, U> offset) {
return ptr = ptr + unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr U* operator-(U* ptr, kj::Quantity<T, U> offset) {
return ptr - unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr const U* operator-(const U* ptr, kj::Quantity<T, U> offset) {
return ptr - unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr U* operator-=(U*& ptr, kj::Quantity<T, U> offset) {
return ptr = ptr - unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
template <typename T, typename U>
inline constexpr const U* operator-=(const U*& ptr, kj::Quantity<T, U> offset) {
return ptr = ptr - unbound(offset / kj::unit<kj::Quantity<T, U>>());
}
constexpr auto BITS = kj::unit<BitCountN<1>>();
constexpr auto BYTES = kj::unit<ByteCountN<1>>();
constexpr auto WORDS = kj::unit<WordCountN<1>>();
constexpr auto ELEMENTS = kj::unit<ElementCountN<1>>();
constexpr auto POINTERS = kj::unit<WirePointerCountN<1>>();
constexpr auto ZERO = kj::bounded<0>();
constexpr auto ONE = kj::bounded<1>();
// GCC 4.7 actually gives unused warnings on these constants in opt mode...
constexpr auto BITS_PER_BYTE KJ_UNUSED = bounded<8>() * BITS / BYTES;
constexpr auto BITS_PER_WORD KJ_UNUSED = bounded<64>() * BITS / WORDS;
constexpr auto BYTES_PER_WORD KJ_UNUSED = bounded<8>() * BYTES / WORDS;
constexpr auto BITS_PER_POINTER KJ_UNUSED = bounded<64>() * BITS / POINTERS;
constexpr auto BYTES_PER_POINTER KJ_UNUSED = bounded<8>() * BYTES / POINTERS;
constexpr auto WORDS_PER_POINTER KJ_UNUSED = ONE * WORDS / POINTERS;
constexpr auto POINTER_SIZE_IN_WORDS = ONE * POINTERS * WORDS_PER_POINTER;
constexpr uint SEGMENT_WORD_COUNT_BITS = 29; // Number of words in a segment.
constexpr uint LIST_ELEMENT_COUNT_BITS = 29; // Number of elements in a list.
constexpr uint STRUCT_DATA_WORD_COUNT_BITS = 16; // Number of words in a Struct data section.
constexpr uint STRUCT_POINTER_COUNT_BITS = 16; // Number of pointers in a Struct pointer section.
constexpr uint BLOB_SIZE_BITS = 29; // Number of bytes in a blob.
typedef WordCountN<SEGMENT_WORD_COUNT_BITS> SegmentWordCount;
typedef ElementCountN<LIST_ELEMENT_COUNT_BITS> ListElementCount;
typedef WordCountN<STRUCT_DATA_WORD_COUNT_BITS, uint16_t> StructDataWordCount;
typedef WirePointerCountN<STRUCT_POINTER_COUNT_BITS, uint16_t> StructPointerCount;
typedef ByteCountN<BLOB_SIZE_BITS> BlobSize;
constexpr auto MAX_SEGMENT_WORDS =
bounded<kj::maxValueForBits<SEGMENT_WORD_COUNT_BITS>()>() * WORDS;
constexpr auto MAX_LIST_ELEMENTS =
bounded<kj::maxValueForBits<LIST_ELEMENT_COUNT_BITS>()>() * ELEMENTS;
constexpr auto MAX_STUCT_DATA_WORDS =
bounded<kj::maxValueForBits<STRUCT_DATA_WORD_COUNT_BITS>()>() * WORDS;
constexpr auto MAX_STRUCT_POINTER_COUNT =
bounded<kj::maxValueForBits<STRUCT_POINTER_COUNT_BITS>()>() * POINTERS;
using StructDataBitCount = decltype(WordCountN<STRUCT_POINTER_COUNT_BITS>() * BITS_PER_WORD);
// Number of bits in a Struct data segment (should come out to BitCountN<22>).
using StructDataOffset = decltype(StructDataBitCount() * (ONE * ELEMENTS / BITS));
using StructPointerOffset = StructPointerCount;
// Type of a field offset.
inline StructDataOffset assumeDataOffset(uint32_t offset) {
return assumeMax(MAX_STUCT_DATA_WORDS * BITS_PER_WORD * (ONE * ELEMENTS / BITS),
bounded(offset) * ELEMENTS);
}
inline StructPointerOffset assumePointerOffset(uint32_t offset) {
return assumeMax(MAX_STRUCT_POINTER_COUNT, bounded(offset) * POINTERS);
}
constexpr uint MAX_TEXT_SIZE = kj::maxValueForBits<BLOB_SIZE_BITS>() - 1;
typedef kj::Quantity<kj::Bounded<MAX_TEXT_SIZE, uint>, byte> TextSize;
// Not including NUL terminator.
template <typename T>
inline KJ_CONSTEXPR() decltype(bounded<sizeof(T)>() * BYTES / ELEMENTS) bytesPerElement() {
return bounded<sizeof(T)>() * BYTES / ELEMENTS;
}
template <typename T>
inline KJ_CONSTEXPR() decltype(bounded<sizeof(T) * 8>() * BITS / ELEMENTS) bitsPerElement() {
return bounded<sizeof(T) * 8>() * BITS / ELEMENTS;
}
template <typename T, uint maxN>
inline constexpr kj::Quantity<kj::Bounded<maxN, size_t>, T>
intervalLength(const T* a, const T* b, kj::Quantity<kj::BoundedConst<maxN>, T>) {
return kj::assumeMax<maxN>(b - a) * kj::unit<kj::Quantity<kj::BoundedConst<1u>, T>>();
}
template <typename T, typename U>
inline constexpr kj::ArrayPtr<const U> arrayPtr(const U* ptr, kj::Quantity<T, U> size) {
return kj::ArrayPtr<const U>(ptr, unbound(size / kj::unit<kj::Quantity<T, U>>()));
}
template <typename T, typename U>
inline constexpr kj::ArrayPtr<U> arrayPtr(U* ptr, kj::Quantity<T, U> size) {
return kj::ArrayPtr<U>(ptr, unbound(size / kj::unit<kj::Quantity<T, U>>()));
}
#else
template <uint width, typename T = uint>
using BitCountN = T;
template <uint width, typename T = uint>
using ByteCountN = T;
template <uint width, typename T = uint>
using WordCountN = T;
template <uint width, typename T = uint>
using ElementCountN = T;
template <uint width, typename T = uint>
using WirePointerCountN = T;
// XXX
typedef BitCountN<8, uint8_t> BitCount8;
typedef BitCountN<16, uint16_t> BitCount16;
typedef BitCountN<32, uint32_t> BitCount32;
typedef BitCountN<64, uint64_t> BitCount64;
typedef BitCountN<sizeof(uint) * 8, uint> BitCount;
typedef ByteCountN<8, uint8_t> ByteCount8;
typedef ByteCountN<16, uint16_t> ByteCount16;
typedef ByteCountN<32, uint32_t> ByteCount32;
typedef ByteCountN<64, uint64_t> ByteCount64;
typedef ByteCountN<sizeof(uint) * 8, uint> ByteCount;
typedef WordCountN<8, uint8_t> WordCount8;
typedef WordCountN<16, uint16_t> WordCount16;
typedef WordCountN<32, uint32_t> WordCount32;
typedef WordCountN<64, uint64_t> WordCount64;
typedef WordCountN<sizeof(uint) * 8, uint> WordCount;
typedef ElementCountN<8, uint8_t> ElementCount8;
typedef ElementCountN<16, uint16_t> ElementCount16;
typedef ElementCountN<32, uint32_t> ElementCount32;
typedef ElementCountN<64, uint64_t> ElementCount64;
typedef ElementCountN<sizeof(uint) * 8, uint> ElementCount;
typedef WirePointerCountN<8, uint8_t> WirePointerCount8;
typedef WirePointerCountN<16, uint16_t> WirePointerCount16;
typedef WirePointerCountN<32, uint32_t> WirePointerCount32;
typedef WirePointerCountN<64, uint64_t> WirePointerCount64;
typedef WirePointerCountN<sizeof(uint) * 8, uint> WirePointerCount;
template <uint width>
using BitsPerElementN = decltype(BitCountN<width>() / ElementCountN<width>());
template <uint width>
using BytesPerElementN = decltype(ByteCountN<width>() / ElementCountN<width>());
template <uint width>
using WordsPerElementN = decltype(WordCountN<width>() / ElementCountN<width>());
template <uint width>
using PointersPerElementN = decltype(WirePointerCountN<width>() / ElementCountN<width>());
using kj::ThrowOverflow;
// YYY
template <uint i> inline constexpr uint bounded() { return i; }
template <typename T> inline constexpr T bounded(T i) { return i; }
template <typename T> inline constexpr T unbound(T i) { return i; }
template <typename T, typename U> inline constexpr T unboundAs(U i) { return i; }
template <uint64_t requestedMax, typename T> inline constexpr uint unboundMax(T i) { return i; }
template <uint bits, typename T> inline constexpr uint unboundMaxBits(T i) { return i; }
template <uint newMax, typename T, typename ErrorFunc>
inline T assertMax(T value, ErrorFunc&& func) {
if (KJ_UNLIKELY(value > newMax)) func();
return value;
}
template <typename T, typename ErrorFunc>
inline T assertMax(uint newMax, T value, ErrorFunc&& func) {
if (KJ_UNLIKELY(value > newMax)) func();
return value;
}
template <uint bits, typename T, typename ErrorFunc = ThrowOverflow>
inline T assertMaxBits(T value, ErrorFunc&& func = ErrorFunc()) {
if (KJ_UNLIKELY(value > kj::maxValueForBits<bits>())) func();
return value;
}
template <typename T, typename ErrorFunc = ThrowOverflow>
inline T assertMaxBits(uint bits, T value, ErrorFunc&& func = ErrorFunc()) {
if (KJ_UNLIKELY(value > (1ull << bits) - 1)) func();
return value;
}
template <typename T, typename U> inline constexpr T upgradeBound(U i) { return i; }
template <uint bits, typename T> inline constexpr T assumeBits(T i) { return i; }
template <uint64_t max, typename T> inline constexpr T assumeMax(T i) { return i; }
template <typename T, typename U, typename ErrorFunc = ThrowOverflow>
inline auto subtractChecked(T a, U b, ErrorFunc&& errorFunc = ErrorFunc())
-> decltype(a - b) {
if (b > a) errorFunc();
return a - b;
}
template <typename T, typename U>
inline auto trySubtract(T a, U b) -> kj::Maybe<decltype(a - b)> {
if (b > a) {
return nullptr;
} else {
return a - b;
}
}
constexpr uint BITS = 1;
constexpr uint BYTES = 1;
constexpr uint WORDS = 1;
constexpr uint ELEMENTS = 1;
constexpr uint POINTERS = 1;
constexpr uint ZERO = 0;
constexpr uint ONE = 1;
// GCC 4.7 actually gives unused warnings on these constants in opt mode...
constexpr uint BITS_PER_BYTE KJ_UNUSED = 8;
constexpr uint BITS_PER_WORD KJ_UNUSED = 64;
constexpr uint BYTES_PER_WORD KJ_UNUSED = 8;
constexpr uint BITS_PER_POINTER KJ_UNUSED = 64;
constexpr uint BYTES_PER_POINTER KJ_UNUSED = 8;
constexpr uint WORDS_PER_POINTER KJ_UNUSED = 1;
// XXX
constexpr uint POINTER_SIZE_IN_WORDS = ONE * POINTERS * WORDS_PER_POINTER;
constexpr uint SEGMENT_WORD_COUNT_BITS = 29; // Number of words in a segment.
constexpr uint LIST_ELEMENT_COUNT_BITS = 29; // Number of elements in a list.
constexpr uint STRUCT_DATA_WORD_COUNT_BITS = 16; // Number of words in a Struct data section.
constexpr uint STRUCT_POINTER_COUNT_BITS = 16; // Number of pointers in a Struct pointer section.
constexpr uint BLOB_SIZE_BITS = 29; // Number of bytes in a blob.
typedef WordCountN<SEGMENT_WORD_COUNT_BITS> SegmentWordCount;
typedef ElementCountN<LIST_ELEMENT_COUNT_BITS> ListElementCount;
typedef WordCountN<STRUCT_DATA_WORD_COUNT_BITS, uint16_t> StructDataWordCount;
typedef WirePointerCountN<STRUCT_POINTER_COUNT_BITS, uint16_t> StructPointerCount;
typedef ByteCountN<BLOB_SIZE_BITS> BlobSize;
// YYY
constexpr auto MAX_SEGMENT_WORDS = kj::maxValueForBits<SEGMENT_WORD_COUNT_BITS>();
constexpr auto MAX_LIST_ELEMENTS = kj::maxValueForBits<LIST_ELEMENT_COUNT_BITS>();
constexpr auto MAX_STUCT_DATA_WORDS = kj::maxValueForBits<STRUCT_DATA_WORD_COUNT_BITS>();
constexpr auto MAX_STRUCT_POINTER_COUNT = kj::maxValueForBits<STRUCT_POINTER_COUNT_BITS>();
typedef uint StructDataBitCount;
typedef uint StructDataOffset;
typedef uint StructPointerOffset;
inline StructDataOffset assumeDataOffset(uint32_t offset) { return offset; }
inline StructPointerOffset assumePointerOffset(uint32_t offset) { return offset; }
constexpr uint MAX_TEXT_SIZE = kj::maxValueForBits<BLOB_SIZE_BITS>() - 1;
typedef uint TextSize;
template <typename T>
inline KJ_CONSTEXPR() size_t bytesPerElement() { return sizeof(T); }
template <typename T>
inline KJ_CONSTEXPR() size_t bitsPerElement() { return sizeof(T) * 8; }
template <typename T>
inline constexpr ptrdiff_t intervalLength(const T* a, const T* b, uint) {
return b - a;
}
template <typename T, typename U>
inline constexpr kj::ArrayPtr<const U> arrayPtr(const U* ptr, T size) {
return kj::arrayPtr(ptr, size);
}
template <typename T, typename U>
inline constexpr kj::ArrayPtr<U> arrayPtr(U* ptr, T size) {
return kj::arrayPtr(ptr, size);
}
#endif
} // namespace capnp
#endif // CAPNP_COMMON_H_
@@ -0,0 +1,860 @@
// Generated by Cap'n Proto compiler, DO NOT EDIT
// source: json.capnp
#ifndef CAPNP_INCLUDED_8ef99297a43a5e34_
#define CAPNP_INCLUDED_8ef99297a43a5e34_
#include <capnp/generated-header-support.h>
#if !CAPNP_LITE
#include <capnp/capability.h>
#endif // !CAPNP_LITE
#if CAPNP_VERSION != 6001
#error "Version mismatch between generated code and library headers. You must use the same version of the Cap'n Proto compiler and library."
#endif
namespace capnp {
namespace schemas {
CAPNP_DECLARE_SCHEMA(8825ffaa852cda72);
CAPNP_DECLARE_SCHEMA(c27855d853a937cc);
CAPNP_DECLARE_SCHEMA(9bbf84153dd4bb60);
} // namespace schemas
} // namespace capnp
namespace capnp {
struct JsonValue {
JsonValue() = delete;
class Reader;
class Builder;
class Pipeline;
enum Which: uint16_t {
NULL_,
BOOLEAN,
NUMBER,
STRING,
ARRAY,
OBJECT,
CALL,
};
struct Field;
struct Call;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(8825ffaa852cda72, 2, 1)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct JsonValue::Field {
Field() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(c27855d853a937cc, 0, 2)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct JsonValue::Call {
Call() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(9bbf84153dd4bb60, 0, 2)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
// =======================================================================================
class JsonValue::Reader {
public:
typedef JsonValue Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline Which which() const;
inline bool isNull() const;
inline ::capnp::Void getNull() const;
inline bool isBoolean() const;
inline bool getBoolean() const;
inline bool isNumber() const;
inline double getNumber() const;
inline bool isString() const;
inline bool hasString() const;
inline ::capnp::Text::Reader getString() const;
inline bool isArray() const;
inline bool hasArray() const;
inline ::capnp::List< ::capnp::JsonValue>::Reader getArray() const;
inline bool isObject() const;
inline bool hasObject() const;
inline ::capnp::List< ::capnp::JsonValue::Field>::Reader getObject() const;
inline bool isCall() const;
inline bool hasCall() const;
inline ::capnp::JsonValue::Call::Reader getCall() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class JsonValue::Builder {
public:
typedef JsonValue Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline Which which();
inline bool isNull();
inline ::capnp::Void getNull();
inline void setNull( ::capnp::Void value = ::capnp::VOID);
inline bool isBoolean();
inline bool getBoolean();
inline void setBoolean(bool value);
inline bool isNumber();
inline double getNumber();
inline void setNumber(double value);
inline bool isString();
inline bool hasString();
inline ::capnp::Text::Builder getString();
inline void setString( ::capnp::Text::Reader value);
inline ::capnp::Text::Builder initString(unsigned int size);
inline void adoptString(::capnp::Orphan< ::capnp::Text>&& value);
inline ::capnp::Orphan< ::capnp::Text> disownString();
inline bool isArray();
inline bool hasArray();
inline ::capnp::List< ::capnp::JsonValue>::Builder getArray();
inline void setArray( ::capnp::List< ::capnp::JsonValue>::Reader value);
inline ::capnp::List< ::capnp::JsonValue>::Builder initArray(unsigned int size);
inline void adoptArray(::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>>&& value);
inline ::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>> disownArray();
inline bool isObject();
inline bool hasObject();
inline ::capnp::List< ::capnp::JsonValue::Field>::Builder getObject();
inline void setObject( ::capnp::List< ::capnp::JsonValue::Field>::Reader value);
inline ::capnp::List< ::capnp::JsonValue::Field>::Builder initObject(unsigned int size);
inline void adoptObject(::capnp::Orphan< ::capnp::List< ::capnp::JsonValue::Field>>&& value);
inline ::capnp::Orphan< ::capnp::List< ::capnp::JsonValue::Field>> disownObject();
inline bool isCall();
inline bool hasCall();
inline ::capnp::JsonValue::Call::Builder getCall();
inline void setCall( ::capnp::JsonValue::Call::Reader value);
inline ::capnp::JsonValue::Call::Builder initCall();
inline void adoptCall(::capnp::Orphan< ::capnp::JsonValue::Call>&& value);
inline ::capnp::Orphan< ::capnp::JsonValue::Call> disownCall();
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class JsonValue::Pipeline {
public:
typedef JsonValue Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class JsonValue::Field::Reader {
public:
typedef Field Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline bool hasName() const;
inline ::capnp::Text::Reader getName() const;
inline bool hasValue() const;
inline ::capnp::JsonValue::Reader getValue() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class JsonValue::Field::Builder {
public:
typedef Field Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline bool hasName();
inline ::capnp::Text::Builder getName();
inline void setName( ::capnp::Text::Reader value);
inline ::capnp::Text::Builder initName(unsigned int size);
inline void adoptName(::capnp::Orphan< ::capnp::Text>&& value);
inline ::capnp::Orphan< ::capnp::Text> disownName();
inline bool hasValue();
inline ::capnp::JsonValue::Builder getValue();
inline void setValue( ::capnp::JsonValue::Reader value);
inline ::capnp::JsonValue::Builder initValue();
inline void adoptValue(::capnp::Orphan< ::capnp::JsonValue>&& value);
inline ::capnp::Orphan< ::capnp::JsonValue> disownValue();
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class JsonValue::Field::Pipeline {
public:
typedef Field Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
inline ::capnp::JsonValue::Pipeline getValue();
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class JsonValue::Call::Reader {
public:
typedef Call Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline bool hasFunction() const;
inline ::capnp::Text::Reader getFunction() const;
inline bool hasParams() const;
inline ::capnp::List< ::capnp::JsonValue>::Reader getParams() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class JsonValue::Call::Builder {
public:
typedef Call Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline bool hasFunction();
inline ::capnp::Text::Builder getFunction();
inline void setFunction( ::capnp::Text::Reader value);
inline ::capnp::Text::Builder initFunction(unsigned int size);
inline void adoptFunction(::capnp::Orphan< ::capnp::Text>&& value);
inline ::capnp::Orphan< ::capnp::Text> disownFunction();
inline bool hasParams();
inline ::capnp::List< ::capnp::JsonValue>::Builder getParams();
inline void setParams( ::capnp::List< ::capnp::JsonValue>::Reader value);
inline ::capnp::List< ::capnp::JsonValue>::Builder initParams(unsigned int size);
inline void adoptParams(::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>>&& value);
inline ::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>> disownParams();
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class JsonValue::Call::Pipeline {
public:
typedef Call Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
// =======================================================================================
inline ::capnp::JsonValue::Which JsonValue::Reader::which() const {
return _reader.getDataField<Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline ::capnp::JsonValue::Which JsonValue::Builder::which() {
return _builder.getDataField<Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline bool JsonValue::Reader::isNull() const {
return which() == JsonValue::NULL_;
}
inline bool JsonValue::Builder::isNull() {
return which() == JsonValue::NULL_;
}
inline ::capnp::Void JsonValue::Reader::getNull() const {
KJ_IREQUIRE((which() == JsonValue::NULL_),
"Must check which() before get()ing a union member.");
return _reader.getDataField< ::capnp::Void>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline ::capnp::Void JsonValue::Builder::getNull() {
KJ_IREQUIRE((which() == JsonValue::NULL_),
"Must check which() before get()ing a union member.");
return _builder.getDataField< ::capnp::Void>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline void JsonValue::Builder::setNull( ::capnp::Void value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::NULL_);
_builder.setDataField< ::capnp::Void>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline bool JsonValue::Reader::isBoolean() const {
return which() == JsonValue::BOOLEAN;
}
inline bool JsonValue::Builder::isBoolean() {
return which() == JsonValue::BOOLEAN;
}
inline bool JsonValue::Reader::getBoolean() const {
KJ_IREQUIRE((which() == JsonValue::BOOLEAN),
"Must check which() before get()ing a union member.");
return _reader.getDataField<bool>(
::capnp::bounded<16>() * ::capnp::ELEMENTS);
}
inline bool JsonValue::Builder::getBoolean() {
KJ_IREQUIRE((which() == JsonValue::BOOLEAN),
"Must check which() before get()ing a union member.");
return _builder.getDataField<bool>(
::capnp::bounded<16>() * ::capnp::ELEMENTS);
}
inline void JsonValue::Builder::setBoolean(bool value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::BOOLEAN);
_builder.setDataField<bool>(
::capnp::bounded<16>() * ::capnp::ELEMENTS, value);
}
inline bool JsonValue::Reader::isNumber() const {
return which() == JsonValue::NUMBER;
}
inline bool JsonValue::Builder::isNumber() {
return which() == JsonValue::NUMBER;
}
inline double JsonValue::Reader::getNumber() const {
KJ_IREQUIRE((which() == JsonValue::NUMBER),
"Must check which() before get()ing a union member.");
return _reader.getDataField<double>(
::capnp::bounded<1>() * ::capnp::ELEMENTS);
}
inline double JsonValue::Builder::getNumber() {
KJ_IREQUIRE((which() == JsonValue::NUMBER),
"Must check which() before get()ing a union member.");
return _builder.getDataField<double>(
::capnp::bounded<1>() * ::capnp::ELEMENTS);
}
inline void JsonValue::Builder::setNumber(double value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::NUMBER);
_builder.setDataField<double>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
}
inline bool JsonValue::Reader::isString() const {
return which() == JsonValue::STRING;
}
inline bool JsonValue::Builder::isString() {
return which() == JsonValue::STRING;
}
inline bool JsonValue::Reader::hasString() const {
if (which() != JsonValue::STRING) return false;
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Builder::hasString() {
if (which() != JsonValue::STRING) return false;
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::Text::Reader JsonValue::Reader::getString() const {
KJ_IREQUIRE((which() == JsonValue::STRING),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::Text::Builder JsonValue::Builder::getString() {
KJ_IREQUIRE((which() == JsonValue::STRING),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Builder::setString( ::capnp::Text::Reader value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::STRING);
::capnp::_::PointerHelpers< ::capnp::Text>::set(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), value);
}
inline ::capnp::Text::Builder JsonValue::Builder::initString(unsigned int size) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::STRING);
return ::capnp::_::PointerHelpers< ::capnp::Text>::init(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), size);
}
inline void JsonValue::Builder::adoptString(
::capnp::Orphan< ::capnp::Text>&& value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::STRING);
::capnp::_::PointerHelpers< ::capnp::Text>::adopt(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::Text> JsonValue::Builder::disownString() {
KJ_IREQUIRE((which() == JsonValue::STRING),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::Text>::disown(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline bool JsonValue::Reader::isArray() const {
return which() == JsonValue::ARRAY;
}
inline bool JsonValue::Builder::isArray() {
return which() == JsonValue::ARRAY;
}
inline bool JsonValue::Reader::hasArray() const {
if (which() != JsonValue::ARRAY) return false;
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Builder::hasArray() {
if (which() != JsonValue::ARRAY) return false;
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::List< ::capnp::JsonValue>::Reader JsonValue::Reader::getArray() const {
KJ_IREQUIRE((which() == JsonValue::ARRAY),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::get(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::List< ::capnp::JsonValue>::Builder JsonValue::Builder::getArray() {
KJ_IREQUIRE((which() == JsonValue::ARRAY),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::get(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Builder::setArray( ::capnp::List< ::capnp::JsonValue>::Reader value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::ARRAY);
::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::set(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), value);
}
inline ::capnp::List< ::capnp::JsonValue>::Builder JsonValue::Builder::initArray(unsigned int size) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::ARRAY);
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::init(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), size);
}
inline void JsonValue::Builder::adoptArray(
::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>>&& value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::ARRAY);
::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::adopt(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>> JsonValue::Builder::disownArray() {
KJ_IREQUIRE((which() == JsonValue::ARRAY),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::disown(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline bool JsonValue::Reader::isObject() const {
return which() == JsonValue::OBJECT;
}
inline bool JsonValue::Builder::isObject() {
return which() == JsonValue::OBJECT;
}
inline bool JsonValue::Reader::hasObject() const {
if (which() != JsonValue::OBJECT) return false;
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Builder::hasObject() {
if (which() != JsonValue::OBJECT) return false;
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::List< ::capnp::JsonValue::Field>::Reader JsonValue::Reader::getObject() const {
KJ_IREQUIRE((which() == JsonValue::OBJECT),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue::Field>>::get(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::List< ::capnp::JsonValue::Field>::Builder JsonValue::Builder::getObject() {
KJ_IREQUIRE((which() == JsonValue::OBJECT),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue::Field>>::get(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Builder::setObject( ::capnp::List< ::capnp::JsonValue::Field>::Reader value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::OBJECT);
::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue::Field>>::set(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), value);
}
inline ::capnp::List< ::capnp::JsonValue::Field>::Builder JsonValue::Builder::initObject(unsigned int size) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::OBJECT);
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue::Field>>::init(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), size);
}
inline void JsonValue::Builder::adoptObject(
::capnp::Orphan< ::capnp::List< ::capnp::JsonValue::Field>>&& value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::OBJECT);
::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue::Field>>::adopt(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::List< ::capnp::JsonValue::Field>> JsonValue::Builder::disownObject() {
KJ_IREQUIRE((which() == JsonValue::OBJECT),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue::Field>>::disown(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline bool JsonValue::Reader::isCall() const {
return which() == JsonValue::CALL;
}
inline bool JsonValue::Builder::isCall() {
return which() == JsonValue::CALL;
}
inline bool JsonValue::Reader::hasCall() const {
if (which() != JsonValue::CALL) return false;
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Builder::hasCall() {
if (which() != JsonValue::CALL) return false;
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::JsonValue::Call::Reader JsonValue::Reader::getCall() const {
KJ_IREQUIRE((which() == JsonValue::CALL),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::JsonValue::Call>::get(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::JsonValue::Call::Builder JsonValue::Builder::getCall() {
KJ_IREQUIRE((which() == JsonValue::CALL),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::JsonValue::Call>::get(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Builder::setCall( ::capnp::JsonValue::Call::Reader value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::CALL);
::capnp::_::PointerHelpers< ::capnp::JsonValue::Call>::set(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), value);
}
inline ::capnp::JsonValue::Call::Builder JsonValue::Builder::initCall() {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::CALL);
return ::capnp::_::PointerHelpers< ::capnp::JsonValue::Call>::init(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Builder::adoptCall(
::capnp::Orphan< ::capnp::JsonValue::Call>&& value) {
_builder.setDataField<JsonValue::Which>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, JsonValue::CALL);
::capnp::_::PointerHelpers< ::capnp::JsonValue::Call>::adopt(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::JsonValue::Call> JsonValue::Builder::disownCall() {
KJ_IREQUIRE((which() == JsonValue::CALL),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::capnp::JsonValue::Call>::disown(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline bool JsonValue::Field::Reader::hasName() const {
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Field::Builder::hasName() {
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::Text::Reader JsonValue::Field::Reader::getName() const {
return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::Text::Builder JsonValue::Field::Builder::getName() {
return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Field::Builder::setName( ::capnp::Text::Reader value) {
::capnp::_::PointerHelpers< ::capnp::Text>::set(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), value);
}
inline ::capnp::Text::Builder JsonValue::Field::Builder::initName(unsigned int size) {
return ::capnp::_::PointerHelpers< ::capnp::Text>::init(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), size);
}
inline void JsonValue::Field::Builder::adoptName(
::capnp::Orphan< ::capnp::Text>&& value) {
::capnp::_::PointerHelpers< ::capnp::Text>::adopt(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::Text> JsonValue::Field::Builder::disownName() {
return ::capnp::_::PointerHelpers< ::capnp::Text>::disown(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline bool JsonValue::Field::Reader::hasValue() const {
return !_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Field::Builder::hasValue() {
return !_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::JsonValue::Reader JsonValue::Field::Reader::getValue() const {
return ::capnp::_::PointerHelpers< ::capnp::JsonValue>::get(_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline ::capnp::JsonValue::Builder JsonValue::Field::Builder::getValue() {
return ::capnp::_::PointerHelpers< ::capnp::JsonValue>::get(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
#if !CAPNP_LITE
inline ::capnp::JsonValue::Pipeline JsonValue::Field::Pipeline::getValue() {
return ::capnp::JsonValue::Pipeline(_typeless.getPointerField(1));
}
#endif // !CAPNP_LITE
inline void JsonValue::Field::Builder::setValue( ::capnp::JsonValue::Reader value) {
::capnp::_::PointerHelpers< ::capnp::JsonValue>::set(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), value);
}
inline ::capnp::JsonValue::Builder JsonValue::Field::Builder::initValue() {
return ::capnp::_::PointerHelpers< ::capnp::JsonValue>::init(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline void JsonValue::Field::Builder::adoptValue(
::capnp::Orphan< ::capnp::JsonValue>&& value) {
::capnp::_::PointerHelpers< ::capnp::JsonValue>::adopt(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::JsonValue> JsonValue::Field::Builder::disownValue() {
return ::capnp::_::PointerHelpers< ::capnp::JsonValue>::disown(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline bool JsonValue::Call::Reader::hasFunction() const {
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Call::Builder::hasFunction() {
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::Text::Reader JsonValue::Call::Reader::getFunction() const {
return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::Text::Builder JsonValue::Call::Builder::getFunction() {
return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline void JsonValue::Call::Builder::setFunction( ::capnp::Text::Reader value) {
::capnp::_::PointerHelpers< ::capnp::Text>::set(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), value);
}
inline ::capnp::Text::Builder JsonValue::Call::Builder::initFunction(unsigned int size) {
return ::capnp::_::PointerHelpers< ::capnp::Text>::init(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), size);
}
inline void JsonValue::Call::Builder::adoptFunction(
::capnp::Orphan< ::capnp::Text>&& value) {
::capnp::_::PointerHelpers< ::capnp::Text>::adopt(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::Text> JsonValue::Call::Builder::disownFunction() {
return ::capnp::_::PointerHelpers< ::capnp::Text>::disown(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline bool JsonValue::Call::Reader::hasParams() const {
return !_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline bool JsonValue::Call::Builder::hasParams() {
return !_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::List< ::capnp::JsonValue>::Reader JsonValue::Call::Reader::getParams() const {
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::get(_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline ::capnp::List< ::capnp::JsonValue>::Builder JsonValue::Call::Builder::getParams() {
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::get(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline void JsonValue::Call::Builder::setParams( ::capnp::List< ::capnp::JsonValue>::Reader value) {
::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::set(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), value);
}
inline ::capnp::List< ::capnp::JsonValue>::Builder JsonValue::Call::Builder::initParams(unsigned int size) {
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::init(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), size);
}
inline void JsonValue::Call::Builder::adoptParams(
::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>>&& value) {
::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::adopt(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::capnp::List< ::capnp::JsonValue>> JsonValue::Call::Builder::disownParams() {
return ::capnp::_::PointerHelpers< ::capnp::List< ::capnp::JsonValue>>::disown(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
} // namespace
#endif // CAPNP_INCLUDED_8ef99297a43a5e34_
@@ -0,0 +1,462 @@
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_COMPAT_JSON_H_
#define CAPNP_COMPAT_JSON_H_
#include <capnp/schema.h>
#include <capnp/dynamic.h>
#include <capnp/compat/json.capnp.h>
namespace capnp {
class JsonCodec {
// Flexible class for encoding Cap'n Proto types as JSON, and decoding JSON back to Cap'n Proto.
//
// Typical usage:
//
// JsonCodec json;
//
// // encode
// kj::String encoded = json.encode(someStructReader);
//
// // decode
// json.decode(encoded, someStructBuilder);
//
// Advanced users can do fancy things like override the way certain types or fields are
// represented in JSON by registering handlers. See the unit test for an example.
//
// Notes:
// - When encoding, all primitive fields are always encoded, even if default-valued. Pointer
// fields are only encoded if they are non-null.
// - 64-bit integers are encoded as strings, since JSON "numbers" are double-precision floating
// points which cannot store a 64-bit integer without losing data.
// - NaNs and infinite floating point numbers are not allowed by the JSON spec, and so are encoded
// as null. This matches the behavior of `JSON.stringify` in at least Firefox and Chrome.
// - Data is encoded as an array of numbers in the range [0,255]. You probably want to register
// a handler that does something better, like maybe base64 encoding, but there are a zillion
// different ways people do this.
// - Encoding/decoding capabilities and AnyPointers requires registering a Handler, since there's
// no obvious default behavior.
// - When decoding, unrecognized field names are ignored. Note: This means that JSON is NOT a
// good format for receiving input from a human. Consider `capnp eval` or the SchemaParser
// library for human input.
public:
JsonCodec();
~JsonCodec() noexcept(false);
// ---------------------------------------------------------------------------
// standard API
void setPrettyPrint(bool enabled);
// Enable to insert newlines, indentation, and other extra spacing into the output. The default
// is to use minimal whitespace.
void setMaxNestingDepth(size_t maxNestingDepth);
// Set maximum nesting depth when decoding JSON to prevent highly nested input from overflowing
// the call stack. The default is 64.
template <typename T>
kj::String encode(T&& value);
// Encode any Cap'n Proto value to JSON, including primitives and
// Dynamic{Enum,Struct,List,Capability}, but not DynamicValue (see below).
kj::String encode(DynamicValue::Reader value, Type type) const;
// Encode a DynamicValue to JSON. `type` is needed because `DynamicValue` itself does
// not distinguish between e.g. int32 and int64, which in JSON are handled differently. Most
// of the time, though, you can use the single-argument templated version of `encode()` instead.
void decode(kj::ArrayPtr<const char> input, DynamicStruct::Builder output) const;
// Decode JSON text directly into a struct builder. This only works for structs since lists
// need to be allocated with the correct size in advance.
//
// (Remember that any Cap'n Proto struct reader type can be implicitly cast to
// DynamicStruct::Reader.)
template <typename T>
Orphan<T> decode(kj::ArrayPtr<const char> input, Orphanage orphanage) const;
// Decode JSON text to any Cap'n Proto object (pointer value), allocated using the given
// orphanage. T must be specified explicitly and cannot be dynamic, e.g.:
//
// Orphan<MyType> orphan = json.decode<MyType>(text, orphanage);
template <typename T>
ReaderFor<T> decode(kj::ArrayPtr<const char> input) const;
// Decode JSON text into a primitive or capability value. T must be specified explicitly and
// cannot be dynamic, e.g.:
//
// uint32_t n = json.decode<uint32_t>(text);
Orphan<DynamicValue> decode(kj::ArrayPtr<const char> input, Type type, Orphanage orphanage) const;
Orphan<DynamicList> decode(
kj::ArrayPtr<const char> input, ListSchema type, Orphanage orphanage) const;
Orphan<DynamicStruct> decode(
kj::ArrayPtr<const char> input, StructSchema type, Orphanage orphanage) const;
DynamicCapability::Client decode(kj::ArrayPtr<const char> input, InterfaceSchema type) const;
DynamicEnum decode(kj::ArrayPtr<const char> input, EnumSchema type) const;
// Decode to a dynamic value, specifying the type schema.
// ---------------------------------------------------------------------------
// layered API
//
// You can separate text <-> JsonValue from JsonValue <-> T. These are particularly useful
// for calling from Handler implementations.
kj::String encodeRaw(JsonValue::Reader value) const;
void decodeRaw(kj::ArrayPtr<const char> input, JsonValue::Builder output) const;
// Translate JsonValue <-> text.
template <typename T>
void encode(T&& value, JsonValue::Builder output);
void encode(DynamicValue::Reader input, Type type, JsonValue::Builder output) const;
void decode(JsonValue::Reader input, DynamicStruct::Builder output) const;
template <typename T>
Orphan<T> decode(JsonValue::Reader input, Orphanage orphanage) const;
template <typename T>
ReaderFor<T> decode(JsonValue::Reader input) const;
Orphan<DynamicValue> decode(JsonValue::Reader input, Type type, Orphanage orphanage) const;
Orphan<DynamicList> decode(JsonValue::Reader input, ListSchema type, Orphanage orphanage) const;
Orphan<DynamicStruct> decode(
JsonValue::Reader input, StructSchema type, Orphanage orphanage) const;
DynamicCapability::Client decode(JsonValue::Reader input, InterfaceSchema type) const;
DynamicEnum decode(JsonValue::Reader input, EnumSchema type) const;
// ---------------------------------------------------------------------------
// specializing particular types
template <typename T, Style s = style<T>()>
class Handler;
// Implement this interface to specify a special encoding for a particular type or field.
//
// The templates are a bit ugly, but subclasses of this type essentially implement two methods,
// one to encode values of this type and one to decode values of this type. `encode()` is simple:
//
// void encode(const JsonCodec& codec, ReaderFor<T> input, JsonValue::Builder output) const;
//
// `decode()` is a bit trickier. When T is a struct (including DynamicStruct), it is:
//
// void decode(const JsonCodec& codec, JsonValue::Reader input, BuilderFor<T> output) const;
//
// However, when T is a primitive, decode() is:
//
// T decode(const JsonCodec& codec, JsonValue::Reader input) const;
//
// Or when T is any non-struct object (list, blob), decode() is:
//
// Orphan<T> decode(const JsonCodec& codec, JsonValue::Reader input, Orphanage orphanage) const;
//
// Or when T is an interface:
//
// T::Client decode(const JsonCodec& codec, JsonValue::Reader input) const;
//
// Additionally, when T is a struct you can *optionally* also implement the orphan-returning form
// of decode(), but it will only be called when the struct would be allocated as an individual
// object, not as part of a list. This allows you to return "nullptr" in these cases to say that
// the pointer value should be null. This does not apply to list elements because struct list
// elements cannot ever be null (since Cap'n Proto encodes struct lists as a flat list rather
// than list-of-pointers).
template <typename T>
void addTypeHandler(Handler<T>& handler);
void addTypeHandler(Type type, Handler<DynamicValue>& handler);
void addTypeHandler(EnumSchema type, Handler<DynamicEnum>& handler);
void addTypeHandler(StructSchema type, Handler<DynamicStruct>& handler);
void addTypeHandler(ListSchema type, Handler<DynamicList>& handler);
void addTypeHandler(InterfaceSchema type, Handler<DynamicCapability>& handler);
// Arrange that whenever the type T appears in the message, your handler will be used to
// encode/decode it.
//
// Note that if you register a handler for a capability type, it will also apply to subtypes.
// Thus Handler<Capability> handles all capabilities.
template <typename T>
void addFieldHandler(StructSchema::Field field, Handler<T>& handler);
// Matches only the specific field. T can be a dynamic type. T must match the field's type.
private:
class HandlerBase;
struct Impl;
kj::Own<Impl> impl;
void encodeField(StructSchema::Field field, DynamicValue::Reader input,
JsonValue::Builder output) const;
void decodeArray(List<JsonValue>::Reader input, DynamicList::Builder output) const;
void decodeObject(List<JsonValue::Field>::Reader input, DynamicStruct::Builder output) const;
void addTypeHandlerImpl(Type type, HandlerBase& handler);
void addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler);
};
// =======================================================================================
// inline implementation details
template <typename T>
kj::String JsonCodec::encode(T&& value) {
typedef FromAny<kj::Decay<T>> Base;
return encode(DynamicValue::Reader(ReaderFor<Base>(kj::fwd<T>(value))), Type::from<Base>());
}
template <typename T>
inline Orphan<T> JsonCodec::decode(kj::ArrayPtr<const char> input, Orphanage orphanage) const {
return decode(input, Type::from<T>(), orphanage).template releaseAs<T>();
}
template <typename T>
inline ReaderFor<T> JsonCodec::decode(kj::ArrayPtr<const char> input) const {
static_assert(style<T>() == Style::PRIMITIVE || style<T>() == Style::CAPABILITY,
"must specify an orphanage to decode an object type");
return decode(input, Type::from<T>(), Orphanage()).getReader().template as<T>();
}
inline Orphan<DynamicList> JsonCodec::decode(
kj::ArrayPtr<const char> input, ListSchema type, Orphanage orphanage) const {
return decode(input, Type(type), orphanage).releaseAs<DynamicList>();
}
inline Orphan<DynamicStruct> JsonCodec::decode(
kj::ArrayPtr<const char> input, StructSchema type, Orphanage orphanage) const {
return decode(input, Type(type), orphanage).releaseAs<DynamicStruct>();
}
inline DynamicCapability::Client JsonCodec::decode(
kj::ArrayPtr<const char> input, InterfaceSchema type) const {
return decode(input, Type(type), Orphanage()).getReader().as<DynamicCapability>();
}
inline DynamicEnum JsonCodec::decode(kj::ArrayPtr<const char> input, EnumSchema type) const {
return decode(input, Type(type), Orphanage()).getReader().as<DynamicEnum>();
}
// -----------------------------------------------------------------------------
template <typename T>
void JsonCodec::encode(T&& value, JsonValue::Builder output) {
typedef FromAny<kj::Decay<T>> Base;
encode(DynamicValue::Reader(ReaderFor<Base>(kj::fwd<T>(value))), Type::from<Base>(), output);
}
template <typename T>
inline Orphan<T> JsonCodec::decode(JsonValue::Reader input, Orphanage orphanage) const {
return decode(input, Type::from<T>(), orphanage).template releaseAs<T>();
}
template <typename T>
inline ReaderFor<T> JsonCodec::decode(JsonValue::Reader input) const {
static_assert(style<T>() == Style::PRIMITIVE || style<T>() == Style::CAPABILITY,
"must specify an orphanage to decode an object type");
return decode(input, Type::from<T>(), Orphanage()).getReader().template as<T>();
}
inline Orphan<DynamicList> JsonCodec::decode(
JsonValue::Reader input, ListSchema type, Orphanage orphanage) const {
return decode(input, Type(type), orphanage).releaseAs<DynamicList>();
}
inline Orphan<DynamicStruct> JsonCodec::decode(
JsonValue::Reader input, StructSchema type, Orphanage orphanage) const {
return decode(input, Type(type), orphanage).releaseAs<DynamicStruct>();
}
inline DynamicCapability::Client JsonCodec::decode(
JsonValue::Reader input, InterfaceSchema type) const {
return decode(input, Type(type), Orphanage()).getReader().as<DynamicCapability>();
}
inline DynamicEnum JsonCodec::decode(JsonValue::Reader input, EnumSchema type) const {
return decode(input, Type(type), Orphanage()).getReader().as<DynamicEnum>();
}
// -----------------------------------------------------------------------------
class JsonCodec::HandlerBase {
// Internal helper; ignore.
public:
virtual void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
JsonValue::Builder output) const = 0;
virtual Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
Type type, Orphanage orphanage) const;
virtual void decodeStructBase(const JsonCodec& codec, JsonValue::Reader input,
DynamicStruct::Builder output) const;
};
template <typename T>
class JsonCodec::Handler<T, Style::POINTER>: private JsonCodec::HandlerBase {
public:
virtual void encode(const JsonCodec& codec, ReaderFor<T> input,
JsonValue::Builder output) const = 0;
virtual Orphan<T> decode(const JsonCodec& codec, JsonValue::Reader input,
Orphanage orphanage) const = 0;
private:
void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
JsonValue::Builder output) const override final {
encode(codec, input.as<T>(), output);
}
Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
Type type, Orphanage orphanage) const override final {
return decode(codec, input, orphanage);
}
friend class JsonCodec;
};
template <typename T>
class JsonCodec::Handler<T, Style::STRUCT>: private JsonCodec::HandlerBase {
public:
virtual void encode(const JsonCodec& codec, ReaderFor<T> input,
JsonValue::Builder output) const = 0;
virtual void decode(const JsonCodec& codec, JsonValue::Reader input,
BuilderFor<T> output) const = 0;
virtual Orphan<T> decode(const JsonCodec& codec, JsonValue::Reader input,
Orphanage orphanage) const {
// If subclass does not override, fall back to regular version.
auto result = orphanage.newOrphan<T>();
decode(codec, input, result.get());
return result;
}
private:
void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
JsonValue::Builder output) const override final {
encode(codec, input.as<T>(), output);
}
Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
Type type, Orphanage orphanage) const override final {
return decode(codec, input, orphanage);
}
void decodeStructBase(const JsonCodec& codec, JsonValue::Reader input,
DynamicStruct::Builder output) const override final {
decode(codec, input, output.as<T>());
}
friend class JsonCodec;
};
template <>
class JsonCodec::Handler<DynamicStruct>: private JsonCodec::HandlerBase {
// Almost identical to Style::STRUCT except that we pass the struct type to decode().
public:
virtual void encode(const JsonCodec& codec, DynamicStruct::Reader input,
JsonValue::Builder output) const = 0;
virtual void decode(const JsonCodec& codec, JsonValue::Reader input,
DynamicStruct::Builder output) const = 0;
virtual Orphan<DynamicStruct> decode(const JsonCodec& codec, JsonValue::Reader input,
StructSchema type, Orphanage orphanage) const {
// If subclass does not override, fall back to regular version.
auto result = orphanage.newOrphan(type);
decode(codec, input, result.get());
return result;
}
private:
void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
JsonValue::Builder output) const override final {
encode(codec, input.as<DynamicStruct>(), output);
}
Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
Type type, Orphanage orphanage) const override final {
return decode(codec, input, type.asStruct(), orphanage);
}
void decodeStructBase(const JsonCodec& codec, JsonValue::Reader input,
DynamicStruct::Builder output) const override final {
decode(codec, input, output.as<DynamicStruct>());
}
friend class JsonCodec;
};
template <typename T>
class JsonCodec::Handler<T, Style::PRIMITIVE>: private JsonCodec::HandlerBase {
public:
virtual void encode(const JsonCodec& codec, T input, JsonValue::Builder output) const = 0;
virtual T decode(const JsonCodec& codec, JsonValue::Reader input) const = 0;
private:
void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
JsonValue::Builder output) const override final {
encode(codec, input.as<T>(), output);
}
Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
Type type, Orphanage orphanage) const override final {
return decode(codec, input);
}
friend class JsonCodec;
};
template <typename T>
class JsonCodec::Handler<T, Style::CAPABILITY>: private JsonCodec::HandlerBase {
public:
virtual void encode(const JsonCodec& codec, typename T::Client input,
JsonValue::Builder output) const = 0;
virtual typename T::Client decode(const JsonCodec& codec, JsonValue::Reader input) const = 0;
private:
void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
JsonValue::Builder output) const override final {
encode(codec, input.as<T>(), output);
}
Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
Type type, Orphanage orphanage) const override final {
return orphanage.newOrphanCopy(decode(codec, input));
}
friend class JsonCodec;
};
template <typename T>
inline void JsonCodec::addTypeHandler(Handler<T>& handler) {
addTypeHandlerImpl(Type::from<T>(), handler);
}
inline void JsonCodec::addTypeHandler(Type type, Handler<DynamicValue>& handler) {
addTypeHandlerImpl(type, handler);
}
inline void JsonCodec::addTypeHandler(EnumSchema type, Handler<DynamicEnum>& handler) {
addTypeHandlerImpl(type, handler);
}
inline void JsonCodec::addTypeHandler(StructSchema type, Handler<DynamicStruct>& handler) {
addTypeHandlerImpl(type, handler);
}
inline void JsonCodec::addTypeHandler(ListSchema type, Handler<DynamicList>& handler) {
addTypeHandlerImpl(type, handler);
}
inline void JsonCodec::addTypeHandler(InterfaceSchema type, Handler<DynamicCapability>& handler) {
addTypeHandlerImpl(type, handler);
}
template <typename T>
inline void JsonCodec::addFieldHandler(StructSchema::Field field, Handler<T>& handler) {
addFieldHandlerImpl(field, Type::from<T>(), handler);
}
template <> void JsonCodec::addTypeHandler(Handler<DynamicValue>& handler)
KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
"try specifying a specific type schema as the first parameter");
template <> void JsonCodec::addTypeHandler(Handler<DynamicEnum>& handler)
KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
"try specifying a specific type schema as the first parameter");
template <> void JsonCodec::addTypeHandler(Handler<DynamicStruct>& handler)
KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
"try specifying a specific type schema as the first parameter");
template <> void JsonCodec::addTypeHandler(Handler<DynamicList>& handler)
KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
"try specifying a specific type schema as the first parameter");
template <> void JsonCodec::addTypeHandler(Handler<DynamicCapability>& handler)
KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
"try specifying a specific type schema as the first parameter");
// TODO(someday): Implement support for registering handlers that cover thinsg like "all structs"
// or "all lists". Currently you can only target a specific struct or list type.
} // namespace capnp
#endif // CAPNP_COMPAT_JSON_H_
File diff suppressed because it is too large Load Diff
+309
View File
@@ -0,0 +1,309 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_ENDIAN_H_
#define CAPNP_ENDIAN_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "common.h"
#include <inttypes.h>
#include <string.h> // memcpy
namespace capnp {
namespace _ { // private
// WireValue
//
// Wraps a primitive value as it appears on the wire. Namely, values are little-endian on the
// wire, because little-endian is the most common endianness in modern CPUs.
//
// Note: In general, code that depends cares about byte ordering is bad. See:
// http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
// Cap'n Proto is special because it is essentially doing compiler-like things, fussing over
// allocation and layout of memory, in order to squeeze out every last drop of performance.
#if _MSC_VER
// Assume Windows is little-endian.
//
// TODO(msvc): This is ugly. Maybe refactor later checks to be based on CAPNP_BYTE_ORDER or
// CAPNP_SWAP_BYTES or something, and define that in turn based on _MSC_VER or the GCC
// intrinsics.
#ifndef __ORDER_BIG_ENDIAN__
#define __ORDER_BIG_ENDIAN__ 4321
#endif
#ifndef __ORDER_LITTLE_ENDIAN__
#define __ORDER_LITTLE_ENDIAN__ 1234
#endif
#ifndef __BYTE_ORDER__
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#endif
#endif
#if CAPNP_REVERSE_ENDIAN
#define CAPNP_WIRE_BYTE_ORDER __ORDER_BIG_ENDIAN__
#define CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER __ORDER_LITTLE_ENDIAN__
#else
#define CAPNP_WIRE_BYTE_ORDER __ORDER_LITTLE_ENDIAN__
#define CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER __ORDER_BIG_ENDIAN__
#endif
#if defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == CAPNP_WIRE_BYTE_ORDER && \
!CAPNP_DISABLE_ENDIAN_DETECTION
// CPU is little-endian. We can just read/write the memory directly.
template <typename T>
class DirectWireValue {
public:
KJ_ALWAYS_INLINE(T get() const) { return value; }
KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
private:
T value;
};
template <typename T>
using WireValue = DirectWireValue<T>;
// To prevent ODR problems when endian-test, endian-reverse-test, and endian-fallback-test are
// linked together, we define each implementation with a different name and define an alias to the
// one we want to use.
#elif defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER && \
defined(__GNUC__) && !CAPNP_DISABLE_ENDIAN_DETECTION
// Big-endian, but GCC's __builtin_bswap() is available.
// TODO(perf): Use dedicated instructions to read little-endian data on big-endian CPUs that have
// them.
// TODO(perf): Verify that this code optimizes reasonably. In particular, ensure that the
// compiler optimizes away the memcpy()s and keeps everything in registers.
template <typename T, size_t size = sizeof(T)>
class SwappingWireValue;
template <typename T>
class SwappingWireValue<T, 1> {
public:
KJ_ALWAYS_INLINE(T get() const) { return value; }
KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
private:
T value;
};
template <typename T>
class SwappingWireValue<T, 2> {
public:
KJ_ALWAYS_INLINE(T get() const) {
// Not all platforms have __builtin_bswap16() for some reason. In particular, it is missing
// on gcc-4.7.3-cygwin32 (but present on gcc-4.8.1-cygwin64).
uint16_t swapped = (value << 8) | (value >> 8);
T result;
memcpy(&result, &swapped, sizeof(T));
return result;
}
KJ_ALWAYS_INLINE(void set(T newValue)) {
uint16_t raw;
memcpy(&raw, &newValue, sizeof(T));
// Not all platforms have __builtin_bswap16() for some reason. In particular, it is missing
// on gcc-4.7.3-cygwin32 (but present on gcc-4.8.1-cygwin64).
value = (raw << 8) | (raw >> 8);
}
private:
uint16_t value;
};
template <typename T>
class SwappingWireValue<T, 4> {
public:
KJ_ALWAYS_INLINE(T get() const) {
uint32_t swapped = __builtin_bswap32(value);
T result;
memcpy(&result, &swapped, sizeof(T));
return result;
}
KJ_ALWAYS_INLINE(void set(T newValue)) {
uint32_t raw;
memcpy(&raw, &newValue, sizeof(T));
value = __builtin_bswap32(raw);
}
private:
uint32_t value;
};
template <typename T>
class SwappingWireValue<T, 8> {
public:
KJ_ALWAYS_INLINE(T get() const) {
uint64_t swapped = __builtin_bswap64(value);
T result;
memcpy(&result, &swapped, sizeof(T));
return result;
}
KJ_ALWAYS_INLINE(void set(T newValue)) {
uint64_t raw;
memcpy(&raw, &newValue, sizeof(T));
value = __builtin_bswap64(raw);
}
private:
uint64_t value;
};
template <typename T>
using WireValue = SwappingWireValue<T>;
// To prevent ODR problems when endian-test, endian-reverse-test, and endian-fallback-test are
// linked together, we define each implementation with a different name and define an alias to the
// one we want to use.
#else
// Unknown endianness. Fall back to bit shifts.
#if !CAPNP_DISABLE_ENDIAN_DETECTION
#if _MSC_VER
#pragma message("Couldn't detect endianness of your platform. Using unoptimized fallback implementation.")
#pragma message("Consider changing this code to detect your platform and send us a patch!")
#else
#warning "Couldn't detect endianness of your platform. Using unoptimized fallback implementation."
#warning "Consider changing this code to detect your platform and send us a patch!"
#endif
#endif // !CAPNP_DISABLE_ENDIAN_DETECTION
template <typename T, size_t size = sizeof(T)>
class ShiftingWireValue;
template <typename T>
class ShiftingWireValue<T, 1> {
public:
KJ_ALWAYS_INLINE(T get() const) { return value; }
KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
private:
T value;
};
template <typename T>
class ShiftingWireValue<T, 2> {
public:
KJ_ALWAYS_INLINE(T get() const) {
uint16_t raw = (static_cast<uint16_t>(bytes[0]) ) |
(static_cast<uint16_t>(bytes[1]) << 8);
T result;
memcpy(&result, &raw, sizeof(T));
return result;
}
KJ_ALWAYS_INLINE(void set(T newValue)) {
uint16_t raw;
memcpy(&raw, &newValue, sizeof(T));
bytes[0] = raw;
bytes[1] = raw >> 8;
}
private:
union {
byte bytes[2];
uint16_t align;
};
};
template <typename T>
class ShiftingWireValue<T, 4> {
public:
KJ_ALWAYS_INLINE(T get() const) {
uint32_t raw = (static_cast<uint32_t>(bytes[0]) ) |
(static_cast<uint32_t>(bytes[1]) << 8) |
(static_cast<uint32_t>(bytes[2]) << 16) |
(static_cast<uint32_t>(bytes[3]) << 24);
T result;
memcpy(&result, &raw, sizeof(T));
return result;
}
KJ_ALWAYS_INLINE(void set(T newValue)) {
uint32_t raw;
memcpy(&raw, &newValue, sizeof(T));
bytes[0] = raw;
bytes[1] = raw >> 8;
bytes[2] = raw >> 16;
bytes[3] = raw >> 24;
}
private:
union {
byte bytes[4];
uint32_t align;
};
};
template <typename T>
class ShiftingWireValue<T, 8> {
public:
KJ_ALWAYS_INLINE(T get() const) {
uint64_t raw = (static_cast<uint64_t>(bytes[0]) ) |
(static_cast<uint64_t>(bytes[1]) << 8) |
(static_cast<uint64_t>(bytes[2]) << 16) |
(static_cast<uint64_t>(bytes[3]) << 24) |
(static_cast<uint64_t>(bytes[4]) << 32) |
(static_cast<uint64_t>(bytes[5]) << 40) |
(static_cast<uint64_t>(bytes[6]) << 48) |
(static_cast<uint64_t>(bytes[7]) << 56);
T result;
memcpy(&result, &raw, sizeof(T));
return result;
}
KJ_ALWAYS_INLINE(void set(T newValue)) {
uint64_t raw;
memcpy(&raw, &newValue, sizeof(T));
bytes[0] = raw;
bytes[1] = raw >> 8;
bytes[2] = raw >> 16;
bytes[3] = raw >> 24;
bytes[4] = raw >> 32;
bytes[5] = raw >> 40;
bytes[6] = raw >> 48;
bytes[7] = raw >> 56;
}
private:
union {
byte bytes[8];
uint64_t align;
};
};
template <typename T>
using WireValue = ShiftingWireValue<T>;
// To prevent ODR problems when endian-test, endian-reverse-test, and endian-fallback-test are
// linked together, we define each implementation with a different name and define an alias to the
// one we want to use.
#endif
} // namespace _ (private)
} // namespace capnp
#endif // CAPNP_ENDIAN_H_
+254
View File
@@ -0,0 +1,254 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_EZ_RPC_H_
#define CAPNP_EZ_RPC_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "rpc.h"
#include "message.h"
struct sockaddr;
namespace kj { class AsyncIoProvider; class LowLevelAsyncIoProvider; }
namespace capnp {
class EzRpcContext;
class EzRpcClient {
// Super-simple interface for setting up a Cap'n Proto RPC client. Example:
//
// # Cap'n Proto schema
// interface Adder {
// add @0 (left :Int32, right :Int32) -> (value :Int32);
// }
//
// // C++ client
// int main() {
// capnp::EzRpcClient client("localhost:3456");
// Adder::Client adder = client.getMain<Adder>();
// auto request = adder.addRequest();
// request.setLeft(12);
// request.setRight(34);
// auto response = request.send().wait(client.getWaitScope());
// assert(response.getValue() == 46);
// return 0;
// }
//
// // C++ server
// class AdderImpl final: public Adder::Server {
// public:
// kj::Promise<void> add(AddContext context) override {
// auto params = context.getParams();
// context.getResults().setValue(params.getLeft() + params.getRight());
// return kj::READY_NOW;
// }
// };
//
// int main() {
// capnp::EzRpcServer server(kj::heap<AdderImpl>(), "*:3456");
// kj::NEVER_DONE.wait(server.getWaitScope());
// }
//
// This interface is easy, but it hides a lot of useful features available from the lower-level
// classes:
// - The server can only export a small set of public, singleton capabilities under well-known
// string names. This is fine for transient services where no state needs to be kept between
// connections, but hides the power of Cap'n Proto when it comes to long-lived resources.
// - EzRpcClient/EzRpcServer automatically set up a `kj::EventLoop` and make it current for the
// thread. Only one `kj::EventLoop` can exist per thread, so you cannot use these interfaces
// if you wish to set up your own event loop. (However, you can safely create multiple
// EzRpcClient / EzRpcServer objects in a single thread; they will make sure to make no more
// than one EventLoop.)
// - These classes only support simple two-party connections, not multilateral VatNetworks.
// - These classes only support communication over a raw, unencrypted socket. If you want to
// build on an abstract stream (perhaps one which supports encryption), you must use the
// lower-level interfaces.
//
// Some of these restrictions will probably be lifted in future versions, but some things will
// always require using the low-level interfaces directly. If you are interested in working
// at a lower level, start by looking at these interfaces:
// - `kj::setupAsyncIo()` in `kj/async-io.h`.
// - `RpcSystem` in `capnp/rpc.h`.
// - `TwoPartyVatNetwork` in `capnp/rpc-twoparty.h`.
public:
explicit EzRpcClient(kj::StringPtr serverAddress, uint defaultPort = 0,
ReaderOptions readerOpts = ReaderOptions());
// Construct a new EzRpcClient and connect to the given address. The connection is formed in
// the background -- if it fails, calls to capabilities returned by importCap() will fail with an
// appropriate exception.
//
// `defaultPort` is the IP port number to use if `serverAddress` does not include it explicitly.
// If unspecified, the port is required in `serverAddress`.
//
// The address is parsed by `kj::Network` in `kj/async-io.h`. See that interface for more info
// on the address format, but basically it's what you'd expect.
//
// `readerOpts` is the ReaderOptions structure used to read each incoming message on the
// connection. Setting this may be necessary if you need to receive very large individual
// messages or messages. However, it is recommended that you instead think about how to change
// your protocol to send large data blobs in multiple small chunks -- this is much better for
// both security and performance. See `ReaderOptions` in `message.h` for more details.
EzRpcClient(const struct sockaddr* serverAddress, uint addrSize,
ReaderOptions readerOpts = ReaderOptions());
// Like the above constructor, but connects to an already-resolved socket address. Any address
// format supported by `kj::Network` in `kj/async-io.h` is accepted.
explicit EzRpcClient(int socketFd, ReaderOptions readerOpts = ReaderOptions());
// Create a client on top of an already-connected socket.
// `readerOpts` acts as in the first constructor.
~EzRpcClient() noexcept(false);
template <typename Type>
typename Type::Client getMain();
Capability::Client getMain();
// Get the server's main (aka "bootstrap") interface.
template <typename Type>
typename Type::Client importCap(kj::StringPtr name)
KJ_DEPRECATED("Change your server to export a main interface, then use getMain() instead.");
Capability::Client importCap(kj::StringPtr name)
KJ_DEPRECATED("Change your server to export a main interface, then use getMain() instead.");
// ** DEPRECATED **
//
// Ask the sever for the capability with the given name. You may specify a type to automatically
// down-cast to that type. It is up to you to specify the correct expected type.
//
// Named interfaces are deprecated. The new preferred usage pattern is for the server to export
// a "main" interface which itself has methods for getting any other interfaces.
kj::WaitScope& getWaitScope();
// Get the `WaitScope` for the client's `EventLoop`, which allows you to synchronously wait on
// promises.
kj::AsyncIoProvider& getIoProvider();
// Get the underlying AsyncIoProvider set up by the RPC system. This is useful if you want
// to do some non-RPC I/O in asynchronous fashion.
kj::LowLevelAsyncIoProvider& getLowLevelIoProvider();
// Get the underlying LowLevelAsyncIoProvider set up by the RPC system. This is useful if you
// want to do some non-RPC I/O in asynchronous fashion.
private:
struct Impl;
kj::Own<Impl> impl;
};
class EzRpcServer {
// The server counterpart to `EzRpcClient`. See `EzRpcClient` for an example.
public:
explicit EzRpcServer(Capability::Client mainInterface, kj::StringPtr bindAddress,
uint defaultPort = 0, ReaderOptions readerOpts = ReaderOptions());
// Construct a new `EzRpcServer` that binds to the given address. An address of "*" means to
// bind to all local addresses.
//
// `defaultPort` is the IP port number to use if `serverAddress` does not include it explicitly.
// If unspecified, a port is chosen automatically, and you must call getPort() to find out what
// it is.
//
// The address is parsed by `kj::Network` in `kj/async-io.h`. See that interface for more info
// on the address format, but basically it's what you'd expect.
//
// The server might not begin listening immediately, especially if `bindAddress` needs to be
// resolved. If you need to wait until the server is definitely up, wait on the promise returned
// by `getPort()`.
//
// `readerOpts` is the ReaderOptions structure used to read each incoming message on the
// connection. Setting this may be necessary if you need to receive very large individual
// messages or messages. However, it is recommended that you instead think about how to change
// your protocol to send large data blobs in multiple small chunks -- this is much better for
// both security and performance. See `ReaderOptions` in `message.h` for more details.
EzRpcServer(Capability::Client mainInterface, struct sockaddr* bindAddress, uint addrSize,
ReaderOptions readerOpts = ReaderOptions());
// Like the above constructor, but binds to an already-resolved socket address. Any address
// format supported by `kj::Network` in `kj/async-io.h` is accepted.
EzRpcServer(Capability::Client mainInterface, int socketFd, uint port,
ReaderOptions readerOpts = ReaderOptions());
// Create a server on top of an already-listening socket (i.e. one on which accept() may be
// called). `port` is returned by `getPort()` -- it serves no other purpose.
// `readerOpts` acts as in the other two above constructors.
explicit EzRpcServer(kj::StringPtr bindAddress, uint defaultPort = 0,
ReaderOptions readerOpts = ReaderOptions())
KJ_DEPRECATED("Please specify a main interface for your server.");
EzRpcServer(struct sockaddr* bindAddress, uint addrSize,
ReaderOptions readerOpts = ReaderOptions())
KJ_DEPRECATED("Please specify a main interface for your server.");
EzRpcServer(int socketFd, uint port, ReaderOptions readerOpts = ReaderOptions())
KJ_DEPRECATED("Please specify a main interface for your server.");
~EzRpcServer() noexcept(false);
void exportCap(kj::StringPtr name, Capability::Client cap);
// Export a capability publicly under the given name, so that clients can import it.
//
// Keep in mind that you can implicitly convert `kj::Own<MyType::Server>&&` to
// `Capability::Client`, so it's typical to pass something like
// `kj::heap<MyImplementation>(<constructor params>)` as the second parameter.
kj::Promise<uint> getPort();
// Get the IP port number on which this server is listening. This promise won't resolve until
// the server is actually listening. If the address was not an IP address (e.g. it was a Unix
// domain socket) then getPort() resolves to zero.
kj::WaitScope& getWaitScope();
// Get the `WaitScope` for the client's `EventLoop`, which allows you to synchronously wait on
// promises.
kj::AsyncIoProvider& getIoProvider();
// Get the underlying AsyncIoProvider set up by the RPC system. This is useful if you want
// to do some non-RPC I/O in asynchronous fashion.
kj::LowLevelAsyncIoProvider& getLowLevelIoProvider();
// Get the underlying LowLevelAsyncIoProvider set up by the RPC system. This is useful if you
// want to do some non-RPC I/O in asynchronous fashion.
private:
struct Impl;
kj::Own<Impl> impl;
};
// =======================================================================================
// inline implementation details
template <typename Type>
inline typename Type::Client EzRpcClient::getMain() {
return getMain().castAs<Type>();
}
template <typename Type>
inline typename Type::Client EzRpcClient::importCap(kj::StringPtr name) {
return importCap(name).castAs<Type>();
}
} // namespace capnp
#endif // CAPNP_EZ_RPC_H_
@@ -0,0 +1,407 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This file is included from all generated headers.
#ifndef CAPNP_GENERATED_HEADER_SUPPORT_H_
#define CAPNP_GENERATED_HEADER_SUPPORT_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "raw-schema.h"
#include "layout.h"
#include "list.h"
#include "orphan.h"
#include "pointer-helpers.h"
#include "any.h"
#include <kj/string.h>
#include <kj/string-tree.h>
namespace capnp {
class MessageBuilder; // So that it can be declared a friend.
template <typename T, Kind k = CAPNP_KIND(T)>
struct ToDynamic_; // Defined in dynamic.h, needs to be declared as everyone's friend.
struct DynamicStruct; // So that it can be declared a friend.
struct Capability; // To declare brandBindingFor<Capability>()
namespace _ { // private
#if !CAPNP_LITE
template <typename T, typename CapnpPrivate = typename T::_capnpPrivate, bool = false>
inline const RawSchema& rawSchema() {
return *CapnpPrivate::schema;
}
template <typename T, uint64_t id = schemas::EnumInfo<T>::typeId>
inline const RawSchema& rawSchema() {
return *schemas::EnumInfo<T>::schema;
}
template <typename T, typename CapnpPrivate = typename T::_capnpPrivate>
inline const RawBrandedSchema& rawBrandedSchema() {
return *CapnpPrivate::brand();
}
template <typename T, uint64_t id = schemas::EnumInfo<T>::typeId>
inline const RawBrandedSchema& rawBrandedSchema() {
return schemas::EnumInfo<T>::schema->defaultBrand;
}
template <typename TypeTag, typename... Params>
struct ChooseBrand;
// If all of `Params` are `AnyPointer`, return the type's default brand. Otherwise, return a
// specific brand instance. TypeTag is the _capnpPrivate struct for the type in question.
template <typename TypeTag>
struct ChooseBrand<TypeTag> {
// All params were AnyPointer. No specific brand needed.
static constexpr _::RawBrandedSchema const* brand() { return &TypeTag::schema->defaultBrand; }
};
template <typename TypeTag, typename... Rest>
struct ChooseBrand<TypeTag, AnyPointer, Rest...>: public ChooseBrand<TypeTag, Rest...> {};
// The first parameter is AnyPointer, so recurse to check the rest.
template <typename TypeTag, typename First, typename... Rest>
struct ChooseBrand<TypeTag, First, Rest...> {
// At least one parameter is not AnyPointer, so use the specificBrand constant.
static constexpr _::RawBrandedSchema const* brand() { return &TypeTag::specificBrand; }
};
template <typename T, Kind k = kind<T>()>
struct BrandBindingFor_;
#define HANDLE_TYPE(Type, which) \
template <> \
struct BrandBindingFor_<Type, Kind::PRIMITIVE> { \
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) { \
return { which, listDepth, nullptr }; \
} \
}
HANDLE_TYPE(Void, 0);
HANDLE_TYPE(bool, 1);
HANDLE_TYPE(int8_t, 2);
HANDLE_TYPE(int16_t, 3);
HANDLE_TYPE(int32_t, 4);
HANDLE_TYPE(int64_t, 5);
HANDLE_TYPE(uint8_t, 6);
HANDLE_TYPE(uint16_t, 7);
HANDLE_TYPE(uint32_t, 8);
HANDLE_TYPE(uint64_t, 9);
HANDLE_TYPE(float, 10);
HANDLE_TYPE(double, 11);
#undef HANDLE_TYPE
template <>
struct BrandBindingFor_<Text, Kind::BLOB> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 12, listDepth, nullptr };
}
};
template <>
struct BrandBindingFor_<Data, Kind::BLOB> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 13, listDepth, nullptr };
}
};
template <typename T>
struct BrandBindingFor_<List<T>, Kind::LIST> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return BrandBindingFor_<T>::get(listDepth + 1);
}
};
template <typename T>
struct BrandBindingFor_<T, Kind::ENUM> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 15, listDepth, nullptr };
}
};
template <typename T>
struct BrandBindingFor_<T, Kind::STRUCT> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 16, listDepth, T::_capnpPrivate::brand() };
}
};
template <typename T>
struct BrandBindingFor_<T, Kind::INTERFACE> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 17, listDepth, T::_capnpPrivate::brand() };
}
};
template <>
struct BrandBindingFor_<AnyPointer, Kind::OTHER> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 18, listDepth, 0, 0 };
}
};
template <>
struct BrandBindingFor_<AnyStruct, Kind::OTHER> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 18, listDepth, 0, 1 };
}
};
template <>
struct BrandBindingFor_<AnyList, Kind::OTHER> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 18, listDepth, 0, 2 };
}
};
template <>
struct BrandBindingFor_<Capability, Kind::OTHER> {
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
return { 18, listDepth, 0, 3 };
}
};
template <typename T>
constexpr RawBrandedSchema::Binding brandBindingFor() {
return BrandBindingFor_<T>::get(0);
}
kj::StringTree structString(StructReader reader, const RawBrandedSchema& schema);
kj::String enumString(uint16_t value, const RawBrandedSchema& schema);
// Declared here so that we can declare inline stringify methods on generated types.
// Defined in stringify.c++, which depends on dynamic.c++, which is allowed not to be linked in.
template <typename T>
inline kj::StringTree structString(StructReader reader) {
return structString(reader, rawBrandedSchema<T>());
}
template <typename T>
inline kj::String enumString(T value) {
return enumString(static_cast<uint16_t>(value), rawBrandedSchema<T>());
}
#endif // !CAPNP_LITE
// TODO(cleanup): Unify ConstStruct and ConstList.
template <typename T>
class ConstStruct {
public:
ConstStruct() = delete;
KJ_DISALLOW_COPY(ConstStruct);
inline explicit constexpr ConstStruct(const word* ptr): ptr(ptr) {}
inline typename T::Reader get() const {
return AnyPointer::Reader(PointerReader::getRootUnchecked(ptr)).getAs<T>();
}
inline operator typename T::Reader() const { return get(); }
inline typename T::Reader operator*() const { return get(); }
inline TemporaryPointer<typename T::Reader> operator->() const { return get(); }
private:
const word* ptr;
};
template <typename T>
class ConstList {
public:
ConstList() = delete;
KJ_DISALLOW_COPY(ConstList);
inline explicit constexpr ConstList(const word* ptr): ptr(ptr) {}
inline typename List<T>::Reader get() const {
return AnyPointer::Reader(PointerReader::getRootUnchecked(ptr)).getAs<List<T>>();
}
inline operator typename List<T>::Reader() const { return get(); }
inline typename List<T>::Reader operator*() const { return get(); }
inline TemporaryPointer<typename List<T>::Reader> operator->() const { return get(); }
private:
const word* ptr;
};
template <size_t size>
class ConstText {
public:
ConstText() = delete;
KJ_DISALLOW_COPY(ConstText);
inline explicit constexpr ConstText(const word* ptr): ptr(ptr) {}
inline Text::Reader get() const {
return Text::Reader(reinterpret_cast<const char*>(ptr), size);
}
inline operator Text::Reader() const { return get(); }
inline Text::Reader operator*() const { return get(); }
inline TemporaryPointer<Text::Reader> operator->() const { return get(); }
inline kj::StringPtr toString() const {
return get();
}
private:
const word* ptr;
};
template <size_t size>
inline kj::StringPtr KJ_STRINGIFY(const ConstText<size>& s) {
return s.get();
}
template <size_t size>
class ConstData {
public:
ConstData() = delete;
KJ_DISALLOW_COPY(ConstData);
inline explicit constexpr ConstData(const word* ptr): ptr(ptr) {}
inline Data::Reader get() const {
return Data::Reader(reinterpret_cast<const byte*>(ptr), size);
}
inline operator Data::Reader() const { return get(); }
inline Data::Reader operator*() const { return get(); }
inline TemporaryPointer<Data::Reader> operator->() const { return get(); }
private:
const word* ptr;
};
template <size_t size>
inline auto KJ_STRINGIFY(const ConstData<size>& s) -> decltype(kj::toCharSequence(s.get())) {
return kj::toCharSequence(s.get());
}
} // namespace _ (private)
template <typename T, typename CapnpPrivate = typename T::_capnpPrivate>
inline constexpr uint64_t typeId() { return CapnpPrivate::typeId; }
template <typename T, uint64_t id = schemas::EnumInfo<T>::typeId>
inline constexpr uint64_t typeId() { return id; }
// typeId<MyType>() returns the type ID as defined in the schema. Works with structs, enums, and
// interfaces.
template <typename T>
inline constexpr uint sizeInWords() {
// Return the size, in words, of a Struct type, if allocated free-standing (not in a list).
// May be useful for pre-computing space needed in order to precisely allocate messages.
return unbound((upgradeBound<uint>(_::structSize<T>().data) +
_::structSize<T>().pointers * WORDS_PER_POINTER) / WORDS);
}
} // namespace capnp
#if _MSC_VER
// MSVC doesn't understand floating-point constexpr yet.
//
// TODO(msvc): Remove this hack when MSVC is fixed.
#define CAPNP_NON_INT_CONSTEXPR_DECL_INIT(value)
#define CAPNP_NON_INT_CONSTEXPR_DEF_INIT(value) = value
#else
#define CAPNP_NON_INT_CONSTEXPR_DECL_INIT(value) = value
#define CAPNP_NON_INT_CONSTEXPR_DEF_INIT(value)
#endif
#if _MSC_VER
// TODO(msvc): A little hack to allow MSVC to use C++14 return type deduction in cases where the
// explicit type exposes bugs in the compiler.
#define CAPNP_AUTO_IF_MSVC(...) auto
#else
#define CAPNP_AUTO_IF_MSVC(...) __VA_ARGS__
#endif
#if CAPNP_LITE
#define CAPNP_DECLARE_SCHEMA(id) \
extern ::capnp::word const* const bp_##id
#define CAPNP_DECLARE_ENUM(type, id) \
inline ::kj::String KJ_STRINGIFY(type##_##id value) { \
return ::kj::str(static_cast<uint16_t>(value)); \
} \
template <> struct EnumInfo<type##_##id> { \
struct IsEnum; \
static constexpr uint64_t typeId = 0x##id; \
static inline ::capnp::word const* encodedSchema() { return bp_##id; } \
}
#if _MSC_VER
// TODO(msvc): MSVC dosen't expect constexprs to have definitions.
#define CAPNP_DEFINE_ENUM(type, id)
#else
#define CAPNP_DEFINE_ENUM(type, id) \
constexpr uint64_t EnumInfo<type>::typeId
#endif
#define CAPNP_DECLARE_STRUCT_HEADER(id, dataWordSize_, pointerCount_) \
struct IsStruct; \
static constexpr uint64_t typeId = 0x##id; \
static constexpr uint16_t dataWordSize = dataWordSize_; \
static constexpr uint16_t pointerCount = pointerCount_; \
static inline ::capnp::word const* encodedSchema() { return ::capnp::schemas::bp_##id; }
#else // CAPNP_LITE
#define CAPNP_DECLARE_SCHEMA(id) \
extern ::capnp::word const* const bp_##id; \
extern const ::capnp::_::RawSchema s_##id
#define CAPNP_DECLARE_ENUM(type, id) \
inline ::kj::String KJ_STRINGIFY(type##_##id value) { \
return ::capnp::_::enumString(value); \
} \
template <> struct EnumInfo<type##_##id> { \
struct IsEnum; \
static constexpr uint64_t typeId = 0x##id; \
static inline ::capnp::word const* encodedSchema() { return bp_##id; } \
static constexpr ::capnp::_::RawSchema const* schema = &s_##id; \
}
#define CAPNP_DEFINE_ENUM(type, id) \
constexpr uint64_t EnumInfo<type>::typeId; \
constexpr ::capnp::_::RawSchema const* EnumInfo<type>::schema
#define CAPNP_DECLARE_STRUCT_HEADER(id, dataWordSize_, pointerCount_) \
struct IsStruct; \
static constexpr uint64_t typeId = 0x##id; \
static constexpr ::capnp::Kind kind = ::capnp::Kind::STRUCT; \
static constexpr uint16_t dataWordSize = dataWordSize_; \
static constexpr uint16_t pointerCount = pointerCount_; \
static inline ::capnp::word const* encodedSchema() { return ::capnp::schemas::bp_##id; } \
static constexpr ::capnp::_::RawSchema const* schema = &::capnp::schemas::s_##id;
#define CAPNP_DECLARE_INTERFACE_HEADER(id) \
struct IsInterface; \
static constexpr uint64_t typeId = 0x##id; \
static constexpr ::capnp::Kind kind = ::capnp::Kind::INTERFACE; \
static inline ::capnp::word const* encodedSchema() { return ::capnp::schemas::bp_##id; } \
static constexpr ::capnp::_::RawSchema const* schema = &::capnp::schemas::s_##id;
#endif // CAPNP_LITE, else
#endif // CAPNP_GENERATED_HEADER_SUPPORT_H_
@@ -0,0 +1,58 @@
# Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0x8ef99297a43a5e34;
$import "/capnp/c++.capnp".namespace("capnp");
struct JsonValue {
union {
null @0 :Void;
boolean @1 :Bool;
number @2 :Float64;
string @3 :Text;
array @4 :List(JsonValue);
object @5 :List(Field);
# Standard JSON values.
call @6 :Call;
# Non-standard: A "function call", applying a named function (named by a single identifier)
# to a parameter list. Examples:
#
# BinData(0, "Zm9vCg==")
# ISODate("2015-04-15T08:44:50.218Z")
#
# Mongo DB users will recognize the above as exactly the syntax Mongo uses to represent BSON
# "binary" and "date" types in text, since JSON has no analog of these. This is basically the
# reason this extension exists. We do NOT recommend using `call` unless you specifically need
# to be compatible with some silly format that uses this syntax.
}
struct Field {
name @0 :Text;
value @1 :JsonValue;
}
struct Call {
function @0 :Text;
params @1 :List(JsonValue);
}
}
File diff suppressed because it is too large Load Diff
+546
View File
@@ -0,0 +1,546 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_LIST_H_
#define CAPNP_LIST_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "layout.h"
#include "orphan.h"
#include <initializer_list>
#ifdef KJ_STD_COMPAT
#include <iterator>
#endif // KJ_STD_COMPAT
namespace capnp {
namespace _ { // private
template <typename T>
class TemporaryPointer {
// This class is a little hack which lets us define operator->() in cases where it needs to
// return a pointer to a temporary value. We instead construct a TemporaryPointer and return that
// (by value). The compiler then invokes operator->() on the TemporaryPointer, which itself is
// able to return a real pointer to its member.
public:
TemporaryPointer(T&& value): value(kj::mv(value)) {}
TemporaryPointer(const T& value): value(value) {}
inline T* operator->() { return &value; }
private:
T value;
};
template <typename Container, typename Element>
class IndexingIterator {
public:
IndexingIterator() = default;
inline Element operator*() const { return (*container)[index]; }
inline TemporaryPointer<Element> operator->() const {
return TemporaryPointer<Element>((*container)[index]);
}
inline Element operator[]( int off) const { return (*container)[index]; }
inline Element operator[](uint off) const { return (*container)[index]; }
inline IndexingIterator& operator++() { ++index; return *this; }
inline IndexingIterator operator++(int) { IndexingIterator other = *this; ++index; return other; }
inline IndexingIterator& operator--() { --index; return *this; }
inline IndexingIterator operator--(int) { IndexingIterator other = *this; --index; return other; }
inline IndexingIterator operator+(uint amount) const { return IndexingIterator(container, index + amount); }
inline IndexingIterator operator-(uint amount) const { return IndexingIterator(container, index - amount); }
inline IndexingIterator operator+( int amount) const { return IndexingIterator(container, index + amount); }
inline IndexingIterator operator-( int amount) const { return IndexingIterator(container, index - amount); }
inline int operator-(const IndexingIterator& other) const { return index - other.index; }
inline IndexingIterator& operator+=(uint amount) { index += amount; return *this; }
inline IndexingIterator& operator-=(uint amount) { index -= amount; return *this; }
inline IndexingIterator& operator+=( int amount) { index += amount; return *this; }
inline IndexingIterator& operator-=( int amount) { index -= amount; return *this; }
// STL says comparing iterators of different containers is not allowed, so we only compare
// indices here.
inline bool operator==(const IndexingIterator& other) const { return index == other.index; }
inline bool operator!=(const IndexingIterator& other) const { return index != other.index; }
inline bool operator<=(const IndexingIterator& other) const { return index <= other.index; }
inline bool operator>=(const IndexingIterator& other) const { return index >= other.index; }
inline bool operator< (const IndexingIterator& other) const { return index < other.index; }
inline bool operator> (const IndexingIterator& other) const { return index > other.index; }
private:
Container* container;
uint index;
friend Container;
inline IndexingIterator(Container* container, uint index)
: container(container), index(index) {}
};
} // namespace _ (private)
template <typename T>
struct List<T, Kind::PRIMITIVE> {
// List of primitives.
List() = delete;
class Reader {
public:
typedef List<T> Reads;
inline Reader(): reader(_::elementSizeForType<T>()) {}
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
inline T operator[](uint index) const {
KJ_IREQUIRE(index < size());
return reader.template getDataElement<T>(bounded(index) * ELEMENTS);
}
typedef _::IndexingIterator<const Reader, T> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
_::ListReader reader;
template <typename U, Kind K>
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
public:
typedef List<T> Builds;
inline Builder(): builder(_::elementSizeForType<T>()) {}
inline Builder(decltype(nullptr)): Builder() {}
inline explicit Builder(_::ListBuilder builder): builder(builder) {}
inline operator Reader() const { return Reader(builder.asReader()); }
inline Reader asReader() const { return Reader(builder.asReader()); }
inline uint size() const { return unbound(builder.size() / ELEMENTS); }
inline T operator[](uint index) {
KJ_IREQUIRE(index < size());
return builder.template getDataElement<T>(bounded(index) * ELEMENTS);
}
inline void set(uint index, T value) {
// Alas, it is not possible to make operator[] return a reference to which you can assign,
// since the encoded representation does not necessarily match the compiler's representation
// of the type. We can't even return a clever class that implements operator T() and
// operator=() because it will lead to surprising behavior when using type inference (e.g.
// calling a template function with inferred argument types, or using "auto" or "decltype").
builder.template setDataElement<T>(bounded(index) * ELEMENTS, value);
}
typedef _::IndexingIterator<Builder, T> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
inline Iterator end() { return Iterator(this, size()); }
private:
_::ListBuilder builder;
template <typename U, Kind K>
friend struct _::PointerHelpers;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Pipeline {};
private:
inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) {
return builder.initList(_::elementSizeForType<T>(), bounded(size) * ELEMENTS);
}
inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) {
return builder.getList(_::elementSizeForType<T>(), defaultValue);
}
inline static _::ListReader getFromPointer(
const _::PointerReader& reader, const word* defaultValue) {
return reader.getList(_::elementSizeForType<T>(), defaultValue);
}
template <typename U, Kind k>
friend struct List;
template <typename U, Kind K>
friend struct _::PointerHelpers;
};
template <typename T>
struct List<T, Kind::ENUM>: public List<T, Kind::PRIMITIVE> {};
template <typename T>
struct List<T, Kind::STRUCT> {
// List of structs.
List() = delete;
class Reader {
public:
typedef List<T> Reads;
inline Reader(): reader(ElementSize::INLINE_COMPOSITE) {}
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
inline typename T::Reader operator[](uint index) const {
KJ_IREQUIRE(index < size());
return typename T::Reader(reader.getStructElement(bounded(index) * ELEMENTS));
}
typedef _::IndexingIterator<const Reader, typename T::Reader> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
_::ListReader reader;
template <typename U, Kind K>
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
public:
typedef List<T> Builds;
inline Builder(): builder(ElementSize::INLINE_COMPOSITE) {}
inline Builder(decltype(nullptr)): Builder() {}
inline explicit Builder(_::ListBuilder builder): builder(builder) {}
inline operator Reader() const { return Reader(builder.asReader()); }
inline Reader asReader() const { return Reader(builder.asReader()); }
inline uint size() const { return unbound(builder.size() / ELEMENTS); }
inline typename T::Builder operator[](uint index) {
KJ_IREQUIRE(index < size());
return typename T::Builder(builder.getStructElement(bounded(index) * ELEMENTS));
}
inline void adoptWithCaveats(uint index, Orphan<T>&& orphan) {
// Mostly behaves like you'd expect `adopt` to behave, but with two caveats originating from
// the fact that structs in a struct list are allocated inline rather than by pointer:
// * This actually performs a shallow copy, effectively adopting each of the orphan's
// children rather than adopting the orphan itself. The orphan ends up being discarded,
// possibly wasting space in the message object.
// * If the orphan is larger than the target struct -- say, because the orphan was built
// using a newer version of the schema that has additional fields -- it will be truncated,
// losing data.
KJ_IREQUIRE(index < size());
// We pass a zero-valued StructSize to asStruct() because we do not want the struct to be
// expanded under any circumstances. We're just going to throw it away anyway, and
// transferContentFrom() already carefully compares the struct sizes before transferring.
builder.getStructElement(bounded(index) * ELEMENTS).transferContentFrom(
orphan.builder.asStruct(_::StructSize(ZERO * WORDS, ZERO * POINTERS)));
}
inline void setWithCaveats(uint index, const typename T::Reader& reader) {
// Mostly behaves like you'd expect `set` to behave, but with a caveat originating from
// the fact that structs in a struct list are allocated inline rather than by pointer:
// If the source struct is larger than the target struct -- say, because the source was built
// using a newer version of the schema that has additional fields -- it will be truncated,
// losing data.
//
// Note: If you are trying to concatenate some lists, use Orphanage::newOrphanConcat() to
// do it without losing any data in case the source lists come from a newer version of the
// protocol. (Plus, it's easier to use anyhow.)
KJ_IREQUIRE(index < size());
builder.getStructElement(bounded(index) * ELEMENTS).copyContentFrom(reader._reader);
}
// There are no init(), set(), adopt(), or disown() methods for lists of structs because the
// elements of the list are inlined and are initialized when the list is initialized. This
// means that init() would be redundant, and set() would risk data loss if the input struct
// were from a newer version of the protocol.
typedef _::IndexingIterator<Builder, typename T::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
inline Iterator end() { return Iterator(this, size()); }
private:
_::ListBuilder builder;
template <typename U, Kind K>
friend struct _::PointerHelpers;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Pipeline {};
private:
inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) {
return builder.initStructList(bounded(size) * ELEMENTS, _::structSize<T>());
}
inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) {
return builder.getStructList(_::structSize<T>(), defaultValue);
}
inline static _::ListReader getFromPointer(
const _::PointerReader& reader, const word* defaultValue) {
return reader.getList(ElementSize::INLINE_COMPOSITE, defaultValue);
}
template <typename U, Kind k>
friend struct List;
template <typename U, Kind K>
friend struct _::PointerHelpers;
};
template <typename T>
struct List<List<T>, Kind::LIST> {
// List of lists.
List() = delete;
class Reader {
public:
typedef List<List<T>> Reads;
inline Reader(): reader(ElementSize::POINTER) {}
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
inline typename List<T>::Reader operator[](uint index) const {
KJ_IREQUIRE(index < size());
return typename List<T>::Reader(_::PointerHelpers<List<T>>::get(
reader.getPointerElement(bounded(index) * ELEMENTS)));
}
typedef _::IndexingIterator<const Reader, typename List<T>::Reader> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
_::ListReader reader;
template <typename U, Kind K>
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
public:
typedef List<List<T>> Builds;
inline Builder(): builder(ElementSize::POINTER) {}
inline Builder(decltype(nullptr)): Builder() {}
inline explicit Builder(_::ListBuilder builder): builder(builder) {}
inline operator Reader() const { return Reader(builder.asReader()); }
inline Reader asReader() const { return Reader(builder.asReader()); }
inline uint size() const { return unbound(builder.size() / ELEMENTS); }
inline typename List<T>::Builder operator[](uint index) {
KJ_IREQUIRE(index < size());
return typename List<T>::Builder(_::PointerHelpers<List<T>>::get(
builder.getPointerElement(bounded(index) * ELEMENTS)));
}
inline typename List<T>::Builder init(uint index, uint size) {
KJ_IREQUIRE(index < this->size());
return typename List<T>::Builder(_::PointerHelpers<List<T>>::init(
builder.getPointerElement(bounded(index) * ELEMENTS), size));
}
inline void set(uint index, typename List<T>::Reader value) {
KJ_IREQUIRE(index < size());
builder.getPointerElement(bounded(index) * ELEMENTS).setList(value.reader);
}
void set(uint index, std::initializer_list<ReaderFor<T>> value) {
KJ_IREQUIRE(index < size());
auto l = init(index, value.size());
uint i = 0;
for (auto& element: value) {
l.set(i++, element);
}
}
inline void adopt(uint index, Orphan<T>&& value) {
KJ_IREQUIRE(index < size());
builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(value.builder));
}
inline Orphan<T> disown(uint index) {
KJ_IREQUIRE(index < size());
return Orphan<T>(builder.getPointerElement(bounded(index) * ELEMENTS).disown());
}
typedef _::IndexingIterator<Builder, typename List<T>::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
inline Iterator end() { return Iterator(this, size()); }
private:
_::ListBuilder builder;
template <typename U, Kind K>
friend struct _::PointerHelpers;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Pipeline {};
private:
inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) {
return builder.initList(ElementSize::POINTER, bounded(size) * ELEMENTS);
}
inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) {
return builder.getList(ElementSize::POINTER, defaultValue);
}
inline static _::ListReader getFromPointer(
const _::PointerReader& reader, const word* defaultValue) {
return reader.getList(ElementSize::POINTER, defaultValue);
}
template <typename U, Kind k>
friend struct List;
template <typename U, Kind K>
friend struct _::PointerHelpers;
};
template <typename T>
struct List<T, Kind::BLOB> {
List() = delete;
class Reader {
public:
typedef List<T> Reads;
inline Reader(): reader(ElementSize::POINTER) {}
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
inline typename T::Reader operator[](uint index) const {
KJ_IREQUIRE(index < size());
return reader.getPointerElement(bounded(index) * ELEMENTS)
.template getBlob<T>(nullptr, ZERO * BYTES);
}
typedef _::IndexingIterator<const Reader, typename T::Reader> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
_::ListReader reader;
template <typename U, Kind K>
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
public:
typedef List<T> Builds;
inline Builder(): builder(ElementSize::POINTER) {}
inline Builder(decltype(nullptr)): Builder() {}
inline explicit Builder(_::ListBuilder builder): builder(builder) {}
inline operator Reader() const { return Reader(builder.asReader()); }
inline Reader asReader() const { return Reader(builder.asReader()); }
inline uint size() const { return unbound(builder.size() / ELEMENTS); }
inline typename T::Builder operator[](uint index) {
KJ_IREQUIRE(index < size());
return builder.getPointerElement(bounded(index) * ELEMENTS)
.template getBlob<T>(nullptr, ZERO * BYTES);
}
inline void set(uint index, typename T::Reader value) {
KJ_IREQUIRE(index < size());
builder.getPointerElement(bounded(index) * ELEMENTS).template setBlob<T>(value);
}
inline typename T::Builder init(uint index, uint size) {
KJ_IREQUIRE(index < this->size());
return builder.getPointerElement(bounded(index) * ELEMENTS)
.template initBlob<T>(bounded(size) * BYTES);
}
inline void adopt(uint index, Orphan<T>&& value) {
KJ_IREQUIRE(index < size());
builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(value.builder));
}
inline Orphan<T> disown(uint index) {
KJ_IREQUIRE(index < size());
return Orphan<T>(builder.getPointerElement(bounded(index) * ELEMENTS).disown());
}
typedef _::IndexingIterator<Builder, typename T::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
inline Iterator end() { return Iterator(this, size()); }
private:
_::ListBuilder builder;
template <typename U, Kind K>
friend struct _::PointerHelpers;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Pipeline {};
private:
inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) {
return builder.initList(ElementSize::POINTER, bounded(size) * ELEMENTS);
}
inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) {
return builder.getList(ElementSize::POINTER, defaultValue);
}
inline static _::ListReader getFromPointer(
const _::PointerReader& reader, const word* defaultValue) {
return reader.getList(ElementSize::POINTER, defaultValue);
}
template <typename U, Kind k>
friend struct List;
template <typename U, Kind K>
friend struct _::PointerHelpers;
};
} // namespace capnp
#ifdef KJ_STD_COMPAT
namespace std {
template <typename Container, typename Element>
struct iterator_traits<capnp::_::IndexingIterator<Container, Element>>
: public std::iterator<std::random_access_iterator_tag, Element, int> {};
} // namespace std
#endif // KJ_STD_COMPAT
#endif // CAPNP_LIST_H_
@@ -0,0 +1,202 @@
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_MEMBRANE_H_
#define CAPNP_MEMBRANE_H_
// In capability theory, a "membrane" is a wrapper around a capability which (usually) forwards
// calls but recursively wraps capabilities in those calls in the same membrane. The purpose of a
// membrane is to enforce a barrier between two capabilities that cannot be bypassed by merely
// introducing new objects.
//
// The most common use case for a membrane is revocation: Say Alice wants to give Bob a capability
// to access Carol, but wants to be able to revoke this capability later. Alice can accomplish this
// by wrapping Carol in a revokable wrapper which passes through calls until such a time as Alice
// indicates it should be revoked, after which all calls through the wrapper will throw exceptions.
// However, a naive wrapper approach has a problem: if Bob makes a call to Carol and sends a new
// capability in that call, or if Carol returns a capability to Bob in the response to a call, then
// the two are now able to communicate using this new capability, which Alice cannot revoke. In
// order to avoid this problem, Alice must use not just a wrapper but a "membrane", which
// recursively wraps all objects that pass through it in either direction. Thus, all connections
// formed between Bob and Carol (originating from Alice's original introduction) can be revoked
// together by revoking the membrane.
//
// Note that when a capability is passed into a membrane and then passed back out, the result is
// the original capability, not a double-membraned capability. This means that in our revocation
// example, if Bob uses his capability to Carol to obtain another capability from her, then send
// it back to her, the capability Carol receives back will NOT be revoked when Bob's access to
// Carol is revoked. Thus Bob can create long-term irrevocable connections. In most practical use
// cases, this is what you want. APIs commonly rely on the fact that a capability obtained and then
// passed back can be recognized as the original capability.
//
// Mark Miller on membranes: http://www.eros-os.org/pipermail/e-lang/2003-January/008434.html
#include "capability.h"
namespace capnp {
class MembranePolicy {
// Applications may implement this interface to define a membrane policy, which allows some
// calls crossing the membrane to be blocked or redirected.
public:
virtual kj::Maybe<Capability::Client> inboundCall(
uint64_t interfaceId, uint16_t methodId, Capability::Client target) = 0;
// Given an inbound call (a call originating "outside" the membrane destined for an object
// "inside" the membrane), decides what to do with it. The policy may:
//
// - Return null to indicate that the call should proceed to the destination. All capabilities
// in the parameters or result will be properly wrapped in the same membrane.
// - Return a capability to have the call redirected to that capability. Note that the redirect
// capability will be treated as outside the membrane, so the params and results will not be
// auto-wrapped; however, the callee can easily wrap the returned capability in the membrane
// itself before returning to achieve this effect.
// - Throw an exception to cause the call to fail with that exception.
//
// `target` is the underlying capability (*inside* the membrane) for which the call is destined.
// Generally, the only way you should use `target` is to wrap it in some capability which you
// return as a redirect. The redirect capability may modify the call in some way and send it to
// `target`. Be careful to use `copyIntoMembrane()` and `copyOutOfMembrane()` as appropriate when
// copying parameters or results across the membrane.
//
// Note that since `target` is inside the capability, if you were to directly return it (rather
// than return null), the effect would be that the membrane would be broken: the call would
// proceed directly and any new capabilities introduced through it would not be membraned. You
// generally should not do that.
virtual kj::Maybe<Capability::Client> outboundCall(
uint64_t interfaceId, uint16_t methodId, Capability::Client target) = 0;
// Like `inboundCall()`, but applies to calls originating *inside* the membrane and terminating
// outside.
//
// Note: It is strongly recommended that `outboundCall()` returns null in exactly the same cases
// that `inboundCall()` return null. Conversely, for any case where `inboundCall()` would
// redirect or throw, `outboundCall()` should also redirect or throw. Otherwise, you can run
// into inconsistent behavion when a promise is returned across a membrane, and that promise
// later resolves to a capability on the other side of the membrane: calls on the promise
// will enter and then exit the membrane, but calls on the eventual resolution will not cross
// the membrane at all, so it is important that these two cases behave the same.
virtual kj::Own<MembranePolicy> addRef() = 0;
// Return a new owned pointer to the same policy.
//
// Typically an implementation of MembranePolicy should also inherit kj::Refcounted and implement
// `addRef()` as `return kj::addRef(*this);`.
//
// Note that the membraning system considers two membranes created with the same MembranePolicy
// object actually to be the *same* membrane. This is relevant when an object passes into the
// membrane and then back out (or out and then back in): instead of double-wrapping the object,
// the wrapping will be removed.
};
Capability::Client membrane(Capability::Client inner, kj::Own<MembranePolicy> policy);
// Wrap `inner` in a membrane specified by `policy`. `inner` is considered "inside" the membrane,
// while the returned capability should only be called from outside the membrane.
Capability::Client reverseMembrane(Capability::Client outer, kj::Own<MembranePolicy> policy);
// Like `membrane` but treat the input capability as "outside" the membrane, and return a
// capability appropriate for use inside.
//
// Applications typically won't use this directly; the membraning code automatically sets up
// reverse membranes where needed.
template <typename ClientType>
ClientType membrane(ClientType inner, kj::Own<MembranePolicy> policy);
template <typename ClientType>
ClientType reverseMembrane(ClientType inner, kj::Own<MembranePolicy> policy);
// Convenience templates which return the same interface type as the input.
template <typename ServerType>
typename ServerType::Serves::Client membrane(
kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy);
template <typename ServerType>
typename ServerType::Serves::Client reverseMembrane(
kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy);
// Convenience templates which input a capability server type and return the appropriate client
// type.
template <typename Reader>
Orphan<typename kj::Decay<Reader>::Reads> copyIntoMembrane(
Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy);
// Copy a Cap'n Proto object (e.g. struct or list), adding the given membrane to any capabilities
// found within it. `from` is interpreted as "outside" the membrane while `to` is "inside".
template <typename Reader>
Orphan<typename kj::Decay<Reader>::Reads> copyOutOfMembrane(
Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy);
// Like copyIntoMembrane() except that `from` is "inside" the membrane and `to` is "outside".
// =======================================================================================
// inline implementation details
template <typename ClientType>
ClientType membrane(ClientType inner, kj::Own<MembranePolicy> policy) {
return membrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
.castAs<typename ClientType::Calls>();
}
template <typename ClientType>
ClientType reverseMembrane(ClientType inner, kj::Own<MembranePolicy> policy) {
return reverseMembrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
.castAs<typename ClientType::Calls>();
}
template <typename ServerType>
typename ServerType::Serves::Client membrane(
kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy) {
return membrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
.castAs<typename ServerType::Serves>();
}
template <typename ServerType>
typename ServerType::Serves::Client reverseMembrane(
kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy) {
return reverseMembrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
.castAs<typename ServerType::Serves>();
}
namespace _ { // private
OrphanBuilder copyOutOfMembrane(PointerReader from, Orphanage to,
kj::Own<MembranePolicy> policy, bool reverse);
OrphanBuilder copyOutOfMembrane(StructReader from, Orphanage to,
kj::Own<MembranePolicy> policy, bool reverse);
OrphanBuilder copyOutOfMembrane(ListReader from, Orphanage to,
kj::Own<MembranePolicy> policy, bool reverse);
} // namespace _ (private)
template <typename Reader>
Orphan<typename kj::Decay<Reader>::Reads> copyIntoMembrane(
Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy) {
return _::copyOutOfMembrane(
_::PointerHelpers<typename kj::Decay<Reader>::Reads>::getInternalReader(from),
to, kj::mv(policy), true);
}
template <typename Reader>
Orphan<typename kj::Decay<Reader>::Reads> copyOutOfMembrane(
Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy) {
return _::copyOutOfMembrane(
_::PointerHelpers<typename kj::Decay<Reader>::Reads>::getInternalReader(from),
to, kj::mv(policy), false);
}
} // namespace capnp
#endif // CAPNP_MEMBRANE_H_
+508
View File
@@ -0,0 +1,508 @@
// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <kj/common.h>
#include <kj/memory.h>
#include <kj/mutex.h>
#include <kj/debug.h>
#include "common.h"
#include "layout.h"
#include "any.h"
#ifndef CAPNP_MESSAGE_H_
#define CAPNP_MESSAGE_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
namespace capnp {
namespace _ { // private
class ReaderArena;
class BuilderArena;
}
class StructSchema;
class Orphanage;
template <typename T>
class Orphan;
// =======================================================================================
struct ReaderOptions {
// Options controlling how data is read.
uint64_t traversalLimitInWords = 8 * 1024 * 1024;
// Limits how many total words of data are allowed to be traversed. Traversal is counted when
// a new struct or list builder is obtained, e.g. from a get() accessor. This means that calling
// the getter for the same sub-struct multiple times will cause it to be double-counted. Once
// the traversal limit is reached, an error will be reported.
//
// This limit exists for security reasons. It is possible for an attacker to construct a message
// in which multiple pointers point at the same location. This is technically invalid, but hard
// to detect. Using such a message, an attacker could cause a message which is small on the wire
// to appear much larger when actually traversed, possibly exhausting server resources leading to
// denial-of-service.
//
// It makes sense to set a traversal limit that is much larger than the underlying message.
// Together with sensible coding practices (e.g. trying to avoid calling sub-object getters
// multiple times, which is expensive anyway), this should provide adequate protection without
// inconvenience.
//
// The default limit is 64 MiB. This may or may not be a sensible number for any given use case,
// but probably at least prevents easy exploitation while also avoiding causing problems in most
// typical cases.
int nestingLimit = 64;
// Limits how deeply-nested a message structure can be, e.g. structs containing other structs or
// lists of structs.
//
// Like the traversal limit, this limit exists for security reasons. Since it is common to use
// recursive code to traverse recursive data structures, an attacker could easily cause a stack
// overflow by sending a very-deeply-nested (or even cyclic) message, without the message even
// being very large. The default limit of 64 is probably low enough to prevent any chance of
// stack overflow, yet high enough that it is never a problem in practice.
};
class MessageReader {
// Abstract interface for an object used to read a Cap'n Proto message. Subclasses of
// MessageReader are responsible for reading the raw, flat message content. Callers should
// usually call `messageReader.getRoot<MyStructType>()` to get a `MyStructType::Reader`
// representing the root of the message, then use that to traverse the message content.
//
// Some common subclasses of `MessageReader` include `SegmentArrayMessageReader`, whose
// constructor accepts pointers to the raw data, and `StreamFdMessageReader` (from
// `serialize.h`), which reads the message from a file descriptor. One might implement other
// subclasses to handle things like reading from shared memory segments, mmap()ed files, etc.
public:
MessageReader(ReaderOptions options);
// It is suggested that subclasses take ReaderOptions as a constructor parameter, but give it a
// default value of "ReaderOptions()". The base class constructor doesn't have a default value
// in order to remind subclasses that they really need to give the user a way to provide this.
virtual ~MessageReader() noexcept(false);
virtual kj::ArrayPtr<const word> getSegment(uint id) = 0;
// Gets the segment with the given ID, or returns null if no such segment exists. This method
// will be called at most once for each segment ID.
inline const ReaderOptions& getOptions();
// Get the options passed to the constructor.
template <typename RootType>
typename RootType::Reader getRoot();
// Get the root struct of the message, interpreting it as the given struct type.
template <typename RootType, typename SchemaType>
typename RootType::Reader getRoot(SchemaType schema);
// Dynamically interpret the root struct of the message using the given schema (a StructSchema).
// RootType in this case must be DynamicStruct, and you must #include <capnp/dynamic.h> to
// use this.
bool isCanonical();
// Returns whether the message encoded in the reader is in canonical form.
private:
ReaderOptions options;
// Space in which we can construct a ReaderArena. We don't use ReaderArena directly here
// because we don't want clients to have to #include arena.h, which itself includes a bunch of
// big STL headers. We don't use a pointer to a ReaderArena because that would require an
// extra malloc on every message which could be expensive when processing small messages.
void* arenaSpace[15 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
bool allocatedArena;
_::ReaderArena* arena() { return reinterpret_cast<_::ReaderArena*>(arenaSpace); }
AnyPointer::Reader getRootInternal();
};
class MessageBuilder {
// Abstract interface for an object used to allocate and build a message. Subclasses of
// MessageBuilder are responsible for allocating the space in which the message will be written.
// The most common subclass is `MallocMessageBuilder`, but other subclasses may be used to do
// tricky things like allocate messages in shared memory or mmap()ed files.
//
// Creating a new message ususually means allocating a new MessageBuilder (ideally on the stack)
// and then calling `messageBuilder.initRoot<MyStructType>()` to get a `MyStructType::Builder`.
// That, in turn, can be used to fill in the message content. When done, you can call
// `messageBuilder.getSegmentsForOutput()` to get a list of flat data arrays containing the
// message.
public:
MessageBuilder();
virtual ~MessageBuilder() noexcept(false);
KJ_DISALLOW_COPY(MessageBuilder);
struct SegmentInit {
kj::ArrayPtr<word> space;
size_t wordsUsed;
// Number of words in `space` which are used; the rest are free space in which additional
// objects may be allocated.
};
explicit MessageBuilder(kj::ArrayPtr<SegmentInit> segments);
// Create a MessageBuilder backed by existing memory. This is an advanced interface that most
// people should not use. THIS METHOD IS INSECURE; see below.
//
// This allows a MessageBuilder to be constructed to modify an in-memory message without first
// making a copy of the content. This is especially useful in conjunction with mmap().
//
// The contents of each segment must outlive the MessageBuilder, but the SegmentInit array itself
// only need outlive the constructor.
//
// SECURITY: Do not use this in conjunction with untrusted data. This constructor assumes that
// the input message is valid. This constructor is designed to be used with data you control,
// e.g. an mmap'd file which is owned and accessed by only one program. When reading data you
// do not trust, you *must* load it into a Reader and then copy into a Builder as a means of
// validating the content.
//
// WARNING: It is NOT safe to initialize a MessageBuilder in this way from memory that is
// currently in use by another MessageBuilder or MessageReader. Other readers/builders will
// not observe changes to the segment sizes nor newly-allocated segments caused by allocating
// new objects in this message.
virtual kj::ArrayPtr<word> allocateSegment(uint minimumSize) = 0;
// Allocates an array of at least the given number of words, throwing an exception or crashing if
// this is not possible. It is expected that this method will usually return more space than
// requested, and the caller should use that extra space as much as possible before allocating
// more. The returned space remains valid at least until the MessageBuilder is destroyed.
//
// Cap'n Proto will only call this once at a time, so the subclass need not worry about
// thread-safety.
template <typename RootType>
typename RootType::Builder initRoot();
// Initialize the root struct of the message as the given struct type.
template <typename Reader>
void setRoot(Reader&& value);
// Set the root struct to a deep copy of the given struct.
template <typename RootType>
typename RootType::Builder getRoot();
// Get the root struct of the message, interpreting it as the given struct type.
template <typename RootType, typename SchemaType>
typename RootType::Builder getRoot(SchemaType schema);
// Dynamically interpret the root struct of the message using the given schema (a StructSchema).
// RootType in this case must be DynamicStruct, and you must #include <capnp/dynamic.h> to
// use this.
template <typename RootType, typename SchemaType>
typename RootType::Builder initRoot(SchemaType schema);
// Dynamically init the root struct of the message using the given schema (a StructSchema).
// RootType in this case must be DynamicStruct, and you must #include <capnp/dynamic.h> to
// use this.
template <typename T>
void adoptRoot(Orphan<T>&& orphan);
// Like setRoot() but adopts the orphan without copying.
kj::ArrayPtr<const kj::ArrayPtr<const word>> getSegmentsForOutput();
// Get the raw data that makes up the message.
Orphanage getOrphanage();
bool isCanonical();
// Check whether the message builder is in canonical form
private:
void* arenaSpace[22];
// Space in which we can construct a BuilderArena. We don't use BuilderArena directly here
// because we don't want clients to have to #include arena.h, which itself includes a bunch of
// big STL headers. We don't use a pointer to a BuilderArena because that would require an
// extra malloc on every message which could be expensive when processing small messages.
bool allocatedArena = false;
// We have to initialize the arena lazily because when we do so we want to allocate the root
// pointer immediately, and this will allocate a segment, which requires a virtual function
// call on the MessageBuilder. We can't do such a call in the constructor since the subclass
// isn't constructed yet. This is kind of annoying because it means that getOrphanage() is
// not thread-safe, but that shouldn't be a huge deal...
_::BuilderArena* arena() { return reinterpret_cast<_::BuilderArena*>(arenaSpace); }
_::SegmentBuilder* getRootSegment();
AnyPointer::Builder getRootInternal();
};
template <typename RootType>
typename RootType::Reader readMessageUnchecked(const word* data);
// IF THE INPUT IS INVALID, THIS MAY CRASH, CORRUPT MEMORY, CREATE A SECURITY HOLE IN YOUR APP,
// MURDER YOUR FIRST-BORN CHILD, AND/OR BRING ABOUT ETERNAL DAMNATION ON ALL OF HUMANITY. DO NOT
// USE UNLESS YOU UNDERSTAND THE CONSEQUENCES.
//
// Given a pointer to a known-valid message located in a single contiguous memory segment,
// returns a reader for that message. No bounds-checking will be done while traversing this
// message. Use this only if you have already verified that all pointers are valid and in-bounds,
// and there are no far pointers in the message.
//
// To create a message that can be passed to this function, build a message using a MallocAllocator
// whose preferred segment size is larger than the message size. This guarantees that the message
// will be allocated as a single segment, meaning getSegmentsForOutput() returns a single word
// array. That word array is your message; you may pass a pointer to its first word into
// readMessageUnchecked() to read the message.
//
// This can be particularly handy for embedding messages in generated code: you can
// embed the raw bytes (using AlignedData) then make a Reader for it using this. This is the way
// default values are embedded in code generated by the Cap'n Proto compiler. E.g., if you have
// a message MyMessage, you can read its default value like so:
// MyMessage::Reader reader = Message<MyMessage>::readMessageUnchecked(MyMessage::DEFAULT.words);
//
// To sanitize a message from an untrusted source such that it can be safely passed to
// readMessageUnchecked(), use copyToUnchecked().
template <typename Reader>
void copyToUnchecked(Reader&& reader, kj::ArrayPtr<word> uncheckedBuffer);
// Copy the content of the given reader into the given buffer, such that it can safely be passed to
// readMessageUnchecked(). The buffer's size must be exactly reader.totalSizeInWords() + 1,
// otherwise an exception will be thrown. The buffer must be zero'd before calling.
template <typename RootType>
typename RootType::Reader readDataStruct(kj::ArrayPtr<const word> data);
// Interprets the given data as a single, data-only struct. Only primitive fields (booleans,
// numbers, and enums) will be readable; all pointers will be null. This is useful if you want
// to use Cap'n Proto as a language/platform-neutral way to pack some bits.
//
// The input is a word array rather than a byte array to enforce alignment. If you have a byte
// array which you know is word-aligned (or if your platform supports unaligned reads and you don't
// mind the performance penalty), then you can use `reinterpret_cast` to convert a byte array into
// a word array:
//
// kj::arrayPtr(reinterpret_cast<const word*>(bytes.begin()),
// reinterpret_cast<const word*>(bytes.end()))
template <typename BuilderType>
typename kj::ArrayPtr<const word> writeDataStruct(BuilderType builder);
// Given a struct builder, get the underlying data section as a word array, suitable for passing
// to `readDataStruct()`.
//
// Note that you may call `.toBytes()` on the returned value to convert to `ArrayPtr<const byte>`.
template <typename Type>
static typename Type::Reader defaultValue();
// Get a default instance of the given struct or list type.
//
// TODO(cleanup): Find a better home for this function?
// =======================================================================================
class SegmentArrayMessageReader: public MessageReader {
// A simple MessageReader that reads from an array of word arrays representing all segments.
// In particular you can read directly from the output of MessageBuilder::getSegmentsForOutput()
// (although it would probably make more sense to call builder.getRoot().asReader() in that case).
public:
SegmentArrayMessageReader(kj::ArrayPtr<const kj::ArrayPtr<const word>> segments,
ReaderOptions options = ReaderOptions());
// Creates a message pointing at the given segment array, without taking ownership of the
// segments. All arrays passed in must remain valid until the MessageReader is destroyed.
KJ_DISALLOW_COPY(SegmentArrayMessageReader);
~SegmentArrayMessageReader() noexcept(false);
virtual kj::ArrayPtr<const word> getSegment(uint id) override;
private:
kj::ArrayPtr<const kj::ArrayPtr<const word>> segments;
};
enum class AllocationStrategy: uint8_t {
FIXED_SIZE,
// The builder will prefer to allocate the same amount of space for each segment with no
// heuristic growth. It will still allocate larger segments when the preferred size is too small
// for some single object. This mode is generally not recommended, but can be particularly useful
// for testing in order to force a message to allocate a predictable number of segments. Note
// that you can force every single object in the message to be located in a separate segment by
// using this mode with firstSegmentWords = 0.
GROW_HEURISTICALLY
// The builder will heuristically decide how much space to allocate for each segment. Each
// allocated segment will be progressively larger than the previous segments on the assumption
// that message sizes are exponentially distributed. The total number of segments that will be
// allocated for a message of size n is O(log n).
};
constexpr uint SUGGESTED_FIRST_SEGMENT_WORDS = 1024;
constexpr AllocationStrategy SUGGESTED_ALLOCATION_STRATEGY = AllocationStrategy::GROW_HEURISTICALLY;
class MallocMessageBuilder: public MessageBuilder {
// A simple MessageBuilder that uses malloc() (actually, calloc()) to allocate segments. This
// implementation should be reasonable for any case that doesn't require writing the message to
// a specific location in memory.
public:
explicit MallocMessageBuilder(uint firstSegmentWords = SUGGESTED_FIRST_SEGMENT_WORDS,
AllocationStrategy allocationStrategy = SUGGESTED_ALLOCATION_STRATEGY);
// Creates a BuilderContext which allocates at least the given number of words for the first
// segment, and then uses the given strategy to decide how much to allocate for subsequent
// segments. When choosing a value for firstSegmentWords, consider that:
// 1) Reading and writing messages gets slower when multiple segments are involved, so it's good
// if most messages fit in a single segment.
// 2) Unused bytes will not be written to the wire, so generally it is not a big deal to allocate
// more space than you need. It only becomes problematic if you are allocating many messages
// in parallel and thus use lots of memory, or if you allocate so much extra space that just
// zeroing it out becomes a bottleneck.
// The defaults have been chosen to be reasonable for most people, so don't change them unless you
// have reason to believe you need to.
explicit MallocMessageBuilder(kj::ArrayPtr<word> firstSegment,
AllocationStrategy allocationStrategy = SUGGESTED_ALLOCATION_STRATEGY);
// This version always returns the given array for the first segment, and then proceeds with the
// allocation strategy. This is useful for optimization when building lots of small messages in
// a tight loop: you can reuse the space for the first segment.
//
// firstSegment MUST be zero-initialized. MallocMessageBuilder's destructor will write new zeros
// over any space that was used so that it can be reused.
KJ_DISALLOW_COPY(MallocMessageBuilder);
virtual ~MallocMessageBuilder() noexcept(false);
virtual kj::ArrayPtr<word> allocateSegment(uint minimumSize) override;
private:
uint nextSize;
AllocationStrategy allocationStrategy;
bool ownFirstSegment;
bool returnedFirstSegment;
void* firstSegment;
struct MoreSegments;
kj::Maybe<kj::Own<MoreSegments>> moreSegments;
};
class FlatMessageBuilder: public MessageBuilder {
// THIS IS NOT THE CLASS YOU'RE LOOKING FOR.
//
// If you want to write a message into already-existing scratch space, use `MallocMessageBuilder`
// and pass the scratch space to its constructor. It will then only fall back to malloc() if
// the scratch space is not large enough.
//
// Do NOT use this class unless you really know what you're doing. This class is problematic
// because it requires advance knowledge of the size of your message, which is usually impossible
// to determine without actually building the message. The class was created primarily to
// implement `copyToUnchecked()`, which itself exists only to support other internal parts of
// the Cap'n Proto implementation.
public:
explicit FlatMessageBuilder(kj::ArrayPtr<word> array);
KJ_DISALLOW_COPY(FlatMessageBuilder);
virtual ~FlatMessageBuilder() noexcept(false);
void requireFilled();
// Throws an exception if the flat array is not exactly full.
virtual kj::ArrayPtr<word> allocateSegment(uint minimumSize) override;
private:
kj::ArrayPtr<word> array;
bool allocated;
};
// =======================================================================================
// implementation details
inline const ReaderOptions& MessageReader::getOptions() {
return options;
}
template <typename RootType>
inline typename RootType::Reader MessageReader::getRoot() {
return getRootInternal().getAs<RootType>();
}
template <typename RootType>
inline typename RootType::Builder MessageBuilder::initRoot() {
return getRootInternal().initAs<RootType>();
}
template <typename Reader>
inline void MessageBuilder::setRoot(Reader&& value) {
getRootInternal().setAs<FromReader<Reader>>(value);
}
template <typename RootType>
inline typename RootType::Builder MessageBuilder::getRoot() {
return getRootInternal().getAs<RootType>();
}
template <typename T>
void MessageBuilder::adoptRoot(Orphan<T>&& orphan) {
return getRootInternal().adopt(kj::mv(orphan));
}
template <typename RootType, typename SchemaType>
typename RootType::Reader MessageReader::getRoot(SchemaType schema) {
return getRootInternal().getAs<RootType>(schema);
}
template <typename RootType, typename SchemaType>
typename RootType::Builder MessageBuilder::getRoot(SchemaType schema) {
return getRootInternal().getAs<RootType>(schema);
}
template <typename RootType, typename SchemaType>
typename RootType::Builder MessageBuilder::initRoot(SchemaType schema) {
return getRootInternal().initAs<RootType>(schema);
}
template <typename RootType>
typename RootType::Reader readMessageUnchecked(const word* data) {
return AnyPointer::Reader(_::PointerReader::getRootUnchecked(data)).getAs<RootType>();
}
template <typename Reader>
void copyToUnchecked(Reader&& reader, kj::ArrayPtr<word> uncheckedBuffer) {
FlatMessageBuilder builder(uncheckedBuffer);
builder.setRoot(kj::fwd<Reader>(reader));
builder.requireFilled();
}
template <typename RootType>
typename RootType::Reader readDataStruct(kj::ArrayPtr<const word> data) {
return typename RootType::Reader(_::StructReader(data));
}
template <typename BuilderType>
typename kj::ArrayPtr<const word> writeDataStruct(BuilderType builder) {
auto bytes = _::PointerHelpers<FromBuilder<BuilderType>>::getInternalBuilder(kj::mv(builder))
.getDataSectionAsBlob();
return kj::arrayPtr(reinterpret_cast<word*>(bytes.begin()),
reinterpret_cast<word*>(bytes.end()));
}
template <typename Type>
static typename Type::Reader defaultValue() {
return typename Type::Reader(_::StructReader());
}
template <typename T>
kj::Array<word> canonicalize(T&& reader) {
return _::PointerHelpers<FromReader<T>>::getInternalReader(reader).canonicalize();
}
} // namespace capnp
#endif // CAPNP_MESSAGE_H_
+440
View File
@@ -0,0 +1,440 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_ORPHAN_H_
#define CAPNP_ORPHAN_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "layout.h"
namespace capnp {
class StructSchema;
class ListSchema;
struct DynamicStruct;
struct DynamicList;
namespace _ { struct OrphanageInternal; }
template <typename T>
class Orphan {
// Represents an object which is allocated within some message builder but has no pointers
// pointing at it. An Orphan can later be "adopted" by some other object as one of that object's
// fields, without having to copy the orphan. For a field `foo` of pointer type, the generated
// code will define builder methods `void adoptFoo(Orphan<T>)` and `Orphan<T> disownFoo()`.
// Orphans can also be created independently of any parent using an Orphanage.
//
// `Orphan<T>` can be moved but not copied, like `Own<T>`, so that it is impossible for one
// orphan to be adopted multiple times. If an orphan is destroyed without being adopted, its
// contents are zero'd out (and possibly reused, if we ever implement the ability to reuse space
// in a message arena).
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
Orphan(Orphan&&) = default;
Orphan& operator=(Orphan&&) = default;
inline Orphan(_::OrphanBuilder&& builder): builder(kj::mv(builder)) {}
inline BuilderFor<T> get();
// Get the underlying builder. If the orphan is null, this will allocate and return a default
// object rather than crash. This is done for security -- otherwise, you might enable a DoS
// attack any time you disown a field and fail to check if it is null. In the case of structs,
// this means that the orphan is no longer null after get() returns. In the case of lists,
// no actual object is allocated since a simple empty ListBuilder can be returned.
inline ReaderFor<T> getReader() const;
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
inline void truncate(uint size);
// Resize an object (which must be a list or a blob) to the given size.
//
// If the new size is less than the original, the remaining elements will be discarded. The
// list is never moved in this case. If the list happens to be located at the end of its segment
// (which is always true if the list was the last thing allocated), the removed memory will be
// reclaimed (reducing the messag size), otherwise it is simply zeroed. The reclaiming behavior
// is particularly useful for allocating buffer space when you aren't sure how much space you
// actually need: you can pre-allocate, say, a 4k byte array, read() from a file into it, and
// then truncate it back to the amount of space actually used.
//
// If the new size is greater than the original, the list is extended with default values. If
// the list is the last object in its segment *and* there is enough space left in the segment to
// extend it to cover the new values, then the list is extended in-place. Otherwise, it must be
// moved to a new location, leaving a zero'd hole in the previous space that won't be filled.
// This copy is shallow; sub-objects will simply be reparented, not copied.
//
// Any existing readers or builders pointing at the object are invalidated by this call (even if
// it doesn't move). You must call `get()` or `getReader()` again to get the new, valid pointer.
private:
_::OrphanBuilder builder;
template <typename, Kind>
friend struct _::PointerHelpers;
template <typename, Kind>
friend struct List;
template <typename U>
friend class Orphan;
friend class Orphanage;
friend class MessageBuilder;
};
class Orphanage: private kj::DisallowConstCopy {
// Use to directly allocate Orphan objects, without having a parent object allocate and then
// disown the object.
public:
inline Orphanage(): arena(nullptr) {}
template <typename BuilderType>
static Orphanage getForMessageContaining(BuilderType builder);
// Construct an Orphanage that allocates within the message containing the given Builder. This
// allows the constructed Orphans to be adopted by objects within said message.
//
// This constructor takes the builder rather than having the builder have a getOrphanage() method
// because this is an advanced feature and we don't want to pollute the builder APIs with it.
//
// Note that if you have a direct pointer to the `MessageBuilder`, you can simply call its
// `getOrphanage()` method.
template <typename RootType>
Orphan<RootType> newOrphan() const;
// Allocate a new orphaned struct.
template <typename RootType>
Orphan<RootType> newOrphan(uint size) const;
// Allocate a new orphaned list or blob.
Orphan<DynamicStruct> newOrphan(StructSchema schema) const;
// Dynamically create an orphan struct with the given schema. You must
// #include <capnp/dynamic.h> to use this.
Orphan<DynamicList> newOrphan(ListSchema schema, uint size) const;
// Dynamically create an orphan list with the given schema. You must #include <capnp/dynamic.h>
// to use this.
template <typename Reader>
Orphan<FromReader<Reader>> newOrphanCopy(Reader copyFrom) const;
// Allocate a new orphaned object (struct, list, or blob) and initialize it as a copy of the
// given object.
template <typename T>
Orphan<List<ListElementType<FromReader<T>>>> newOrphanConcat(kj::ArrayPtr<T> lists) const;
template <typename T>
Orphan<List<ListElementType<FromReader<T>>>> newOrphanConcat(kj::ArrayPtr<const T> lists) const;
// Given an array of List readers, copy and concatenate the lists, creating a new Orphan.
//
// Note that compared to allocating the list yourself and using `setWithCaveats()` to set each
// item, this method avoids the "caveats": the new list will be allocated with the element size
// being the maximum of that from all the input lists. This is particularly important when
// concatenating struct lists: if the lists were created using a newer version of the protocol
// in which some new fields had been added to the struct, using `setWithCaveats()` would
// truncate off those new fields.
Orphan<Data> referenceExternalData(Data::Reader data) const;
// Creates an Orphan<Data> that points at an existing region of memory (e.g. from another message)
// without copying it. There are some SEVERE restrictions on how this can be used:
// - The memory must remain valid until the `MessageBuilder` is destroyed (even if the orphan is
// abandoned).
// - Because the data is const, you will not be allowed to obtain a `Data::Builder`
// for this blob. Any call which would return such a builder will throw an exception. You
// can, however, obtain a Reader, e.g. via orphan.getReader() or from a parent Reader (once
// the orphan is adopted). It is your responsibility to make sure your code can deal with
// these problems when using this optimization; if you can't, allocate a copy instead.
// - `data.begin()` must be aligned to a machine word boundary (32-bit or 64-bit depending on
// the CPU). Any pointer returned by malloc() as well as any data blob obtained from another
// Cap'n Proto message satisfies this.
// - If `data.size()` is not a multiple of 8, extra bytes past data.end() up until the next 8-byte
// boundary will be visible in the raw message when it is written out. Thus, there must be no
// secrets in these bytes. Data blobs obtained from other Cap'n Proto messages should be safe
// as these bytes should be zero (unless the sender had the same problem).
//
// The array will actually become one of the message's segments. The data can thus be adopted
// into the message tree without copying it. This is particularly useful when referencing very
// large blobs, such as whole mmap'd files.
private:
_::BuilderArena* arena;
_::CapTableBuilder* capTable;
inline explicit Orphanage(_::BuilderArena* arena, _::CapTableBuilder* capTable)
: arena(arena), capTable(capTable) {}
template <typename T, Kind = CAPNP_KIND(T)>
struct GetInnerBuilder;
template <typename T, Kind = CAPNP_KIND(T)>
struct GetInnerReader;
template <typename T>
struct NewOrphanListImpl;
friend class MessageBuilder;
friend struct _::OrphanageInternal;
};
// =======================================================================================
// Inline implementation details.
namespace _ { // private
template <typename T, Kind = CAPNP_KIND(T)>
struct OrphanGetImpl;
template <typename T>
struct OrphanGetImpl<T, Kind::PRIMITIVE> {
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, _::elementSizeForType<T>());
}
};
template <typename T>
struct OrphanGetImpl<T, Kind::STRUCT> {
static inline typename T::Builder apply(_::OrphanBuilder& builder) {
return typename T::Builder(builder.asStruct(_::structSize<T>()));
}
static inline typename T::Reader applyReader(const _::OrphanBuilder& builder) {
return typename T::Reader(builder.asStructReader(_::structSize<T>()));
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, _::structSize<T>());
}
};
#if !CAPNP_LITE
template <typename T>
struct OrphanGetImpl<T, Kind::INTERFACE> {
static inline typename T::Client apply(_::OrphanBuilder& builder) {
return typename T::Client(builder.asCapability());
}
static inline typename T::Client applyReader(const _::OrphanBuilder& builder) {
return typename T::Client(builder.asCapability());
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
#endif // !CAPNP_LITE
template <typename T, Kind k>
struct OrphanGetImpl<List<T, k>, Kind::LIST> {
static inline typename List<T>::Builder apply(_::OrphanBuilder& builder) {
return typename List<T>::Builder(builder.asList(_::ElementSizeForType<T>::value));
}
static inline typename List<T>::Reader applyReader(const _::OrphanBuilder& builder) {
return typename List<T>::Reader(builder.asListReader(_::ElementSizeForType<T>::value));
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
template <typename T>
struct OrphanGetImpl<List<T, Kind::STRUCT>, Kind::LIST> {
static inline typename List<T>::Builder apply(_::OrphanBuilder& builder) {
return typename List<T>::Builder(builder.asStructList(_::structSize<T>()));
}
static inline typename List<T>::Reader applyReader(const _::OrphanBuilder& builder) {
return typename List<T>::Reader(builder.asListReader(_::ElementSizeForType<T>::value));
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
template <>
struct OrphanGetImpl<Text, Kind::BLOB> {
static inline Text::Builder apply(_::OrphanBuilder& builder) {
return Text::Builder(builder.asText());
}
static inline Text::Reader applyReader(const _::OrphanBuilder& builder) {
return Text::Reader(builder.asTextReader());
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
template <>
struct OrphanGetImpl<Data, Kind::BLOB> {
static inline Data::Builder apply(_::OrphanBuilder& builder) {
return Data::Builder(builder.asData());
}
static inline Data::Reader applyReader(const _::OrphanBuilder& builder) {
return Data::Reader(builder.asDataReader());
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
struct OrphanageInternal {
static inline _::BuilderArena* getArena(Orphanage orphanage) { return orphanage.arena; }
static inline _::CapTableBuilder* getCapTable(Orphanage orphanage) { return orphanage.capTable; }
};
} // namespace _ (private)
template <typename T>
inline BuilderFor<T> Orphan<T>::get() {
return _::OrphanGetImpl<T>::apply(builder);
}
template <typename T>
inline ReaderFor<T> Orphan<T>::getReader() const {
return _::OrphanGetImpl<T>::applyReader(builder);
}
template <typename T>
inline void Orphan<T>::truncate(uint size) {
_::OrphanGetImpl<ListElementType<T>>::truncateListOf(builder, bounded(size) * ELEMENTS);
}
template <>
inline void Orphan<Text>::truncate(uint size) {
builder.truncateText(bounded(size) * ELEMENTS);
}
template <>
inline void Orphan<Data>::truncate(uint size) {
builder.truncate(bounded(size) * ELEMENTS, ElementSize::BYTE);
}
template <typename T>
struct Orphanage::GetInnerBuilder<T, Kind::STRUCT> {
static inline _::StructBuilder apply(typename T::Builder& t) {
return t._builder;
}
};
template <typename T>
struct Orphanage::GetInnerBuilder<T, Kind::LIST> {
static inline _::ListBuilder apply(typename T::Builder& t) {
return t.builder;
}
};
template <typename BuilderType>
Orphanage Orphanage::getForMessageContaining(BuilderType builder) {
auto inner = GetInnerBuilder<FromBuilder<BuilderType>>::apply(builder);
return Orphanage(inner.getArena(), inner.getCapTable());
}
template <typename RootType>
Orphan<RootType> Orphanage::newOrphan() const {
return Orphan<RootType>(_::OrphanBuilder::initStruct(arena, capTable, _::structSize<RootType>()));
}
template <typename T, Kind k>
struct Orphanage::NewOrphanListImpl<List<T, k>> {
static inline _::OrphanBuilder apply(
_::BuilderArena* arena, _::CapTableBuilder* capTable, uint size) {
return _::OrphanBuilder::initList(
arena, capTable, bounded(size) * ELEMENTS, _::ElementSizeForType<T>::value);
}
};
template <typename T>
struct Orphanage::NewOrphanListImpl<List<T, Kind::STRUCT>> {
static inline _::OrphanBuilder apply(
_::BuilderArena* arena, _::CapTableBuilder* capTable, uint size) {
return _::OrphanBuilder::initStructList(
arena, capTable, bounded(size) * ELEMENTS, _::structSize<T>());
}
};
template <>
struct Orphanage::NewOrphanListImpl<Text> {
static inline _::OrphanBuilder apply(
_::BuilderArena* arena, _::CapTableBuilder* capTable, uint size) {
return _::OrphanBuilder::initText(arena, capTable, bounded(size) * BYTES);
}
};
template <>
struct Orphanage::NewOrphanListImpl<Data> {
static inline _::OrphanBuilder apply(
_::BuilderArena* arena, _::CapTableBuilder* capTable, uint size) {
return _::OrphanBuilder::initData(arena, capTable, bounded(size) * BYTES);
}
};
template <typename RootType>
Orphan<RootType> Orphanage::newOrphan(uint size) const {
return Orphan<RootType>(NewOrphanListImpl<RootType>::apply(arena, capTable, size));
}
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::STRUCT> {
static inline _::StructReader apply(const typename T::Reader& t) {
return t._reader;
}
};
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::LIST> {
static inline _::ListReader apply(const typename T::Reader& t) {
return t.reader;
}
};
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::BLOB> {
static inline const typename T::Reader& apply(const typename T::Reader& t) {
return t;
}
};
template <typename Reader>
inline Orphan<FromReader<Reader>> Orphanage::newOrphanCopy(Reader copyFrom) const {
return Orphan<FromReader<Reader>>(_::OrphanBuilder::copy(
arena, capTable, GetInnerReader<FromReader<Reader>>::apply(copyFrom)));
}
template <typename T>
inline Orphan<List<ListElementType<FromReader<T>>>>
Orphanage::newOrphanConcat(kj::ArrayPtr<T> lists) const {
return newOrphanConcat(kj::implicitCast<kj::ArrayPtr<const T>>(lists));
}
template <typename T>
inline Orphan<List<ListElementType<FromReader<T>>>>
Orphanage::newOrphanConcat(kj::ArrayPtr<const T> lists) const {
// Optimization / simplification: Rely on List<T>::Reader containing nothing except a
// _::ListReader.
static_assert(sizeof(T) == sizeof(_::ListReader), "lists are not bare readers?");
kj::ArrayPtr<const _::ListReader> raw(
reinterpret_cast<const _::ListReader*>(lists.begin()), lists.size());
typedef ListElementType<FromReader<T>> Element;
return Orphan<List<Element>>(
_::OrphanBuilder::concat(arena, capTable,
_::elementSizeForType<Element>(),
_::minStructSizeForElement<Element>(), raw));
}
inline Orphan<Data> Orphanage::referenceExternalData(Data::Reader data) const {
return Orphan<Data>(_::OrphanBuilder::referenceExternalData(arena, data));
}
} // namespace capnp
#endif // CAPNP_ORPHAN_H_
@@ -0,0 +1,139 @@
# Copyright (c) 2014 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0xb8630836983feed7;
$import "/capnp/c++.capnp".namespace("capnp");
interface Persistent@0xc8cb212fcd9f5691(SturdyRef, Owner) {
# Interface implemented by capabilities that outlive a single connection. A client may save()
# the capability, producing a SturdyRef. The SturdyRef can be stored to disk, then later used to
# obtain a new reference to the capability on a future connection.
#
# The exact format of SturdyRef depends on the "realm" in which the SturdyRef appears. A "realm"
# is an abstract space in which all SturdyRefs have the same format and refer to the same set of
# resources. Every vat is in exactly one realm. All capability clients within that vat must
# produce SturdyRefs of the format appropriate for the realm.
#
# Similarly, every VatNetwork also resides in a particular realm. Usually, a vat's "realm"
# corresponds to the realm of its main VatNetwork. However, a Vat can in fact communicate over
# a VatNetwork in a different realm -- in this case, all SturdyRefs need to be transformed when
# coming or going through said VatNetwork. The RPC system has hooks for registering
# transformation callbacks for this purpose.
#
# Since the format of SturdyRef is realm-dependent, it is not defined here. An application should
# choose an appropriate realm for itself as part of its design. Note that under Sandstorm, every
# application exists in its own realm and is therefore free to define its own SturdyRef format;
# the Sandstorm platform handles translating between realms.
#
# Note that whether a capability is persistent is often orthogonal to its type. In these cases,
# the capability's interface should NOT inherit `Persistent`; instead, just perform a cast at
# runtime. It's not type-safe, but trying to be type-safe in these cases will likely lead to
# tears. In cases where a particular interface only makes sense on persistent capabilities, it
# still should not explicitly inherit Persistent because the `SturdyRef` and `Owner` types will
# vary between realms (they may even be different at the call site than they are on the
# implementation). Instead, mark persistent interfaces with the $persistent annotation (defined
# below).
#
# Sealing
# -------
#
# As an added security measure, SturdyRefs may be "sealed" to a particular owner, such that
# if the SturdyRef itself leaks to a third party, that party cannot actually restore it because
# they are not the owner. To restore a sealed capability, you must first prove to its host that
# you are the rightful owner. The precise mechanism for this authentication is defined by the
# realm.
#
# Sealing is a defense-in-depth mechanism meant to mitigate damage in the case of catastrophic
# attacks. For example, say an attacker temporarily gains read access to a database full of
# SturdyRefs: it would be unfortunate if it were then necessary to revoke every single reference
# in the database to prevent the attacker from using them.
#
# In general, an "owner" is a course-grained identity. Because capability-based security is still
# the primary mechanism of security, it is not necessary nor desirable to have a separate "owner"
# identity for every single process or object; that is exactly what capabilities are supposed to
# avoid! Instead, it makes sense for an "owner" to literally identify the owner of the machines
# where the capability is stored. If untrusted third parties are able to run arbitrary code on
# said machines, then the sandbox for that code should be designed using Distributed Confinement
# such that the third-party code never sees the bits of the SturdyRefs and cannot directly
# exercise the owner's power to restore refs. See:
#
# http://www.erights.org/elib/capability/dist-confine.html
#
# Resist the urge to represent an Owner as a simple public key. The whole point of sealing is to
# defend against leaked-storage attacks. Such attacks can easily result in the owner's private
# key being stolen as well. A better solution is for `Owner` to contain a simple globally unique
# identifier for the owner, and for everyone to separately maintain a mapping of owner IDs to
# public keys. If an owner's private key is compromised, then humans will need to communicate
# and agree on a replacement public key, then update the mapping.
#
# As a concrete example, an `Owner` could simply contain a domain name, and restoring a SturdyRef
# would require signing a request using the domain's private key. Authenticating this key could
# be accomplished through certificate authorities or web-of-trust techniques.
save @0 SaveParams -> SaveResults;
# Save a capability persistently so that it can be restored by a future connection. Not all
# capabilities can be saved -- application interfaces should define which capabilities support
# this and which do not.
struct SaveParams {
sealFor @0 :Owner;
# Seal the SturdyRef so that it can only be restored by the specified Owner. This is meant
# to mitigate damage when a SturdyRef is leaked. See comments above.
#
# Leaving this value null may or may not be allowed; it is up to the realm to decide. If a
# realm does allow a null owner, this should indicate that anyone is allowed to restore the
# ref.
}
struct SaveResults {
sturdyRef @0 :SturdyRef;
}
}
interface RealmGateway(InternalRef, ExternalRef, InternalOwner, ExternalOwner) {
# Interface invoked when a SturdyRef is about to cross realms. The RPC system supports providing
# a RealmGateway as a callback hook when setting up RPC over some VatNetwork.
import @0 (cap :Persistent(ExternalRef, ExternalOwner),
params :Persistent(InternalRef, InternalOwner).SaveParams)
-> Persistent(InternalRef, InternalOwner).SaveResults;
# Given an external capability, save it and return an internal reference. Used when someone
# inside the realm tries to save a capability from outside the realm.
export @1 (cap :Persistent(InternalRef, InternalOwner),
params :Persistent(ExternalRef, ExternalOwner).SaveParams)
-> Persistent(ExternalRef, ExternalOwner).SaveResults;
# Given an internal capability, save it and return an external reference. Used when someone
# outside the realm tries to save a capability from inside the realm.
}
annotation persistent(interface, field) :Void;
# Apply this annotation to interfaces for objects that will always be persistent, instead of
# extending the Persistent capability, since the correct type parameters to Persistent depend on
# the realm, which is orthogonal to the interface type and therefore should not be defined
# along-side it.
#
# You may also apply this annotation to a capability-typed field which will always contain a
# persistent capability, but where the capability's interface itself is not already marked
# persistent.
#
# Note that absence of the $persistent annotation doesn't mean a capability of that type isn't
# persistent; it just means not *all* such capabilities are persistent.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,160 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_POINTER_HELPERS_H_
#define CAPNP_POINTER_HELPERS_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "layout.h"
#include "list.h"
namespace capnp {
namespace _ { // private
// PointerHelpers is a template class that assists in wrapping/unwrapping the low-level types in
// layout.h with the high-level public API and generated types. This way, the code generator
// and other templates do not have to specialize on each kind of pointer.
template <typename T>
struct PointerHelpers<T, Kind::STRUCT> {
static inline typename T::Reader get(PointerReader reader, const word* defaultValue = nullptr) {
return typename T::Reader(reader.getStruct(defaultValue));
}
static inline typename T::Builder get(PointerBuilder builder,
const word* defaultValue = nullptr) {
return typename T::Builder(builder.getStruct(structSize<T>(), defaultValue));
}
static inline void set(PointerBuilder builder, typename T::Reader value) {
builder.setStruct(value._reader);
}
static inline void setCanonical(PointerBuilder builder, typename T::Reader value) {
builder.setStruct(value._reader, true);
}
static inline typename T::Builder init(PointerBuilder builder) {
return typename T::Builder(builder.initStruct(structSize<T>()));
}
static inline void adopt(PointerBuilder builder, Orphan<T>&& value) {
builder.adopt(kj::mv(value.builder));
}
static inline Orphan<T> disown(PointerBuilder builder) {
return Orphan<T>(builder.disown());
}
static inline _::StructReader getInternalReader(const typename T::Reader& reader) {
return reader._reader;
}
static inline _::StructBuilder getInternalBuilder(typename T::Builder&& builder) {
return builder._builder;
}
};
template <typename T>
struct PointerHelpers<List<T>, Kind::LIST> {
static inline typename List<T>::Reader get(PointerReader reader,
const word* defaultValue = nullptr) {
return typename List<T>::Reader(List<T>::getFromPointer(reader, defaultValue));
}
static inline typename List<T>::Builder get(PointerBuilder builder,
const word* defaultValue = nullptr) {
return typename List<T>::Builder(List<T>::getFromPointer(builder, defaultValue));
}
static inline void set(PointerBuilder builder, typename List<T>::Reader value) {
builder.setList(value.reader);
}
static inline void setCanonical(PointerBuilder builder, typename List<T>::Reader value) {
builder.setList(value.reader, true);
}
static void set(PointerBuilder builder, kj::ArrayPtr<const ReaderFor<T>> value) {
auto l = init(builder, value.size());
uint i = 0;
for (auto& element: value) {
l.set(i++, element);
}
}
static inline typename List<T>::Builder init(PointerBuilder builder, uint size) {
return typename List<T>::Builder(List<T>::initPointer(builder, size));
}
static inline void adopt(PointerBuilder builder, Orphan<List<T>>&& value) {
builder.adopt(kj::mv(value.builder));
}
static inline Orphan<List<T>> disown(PointerBuilder builder) {
return Orphan<List<T>>(builder.disown());
}
static inline _::ListReader getInternalReader(const typename List<T>::Reader& reader) {
return reader.reader;
}
static inline _::ListBuilder getInternalBuilder(typename List<T>::Builder&& builder) {
return builder.builder;
}
};
template <typename T>
struct PointerHelpers<T, Kind::BLOB> {
static inline typename T::Reader get(PointerReader reader,
const void* defaultValue = nullptr,
uint defaultBytes = 0) {
return reader.getBlob<T>(defaultValue, bounded(defaultBytes) * BYTES);
}
static inline typename T::Builder get(PointerBuilder builder,
const void* defaultValue = nullptr,
uint defaultBytes = 0) {
return builder.getBlob<T>(defaultValue, bounded(defaultBytes) * BYTES);
}
static inline void set(PointerBuilder builder, typename T::Reader value) {
builder.setBlob<T>(value);
}
static inline void setCanonical(PointerBuilder builder, typename T::Reader value) {
builder.setBlob<T>(value);
}
static inline typename T::Builder init(PointerBuilder builder, uint size) {
return builder.initBlob<T>(bounded(size) * BYTES);
}
static inline void adopt(PointerBuilder builder, Orphan<T>&& value) {
builder.adopt(kj::mv(value.builder));
}
static inline Orphan<T> disown(PointerBuilder builder) {
return Orphan<T>(builder.disown());
}
};
struct UncheckedMessage {
typedef const word* Reader;
};
template <> struct Kind_<UncheckedMessage> { static constexpr Kind kind = Kind::OTHER; };
template <>
struct PointerHelpers<UncheckedMessage> {
// Reads an AnyPointer field as an unchecked message pointer. Requires that the containing
// message is itself unchecked. This hack is currently private. It is used to locate default
// values within encoded schemas.
static inline const word* get(PointerReader reader) {
return reader.getUnchecked();
}
};
} // namespace _ (private)
} // namespace capnp
#endif // CAPNP_POINTER_HELPERS_H_
@@ -0,0 +1,47 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_PRETTY_PRINT_H_
#define CAPNP_PRETTY_PRINT_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "dynamic.h"
#include <kj/string-tree.h>
namespace capnp {
kj::StringTree prettyPrint(DynamicStruct::Reader value);
kj::StringTree prettyPrint(DynamicStruct::Builder value);
kj::StringTree prettyPrint(DynamicList::Reader value);
kj::StringTree prettyPrint(DynamicList::Builder value);
// Print the given Cap'n Proto struct or list with nice indentation. Note that you can pass any
// struct or list reader or builder type to this method, since they can be implicitly converted
// to one of the dynamic types.
//
// If you don't want indentation, just use the value's KJ stringifier (e.g. pass it to kj::str(),
// any of the KJ debug macros, etc.).
} // namespace capnp
#endif // PRETTY_PRINT_H_
@@ -0,0 +1,242 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_RAW_SCHEMA_H_
#define CAPNP_RAW_SCHEMA_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "common.h" // for uint and friends
#if _MSC_VER
#include <atomic>
#endif
namespace capnp {
namespace _ { // private
struct RawSchema;
struct RawBrandedSchema {
// Represents a combination of a schema and bindings for its generic parameters.
//
// Note that while we generate one `RawSchema` per type, we generate a `RawBrandedSchema` for
// every _instance_ of a generic type -- or, at least, every instance that is actually used. For
// generated-code types, we use template magic to initialize these.
const RawSchema* generic;
// Generic type which we're branding.
struct Binding {
uint8_t which; // Numeric value of one of schema::Type::Which.
bool isImplicitParameter;
// For AnyPointer, true if it's an implicit method parameter.
uint16_t listDepth; // Number of times to wrap the base type in List().
uint16_t paramIndex;
// For AnyPointer. If it's a type parameter (scopeId is non-zero) or it's an implicit parameter
// (isImplicitParameter is true), then this is the parameter index. Otherwise this is a numeric
// value of one of schema::Type::AnyPointer::Unconstrained::Which.
union {
const RawBrandedSchema* schema; // for struct, enum, interface
uint64_t scopeId; // for AnyPointer, if it's a type parameter
};
Binding() = default;
inline constexpr Binding(uint8_t which, uint16_t listDepth, const RawBrandedSchema* schema)
: which(which), isImplicitParameter(false), listDepth(listDepth), paramIndex(0),
schema(schema) {}
inline constexpr Binding(uint8_t which, uint16_t listDepth,
uint64_t scopeId, uint16_t paramIndex)
: which(which), isImplicitParameter(false), listDepth(listDepth), paramIndex(paramIndex),
scopeId(scopeId) {}
inline constexpr Binding(uint8_t which, uint16_t listDepth, uint16_t implicitParamIndex)
: which(which), isImplicitParameter(true), listDepth(listDepth),
paramIndex(implicitParamIndex), scopeId(0) {}
};
struct Scope {
uint64_t typeId;
// Type ID whose parameters are being bound.
const Binding* bindings;
uint bindingCount;
// Bindings for those parameters.
bool isUnbound;
// This scope is unbound, in the sense of SchemaLoader::getUnbound().
};
const Scope* scopes;
// Array of enclosing scopes for which generic variables have been bound, sorted by type ID.
struct Dependency {
uint location;
const RawBrandedSchema* schema;
};
const Dependency* dependencies;
// Map of branded schemas for dependencies of this type, given our brand. Only dependencies that
// are branded are included in this map; if a dependency is missing, use its `defaultBrand`.
uint32_t scopeCount;
uint32_t dependencyCount;
enum class DepKind {
// Component of a Dependency::location. Specifies what sort of dependency this is.
INVALID,
// Mostly defined to ensure that zero is not a valid location.
FIELD,
// Binding needed for a field's type. The index is the field index (NOT ordinal!).
METHOD_PARAMS,
// Bindings needed for a method's params type. The index is the method number.
METHOD_RESULTS,
// Bindings needed for a method's results type. The index is the method ordinal.
SUPERCLASS,
// Bindings needed for a superclass type. The index is the superclass's index in the
// "extends" list.
CONST_TYPE
// Bindings needed for the type of a constant. The index is zero.
};
static inline uint makeDepLocation(DepKind kind, uint index) {
// Make a number representing the location of a particular dependency within its parent
// schema.
return (static_cast<uint>(kind) << 24) | index;
}
class Initializer {
public:
virtual void init(const RawBrandedSchema* generic) const = 0;
};
const Initializer* lazyInitializer;
// Lazy initializer, invoked by ensureInitialized().
inline void ensureInitialized() const {
// Lazy initialization support. Invoke to ensure that initialization has taken place. This
// is required in particular when traversing the dependency list. RawSchemas for compiled-in
// types are always initialized; only dynamically-loaded schemas may be lazy.
#if __GNUC__
const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_ACQUIRE);
#elif _MSC_VER
const Initializer* i = *static_cast<Initializer const* const volatile*>(&lazyInitializer);
std::atomic_thread_fence(std::memory_order_acquire);
#else
#error "Platform not supported"
#endif
if (i != nullptr) i->init(this);
}
inline bool isUnbound() const;
// Checks if this schema is the result of calling SchemaLoader::getUnbound(), in which case
// binding lookups need to be handled specially.
};
struct RawSchema {
// The generated code defines a constant RawSchema for every compiled declaration.
//
// This is an internal structure which could change in the future.
uint64_t id;
const word* encodedNode;
// Encoded SchemaNode, readable via readMessageUnchecked<schema::Node>(encodedNode).
uint32_t encodedSize;
// Size of encodedNode, in words.
const RawSchema* const* dependencies;
// Pointers to other types on which this one depends, sorted by ID. The schemas in this table
// may be uninitialized -- you must call ensureInitialized() on the one you wish to use before
// using it.
//
// TODO(someday): Make this a hashtable.
const uint16_t* membersByName;
// Indexes of members sorted by name. Used to implement name lookup.
// TODO(someday): Make this a hashtable.
uint32_t dependencyCount;
uint32_t memberCount;
// Sizes of above tables.
const uint16_t* membersByDiscriminant;
// List of all member indexes ordered by discriminant value. Those which don't have a
// discriminant value are listed at the end, in order by ordinal.
const RawSchema* canCastTo;
// Points to the RawSchema of a compiled-in type to which it is safe to cast any DynamicValue
// with this schema. This is null for all compiled-in types; it is only set by SchemaLoader on
// dynamically-loaded types.
class Initializer {
public:
virtual void init(const RawSchema* schema) const = 0;
};
const Initializer* lazyInitializer;
// Lazy initializer, invoked by ensureInitialized().
inline void ensureInitialized() const {
// Lazy initialization support. Invoke to ensure that initialization has taken place. This
// is required in particular when traversing the dependency list. RawSchemas for compiled-in
// types are always initialized; only dynamically-loaded schemas may be lazy.
#if __GNUC__
const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_ACQUIRE);
#elif _MSC_VER
const Initializer* i = *static_cast<Initializer const* const volatile*>(&lazyInitializer);
std::atomic_thread_fence(std::memory_order_acquire);
#else
#error "Platform not supported"
#endif
if (i != nullptr) i->init(this);
}
RawBrandedSchema defaultBrand;
// Specifies the brand to use for this schema if no generic parameters have been bound to
// anything. Generally, in the default brand, all generic parameters are treated as if they were
// bound to `AnyPointer`.
};
inline bool RawBrandedSchema::isUnbound() const {
// The unbound schema is the only one that has no scopes but is not the default schema.
return scopeCount == 0 && this != &generic->defaultBrand;
}
} // namespace _ (private)
} // namespace capnp
#endif // CAPNP_RAW_SCHEMA_H_
@@ -0,0 +1,130 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This file contains a bunch of internal declarations that must appear before rpc.h can start.
// We don't define these directly in rpc.h because it makes the file hard to read.
#ifndef CAPNP_RPC_PRELUDE_H_
#define CAPNP_RPC_PRELUDE_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "capability.h"
#include "persistent.capnp.h"
namespace capnp {
class OutgoingRpcMessage;
class IncomingRpcMessage;
template <typename SturdyRefHostId>
class RpcSystem;
namespace _ { // private
class VatNetworkBase {
// Non-template version of VatNetwork. Ignore this class; see VatNetwork in rpc.h.
public:
class Connection;
struct ConnectionAndProvisionId {
kj::Own<Connection> connection;
kj::Own<OutgoingRpcMessage> firstMessage;
Orphan<AnyPointer> provisionId;
};
class Connection {
public:
virtual kj::Own<OutgoingRpcMessage> newOutgoingMessage(uint firstSegmentWordSize) = 0;
virtual kj::Promise<kj::Maybe<kj::Own<IncomingRpcMessage>>> receiveIncomingMessage() = 0;
virtual kj::Promise<void> shutdown() = 0;
virtual AnyStruct::Reader baseGetPeerVatId() = 0;
};
virtual kj::Maybe<kj::Own<Connection>> baseConnect(AnyStruct::Reader vatId) = 0;
virtual kj::Promise<kj::Own<Connection>> baseAccept() = 0;
};
class SturdyRefRestorerBase {
public:
virtual Capability::Client baseRestore(AnyPointer::Reader ref) = 0;
};
class BootstrapFactoryBase {
// Non-template version of BootstrapFactory. Ignore this class; see BootstrapFactory in rpc.h.
public:
virtual Capability::Client baseCreateFor(AnyStruct::Reader clientId) = 0;
};
class RpcSystemBase {
// Non-template version of RpcSystem. Ignore this class; see RpcSystem in rpc.h.
public:
RpcSystemBase(VatNetworkBase& network, kj::Maybe<Capability::Client> bootstrapInterface,
kj::Maybe<RealmGateway<>::Client> gateway);
RpcSystemBase(VatNetworkBase& network, BootstrapFactoryBase& bootstrapFactory,
kj::Maybe<RealmGateway<>::Client> gateway);
RpcSystemBase(VatNetworkBase& network, SturdyRefRestorerBase& restorer);
RpcSystemBase(RpcSystemBase&& other) noexcept;
~RpcSystemBase() noexcept(false);
private:
class Impl;
kj::Own<Impl> impl;
Capability::Client baseBootstrap(AnyStruct::Reader vatId);
Capability::Client baseRestore(AnyStruct::Reader vatId, AnyPointer::Reader objectId);
void baseSetFlowLimit(size_t words);
template <typename>
friend class capnp::RpcSystem;
};
template <typename T> struct InternalRefFromRealmGateway_;
template <typename InternalRef, typename ExternalRef, typename InternalOwner,
typename ExternalOwner>
struct InternalRefFromRealmGateway_<RealmGateway<InternalRef, ExternalRef, InternalOwner,
ExternalOwner>> {
typedef InternalRef Type;
};
template <typename T>
using InternalRefFromRealmGateway = typename InternalRefFromRealmGateway_<T>::Type;
template <typename T>
using InternalRefFromRealmGatewayClient = InternalRefFromRealmGateway<typename T::Calls>;
template <typename T> struct ExternalRefFromRealmGateway_;
template <typename InternalRef, typename ExternalRef, typename InternalOwner,
typename ExternalOwner>
struct ExternalRefFromRealmGateway_<RealmGateway<InternalRef, ExternalRef, InternalOwner,
ExternalOwner>> {
typedef ExternalRef Type;
};
template <typename T>
using ExternalRefFromRealmGateway = typename ExternalRefFromRealmGateway_<T>::Type;
template <typename T>
using ExternalRefFromRealmGatewayClient = ExternalRefFromRealmGateway<typename T::Calls>;
} // namespace _ (private)
} // namespace capnp
#endif // CAPNP_RPC_PRELUDE_H_
@@ -0,0 +1,169 @@
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0xa184c7885cdaf2a1;
# This file defines the "network-specific parameters" in rpc.capnp to support a network consisting
# of two vats. Each of these vats may in fact be in communication with other vats, but any
# capabilities they forward must be proxied. Thus, to each end of the connection, all capabilities
# received from the other end appear to live in a single vat.
#
# Two notable use cases for this model include:
# - Regular client-server communications, where a remote client machine (perhaps living on an end
# user's personal device) connects to a server. The server may be part of a cluster, and may
# call on other servers in the cluster to help service the user's request. It may even obtain
# capabilities from these other servers which it passes on to the user. To simplify network
# common traversal problems (e.g. if the user is behind a firewall), it is probably desirable to
# multiplex all communications between the server cluster and the client over the original
# connection rather than form new ones. This connection should use the two-party protocol, as
# the client has no interest in knowing about additional servers.
# - Applications running in a sandbox. A supervisor process may execute a confined application
# such that all of the confined app's communications with the outside world must pass through
# the supervisor. In this case, the connection between the confined app and the supervisor might
# as well use the two-party protocol, because the confined app is intentionally prevented from
# talking to any other vat anyway. Any external resources will be proxied through the supervisor,
# and so to the contained app will appear as if they were hosted by the supervisor itself.
#
# Since there are only two vats in this network, there is never a need for three-way introductions,
# so level 3 is free. Moreover, because it is never necessary to form new connections, the
# two-party protocol can be used easily anywhere where a two-way byte stream exists, without regard
# to where that byte stream goes or how it was initiated. This makes the two-party runtime library
# highly reusable.
#
# Joins (level 4) _could_ be needed in cases where one or both vats are participating in other
# networks that use joins. For instance, if Alice and Bob are speaking through the two-party
# protocol, and Bob is also participating on another network, Bob may send Alice two or more
# proxied capabilities which, unbeknownst to Bob at the time, are in fact pointing at the same
# remote object. Alice may then request to join these capabilities, at which point Bob will have
# to forward the join to the other network. Note, however, that if Alice is _not_ participating on
# any other network, then Alice will never need to _receive_ a Join, because Alice would always
# know when two locally-hosted capabilities are the same and would never export a redundant alias
# to Bob. So, Alice can respond to all incoming joins with an error, and only needs to implement
# outgoing joins if she herself desires to use this feature. Also, outgoing joins are relatively
# easy to implement in this scenario.
#
# What all this means is that a level 4 implementation of the confined network is barely more
# complicated than a level 2 implementation. However, such an implementation allows the "client"
# or "confined" app to access the server's/supervisor's network with equal functionality to any
# native participant. In other words, an application which implements only the two-party protocol
# can be paired with a proxy app in order to participate in any network.
#
# So, when implementing Cap'n Proto in a new language, it makes sense to implement only the
# two-party protocol initially, and then pair applications with an appropriate proxy written in
# C++, rather than implement other parameterizations of the RPC protocol directly.
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("capnp::rpc::twoparty");
# Note: SturdyRef is not specified here. It is up to the application to define semantics of
# SturdyRefs if desired.
enum Side {
server @0;
# The object lives on the "server" or "supervisor" end of the connection. Only the
# server/supervisor knows how to interpret the ref; to the client, it is opaque.
#
# Note that containers intending to implement strong confinement should rewrite SturdyRefs
# received from the external network before passing them on to the confined app. The confined
# app thus does not ever receive the raw bits of the SturdyRef (which it could perhaps
# maliciously leak), but instead receives only a thing that it can pass back to the container
# later to restore the ref. See:
# http://www.erights.org/elib/capability/dist-confine.html
client @1;
# The object lives on the "client" or "confined app" end of the connection. Only the client
# knows how to interpret the ref; to the server/supervisor, it is opaque. Most clients do not
# actually know how to persist capabilities at all, so use of this is unusual.
}
struct VatId {
side @0 :Side;
}
struct ProvisionId {
# Only used for joins, since three-way introductions never happen on a two-party network.
joinId @0 :UInt32;
# The ID from `JoinKeyPart`.
}
struct RecipientId {}
# Never used, because there are only two parties.
struct ThirdPartyCapId {}
# Never used, because there is no third party.
struct JoinKeyPart {
# Joins in the two-party case are simplified by a few observations.
#
# First, on a two-party network, a Join only ever makes sense if the receiving end is also
# connected to other networks. A vat which is not connected to any other network can safely
# reject all joins.
#
# Second, since a two-party connection bisects the network -- there can be no other connections
# between the networks at either end of the connection -- if one part of a join crosses the
# connection, then _all_ parts must cross it. Therefore, a vat which is receiving a Join request
# off some other network which needs to be forwarded across the two-party connection can
# collect all the parts on its end and only forward them across the two-party connection when all
# have been received.
#
# For example, imagine that Alice and Bob are vats connected over a two-party connection, and
# each is also connected to other networks. At some point, Alice receives one part of a Join
# request off her network. The request is addressed to a capability that Alice received from
# Bob and is proxying to her other network. Alice goes ahead and responds to the Join part as
# if she hosted the capability locally (this is important so that if not all the Join parts end
# up at Alice, the original sender can detect the failed Join without hanging). As other parts
# trickle in, Alice verifies that each part is addressed to a capability from Bob and continues
# to respond to each one. Once the complete set of join parts is received, Alice checks if they
# were all for the exact same capability. If so, she doesn't need to send anything to Bob at
# all. Otherwise, she collects the set of capabilities (from Bob) to which the join parts were
# addressed and essentially initiates a _new_ Join request on those capabilities to Bob. Alice
# does not forward the Join parts she received herself, but essentially forwards the Join as a
# whole.
#
# On Bob's end, since he knows that Alice will always send all parts of a Join together, he
# simply waits until he's received them all, then performs a join on the respective capabilities
# as if it had been requested locally.
joinId @0 :UInt32;
# A number identifying this join, chosen by the sender. May be reused once `Finish` messages are
# sent corresponding to all of the `Join` messages.
partCount @1 :UInt16;
# The number of capabilities to be joined.
partNum @2 :UInt16;
# Which part this request targets -- a number in the range [0, partCount).
}
struct JoinResult {
joinId @0 :UInt32;
# Matches `JoinKeyPart`.
succeeded @1 :Bool;
# All JoinResults in the set will have the same value for `succeeded`. The receiver actually
# implements the join by waiting for all the `JoinKeyParts` and then performing its own join on
# them, then going back and answering all the join requests afterwards.
cap @2 :AnyPointer;
# One of the JoinResults will have a non-null `cap` which is the joined capability.
#
# TODO(cleanup): Change `AnyPointer` to `Capability` when that is supported.
}
@@ -0,0 +1,726 @@
// Generated by Cap'n Proto compiler, DO NOT EDIT
// source: rpc-twoparty.capnp
#ifndef CAPNP_INCLUDED_a184c7885cdaf2a1_
#define CAPNP_INCLUDED_a184c7885cdaf2a1_
#include <capnp/generated-header-support.h>
#if CAPNP_VERSION != 6001
#error "Version mismatch between generated code and library headers. You must use the same version of the Cap'n Proto compiler and library."
#endif
namespace capnp {
namespace schemas {
CAPNP_DECLARE_SCHEMA(9fd69ebc87b9719c);
enum class Side_9fd69ebc87b9719c: uint16_t {
SERVER,
CLIENT,
};
CAPNP_DECLARE_ENUM(Side, 9fd69ebc87b9719c);
CAPNP_DECLARE_SCHEMA(d20b909fee733a8e);
CAPNP_DECLARE_SCHEMA(b88d09a9c5f39817);
CAPNP_DECLARE_SCHEMA(89f389b6fd4082c1);
CAPNP_DECLARE_SCHEMA(b47f4979672cb59d);
CAPNP_DECLARE_SCHEMA(95b29059097fca83);
CAPNP_DECLARE_SCHEMA(9d263a3630b7ebee);
} // namespace schemas
} // namespace capnp
namespace capnp {
namespace rpc {
namespace twoparty {
typedef ::capnp::schemas::Side_9fd69ebc87b9719c Side;
struct VatId {
VatId() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(d20b909fee733a8e, 1, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct ProvisionId {
ProvisionId() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(b88d09a9c5f39817, 1, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct RecipientId {
RecipientId() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(89f389b6fd4082c1, 0, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct ThirdPartyCapId {
ThirdPartyCapId() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(b47f4979672cb59d, 0, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct JoinKeyPart {
JoinKeyPart() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(95b29059097fca83, 1, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct JoinResult {
JoinResult() = delete;
class Reader;
class Builder;
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(9d263a3630b7ebee, 1, 1)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
// =======================================================================================
class VatId::Reader {
public:
typedef VatId Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline ::capnp::rpc::twoparty::Side getSide() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class VatId::Builder {
public:
typedef VatId Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline ::capnp::rpc::twoparty::Side getSide();
inline void setSide( ::capnp::rpc::twoparty::Side value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class VatId::Pipeline {
public:
typedef VatId Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class ProvisionId::Reader {
public:
typedef ProvisionId Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline ::uint32_t getJoinId() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class ProvisionId::Builder {
public:
typedef ProvisionId Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline ::uint32_t getJoinId();
inline void setJoinId( ::uint32_t value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class ProvisionId::Pipeline {
public:
typedef ProvisionId Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class RecipientId::Reader {
public:
typedef RecipientId Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class RecipientId::Builder {
public:
typedef RecipientId Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class RecipientId::Pipeline {
public:
typedef RecipientId Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class ThirdPartyCapId::Reader {
public:
typedef ThirdPartyCapId Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class ThirdPartyCapId::Builder {
public:
typedef ThirdPartyCapId Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class ThirdPartyCapId::Pipeline {
public:
typedef ThirdPartyCapId Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class JoinKeyPart::Reader {
public:
typedef JoinKeyPart Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline ::uint32_t getJoinId() const;
inline ::uint16_t getPartCount() const;
inline ::uint16_t getPartNum() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class JoinKeyPart::Builder {
public:
typedef JoinKeyPart Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline ::uint32_t getJoinId();
inline void setJoinId( ::uint32_t value);
inline ::uint16_t getPartCount();
inline void setPartCount( ::uint16_t value);
inline ::uint16_t getPartNum();
inline void setPartNum( ::uint16_t value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class JoinKeyPart::Pipeline {
public:
typedef JoinKeyPart Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class JoinResult::Reader {
public:
typedef JoinResult Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline ::uint32_t getJoinId() const;
inline bool getSucceeded() const;
inline bool hasCap() const;
inline ::capnp::AnyPointer::Reader getCap() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class JoinResult::Builder {
public:
typedef JoinResult Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline ::uint32_t getJoinId();
inline void setJoinId( ::uint32_t value);
inline bool getSucceeded();
inline void setSucceeded(bool value);
inline bool hasCap();
inline ::capnp::AnyPointer::Builder getCap();
inline ::capnp::AnyPointer::Builder initCap();
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class JoinResult::Pipeline {
public:
typedef JoinResult Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
// =======================================================================================
inline ::capnp::rpc::twoparty::Side VatId::Reader::getSide() const {
return _reader.getDataField< ::capnp::rpc::twoparty::Side>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline ::capnp::rpc::twoparty::Side VatId::Builder::getSide() {
return _builder.getDataField< ::capnp::rpc::twoparty::Side>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline void VatId::Builder::setSide( ::capnp::rpc::twoparty::Side value) {
_builder.setDataField< ::capnp::rpc::twoparty::Side>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline ::uint32_t ProvisionId::Reader::getJoinId() const {
return _reader.getDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline ::uint32_t ProvisionId::Builder::getJoinId() {
return _builder.getDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline void ProvisionId::Builder::setJoinId( ::uint32_t value) {
_builder.setDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline ::uint32_t JoinKeyPart::Reader::getJoinId() const {
return _reader.getDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline ::uint32_t JoinKeyPart::Builder::getJoinId() {
return _builder.getDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline void JoinKeyPart::Builder::setJoinId( ::uint32_t value) {
_builder.setDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline ::uint16_t JoinKeyPart::Reader::getPartCount() const {
return _reader.getDataField< ::uint16_t>(
::capnp::bounded<2>() * ::capnp::ELEMENTS);
}
inline ::uint16_t JoinKeyPart::Builder::getPartCount() {
return _builder.getDataField< ::uint16_t>(
::capnp::bounded<2>() * ::capnp::ELEMENTS);
}
inline void JoinKeyPart::Builder::setPartCount( ::uint16_t value) {
_builder.setDataField< ::uint16_t>(
::capnp::bounded<2>() * ::capnp::ELEMENTS, value);
}
inline ::uint16_t JoinKeyPart::Reader::getPartNum() const {
return _reader.getDataField< ::uint16_t>(
::capnp::bounded<3>() * ::capnp::ELEMENTS);
}
inline ::uint16_t JoinKeyPart::Builder::getPartNum() {
return _builder.getDataField< ::uint16_t>(
::capnp::bounded<3>() * ::capnp::ELEMENTS);
}
inline void JoinKeyPart::Builder::setPartNum( ::uint16_t value) {
_builder.setDataField< ::uint16_t>(
::capnp::bounded<3>() * ::capnp::ELEMENTS, value);
}
inline ::uint32_t JoinResult::Reader::getJoinId() const {
return _reader.getDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline ::uint32_t JoinResult::Builder::getJoinId() {
return _builder.getDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline void JoinResult::Builder::setJoinId( ::uint32_t value) {
_builder.setDataField< ::uint32_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline bool JoinResult::Reader::getSucceeded() const {
return _reader.getDataField<bool>(
::capnp::bounded<32>() * ::capnp::ELEMENTS);
}
inline bool JoinResult::Builder::getSucceeded() {
return _builder.getDataField<bool>(
::capnp::bounded<32>() * ::capnp::ELEMENTS);
}
inline void JoinResult::Builder::setSucceeded(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<32>() * ::capnp::ELEMENTS, value);
}
inline bool JoinResult::Reader::hasCap() const {
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline bool JoinResult::Builder::hasCap() {
return !_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
}
inline ::capnp::AnyPointer::Reader JoinResult::Reader::getCap() const {
return ::capnp::AnyPointer::Reader(_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::AnyPointer::Builder JoinResult::Builder::getCap() {
return ::capnp::AnyPointer::Builder(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline ::capnp::AnyPointer::Builder JoinResult::Builder::initCap() {
auto result = ::capnp::AnyPointer::Builder(_builder.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS));
result.clear();
return result;
}
} // namespace
} // namespace
} // namespace
#endif // CAPNP_INCLUDED_a184c7885cdaf2a1_
@@ -0,0 +1,160 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_RPC_TWOPARTY_H_
#define CAPNP_RPC_TWOPARTY_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "rpc.h"
#include "message.h"
#include <kj/async-io.h>
#include <capnp/rpc-twoparty.capnp.h>
namespace capnp {
namespace rpc {
namespace twoparty {
typedef VatId SturdyRefHostId; // For backwards-compatibility with version 0.4.
}
}
typedef VatNetwork<rpc::twoparty::VatId, rpc::twoparty::ProvisionId,
rpc::twoparty::RecipientId, rpc::twoparty::ThirdPartyCapId, rpc::twoparty::JoinResult>
TwoPartyVatNetworkBase;
class TwoPartyVatNetwork: public TwoPartyVatNetworkBase,
private TwoPartyVatNetworkBase::Connection {
// A `VatNetwork` that consists of exactly two parties communicating over an arbitrary byte
// stream. This is used to implement the common case of a client/server network.
//
// See `ez-rpc.h` for a simple interface for setting up two-party clients and servers.
// Use `TwoPartyVatNetwork` only if you need the advanced features.
public:
TwoPartyVatNetwork(kj::AsyncIoStream& stream, rpc::twoparty::Side side,
ReaderOptions receiveOptions = ReaderOptions());
KJ_DISALLOW_COPY(TwoPartyVatNetwork);
kj::Promise<void> onDisconnect() { return disconnectPromise.addBranch(); }
// Returns a promise that resolves when the peer disconnects.
rpc::twoparty::Side getSide() { return side; }
// implements VatNetwork -----------------------------------------------------
kj::Maybe<kj::Own<TwoPartyVatNetworkBase::Connection>> connect(
rpc::twoparty::VatId::Reader ref) override;
kj::Promise<kj::Own<TwoPartyVatNetworkBase::Connection>> accept() override;
private:
class OutgoingMessageImpl;
class IncomingMessageImpl;
kj::AsyncIoStream& stream;
rpc::twoparty::Side side;
MallocMessageBuilder peerVatId;
ReaderOptions receiveOptions;
bool accepted = false;
kj::Maybe<kj::Promise<void>> previousWrite;
// Resolves when the previous write completes. This effectively serves as the write queue.
// Becomes null when shutdown() is called.
kj::Own<kj::PromiseFulfiller<kj::Own<TwoPartyVatNetworkBase::Connection>>> acceptFulfiller;
// Fulfiller for the promise returned by acceptConnectionAsRefHost() on the client side, or the
// second call on the server side. Never fulfilled, because there is only one connection.
kj::ForkedPromise<void> disconnectPromise = nullptr;
class FulfillerDisposer: public kj::Disposer {
// Hack: TwoPartyVatNetwork is both a VatNetwork and a VatNetwork::Connection. When the RPC
// system detects (or initiates) a disconnection, it drops its reference to the Connection.
// When all references have been dropped, then we want disconnectPromise to be fulfilled.
// So we hand out Own<Connection>s with this disposer attached, so that we can detect when
// they are dropped.
public:
mutable kj::Own<kj::PromiseFulfiller<void>> fulfiller;
mutable uint refcount = 0;
void disposeImpl(void* pointer) const override;
};
FulfillerDisposer disconnectFulfiller;
kj::Own<TwoPartyVatNetworkBase::Connection> asConnection();
// Returns a pointer to this with the disposer set to disconnectFulfiller.
// implements Connection -----------------------------------------------------
rpc::twoparty::VatId::Reader getPeerVatId() override;
kj::Own<OutgoingRpcMessage> newOutgoingMessage(uint firstSegmentWordSize) override;
kj::Promise<kj::Maybe<kj::Own<IncomingRpcMessage>>> receiveIncomingMessage() override;
kj::Promise<void> shutdown() override;
};
class TwoPartyServer: private kj::TaskSet::ErrorHandler {
// Convenience class which implements a simple server which accepts connections on a listener
// socket and serices them as two-party connections.
public:
explicit TwoPartyServer(Capability::Client bootstrapInterface);
void accept(kj::Own<kj::AsyncIoStream>&& connection);
// Accepts the connection for servicing.
kj::Promise<void> listen(kj::ConnectionReceiver& listener);
// Listens for connections on the given listener. The returned promise never resolves unless an
// exception is thrown while trying to accept. You may discard the returned promise to cancel
// listening.
private:
Capability::Client bootstrapInterface;
kj::TaskSet tasks;
struct AcceptedConnection;
void taskFailed(kj::Exception&& exception) override;
};
class TwoPartyClient {
// Convenience class which implements a simple client.
public:
explicit TwoPartyClient(kj::AsyncIoStream& connection);
TwoPartyClient(kj::AsyncIoStream& connection, Capability::Client bootstrapInterface,
rpc::twoparty::Side side = rpc::twoparty::Side::CLIENT);
Capability::Client bootstrap();
// Get the server's bootstrap interface.
inline kj::Promise<void> onDisconnect() { return network.onDisconnect(); }
private:
TwoPartyVatNetwork network;
RpcSystem<rpc::twoparty::VatId> rpcSystem;
};
} // namespace capnp
#endif // CAPNP_RPC_TWOPARTY_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+537
View File
@@ -0,0 +1,537 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_RPC_H_
#define CAPNP_RPC_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "capability.h"
#include "rpc-prelude.h"
namespace capnp {
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
class VatNetwork;
template <typename SturdyRefObjectId>
class SturdyRefRestorer;
template <typename VatId>
class BootstrapFactory: public _::BootstrapFactoryBase {
// Interface that constructs per-client bootstrap interfaces. Use this if you want each client
// who connects to see a different bootstrap interface based on their (authenticated) VatId.
// This allows an application to bootstrap off of the authentication performed at the VatNetwork
// level. (Typically VatId is some sort of public key.)
//
// This is only useful for multi-party networks. For TwoPartyVatNetwork, there's no reason to
// use a BootstrapFactory; just specify a single bootstrap capability in this case.
public:
virtual Capability::Client createFor(typename VatId::Reader clientId) = 0;
// Create a bootstrap capability appropriate for exposing to the given client. VatNetwork will
// have authenticated the client VatId before this is called.
private:
Capability::Client baseCreateFor(AnyStruct::Reader clientId) override;
};
template <typename VatId>
class RpcSystem: public _::RpcSystemBase {
// Represents the RPC system, which is the portal to objects available on the network.
//
// The RPC implementation sits on top of an implementation of `VatNetwork`. The `VatNetwork`
// determines how to form connections between vats -- specifically, two-way, private, reliable,
// sequenced datagram connections. The RPC implementation determines how to use such connections
// to manage object references and make method calls.
//
// See `makeRpcServer()` and `makeRpcClient()` below for convenient syntax for setting up an
// `RpcSystem` given a `VatNetwork`.
//
// See `ez-rpc.h` for an even simpler interface for setting up RPC in a typical two-party
// client/server scenario.
public:
template <typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
kj::Maybe<Capability::Client> bootstrapInterface,
kj::Maybe<RealmGateway<>::Client> gateway = nullptr);
template <typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
BootstrapFactory<VatId>& bootstrapFactory,
kj::Maybe<RealmGateway<>::Client> gateway = nullptr);
template <typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult,
typename LocalSturdyRefObjectId>
RpcSystem(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
SturdyRefRestorer<LocalSturdyRefObjectId>& restorer);
RpcSystem(RpcSystem&& other) = default;
Capability::Client bootstrap(typename VatId::Reader vatId);
// Connect to the given vat and return its bootstrap interface.
Capability::Client restore(typename VatId::Reader hostId, AnyPointer::Reader objectId)
KJ_DEPRECATED("Please transition to using a bootstrap interface instead.");
// ** DEPRECATED **
//
// Restores the given SturdyRef from the network and return the capability representing it.
//
// `hostId` identifies the host from which to request the ref, in the format specified by the
// `VatNetwork` in use. `objectId` is the object ID in whatever format is expected by said host.
//
// This method will be removed in a future version of Cap'n Proto. Instead, please transition
// to using bootstrap(), which is equivalent to calling restore() with a null `objectId`.
// You may emulate the old concept of object IDs by exporting a bootstrap interface which has
// methods that can be used to obtain other capabilities by ID.
void setFlowLimit(size_t words);
// Sets the incoming call flow limit. If more than `words` worth of call messages have not yet
// received responses, the RpcSystem will not read further messages from the stream. This can be
// used as a crude way to prevent a resource exhaustion attack (or bug) in which a peer makes an
// excessive number of simultaneous calls that consume the receiver's RAM.
//
// There are some caveats. When over the flow limit, all messages are blocked, including returns.
// If the outstanding calls are themselves waiting on calls going in the opposite direction, the
// flow limit may prevent those calls from completing, leading to deadlock. However, a
// sufficiently high limit should make this unlikely.
//
// Note that a call's parameter size counts against the flow limit until the call returns, even
// if the recipient calls releaseParams() to free the parameter memory early. This is because
// releaseParams() may simply indicate that the parameters have been forwarded to another
// machine, but are still in-memory there. For illustration, say that Alice made a call to Bob
// who forwarded the call to Carol. Bob has imposed a flow limit on Alice. Alice's calls are
// being forwarded to Carol, so Bob never keeps the parameters in-memory for more than a brief
// period. However, the flow limit counts all calls that haven't returned, even if Bob has
// already freed the memory they consumed. You might argue that the right solution here is
// instead for Carol to impose her own flow limit on Bob. This has a serious problem, though:
// Bob might be forwarding requests to Carol on behalf of many different parties, not just Alice.
// If Alice can pump enough data to hit the Bob -> Carol flow limit, then those other parties
// will be disrupted. Thus, we can only really impose the limit on the Alice -> Bob link, which
// only affects Alice. We need that one flow limit to limit Alice's impact on the whole system,
// so it has to count all in-flight calls.
//
// In Sandstorm, flow limits are imposed by the supervisor on calls coming out of a grain, in
// order to prevent a grain from inundating the system with in-flight calls. In practice, the
// main time this happens is when a grain is pushing a large file download and doesn't implement
// proper cooperative flow control.
};
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
Capability::Client bootstrapInterface);
// Make an RPC server. Typical usage (e.g. in a main() function):
//
// MyEventLoop eventLoop;
// kj::WaitScope waitScope(eventLoop);
// MyNetwork network;
// MyMainInterface::Client bootstrap = makeMain();
// auto server = makeRpcServer(network, bootstrap);
// kj::NEVER_DONE.wait(waitScope); // run forever
//
// See also ez-rpc.h, which has simpler instructions for the common case of a two-party
// client-server RPC connection.
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult, typename RealmGatewayClient,
typename InternalRef = _::InternalRefFromRealmGatewayClient<RealmGatewayClient>,
typename ExternalRef = _::ExternalRefFromRealmGatewayClient<RealmGatewayClient>>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
Capability::Client bootstrapInterface, RealmGatewayClient gateway);
// Make an RPC server for a VatNetwork that resides in a different realm from the application.
// The given RealmGateway is used to translate SturdyRefs between the app's ("internal") format
// and the network's ("external") format.
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
BootstrapFactory<VatId>& bootstrapFactory);
// Make an RPC server that can serve different bootstrap interfaces to different clients via a
// BootstrapInterface.
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult, typename RealmGatewayClient,
typename InternalRef = _::InternalRefFromRealmGatewayClient<RealmGatewayClient>,
typename ExternalRef = _::ExternalRefFromRealmGatewayClient<RealmGatewayClient>>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
BootstrapFactory<VatId>& bootstrapFactory, RealmGatewayClient gateway);
// Make an RPC server that can serve different bootstrap interfaces to different clients via a
// BootstrapInterface and communicates with a different realm than the application is in via a
// RealmGateway.
template <typename VatId, typename LocalSturdyRefObjectId,
typename ProvisionId, typename RecipientId, typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
SturdyRefRestorer<LocalSturdyRefObjectId>& restorer)
KJ_DEPRECATED("Please transition to using a bootstrap interface instead.");
// ** DEPRECATED **
//
// Create an RPC server which exports multiple main interfaces by object ID. The `restorer` object
// can be used to look up objects by ID.
//
// Please transition to exporting only one interface, which is known as the "bootstrap" interface.
// For backwards-compatibility with old clients, continue to implement SturdyRefRestorer, but
// return the new bootstrap interface when the request object ID is null. When new clients connect
// and request the bootstrap interface, they will get that interface. Eventually, once all clients
// are updated to request only the bootstrap interface, stop implementing SturdyRefRestorer and
// switch to passing the bootstrap capability itself as the second parameter to `makeRpcServer()`.
template <typename VatId, typename ProvisionId,
typename RecipientId, typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcClient(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network);
// Make an RPC client. Typical usage (e.g. in a main() function):
//
// MyEventLoop eventLoop;
// kj::WaitScope waitScope(eventLoop);
// MyNetwork network;
// auto client = makeRpcClient(network);
// MyCapability::Client cap = client.restore(hostId, objId).castAs<MyCapability>();
// auto response = cap.fooRequest().send().wait(waitScope);
// handleMyResponse(response);
//
// See also ez-rpc.h, which has simpler instructions for the common case of a two-party
// client-server RPC connection.
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult, typename RealmGatewayClient,
typename InternalRef = _::InternalRefFromRealmGatewayClient<RealmGatewayClient>,
typename ExternalRef = _::ExternalRefFromRealmGatewayClient<RealmGatewayClient>>
RpcSystem<VatId> makeRpcClient(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
RealmGatewayClient gateway);
// Make an RPC client for a VatNetwork that resides in a different realm from the application.
// The given RealmGateway is used to translate SturdyRefs between the app's ("internal") format
// and the network's ("external") format.
template <typename SturdyRefObjectId>
class SturdyRefRestorer: public _::SturdyRefRestorerBase {
// ** DEPRECATED **
//
// In Cap'n Proto 0.4.x, applications could export multiple main interfaces identified by
// object IDs. The callback used to map object IDs to objects was `SturdyRefRestorer`, as we
// imagined this would eventually be used for restoring SturdyRefs as well. In practice, it was
// never used for real SturdyRefs, only for exporting singleton objects under well-known names.
//
// The new preferred strategy is to export only a _single_ such interface, called the
// "bootstrap interface". That interface can itself have methods for obtaining other objects, of
// course, but that is up to the app. `SturdyRefRestorer` exists for backwards-compatibility.
//
// Hint: Use SturdyRefRestorer<capnp::Text> to define a server that exports services under
// string names.
public:
virtual Capability::Client restore(typename SturdyRefObjectId::Reader ref)
KJ_DEPRECATED(
"Please transition to using bootstrap interfaces instead of SturdyRefRestorer.") = 0;
// Restore the given object, returning a capability representing it.
private:
Capability::Client baseRestore(AnyPointer::Reader ref) override final;
};
// =======================================================================================
// VatNetwork
class OutgoingRpcMessage {
// A message to be sent by a `VatNetwork`.
public:
virtual AnyPointer::Builder getBody() = 0;
// Get the message body, which the caller may fill in any way it wants. (The standard RPC
// implementation initializes it as a Message as defined in rpc.capnp.)
virtual void send() = 0;
// Send the message, or at least put it in a queue to be sent later. Note that the builder
// returned by `getBody()` remains valid at least until the `OutgoingRpcMessage` is destroyed.
};
class IncomingRpcMessage {
// A message received from a `VatNetwork`.
public:
virtual AnyPointer::Reader getBody() = 0;
// Get the message body, to be interpreted by the caller. (The standard RPC implementation
// interprets it as a Message as defined in rpc.capnp.)
};
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
class VatNetwork: public _::VatNetworkBase {
// Cap'n Proto RPC operates between vats, where a "vat" is some sort of host of objects.
// Typically one Cap'n Proto process (in the Unix sense) is one vat. The RPC system is what
// allows calls between objects hosted in different vats.
//
// The RPC implementation sits on top of an implementation of `VatNetwork`. The `VatNetwork`
// determines how to form connections between vats -- specifically, two-way, private, reliable,
// sequenced datagram connections. The RPC implementation determines how to use such connections
// to manage object references and make method calls.
//
// The most common implementation of VatNetwork is TwoPartyVatNetwork (rpc-twoparty.h). Most
// simple client-server apps will want to use it. (You may even want to use the EZ RPC
// interfaces in `ez-rpc.h` and avoid all of this.)
//
// TODO(someday): Provide a standard implementation for the public internet.
public:
class Connection;
struct ConnectionAndProvisionId {
// Result of connecting to a vat introduced by another vat.
kj::Own<Connection> connection;
// Connection to the new vat.
kj::Own<OutgoingRpcMessage> firstMessage;
// An already-allocated `OutgoingRpcMessage` associated with `connection`. The RPC system will
// construct this as an `Accept` message and send it.
Orphan<ProvisionId> provisionId;
// A `ProvisionId` already allocated inside `firstMessage`, which the RPC system will use to
// build the `Accept` message.
};
class Connection: public _::VatNetworkBase::Connection {
// A two-way RPC connection.
//
// This object may represent a connection that doesn't exist yet, but is expected to exist
// in the future. In this case, sent messages will automatically be queued and sent once the
// connection is ready, so that the caller doesn't need to know the difference.
public:
// Level 0 features ----------------------------------------------
virtual typename VatId::Reader getPeerVatId() = 0;
// Returns the connected vat's authenticated VatId. It is the VatNetwork's responsibility to
// authenticate this, so that the caller can be assured that they are really talking to the
// identified vat and not an imposter.
virtual kj::Own<OutgoingRpcMessage> newOutgoingMessage(uint firstSegmentWordSize) override = 0;
// Allocate a new message to be sent on this connection.
//
// If `firstSegmentWordSize` is non-zero, it should be treated as a hint suggesting how large
// to make the first segment. This is entirely a hint and the connection may adjust it up or
// down. If it is zero, the connection should choose the size itself.
virtual kj::Promise<kj::Maybe<kj::Own<IncomingRpcMessage>>> receiveIncomingMessage() override = 0;
// Wait for a message to be received and return it. If the read stream cleanly terminates,
// return null. If any other problem occurs, throw an exception.
virtual kj::Promise<void> shutdown() override KJ_WARN_UNUSED_RESULT = 0;
// Waits until all outgoing messages have been sent, then shuts down the outgoing stream. The
// returned promise resolves after shutdown is complete.
private:
AnyStruct::Reader baseGetPeerVatId() override;
};
// Level 0 features ------------------------------------------------
virtual kj::Maybe<kj::Own<Connection>> connect(typename VatId::Reader hostId) = 0;
// Connect to a VatId. Note that this method immediately returns a `Connection`, even
// if the network connection has not yet been established. Messages can be queued to this
// connection and will be delivered once it is open. The caller must attempt to read from the
// connection to verify that it actually succeeded; the read will fail if the connection
// couldn't be opened. Some network implementations may actually start sending messages before
// hearing back from the server at all, to avoid a round trip.
//
// Returns nullptr if `hostId` refers to the local host.
virtual kj::Promise<kj::Own<Connection>> accept() = 0;
// Wait for the next incoming connection and return it.
// Level 4 features ------------------------------------------------
// TODO(someday)
private:
kj::Maybe<kj::Own<_::VatNetworkBase::Connection>>
baseConnect(AnyStruct::Reader hostId) override final;
kj::Promise<kj::Own<_::VatNetworkBase::Connection>> baseAccept() override final;
};
// =======================================================================================
// ***************************************************************************************
// Inline implementation details start here
// ***************************************************************************************
// =======================================================================================
template <typename VatId>
Capability::Client BootstrapFactory<VatId>::baseCreateFor(AnyStruct::Reader clientId) {
return createFor(clientId.as<VatId>());
}
template <typename SturdyRef, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
kj::Maybe<kj::Own<_::VatNetworkBase::Connection>>
VatNetwork<SturdyRef, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>::
baseConnect(AnyStruct::Reader ref) {
auto maybe = connect(ref.as<SturdyRef>());
return maybe.map([](kj::Own<Connection>& conn) -> kj::Own<_::VatNetworkBase::Connection> {
return kj::mv(conn);
});
}
template <typename SturdyRef, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
kj::Promise<kj::Own<_::VatNetworkBase::Connection>>
VatNetwork<SturdyRef, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>::baseAccept() {
return accept().then(
[](kj::Own<Connection>&& connection) -> kj::Own<_::VatNetworkBase::Connection> {
return kj::mv(connection);
});
}
template <typename SturdyRef, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
AnyStruct::Reader VatNetwork<
SturdyRef, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>::
Connection::baseGetPeerVatId() {
return getPeerVatId();
}
template <typename SturdyRef>
Capability::Client SturdyRefRestorer<SturdyRef>::baseRestore(AnyPointer::Reader ref) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return restore(ref.getAs<SturdyRef>());
#pragma GCC diagnostic pop
}
template <typename VatId>
template <typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId>::RpcSystem(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
kj::Maybe<Capability::Client> bootstrap,
kj::Maybe<RealmGateway<>::Client> gateway)
: _::RpcSystemBase(network, kj::mv(bootstrap), kj::mv(gateway)) {}
template <typename VatId>
template <typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId>::RpcSystem(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
BootstrapFactory<VatId>& bootstrapFactory,
kj::Maybe<RealmGateway<>::Client> gateway)
: _::RpcSystemBase(network, bootstrapFactory, kj::mv(gateway)) {}
template <typename VatId>
template <typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult,
typename LocalSturdyRefObjectId>
RpcSystem<VatId>::RpcSystem(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
SturdyRefRestorer<LocalSturdyRefObjectId>& restorer)
: _::RpcSystemBase(network, restorer) {}
template <typename VatId>
Capability::Client RpcSystem<VatId>::bootstrap(typename VatId::Reader vatId) {
return baseBootstrap(_::PointerHelpers<VatId>::getInternalReader(vatId));
}
template <typename VatId>
Capability::Client RpcSystem<VatId>::restore(
typename VatId::Reader hostId, AnyPointer::Reader objectId) {
return baseRestore(_::PointerHelpers<VatId>::getInternalReader(hostId), objectId);
}
template <typename VatId>
inline void RpcSystem<VatId>::setFlowLimit(size_t words) {
baseSetFlowLimit(words);
}
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
Capability::Client bootstrapInterface) {
return RpcSystem<VatId>(network, kj::mv(bootstrapInterface));
}
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult,
typename RealmGatewayClient, typename InternalRef, typename ExternalRef>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
Capability::Client bootstrapInterface, RealmGatewayClient gateway) {
return RpcSystem<VatId>(network, kj::mv(bootstrapInterface),
gateway.template castAs<RealmGateway<>>());
}
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
BootstrapFactory<VatId>& bootstrapFactory) {
return RpcSystem<VatId>(network, bootstrapFactory);
}
template <typename VatId, typename ProvisionId, typename RecipientId,
typename ThirdPartyCapId, typename JoinResult,
typename RealmGatewayClient, typename InternalRef, typename ExternalRef>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
BootstrapFactory<VatId>& bootstrapFactory, RealmGatewayClient gateway) {
return RpcSystem<VatId>(network, bootstrapFactory, gateway.template castAs<RealmGateway<>>());
}
template <typename VatId, typename LocalSturdyRefObjectId,
typename ProvisionId, typename RecipientId, typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcServer(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
SturdyRefRestorer<LocalSturdyRefObjectId>& restorer) {
return RpcSystem<VatId>(network, restorer);
}
template <typename VatId, typename ProvisionId,
typename RecipientId, typename ThirdPartyCapId, typename JoinResult>
RpcSystem<VatId> makeRpcClient(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network) {
return RpcSystem<VatId>(network, nullptr);
}
template <typename VatId, typename ProvisionId,
typename RecipientId, typename ThirdPartyCapId, typename JoinResult,
typename RealmGatewayClient, typename InternalRef, typename ExternalRef>
RpcSystem<VatId> makeRpcClient(
VatNetwork<VatId, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>& network,
RealmGatewayClient gateway) {
return RpcSystem<VatId>(network, nullptr, gateway.template castAs<RealmGateway<>>());
}
} // namespace capnp
#endif // CAPNP_RPC_H_
@@ -0,0 +1,48 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SCHEMA_LITE_H_
#define CAPNP_SCHEMA_LITE_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include <capnp/schema.capnp.h>
#include "message.h"
namespace capnp {
template <typename T, typename CapnpPrivate = typename T::_capnpPrivate>
inline schema::Node::Reader schemaProto() {
// Get the schema::Node for this type's schema. This function works even in lite mode.
return readMessageUnchecked<schema::Node>(CapnpPrivate::encodedSchema());
}
template <typename T, uint64_t id = schemas::EnumInfo<T>::typeId>
inline schema::Node::Reader schemaProto() {
// Get the schema::Node for this type's schema. This function works even in lite mode.
return readMessageUnchecked<schema::Node>(schemas::EnumInfo<T>::encodedSchema());
}
} // namespace capnp
#endif // CAPNP_SCHEMA_LITE_H_
@@ -0,0 +1,173 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SCHEMA_LOADER_H_
#define CAPNP_SCHEMA_LOADER_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "schema.h"
#include <kj/memory.h>
#include <kj/mutex.h>
namespace capnp {
class SchemaLoader {
// Class which can be used to construct Schema objects from schema::Nodes as defined in
// schema.capnp.
//
// It is a bad idea to use this class on untrusted input with exceptions disabled -- you may
// be exposing yourself to denial-of-service attacks, as attackers can easily construct schemas
// that are subtly inconsistent in a way that causes exceptions to be thrown either by
// SchemaLoader or by the dynamic API when the schemas are subsequently used. If you enable and
// properly catch exceptions, you should be OK -- assuming no bugs in the Cap'n Proto
// implementation, of course.
public:
class LazyLoadCallback {
public:
virtual void load(const SchemaLoader& loader, uint64_t id) const = 0;
// Request that the schema node with the given ID be loaded into the given SchemaLoader. If
// the callback is able to find a schema for this ID, it should invoke `loadOnce()` on
// `loader` to load it. If no such node exists, it should simply do nothing and return.
//
// The callback is allowed to load schema nodes other than the one requested, e.g. because it
// expects they will be needed soon.
//
// If the `SchemaLoader` is used from multiple threads, the callback must be thread-safe.
// In particular, it's possible for multiple threads to invoke `load()` with the same ID.
// If the callback performs a large amount of work to look up IDs, it should be sure to
// de-dup these requests.
};
SchemaLoader();
SchemaLoader(const LazyLoadCallback& callback);
// Construct a SchemaLoader which will invoke the given callback when a schema node is requested
// that isn't already loaded.
~SchemaLoader() noexcept(false);
KJ_DISALLOW_COPY(SchemaLoader);
Schema get(uint64_t id, schema::Brand::Reader brand = schema::Brand::Reader(),
Schema scope = Schema()) const;
// Gets the schema for the given ID, throwing an exception if it isn't present.
//
// The returned schema may be invalidated if load() is called with a new schema for the same ID.
// In general, you should not call load() while a schema from this loader is in-use.
//
// `brand` and `scope` are used to determine brand bindings where relevant. `brand` gives
// parameter bindings for the target type's brand parameters that were specified at the reference
// site. `scope` specifies the scope in which the type ID appeared -- if `brand` itself contains
// parameter references or indicates that some parameters will be inherited, these will be
// interpreted within / inherited from `scope`.
kj::Maybe<Schema> tryGet(uint64_t id, schema::Brand::Reader bindings = schema::Brand::Reader(),
Schema scope = Schema()) const;
// Like get() but doesn't throw.
Schema getUnbound(uint64_t id) const;
// Gets a special version of the schema in which all brand parameters are "unbound". This means
// that if you look up a type via the Schema API, and it resolves to a brand parameter, the
// returned Type's getBrandParameter() method will return info about that parameter. Otherwise,
// normally, all brand parameters that aren't otherwise bound are assumed to simply be
// "AnyPointer".
Type getType(schema::Type::Reader type, Schema scope = Schema()) const;
// Convenience method which interprets a schema::Type to produce a Type object. Implemented in
// terms of get().
Schema load(const schema::Node::Reader& reader);
// Loads the given schema node. Validates the node and throws an exception if invalid. This
// makes a copy of the schema, so the object passed in can be destroyed after this returns.
//
// If the node has any dependencies which are not already loaded, they will be initialized as
// stubs -- empty schemas of whichever kind is expected.
//
// If another schema for the given reader has already been seen, the loader will inspect both
// schemas to determine which one is newer, and use that that one. If the two versions are
// found to be incompatible, an exception is thrown. If the two versions differ but are
// compatible and the loader cannot determine which is newer (e.g., the only changes are renames),
// the existing schema will be preferred. Note that in any case, the loader will end up keeping
// around copies of both schemas, so you shouldn't repeatedly reload schemas into the same loader.
//
// The following properties of the schema node are validated:
// - Struct size and preferred list encoding are valid and consistent.
// - Struct members are fields or unions.
// - Union members are fields.
// - Field offsets are in-bounds.
// - Ordinals and codeOrders are sequential starting from zero.
// - Values are of the right union case to match their types.
//
// You should assume anything not listed above is NOT validated. In particular, things that are
// not validated now, but could be in the future, include but are not limited to:
// - Names.
// - Annotation values. (This is hard because the annotation declaration is not always
// available.)
// - Content of default/constant values of pointer type. (Validating these would require knowing
// their schema, but even if the schemas are available at validation time, they could be
// updated by a subsequent load(), invalidating existing values. Instead, these values are
// validated at the time they are used, as usual for Cap'n Proto objects.)
//
// Also note that unknown types are not considered invalid. Instead, the dynamic API returns
// a DynamicValue with type UNKNOWN for these.
Schema loadOnce(const schema::Node::Reader& reader) const;
// Like `load()` but does nothing if a schema with the same ID is already loaded. In contrast,
// `load()` would attempt to compare the schemas and take the newer one. `loadOnce()` is safe
// to call even while concurrently using schemas from this loader. It should be considered an
// error to call `loadOnce()` with two non-identical schemas that share the same ID, although
// this error may or may not actually be detected by the implementation.
template <typename T>
void loadCompiledTypeAndDependencies();
// Load the schema for the given compiled-in type and all of its dependencies.
//
// If you want to be able to cast a DynamicValue built from this SchemaLoader to the compiled-in
// type using as<T>(), you must call this method before constructing the DynamicValue. Otherwise,
// as<T>() will throw an exception complaining about type mismatch.
kj::Array<Schema> getAllLoaded() const;
// Get a complete list of all loaded schema nodes. It is particularly useful to call this after
// loadCompiledTypeAndDependencies<T>() in order to get a flat list of all of T's transitive
// dependencies.
private:
class Validator;
class CompatibilityChecker;
class Impl;
class InitializerImpl;
class BrandedInitializerImpl;
kj::MutexGuarded<kj::Own<Impl>> impl;
void loadNative(const _::RawSchema* nativeSchema);
};
template <typename T>
inline void SchemaLoader::loadCompiledTypeAndDependencies() {
loadNative(&_::rawSchema<T>());
}
} // namespace capnp
#endif // CAPNP_SCHEMA_LOADER_H_
@@ -0,0 +1,207 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SCHEMA_PARSER_H_
#define CAPNP_SCHEMA_PARSER_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "schema-loader.h"
#include <kj/string.h>
namespace capnp {
class ParsedSchema;
class SchemaFile;
class SchemaParser {
// Parses `.capnp` files to produce `Schema` objects.
//
// This class is thread-safe, hence all its methods are const.
public:
SchemaParser();
~SchemaParser() noexcept(false);
ParsedSchema parseDiskFile(kj::StringPtr displayName, kj::StringPtr diskPath,
kj::ArrayPtr<const kj::StringPtr> importPath) const;
// Parse a file located on disk. Throws an exception if the file dosen't exist.
//
// Parameters:
// * `displayName`: The name that will appear in the file's schema node. (If the file has
// already been parsed, this will be ignored and the display name from the first time it was
// parsed will be kept.)
// * `diskPath`: The path to the file on disk.
// * `importPath`: Directories to search when resolving absolute imports within this file
// (imports that start with a `/`). Must remain valid until the SchemaParser is destroyed.
// (If the file has already been parsed, this will be ignored and the import path from the
// first time it was parsed will be kept.)
//
// This method is a shortcut, equivalent to:
// parser.parseFile(SchemaFile::newDiskFile(displayName, diskPath, importPath))`;
//
// This method throws an exception if any errors are encountered in the file or in anything the
// file depends on. Note that merely importing another file does not count as a dependency on
// anything in the imported file -- only the imported types which are actually used are
// "dependencies".
ParsedSchema parseFile(kj::Own<SchemaFile>&& file) const;
// Advanced interface for parsing a file that may or may not be located in any global namespace.
// Most users will prefer `parseDiskFile()`.
//
// If the file has already been parsed (that is, a SchemaFile that compares equal to this one
// was parsed previously), the existing schema will be returned again.
//
// This method reports errors by calling SchemaFile::reportError() on the file where the error
// is located. If that call does not throw an exception, `parseFile()` may in fact return
// normally. In this case, the result is a best-effort attempt to compile the schema, but it
// may be invalid or corrupt, and using it for anything may cause exceptions to be thrown.
template <typename T>
inline void loadCompiledTypeAndDependencies() {
// See SchemaLoader::loadCompiledTypeAndDependencies().
getLoader().loadCompiledTypeAndDependencies<T>();
}
private:
struct Impl;
class ModuleImpl;
kj::Own<Impl> impl;
mutable bool hadErrors = false;
ModuleImpl& getModuleImpl(kj::Own<SchemaFile>&& file) const;
SchemaLoader& getLoader();
friend class ParsedSchema;
};
class ParsedSchema: public Schema {
// ParsedSchema is an extension of Schema which also has the ability to look up nested nodes
// by name. See `SchemaParser`.
public:
inline ParsedSchema(): parser(nullptr) {}
kj::Maybe<ParsedSchema> findNested(kj::StringPtr name) const;
// Gets the nested node with the given name, or returns null if there is no such nested
// declaration.
ParsedSchema getNested(kj::StringPtr name) const;
// Gets the nested node with the given name, or throws an exception if there is no such nested
// declaration.
private:
inline ParsedSchema(Schema inner, const SchemaParser& parser): Schema(inner), parser(&parser) {}
const SchemaParser* parser;
friend class SchemaParser;
};
// =======================================================================================
// Advanced API
class SchemaFile {
// Abstract interface representing a schema file. You can implement this yourself in order to
// gain more control over how the compiler resolves imports and reads files. For the
// common case of files on disk or other global filesystem-like namespaces, use
// `SchemaFile::newDiskFile()`.
public:
class FileReader {
public:
virtual bool exists(kj::StringPtr path) const = 0;
virtual kj::Array<const char> read(kj::StringPtr path) const = 0;
};
class DiskFileReader final: public FileReader {
// Implementation of FileReader that uses the local disk. Files are read using mmap() if
// possible.
public:
static const DiskFileReader instance;
bool exists(kj::StringPtr path) const override;
kj::Array<const char> read(kj::StringPtr path) const override;
};
static kj::Own<SchemaFile> newDiskFile(
kj::StringPtr displayName, kj::StringPtr diskPath,
kj::ArrayPtr<const kj::StringPtr> importPath,
const FileReader& fileReader = DiskFileReader::instance);
// Construct a SchemaFile representing a file on disk (or located in the filesystem-like
// namespace represented by `fileReader`).
//
// Parameters:
// * `displayName`: The name that will appear in the file's schema node.
// * `diskPath`: The path to the file on disk.
// * `importPath`: Directories to search when resolving absolute imports within this file
// (imports that start with a `/`). The array content must remain valid as long as the
// SchemaFile exists (which is at least as long as the SchemaParser that parses it exists).
// * `fileReader`: Allows you to use a filesystem other than the actual local disk. Although,
// if you find yourself using this, it may make more sense for you to implement SchemaFile
// yourself.
//
// The SchemaFile compares equal to any other SchemaFile that has exactly the same disk path,
// after canonicalization.
//
// The SchemaFile will throw an exception if any errors are reported.
// -----------------------------------------------------------------
// For more control, you can implement this interface.
virtual kj::StringPtr getDisplayName() const = 0;
// Get the file's name, as it should appear in the schema.
virtual kj::Array<const char> readContent() const = 0;
// Read the file's entire content and return it as a byte array.
virtual kj::Maybe<kj::Own<SchemaFile>> import(kj::StringPtr path) const = 0;
// Resolve an import, relative to this file.
//
// `path` is exactly what appears between quotes after the `import` keyword in the source code.
// It is entirely up to the `SchemaFile` to decide how to map this to another file. Typically,
// a leading '/' means that the file is an "absolute" path and is searched for in some list of
// schema file repositories. On the other hand, a path that doesn't start with '/' is relative
// to the importing file.
virtual bool operator==(const SchemaFile& other) const = 0;
virtual bool operator!=(const SchemaFile& other) const = 0;
virtual size_t hashCode() const = 0;
// Compare two SchemaFiles to see if they refer to the same underlying file. This is an
// optimization used to avoid the need to re-parse a file to check its ID.
struct SourcePos {
uint byte;
uint line;
uint column;
};
virtual void reportError(SourcePos start, SourcePos end, kj::StringPtr message) const = 0;
// Report that the file contains an error at the given interval.
private:
class DiskSchemaFile;
};
} // namespace capnp
#endif // CAPNP_SCHEMA_PARSER_H_
@@ -0,0 +1,498 @@
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
using Cxx = import "/capnp/c++.capnp";
@0xa93fc509624c72d9;
$Cxx.namespace("capnp::schema");
using Id = UInt64;
# The globally-unique ID of a file, type, or annotation.
struct Node {
id @0 :Id;
displayName @1 :Text;
# Name to present to humans to identify this Node. You should not attempt to parse this. Its
# format could change. It is not guaranteed to be unique.
#
# (On Zooko's triangle, this is the node's nickname.)
displayNamePrefixLength @2 :UInt32;
# If you want a shorter version of `displayName` (just naming this node, without its surrounding
# scope), chop off this many characters from the beginning of `displayName`.
scopeId @3 :Id;
# ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back
# at this node, but robust code should avoid relying on this (and, in fact, group nodes are not
# listed in the outer struct's nestedNodes, since they are listed in the fields). `scopeId` is
# zero if the node has no parent, which is normally only the case with files, but should be
# allowed for any kind of node (in order to make runtime type generation easier).
parameters @32 :List(Parameter);
# If this node is parameterized (generic), the list of parameters. Empty for non-generic types.
isGeneric @33 :Bool;
# True if this node is generic, meaning that it or one of its parent scopes has a non-empty
# `parameters`.
struct Parameter {
# Information about one of the node's parameters.
name @0 :Text;
}
nestedNodes @4 :List(NestedNode);
# List of nodes nested within this node, along with the names under which they were declared.
struct NestedNode {
name @0 :Text;
# Unqualified symbol name. Unlike Node.displayName, this *can* be used programmatically.
#
# (On Zooko's triangle, this is the node's petname according to its parent scope.)
id @1 :Id;
# ID of the nested node. Typically, the target node's scopeId points back to this node, but
# robust code should avoid relying on this.
}
annotations @5 :List(Annotation);
# Annotations applied to this node.
union {
# Info specific to each kind of node.
file @6 :Void;
struct :group {
dataWordCount @7 :UInt16;
# Size of the data section, in words.
pointerCount @8 :UInt16;
# Size of the pointer section, in pointers (which are one word each).
preferredListEncoding @9 :ElementSize;
# The preferred element size to use when encoding a list of this struct. If this is anything
# other than `inlineComposite` then the struct is one word or less in size and is a candidate
# for list packing optimization.
isGroup @10 :Bool;
# If true, then this "struct" node is actually not an independent node, but merely represents
# some named union or group within a particular parent struct. This node's scopeId refers
# to the parent struct, which may itself be a union/group in yet another struct.
#
# All group nodes share the same dataWordCount and pointerCount as the top-level
# struct, and their fields live in the same ordinal and offset spaces as all other fields in
# the struct.
#
# Note that a named union is considered a special kind of group -- in fact, a named union
# is exactly equivalent to a group that contains nothing but an unnamed union.
discriminantCount @11 :UInt16;
# Number of fields in this struct which are members of an anonymous union, and thus may
# overlap. If this is non-zero, then a 16-bit discriminant is present indicating which
# of the overlapping fields is active. This can never be 1 -- if it is non-zero, it must be
# two or more.
#
# Note that the fields of an unnamed union are considered fields of the scope containing the
# union -- an unnamed union is not its own group. So, a top-level struct may contain a
# non-zero discriminant count. Named unions, on the other hand, are equivalent to groups
# containing unnamed unions. So, a named union has its own independent schema node, with
# `isGroup` = true.
discriminantOffset @12 :UInt32;
# If `discriminantCount` is non-zero, this is the offset of the union discriminant, in
# multiples of 16 bits.
fields @13 :List(Field);
# Fields defined within this scope (either the struct's top-level fields, or the fields of
# a particular group; see `isGroup`).
#
# The fields are sorted by ordinal number, but note that because groups share the same
# ordinal space, the field's index in this list is not necessarily exactly its ordinal.
# On the other hand, the field's position in this list does remain the same even as the
# protocol evolves, since it is not possible to insert or remove an earlier ordinal.
# Therefore, for most use cases, if you want to identify a field by number, it may make the
# most sense to use the field's index in this list rather than its ordinal.
}
enum :group {
enumerants@14 :List(Enumerant);
# Enumerants ordered by numeric value (ordinal).
}
interface :group {
methods @15 :List(Method);
# Methods ordered by ordinal.
superclasses @31 :List(Superclass);
# Superclasses of this interface.
}
const :group {
type @16 :Type;
value @17 :Value;
}
annotation :group {
type @18 :Type;
targetsFile @19 :Bool;
targetsConst @20 :Bool;
targetsEnum @21 :Bool;
targetsEnumerant @22 :Bool;
targetsStruct @23 :Bool;
targetsField @24 :Bool;
targetsUnion @25 :Bool;
targetsGroup @26 :Bool;
targetsInterface @27 :Bool;
targetsMethod @28 :Bool;
targetsParam @29 :Bool;
targetsAnnotation @30 :Bool;
}
}
}
struct Field {
# Schema for a field of a struct.
name @0 :Text;
codeOrder @1 :UInt16;
# Indicates where this member appeared in the code, relative to other members.
# Code ordering may have semantic relevance -- programmers tend to place related fields
# together. So, using code ordering makes sense in human-readable formats where ordering is
# otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so the maximum
# value is count(members) - 1. Fields that are members of a union are only ordered relative to
# the other members of that union, so the maximum value there is count(union.members).
annotations @2 :List(Annotation);
const noDiscriminant :UInt16 = 0xffff;
discriminantValue @3 :UInt16 = Field.noDiscriminant;
# If the field is in a union, this is the value which the union's discriminant should take when
# the field is active. If the field is not in a union, this is 0xffff.
union {
slot :group {
# A regular, non-group, non-fixed-list field.
offset @4 :UInt32;
# Offset, in units of the field's size, from the beginning of the section in which the field
# resides. E.g. for a UInt32 field, multiply this by 4 to get the byte offset from the
# beginning of the data section.
type @5 :Type;
defaultValue @6 :Value;
hadExplicitDefault @10 :Bool;
# Whether the default value was specified explicitly. Non-explicit default values are always
# zero or empty values. Usually, whether the default value was explicit shouldn't matter.
# The main use case for this flag is for structs representing method parameters:
# explicitly-defaulted parameters may be allowed to be omitted when calling the method.
}
group :group {
# A group.
typeId @7 :Id;
# The ID of the group's node.
}
}
ordinal :union {
implicit @8 :Void;
explicit @9 :UInt16;
# The original ordinal number given to the field. You probably should NOT use this; if you need
# a numeric identifier for a field, use its position within the field array for its scope.
# The ordinal is given here mainly just so that the original schema text can be reproduced given
# the compiled version -- i.e. so that `capnp compile -ocapnp` can do its job.
}
}
struct Enumerant {
# Schema for member of an enum.
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the enumerants were declared in the code.
# Like Struct.Field.codeOrder.
annotations @2 :List(Annotation);
}
struct Superclass {
id @0 :Id;
brand @1 :Brand;
}
struct Method {
# Schema for method of an interface.
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the methods were declared in the code.
# Like Struct.Field.codeOrder.
implicitParameters @7 :List(Node.Parameter);
# The parameters listed in [] (typically, type / generic parameters), whose bindings are intended
# to be inferred rather than specified explicitly, although not all languages support this.
paramStructType @2 :Id;
# ID of the parameter struct type. If a named parameter list was specified in the method
# declaration (rather than a single struct parameter type) then a corresponding struct type is
# auto-generated. Such an auto-generated type will not be listed in the interface's
# `nestedNodes` and its `scopeId` will be zero -- it is completely detached from the namespace.
# (Awkwardly, it does of course inherit generic parameters from the method's scope, which makes
# this a situation where you can't just climb the scope chain to find where a particular
# generic parameter was introduced. Making the `scopeId` zero was a mistake.)
paramBrand @5 :Brand;
# Brand of param struct type.
resultStructType @3 :Id;
# ID of the return struct type; similar to `paramStructType`.
resultBrand @6 :Brand;
# Brand of result struct type.
annotations @4 :List(Annotation);
}
struct Type {
# Represents a type expression.
union {
# The ordinals intentionally match those of Value.
void @0 :Void;
bool @1 :Void;
int8 @2 :Void;
int16 @3 :Void;
int32 @4 :Void;
int64 @5 :Void;
uint8 @6 :Void;
uint16 @7 :Void;
uint32 @8 :Void;
uint64 @9 :Void;
float32 @10 :Void;
float64 @11 :Void;
text @12 :Void;
data @13 :Void;
list :group {
elementType @14 :Type;
}
enum :group {
typeId @15 :Id;
brand @21 :Brand;
}
struct :group {
typeId @16 :Id;
brand @22 :Brand;
}
interface :group {
typeId @17 :Id;
brand @23 :Brand;
}
anyPointer :union {
unconstrained :union {
# A regular AnyPointer.
#
# The name "unconstrained" means as opposed to constraining it to match a type parameter.
# In retrospect this name is probably a poor choice given that it may still be constrained
# to be a struct, list, or capability.
anyKind @18 :Void; # truly AnyPointer
struct @25 :Void; # AnyStruct
list @26 :Void; # AnyList
capability @27 :Void; # Capability
}
parameter :group {
# This is actually a reference to a type parameter defined within this scope.
scopeId @19 :Id;
# ID of the generic type whose parameter we're referencing. This should be a parent of the
# current scope.
parameterIndex @20 :UInt16;
# Index of the parameter within the generic type's parameter list.
}
implicitMethodParameter :group {
# This is actually a reference to an implicit (generic) parameter of a method. The only
# legal context for this type to appear is inside Method.paramBrand or Method.resultBrand.
parameterIndex @24 :UInt16;
}
}
}
}
struct Brand {
# Specifies bindings for parameters of generics. Since these bindings turn a generic into a
# non-generic, we call it the "brand".
scopes @0 :List(Scope);
# For each of the target type and each of its parent scopes, a parameterization may be included
# in this list. If no parameterization is included for a particular relevant scope, then either
# that scope has no parameters or all parameters should be considered to be `AnyPointer`.
struct Scope {
scopeId @0 :Id;
# ID of the scope to which these params apply.
union {
bind @1 :List(Binding);
# List of parameter bindings.
inherit @2 :Void;
# The place where this Brand appears is actually within this scope or a sub-scope,
# and the bindings for this scope should be inherited from the reference point.
}
}
struct Binding {
union {
unbound @0 :Void;
type @1 :Type;
# TODO(someday): Allow non-type parameters? Unsure if useful.
}
}
}
struct Value {
# Represents a value, e.g. a field default value, constant value, or annotation value.
union {
# The ordinals intentionally match those of Type.
void @0 :Void;
bool @1 :Bool;
int8 @2 :Int8;
int16 @3 :Int16;
int32 @4 :Int32;
int64 @5 :Int64;
uint8 @6 :UInt8;
uint16 @7 :UInt16;
uint32 @8 :UInt32;
uint64 @9 :UInt64;
float32 @10 :Float32;
float64 @11 :Float64;
text @12 :Text;
data @13 :Data;
list @14 :AnyPointer;
enum @15 :UInt16;
struct @16 :AnyPointer;
interface @17 :Void;
# The only interface value that can be represented statically is "null", whose methods always
# throw exceptions.
anyPointer @18 :AnyPointer;
}
}
struct Annotation {
# Describes an annotation applied to a declaration. Note AnnotationNode describes the
# annotation's declaration, while this describes a use of the annotation.
id @0 :Id;
# ID of the annotation node.
brand @2 :Brand;
# Brand of the annotation.
#
# Note that the annotation itself is not allowed to be parameterized, but its scope might be.
value @1 :Value;
}
enum ElementSize {
# Possible element sizes for encoded lists. These correspond exactly to the possible values of
# the 3-bit element size component of a list pointer.
empty @0; # aka "void", but that's a keyword.
bit @1;
byte @2;
twoBytes @3;
fourBytes @4;
eightBytes @5;
pointer @6;
inlineComposite @7;
}
struct CapnpVersion {
major @0 :UInt16;
minor @1 :UInt8;
micro @2 :UInt8;
}
struct CodeGeneratorRequest {
capnpVersion @2 :CapnpVersion;
# Version of the `capnp` executable. Generally, code generators should ignore this, but the code
# generators that ship with `capnp` itself will print a warning if this mismatches since that
# probably indicates something is misconfigured.
#
# The first version of 'capnp' to set this was 0.6.0. So, if it's missing, the compiler version
# is older than that.
nodes @0 :List(Node);
# All nodes parsed by the compiler, including for the files on the command line and their
# imports.
requestedFiles @1 :List(RequestedFile);
# Files which were listed on the command line.
struct RequestedFile {
id @0 :Id;
# ID of the file.
filename @1 :Text;
# Name of the file as it appeared on the command-line (minus the src-prefix). You may use
# this to decide where to write the output.
imports @2 :List(Import);
# List of all imported paths seen in this file.
struct Import {
id @0 :Id;
# ID of the imported file.
name @1 :Text;
# Name which *this* file used to refer to the foreign file. This may be a relative name.
# This information is provided because it might be useful for code generation, e.g. to
# generate #include directives in C++. We don't put this in Node.file because this
# information is only meaningful at compile time anyway.
#
# (On Zooko's triangle, this is the import's petname according to the importing file.)
}
}
}
File diff suppressed because it is too large Load Diff
+934
View File
@@ -0,0 +1,934 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SCHEMA_H_
#define CAPNP_SCHEMA_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#if CAPNP_LITE
#error "Reflection APIs, including this header, are not available in lite mode."
#endif
#include <capnp/schema.capnp.h>
namespace capnp {
class Schema;
class StructSchema;
class EnumSchema;
class InterfaceSchema;
class ConstSchema;
class ListSchema;
class Type;
template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; };
template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; };
template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; };
template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; };
template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; };
template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; };
template <typename T>
using SchemaType = typename SchemaType_<T>::Type;
// SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct.
namespace _ { // private
extern const RawSchema NULL_SCHEMA;
extern const RawSchema NULL_STRUCT_SCHEMA;
extern const RawSchema NULL_ENUM_SCHEMA;
extern const RawSchema NULL_INTERFACE_SCHEMA;
extern const RawSchema NULL_CONST_SCHEMA;
// The schema types default to these null (empty) schemas in case of error, especially when
// exceptions are disabled.
} // namespace _ (private)
class Schema {
// Convenience wrapper around capnp::schema::Node.
public:
inline Schema(): raw(&_::NULL_SCHEMA.defaultBrand) {}
template <typename T>
static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); }
// Get the Schema for a particular compiled-in type.
schema::Node::Reader getProto() const;
// Get the underlying Cap'n Proto representation of the schema node. (Note that this accessor
// has performance comparable to accessors of struct-typed fields on Reader classes.)
kj::ArrayPtr<const word> asUncheckedMessage() const;
// Get the encoded schema node content as a single message segment. It is safe to read as an
// unchecked message.
Schema getDependency(uint64_t id) const KJ_DEPRECATED("Does not handle generics correctly.");
// DEPRECATED: This method cannot correctly account for generic type parameter bindings that
// may apply to the dependency. Instead of using this method, use a method of the Schema API
// that corresponds to the exact kind of dependency. For example, to get a field type, use
// StructSchema::Field::getType().
//
// Gets the Schema for one of this Schema's dependencies. For example, if this Schema is for a
// struct, you could look up the schema for one of its fields' types. Throws an exception if this
// schema doesn't actually depend on the given id.
//
// Note that not all type IDs found in the schema node are considered "dependencies" -- only the
// ones that are needed to implement the dynamic API are. That includes:
// - Field types.
// - Group types.
// - scopeId for group nodes, but NOT otherwise.
// - Method parameter and return types.
//
// The following are NOT considered dependencies:
// - Nested nodes.
// - scopeId for a non-group node.
// - Annotations.
//
// To obtain schemas for those, you would need a SchemaLoader.
bool isBranded() const;
// Returns true if this schema represents a non-default parameterization of this type.
Schema getGeneric() const;
// Get the version of this schema with any brands removed.
class BrandArgumentList;
BrandArgumentList getBrandArgumentsAtScope(uint64_t scopeId) const;
// Gets the values bound to the brand parameters at the given scope.
StructSchema asStruct() const;
EnumSchema asEnum() const;
InterfaceSchema asInterface() const;
ConstSchema asConst() const;
// Cast the Schema to a specific type. Throws an exception if the type doesn't match. Use
// getProto() to determine type, e.g. getProto().isStruct().
inline bool operator==(const Schema& other) const { return raw == other.raw; }
inline bool operator!=(const Schema& other) const { return raw != other.raw; }
// Determine whether two Schemas are wrapping the exact same underlying data, by identity. If
// you want to check if two Schemas represent the same type (but possibly different versions of
// it), compare their IDs instead.
template <typename T>
void requireUsableAs() const;
// Throws an exception if a value with this Schema cannot safely be cast to a native value of
// the given type. This passes if either:
// - *this == from<T>()
// - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and
// loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader.
kj::StringPtr getShortDisplayName() const;
// Get the short version of the node's display name.
private:
const _::RawBrandedSchema* raw;
inline explicit Schema(const _::RawBrandedSchema* raw): raw(raw) {
KJ_IREQUIRE(raw->lazyInitializer == nullptr,
"Must call ensureInitialized() on RawSchema before constructing Schema.");
}
template <typename T> static inline Schema fromImpl() {
return Schema(&_::rawSchema<T>());
}
void requireUsableAs(const _::RawSchema* expected) const;
uint32_t getSchemaOffset(const schema::Value::Reader& value) const;
Type getBrandBinding(uint64_t scopeId, uint index) const;
// Look up the binding for a brand parameter used by this Schema. Returns `AnyPointer` if the
// parameter is not bound.
//
// TODO(someday): Public interface for iterating over all bindings?
Schema getDependency(uint64_t id, uint location) const;
// Look up schema for a particular dependency of this schema. `location` is the dependency
// location number as defined in _::RawBrandedSchema.
Type interpretType(schema::Type::Reader proto, uint location) const;
// Interpret a schema::Type in the given location within the schema, compiling it into a
// Type object.
friend class StructSchema;
friend class EnumSchema;
friend class InterfaceSchema;
friend class ConstSchema;
friend class ListSchema;
friend class SchemaLoader;
friend class Type;
friend kj::StringTree _::structString(
_::StructReader reader, const _::RawBrandedSchema& schema);
friend kj::String _::enumString(uint16_t value, const _::RawBrandedSchema& schema);
};
kj::StringPtr KJ_STRINGIFY(const Schema& schema);
class Schema::BrandArgumentList {
// A list of generic parameter bindings for parameters of some particular type. Note that since
// parameters on an outer type apply to all inner types as well, a deeply-nested type can have
// multiple BrandArgumentLists that apply to it.
//
// A BrandArgumentList only represents the arguments that the client of the type specified. Since
// new parameters can be added over time, this list may not cover all defined parameters for the
// type. Missing parameters should be treated as AnyPointer. This class's implementation of
// operator[] already does this for you; out-of-bounds access will safely return AnyPointer.
public:
inline BrandArgumentList(): scopeId(0), size_(0), bindings(nullptr) {}
inline uint size() const { return size_; }
Type operator[](uint index) const;
typedef _::IndexingIterator<const BrandArgumentList, Type> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
uint64_t scopeId;
uint size_;
bool isUnbound;
const _::RawBrandedSchema::Binding* bindings;
inline BrandArgumentList(uint64_t scopeId, bool isUnbound)
: scopeId(scopeId), size_(0), isUnbound(isUnbound), bindings(nullptr) {}
inline BrandArgumentList(uint64_t scopeId, uint size,
const _::RawBrandedSchema::Binding* bindings)
: scopeId(scopeId), size_(size), isUnbound(false), bindings(bindings) {}
friend class Schema;
};
// -------------------------------------------------------------------
class StructSchema: public Schema {
public:
inline StructSchema(): Schema(&_::NULL_STRUCT_SCHEMA.defaultBrand) {}
class Field;
class FieldList;
class FieldSubset;
FieldList getFields() const;
// List top-level fields of this struct. This list will contain top-level groups (including
// named unions) but not the members of those groups. The list does, however, contain the
// members of the unnamed union, if there is one.
FieldSubset getUnionFields() const;
// If the field contains an unnamed union, get a list of fields in the union, ordered by
// ordinal. Since discriminant values are assigned sequentially by ordinal, you may index this
// list by discriminant value.
FieldSubset getNonUnionFields() const;
// Get the fields of this struct which are not in an unnamed union, ordered by ordinal.
kj::Maybe<Field> findFieldByName(kj::StringPtr name) const;
// Find the field with the given name, or return null if there is no such field. If the struct
// contains an unnamed union, then this will find fields of that union in addition to fields
// of the outer struct, since they exist in the same namespace. It will not, however, find
// members of groups (including named unions) -- you must first look up the group itself,
// then dig into its type.
Field getFieldByName(kj::StringPtr name) const;
// Like findFieldByName() but throws an exception on failure.
kj::Maybe<Field> getFieldByDiscriminant(uint16_t discriminant) const;
// Finds the field whose `discriminantValue` is equal to the given value, or returns null if
// there is no such field. (If the schema does not represent a union or a struct containing
// an unnamed union, then this always returns null.)
private:
StructSchema(Schema base): Schema(base) {}
template <typename T> static inline StructSchema fromImpl() {
return StructSchema(Schema(&_::rawBrandedSchema<T>()));
}
friend class Schema;
friend class Type;
};
class StructSchema::Field {
public:
Field() = default;
inline schema::Field::Reader getProto() const { return proto; }
inline StructSchema getContainingStruct() const { return parent; }
inline uint getIndex() const { return index; }
// Get the index of this field within the containing struct or union.
Type getType() const;
// Get the type of this field. Note that this is preferred over getProto().getType() as this
// method will apply generics.
uint32_t getDefaultValueSchemaOffset() const;
// For struct, list, and object fields, returns the offset, in words, within the first segment of
// the struct's schema, where this field's default value pointer is located. The schema is
// always stored as a single-segment unchecked message, which in turn means that the default
// value pointer itself can be treated as the root of an unchecked message -- if you know where
// to find it, which is what this method helps you with.
//
// For blobs, returns the offset of the beginning of the blob's content within the first segment
// of the struct's schema.
//
// This is primarily useful for code generators. The C++ code generator, for example, embeds
// the entire schema as a raw word array within the generated code. Of course, to implement
// field accessors, it needs access to those fields' default values. Embedding separate copies
// of those default values would be redundant since they are already included in the schema, but
// seeking through the schema at runtime to find the default values would be ugly. Instead,
// the code generator can use getDefaultValueSchemaOffset() to find the offset of the default
// value within the schema, and can simply apply that offset at runtime.
//
// If the above does not make sense, you probably don't need this method.
inline bool operator==(const Field& other) const;
inline bool operator!=(const Field& other) const { return !(*this == other); }
private:
StructSchema parent;
uint index;
schema::Field::Reader proto;
inline Field(StructSchema parent, uint index, schema::Field::Reader proto)
: parent(parent), index(index), proto(proto) {}
friend class StructSchema;
};
kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field);
class StructSchema::FieldList {
public:
FieldList() = default; // empty list
inline uint size() const { return list.size(); }
inline Field operator[](uint index) const { return Field(parent, index, list[index]); }
typedef _::IndexingIterator<const FieldList, Field> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
StructSchema parent;
List<schema::Field>::Reader list;
inline FieldList(StructSchema parent, List<schema::Field>::Reader list)
: parent(parent), list(list) {}
friend class StructSchema;
};
class StructSchema::FieldSubset {
public:
FieldSubset() = default; // empty list
inline uint size() const { return size_; }
inline Field operator[](uint index) const {
return Field(parent, indices[index], list[indices[index]]);
}
typedef _::IndexingIterator<const FieldSubset, Field> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
StructSchema parent;
List<schema::Field>::Reader list;
const uint16_t* indices;
uint size_;
inline FieldSubset(StructSchema parent, List<schema::Field>::Reader list,
const uint16_t* indices, uint size)
: parent(parent), list(list), indices(indices), size_(size) {}
friend class StructSchema;
};
// -------------------------------------------------------------------
class EnumSchema: public Schema {
public:
inline EnumSchema(): Schema(&_::NULL_ENUM_SCHEMA.defaultBrand) {}
class Enumerant;
class EnumerantList;
EnumerantList getEnumerants() const;
kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const;
Enumerant getEnumerantByName(kj::StringPtr name) const;
// Like findEnumerantByName() but throws an exception on failure.
private:
EnumSchema(Schema base): Schema(base) {}
template <typename T> static inline EnumSchema fromImpl() {
return EnumSchema(Schema(&_::rawBrandedSchema<T>()));
}
friend class Schema;
friend class Type;
};
class EnumSchema::Enumerant {
public:
Enumerant() = default;
inline schema::Enumerant::Reader getProto() const { return proto; }
inline EnumSchema getContainingEnum() const { return parent; }
inline uint16_t getOrdinal() const { return ordinal; }
inline uint getIndex() const { return ordinal; }
inline bool operator==(const Enumerant& other) const;
inline bool operator!=(const Enumerant& other) const { return !(*this == other); }
private:
EnumSchema parent;
uint16_t ordinal;
schema::Enumerant::Reader proto;
inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::Enumerant::Reader proto)
: parent(parent), ordinal(ordinal), proto(proto) {}
friend class EnumSchema;
};
class EnumSchema::EnumerantList {
public:
EnumerantList() = default; // empty list
inline uint size() const { return list.size(); }
inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); }
typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
EnumSchema parent;
List<schema::Enumerant>::Reader list;
inline EnumerantList(EnumSchema parent, List<schema::Enumerant>::Reader list)
: parent(parent), list(list) {}
friend class EnumSchema;
};
// -------------------------------------------------------------------
class InterfaceSchema: public Schema {
public:
inline InterfaceSchema(): Schema(&_::NULL_INTERFACE_SCHEMA.defaultBrand) {}
class Method;
class MethodList;
MethodList getMethods() const;
kj::Maybe<Method> findMethodByName(kj::StringPtr name) const;
Method getMethodByName(kj::StringPtr name) const;
// Like findMethodByName() but throws an exception on failure.
class SuperclassList;
SuperclassList getSuperclasses() const;
// Get the immediate superclasses of this type, after applying generics.
bool extends(InterfaceSchema other) const;
// Returns true if `other` is a superclass of this interface (including if `other == *this`).
kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const;
// Find the superclass of this interface with the given type ID. Returns null if the interface
// extends no such type.
private:
InterfaceSchema(Schema base): Schema(base) {}
template <typename T> static inline InterfaceSchema fromImpl() {
return InterfaceSchema(Schema(&_::rawBrandedSchema<T>()));
}
friend class Schema;
friend class Type;
kj::Maybe<Method> findMethodByName(kj::StringPtr name, uint& counter) const;
bool extends(InterfaceSchema other, uint& counter) const;
kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId, uint& counter) const;
// We protect against malicious schemas with large or cyclic hierarchies by cutting off the
// search when the counter reaches a threshold.
};
class InterfaceSchema::Method {
public:
Method() = default;
inline schema::Method::Reader getProto() const { return proto; }
inline InterfaceSchema getContainingInterface() const { return parent; }
inline uint16_t getOrdinal() const { return ordinal; }
inline uint getIndex() const { return ordinal; }
StructSchema getParamType() const;
StructSchema getResultType() const;
// Get the parameter and result types, including substituting generic parameters.
inline bool operator==(const Method& other) const;
inline bool operator!=(const Method& other) const { return !(*this == other); }
private:
InterfaceSchema parent;
uint16_t ordinal;
schema::Method::Reader proto;
inline Method(InterfaceSchema parent, uint16_t ordinal,
schema::Method::Reader proto)
: parent(parent), ordinal(ordinal), proto(proto) {}
friend class InterfaceSchema;
};
class InterfaceSchema::MethodList {
public:
MethodList() = default; // empty list
inline uint size() const { return list.size(); }
inline Method operator[](uint index) const { return Method(parent, index, list[index]); }
typedef _::IndexingIterator<const MethodList, Method> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
InterfaceSchema parent;
List<schema::Method>::Reader list;
inline MethodList(InterfaceSchema parent, List<schema::Method>::Reader list)
: parent(parent), list(list) {}
friend class InterfaceSchema;
};
class InterfaceSchema::SuperclassList {
public:
SuperclassList() = default; // empty list
inline uint size() const { return list.size(); }
InterfaceSchema operator[](uint index) const;
typedef _::IndexingIterator<const SuperclassList, InterfaceSchema> Iterator;
inline Iterator begin() const { return Iterator(this, 0); }
inline Iterator end() const { return Iterator(this, size()); }
private:
InterfaceSchema parent;
List<schema::Superclass>::Reader list;
inline SuperclassList(InterfaceSchema parent, List<schema::Superclass>::Reader list)
: parent(parent), list(list) {}
friend class InterfaceSchema;
};
// -------------------------------------------------------------------
class ConstSchema: public Schema {
// Represents a constant declaration.
//
// `ConstSchema` can be implicitly cast to DynamicValue to read its value.
public:
inline ConstSchema(): Schema(&_::NULL_CONST_SCHEMA.defaultBrand) {}
template <typename T>
ReaderFor<T> as() const;
// Read the constant's value. This is a convenience method equivalent to casting the ConstSchema
// to a DynamicValue and then calling its `as<T>()` method. For dependency reasons, this method
// is defined in <capnp/dynamic.h>, which you must #include explicitly.
uint32_t getValueSchemaOffset() const;
// Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer
// type, this gets the offset from the beginning of the constant's schema node to a pointer
// representing the constant value.
Type getType() const;
private:
ConstSchema(Schema base): Schema(base) {}
friend class Schema;
};
// -------------------------------------------------------------------
class Type {
public:
struct BrandParameter {
uint64_t scopeId;
uint index;
};
struct ImplicitParameter {
uint index;
};
inline Type();
inline Type(schema::Type::Which primitive);
inline Type(StructSchema schema);
inline Type(EnumSchema schema);
inline Type(InterfaceSchema schema);
inline Type(ListSchema schema);
inline Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind);
inline Type(BrandParameter param);
inline Type(ImplicitParameter param);
template <typename T>
inline static Type from();
inline schema::Type::Which which() const;
StructSchema asStruct() const;
EnumSchema asEnum() const;
InterfaceSchema asInterface() const;
ListSchema asList() const;
// Each of these methods may only be called if which() returns the corresponding type.
kj::Maybe<BrandParameter> getBrandParameter() const;
// Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular
// AnyPointer and not a parameter.
kj::Maybe<ImplicitParameter> getImplicitParameter() const;
// Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular
// AnyPointer and not a parameter. "Implicit parameters" refer to type parameters on methods.
inline schema::Type::AnyPointer::Unconstrained::Which whichAnyPointerKind() const;
// Only callable if which() returns ANY_POINTER.
inline bool isVoid() const;
inline bool isBool() const;
inline bool isInt8() const;
inline bool isInt16() const;
inline bool isInt32() const;
inline bool isInt64() const;
inline bool isUInt8() const;
inline bool isUInt16() const;
inline bool isUInt32() const;
inline bool isUInt64() const;
inline bool isFloat32() const;
inline bool isFloat64() const;
inline bool isText() const;
inline bool isData() const;
inline bool isList() const;
inline bool isEnum() const;
inline bool isStruct() const;
inline bool isInterface() const;
inline bool isAnyPointer() const;
bool operator==(const Type& other) const;
inline bool operator!=(const Type& other) const { return !(*this == other); }
size_t hashCode() const;
inline Type wrapInList(uint depth = 1) const;
// Return the Type formed by wrapping this type in List() `depth` times.
inline Type(schema::Type::Which derived, const _::RawBrandedSchema* schema);
// For internal use.
private:
schema::Type::Which baseType; // type not including applications of List()
uint8_t listDepth; // 0 for T, 1 for List(T), 2 for List(List(T)), ...
bool isImplicitParam;
// If true, this refers to an implicit method parameter. baseType must be ANY_POINTER, scopeId
// must be zero, and paramIndex indicates the parameter index.
union {
uint16_t paramIndex;
// If baseType is ANY_POINTER but this Type actually refers to a type parameter, this is the
// index of the parameter among the parameters at its scope, and `scopeId` below is the type ID
// of the scope where the parameter was defined.
schema::Type::AnyPointer::Unconstrained::Which anyPointerKind;
// If scopeId is zero and isImplicitParam is false.
};
union {
const _::RawBrandedSchema* schema; // if type is struct, enum, interface...
uint64_t scopeId; // if type is AnyPointer but it's actually a type parameter...
};
Type(schema::Type::Which baseType, uint8_t listDepth, const _::RawBrandedSchema* schema)
: baseType(baseType), listDepth(listDepth), schema(schema) {
KJ_IREQUIRE(baseType != schema::Type::ANY_POINTER);
}
void requireUsableAs(Type expected) const;
friend class ListSchema; // only for requireUsableAs()
};
// -------------------------------------------------------------------
class ListSchema {
// ListSchema is a little different because list types are not described by schema nodes. So,
// ListSchema doesn't subclass Schema.
public:
ListSchema() = default;
static ListSchema of(schema::Type::Which primitiveType);
static ListSchema of(StructSchema elementType);
static ListSchema of(EnumSchema elementType);
static ListSchema of(InterfaceSchema elementType);
static ListSchema of(ListSchema elementType);
static ListSchema of(Type elementType);
// Construct the schema for a list of the given type.
static ListSchema of(schema::Type::Reader elementType, Schema context)
KJ_DEPRECATED("Does not handle generics correctly.");
// DEPRECATED: This method cannot correctly account for generic type parameter bindings that
// may apply to the input type. Instead of using this method, use a method of the Schema API
// that corresponds to the exact kind of dependency. For example, to get a field type, use
// StructSchema::Field::getType().
//
// Construct from an element type schema. Requires a context which can handle getDependency()
// requests for any type ID found in the schema.
Type getElementType() const;
inline schema::Type::Which whichElementType() const;
// Get the element type's "which()". ListSchema does not actually store a schema::Type::Reader
// describing the element type, but if it did, this would be equivalent to calling
// .getBody().which() on that type.
StructSchema getStructElementType() const;
EnumSchema getEnumElementType() const;
InterfaceSchema getInterfaceElementType() const;
ListSchema getListElementType() const;
// Get the schema for complex element types. Each of these throws an exception if the element
// type is not of the requested kind.
inline bool operator==(const ListSchema& other) const { return elementType == other.elementType; }
inline bool operator!=(const ListSchema& other) const { return elementType != other.elementType; }
template <typename T>
void requireUsableAs() const;
private:
Type elementType;
inline explicit ListSchema(Type elementType): elementType(elementType) {}
template <typename T>
struct FromImpl;
template <typename T> static inline ListSchema fromImpl() {
return FromImpl<T>::get();
}
void requireUsableAs(ListSchema expected) const;
friend class Schema;
};
// =======================================================================================
// inline implementation
template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; }
template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; }
template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; }
template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; }
template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; }
template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; }
template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; }
template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; }
template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; }
template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; }
template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; }
template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; }
template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; }
template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; }
inline Schema Schema::getDependency(uint64_t id) const {
return getDependency(id, 0);
}
inline bool Schema::isBranded() const {
return raw != &raw->generic->defaultBrand;
}
inline Schema Schema::getGeneric() const {
return Schema(&raw->generic->defaultBrand);
}
template <typename T>
inline void Schema::requireUsableAs() const {
requireUsableAs(&_::rawSchema<T>());
}
inline bool StructSchema::Field::operator==(const Field& other) const {
return parent == other.parent && index == other.index;
}
inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const {
return parent == other.parent && ordinal == other.ordinal;
}
inline bool InterfaceSchema::Method::operator==(const Method& other) const {
return parent == other.parent && ordinal == other.ordinal;
}
inline ListSchema ListSchema::of(StructSchema elementType) {
return ListSchema(Type(elementType));
}
inline ListSchema ListSchema::of(EnumSchema elementType) {
return ListSchema(Type(elementType));
}
inline ListSchema ListSchema::of(InterfaceSchema elementType) {
return ListSchema(Type(elementType));
}
inline ListSchema ListSchema::of(ListSchema elementType) {
return ListSchema(Type(elementType));
}
inline ListSchema ListSchema::of(Type elementType) {
return ListSchema(elementType);
}
inline Type ListSchema::getElementType() const {
return elementType;
}
inline schema::Type::Which ListSchema::whichElementType() const {
return elementType.which();
}
inline StructSchema ListSchema::getStructElementType() const {
return elementType.asStruct();
}
inline EnumSchema ListSchema::getEnumElementType() const {
return elementType.asEnum();
}
inline InterfaceSchema ListSchema::getInterfaceElementType() const {
return elementType.asInterface();
}
inline ListSchema ListSchema::getListElementType() const {
return elementType.asList();
}
template <typename T>
inline void ListSchema::requireUsableAs() const {
static_assert(kind<T>() == Kind::LIST,
"ListSchema::requireUsableAs<T>() requires T is a list type.");
requireUsableAs(Schema::from<T>());
}
inline void ListSchema::requireUsableAs(ListSchema expected) const {
elementType.requireUsableAs(expected.elementType);
}
template <typename T>
struct ListSchema::FromImpl<List<T>> {
static inline ListSchema get() { return of(Schema::from<T>()); }
};
inline Type::Type(): baseType(schema::Type::VOID), listDepth(0), schema(nullptr) {}
inline Type::Type(schema::Type::Which primitive)
: baseType(primitive), listDepth(0), isImplicitParam(false) {
KJ_IREQUIRE(primitive != schema::Type::STRUCT &&
primitive != schema::Type::ENUM &&
primitive != schema::Type::INTERFACE &&
primitive != schema::Type::LIST);
if (primitive == schema::Type::ANY_POINTER) {
scopeId = 0;
anyPointerKind = schema::Type::AnyPointer::Unconstrained::ANY_KIND;
} else {
schema = nullptr;
}
}
inline Type::Type(schema::Type::Which derived, const _::RawBrandedSchema* schema)
: baseType(derived), listDepth(0), isImplicitParam(false), schema(schema) {
KJ_IREQUIRE(derived == schema::Type::STRUCT ||
derived == schema::Type::ENUM ||
derived == schema::Type::INTERFACE);
}
inline Type::Type(StructSchema schema)
: baseType(schema::Type::STRUCT), listDepth(0), schema(schema.raw) {}
inline Type::Type(EnumSchema schema)
: baseType(schema::Type::ENUM), listDepth(0), schema(schema.raw) {}
inline Type::Type(InterfaceSchema schema)
: baseType(schema::Type::INTERFACE), listDepth(0), schema(schema.raw) {}
inline Type::Type(ListSchema schema)
: Type(schema.getElementType()) { ++listDepth; }
inline Type::Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind)
: baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false),
anyPointerKind(anyPointerKind), scopeId(0) {}
inline Type::Type(BrandParameter param)
: baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false),
paramIndex(param.index), scopeId(param.scopeId) {}
inline Type::Type(ImplicitParameter param)
: baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(true),
paramIndex(param.index), scopeId(0) {}
inline schema::Type::Which Type::which() const {
return listDepth > 0 ? schema::Type::LIST : baseType;
}
inline schema::Type::AnyPointer::Unconstrained::Which Type::whichAnyPointerKind() const {
KJ_IREQUIRE(baseType == schema::Type::ANY_POINTER);
return !isImplicitParam && scopeId == 0 ? anyPointerKind
: schema::Type::AnyPointer::Unconstrained::ANY_KIND;
}
template <typename T>
inline Type Type::from() { return Type(Schema::from<T>()); }
inline bool Type::isVoid () const { return baseType == schema::Type::VOID && listDepth == 0; }
inline bool Type::isBool () const { return baseType == schema::Type::BOOL && listDepth == 0; }
inline bool Type::isInt8 () const { return baseType == schema::Type::INT8 && listDepth == 0; }
inline bool Type::isInt16 () const { return baseType == schema::Type::INT16 && listDepth == 0; }
inline bool Type::isInt32 () const { return baseType == schema::Type::INT32 && listDepth == 0; }
inline bool Type::isInt64 () const { return baseType == schema::Type::INT64 && listDepth == 0; }
inline bool Type::isUInt8 () const { return baseType == schema::Type::UINT8 && listDepth == 0; }
inline bool Type::isUInt16 () const { return baseType == schema::Type::UINT16 && listDepth == 0; }
inline bool Type::isUInt32 () const { return baseType == schema::Type::UINT32 && listDepth == 0; }
inline bool Type::isUInt64 () const { return baseType == schema::Type::UINT64 && listDepth == 0; }
inline bool Type::isFloat32() const { return baseType == schema::Type::FLOAT32 && listDepth == 0; }
inline bool Type::isFloat64() const { return baseType == schema::Type::FLOAT64 && listDepth == 0; }
inline bool Type::isText () const { return baseType == schema::Type::TEXT && listDepth == 0; }
inline bool Type::isData () const { return baseType == schema::Type::DATA && listDepth == 0; }
inline bool Type::isList () const { return listDepth > 0; }
inline bool Type::isEnum () const { return baseType == schema::Type::ENUM && listDepth == 0; }
inline bool Type::isStruct () const { return baseType == schema::Type::STRUCT && listDepth == 0; }
inline bool Type::isInterface() const {
return baseType == schema::Type::INTERFACE && listDepth == 0;
}
inline bool Type::isAnyPointer() const {
return baseType == schema::Type::ANY_POINTER && listDepth == 0;
}
inline Type Type::wrapInList(uint depth) const {
Type result = *this;
result.listDepth += depth;
return result;
}
} // namespace capnp
#endif // CAPNP_SCHEMA_H_
@@ -0,0 +1,64 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SERIALIZE_ASYNC_H_
#define CAPNP_SERIALIZE_ASYNC_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include <kj/async-io.h>
#include "message.h"
namespace capnp {
kj::Promise<kj::Own<MessageReader>> readMessage(
kj::AsyncInputStream& input, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
// Read a message asynchronously.
//
// `input` must remain valid until the returned promise resolves (or is canceled).
//
// `scratchSpace`, if provided, must remain valid until the returned MessageReader is destroyed.
kj::Promise<kj::Maybe<kj::Own<MessageReader>>> tryReadMessage(
kj::AsyncInputStream& input, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
// Like `readMessage` but returns null on EOF.
kj::Promise<void> writeMessage(kj::AsyncOutputStream& output,
kj::ArrayPtr<const kj::ArrayPtr<const word>> segments)
KJ_WARN_UNUSED_RESULT;
kj::Promise<void> writeMessage(kj::AsyncOutputStream& output, MessageBuilder& builder)
KJ_WARN_UNUSED_RESULT;
// Write asynchronously. The parameters must remain valid until the returned promise resolves.
// =======================================================================================
// inline implementation details
inline kj::Promise<void> writeMessage(kj::AsyncOutputStream& output, MessageBuilder& builder) {
return writeMessage(output, builder.getSegmentsForOutput());
}
} // namespace capnp
#endif // CAPNP_SERIALIZE_ASYNC_H_
@@ -0,0 +1,130 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SERIALIZE_PACKED_H_
#define CAPNP_SERIALIZE_PACKED_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "serialize.h"
namespace capnp {
namespace _ { // private
class PackedInputStream: public kj::InputStream {
// An input stream that unpacks packed data with a picky constraint: The caller must read data
// in the exact same size and sequence as the data was written to PackedOutputStream.
public:
explicit PackedInputStream(kj::BufferedInputStream& inner);
KJ_DISALLOW_COPY(PackedInputStream);
~PackedInputStream() noexcept(false);
// implements InputStream ------------------------------------------
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override;
private:
kj::BufferedInputStream& inner;
};
class PackedOutputStream: public kj::OutputStream {
public:
explicit PackedOutputStream(kj::BufferedOutputStream& inner);
KJ_DISALLOW_COPY(PackedOutputStream);
~PackedOutputStream() noexcept(false);
// implements OutputStream -----------------------------------------
void write(const void* buffer, size_t bytes) override;
private:
kj::BufferedOutputStream& inner;
};
} // namespace _ (private)
class PackedMessageReader: private _::PackedInputStream, public InputStreamMessageReader {
public:
PackedMessageReader(kj::BufferedInputStream& inputStream, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
KJ_DISALLOW_COPY(PackedMessageReader);
~PackedMessageReader() noexcept(false);
};
class PackedFdMessageReader: private kj::FdInputStream, private kj::BufferedInputStreamWrapper,
public PackedMessageReader {
public:
PackedFdMessageReader(int fd, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
// Read message from a file descriptor, without taking ownership of the descriptor.
// Note that if you want to reuse the descriptor after the reader is destroyed, you'll need to
// seek it, since otherwise the position is unspecified.
PackedFdMessageReader(kj::AutoCloseFd fd, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
// Read a message from a file descriptor, taking ownership of the descriptor.
KJ_DISALLOW_COPY(PackedFdMessageReader);
~PackedFdMessageReader() noexcept(false);
};
void writePackedMessage(kj::BufferedOutputStream& output, MessageBuilder& builder);
void writePackedMessage(kj::BufferedOutputStream& output,
kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Write a packed message to a buffered output stream.
void writePackedMessage(kj::OutputStream& output, MessageBuilder& builder);
void writePackedMessage(kj::OutputStream& output,
kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Write a packed message to an unbuffered output stream. If you intend to write multiple messages
// in succession, consider wrapping your output in a buffered stream in order to reduce system
// call overhead.
void writePackedMessageToFd(int fd, MessageBuilder& builder);
void writePackedMessageToFd(int fd, kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Write a single packed message to the file descriptor.
size_t computeUnpackedSizeInWords(kj::ArrayPtr<const byte> packedBytes);
// Computes the number of words to which the given packed bytes will unpack. Not intended for use
// in performance-sensitive situations.
// =======================================================================================
// inline stuff
inline void writePackedMessage(kj::BufferedOutputStream& output, MessageBuilder& builder) {
writePackedMessage(output, builder.getSegmentsForOutput());
}
inline void writePackedMessage(kj::OutputStream& output, MessageBuilder& builder) {
writePackedMessage(output, builder.getSegmentsForOutput());
}
inline void writePackedMessageToFd(int fd, MessageBuilder& builder) {
writePackedMessageToFd(fd, builder.getSegmentsForOutput());
}
} // namespace capnp
#endif // CAPNP_SERIALIZE_PACKED_H_
@@ -0,0 +1,96 @@
// Copyright (c) 2015 Philip Quinn.
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_SERIALIZE_TEXT_H_
#define CAPNP_SERIALIZE_TEXT_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include <kj/string.h>
#include "dynamic.h"
#include "orphan.h"
#include "schema.h"
namespace capnp {
class TextCodec {
// Reads and writes Cap'n Proto objects in a plain text format (as used in the schema
// language for constants, and read/written by the 'decode' and 'encode' commands of
// the capnp tool).
//
// This format is useful for debugging or human input, but it is not a robust alternative
// to the binary format. Changes to a schema's types or names that are permitted in a
// schema's binary evolution will likely break messages stored in this format.
//
// Note that definitions or references (to constants, other fields, or files) are not
// permitted in this format. To evaluate declarations with the full expressiveness of the
// schema language, see `capnp::SchemaParser`.
//
// Requires linking with the capnpc library.
public:
TextCodec();
~TextCodec() noexcept(true);
void setPrettyPrint(bool enabled);
// If enabled, pads the output of `encode()` with spaces and newlines to make it more
// human-readable.
template <typename T>
kj::String encode(T&& value) const;
kj::String encode(DynamicValue::Reader value) const;
// Encode any Cap'n Proto value.
template <typename T>
Orphan<T> decode(kj::StringPtr input, Orphanage orphanage) const;
// Decode a text message into a Cap'n Proto object of type T, allocated in the given
// orphanage. Any errors parsing the input or assigning the fields of T are thrown as
// exceptions.
void decode(kj::StringPtr input, DynamicStruct::Builder output) const;
// Decode a text message for a struct into the given builder. Any errors parsing the
// input or assigning the fields of the output are thrown as exceptions.
// TODO(someday): expose some control over the error handling?
private:
Orphan<DynamicValue> decode(kj::StringPtr input, Type type, Orphanage orphanage) const;
bool prettyPrint;
};
// =======================================================================================
// inline stuff
template <typename T>
inline kj::String TextCodec::encode(T&& value) const {
return encode(DynamicValue::Reader(ReaderFor<FromAny<T>>(kj::fwd<T>(value))));
}
template <typename T>
inline Orphan<T> TextCodec::decode(kj::StringPtr input, Orphanage orphanage) const {
return decode(input, Type::from<T>(), orphanage).template releaseAs<T>();
}
} // namespace capnp
#endif // CAPNP_SERIALIZE_TEXT_H_
@@ -0,0 +1,237 @@
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This file implements a simple serialization format for Cap'n Proto messages. The format
// is as follows:
//
// * 32-bit little-endian segment count (4 bytes).
// * 32-bit little-endian size of each segment (4*(segment count) bytes).
// * Padding so that subsequent data is 64-bit-aligned (0 or 4 bytes). (I.e., if there are an even
// number of segments, there are 4 bytes of zeros here, otherwise there is no padding.)
// * Data from each segment, in order (8*sum(segment sizes) bytes)
//
// This format has some important properties:
// - It is self-delimiting, so multiple messages may be written to a stream without any external
// delimiter.
// - The total size and position of each segment can be determined by reading only the first part
// of the message, allowing lazy and random-access reading of the segment data.
// - A message is always at least 8 bytes.
// - A single-segment message can be read entirely in two system calls with no buffering.
// - A multi-segment message can be read entirely in three system calls with no buffering.
// - The format is appropriate for mmap()ing since all data is aligned.
#ifndef CAPNP_SERIALIZE_H_
#define CAPNP_SERIALIZE_H_
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#include "message.h"
#include <kj/io.h>
namespace capnp {
class FlatArrayMessageReader: public MessageReader {
// Parses a message from a flat array. Note that it makes sense to use this together with mmap()
// for extremely fast parsing.
public:
FlatArrayMessageReader(kj::ArrayPtr<const word> array, ReaderOptions options = ReaderOptions());
// The array must remain valid until the MessageReader is destroyed.
kj::ArrayPtr<const word> getSegment(uint id) override;
const word* getEnd() const { return end; }
// Get a pointer just past the end of the message as determined by reading the message header.
// This could actually be before the end of the input array. This pointer is useful e.g. if
// you know that the input array has extra stuff appended after the message and you want to
// get at it.
private:
// Optimize for single-segment case.
kj::ArrayPtr<const word> segment0;
kj::Array<kj::ArrayPtr<const word>> moreSegments;
const word* end;
};
kj::ArrayPtr<const word> initMessageBuilderFromFlatArrayCopy(
kj::ArrayPtr<const word> array, MessageBuilder& target,
ReaderOptions options = ReaderOptions());
// Convenience function which reads a message using `FlatArrayMessageReader` then copies the
// content into the target `MessageBuilder`, verifying that the message structure is valid
// (although not necessarily that it matches the desired schema).
//
// Returns an ArrayPtr containing any words left over in the array after consuming the whole
// message. This is useful when reading multiple messages that have been concatenated. See also
// FlatArrayMessageReader::getEnd().
//
// (Note that it's also possible to initialize a `MessageBuilder` directly without a copy using one
// of `MessageBuilder`'s constructors. However, this approach skips the validation step and is not
// safe to use on untrusted input. Therefore, we do not provide a convenience method for it.)
kj::Array<word> messageToFlatArray(MessageBuilder& builder);
// Constructs a flat array containing the entire content of the given message.
//
// To output the message as bytes, use `.asBytes()` on the returned word array. Keep in mind that
// `asBytes()` returns an ArrayPtr, so you have to save the Array as well to prevent it from being
// deleted. For example:
//
// kj::Array<capnp::word> words = messageToFlatArray(myMessage);
// kj::ArrayPtr<kj::byte> bytes = words.asBytes();
// write(fd, bytes.begin(), bytes.size());
kj::Array<word> messageToFlatArray(kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Version of messageToFlatArray that takes a raw segment array.
size_t computeSerializedSizeInWords(MessageBuilder& builder);
// Returns the size, in words, that will be needed to serialize the message, including the header.
size_t computeSerializedSizeInWords(kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Version of computeSerializedSizeInWords that takes a raw segment array.
size_t expectedSizeInWordsFromPrefix(kj::ArrayPtr<const word> messagePrefix);
// Given a prefix of a serialized message, try to determine the expected total size of the message,
// in words. The returned size is based on the information known so far; it may be an underestimate
// if the prefix doesn't contain the full segment table.
//
// If the returned value is greater than `messagePrefix.size()`, then the message is not yet
// complete and the app cannot parse it yet. If the returned value is less than or equal to
// `messagePrefix.size()`, then the returned value is the exact total size of the message; any
// remaining bytes are part of the next message.
//
// This function is useful when reading messages from a stream in an asynchronous way, but when
// using the full KJ async infrastructure would be too difficult. Each time bytes are received,
// use this function to determine if an entire message is ready to be parsed.
// =======================================================================================
class InputStreamMessageReader: public MessageReader {
// A MessageReader that reads from an abstract kj::InputStream. See also StreamFdMessageReader
// for a subclass specific to file descriptors.
public:
InputStreamMessageReader(kj::InputStream& inputStream,
ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
~InputStreamMessageReader() noexcept(false);
// implements MessageReader ----------------------------------------
kj::ArrayPtr<const word> getSegment(uint id) override;
private:
kj::InputStream& inputStream;
byte* readPos;
// Optimize for single-segment case.
kj::ArrayPtr<const word> segment0;
kj::Array<kj::ArrayPtr<const word>> moreSegments;
kj::Array<word> ownedSpace;
// Only if scratchSpace wasn't big enough.
kj::UnwindDetector unwindDetector;
};
void readMessageCopy(kj::InputStream& input, MessageBuilder& target,
ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
// Convenience function which reads a message using `InputStreamMessageReader` then copies the
// content into the target `MessageBuilder`, verifying that the message structure is valid
// (although not necessarily that it matches the desired schema).
//
// (Note that it's also possible to initialize a `MessageBuilder` directly without a copy using one
// of `MessageBuilder`'s constructors. However, this approach skips the validation step and is not
// safe to use on untrusted input. Therefore, we do not provide a convenience method for it.)
void writeMessage(kj::OutputStream& output, MessageBuilder& builder);
// Write the message to the given output stream.
void writeMessage(kj::OutputStream& output, kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Write the segment array to the given output stream.
// =======================================================================================
// Specializations for reading from / writing to file descriptors.
class StreamFdMessageReader: private kj::FdInputStream, public InputStreamMessageReader {
// A MessageReader that reads from a steam-based file descriptor.
public:
StreamFdMessageReader(int fd, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr)
: FdInputStream(fd), InputStreamMessageReader(*this, options, scratchSpace) {}
// Read message from a file descriptor, without taking ownership of the descriptor.
StreamFdMessageReader(kj::AutoCloseFd fd, ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr)
: FdInputStream(kj::mv(fd)), InputStreamMessageReader(*this, options, scratchSpace) {}
// Read a message from a file descriptor, taking ownership of the descriptor.
~StreamFdMessageReader() noexcept(false);
};
void readMessageCopyFromFd(int fd, MessageBuilder& target,
ReaderOptions options = ReaderOptions(),
kj::ArrayPtr<word> scratchSpace = nullptr);
// Convenience function which reads a message using `StreamFdMessageReader` then copies the
// content into the target `MessageBuilder`, verifying that the message structure is valid
// (although not necessarily that it matches the desired schema).
//
// (Note that it's also possible to initialize a `MessageBuilder` directly without a copy using one
// of `MessageBuilder`'s constructors. However, this approach skips the validation step and is not
// safe to use on untrusted input. Therefore, we do not provide a convenience method for it.)
void writeMessageToFd(int fd, MessageBuilder& builder);
// Write the message to the given file descriptor.
//
// This function throws an exception on any I/O error. If your code is not exception-safe, be sure
// you catch this exception at the call site. If throwing an exception is not acceptable, you
// can implement your own OutputStream with arbitrary error handling and then use writeMessage().
void writeMessageToFd(int fd, kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
// Write the segment array to the given file descriptor.
//
// This function throws an exception on any I/O error. If your code is not exception-safe, be sure
// you catch this exception at the call site. If throwing an exception is not acceptable, you
// can implement your own OutputStream with arbitrary error handling and then use writeMessage().
// =======================================================================================
// inline stuff
inline kj::Array<word> messageToFlatArray(MessageBuilder& builder) {
return messageToFlatArray(builder.getSegmentsForOutput());
}
inline size_t computeSerializedSizeInWords(MessageBuilder& builder) {
return computeSerializedSizeInWords(builder.getSegmentsForOutput());
}
inline void writeMessage(kj::OutputStream& output, MessageBuilder& builder) {
writeMessage(output, builder.getSegmentsForOutput());
}
inline void writeMessageToFd(int fd, MessageBuilder& builder) {
writeMessageToFd(fd, builder.getSegmentsForOutput());
}
} // namespace capnp
#endif // SERIALIZE_H_