parent
660a156557
commit
e10f330290
93 changed files with 44625 additions and 13 deletions
File diff suppressed because it is too large
Load Diff
@ -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_
|
@ -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_
|
@ -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
@ -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_
|
@ -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
@ -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_
|
@ -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_
|
@ -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
@ -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
@ -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_
|
@ -0,0 +1,426 @@ |
||||
/* vim: set sw=8 ts=8 sts=8 noet: */ |
||||
/* capnp_c.h
|
||||
* |
||||
* Copyright (C) 2013 James McKaskill |
||||
* Copyright (C) 2014 Steve Dee |
||||
* |
||||
* This software may be modified and distributed under the terms |
||||
* of the MIT license. See the LICENSE file for details. |
||||
*/ |
||||
|
||||
#ifndef CAPNP_C_H |
||||
#define CAPNP_C_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#if defined(unix) && !defined(__APPLE__) |
||||
#include <endian.h> |
||||
#endif |
||||
|
||||
// ssize_t is not defined in stdint.h in MSVC.
|
||||
#ifdef _MSC_VER |
||||
typedef intmax_t ssize_t; |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) |
||||
#define CAPN_INLINE static inline |
||||
#else |
||||
#define CAPN_INLINE static |
||||
#endif |
||||
|
||||
#define CAPN_VERSION 1 |
||||
|
||||
/* struct capn is a common structure shared between segments in the same
|
||||
* session/context so that far pointers between segments will be created. |
||||
* |
||||
* lookup is used to lookup segments by id when derefencing a far pointer |
||||
* |
||||
* create is used to create or lookup an alternate segment that has at least |
||||
* sz available (ie returned seg->len + sz <= seg->cap) |
||||
* |
||||
* create_local is used to create a segment for the copy tree and should be |
||||
* allocated in the local memory space. |
||||
* |
||||
* Allocated segments must be zero initialized. |
||||
* |
||||
* create and lookup can be NULL if you don't need multiple segments and don't |
||||
* want to support copying |
||||
* |
||||
* seglist and copylist are linked lists which can be used to free up segments |
||||
* on cleanup, but should not be modified by the user. |
||||
* |
||||
* lookup, create, create_local, and user can be set by the user. Other values |
||||
* should be zero initialized. |
||||
*/ |
||||
struct capn { |
||||
/* user settable */ |
||||
struct capn_segment *(*lookup)(void* /*user*/, uint32_t /*id */); |
||||
struct capn_segment *(*create)(void* /*user*/, uint32_t /*id */, int /*sz*/); |
||||
struct capn_segment *(*create_local)(void* /*user*/, int /*sz*/); |
||||
void *user; |
||||
/* zero initialized, user should not modify */ |
||||
uint32_t segnum; |
||||
struct capn_tree *copy; |
||||
struct capn_tree *segtree; |
||||
struct capn_segment *seglist, *lastseg; |
||||
struct capn_segment *copylist; |
||||
}; |
||||
|
||||
/* struct capn_tree is a rb tree header used internally for the segment id
|
||||
* lookup and copy tree */ |
||||
struct capn_tree { |
||||
struct capn_tree *parent, *link[2]; |
||||
unsigned int red : 1; |
||||
}; |
||||
|
||||
struct capn_tree *capn_tree_insert(struct capn_tree *root, struct capn_tree *n); |
||||
|
||||
/* struct capn_segment contains the information about a single segment.
|
||||
* |
||||
* capn points to a struct capn that is shared between segments in the |
||||
* same session |
||||
* |
||||
* id specifies the segment id, used for far pointers |
||||
* |
||||
* data specifies the segment data. This should not move after creation. |
||||
* |
||||
* len specifies the current segment length. This is 0 for a blank |
||||
* segment. |
||||
* |
||||
* cap specifies the segment capacity. |
||||
* |
||||
* When creating new structures len will be incremented until it reaces cap, |
||||
* at which point a new segment will be requested via capn->create. The |
||||
* create callback can either create a new segment or expand an existing |
||||
* one by incrementing cap and returning the expanded segment. |
||||
* |
||||
* data, len, and cap must all by 8 byte aligned |
||||
* |
||||
* data, len, cap, and user should all set by the user. Other values |
||||
* should be zero initialized. |
||||
*/ |
||||
#ifdef _MSC_VER |
||||
__declspec(align(64)) |
||||
#endif |
||||
struct capn_segment { |
||||
struct capn_tree hdr; |
||||
struct capn_segment *next; |
||||
struct capn *capn; |
||||
uint32_t id; |
||||
/* user settable */ |
||||
char *data; |
||||
size_t len, cap; |
||||
void *user; |
||||
}; |
||||
|
||||
enum CAPN_TYPE { |
||||
CAPN_NULL = 0, |
||||
CAPN_STRUCT = 1, |
||||
CAPN_LIST = 2, |
||||
CAPN_PTR_LIST = 3, |
||||
CAPN_BIT_LIST = 4, |
||||
CAPN_FAR_POINTER = 5, |
||||
}; |
||||
|
||||
struct capn_ptr { |
||||
unsigned int type : 4; |
||||
unsigned int has_ptr_tag : 1; |
||||
unsigned int is_list_member : 1; |
||||
unsigned int is_composite_list : 1; |
||||
unsigned int datasz : 19; |
||||
unsigned int ptrs : 16; |
||||
int len; |
||||
char *data; |
||||
struct capn_segment *seg; |
||||
}; |
||||
|
||||
struct capn_text { |
||||
int len; |
||||
const char *str; |
||||
struct capn_segment *seg; |
||||
}; |
||||
|
||||
typedef struct capn_ptr capn_ptr; |
||||
typedef struct capn_text capn_text; |
||||
typedef struct {capn_ptr p;} capn_data; |
||||
typedef struct {capn_ptr p;} capn_list1; |
||||
typedef struct {capn_ptr p;} capn_list8; |
||||
typedef struct {capn_ptr p;} capn_list16; |
||||
typedef struct {capn_ptr p;} capn_list32; |
||||
typedef struct {capn_ptr p;} capn_list64; |
||||
|
||||
struct capn_msg { |
||||
struct capn_segment *seg; |
||||
uint64_t iface; |
||||
uint16_t method; |
||||
capn_ptr args; |
||||
}; |
||||
|
||||
/* capn_append_segment appends a segment to a session */ |
||||
void capn_append_segment(struct capn*, struct capn_segment*); |
||||
|
||||
capn_ptr capn_root(struct capn *c); |
||||
void capn_resolve(capn_ptr *p); |
||||
|
||||
#define capn_len(list) ((list).p.type == CAPN_FAR_POINTER ? (capn_resolve(&(list).p), (list).p.len) : (list).p.len) |
||||
|
||||
/* capn_getp|setp functions get/set ptrs in list/structs
|
||||
* off is the list index or pointer index in a struct |
||||
* capn_setp will copy the data, create far pointers, etc if the target |
||||
* is in a different segment/context. |
||||
* Both of these will use/return inner pointers for composite lists. |
||||
*/ |
||||
capn_ptr capn_getp(capn_ptr p, int off, int resolve); |
||||
int capn_setp(capn_ptr p, int off, capn_ptr tgt); |
||||
|
||||
capn_text capn_get_text(capn_ptr p, int off, capn_text def); |
||||
capn_data capn_get_data(capn_ptr p, int off); |
||||
int capn_set_text(capn_ptr p, int off, capn_text tgt); |
||||
|
||||
/* capn_get* functions get data from a list
|
||||
* The length of the list is given by p->size |
||||
* off specifies how far into the list to start |
||||
* sz indicates the number of elements to get |
||||
* The function returns the number of elements read or -1 on an error. |
||||
* off must be byte aligned for capn_getv1 |
||||
*/ |
||||
int capn_get1(capn_list1 p, int off); |
||||
uint8_t capn_get8(capn_list8 p, int off); |
||||
uint16_t capn_get16(capn_list16 p, int off); |
||||
uint32_t capn_get32(capn_list32 p, int off); |
||||
uint64_t capn_get64(capn_list64 p, int off); |
||||
int capn_getv1(capn_list1 p, int off, uint8_t *data, int sz); |
||||
int capn_getv8(capn_list8 p, int off, uint8_t *data, int sz); |
||||
int capn_getv16(capn_list16 p, int off, uint16_t *data, int sz); |
||||
int capn_getv32(capn_list32 p, int off, uint32_t *data, int sz); |
||||
int capn_getv64(capn_list64 p, int off, uint64_t *data, int sz); |
||||
|
||||
/* capn_set* functions set data in a list
|
||||
* off specifies how far into the list to start |
||||
* sz indicates the number of elements to write |
||||
* The function returns the number of elemnts written or -1 on an error. |
||||
* off must be byte aligned for capn_setv1 |
||||
*/ |
||||
int capn_set1(capn_list1 p, int off, int v); |
||||
int capn_set8(capn_list8 p, int off, uint8_t v); |
||||
int capn_set16(capn_list16 p, int off, uint16_t v); |
||||
int capn_set32(capn_list32 p, int off, uint32_t v); |
||||
int capn_set64(capn_list64 p, int off, uint64_t v); |
||||
int capn_setv1(capn_list1 p, int off, const uint8_t *data, int sz); |
||||
int capn_setv8(capn_list8 p, int off, const uint8_t *data, int sz); |
||||
int capn_setv16(capn_list16 p, int off, const uint16_t *data, int sz); |
||||
int capn_setv32(capn_list32 p, int off, const uint32_t *data, int sz); |
||||
int capn_setv64(capn_list64 p, int off, const uint64_t *data, int sz); |
||||
|
||||
/* capn_new_* functions create a new object
|
||||
* datasz is in bytes, ptrs is # of pointers, sz is # of elements in the list |
||||
* On an error a CAPN_NULL pointer is returned |
||||
*/ |
||||
capn_ptr capn_new_string(struct capn_segment *seg, const char *str, ssize_t sz); |
||||
capn_ptr capn_new_struct(struct capn_segment *seg, int datasz, int ptrs); |
||||
capn_ptr capn_new_interface(struct capn_segment *seg, int datasz, int ptrs); |
||||
capn_ptr capn_new_ptr_list(struct capn_segment *seg, int sz); |
||||
capn_ptr capn_new_list(struct capn_segment *seg, int sz, int datasz, int ptrs); |
||||
capn_list1 capn_new_list1(struct capn_segment *seg, int sz); |
||||
capn_list8 capn_new_list8(struct capn_segment *seg, int sz); |
||||
capn_list16 capn_new_list16(struct capn_segment *seg, int sz); |
||||
capn_list32 capn_new_list32(struct capn_segment *seg, int sz); |
||||
capn_list64 capn_new_list64(struct capn_segment *seg, int sz); |
||||
|
||||
/* capn_read|write* functions read/write struct values
|
||||
* off is the offset into the structure in bytes |
||||
* Rarely should these be called directly, instead use the generated code. |
||||
* Data must be xored with the default value |
||||
* These are inlined |
||||
*/ |
||||
CAPN_INLINE uint8_t capn_read8(capn_ptr p, int off); |
||||
CAPN_INLINE uint16_t capn_read16(capn_ptr p, int off); |
||||
CAPN_INLINE uint32_t capn_read32(capn_ptr p, int off); |
||||
CAPN_INLINE uint64_t capn_read64(capn_ptr p, int off); |
||||
CAPN_INLINE int capn_write1(capn_ptr p, int off, int val); |
||||
CAPN_INLINE int capn_write8(capn_ptr p, int off, uint8_t val); |
||||
CAPN_INLINE int capn_write16(capn_ptr p, int off, uint16_t val); |
||||
CAPN_INLINE int capn_write32(capn_ptr p, int off, uint32_t val); |
||||
CAPN_INLINE int capn_write64(capn_ptr p, int off, uint64_t val); |
||||
|
||||
/* capn_init_malloc inits the capn struct with a create function which
|
||||
* allocates segments on the heap using malloc |
||||
* |
||||
* capn_init_(fp|mem) inits by reading segments in from the file/memory buffer |
||||
* in serialized form (optionally packed). It will then setup the create |
||||
* function ala capn_init_malloc so that further segments can be created. |
||||
* |
||||
* capn_free frees all the segment headers and data created by the create |
||||
* function setup by capn_init_* |
||||
*/ |
||||
void capn_init_malloc(struct capn *c); |
||||
int capn_init_fp(struct capn *c, FILE *f, int packed); |
||||
int capn_init_mem(struct capn *c, const uint8_t *p, size_t sz, int packed); |
||||
|
||||
/* capn_write_(fp|mem) writes segments to the file/memory buffer in
|
||||
* serialized form and returns the number of bytes written. |
||||
*/ |
||||
/* TODO */ |
||||
/*int capn_write_fp(struct capn *c, FILE *f, int packed);*/ |
||||
int capn_write_fd(struct capn *c, ssize_t (*write_fd)(int fd, void *p, size_t count), int fd, int packed); |
||||
int capn_write_mem(struct capn *c, uint8_t *p, size_t sz, int packed); |
||||
|
||||
void capn_free(struct capn *c); |
||||
void capn_reset_copy(struct capn *c); |
||||
|
||||
/* Inline functions */ |
||||
|
||||
|
||||
CAPN_INLINE uint8_t capn_flip8(uint8_t v) { |
||||
return v; |
||||
} |
||||
CAPN_INLINE uint16_t capn_flip16(uint16_t v) { |
||||
#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN) |
||||
return v; |
||||
#elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) && \ |
||||
defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 |
||||
return __builtin_bswap16(v); |
||||
#else |
||||
union { uint16_t u; uint8_t v[2]; } s; |
||||
s.v[0] = (uint8_t)v; |
||||
s.v[1] = (uint8_t)(v>>8); |
||||
return s.u; |
||||
#endif |
||||
} |
||||
CAPN_INLINE uint32_t capn_flip32(uint32_t v) { |
||||
#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN) |
||||
return v; |
||||
#elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) && \ |
||||
defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 |
||||
return __builtin_bswap32(v); |
||||
#else |
||||
union { uint32_t u; uint8_t v[4]; } s; |
||||
s.v[0] = (uint8_t)v; |
||||
s.v[1] = (uint8_t)(v>>8); |
||||
s.v[2] = (uint8_t)(v>>16); |
||||
s.v[3] = (uint8_t)(v>>24); |
||||
return s.u; |
||||
#endif |
||||
} |
||||
CAPN_INLINE uint64_t capn_flip64(uint64_t v) { |
||||
#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN) |
||||
return v; |
||||
#elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) && \ |
||||
defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 |
||||
return __builtin_bswap64(v); |
||||
#else |
||||
union { uint64_t u; uint8_t v[8]; } s; |
||||
s.v[0] = (uint8_t)v; |
||||
s.v[1] = (uint8_t)(v>>8); |
||||
s.v[2] = (uint8_t)(v>>16); |
||||
s.v[3] = (uint8_t)(v>>24); |
||||
s.v[4] = (uint8_t)(v>>32); |
||||
s.v[5] = (uint8_t)(v>>40); |
||||
s.v[6] = (uint8_t)(v>>48); |
||||
s.v[7] = (uint8_t)(v>>56); |
||||
return s.u; |
||||
#endif |
||||
} |
||||
|
||||
CAPN_INLINE int capn_write1(capn_ptr p, int off, int val) { |
||||
if (off >= p.datasz*8) { |
||||
return -1; |
||||
} else if (val) { |
||||
uint8_t tmp = (uint8_t)(1 << (off & 7)); |
||||
((uint8_t*) p.data)[off >> 3] |= tmp; |
||||
return 0; |
||||
} else { |
||||
uint8_t tmp = (uint8_t)(~(1 << (off & 7))); |
||||
((uint8_t*) p.data)[off >> 3] &= tmp; |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
CAPN_INLINE uint8_t capn_read8(capn_ptr p, int off) { |
||||
return off+1 <= p.datasz ? capn_flip8(*(uint8_t*) (p.data+off)) : 0; |
||||
} |
||||
CAPN_INLINE int capn_write8(capn_ptr p, int off, uint8_t val) { |
||||
if (off+1 <= p.datasz) { |
||||
*(uint8_t*) (p.data+off) = capn_flip8(val); |
||||
return 0; |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
CAPN_INLINE uint16_t capn_read16(capn_ptr p, int off) { |
||||
return off+2 <= p.datasz ? capn_flip16(*(uint16_t*) (p.data+off)) : 0; |
||||
} |
||||
CAPN_INLINE int capn_write16(capn_ptr p, int off, uint16_t val) { |
||||
if (off+2 <= p.datasz) { |
||||
*(uint16_t*) (p.data+off) = capn_flip16(val); |
||||
return 0; |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
CAPN_INLINE uint32_t capn_read32(capn_ptr p, int off) { |
||||
return off+4 <= p.datasz ? capn_flip32(*(uint32_t*) (p.data+off)) : 0; |
||||
} |
||||
CAPN_INLINE int capn_write32(capn_ptr p, int off, uint32_t val) { |
||||
if (off+4 <= p.datasz) { |
||||
*(uint32_t*) (p.data+off) = capn_flip32(val); |
||||
return 0; |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
CAPN_INLINE uint64_t capn_read64(capn_ptr p, int off) { |
||||
return off+8 <= p.datasz ? capn_flip64(*(uint64_t*) (p.data+off)) : 0; |
||||
} |
||||
CAPN_INLINE int capn_write64(capn_ptr p, int off, uint64_t val) { |
||||
if (off+8 <= p.datasz) { |
||||
*(uint64_t*) (p.data+off) = capn_flip64(val); |
||||
return 0; |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
union capn_conv_f32 { |
||||
uint32_t u; |
||||
float f; |
||||
}; |
||||
|
||||
union capn_conv_f64 { |
||||
uint64_t u; |
||||
double f; |
||||
}; |
||||
|
||||
CAPN_INLINE float capn_to_f32(uint32_t v) { |
||||
union capn_conv_f32 u; |
||||
u.u = v; |
||||
return u.f; |
||||
} |
||||
CAPN_INLINE double capn_to_f64(uint64_t v) { |
||||
union capn_conv_f64 u; |
||||
u.u = v; |
||||
return u.f; |
||||
} |
||||
CAPN_INLINE uint32_t capn_from_f32(float v) { |
||||
union capn_conv_f32 u; |
||||
u.f = v; |
||||
return u.u; |
||||
} |
||||
CAPN_INLINE uint64_t capn_from_f64(double v) { |
||||
union capn_conv_f64 u; |
||||
u.f = v; |
||||
return u.u; |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,213 @@ |
||||
// 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 KJ_ARENA_H_ |
||||
#define KJ_ARENA_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "memory.h" |
||||
#include "array.h" |
||||
#include "string.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class Arena { |
||||
// A class which allows several objects to be allocated in contiguous chunks of memory, then
|
||||
// frees them all at once.
|
||||
//
|
||||
// Allocating from the same Arena in multiple threads concurrently is NOT safe, because making
|
||||
// it safe would require atomic operations that would slow down allocation even when
|
||||
// single-threaded. If you need to use arena allocation in a multithreaded context, consider
|
||||
// allocating thread-local arenas.
|
||||
|
||||
public: |
||||
explicit Arena(size_t chunkSizeHint = 1024); |
||||
// Create an Arena. `chunkSizeHint` hints at where to start when allocating chunks, but is only
|
||||
// a hint -- the Arena will, for example, allocate progressively larger chunks as time goes on,
|
||||
// in order to reduce overall allocation overhead.
|
||||
|
||||
explicit Arena(ArrayPtr<byte> scratch); |
||||
// Allocates from the given scratch space first, only resorting to the heap when it runs out.
|
||||
|
||||
KJ_DISALLOW_COPY(Arena); |
||||
~Arena() noexcept(false); |
||||
|
||||
template <typename T, typename... Params> |
||||
T& allocate(Params&&... params); |
||||
template <typename T> |
||||
ArrayPtr<T> allocateArray(size_t size); |
||||
// Allocate an object or array of type T. If T has a non-trivial destructor, that destructor
|
||||
// will be run during the Arena's destructor. Such destructors are run in opposite order of
|
||||
// allocation. Note that these methods must maintain a list of destructors to call, which has
|
||||
// overhead, but this overhead only applies if T has a non-trivial destructor.
|
||||
|
||||
template <typename T, typename... Params> |
||||
Own<T> allocateOwn(Params&&... params); |
||||
template <typename T> |
||||
Array<T> allocateOwnArray(size_t size); |
||||
template <typename T> |
||||
ArrayBuilder<T> allocateOwnArrayBuilder(size_t capacity); |
||||
// Allocate an object or array of type T. Destructors are executed when the returned Own<T>
|
||||
// or Array<T> goes out-of-scope, which must happen before the Arena is destroyed. This variant
|
||||
// is useful when you need to control when the destructor is called. This variant also avoids
|
||||
// the need for the Arena itself to keep track of destructors to call later, which may make it
|
||||
// slightly more efficient.
|
||||
|
||||
template <typename T> |
||||
inline T& copy(T&& value) { return allocate<Decay<T>>(kj::fwd<T>(value)); } |
||||
// Allocate a copy of the given value in the arena. This is just a shortcut for calling the
|
||||
// type's copy (or move) constructor.
|
||||
|
||||
StringPtr copyString(StringPtr content); |
||||
// Make a copy of the given string inside the arena, and return a pointer to the copy.
|
||||
|
||||
private: |
||||
struct ChunkHeader { |
||||
ChunkHeader* next; |
||||
byte* pos; // first unallocated byte in this chunk
|
||||
byte* end; // end of this chunk
|
||||
}; |
||||
struct ObjectHeader { |
||||
void (*destructor)(void*); |
||||
ObjectHeader* next; |
||||
}; |
||||
|
||||
size_t nextChunkSize; |
||||
ChunkHeader* chunkList = nullptr; |
||||
ObjectHeader* objectList = nullptr; |
||||
|
||||
ChunkHeader* currentChunk = nullptr; |
||||
|
||||
void cleanup(); |
||||
// Run all destructors, leaving the above pointers null. If a destructor throws, the State is
|
||||
// left in a consistent state, such that if cleanup() is called again, it will pick up where
|
||||
// it left off.
|
||||
|
||||
void* allocateBytes(size_t amount, uint alignment, bool hasDisposer); |
||||
// Allocate the given number of bytes. `hasDisposer` must be true if `setDisposer()` may be
|
||||
// called on this pointer later.
|
||||
|
||||
void* allocateBytesInternal(size_t amount, uint alignment); |
||||
// Try to allocate the given number of bytes without taking a lock. Fails if and only if there
|
||||
// is no space left in the current chunk.
|
||||
|
||||
void setDestructor(void* ptr, void (*destructor)(void*)); |
||||
// Schedule the given destructor to be executed when the Arena is destroyed. `ptr` must be a
|
||||
// pointer previously returned by an `allocateBytes()` call for which `hasDisposer` was true.
|
||||
|
||||
template <typename T> |
||||
static void destroyArray(void* pointer) { |
||||
size_t elementCount = *reinterpret_cast<size_t*>(pointer); |
||||
constexpr size_t prefixSize = kj::max(alignof(T), sizeof(size_t)); |
||||
DestructorOnlyArrayDisposer::instance.disposeImpl( |
||||
reinterpret_cast<byte*>(pointer) + prefixSize, |
||||
sizeof(T), elementCount, elementCount, &destroyObject<T>); |
||||
} |
||||
|
||||
template <typename T> |
||||
static void destroyObject(void* pointer) { |
||||
dtor(*reinterpret_cast<T*>(pointer)); |
||||
} |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
template <typename T, typename... Params> |
||||
T& Arena::allocate(Params&&... params) { |
||||
T& result = *reinterpret_cast<T*>(allocateBytes( |
||||
sizeof(T), alignof(T), !__has_trivial_destructor(T))); |
||||
if (!__has_trivial_constructor(T) || sizeof...(Params) > 0) { |
||||
ctor(result, kj::fwd<Params>(params)...); |
||||
} |
||||
if (!__has_trivial_destructor(T)) { |
||||
setDestructor(&result, &destroyObject<T>); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
template <typename T> |
||||
ArrayPtr<T> Arena::allocateArray(size_t size) { |
||||
if (__has_trivial_destructor(T)) { |
||||
ArrayPtr<T> result = |
||||
arrayPtr(reinterpret_cast<T*>(allocateBytes( |
||||
sizeof(T) * size, alignof(T), false)), size); |
||||
if (!__has_trivial_constructor(T)) { |
||||
for (size_t i = 0; i < size; i++) { |
||||
ctor(result[i]); |
||||
} |
||||
} |
||||
return result; |
||||
} else { |
||||
// Allocate with a 64-bit prefix in which we store the array size.
|
||||
constexpr size_t prefixSize = kj::max(alignof(T), sizeof(size_t)); |
||||
void* base = allocateBytes(sizeof(T) * size + prefixSize, alignof(T), true); |
||||
size_t& tag = *reinterpret_cast<size_t*>(base); |
||||
ArrayPtr<T> result = |
||||
arrayPtr(reinterpret_cast<T*>(reinterpret_cast<byte*>(base) + prefixSize), size); |
||||
setDestructor(base, &destroyArray<T>); |
||||
|
||||
if (__has_trivial_constructor(T)) { |
||||
tag = size; |
||||
} else { |
||||
// In case of constructor exceptions, we need the tag to end up storing the number of objects
|
||||
// that were successfully constructed, so that they'll be properly destroyed.
|
||||
tag = 0; |
||||
for (size_t i = 0; i < size; i++) { |
||||
ctor(result[i]); |
||||
tag = i + 1; |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
template <typename T, typename... Params> |
||||
Own<T> Arena::allocateOwn(Params&&... params) { |
||||
T& result = *reinterpret_cast<T*>(allocateBytes(sizeof(T), alignof(T), false)); |
||||
if (!__has_trivial_constructor(T) || sizeof...(Params) > 0) { |
||||
ctor(result, kj::fwd<Params>(params)...); |
||||
} |
||||
return Own<T>(&result, DestructorOnlyDisposer<T>::instance); |
||||
} |
||||
|
||||
template <typename T> |
||||
Array<T> Arena::allocateOwnArray(size_t size) { |
||||
ArrayBuilder<T> result = allocateOwnArrayBuilder<T>(size); |
||||
for (size_t i = 0; i < size; i++) { |
||||
result.add(); |
||||
} |
||||
return result.finish(); |
||||
} |
||||
|
||||
template <typename T> |
||||
ArrayBuilder<T> Arena::allocateOwnArrayBuilder(size_t capacity) { |
||||
return ArrayBuilder<T>( |
||||
reinterpret_cast<T*>(allocateBytes(sizeof(T) * capacity, alignof(T), false)), |
||||
capacity, DestructorOnlyArrayDisposer::instance); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ARENA_H_
|
@ -0,0 +1,813 @@ |
||||
// 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 KJ_ARRAY_H_ |
||||
#define KJ_ARRAY_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "common.h" |
||||
#include <string.h> |
||||
#include <initializer_list> |
||||
|
||||
namespace kj { |
||||
|
||||
// =======================================================================================
|
||||
// ArrayDisposer -- Implementation details.
|
||||
|
||||
class ArrayDisposer { |
||||
// Much like Disposer from memory.h.
|
||||
|
||||
protected: |
||||
// Do not declare a destructor, as doing so will force a global initializer for
|
||||
// HeapArrayDisposer::instance.
|
||||
|
||||
virtual void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount, |
||||
size_t capacity, void (*destroyElement)(void*)) const = 0; |
||||
// Disposes of the array. `destroyElement` invokes the destructor of each element, or is nullptr
|
||||
// if the elements have trivial destructors. `capacity` is the amount of space that was
|
||||
// allocated while `elementCount` is the number of elements that were actually constructed;
|
||||
// these are always the same number for Array<T> but may be different when using ArrayBuilder<T>.
|
||||
|
||||
public: |
||||
|
||||
template <typename T> |
||||
void dispose(T* firstElement, size_t elementCount, size_t capacity) const; |
||||
// Helper wrapper around disposeImpl().
|
||||
//
|
||||
// Callers must not call dispose() on the same array twice, even if the first call throws
|
||||
// an exception.
|
||||
|
||||
private: |
||||
template <typename T, bool hasTrivialDestructor = __has_trivial_destructor(T)> |
||||
struct Dispose_; |
||||
}; |
||||
|
||||
class ExceptionSafeArrayUtil { |
||||
// Utility class that assists in constructing or destroying elements of an array, where the
|
||||
// constructor or destructor could throw exceptions. In case of an exception,
|
||||
// ExceptionSafeArrayUtil's destructor will call destructors on all elements that have been
|
||||
// constructed but not destroyed. Remember that destructors that throw exceptions are required
|
||||
// to use UnwindDetector to detect unwind and avoid exceptions in this case. Therefore, no more
|
||||
// than one exception will be thrown (and the program will not terminate).
|
||||
|
||||
public: |
||||
inline ExceptionSafeArrayUtil(void* ptr, size_t elementSize, size_t constructedElementCount, |
||||
void (*destroyElement)(void*)) |
||||
: pos(reinterpret_cast<byte*>(ptr) + elementSize * constructedElementCount), |
||||
elementSize(elementSize), constructedElementCount(constructedElementCount), |
||||
destroyElement(destroyElement) {} |
||||
KJ_DISALLOW_COPY(ExceptionSafeArrayUtil); |
||||
|
||||
inline ~ExceptionSafeArrayUtil() noexcept(false) { |
||||
if (constructedElementCount > 0) destroyAll(); |
||||
} |
||||
|
||||
void construct(size_t count, void (*constructElement)(void*)); |
||||
// Construct the given number of elements.
|
||||
|
||||
void destroyAll(); |
||||
// Destroy all elements. Call this immediately before ExceptionSafeArrayUtil goes out-of-scope
|
||||
// to ensure that one element throwing an exception does not prevent the others from being
|
||||
// destroyed.
|
||||
|
||||
void release() { constructedElementCount = 0; } |
||||
// Prevent ExceptionSafeArrayUtil's destructor from destroying the constructed elements.
|
||||
// Call this after you've successfully finished constructing.
|
||||
|
||||
private: |
||||
byte* pos; |
||||
size_t elementSize; |
||||
size_t constructedElementCount; |
||||
void (*destroyElement)(void*); |
||||
}; |
||||
|
||||
class DestructorOnlyArrayDisposer: public ArrayDisposer { |
||||
public: |
||||
static const DestructorOnlyArrayDisposer instance; |
||||
|
||||
void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount, |
||||
size_t capacity, void (*destroyElement)(void*)) const override; |
||||
}; |
||||
|
||||
class NullArrayDisposer: public ArrayDisposer { |
||||
// An ArrayDisposer that does nothing. Can be used to construct a fake Arrays that doesn't
|
||||
// actually own its content.
|
||||
|
||||
public: |
||||
static const NullArrayDisposer instance; |
||||
|
||||
void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount, |
||||
size_t capacity, void (*destroyElement)(void*)) const override; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Array
|
||||
|
||||
template <typename T> |
||||
class Array { |
||||
// An owned array which will automatically be disposed of (using an ArrayDisposer) in the
|
||||
// destructor. Can be moved, but not copied. Much like Own<T>, but for arrays rather than
|
||||
// single objects.
|
||||
|
||||
public: |
||||
inline Array(): ptr(nullptr), size_(0), disposer(nullptr) {} |
||||
inline Array(decltype(nullptr)): ptr(nullptr), size_(0), disposer(nullptr) {} |
||||
inline Array(Array&& other) noexcept |
||||
: ptr(other.ptr), size_(other.size_), disposer(other.disposer) { |
||||
other.ptr = nullptr; |
||||
other.size_ = 0; |
||||
} |
||||
inline Array(Array<RemoveConstOrDisable<T>>&& other) noexcept |
||||
: ptr(other.ptr), size_(other.size_), disposer(other.disposer) { |
||||
other.ptr = nullptr; |
||||
other.size_ = 0; |
||||
} |
||||
inline Array(T* firstElement, size_t size, const ArrayDisposer& disposer) |
||||
: ptr(firstElement), size_(size), disposer(&disposer) {} |
||||
|
||||
KJ_DISALLOW_COPY(Array); |
||||
inline ~Array() noexcept { dispose(); } |
||||
|
||||
inline operator ArrayPtr<T>() { |
||||
return ArrayPtr<T>(ptr, size_); |
||||
} |
||||
inline operator ArrayPtr<const T>() const { |
||||
return ArrayPtr<T>(ptr, size_); |
||||
} |
||||
inline ArrayPtr<T> asPtr() { |
||||
return ArrayPtr<T>(ptr, size_); |
||||
} |
||||
inline ArrayPtr<const T> asPtr() const { |
||||
return ArrayPtr<T>(ptr, size_); |
||||
} |
||||
|
||||
inline size_t size() const { return size_; } |
||||
inline T& operator[](size_t index) const { |
||||
KJ_IREQUIRE(index < size_, "Out-of-bounds Array access."); |
||||
return ptr[index]; |
||||
} |
||||
|
||||
inline const T* begin() const { return ptr; } |
||||
inline const T* end() const { return ptr + size_; } |
||||
inline const T& front() const { return *ptr; } |
||||
inline const T& back() const { return *(ptr + size_ - 1); } |
||||
inline T* begin() { return ptr; } |
||||
inline T* end() { return ptr + size_; } |
||||
inline T& front() { return *ptr; } |
||||
inline T& back() { return *(ptr + size_ - 1); } |
||||
|
||||
inline ArrayPtr<T> slice(size_t start, size_t end) { |
||||
KJ_IREQUIRE(start <= end && end <= size_, "Out-of-bounds Array::slice()."); |
||||
return ArrayPtr<T>(ptr + start, end - start); |
||||
} |
||||
inline ArrayPtr<const T> slice(size_t start, size_t end) const { |
||||
KJ_IREQUIRE(start <= end && end <= size_, "Out-of-bounds Array::slice()."); |
||||
return ArrayPtr<const T>(ptr + start, end - start); |
||||
} |
||||
|
||||
inline ArrayPtr<const byte> asBytes() const { return asPtr().asBytes(); } |
||||
inline ArrayPtr<PropagateConst<T, byte>> asBytes() { return asPtr().asBytes(); } |
||||
inline ArrayPtr<const char> asChars() const { return asPtr().asChars(); } |
||||
inline ArrayPtr<PropagateConst<T, char>> asChars() { return asPtr().asChars(); } |
||||
|
||||
inline Array<PropagateConst<T, byte>> releaseAsBytes() { |
||||
// Like asBytes() but transfers ownership.
|
||||
static_assert(sizeof(T) == sizeof(byte), |
||||
"releaseAsBytes() only possible on arrays with byte-size elements (e.g. chars)."); |
||||
Array<PropagateConst<T, byte>> result( |
||||
reinterpret_cast<PropagateConst<T, byte>*>(ptr), size_, *disposer); |
||||
ptr = nullptr; |
||||
size_ = 0; |
||||
return result; |
||||
} |
||||
inline Array<PropagateConst<T, char>> releaseAsChars() { |
||||
// Like asChars() but transfers ownership.
|
||||
static_assert(sizeof(T) == sizeof(PropagateConst<T, char>), |
||||
"releaseAsChars() only possible on arrays with char-size elements (e.g. bytes)."); |
||||
Array<PropagateConst<T, char>> result( |
||||
reinterpret_cast<PropagateConst<T, char>*>(ptr), size_, *disposer); |
||||
ptr = nullptr; |
||||
size_ = 0; |
||||
return result; |
||||
} |
||||
|
||||
inline bool operator==(decltype(nullptr)) const { return size_ == 0; } |
||||
inline bool operator!=(decltype(nullptr)) const { return size_ != 0; } |
||||
|
||||
inline Array& operator=(decltype(nullptr)) { |
||||
dispose(); |
||||
return *this; |
||||
} |
||||
|
||||
inline Array& operator=(Array&& other) { |
||||
dispose(); |
||||
ptr = other.ptr; |
||||
size_ = other.size_; |
||||
disposer = other.disposer; |
||||
other.ptr = nullptr; |
||||
other.size_ = 0; |
||||
return *this; |
||||
} |
||||
|
||||
private: |
||||
T* ptr; |
||||
size_t size_; |
||||
const ArrayDisposer* disposer; |
||||
|
||||
inline void dispose() { |
||||
// Make sure that if an exception is thrown, we are left with a null ptr, so we won't possibly
|
||||
// dispose again.
|
||||
T* ptrCopy = ptr; |
||||
size_t sizeCopy = size_; |
||||
if (ptrCopy != nullptr) { |
||||
ptr = nullptr; |
||||
size_ = 0; |
||||
disposer->dispose(ptrCopy, sizeCopy, sizeCopy); |
||||
} |
||||
} |
||||
|
||||
template <typename U> |
||||
friend class Array; |
||||
}; |
||||
|
||||
static_assert(!canMemcpy<Array<char>>(), "canMemcpy<>() is broken"); |
||||
|
||||
namespace _ { // private
|
||||
|
||||
class HeapArrayDisposer final: public ArrayDisposer { |
||||
public: |
||||
template <typename T> |
||||
static T* allocate(size_t count); |
||||
template <typename T> |
||||
static T* allocateUninitialized(size_t count); |
||||
|
||||
static const HeapArrayDisposer instance; |
||||
|
||||
private: |
||||
static void* allocateImpl(size_t elementSize, size_t elementCount, size_t capacity, |
||||
void (*constructElement)(void*), void (*destroyElement)(void*)); |
||||
// Allocates and constructs the array. Both function pointers are null if the constructor is
|
||||
// trivial, otherwise destroyElement is null if the constructor doesn't throw.
|
||||
|
||||
virtual void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount, |
||||
size_t capacity, void (*destroyElement)(void*)) const override; |
||||
|
||||
template <typename T, bool hasTrivialConstructor = __has_trivial_constructor(T), |
||||
bool hasNothrowConstructor = __has_nothrow_constructor(T)> |
||||
struct Allocate_; |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T> |
||||
inline Array<T> heapArray(size_t size) { |
||||
// Much like `heap<T>()` from memory.h, allocates a new array on the heap.
|
||||
|
||||
return Array<T>(_::HeapArrayDisposer::allocate<T>(size), size, |
||||
_::HeapArrayDisposer::instance); |
||||
} |
||||
|
||||
template <typename T> Array<T> heapArray(const T* content, size_t size); |
||||
template <typename T> Array<T> heapArray(ArrayPtr<T> content); |
||||
template <typename T> Array<T> heapArray(ArrayPtr<const T> content); |
||||
template <typename T, typename Iterator> Array<T> heapArray(Iterator begin, Iterator end); |
||||
template <typename T> Array<T> heapArray(std::initializer_list<T> init); |
||||
// Allocate a heap array containing a copy of the given content.
|
||||
|
||||
template <typename T, typename Container> |
||||
Array<T> heapArrayFromIterable(Container&& a) { return heapArray<T>(a.begin(), a.end()); } |
||||
template <typename T> |
||||
Array<T> heapArrayFromIterable(Array<T>&& a) { return mv(a); } |
||||
|
||||
// =======================================================================================
|
||||
// ArrayBuilder
|
||||
|
||||
template <typename T> |
||||
class ArrayBuilder { |
||||
// Class which lets you build an Array<T> specifying the exact constructor arguments for each
|
||||
// element, rather than starting by default-constructing them.
|
||||
|
||||
public: |
||||
ArrayBuilder(): ptr(nullptr), pos(nullptr), endPtr(nullptr) {} |
||||
ArrayBuilder(decltype(nullptr)): ptr(nullptr), pos(nullptr), endPtr(nullptr) {} |
||||
explicit ArrayBuilder(RemoveConst<T>* firstElement, size_t capacity, |
||||
const ArrayDisposer& disposer) |
||||
: ptr(firstElement), pos(firstElement), endPtr(firstElement + capacity), |
||||
disposer(&disposer) {} |
||||
ArrayBuilder(ArrayBuilder&& other) |
||||
: ptr(other.ptr), pos(other.pos), endPtr(other.endPtr), disposer(other.disposer) { |
||||
other.ptr = nullptr; |
||||
other.pos = nullptr; |
||||
other.endPtr = nullptr; |
||||
} |
||||
KJ_DISALLOW_COPY(ArrayBuilder); |
||||
inline ~ArrayBuilder() noexcept(false) { dispose(); } |
||||
|
||||
inline operator ArrayPtr<T>() { |
||||
return arrayPtr(ptr, pos); |
||||
} |
||||
inline operator ArrayPtr<const T>() const { |
||||
return arrayPtr(ptr, pos); |
||||
} |
||||
inline ArrayPtr<T> asPtr() { |
||||
return arrayPtr(ptr, pos); |
||||
} |
||||
inline ArrayPtr<const T> asPtr() const { |
||||
return arrayPtr(ptr, pos); |
||||
} |
||||
|
||||
inline size_t size() const { return pos - ptr; } |
||||
inline size_t capacity() const { return endPtr - ptr; } |
||||
inline T& operator[](size_t index) const { |
||||
KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access."); |
||||
return ptr[index]; |
||||
} |
||||
|
||||
inline const T* begin() const { return ptr; } |
||||
inline const T* end() const { return pos; } |
||||
inline const T& front() const { return *ptr; } |
||||
inline const T& back() const { return *(pos - 1); } |
||||
inline T* begin() { return ptr; } |
||||
inline T* end() { return pos; } |
||||
inline T& front() { return *ptr; } |
||||
inline T& back() { return *(pos - 1); } |
||||
|
||||
ArrayBuilder& operator=(ArrayBuilder&& other) { |
||||
dispose(); |
||||
ptr = other.ptr; |
||||
pos = other.pos; |
||||
endPtr = other.endPtr; |
||||
disposer = other.disposer; |
||||
other.ptr = nullptr; |
||||
other.pos = nullptr; |
||||
other.endPtr = nullptr; |
||||
return *this; |
||||
} |
||||
ArrayBuilder& operator=(decltype(nullptr)) { |
||||
dispose(); |
||||
return *this; |
||||
} |
||||
|
||||
template <typename... Params> |
||||
T& add(Params&&... params) { |
||||
KJ_IREQUIRE(pos < endPtr, "Added too many elements to ArrayBuilder."); |
||||
ctor(*pos, kj::fwd<Params>(params)...); |
||||
return *pos++; |
||||
} |
||||
|
||||
template <typename Container> |
||||
void addAll(Container&& container) { |
||||
addAll<decltype(container.begin()), !isReference<Container>()>( |
||||
container.begin(), container.end()); |
||||
} |
||||
|
||||
template <typename Iterator, bool move = false> |
||||
void addAll(Iterator start, Iterator end); |
||||
|
||||
void removeLast() { |
||||
KJ_IREQUIRE(pos > ptr, "No elements present to remove."); |
||||
kj::dtor(*--pos); |
||||
} |
||||
|
||||
void truncate(size_t size) { |
||||
KJ_IREQUIRE(size <= this->size(), "can't use truncate() to expand"); |
||||
|
||||
T* target = ptr + size; |
||||
if (__has_trivial_destructor(T)) { |
||||
pos = target; |
||||
} else { |
||||
while (pos > target) { |
||||
kj::dtor(*--pos); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void resize(size_t size) { |
||||
KJ_IREQUIRE(size <= capacity(), "can't resize past capacity"); |
||||
|
||||
T* target = ptr + size; |
||||
if (target > pos) { |
||||
// expand
|
||||
if (__has_trivial_constructor(T)) { |
||||
pos = target; |
||||
} else { |
||||
while (pos < target) { |
||||
kj::ctor(*pos++); |
||||
} |
||||
} |
||||
} else { |
||||
// truncate
|
||||
if (__has_trivial_destructor(T)) { |
||||
pos = target; |
||||
} else { |
||||
while (pos > target) { |
||||
kj::dtor(*--pos); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Array<T> finish() { |
||||
// We could safely remove this check if we assume that the disposer implementation doesn't
|
||||
// need to know the original capacity, as is thes case with HeapArrayDisposer since it uses
|
||||
// operator new() or if we created a custom disposer for ArrayBuilder which stores the capacity
|
||||
// in a prefix. But that would make it hard to write cleverer heap allocators, and anyway this
|
||||
// check might catch bugs. Probably people should use Vector if they want to build arrays
|
||||
// without knowing the final size in advance.
|
||||
KJ_IREQUIRE(pos == endPtr, "ArrayBuilder::finish() called prematurely."); |
||||
Array<T> result(reinterpret_cast<T*>(ptr), pos - ptr, *disposer); |
||||
ptr = nullptr; |
||||
pos = nullptr; |
||||
endPtr = nullptr; |
||||
return result; |
||||
} |
||||
|
||||
inline bool isFull() const { |
||||
return pos == endPtr; |
||||
} |
||||
|
||||
private: |
||||
T* ptr; |
||||
RemoveConst<T>* pos; |
||||
T* endPtr; |
||||
const ArrayDisposer* disposer; |
||||
|
||||
inline void dispose() { |
||||
// Make sure that if an exception is thrown, we are left with a null ptr, so we won't possibly
|
||||
// dispose again.
|
||||
T* ptrCopy = ptr; |
||||
T* posCopy = pos; |
||||
T* endCopy = endPtr; |
||||
if (ptrCopy != nullptr) { |
||||
ptr = nullptr; |
||||
pos = nullptr; |
||||
endPtr = nullptr; |
||||
disposer->dispose(ptrCopy, posCopy - ptrCopy, endCopy - ptrCopy); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
inline ArrayBuilder<T> heapArrayBuilder(size_t size) { |
||||
// Like `heapArray<T>()` but does not default-construct the elements. You must construct them
|
||||
// manually by calling `add()`.
|
||||
|
||||
return ArrayBuilder<T>(_::HeapArrayDisposer::allocateUninitialized<RemoveConst<T>>(size), |
||||
size, _::HeapArrayDisposer::instance); |
||||
} |
||||
|
||||
// =======================================================================================
|
||||
// Inline Arrays
|
||||
|
||||
template <typename T, size_t fixedSize> |
||||
class FixedArray { |
||||
// A fixed-width array whose storage is allocated inline rather than on the heap.
|
||||
|
||||
public: |
||||
inline size_t size() const { return fixedSize; } |
||||
inline T* begin() { return content; } |
||||
inline T* end() { return content + fixedSize; } |
||||
inline const T* begin() const { return content; } |
||||
inline const T* end() const { return content + fixedSize; } |
||||
|
||||
inline operator ArrayPtr<T>() { |
||||
return arrayPtr(content, fixedSize); |
||||
} |
||||
inline operator ArrayPtr<const T>() const { |
||||
return arrayPtr(content, fixedSize); |
||||
} |
||||
|
||||
inline T& operator[](size_t index) { return content[index]; } |
||||
inline const T& operator[](size_t index) const { return content[index]; } |
||||
|
||||
private: |
||||
T content[fixedSize]; |
||||
}; |
||||
|
||||
template <typename T, size_t fixedSize> |
||||
class CappedArray { |
||||
// Like `FixedArray` but can be dynamically resized as long as the size does not exceed the limit
|
||||
// specified by the template parameter.
|
||||
//
|
||||
// TODO(someday): Don't construct elements past currentSize?
|
||||
|
||||
public: |
||||
inline KJ_CONSTEXPR() CappedArray(): currentSize(fixedSize) {} |
||||
inline explicit constexpr CappedArray(size_t s): currentSize(s) {} |
||||
|
||||
inline size_t size() const { return currentSize; } |
||||
inline void setSize(size_t s) { KJ_IREQUIRE(s <= fixedSize); currentSize = s; } |
||||
inline T* begin() { return content; } |
||||
inline T* end() { return content + currentSize; } |
||||
inline const T* begin() const { return content; } |
||||
inline const T* end() const { return content + currentSize; } |
||||
|
||||
inline operator ArrayPtr<T>() { |
||||
return arrayPtr(content, currentSize); |
||||
} |
||||
inline operator ArrayPtr<const T>() const { |
||||
return arrayPtr(content, currentSize); |
||||
} |
||||
|
||||
inline T& operator[](size_t index) { return content[index]; } |
||||
inline const T& operator[](size_t index) const { return content[index]; } |
||||
|
||||
private: |
||||
size_t currentSize; |
||||
T content[fixedSize]; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// KJ_MAP
|
||||
|
||||
#define KJ_MAP(elementName, array) \ |
||||
::kj::_::Mapper<KJ_DECLTYPE_REF(array)>(array) * \
|
||||
[&](typename ::kj::_::Mapper<KJ_DECLTYPE_REF(array)>::Element elementName) |
||||
// Applies some function to every element of an array, returning an Array of the results, with
|
||||
// nice syntax. Example:
|
||||
//
|
||||
// StringPtr foo = "abcd";
|
||||
// Array<char> bar = KJ_MAP(c, foo) -> char { return c + 1; };
|
||||
// KJ_ASSERT(str(bar) == "bcde");
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> |
||||
struct Mapper { |
||||
T array; |
||||
Mapper(T&& array): array(kj::fwd<T>(array)) {} |
||||
template <typename Func> |
||||
auto operator*(Func&& func) -> Array<decltype(func(*array.begin()))> { |
||||
auto builder = heapArrayBuilder<decltype(func(*array.begin()))>(array.size()); |
||||
for (auto iter = array.begin(); iter != array.end(); ++iter) { |
||||
builder.add(func(*iter)); |
||||
} |
||||
return builder.finish(); |
||||
} |
||||
typedef decltype(*kj::instance<T>().begin()) Element; |
||||
}; |
||||
|
||||
template <typename T, size_t s> |
||||
struct Mapper<T(&)[s]> { |
||||
T* array; |
||||
Mapper(T* array): array(array) {} |
||||
template <typename Func> |
||||
auto operator*(Func&& func) -> Array<decltype(func(*array))> { |
||||
auto builder = heapArrayBuilder<decltype(func(*array))>(s); |
||||
for (size_t i = 0; i < s; i++) { |
||||
builder.add(func(array[i])); |
||||
} |
||||
return builder.finish(); |
||||
} |
||||
typedef decltype(*array)& Element; |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
template <typename T> |
||||
struct ArrayDisposer::Dispose_<T, true> { |
||||
static void dispose(T* firstElement, size_t elementCount, size_t capacity, |
||||
const ArrayDisposer& disposer) { |
||||
disposer.disposeImpl(const_cast<RemoveConst<T>*>(firstElement), |
||||
sizeof(T), elementCount, capacity, nullptr); |
||||
} |
||||
}; |
||||
template <typename T> |
||||
struct ArrayDisposer::Dispose_<T, false> { |
||||
static void destruct(void* ptr) { |
||||
kj::dtor(*reinterpret_cast<T*>(ptr)); |
||||
} |
||||
|
||||
static void dispose(T* firstElement, size_t elementCount, size_t capacity, |
||||
const ArrayDisposer& disposer) { |
||||
disposer.disposeImpl(firstElement, sizeof(T), elementCount, capacity, &destruct); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
void ArrayDisposer::dispose(T* firstElement, size_t elementCount, size_t capacity) const { |
||||
Dispose_<T>::dispose(firstElement, elementCount, capacity, *this); |
||||
} |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> |
||||
struct HeapArrayDisposer::Allocate_<T, true, true> { |
||||
static T* allocate(size_t elementCount, size_t capacity) { |
||||
return reinterpret_cast<T*>(allocateImpl( |
||||
sizeof(T), elementCount, capacity, nullptr, nullptr)); |
||||
} |
||||
}; |
||||
template <typename T> |
||||
struct HeapArrayDisposer::Allocate_<T, false, true> { |
||||
static void construct(void* ptr) { |
||||
kj::ctor(*reinterpret_cast<T*>(ptr)); |
||||
} |
||||
static T* allocate(size_t elementCount, size_t capacity) { |
||||
return reinterpret_cast<T*>(allocateImpl( |
||||
sizeof(T), elementCount, capacity, &construct, nullptr)); |
||||
} |
||||
}; |
||||
template <typename T> |
||||
struct HeapArrayDisposer::Allocate_<T, false, false> { |
||||
static void construct(void* ptr) { |
||||
kj::ctor(*reinterpret_cast<T*>(ptr)); |
||||
} |
||||
static void destruct(void* ptr) { |
||||
kj::dtor(*reinterpret_cast<T*>(ptr)); |
||||
} |
||||
static T* allocate(size_t elementCount, size_t capacity) { |
||||
return reinterpret_cast<T*>(allocateImpl( |
||||
sizeof(T), elementCount, capacity, &construct, &destruct)); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
T* HeapArrayDisposer::allocate(size_t count) { |
||||
return Allocate_<T>::allocate(count, count); |
||||
} |
||||
|
||||
template <typename T> |
||||
T* HeapArrayDisposer::allocateUninitialized(size_t count) { |
||||
return Allocate_<T, true, true>::allocate(0, count); |
||||
} |
||||
|
||||
template <typename Element, typename Iterator, bool move, bool = canMemcpy<Element>()> |
||||
struct CopyConstructArray_; |
||||
|
||||
template <typename T, bool move> |
||||
struct CopyConstructArray_<T, T*, move, true> { |
||||
static inline T* apply(T* __restrict__ pos, T* start, T* end) { |
||||
memcpy(pos, start, reinterpret_cast<byte*>(end) - reinterpret_cast<byte*>(start)); |
||||
return pos + (end - start); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
struct CopyConstructArray_<T, const T*, false, true> { |
||||
static inline T* apply(T* __restrict__ pos, const T* start, const T* end) { |
||||
memcpy(pos, start, reinterpret_cast<const byte*>(end) - reinterpret_cast<const byte*>(start)); |
||||
return pos + (end - start); |
||||
} |
||||
}; |
||||
|
||||
template <typename T, typename Iterator, bool move> |
||||
struct CopyConstructArray_<T, Iterator, move, true> { |
||||
static inline T* apply(T* __restrict__ pos, Iterator start, Iterator end) { |
||||
// Since both the copy constructor and assignment operator are trivial, we know that assignment
|
||||
// is equivalent to copy-constructing. So we can make this case somewhat easier for the
|
||||
// compiler to optimize.
|
||||
while (start != end) { |
||||
*pos++ = *start++; |
||||
} |
||||
return pos; |
||||
} |
||||
}; |
||||
|
||||
template <typename T, typename Iterator> |
||||
struct CopyConstructArray_<T, Iterator, false, false> { |
||||
struct ExceptionGuard { |
||||
T* start; |
||||
T* pos; |
||||
inline explicit ExceptionGuard(T* pos): start(pos), pos(pos) {} |
||||
~ExceptionGuard() noexcept(false) { |
||||
while (pos > start) { |
||||
dtor(*--pos); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
static T* apply(T* __restrict__ pos, Iterator start, Iterator end) { |
||||
// Verify that T can be *implicitly* constructed from the source values.
|
||||
if (false) implicitCast<T>(*start); |
||||
|
||||
if (noexcept(T(*start))) { |
||||
while (start != end) { |
||||
ctor(*pos++, *start++); |
||||
} |
||||
return pos; |
||||
} else { |
||||
// Crap. This is complicated.
|
||||
ExceptionGuard guard(pos); |
||||
while (start != end) { |
||||
ctor(*guard.pos, *start++); |
||||
++guard.pos; |
||||
} |
||||
guard.start = guard.pos; |
||||
return guard.pos; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
template <typename T, typename Iterator> |
||||
struct CopyConstructArray_<T, Iterator, true, false> { |
||||
// Actually move-construct.
|
||||
|
||||
struct ExceptionGuard { |
||||
T* start; |
||||
T* pos; |
||||
inline explicit ExceptionGuard(T* pos): start(pos), pos(pos) {} |
||||
~ExceptionGuard() noexcept(false) { |
||||
while (pos > start) { |
||||
dtor(*--pos); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
static T* apply(T* __restrict__ pos, Iterator start, Iterator end) { |
||||
// Verify that T can be *implicitly* constructed from the source values.
|
||||
if (false) implicitCast<T>(kj::mv(*start)); |
||||
|
||||
if (noexcept(T(kj::mv(*start)))) { |
||||
while (start != end) { |
||||
ctor(*pos++, kj::mv(*start++)); |
||||
} |
||||
return pos; |
||||
} else { |
||||
// Crap. This is complicated.
|
||||
ExceptionGuard guard(pos); |
||||
while (start != end) { |
||||
ctor(*guard.pos, kj::mv(*start++)); |
||||
++guard.pos; |
||||
} |
||||
guard.start = guard.pos; |
||||
return guard.pos; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T> |
||||
template <typename Iterator, bool move> |
||||
void ArrayBuilder<T>::addAll(Iterator start, Iterator end) { |
||||
pos = _::CopyConstructArray_<RemoveConst<T>, Decay<Iterator>, move>::apply(pos, start, end); |
||||
} |
||||
|
||||
template <typename T> |
||||
Array<T> heapArray(const T* content, size_t size) { |
||||
ArrayBuilder<T> builder = heapArrayBuilder<T>(size); |
||||
builder.addAll(content, content + size); |
||||
return builder.finish(); |
||||
} |
||||
|
||||
template <typename T> |
||||
Array<T> heapArray(T* content, size_t size) { |
||||
ArrayBuilder<T> builder = heapArrayBuilder<T>(size); |
||||
builder.addAll(content, content + size); |
||||
return builder.finish(); |
||||
} |
||||
|
||||
template <typename T> |
||||
Array<T> heapArray(ArrayPtr<T> content) { |
||||
ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size()); |
||||
builder.addAll(content); |
||||
return builder.finish(); |
||||
} |
||||
|
||||
template <typename T> |
||||
Array<T> heapArray(ArrayPtr<const T> content) { |
||||
ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size()); |
||||
builder.addAll(content); |
||||
return builder.finish(); |
||||
} |
||||
|
||||
template <typename T, typename Iterator> Array<T> |
||||
heapArray(Iterator begin, Iterator end) { |
||||
ArrayBuilder<T> builder = heapArrayBuilder<T>(end - begin); |
||||
builder.addAll(begin, end); |
||||
return builder.finish(); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline Array<T> heapArray(std::initializer_list<T> init) { |
||||
return heapArray<T>(init.begin(), init.end()); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ARRAY_H_
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,561 @@ |
||||
// 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 KJ_ASYNC_IO_H_ |
||||
#define KJ_ASYNC_IO_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "async.h" |
||||
#include "function.h" |
||||
#include "thread.h" |
||||
#include "time.h" |
||||
|
||||
struct sockaddr; |
||||
|
||||
namespace kj { |
||||
|
||||
#if _WIN32 |
||||
class Win32EventPort; |
||||
#else |
||||
class UnixEventPort; |
||||
#endif |
||||
|
||||
class NetworkAddress; |
||||
class AsyncOutputStream; |
||||
|
||||
// =======================================================================================
|
||||
// Streaming I/O
|
||||
|
||||
class AsyncInputStream { |
||||
// Asynchronous equivalent of InputStream (from io.h).
|
||||
|
||||
public: |
||||
virtual Promise<size_t> read(void* buffer, size_t minBytes, size_t maxBytes); |
||||
virtual Promise<size_t> tryRead(void* buffer, size_t minBytes, size_t maxBytes) = 0; |
||||
|
||||
Promise<void> read(void* buffer, size_t bytes); |
||||
|
||||
virtual Maybe<uint64_t> tryGetLength(); |
||||
// Get the remaining number of bytes that will be produced by this stream, if known.
|
||||
//
|
||||
// This is used e.g. to fill in the Content-Length header of an HTTP message. If unknown, the
|
||||
// HTTP implementation may need to fall back to Transfer-Encoding: chunked.
|
||||
//
|
||||
// The default implementation always returns null.
|
||||
|
||||
virtual Promise<uint64_t> pumpTo( |
||||
AsyncOutputStream& output, uint64_t amount = kj::maxValue); |
||||
// Read `amount` bytes from this stream (or to EOF) and write them to `output`, returning the
|
||||
// total bytes actually pumped (which is only less than `amount` if EOF was reached).
|
||||
//
|
||||
// Override this if your stream type knows how to pump itself to certain kinds of output
|
||||
// streams more efficiently than via the naive approach. You can use
|
||||
// kj::dynamicDowncastIfAvailable() to test for stream types you recognize, and if none match,
|
||||
// delegate to the default implementation.
|
||||
//
|
||||
// The default implementation first tries calling output.tryPumpFrom(), but if that fails, it
|
||||
// performs a naive pump by allocating a buffer and reading to it / writing from it in a loop.
|
||||
|
||||
Promise<Array<byte>> readAllBytes(); |
||||
Promise<String> readAllText(); |
||||
// Read until EOF and return as one big byte array or string.
|
||||
}; |
||||
|
||||
class AsyncOutputStream { |
||||
// Asynchronous equivalent of OutputStream (from io.h).
|
||||
|
||||
public: |
||||
virtual Promise<void> write(const void* buffer, size_t size) KJ_WARN_UNUSED_RESULT = 0; |
||||
virtual Promise<void> write(ArrayPtr<const ArrayPtr<const byte>> pieces) |
||||
KJ_WARN_UNUSED_RESULT = 0; |
||||
|
||||
virtual Maybe<Promise<uint64_t>> tryPumpFrom( |
||||
AsyncInputStream& input, uint64_t amount = kj::maxValue); |
||||
// Implements double-dispatch for AsyncInputStream::pumpTo().
|
||||
//
|
||||
// This method should only be called from within an implementation of pumpTo().
|
||||
//
|
||||
// This method examines the type of `input` to find optimized ways to pump data from it to this
|
||||
// output stream. If it finds one, it performs the pump. Otherwise, it returns null.
|
||||
//
|
||||
// The default implementation always returns null.
|
||||
}; |
||||
|
||||
class AsyncIoStream: public AsyncInputStream, public AsyncOutputStream { |
||||
// A combination input and output stream.
|
||||
|
||||
public: |
||||
virtual void shutdownWrite() = 0; |
||||
// Cleanly shut down just the write end of the stream, while keeping the read end open.
|
||||
|
||||
virtual void abortRead() {} |
||||
// Similar to shutdownWrite, but this will shut down the read end of the stream, and should only
|
||||
// be called when an error has occurred.
|
||||
|
||||
virtual void getsockopt(int level, int option, void* value, uint* length); |
||||
virtual void setsockopt(int level, int option, const void* value, uint length); |
||||
// Corresponds to getsockopt() and setsockopt() syscalls. Will throw an "unimplemented" exception
|
||||
// if the stream is not a socket or the option is not appropriate for the socket type. The
|
||||
// default implementations always throw "unimplemented".
|
||||
|
||||
virtual void getsockname(struct sockaddr* addr, uint* length); |
||||
virtual void getpeername(struct sockaddr* addr, uint* length); |
||||
// Corresponds to getsockname() and getpeername() syscalls. Will throw an "unimplemented"
|
||||
// exception if the stream is not a socket. The default implementations always throw
|
||||
// "unimplemented".
|
||||
//
|
||||
// Note that we don't provide methods that return NetworkAddress because it usually wouldn't
|
||||
// be useful. You can't connect() to or listen() on these addresses, obviously, because they are
|
||||
// ephemeral addresses for a single connection.
|
||||
}; |
||||
|
||||
struct OneWayPipe { |
||||
// A data pipe with an input end and an output end. (Typically backed by pipe() system call.)
|
||||
|
||||
Own<AsyncInputStream> in; |
||||
Own<AsyncOutputStream> out; |
||||
}; |
||||
|
||||
struct TwoWayPipe { |
||||
// A data pipe that supports sending in both directions. Each end's output sends data to the
|
||||
// other end's input. (Typically backed by socketpair() system call.)
|
||||
|
||||
Own<AsyncIoStream> ends[2]; |
||||
}; |
||||
|
||||
class ConnectionReceiver { |
||||
// Represents a server socket listening on a port.
|
||||
|
||||
public: |
||||
virtual Promise<Own<AsyncIoStream>> accept() = 0; |
||||
// Accept the next incoming connection.
|
||||
|
||||
virtual uint getPort() = 0; |
||||
// Gets the port number, if applicable (i.e. if listening on IP). This is useful if you didn't
|
||||
// specify a port when constructing the NetworkAddress -- one will have been assigned
|
||||
// automatically.
|
||||
|
||||
virtual void getsockopt(int level, int option, void* value, uint* length); |
||||
virtual void setsockopt(int level, int option, const void* value, uint length); |
||||
// Same as the methods of AsyncIoStream.
|
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Datagram I/O
|
||||
|
||||
class AncillaryMessage { |
||||
// Represents an ancillary message (aka control message) received using the recvmsg() system
|
||||
// call (or equivalent). Most apps will not use this.
|
||||
|
||||
public: |
||||
inline AncillaryMessage(int level, int type, ArrayPtr<const byte> data); |
||||
AncillaryMessage() = default; |
||||
|
||||
inline int getLevel() const; |
||||
// Originating protocol / socket level.
|
||||
|
||||
inline int getType() const; |
||||
// Protocol-specific message type.
|
||||
|
||||
template <typename T> |
||||
inline Maybe<const T&> as(); |
||||
// Interpret the ancillary message as the given struct type. Most ancillary messages are some
|
||||
// sort of struct, so this is a convenient way to access it. Returns nullptr if the message
|
||||
// is smaller than the struct -- this can happen if the message was truncated due to
|
||||
// insufficient ancillary buffer space.
|
||||
|
||||
template <typename T> |
||||
inline ArrayPtr<const T> asArray(); |
||||
// Interpret the ancillary message as an array of items. If the message size does not evenly
|
||||
// divide into elements of type T, the remainder is discarded -- this can happen if the message
|
||||
// was truncated due to insufficient ancillary buffer space.
|
||||
|
||||
private: |
||||
int level; |
||||
int type; |
||||
ArrayPtr<const byte> data; |
||||
// Message data. In most cases you should use `as()` or `asArray()`.
|
||||
}; |
||||
|
||||
class DatagramReceiver { |
||||
// Class encapsulating the recvmsg() system call. You must specify the DatagramReceiver's
|
||||
// capacity in advance; if a received packet is larger than the capacity, it will be truncated.
|
||||
|
||||
public: |
||||
virtual Promise<void> receive() = 0; |
||||
// Receive a new message, overwriting this object's content.
|
||||
//
|
||||
// receive() may reuse the same buffers for content and ancillary data with each call.
|
||||
|
||||
template <typename T> |
||||
struct MaybeTruncated { |
||||
T value; |
||||
|
||||
bool isTruncated; |
||||
// True if the Receiver's capacity was insufficient to receive the value and therefore the
|
||||
// value is truncated.
|
||||
}; |
||||
|
||||
virtual MaybeTruncated<ArrayPtr<const byte>> getContent() = 0; |
||||
// Get the content of the datagram.
|
||||
|
||||
virtual MaybeTruncated<ArrayPtr<const AncillaryMessage>> getAncillary() = 0; |
||||
// Ancilarry messages received with the datagram. See the recvmsg() system call and the cmsghdr
|
||||
// struct. Most apps don't need this.
|
||||
//
|
||||
// If the returned value is truncated, then the last message in the array may itself be
|
||||
// truncated, meaning its as<T>() method will return nullptr or its asArray<T>() method will
|
||||
// return fewer elements than expected. Truncation can also mean that additional messages were
|
||||
// available but discarded.
|
||||
|
||||
virtual NetworkAddress& getSource() = 0; |
||||
// Get the datagram sender's address.
|
||||
|
||||
struct Capacity { |
||||
size_t content = 8192; |
||||
// How much space to allocate for the datagram content. If a datagram is received that is
|
||||
// larger than this, it will be truncated, with no way to recover the tail.
|
||||
|
||||
size_t ancillary = 0; |
||||
// How much space to allocate for ancillary messages. As with content, if the ancillary data
|
||||
// is larger than this, it will be truncated.
|
||||
}; |
||||
}; |
||||
|
||||
class DatagramPort { |
||||
public: |
||||
virtual Promise<size_t> send(const void* buffer, size_t size, NetworkAddress& destination) = 0; |
||||
virtual Promise<size_t> send(ArrayPtr<const ArrayPtr<const byte>> pieces, |
||||
NetworkAddress& destination) = 0; |
||||
|
||||
virtual Own<DatagramReceiver> makeReceiver( |
||||
DatagramReceiver::Capacity capacity = DatagramReceiver::Capacity()) = 0; |
||||
// Create a new `Receiver` that can be used to receive datagrams. `capacity` specifies how much
|
||||
// space to allocate for the received message. The `DatagramPort` must outlive the `Receiver`.
|
||||
|
||||
virtual uint getPort() = 0; |
||||
// Gets the port number, if applicable (i.e. if listening on IP). This is useful if you didn't
|
||||
// specify a port when constructing the NetworkAddress -- one will have been assigned
|
||||
// automatically.
|
||||
|
||||
virtual void getsockopt(int level, int option, void* value, uint* length); |
||||
virtual void setsockopt(int level, int option, const void* value, uint length); |
||||
// Same as the methods of AsyncIoStream.
|
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Networks
|
||||
|
||||
class NetworkAddress { |
||||
// Represents a remote address to which the application can connect.
|
||||
|
||||
public: |
||||
virtual Promise<Own<AsyncIoStream>> connect() = 0; |
||||
// Make a new connection to this address.
|
||||
//
|
||||
// The address must not be a wildcard ("*"). If it is an IP address, it must have a port number.
|
||||
|
||||
virtual Own<ConnectionReceiver> listen() = 0; |
||||
// Listen for incoming connections on this address.
|
||||
//
|
||||
// The address must be local.
|
||||
|
||||
virtual Own<DatagramPort> bindDatagramPort(); |
||||
// Open this address as a datagram (e.g. UDP) port.
|
||||
//
|
||||
// The address must be local.
|
||||
|
||||
virtual Own<NetworkAddress> clone() = 0; |
||||
// Returns an equivalent copy of this NetworkAddress.
|
||||
|
||||
virtual String toString() = 0; |
||||
// Produce a human-readable string which hopefully can be passed to Network::parseAddress()
|
||||
// to reproduce this address, although whether or not that works of course depends on the Network
|
||||
// implementation. This should be called only to display the address to human users, who will
|
||||
// hopefully know what they are able to do with it.
|
||||
}; |
||||
|
||||
class Network { |
||||
// Factory for NetworkAddress instances, representing the network services offered by the
|
||||
// operating system.
|
||||
//
|
||||
// This interface typically represents broad authority, and well-designed code should limit its
|
||||
// use to high-level startup code and user interaction. Low-level APIs should accept
|
||||
// NetworkAddress instances directly and work from there, if at all possible.
|
||||
|
||||
public: |
||||
virtual Promise<Own<NetworkAddress>> parseAddress(StringPtr addr, uint portHint = 0) = 0; |
||||
// Construct a network address from a user-provided string. The format of the address
|
||||
// strings is not specified at the API level, and application code should make no assumptions
|
||||
// about them. These strings should always be provided by humans, and said humans will know
|
||||
// what format to use in their particular context.
|
||||
//
|
||||
// `portHint`, if provided, specifies the "standard" IP port number for the application-level
|
||||
// service in play. If the address turns out to be an IP address (v4 or v6), and it lacks a
|
||||
// port number, this port will be used. If `addr` lacks a port number *and* `portHint` is
|
||||
// omitted, then the returned address will only support listen() and bindDatagramPort()
|
||||
// (not connect()), and an unused port will be chosen each time one of those methods is called.
|
||||
|
||||
virtual Own<NetworkAddress> getSockaddr(const void* sockaddr, uint len) = 0; |
||||
// Construct a network address from a legacy struct sockaddr.
|
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// I/O Provider
|
||||
|
||||
class AsyncIoProvider { |
||||
// Class which constructs asynchronous wrappers around the operating system's I/O facilities.
|
||||
//
|
||||
// Generally, the implementation of this interface must integrate closely with a particular
|
||||
// `EventLoop` implementation. Typically, the EventLoop implementation itself will provide
|
||||
// an AsyncIoProvider.
|
||||
|
||||
public: |
||||
virtual OneWayPipe newOneWayPipe() = 0; |
||||
// Creates an input/output stream pair representing the ends of a one-way pipe (e.g. created with
|
||||
// the pipe(2) system call).
|
||||
|
||||
virtual TwoWayPipe newTwoWayPipe() = 0; |
||||
// Creates two AsyncIoStreams representing the two ends of a two-way pipe (e.g. created with
|
||||
// socketpair(2) system call). Data written to one end can be read from the other.
|
||||
|
||||
virtual Network& getNetwork() = 0; |
||||
// Creates a new `Network` instance representing the networks exposed by the operating system.
|
||||
//
|
||||
// DO NOT CALL THIS except at the highest levels of your code, ideally in the main() function. If
|
||||
// you call this from low-level code, then you are preventing higher-level code from injecting an
|
||||
// alternative implementation. Instead, if your code needs to use network functionality, it
|
||||
// should ask for a `Network` as a constructor or method parameter, so that higher-level code can
|
||||
// chose what implementation to use. The system network is essentially a singleton. See:
|
||||
// http://www.object-oriented-security.org/lets-argue/singletons
|
||||
//
|
||||
// Code that uses the system network should not make any assumptions about what kinds of
|
||||
// addresses it will parse, as this could differ across platforms. String addresses should come
|
||||
// strictly from the user, who will know how to write them correctly for their system.
|
||||
//
|
||||
// With that said, KJ currently supports the following string address formats:
|
||||
// - IPv4: "1.2.3.4", "1.2.3.4:80"
|
||||
// - IPv6: "1234:5678::abcd", "[1234:5678::abcd]:80"
|
||||
// - Local IP wildcard (covers both v4 and v6): "*", "*:80"
|
||||
// - Symbolic names: "example.com", "example.com:80", "example.com:http", "1.2.3.4:http"
|
||||
// - Unix domain: "unix:/path/to/socket"
|
||||
|
||||
struct PipeThread { |
||||
// A combination of a thread and a two-way pipe that communicates with that thread.
|
||||
//
|
||||
// The fields are intentionally ordered so that the pipe will be destroyed (and therefore
|
||||
// disconnected) before the thread is destroyed (and therefore joined). Thus if the thread
|
||||
// arranges to exit when it detects disconnect, destruction should be clean.
|
||||
|
||||
Own<Thread> thread; |
||||
Own<AsyncIoStream> pipe; |
||||
}; |
||||
|
||||
virtual PipeThread newPipeThread( |
||||
Function<void(AsyncIoProvider&, AsyncIoStream&, WaitScope&)> startFunc) = 0; |
||||
// Create a new thread and set up a two-way pipe (socketpair) which can be used to communicate
|
||||
// with it. One end of the pipe is passed to the thread's start function and the other end of
|
||||
// the pipe is returned. The new thread also gets its own `AsyncIoProvider` instance and will
|
||||
// already have an active `EventLoop` when `startFunc` is called.
|
||||
//
|
||||
// TODO(someday): I'm not entirely comfortable with this interface. It seems to be doing too
|
||||
// much at once but I'm not sure how to cleanly break it down.
|
||||
|
||||
virtual Timer& getTimer() = 0; |
||||
// Returns a `Timer` based on real time. Time does not pass while event handlers are running --
|
||||
// it only updates when the event loop polls for system events. This means that calling `now()`
|
||||
// on this timer does not require a system call.
|
||||
//
|
||||
// This timer is not affected by changes to the system date. It is unspecified whether the timer
|
||||
// continues to count while the system is suspended.
|
||||
}; |
||||
|
||||
class LowLevelAsyncIoProvider { |
||||
// Similar to `AsyncIoProvider`, but represents a lower-level interface that may differ on
|
||||
// different operating systems. You should prefer to use `AsyncIoProvider` over this interface
|
||||
// whenever possible, as `AsyncIoProvider` is portable and friendlier to dependency-injection.
|
||||
//
|
||||
// On Unix, this interface can be used to import native file descriptors into the async framework.
|
||||
// Different implementations of this interface might work on top of different event handling
|
||||
// primitives, such as poll vs. epoll vs. kqueue vs. some higher-level event library.
|
||||
//
|
||||
// On Windows, this interface can be used to import native HANDLEs into the async framework.
|
||||
// Different implementations of this interface might work on top of different event handling
|
||||
// primitives, such as I/O completion ports vs. completion routines.
|
||||
//
|
||||
// TODO(port): Actually implement Windows support.
|
||||
|
||||
public: |
||||
// ---------------------------------------------------------------------------
|
||||
// Unix-specific stuff
|
||||
|
||||
enum Flags { |
||||
// Flags controlling how to wrap a file descriptor.
|
||||
|
||||
TAKE_OWNERSHIP = 1 << 0, |
||||
// The returned object should own the file descriptor, automatically closing it when destroyed.
|
||||
// The close-on-exec flag will be set on the descriptor if it is not already.
|
||||
//
|
||||
// If this flag is not used, then the file descriptor is not automatically closed and the
|
||||
// close-on-exec flag is not modified.
|
||||
|
||||
#if !_WIN32 |
||||
ALREADY_CLOEXEC = 1 << 1, |
||||
// Indicates that the close-on-exec flag is known already to be set, so need not be set again.
|
||||
// Only relevant when combined with TAKE_OWNERSHIP.
|
||||
//
|
||||
// On Linux, all system calls which yield new file descriptors have flags or variants which
|
||||
// set the close-on-exec flag immediately. Unfortunately, other OS's do not.
|
||||
|
||||
ALREADY_NONBLOCK = 1 << 2 |
||||
// Indicates that the file descriptor is known already to be in non-blocking mode, so the flag
|
||||
// need not be set again. Otherwise, all wrap*Fd() methods will enable non-blocking mode
|
||||
// automatically.
|
||||
//
|
||||
// On Linux, all system calls which yield new file descriptors have flags or variants which
|
||||
// enable non-blocking mode immediately. Unfortunately, other OS's do not.
|
||||
#endif |
||||
}; |
||||
|
||||
#if _WIN32 |
||||
typedef uintptr_t Fd; |
||||
// On Windows, the `fd` parameter to each of these methods must be a SOCKET, and must have the
|
||||
// flag WSA_FLAG_OVERLAPPED (which socket() uses by default, but WSASocket() wants you to specify
|
||||
// explicitly).
|
||||
#else |
||||
typedef int Fd; |
||||
// On Unix, any arbitrary file descriptor is supported.
|
||||
#endif |
||||
|
||||
virtual Own<AsyncInputStream> wrapInputFd(Fd fd, uint flags = 0) = 0; |
||||
// Create an AsyncInputStream wrapping a file descriptor.
|
||||
//
|
||||
// `flags` is a bitwise-OR of the values of the `Flags` enum.
|
||||
|
||||
virtual Own<AsyncOutputStream> wrapOutputFd(Fd fd, uint flags = 0) = 0; |
||||
// Create an AsyncOutputStream wrapping a file descriptor.
|
||||
//
|
||||
// `flags` is a bitwise-OR of the values of the `Flags` enum.
|
||||
|
||||
virtual Own<AsyncIoStream> wrapSocketFd(Fd fd, uint flags = 0) = 0; |
||||
// Create an AsyncIoStream wrapping a socket file descriptor.
|
||||
//
|
||||
// `flags` is a bitwise-OR of the values of the `Flags` enum.
|
||||
|
||||
virtual Promise<Own<AsyncIoStream>> wrapConnectingSocketFd( |
||||
Fd fd, const struct sockaddr* addr, uint addrlen, uint flags = 0) = 0; |
||||
// Create an AsyncIoStream wrapping a socket and initiate a connection to the given address.
|
||||
// The returned promise does not resolve until connection has completed.
|
||||
//
|
||||
// `flags` is a bitwise-OR of the values of the `Flags` enum.
|
||||
|
||||
virtual Own<ConnectionReceiver> wrapListenSocketFd(Fd fd, uint flags = 0) = 0; |
||||
// Create an AsyncIoStream wrapping a listen socket file descriptor. This socket should already
|
||||
// have had `bind()` and `listen()` called on it, so it's ready for `accept()`.
|
||||
//
|
||||
// `flags` is a bitwise-OR of the values of the `Flags` enum.
|
||||
|
||||
virtual Own<DatagramPort> wrapDatagramSocketFd(Fd fd, uint flags = 0); |
||||
|
||||
virtual Timer& getTimer() = 0; |
||||
// Returns a `Timer` based on real time. Time does not pass while event handlers are running --
|
||||
// it only updates when the event loop polls for system events. This means that calling `now()`
|
||||
// on this timer does not require a system call.
|
||||
//
|
||||
// This timer is not affected by changes to the system date. It is unspecified whether the timer
|
||||
// continues to count while the system is suspended.
|
||||
}; |
||||
|
||||
Own<AsyncIoProvider> newAsyncIoProvider(LowLevelAsyncIoProvider& lowLevel); |
||||
// Make a new AsyncIoProvider wrapping a `LowLevelAsyncIoProvider`.
|
||||
|
||||
struct AsyncIoContext { |
||||
Own<LowLevelAsyncIoProvider> lowLevelProvider; |
||||
Own<AsyncIoProvider> provider; |
||||
WaitScope& waitScope; |
||||
|
||||
#if _WIN32 |
||||
Win32EventPort& win32EventPort; |
||||
#else |
||||
UnixEventPort& unixEventPort; |
||||
// TEMPORARY: Direct access to underlying UnixEventPort, mainly for waiting on signals. This
|
||||
// field will go away at some point when we have a chance to improve these interfaces.
|
||||
#endif |
||||
}; |
||||
|
||||
AsyncIoContext setupAsyncIo(); |
||||
// Convenience method which sets up the current thread with everything it needs to do async I/O.
|
||||
// The returned objects contain an `EventLoop` which is wrapping an appropriate `EventPort` for
|
||||
// doing I/O on the host system, so everything is ready for the thread to start making async calls
|
||||
// and waiting on promises.
|
||||
//
|
||||
// You would typically call this in your main() loop or in the start function of a thread.
|
||||
// Example:
|
||||
//
|
||||
// int main() {
|
||||
// auto ioContext = kj::setupAsyncIo();
|
||||
//
|
||||
// // Now we can call an async function.
|
||||
// Promise<String> textPromise = getHttp(*ioContext.provider, "http://example.com");
|
||||
//
|
||||
// // And we can wait for the promise to complete. Note that you can only use `wait()`
|
||||
// // from the top level, not from inside a promise callback.
|
||||
// String text = textPromise.wait(ioContext.waitScope);
|
||||
// print(text);
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// WARNING: An AsyncIoContext can only be used in the thread and process that created it. In
|
||||
// particular, note that after a fork(), an AsyncIoContext created in the parent process will
|
||||
// not work correctly in the child, even if the parent ceases to use its copy. In particular
|
||||
// note that this means that server processes which daemonize themselves at startup must wait
|
||||
// until after daemonization to create an AsyncIoContext.
|
||||
|
||||
// =======================================================================================
|
||||
// inline implementation details
|
||||
|
||||
inline AncillaryMessage::AncillaryMessage( |
||||
int level, int type, ArrayPtr<const byte> data) |
||||
: level(level), type(type), data(data) {} |
||||
|
||||
inline int AncillaryMessage::getLevel() const { return level; } |
||||
inline int AncillaryMessage::getType() const { return type; } |
||||
|
||||
template <typename T> |
||||
inline Maybe<const T&> AncillaryMessage::as() { |
||||
if (data.size() >= sizeof(T)) { |
||||
return *reinterpret_cast<const T*>(data.begin()); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
inline ArrayPtr<const T> AncillaryMessage::asArray() { |
||||
return arrayPtr(reinterpret_cast<const T*>(data.begin()), data.size() / sizeof(T)); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ASYNC_IO_H_
|
@ -0,0 +1,218 @@ |
||||
// 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 async.h can start.
|
||||
// We don't define these directly in async.h because it makes the file hard to read.
|
||||
|
||||
#ifndef KJ_ASYNC_PRELUDE_H_ |
||||
#define KJ_ASYNC_PRELUDE_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "exception.h" |
||||
#include "tuple.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class EventLoop; |
||||
template <typename T> |
||||
class Promise; |
||||
class WaitScope; |
||||
|
||||
template <typename T> |
||||
Promise<Array<T>> joinPromises(Array<Promise<T>>&& promises); |
||||
Promise<void> joinPromises(Array<Promise<void>>&& promises); |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> struct JoinPromises_ { typedef T Type; }; |
||||
template <typename T> struct JoinPromises_<Promise<T>> { typedef T Type; }; |
||||
|
||||
template <typename T> |
||||
using JoinPromises = typename JoinPromises_<T>::Type; |
||||
// If T is Promise<U>, resolves to U, otherwise resolves to T.
|
||||
//
|
||||
// TODO(cleanup): Rename to avoid confusion with joinPromises() call which is completely
|
||||
// unrelated.
|
||||
|
||||
class PropagateException { |
||||
// A functor which accepts a kj::Exception as a parameter and returns a broken promise of
|
||||
// arbitrary type which simply propagates the exception.
|
||||
public: |
||||
class Bottom { |
||||
public: |
||||
Bottom(Exception&& exception): exception(kj::mv(exception)) {} |
||||
|
||||
Exception asException() { return kj::mv(exception); } |
||||
|
||||
private: |
||||
Exception exception; |
||||
}; |
||||
|
||||
Bottom operator()(Exception&& e) { |
||||
return Bottom(kj::mv(e)); |
||||
} |
||||
Bottom operator()(const Exception& e) { |
||||
return Bottom(kj::cp(e)); |
||||
} |
||||
}; |
||||
|
||||
template <typename Func, typename T> |
||||
struct ReturnType_ { typedef decltype(instance<Func>()(instance<T>())) Type; }; |
||||
template <typename Func> |
||||
struct ReturnType_<Func, void> { typedef decltype(instance<Func>()()) Type; }; |
||||
|
||||
template <typename Func, typename T> |
||||
using ReturnType = typename ReturnType_<Func, T>::Type; |
||||
// The return type of functor Func given a parameter of type T, with the special exception that if
|
||||
// T is void, this is the return type of Func called with no arguments.
|
||||
|
||||
template <typename T> struct SplitTuplePromise_ { typedef Promise<T> Type; }; |
||||
template <typename... T> |
||||
struct SplitTuplePromise_<kj::_::Tuple<T...>> { |
||||
typedef kj::Tuple<Promise<JoinPromises<T>>...> Type; |
||||
}; |
||||
|
||||
template <typename T> |
||||
using SplitTuplePromise = typename SplitTuplePromise_<T>::Type; |
||||
// T -> Promise<T>
|
||||
// Tuple<T> -> Tuple<Promise<T>>
|
||||
|
||||
struct Void {}; |
||||
// Application code should NOT refer to this! See `kj::READY_NOW` instead.
|
||||
|
||||
template <typename T> struct FixVoid_ { typedef T Type; }; |
||||
template <> struct FixVoid_<void> { typedef Void Type; }; |
||||
template <typename T> using FixVoid = typename FixVoid_<T>::Type; |
||||
// FixVoid<T> is just T unless T is void in which case it is _::Void (an empty struct).
|
||||
|
||||
template <typename T> struct UnfixVoid_ { typedef T Type; }; |
||||
template <> struct UnfixVoid_<Void> { typedef void Type; }; |
||||
template <typename T> using UnfixVoid = typename UnfixVoid_<T>::Type; |
||||
// UnfixVoid is the opposite of FixVoid.
|
||||
|
||||
template <typename In, typename Out> |
||||
struct MaybeVoidCaller { |
||||
// Calls the function converting a Void input to an empty parameter list and a void return
|
||||
// value to a Void output.
|
||||
|
||||
template <typename Func> |
||||
static inline Out apply(Func& func, In&& in) { |
||||
return func(kj::mv(in)); |
||||
} |
||||
}; |
||||
template <typename In, typename Out> |
||||
struct MaybeVoidCaller<In&, Out> { |
||||
template <typename Func> |
||||
static inline Out apply(Func& func, In& in) { |
||||
return func(in); |
||||
} |
||||
}; |
||||
template <typename Out> |
||||
struct MaybeVoidCaller<Void, Out> { |
||||
template <typename Func> |
||||
static inline Out apply(Func& func, Void&& in) { |
||||
return func(); |
||||
} |
||||
}; |
||||
template <typename In> |
||||
struct MaybeVoidCaller<In, Void> { |
||||
template <typename Func> |
||||
static inline Void apply(Func& func, In&& in) { |
||||
func(kj::mv(in)); |
||||
return Void(); |
||||
} |
||||
}; |
||||
template <typename In> |
||||
struct MaybeVoidCaller<In&, Void> { |
||||
template <typename Func> |
||||
static inline Void apply(Func& func, In& in) { |
||||
func(in); |
||||
return Void(); |
||||
} |
||||
}; |
||||
template <> |
||||
struct MaybeVoidCaller<Void, Void> { |
||||
template <typename Func> |
||||
static inline Void apply(Func& func, Void&& in) { |
||||
func(); |
||||
return Void(); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
inline T&& returnMaybeVoid(T&& t) { |
||||
return kj::fwd<T>(t); |
||||
} |
||||
inline void returnMaybeVoid(Void&& v) {} |
||||
|
||||
class ExceptionOrValue; |
||||
class PromiseNode; |
||||
class ChainPromiseNode; |
||||
template <typename T> |
||||
class ForkHub; |
||||
|
||||
class TaskSetImpl; |
||||
|
||||
class Event; |
||||
|
||||
class PromiseBase { |
||||
public: |
||||
kj::String trace(); |
||||
// Dump debug info about this promise.
|
||||
|
||||
private: |
||||
Own<PromiseNode> node; |
||||
|
||||
PromiseBase() = default; |
||||
PromiseBase(Own<PromiseNode>&& node): node(kj::mv(node)) {} |
||||
|
||||
friend class kj::EventLoop; |
||||
friend class ChainPromiseNode; |
||||
template <typename> |
||||
friend class kj::Promise; |
||||
friend class TaskSetImpl; |
||||
template <typename U> |
||||
friend Promise<Array<U>> kj::joinPromises(Array<Promise<U>>&& promises); |
||||
friend Promise<void> kj::joinPromises(Array<Promise<void>>&& promises); |
||||
}; |
||||
|
||||
void detach(kj::Promise<void>&& promise); |
||||
void waitImpl(Own<_::PromiseNode>&& node, _::ExceptionOrValue& result, WaitScope& waitScope); |
||||
Promise<void> yield(); |
||||
Own<PromiseNode> neverDone(); |
||||
|
||||
class NeverDone { |
||||
public: |
||||
template <typename T> |
||||
operator Promise<T>() const { |
||||
return Promise<T>(false, neverDone()); |
||||
} |
||||
|
||||
KJ_NORETURN(void wait(WaitScope& waitScope) const); |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ASYNC_PRELUDE_H_
|
@ -0,0 +1,274 @@ |
||||
// 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 KJ_ASYNC_UNIX_H_ |
||||
#define KJ_ASYNC_UNIX_H_ |
||||
|
||||
#if _WIN32 |
||||
#error "This file is Unix-specific. On Windows, include async-win32.h instead." |
||||
#endif |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "async.h" |
||||
#include "time.h" |
||||
#include "vector.h" |
||||
#include "io.h" |
||||
#include <signal.h> |
||||
|
||||
#if __linux__ && !__BIONIC__ && !defined(KJ_USE_EPOLL) |
||||
// Default to epoll on Linux, except on Bionic (Android) which doesn't have signalfd.h.
|
||||
#define KJ_USE_EPOLL 1 |
||||
#endif |
||||
|
||||
namespace kj { |
||||
|
||||
class UnixEventPort: public EventPort { |
||||
// An EventPort implementation which can wait for events on file descriptors as well as signals.
|
||||
// This API only makes sense on Unix.
|
||||
//
|
||||
// The implementation uses `poll()` or possibly a platform-specific API (e.g. epoll, kqueue).
|
||||
// To also wait on signals without race conditions, the implementation may block signals until
|
||||
// just before `poll()` while using a signal handler which `siglongjmp()`s back to just before
|
||||
// the signal was unblocked, or it may use a nicer platform-specific API like signalfd.
|
||||
//
|
||||
// The implementation reserves a signal for internal use. By default, it uses SIGUSR1. If you
|
||||
// need to use SIGUSR1 for something else, you must offer a different signal by calling
|
||||
// setReservedSignal() at startup.
|
||||
//
|
||||
// WARNING: A UnixEventPort can only be used in the thread and process that created it. In
|
||||
// particular, note that after a fork(), a UnixEventPort created in the parent process will
|
||||
// not work correctly in the child, even if the parent ceases to use its copy. In particular
|
||||
// note that this means that server processes which daemonize themselves at startup must wait
|
||||
// until after daemonization to create a UnixEventPort.
|
||||
|
||||
public: |
||||
UnixEventPort(); |
||||
~UnixEventPort() noexcept(false); |
||||
|
||||
class FdObserver; |
||||
// Class that watches an fd for readability or writability. See definition below.
|
||||
|
||||
Promise<siginfo_t> onSignal(int signum); |
||||
// When the given signal is delivered to this thread, return the corresponding siginfo_t.
|
||||
// The signal must have been captured using `captureSignal()`.
|
||||
//
|
||||
// If `onSignal()` has not been called, the signal will remain blocked in this thread.
|
||||
// Therefore, a signal which arrives before `onSignal()` was called will not be "missed" -- the
|
||||
// next call to 'onSignal()' will receive it. Also, you can control which thread receives a
|
||||
// process-wide signal by only calling `onSignal()` on that thread's event loop.
|
||||
//
|
||||
// The result of waiting on the same signal twice at once is undefined.
|
||||
|
||||
static void captureSignal(int signum); |
||||
// Arranges for the given signal to be captured and handled via UnixEventPort, so that you may
|
||||
// then pass it to `onSignal()`. This method is static because it registers a signal handler
|
||||
// which applies process-wide. If any other threads exist in the process when `captureSignal()`
|
||||
// is called, you *must* set the signal mask in those threads to block this signal, otherwise
|
||||
// terrible things will happen if the signal happens to be delivered to those threads. If at
|
||||
// all possible, call `captureSignal()` *before* creating threads, so that threads you create in
|
||||
// the future will inherit the proper signal mask.
|
||||
//
|
||||
// To un-capture a signal, simply install a different signal handler and then un-block it from
|
||||
// the signal mask.
|
||||
|
||||
static void setReservedSignal(int signum); |
||||
// Sets the signal number which `UnixEventPort` reserves for internal use. If your application
|
||||
// needs to use SIGUSR1, call this at startup (before any calls to `captureSignal()` and before
|
||||
// constructing an `UnixEventPort`) to offer a different signal.
|
||||
|
||||
Timer& getTimer() { return timerImpl; } |
||||
|
||||
// implements EventPort ------------------------------------------------------
|
||||
bool wait() override; |
||||
bool poll() override; |
||||
void wake() const override; |
||||
|
||||
private: |
||||
struct TimerSet; // Defined in source file to avoid STL include.
|
||||
class TimerPromiseAdapter; |
||||
class SignalPromiseAdapter; |
||||
|
||||
TimerImpl timerImpl; |
||||
|
||||
SignalPromiseAdapter* signalHead = nullptr; |
||||
SignalPromiseAdapter** signalTail = &signalHead; |
||||
|
||||
TimePoint readClock(); |
||||
void gotSignal(const siginfo_t& siginfo); |
||||
|
||||
friend class TimerPromiseAdapter; |
||||
|
||||
#if KJ_USE_EPOLL |
||||
AutoCloseFd epollFd; |
||||
AutoCloseFd signalFd; |
||||
AutoCloseFd eventFd; // Used for cross-thread wakeups.
|
||||
|
||||
sigset_t signalFdSigset; |
||||
// Signal mask as currently set on the signalFd. Tracked so we can detect whether or not it
|
||||
// needs updating.
|
||||
|
||||
bool doEpollWait(int timeout); |
||||
|
||||
#else |
||||
class PollContext; |
||||
|
||||
FdObserver* observersHead = nullptr; |
||||
FdObserver** observersTail = &observersHead; |
||||
|
||||
unsigned long long threadId; // actually pthread_t
|
||||
#endif |
||||
}; |
||||
|
||||
class UnixEventPort::FdObserver { |
||||
// Object which watches a file descriptor to determine when it is readable or writable.
|
||||
//
|
||||
// For listen sockets, "readable" means that there is a connection to accept(). For everything
|
||||
// else, it means that read() (or recv()) will return data.
|
||||
//
|
||||
// The presence of out-of-band data should NOT fire this event. However, the event may
|
||||
// occasionally fire spuriously (when there is actually no data to read), and one thing that can
|
||||
// cause such spurious events is the arrival of OOB data on certain platforms whose event
|
||||
// interfaces fail to distinguish between regular and OOB data (e.g. Mac OSX).
|
||||
//
|
||||
// WARNING: The exact behavior of this class differs across systems, since event interfaces
|
||||
// vary wildly. Be sure to read the documentation carefully and avoid depending on unspecified
|
||||
// behavior. If at all possible, use the higher-level AsyncInputStream interface instead.
|
||||
|
||||
public: |
||||
enum Flags { |
||||
OBSERVE_READ = 1, |
||||
OBSERVE_WRITE = 2, |
||||
OBSERVE_URGENT = 4, |
||||
OBSERVE_READ_WRITE = OBSERVE_READ | OBSERVE_WRITE |
||||
}; |
||||
|
||||
FdObserver(UnixEventPort& eventPort, int fd, uint flags); |
||||
// Begin watching the given file descriptor for readability. Only one ReadObserver may exist
|
||||
// for a given file descriptor at a time.
|
||||
|
||||
~FdObserver() noexcept(false); |
||||
|
||||
KJ_DISALLOW_COPY(FdObserver); |
||||
|
||||
Promise<void> whenBecomesReadable(); |
||||
// Resolves the next time the file descriptor transitions from having no data to read to having
|
||||
// some data to read.
|
||||
//
|
||||
// KJ uses "edge-triggered" event notification whenever possible. As a result, it is an error
|
||||
// to call this method when there is already data in the read buffer which has been there since
|
||||
// prior to the last turn of the event loop or prior to creation FdWatcher. In this case, it is
|
||||
// unspecified whether the promise will ever resolve -- it depends on the underlying event
|
||||
// mechanism being used.
|
||||
//
|
||||
// In order to avoid this problem, make sure that you only call `whenBecomesReadable()`
|
||||
// only at times when you know the buffer is empty. You know this for sure when one of the
|
||||
// following happens:
|
||||
// * read() or recv() fails with EAGAIN or EWOULDBLOCK. (You MUST have non-blocking mode
|
||||
// enabled on the fd!)
|
||||
// * The file descriptor is a regular byte-oriented object (like a socket or pipe),
|
||||
// read() or recv() returns fewer than the number of bytes requested, and `atEndHint()`
|
||||
// returns false. This can only happen if the buffer is empty but EOF is not reached. (Note,
|
||||
// though, that for record-oriented file descriptors like Linux's inotify interface, this
|
||||
// rule does not hold, because it could simply be that the next record did not fit into the
|
||||
// space available.)
|
||||
//
|
||||
// It is an error to call `whenBecomesReadable()` again when the promise returned previously
|
||||
// has not yet resolved. If you do this, the previous promise may throw an exception.
|
||||
|
||||
inline Maybe<bool> atEndHint() { return atEnd; } |
||||
// Returns true if the event system has indicated that EOF has been received. There may still
|
||||
// be data in the read buffer, but once that is gone, there's nothing left.
|
||||
//
|
||||
// Returns false if the event system has indicated that EOF had NOT been received as of the
|
||||
// last turn of the event loop.
|
||||
//
|
||||
// Returns nullptr if the event system does not know whether EOF has been reached. In this
|
||||
// case, the only way to know for sure is to call read() or recv() and check if it returns
|
||||
// zero.
|
||||
//
|
||||
// This hint may be useful as an optimization to avoid an unnecessary system call.
|
||||
|
||||
Promise<void> whenBecomesWritable(); |
||||
// Resolves the next time the file descriptor transitions from having no space available in the
|
||||
// write buffer to having some space available.
|
||||
//
|
||||
// KJ uses "edge-triggered" event notification whenever possible. As a result, it is an error
|
||||
// to call this method when there is already space in the write buffer which has been there
|
||||
// since prior to the last turn of the event loop or prior to creation FdWatcher. In this case,
|
||||
// it is unspecified whether the promise will ever resolve -- it depends on the underlying
|
||||
// event mechanism being used.
|
||||
//
|
||||
// In order to avoid this problem, make sure that you only call `whenBecomesWritable()`
|
||||
// only at times when you know the buffer is full. You know this for sure when one of the
|
||||
// following happens:
|
||||
// * write() or send() fails with EAGAIN or EWOULDBLOCK. (You MUST have non-blocking mode
|
||||
// enabled on the fd!)
|
||||
// * write() or send() succeeds but accepts fewer than the number of bytes provided. This can
|
||||
// only happen if the buffer is full.
|
||||
//
|
||||
// It is an error to call `whenBecomesWritable()` again when the promise returned previously
|
||||
// has not yet resolved. If you do this, the previous promise may throw an exception.
|
||||
|
||||
Promise<void> whenUrgentDataAvailable(); |
||||
// Resolves the next time the file descriptor's read buffer contains "urgent" data.
|
||||
//
|
||||
// The conditions for availability of urgent data are specific to the file descriptor's
|
||||
// underlying implementation.
|
||||
//
|
||||
// It is an error to call `whenUrgentDataAvailable()` again when the promise returned previously
|
||||
// has not yet resolved. If you do this, the previous promise may throw an exception.
|
||||
//
|
||||
// WARNING: This has some known weird behavior on macOS. See
|
||||
// https://github.com/sandstorm-io/capnproto/issues/374.
|
||||
|
||||
private: |
||||
UnixEventPort& eventPort; |
||||
int fd; |
||||
uint flags; |
||||
|
||||
kj::Maybe<Own<PromiseFulfiller<void>>> readFulfiller; |
||||
kj::Maybe<Own<PromiseFulfiller<void>>> writeFulfiller; |
||||
kj::Maybe<Own<PromiseFulfiller<void>>> urgentFulfiller; |
||||
// Replaced each time `whenBecomesReadable()` or `whenBecomesWritable()` is called. Reverted to
|
||||
// null every time an event is fired.
|
||||
|
||||
Maybe<bool> atEnd; |
||||
|
||||
void fire(short events); |
||||
|
||||
#if !KJ_USE_EPOLL |
||||
FdObserver* next; |
||||
FdObserver** prev; |
||||
// Linked list of observers which currently have a non-null readFulfiller or writeFulfiller.
|
||||
// If `prev` is null then the observer is not currently in the list.
|
||||
|
||||
short getEventMask(); |
||||
#endif |
||||
|
||||
friend class UnixEventPort; |
||||
}; |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ASYNC_UNIX_H_
|
@ -0,0 +1,234 @@ |
||||
// Copyright (c) 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.
|
||||
|
||||
#ifndef KJ_ASYNC_WIN32_H_ |
||||
#define KJ_ASYNC_WIN32_H_ |
||||
|
||||
#if !_WIN32 |
||||
#error "This file is Windows-specific. On Unix, include async-unix.h instead." |
||||
#endif |
||||
|
||||
#include "async.h" |
||||
#include "time.h" |
||||
#include "io.h" |
||||
#include <atomic> |
||||
#include <inttypes.h> |
||||
|
||||
// Include windows.h as lean as possible. (If you need more of the Windows API for your app,
|
||||
// #include windows.h yourself before including this header.)
|
||||
#define WIN32_LEAN_AND_MEAN 1 |
||||
#define NOSERVICE 1 |
||||
#define NOMCX 1 |
||||
#define NOIME 1 |
||||
#include <windows.h> |
||||
#include "windows-sanity.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class Win32EventPort: public EventPort { |
||||
// Abstract base interface for EventPorts that can listen on Win32 event types. Due to the
|
||||
// absurd complexity of the Win32 API, it's not possible to standardize on a single
|
||||
// implementation of EventPort. In particular, there is no way for a single thread to use I/O
|
||||
// completion ports (the most efficient way of handling I/O) while at the same time waiting for
|
||||
// signalable handles or UI messages.
|
||||
//
|
||||
// Note that UI messages are not supported at all by this interface because the message queue
|
||||
// is implemented by user32.dll and we want libkj to depend only on kernel32.dll. A separate
|
||||
// compat library could provide a Win32EventPort implementation that works with the UI message
|
||||
// queue.
|
||||
|
||||
public: |
||||
// ---------------------------------------------------------------------------
|
||||
// overlapped I/O
|
||||
|
||||
struct IoResult { |
||||
DWORD errorCode; |
||||
DWORD bytesTransferred; |
||||
}; |
||||
|
||||
class IoOperation { |
||||
public: |
||||
virtual LPOVERLAPPED getOverlapped() = 0; |
||||
// Gets the OVERLAPPED structure to pass to the Win32 I/O call. Do NOT modify it; just pass it
|
||||
// on.
|
||||
|
||||
virtual Promise<IoResult> onComplete() = 0; |
||||
// After making the Win32 call, if the return value indicates that the operation was
|
||||
// successfully queued (i.e. the completion event will definitely occur), call this to wait
|
||||
// for completion.
|
||||
//
|
||||
// You MUST call this if the operation was successfully queued, and you MUST NOT call this
|
||||
// otherwise. If the Win32 call failed (without queuing any operation or event) then you should
|
||||
// simply drop the IoOperation object.
|
||||
//
|
||||
// Dropping the returned Promise cancels the operation via Win32's CancelIoEx(). The destructor
|
||||
// will wait for the cancellation to complete, such that after dropping the proimse it is safe
|
||||
// to free the buffer that the operation was reading from / writing to.
|
||||
//
|
||||
// You may safely drop the `IoOperation` while still waiting for this promise. You may not,
|
||||
// however, drop the `IoObserver`.
|
||||
}; |
||||
|
||||
class IoObserver { |
||||
public: |
||||
virtual Own<IoOperation> newOperation(uint64_t offset) = 0; |
||||
// Begin an I/O operation. For file operations, `offset` is the offset within the file at
|
||||
// which the operation will start. For stream operations, `offset` is ignored.
|
||||
}; |
||||
|
||||
virtual Own<IoObserver> observeIo(HANDLE handle) = 0; |
||||
// Given a handle which supports overlapped I/O, arrange to receive I/O completion events via
|
||||
// this EventPort.
|
||||
//
|
||||
// Different Win32EventPort implementations may handle this in different ways, such as by using
|
||||
// completion routines (APCs) or by using I/O completion ports. The caller should not assume
|
||||
// any particular technique.
|
||||
//
|
||||
// WARNING: It is only safe to call observeIo() on a particular handle once during its lifetime.
|
||||
// You cannot observe the same handle from multiple Win32EventPorts, even if not at the same
|
||||
// time. This is because the Win32 API provides no way to disassociate a handle from an I/O
|
||||
// completion port once it is associated.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signalable handles
|
||||
//
|
||||
// Warning: Due to limitations in the Win32 API, implementations of EventPort may be forced to
|
||||
// spawn additional threads to wait for signaled objects. This is necessary if the EventPort
|
||||
// implementation is based on I/O completion ports, or if you need to wait on more than 64
|
||||
// handles at once.
|
||||
|
||||
class SignalObserver { |
||||
public: |
||||
virtual Promise<void> onSignaled() = 0; |
||||
// Returns a promise that completes the next time the handle enters the signaled state.
|
||||
//
|
||||
// Depending on the type of handle, the handle may automatically be reset to a non-signaled
|
||||
// state before the promise resolves. The underlying implementaiton uses WaitForSingleObject()
|
||||
// or an equivalent wait call, so check the documentation for that to understand the semantics.
|
||||
//
|
||||
// If the handle is a mutex and it is abandoned without being unlocked, the promise breaks with
|
||||
// an exception.
|
||||
|
||||
virtual Promise<bool> onSignaledOrAbandoned() = 0; |
||||
// Like onSingaled(), but instead of throwing when a mutex is abandoned, resolves to `true`.
|
||||
// Resolves to `false` for non-abandoned signals.
|
||||
}; |
||||
|
||||
virtual Own<SignalObserver> observeSignalState(HANDLE handle) = 0; |
||||
// Given a handle that supports waiting for it to become "signaled" via WaitForSingleObject(),
|
||||
// return an object that can wait for this state using the EventPort.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// APCs
|
||||
|
||||
virtual void allowApc() = 0; |
||||
// If this is ever called, the Win32EventPort will switch modes so that APCs can be scheduled
|
||||
// on the thread, e.g. through the Win32 QueueUserAPC() call. In the future, this may be enabled
|
||||
// by default. However, as of this writing, Wine does not support the necessary
|
||||
// GetQueuedCompletionStatusEx() call, thus allowApc() breaks Wine support. (Tested on Wine
|
||||
// 1.8.7.)
|
||||
//
|
||||
// If the event port implementation can't support APCs for some reason, this throws.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// time
|
||||
|
||||
virtual Timer& getTimer() = 0; |
||||
}; |
||||
|
||||
class Win32WaitObjectThreadPool { |
||||
// Helper class that implements Win32EventPort::observeSignalState() by spawning additional
|
||||
// threads as needed to perform the actual waiting.
|
||||
//
|
||||
// This class is intended to be used to assist in building Win32EventPort implementations.
|
||||
|
||||
public: |
||||
Win32WaitObjectThreadPool(uint mainThreadCount = 0); |
||||
// `mainThreadCount` indicates the number of objects the main thread is able to listen on
|
||||
// directly. Typically this would be zero (e.g. if the main thread watches an I/O completion
|
||||
// port) or MAXIMUM_WAIT_OBJECTS (e.g. if the main thread is a UI thread but can use
|
||||
// MsgWaitForMultipleObjectsEx() to wait on some handles at the same time as messages).
|
||||
|
||||
Own<Win32EventPort::SignalObserver> observeSignalState(HANDLE handle); |
||||
// Implemetns Win32EventPort::observeSignalState().
|
||||
|
||||
uint prepareMainThreadWait(HANDLE* handles[]); |
||||
// Call immediately before invoking WaitForMultipleObjects() or similar in the main thread.
|
||||
// Fills in `handles` with the handle pointers to wait on, and returns the number of handles
|
||||
// in this array. (The array should be allocated to be at least the size passed to the
|
||||
// constructor).
|
||||
//
|
||||
// There's no need to call this if `mainThreadCount` as passed to the constructor was zero.
|
||||
|
||||
bool finishedMainThreadWait(DWORD returnCode); |
||||
// Call immediately after invoking WaitForMultipleObjects() or similar in the main thread,
|
||||
// passing the value returend by that call. Returns true if the event indicated by `returnCode`
|
||||
// has been handled (i.e. it was WAIT_OBJECT_n or WAIT_ABANDONED_n where n is in-range for the
|
||||
// last call to prepareMainThreadWait()).
|
||||
}; |
||||
|
||||
class Win32IocpEventPort final: public Win32EventPort { |
||||
// An EventPort implementation which uses Windows I/O completion ports to listen for events.
|
||||
//
|
||||
// With this implementation, observeSignalState() requires spawning a separate thread.
|
||||
|
||||
public: |
||||
Win32IocpEventPort(); |
||||
~Win32IocpEventPort() noexcept(false); |
||||
|
||||
// implements EventPort ------------------------------------------------------
|
||||
bool wait() override; |
||||
bool poll() override; |
||||
void wake() const override; |
||||
|
||||
// implements Win32IocpEventPort ---------------------------------------------
|
||||
Own<IoObserver> observeIo(HANDLE handle) override; |
||||
Own<SignalObserver> observeSignalState(HANDLE handle) override; |
||||
Timer& getTimer() override { return timerImpl; } |
||||
void allowApc() override { isAllowApc = true; } |
||||
|
||||
private: |
||||
class IoPromiseAdapter; |
||||
class IoOperationImpl; |
||||
class IoObserverImpl; |
||||
|
||||
AutoCloseHandle iocp; |
||||
AutoCloseHandle thread; |
||||
Win32WaitObjectThreadPool waitThreads; |
||||
TimerImpl timerImpl; |
||||
mutable std::atomic<bool> sentWake {false}; |
||||
bool isAllowApc = false; |
||||
|
||||
static TimePoint readClock(); |
||||
|
||||
void waitIocp(DWORD timeoutMs); |
||||
// Wait on the I/O completion port for up to timeoutMs and pump events. Does not advance the
|
||||
// timer; caller must do that.
|
||||
|
||||
bool receivedWake(); |
||||
|
||||
static AutoCloseHandle newIocpHandle(); |
||||
static AutoCloseHandle openCurrentThread(); |
||||
}; |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ASYNC_WIN32_H_
|
@ -0,0 +1,682 @@ |
||||
// 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 KJ_ASYNC_H_ |
||||
#define KJ_ASYNC_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "async-prelude.h" |
||||
#include "exception.h" |
||||
#include "refcount.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class EventLoop; |
||||
class WaitScope; |
||||
|
||||
template <typename T> |
||||
class Promise; |
||||
template <typename T> |
||||
class ForkedPromise; |
||||
template <typename T> |
||||
class PromiseFulfiller; |
||||
template <typename T> |
||||
struct PromiseFulfillerPair; |
||||
|
||||
template <typename Func, typename T> |
||||
using PromiseForResult = Promise<_::JoinPromises<_::ReturnType<Func, T>>>; |
||||
// Evaluates to the type of Promise for the result of calling functor type Func with parameter type
|
||||
// T. If T is void, then the promise is for the result of calling Func with no arguments. If
|
||||
// Func itself returns a promise, the promises are joined, so you never get Promise<Promise<T>>.
|
||||
|
||||
// =======================================================================================
|
||||
// Promises
|
||||
|
||||
template <typename T> |
||||
class Promise: protected _::PromiseBase { |
||||
// The basic primitive of asynchronous computation in KJ. Similar to "futures", but designed
|
||||
// specifically for event loop concurrency. Similar to E promises and JavaScript Promises/A.
|
||||
//
|
||||
// A Promise represents a promise to produce a value of type T some time in the future. Once
|
||||
// that value has been produced, the promise is "fulfilled". Alternatively, a promise can be
|
||||
// "broken", with an Exception describing what went wrong. You may implicitly convert a value of
|
||||
// type T to an already-fulfilled Promise<T>. You may implicitly convert the constant
|
||||
// `kj::READY_NOW` to an already-fulfilled Promise<void>. You may also implicitly convert a
|
||||
// `kj::Exception` to an already-broken promise of any type.
|
||||
//
|
||||
// Promises are linear types -- they are moveable but not copyable. If a Promise is destroyed
|
||||
// or goes out of scope (without being moved elsewhere), any ongoing asynchronous operations
|
||||
// meant to fulfill the promise will be canceled if possible. All methods of `Promise` (unless
|
||||
// otherwise noted) actually consume the promise in the sense of move semantics. (Arguably they
|
||||
// should be rvalue-qualified, but at the time this interface was created compilers didn't widely
|
||||
// support that yet and anyway it would be pretty ugly typing kj::mv(promise).whatever().) If
|
||||
// you want to use one Promise in two different places, you must fork it with `fork()`.
|
||||
//
|
||||
// To use the result of a Promise, you must call `then()` and supply a callback function to
|
||||
// call with the result. `then()` returns another promise, for the result of the callback.
|
||||
// Any time that this would result in Promise<Promise<T>>, the promises are collapsed into a
|
||||
// simple Promise<T> that first waits for the outer promise, then the inner. Example:
|
||||
//
|
||||
// // Open a remote file, read the content, and then count the
|
||||
// // number of lines of text.
|
||||
// // Note that none of the calls here block. `file`, `content`
|
||||
// // and `lineCount` are all initialized immediately before any
|
||||
// // asynchronous operations occur. The lambda callbacks are
|
||||
// // called later.
|
||||
// Promise<Own<File>> file = openFtp("ftp://host/foo/bar");
|
||||
// Promise<String> content = file.then(
|
||||
// [](Own<File> file) -> Promise<String> {
|
||||
// return file.readAll();
|
||||
// });
|
||||
// Promise<int> lineCount = content.then(
|
||||
// [](String text) -> int {
|
||||
// uint count = 0;
|
||||
// for (char c: text) count += (c == '\n');
|
||||
// return count;
|
||||
// });
|
||||
//
|
||||
// For `then()` to work, the current thread must have an active `EventLoop`. Each callback
|
||||
// is scheduled to execute in that loop. Since `then()` schedules callbacks only on the current
|
||||
// thread's event loop, you do not need to worry about two callbacks running at the same time.
|
||||
// You will need to set up at least one `EventLoop` at the top level of your program before you
|
||||
// can use promises.
|
||||
//
|
||||
// To adapt a non-Promise-based asynchronous API to promises, use `newAdaptedPromise()`.
|
||||
//
|
||||
// Systems using promises should consider supporting the concept of "pipelining". Pipelining
|
||||
// means allowing a caller to start issuing method calls against a promised object before the
|
||||
// promise has actually been fulfilled. This is particularly useful if the promise is for a
|
||||
// remote object living across a network, as this can avoid round trips when chaining a series
|
||||
// of calls. It is suggested that any class T which supports pipelining implement a subclass of
|
||||
// Promise<T> which adds "eventual send" methods -- methods which, when called, say "please
|
||||
// invoke the corresponding method on the promised value once it is available". These methods
|
||||
// should in turn return promises for the eventual results of said invocations. Cap'n Proto,
|
||||
// for example, implements the type `RemotePromise` which supports pipelining RPC requests -- see
|
||||
// `capnp/capability.h`.
|
||||
//
|
||||
// KJ Promises are based on E promises:
|
||||
// http://wiki.erights.org/wiki/Walnut/Distributed_Computing#Promises
|
||||
//
|
||||
// KJ Promises are also inspired in part by the evolving standards for JavaScript/ECMAScript
|
||||
// promises, which are themselves influenced by E promises:
|
||||
// http://promisesaplus.com/
|
||||
// https://github.com/domenic/promises-unwrapping
|
||||
|
||||
public: |
||||
Promise(_::FixVoid<T> value); |
||||
// Construct an already-fulfilled Promise from a value of type T. For non-void promises, the
|
||||
// parameter type is simply T. So, e.g., in a function that returns `Promise<int>`, you can
|
||||
// say `return 123;` to return a promise that is already fulfilled to 123.
|
||||
//
|
||||
// For void promises, use `kj::READY_NOW` as the value, e.g. `return kj::READY_NOW`.
|
||||
|
||||
Promise(kj::Exception&& e); |
||||
// Construct an already-broken Promise.
|
||||
|
||||
inline Promise(decltype(nullptr)) {} |
||||
|
||||
template <typename Func, typename ErrorFunc = _::PropagateException> |
||||
PromiseForResult<Func, T> then(Func&& func, ErrorFunc&& errorHandler = _::PropagateException()) |
||||
KJ_WARN_UNUSED_RESULT; |
||||
// Register a continuation function to be executed when the promise completes. The continuation
|
||||
// (`func`) takes the promised value (an rvalue of type `T`) as its parameter. The continuation
|
||||
// may return a new value; `then()` itself returns a promise for the continuation's eventual
|
||||
// result. If the continuation itself returns a `Promise<U>`, then `then()` shall also return
|
||||
// a `Promise<U>` which first waits for the original promise, then executes the continuation,
|
||||
// then waits for the inner promise (i.e. it automatically "unwraps" the promise).
|
||||
//
|
||||
// In all cases, `then()` returns immediately. The continuation is executed later. The
|
||||
// continuation is always executed on the same EventLoop (and, therefore, the same thread) which
|
||||
// called `then()`, therefore no synchronization is necessary on state shared by the continuation
|
||||
// and the surrounding scope. If no EventLoop is running on the current thread, `then()` throws
|
||||
// an exception.
|
||||
//
|
||||
// You may also specify an error handler continuation as the second parameter. `errorHandler`
|
||||
// must be a functor taking a parameter of type `kj::Exception&&`. It must return the same
|
||||
// type as `func` returns (except when `func` returns `Promise<U>`, in which case `errorHandler`
|
||||
// may return either `Promise<U>` or just `U`). The default error handler simply propagates the
|
||||
// exception to the returned promise.
|
||||
//
|
||||
// Either `func` or `errorHandler` may, of course, throw an exception, in which case the promise
|
||||
// is broken. When compiled with -fno-exceptions, the framework will still detect when a
|
||||
// recoverable exception was thrown inside of a continuation and will consider the promise
|
||||
// broken even though a (presumably garbage) result was returned.
|
||||
//
|
||||
// If the returned promise is destroyed before the callback runs, the callback will be canceled
|
||||
// (it will never run).
|
||||
//
|
||||
// Note that `then()` -- like all other Promise methods -- consumes the promise on which it is
|
||||
// called, in the sense of move semantics. After returning, the original promise is no longer
|
||||
// valid, but `then()` returns a new promise.
|
||||
//
|
||||
// *Advanced implementation tips:* Most users will never need to worry about the below, but
|
||||
// it is good to be aware of.
|
||||
//
|
||||
// As an optimization, if the callback function `func` does _not_ return another promise, then
|
||||
// execution of `func` itself may be delayed until its result is known to be needed. The
|
||||
// expectation here is that `func` is just doing some transformation on the results, not
|
||||
// scheduling any other actions, therefore the system doesn't need to be proactive about
|
||||
// evaluating it. This way, a chain of trivial then() transformations can be executed all at
|
||||
// once without repeatedly re-scheduling through the event loop. Use the `eagerlyEvaluate()`
|
||||
// method to suppress this behavior.
|
||||
//
|
||||
// On the other hand, if `func` _does_ return another promise, then the system evaluates `func`
|
||||
// as soon as possible, because the promise it returns might be for a newly-scheduled
|
||||
// long-running asynchronous task.
|
||||
//
|
||||
// As another optimization, when a callback function registered with `then()` is actually
|
||||
// scheduled, it is scheduled to occur immediately, preempting other work in the event queue.
|
||||
// This allows a long chain of `then`s to execute all at once, improving cache locality by
|
||||
// clustering operations on the same data. However, this implies that starvation can occur
|
||||
// if a chain of `then()`s takes a very long time to execute without ever stopping to wait for
|
||||
// actual I/O. To solve this, use `kj::evalLater()` to yield control; this way, all other events
|
||||
// in the queue will get a chance to run before your callback is executed.
|
||||
|
||||
Promise<void> ignoreResult() KJ_WARN_UNUSED_RESULT { return then([](T&&) {}); } |
||||
// Convenience method to convert the promise to a void promise by ignoring the return value.
|
||||
//
|
||||
// You must still wait on the returned promise if you want the task to execute.
|
||||
|
||||
template <typename ErrorFunc> |
||||
Promise<T> catch_(ErrorFunc&& errorHandler) KJ_WARN_UNUSED_RESULT; |
||||
// Equivalent to `.then(identityFunc, errorHandler)`, where `identifyFunc` is a function that
|
||||
// just returns its input.
|
||||
|
||||
T wait(WaitScope& waitScope); |
||||
// Run the event loop until the promise is fulfilled, then return its result. If the promise
|
||||
// is rejected, throw an exception.
|
||||
//
|
||||
// wait() is primarily useful at the top level of a program -- typically, within the function
|
||||
// that allocated the EventLoop. For example, a program that performs one or two RPCs and then
|
||||
// exits would likely use wait() in its main() function to wait on each RPC. On the other hand,
|
||||
// server-side code generally cannot use wait(), because it has to be able to accept multiple
|
||||
// requests at once.
|
||||
//
|
||||
// If the promise is rejected, `wait()` throws an exception. If the program was compiled without
|
||||
// exceptions (-fno-exceptions), this will usually abort. In this case you really should first
|
||||
// use `then()` to set an appropriate handler for the exception case, so that the promise you
|
||||
// actually wait on never throws.
|
||||
//
|
||||
// `waitScope` is an object proving that the caller is in a scope where wait() is allowed. By
|
||||
// convention, any function which might call wait(), or which might call another function which
|
||||
// might call wait(), must take `WaitScope&` as one of its parameters. This is needed for two
|
||||
// reasons:
|
||||
// * `wait()` is not allowed during an event callback, because event callbacks are themselves
|
||||
// called during some other `wait()`, and such recursive `wait()`s would only be able to
|
||||
// complete in LIFO order, which might mean that the outer `wait()` ends up waiting longer
|
||||
// than it is supposed to. To prevent this, a `WaitScope` cannot be constructed or used during
|
||||
// an event callback.
|
||||
// * Since `wait()` runs the event loop, unrelated event callbacks may execute before `wait()`
|
||||
// returns. This means that anyone calling `wait()` must be reentrant -- state may change
|
||||
// around them in arbitrary ways. Therefore, callers really need to know if a function they
|
||||
// are calling might wait(), and the `WaitScope&` parameter makes this clear.
|
||||
//
|
||||
// TODO(someday): Implement fibers, and let them call wait() even when they are handling an
|
||||
// event.
|
||||
|
||||
ForkedPromise<T> fork() KJ_WARN_UNUSED_RESULT; |
||||
// Forks the promise, so that multiple different clients can independently wait on the result.
|
||||
// `T` must be copy-constructable for this to work. Or, in the special case where `T` is
|
||||
// `Own<U>`, `U` must have a method `Own<U> addRef()` which returns a new reference to the same
|
||||
// (or an equivalent) object (probably implemented via reference counting).
|
||||
|
||||
_::SplitTuplePromise<T> split(); |
||||
// Split a promise for a tuple into a tuple of promises.
|
||||
//
|
||||
// E.g. if you have `Promise<kj::Tuple<T, U>>`, `split()` returns
|
||||
// `kj::Tuple<Promise<T>, Promise<U>>`.
|
||||
|
||||
Promise<T> exclusiveJoin(Promise<T>&& other) KJ_WARN_UNUSED_RESULT; |
||||
// Return a new promise that resolves when either the original promise resolves or `other`
|
||||
// resolves (whichever comes first). The promise that didn't resolve first is canceled.
|
||||
|
||||
// TODO(someday): inclusiveJoin(), or perhaps just join(), which waits for both completions
|
||||
// and produces a tuple?
|
||||
|
||||
template <typename... Attachments> |
||||
Promise<T> attach(Attachments&&... attachments) KJ_WARN_UNUSED_RESULT; |
||||
// "Attaches" one or more movable objects (often, Own<T>s) to the promise, such that they will
|
||||
// be destroyed when the promise resolves. This is useful when a promise's callback contains
|
||||
// pointers into some object and you want to make sure the object still exists when the callback
|
||||
// runs -- after calling then(), use attach() to add necessary objects to the result.
|
||||
|
||||
template <typename ErrorFunc> |
||||
Promise<T> eagerlyEvaluate(ErrorFunc&& errorHandler) KJ_WARN_UNUSED_RESULT; |
||||
Promise<T> eagerlyEvaluate(decltype(nullptr)) KJ_WARN_UNUSED_RESULT; |
||||
// Force eager evaluation of this promise. Use this if you are going to hold on to the promise
|
||||
// for awhile without consuming the result, but you want to make sure that the system actually
|
||||
// processes it.
|
||||
//
|
||||
// `errorHandler` is a function that takes `kj::Exception&&`, like the second parameter to
|
||||
// `then()`, except that it must return void. We make you specify this because otherwise it's
|
||||
// easy to forget to handle errors in a promise that you never use. You may specify nullptr for
|
||||
// the error handler if you are sure that ignoring errors is fine, or if you know that you'll
|
||||
// eventually wait on the promise somewhere.
|
||||
|
||||
template <typename ErrorFunc> |
||||
void detach(ErrorFunc&& errorHandler); |
||||
// Allows the promise to continue running in the background until it completes or the
|
||||
// `EventLoop` is destroyed. Be careful when using this: since you can no longer cancel this
|
||||
// promise, you need to make sure that the promise owns all the objects it touches or make sure
|
||||
// those objects outlive the EventLoop.
|
||||
//
|
||||
// `errorHandler` is a function that takes `kj::Exception&&`, like the second parameter to
|
||||
// `then()`, except that it must return void.
|
||||
//
|
||||
// This function exists mainly to implement the Cap'n Proto requirement that RPC calls cannot be
|
||||
// canceled unless the callee explicitly permits it.
|
||||
|
||||
kj::String trace(); |
||||
// Returns a dump of debug info about this promise. Not for production use. Requires RTTI.
|
||||
// This method does NOT consume the promise as other methods do.
|
||||
|
||||
private: |
||||
Promise(bool, Own<_::PromiseNode>&& node): PromiseBase(kj::mv(node)) {} |
||||
// Second parameter prevent ambiguity with immediate-value constructor.
|
||||
|
||||
template <typename> |
||||
friend class Promise; |
||||
friend class EventLoop; |
||||
template <typename U, typename Adapter, typename... Params> |
||||
friend Promise<U> newAdaptedPromise(Params&&... adapterConstructorParams); |
||||
template <typename U> |
||||
friend PromiseFulfillerPair<U> newPromiseAndFulfiller(); |
||||
template <typename> |
||||
friend class _::ForkHub; |
||||
friend class _::TaskSetImpl; |
||||
friend Promise<void> _::yield(); |
||||
friend class _::NeverDone; |
||||
template <typename U> |
||||
friend Promise<Array<U>> joinPromises(Array<Promise<U>>&& promises); |
||||
friend Promise<void> joinPromises(Array<Promise<void>>&& promises); |
||||
}; |
||||
|
||||
template <typename T> |
||||
class ForkedPromise { |
||||
// The result of `Promise::fork()` and `EventLoop::fork()`. Allows branches to be created.
|
||||
// Like `Promise<T>`, this is a pass-by-move type.
|
||||
|
||||
public: |
||||
inline ForkedPromise(decltype(nullptr)) {} |
||||
|
||||
Promise<T> addBranch(); |
||||
// Add a new branch to the fork. The branch is equivalent to the original promise.
|
||||
|
||||
private: |
||||
Own<_::ForkHub<_::FixVoid<T>>> hub; |
||||
|
||||
inline ForkedPromise(bool, Own<_::ForkHub<_::FixVoid<T>>>&& hub): hub(kj::mv(hub)) {} |
||||
|
||||
friend class Promise<T>; |
||||
friend class EventLoop; |
||||
}; |
||||
|
||||
constexpr _::Void READY_NOW = _::Void(); |
||||
// Use this when you need a Promise<void> that is already fulfilled -- this value can be implicitly
|
||||
// cast to `Promise<void>`.
|
||||
|
||||
constexpr _::NeverDone NEVER_DONE = _::NeverDone(); |
||||
// The opposite of `READY_NOW`, return this when the promise should never resolve. This can be
|
||||
// implicitly converted to any promise type. You may also call `NEVER_DONE.wait()` to wait
|
||||
// forever (useful for servers).
|
||||
|
||||
template <typename Func> |
||||
PromiseForResult<Func, void> evalLater(Func&& func) KJ_WARN_UNUSED_RESULT; |
||||
// Schedule for the given zero-parameter function to be executed in the event loop at some
|
||||
// point in the near future. Returns a Promise for its result -- or, if `func()` itself returns
|
||||
// a promise, `evalLater()` returns a Promise for the result of resolving that promise.
|
||||
//
|
||||
// Example usage:
|
||||
// Promise<int> x = evalLater([]() { return 123; });
|
||||
//
|
||||
// The above is exactly equivalent to:
|
||||
// Promise<int> x = Promise<void>(READY_NOW).then([]() { return 123; });
|
||||
//
|
||||
// If the returned promise is destroyed before the callback runs, the callback will be canceled
|
||||
// (never called).
|
||||
//
|
||||
// If you schedule several evaluations with `evalLater` during the same callback, they are
|
||||
// guaranteed to be executed in order.
|
||||
|
||||
template <typename Func> |
||||
PromiseForResult<Func, void> evalNow(Func&& func) KJ_WARN_UNUSED_RESULT; |
||||
// Run `func()` and return a promise for its result. `func()` executes before `evalNow()` returns.
|
||||
// If `func()` throws an exception, the exception is caught and wrapped in a promise -- this is the
|
||||
// main reason why `evalNow()` is useful.
|
||||
|
||||
template <typename T> |
||||
Promise<Array<T>> joinPromises(Array<Promise<T>>&& promises); |
||||
// Join an array of promises into a promise for an array.
|
||||
|
||||
// =======================================================================================
|
||||
// Hack for creating a lambda that holds an owned pointer.
|
||||
|
||||
template <typename Func, typename MovedParam> |
||||
class CaptureByMove { |
||||
public: |
||||
inline CaptureByMove(Func&& func, MovedParam&& param) |
||||
: func(kj::mv(func)), param(kj::mv(param)) {} |
||||
|
||||
template <typename... Params> |
||||
inline auto operator()(Params&&... params) |
||||
-> decltype(kj::instance<Func>()(kj::instance<MovedParam&&>(), kj::fwd<Params>(params)...)) { |
||||
return func(kj::mv(param), kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
private: |
||||
Func func; |
||||
MovedParam param; |
||||
}; |
||||
|
||||
template <typename Func, typename MovedParam> |
||||
inline CaptureByMove<Func, Decay<MovedParam>> mvCapture(MovedParam&& param, Func&& func) { |
||||
// Hack to create a "lambda" which captures a variable by moving it rather than copying or
|
||||
// referencing. C++14 generalized captures should make this obsolete, but for now in C++11 this
|
||||
// is commonly needed for Promise continuations that own their state. Example usage:
|
||||
//
|
||||
// Own<Foo> ptr = makeFoo();
|
||||
// Promise<int> promise = callRpc();
|
||||
// promise.then(mvCapture(ptr, [](Own<Foo>&& ptr, int result) {
|
||||
// return ptr->finish(result);
|
||||
// }));
|
||||
|
||||
return CaptureByMove<Func, Decay<MovedParam>>(kj::fwd<Func>(func), kj::mv(param)); |
||||
} |
||||
|
||||
// =======================================================================================
|
||||
// Advanced promise construction
|
||||
|
||||
template <typename T> |
||||
class PromiseFulfiller { |
||||
// A callback which can be used to fulfill a promise. Only the first call to fulfill() or
|
||||
// reject() matters; subsequent calls are ignored.
|
||||
|
||||
public: |
||||
virtual void fulfill(T&& value) = 0; |
||||
// Fulfill the promise with the given value.
|
||||
|
||||
virtual void reject(Exception&& exception) = 0; |
||||
// Reject the promise with an error.
|
||||
|
||||
virtual bool isWaiting() = 0; |
||||
// Returns true if the promise is still unfulfilled and someone is potentially waiting for it.
|
||||
// Returns false if fulfill()/reject() has already been called *or* if the promise to be
|
||||
// fulfilled has been discarded and therefore the result will never be used anyway.
|
||||
|
||||
template <typename Func> |
||||
bool rejectIfThrows(Func&& func); |
||||
// Call the function (with no arguments) and return true. If an exception is thrown, call
|
||||
// `fulfiller.reject()` and then return false. When compiled with exceptions disabled,
|
||||
// non-fatal exceptions are still detected and handled correctly.
|
||||
}; |
||||
|
||||
template <> |
||||
class PromiseFulfiller<void> { |
||||
// Specialization of PromiseFulfiller for void promises. See PromiseFulfiller<T>.
|
||||
|
||||
public: |
||||
virtual void fulfill(_::Void&& value = _::Void()) = 0; |
||||
// Call with zero parameters. The parameter is a dummy that only exists so that subclasses don't
|
||||
// have to specialize for <void>.
|
||||
|
||||
virtual void reject(Exception&& exception) = 0; |
||||
virtual bool isWaiting() = 0; |
||||
|
||||
template <typename Func> |
||||
bool rejectIfThrows(Func&& func); |
||||
}; |
||||
|
||||
template <typename T, typename Adapter, typename... Params> |
||||
Promise<T> newAdaptedPromise(Params&&... adapterConstructorParams); |
||||
// Creates a new promise which owns an instance of `Adapter` which encapsulates the operation
|
||||
// that will eventually fulfill the promise. This is primarily useful for adapting non-KJ
|
||||
// asynchronous APIs to use promises.
|
||||
//
|
||||
// An instance of `Adapter` will be allocated and owned by the returned `Promise`. A
|
||||
// `PromiseFulfiller<T>&` will be passed as the first parameter to the adapter's constructor,
|
||||
// and `adapterConstructorParams` will be forwarded as the subsequent parameters. The adapter
|
||||
// is expected to perform some asynchronous operation and call the `PromiseFulfiller<T>` once
|
||||
// it is finished.
|
||||
//
|
||||
// The adapter is destroyed when its owning Promise is destroyed. This may occur before the
|
||||
// Promise has been fulfilled. In this case, the adapter's destructor should cancel the
|
||||
// asynchronous operation. Once the adapter is destroyed, the fulfillment callback cannot be
|
||||
// called.
|
||||
//
|
||||
// An adapter implementation should be carefully written to ensure that it cannot accidentally
|
||||
// be left unfulfilled permanently because of an exception. Consider making liberal use of
|
||||
// `PromiseFulfiller<T>::rejectIfThrows()`.
|
||||
|
||||
template <typename T> |
||||
struct PromiseFulfillerPair { |
||||
Promise<_::JoinPromises<T>> promise; |
||||
Own<PromiseFulfiller<T>> fulfiller; |
||||
}; |
||||
|
||||
template <typename T> |
||||
PromiseFulfillerPair<T> newPromiseAndFulfiller(); |
||||
// Construct a Promise and a separate PromiseFulfiller which can be used to fulfill the promise.
|
||||
// If the PromiseFulfiller is destroyed before either of its methods are called, the Promise is
|
||||
// implicitly rejected.
|
||||
//
|
||||
// Although this function is easier to use than `newAdaptedPromise()`, it has the serious drawback
|
||||
// that there is no way to handle cancellation (i.e. detect when the Promise is discarded).
|
||||
//
|
||||
// You can arrange to fulfill a promise with another promise by using a promise type for T. E.g.
|
||||
// `newPromiseAndFulfiller<Promise<U>>()` will produce a promise of type `Promise<U>` but the
|
||||
// fulfiller will be of type `PromiseFulfiller<Promise<U>>`. Thus you pass a `Promise<U>` to the
|
||||
// `fulfill()` callback, and the promises are chained.
|
||||
|
||||
// =======================================================================================
|
||||
// TaskSet
|
||||
|
||||
class TaskSet { |
||||
// Holds a collection of Promise<void>s and ensures that each executes to completion. Memory
|
||||
// associated with each promise is automatically freed when the promise completes. Destroying
|
||||
// the TaskSet itself automatically cancels all unfinished promises.
|
||||
//
|
||||
// This is useful for "daemon" objects that perform background tasks which aren't intended to
|
||||
// fulfill any particular external promise, but which may need to be canceled (and thus can't
|
||||
// use `Promise::detach()`). The daemon object holds a TaskSet to collect these tasks it is
|
||||
// working on. This way, if the daemon itself is destroyed, the TaskSet is detroyed as well,
|
||||
// and everything the daemon is doing is canceled.
|
||||
|
||||
public: |
||||
class ErrorHandler { |
||||
public: |
||||
virtual void taskFailed(kj::Exception&& exception) = 0; |
||||
}; |
||||
|
||||
TaskSet(ErrorHandler& errorHandler); |
||||
// `loop` will be used to wait on promises. `errorHandler` will be executed any time a task
|
||||
// throws an exception, and will execute within the given EventLoop.
|
||||
|
||||
~TaskSet() noexcept(false); |
||||
|
||||
void add(Promise<void>&& promise); |
||||
|
||||
kj::String trace(); |
||||
// Return debug info about all promises currently in the TaskSet.
|
||||
|
||||
private: |
||||
Own<_::TaskSetImpl> impl; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// The EventLoop class
|
||||
|
||||
class EventPort { |
||||
// Interfaces between an `EventLoop` and events originating from outside of the loop's thread.
|
||||
// All such events come in through the `EventPort` implementation.
|
||||
//
|
||||
// An `EventPort` implementation may interface with low-level operating system APIs and/or other
|
||||
// threads. You can also write an `EventPort` which wraps some other (non-KJ) event loop
|
||||
// framework, allowing the two to coexist in a single thread.
|
||||
|
||||
public: |
||||
virtual bool wait() = 0; |
||||
// Wait for an external event to arrive, sleeping if necessary. Once at least one event has
|
||||
// arrived, queue it to the event loop (e.g. by fulfilling a promise) and return.
|
||||
//
|
||||
// This is called during `Promise::wait()` whenever the event queue becomes empty, in order to
|
||||
// wait for new events to populate the queue.
|
||||
//
|
||||
// It is safe to return even if nothing has actually been queued, so long as calling `wait()` in
|
||||
// a loop will eventually sleep. (That is to say, false positives are fine.)
|
||||
//
|
||||
// Returns true if wake() has been called from another thread. (Precisely, returns true if
|
||||
// no previous call to wait `wait()` nor `poll()` has returned true since `wake()` was last
|
||||
// called.)
|
||||
|
||||
virtual bool poll() = 0; |
||||
// Check if any external events have arrived, but do not sleep. If any events have arrived,
|
||||
// add them to the event queue (e.g. by fulfilling promises) before returning.
|
||||
//
|
||||
// This may be called during `Promise::wait()` when the EventLoop has been executing for a while
|
||||
// without a break but is still non-empty.
|
||||
//
|
||||
// Returns true if wake() has been called from another thread. (Precisely, returns true if
|
||||
// no previous call to wait `wait()` nor `poll()` has returned true since `wake()` was last
|
||||
// called.)
|
||||
|
||||
virtual void setRunnable(bool runnable); |
||||
// Called to notify the `EventPort` when the `EventLoop` has work to do; specifically when it
|
||||
// transitions from empty -> runnable or runnable -> empty. This is typically useful when
|
||||
// integrating with an external event loop; if the loop is currently runnable then you should
|
||||
// arrange to call run() on it soon. The default implementation does nothing.
|
||||
|
||||
virtual void wake() const; |
||||
// Wake up the EventPort's thread from another thread.
|
||||
//
|
||||
// Unlike all other methods on this interface, `wake()` may be called from another thread, hence
|
||||
// it is `const`.
|
||||
//
|
||||
// Technically speaking, `wake()` causes the target thread to cease sleeping and not to sleep
|
||||
// again until `wait()` or `poll()` has returned true at least once.
|
||||
//
|
||||
// The default implementation throws an UNIMPLEMENTED exception.
|
||||
}; |
||||
|
||||
class EventLoop { |
||||
// Represents a queue of events being executed in a loop. Most code won't interact with
|
||||
// EventLoop directly, but instead use `Promise`s to interact with it indirectly. See the
|
||||
// documentation for `Promise`.
|
||||
//
|
||||
// Each thread can have at most one current EventLoop. To make an `EventLoop` current for
|
||||
// the thread, create a `WaitScope`. Async APIs require that the thread has a current EventLoop,
|
||||
// or they will throw exceptions. APIs that use `Promise::wait()` additionally must explicitly
|
||||
// be passed a reference to the `WaitScope` to make the caller aware that they might block.
|
||||
//
|
||||
// Generally, you will want to construct an `EventLoop` at the top level of your program, e.g.
|
||||
// in the main() function, or in the start function of a thread. You can then use it to
|
||||
// construct some promises and wait on the result. Example:
|
||||
//
|
||||
// int main() {
|
||||
// // `loop` becomes the official EventLoop for the thread.
|
||||
// MyEventPort eventPort;
|
||||
// EventLoop loop(eventPort);
|
||||
//
|
||||
// // Now we can call an async function.
|
||||
// Promise<String> textPromise = getHttp("http://example.com");
|
||||
//
|
||||
// // And we can wait for the promise to complete. Note that you can only use `wait()`
|
||||
// // from the top level, not from inside a promise callback.
|
||||
// String text = textPromise.wait();
|
||||
// print(text);
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// Most applications that do I/O will prefer to use `setupAsyncIo()` from `async-io.h` rather
|
||||
// than allocate an `EventLoop` directly.
|
||||
|
||||
public: |
||||
EventLoop(); |
||||
// Construct an `EventLoop` which does not receive external events at all.
|
||||
|
||||
explicit EventLoop(EventPort& port); |
||||
// Construct an `EventLoop` which receives external events through the given `EventPort`.
|
||||
|
||||
~EventLoop() noexcept(false); |
||||
|
||||
void run(uint maxTurnCount = maxValue); |
||||
// Run the event loop for `maxTurnCount` turns or until there is nothing left to be done,
|
||||
// whichever comes first. This never calls the `EventPort`'s `sleep()` or `poll()`. It will
|
||||
// call the `EventPort`'s `setRunnable(false)` if the queue becomes empty.
|
||||
|
||||
bool isRunnable(); |
||||
// Returns true if run() would currently do anything, or false if the queue is empty.
|
||||
|
||||
private: |
||||
EventPort& port; |
||||
|
||||
bool running = false; |
||||
// True while looping -- wait() is then not allowed.
|
||||
|
||||
bool lastRunnableState = false; |
||||
// What did we last pass to port.setRunnable()?
|
||||
|
||||
_::Event* head = nullptr; |
||||
_::Event** tail = &head; |
||||
_::Event** depthFirstInsertPoint = &head; |
||||
|
||||
Own<_::TaskSetImpl> daemons; |
||||
|
||||
bool turn(); |
||||
void setRunnable(bool runnable); |
||||
void enterScope(); |
||||
void leaveScope(); |
||||
|
||||
friend void _::detach(kj::Promise<void>&& promise); |
||||
friend void _::waitImpl(Own<_::PromiseNode>&& node, _::ExceptionOrValue& result, |
||||
WaitScope& waitScope); |
||||
friend class _::Event; |
||||
friend class WaitScope; |
||||
}; |
||||
|
||||
class WaitScope { |
||||
// Represents a scope in which asynchronous programming can occur. A `WaitScope` should usually
|
||||
// be allocated on the stack and serves two purposes:
|
||||
// * While the `WaitScope` exists, its `EventLoop` is registered as the current loop for the
|
||||
// thread. Most operations dealing with `Promise` (including all of its methods) do not work
|
||||
// unless the thread has a current `EventLoop`.
|
||||
// * `WaitScope` may be passed to `Promise::wait()` to synchronously wait for a particular
|
||||
// promise to complete. See `Promise::wait()` for an extended discussion.
|
||||
|
||||
public: |
||||
inline explicit WaitScope(EventLoop& loop): loop(loop) { loop.enterScope(); } |
||||
inline ~WaitScope() { loop.leaveScope(); } |
||||
KJ_DISALLOW_COPY(WaitScope); |
||||
|
||||
private: |
||||
EventLoop& loop; |
||||
friend class EventLoop; |
||||
friend void _::waitImpl(Own<_::PromiseNode>&& node, _::ExceptionOrValue& result, |
||||
WaitScope& waitScope); |
||||
}; |
||||
|
||||
} // namespace kj
|
||||
|
||||
#include "async-inl.h" |
||||
|
||||
#endif // KJ_ASYNC_H_
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,122 @@ |
||||
// 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 KJ_COMPAT_GTEST_H_ |
||||
#define KJ_COMPAT_GTEST_H_ |
||||
// This file defines compatibility macros converting Google Test tests into KJ tests.
|
||||
//
|
||||
// This is only intended to cover the most common functionality. Many tests will likely need
|
||||
// additional tweaks. For instance:
|
||||
// - Using operator<< to print information on failure is not supported. Instead, switch to
|
||||
// KJ_ASSERT/KJ_EXPECT and pass in stuff to print as additional parameters.
|
||||
// - Test fixtures are not supported. Allocate your "test fixture" on the stack instead. Do setup
|
||||
// in the constructor, teardown in the destructor.
|
||||
|
||||
#include "../test.h" |
||||
|
||||
namespace kj { |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> |
||||
T abs(T value) { return value < 0 ? -value : value; } |
||||
|
||||
inline bool floatAlmostEqual(float a, float b) { |
||||
return a == b || abs(a - b) < (abs(a) + abs(b)) * 1e-5; |
||||
} |
||||
|
||||
inline bool doubleAlmostEqual(double a, double b) { |
||||
return a == b || abs(a - b) < (abs(a) + abs(b)) * 1e-12; |
||||
} |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#define EXPECT_FALSE(x) KJ_EXPECT(!(x)) |
||||
#define EXPECT_TRUE(x) KJ_EXPECT(x) |
||||
#define EXPECT_EQ(x, y) KJ_EXPECT((x) == (y), x, y) |
||||
#define EXPECT_NE(x, y) KJ_EXPECT((x) != (y), x, y) |
||||
#define EXPECT_LE(x, y) KJ_EXPECT((x) <= (y), x, y) |
||||
#define EXPECT_GE(x, y) KJ_EXPECT((x) >= (y), x, y) |
||||
#define EXPECT_LT(x, y) KJ_EXPECT((x) < (y), x, y) |
||||
#define EXPECT_GT(x, y) KJ_EXPECT((x) > (y), x, y) |
||||
#define EXPECT_STREQ(x, y) KJ_EXPECT(::strcmp(x, y) == 0, x, y) |
||||
#define EXPECT_FLOAT_EQ(x, y) KJ_EXPECT(::kj::_::floatAlmostEqual(y, x), y, x); |
||||
#define EXPECT_DOUBLE_EQ(x, y) KJ_EXPECT(::kj::_::doubleAlmostEqual(y, x), y, x); |
||||
|
||||
#define ASSERT_FALSE(x) KJ_ASSERT(!(x)) |
||||
#define ASSERT_TRUE(x) KJ_ASSERT(x) |
||||
#define ASSERT_EQ(x, y) KJ_ASSERT((x) == (y), x, y) |
||||
#define ASSERT_NE(x, y) KJ_ASSERT((x) != (y), x, y) |
||||
#define ASSERT_LE(x, y) KJ_ASSERT((x) <= (y), x, y) |
||||
#define ASSERT_GE(x, y) KJ_ASSERT((x) >= (y), x, y) |
||||
#define ASSERT_LT(x, y) KJ_ASSERT((x) < (y), x, y) |
||||
#define ASSERT_GT(x, y) KJ_ASSERT((x) > (y), x, y) |
||||
#define ASSERT_STREQ(x, y) KJ_ASSERT(::strcmp(x, y) == 0, x, y) |
||||
#define ASSERT_FLOAT_EQ(x, y) KJ_ASSERT(::kj::_::floatAlmostEqual(y, x), y, x); |
||||
#define ASSERT_DOUBLE_EQ(x, y) KJ_ASSERT(::kj::_::doubleAlmostEqual(y, x), y, x); |
||||
|
||||
class AddFailureAdapter { |
||||
public: |
||||
AddFailureAdapter(const char* file, int line): file(file), line(line) {} |
||||
|
||||
~AddFailureAdapter() { |
||||
if (!handled) { |
||||
_::Debug::log(file, line, LogSeverity::ERROR, "expectation failed"); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
void operator<<(T&& info) { |
||||
handled = true; |
||||
_::Debug::log(file, line, LogSeverity::ERROR, "\"expectation failed\", info", |
||||
"expectation failed", kj::fwd<T>(info)); |
||||
} |
||||
|
||||
private: |
||||
bool handled = false; |
||||
const char* file; |
||||
int line; |
||||
}; |
||||
|
||||
#define ADD_FAILURE() ::kj::AddFailureAdapter(__FILE__, __LINE__) |
||||
|
||||
#if KJ_NO_EXCEPTIONS |
||||
#define EXPECT_ANY_THROW(code) \ |
||||
KJ_EXPECT(::kj::_::expectFatalThrow(nullptr, nullptr, [&]() { code; })) |
||||
#else |
||||
#define EXPECT_ANY_THROW(code) \ |
||||
KJ_EXPECT(::kj::runCatchingExceptions([&]() { code; }) != nullptr) |
||||
#endif |
||||
|
||||
#define EXPECT_NONFATAL_FAILURE(code) \ |
||||
EXPECT_TRUE(kj::runCatchingExceptions([&]() { code; }) != nullptr); |
||||
|
||||
#ifdef KJ_DEBUG |
||||
#define EXPECT_DEBUG_ANY_THROW EXPECT_ANY_THROW |
||||
#else |
||||
#define EXPECT_DEBUG_ANY_THROW(EXP) |
||||
#endif |
||||
|
||||
#define TEST(x, y) KJ_TEST("legacy test: " #x "/" #y) |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_COMPAT_GTEST_H_
|
@ -0,0 +1,636 @@ |
||||
// Copyright (c) 2017 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 KJ_COMPAT_HTTP_H_ |
||||
#define KJ_COMPAT_HTTP_H_ |
||||
// The KJ HTTP client/server library.
|
||||
//
|
||||
// This is a simple library which can be used to implement an HTTP client or server. Properties
|
||||
// of this library include:
|
||||
// - Uses KJ async framework.
|
||||
// - Agnostic to transport layer -- you can provide your own.
|
||||
// - Header parsing is zero-copy -- it results in strings that point directly into the buffer
|
||||
// received off the wire.
|
||||
// - Application code which reads and writes headers refers to headers by symbolic names, not by
|
||||
// string literals, with lookups being array-index-based, not map-based. To make this possible,
|
||||
// the application announces what headers it cares about in advance, in order to assign numeric
|
||||
// values to them.
|
||||
// - Methods are identified by an enum.
|
||||
|
||||
#include <kj/string.h> |
||||
#include <kj/vector.h> |
||||
#include <kj/memory.h> |
||||
#include <kj/one-of.h> |
||||
#include <kj/async-io.h> |
||||
|
||||
namespace kj { |
||||
|
||||
#define KJ_HTTP_FOR_EACH_METHOD(MACRO) \ |
||||
MACRO(GET) \
|
||||
MACRO(HEAD) \
|
||||
MACRO(POST) \
|
||||
MACRO(PUT) \
|
||||
MACRO(DELETE) \
|
||||
MACRO(PATCH) \
|
||||
MACRO(PURGE) \
|
||||
MACRO(OPTIONS) \
|
||||
MACRO(TRACE) \
|
||||
/* standard methods */ \
|
||||
/* */ \
|
||||
/* (CONNECT is intentionally omitted since it is handled specially in HttpHandler) */ \
|
||||
\
|
||||
MACRO(COPY) \
|
||||
MACRO(LOCK) \
|
||||
MACRO(MKCOL) \
|
||||
MACRO(MOVE) \
|
||||
MACRO(PROPFIND) \
|
||||
MACRO(PROPPATCH) \
|
||||
MACRO(SEARCH) \
|
||||
MACRO(UNLOCK) \
|
||||
/* WebDAV */ \
|
||||
\
|
||||
MACRO(REPORT) \
|
||||
MACRO(MKACTIVITY) \
|
||||
MACRO(CHECKOUT) \
|
||||
MACRO(MERGE) \
|
||||
/* Subversion */ \
|
||||
\
|
||||
MACRO(MSEARCH) \
|
||||
MACRO(NOTIFY) \
|
||||
MACRO(SUBSCRIBE) \
|
||||
MACRO(UNSUBSCRIBE) |
||||
/* UPnP */ |
||||
|
||||
#define KJ_HTTP_FOR_EACH_CONNECTION_HEADER(MACRO) \ |
||||
MACRO(connection, "Connection") \
|
||||
MACRO(contentLength, "Content-Length") \
|
||||
MACRO(keepAlive, "Keep-Alive") \
|
||||
MACRO(te, "TE") \
|
||||
MACRO(trailer, "Trailer") \
|
||||
MACRO(transferEncoding, "Transfer-Encoding") \
|
||||
MACRO(upgrade, "Upgrade") |
||||
|
||||
enum class HttpMethod { |
||||
// Enum of known HTTP methods.
|
||||
//
|
||||
// We use an enum rather than a string to allow for faster parsing and switching and to reduce
|
||||
// ambiguity.
|
||||
|
||||
#define DECLARE_METHOD(id) id, |
||||
KJ_HTTP_FOR_EACH_METHOD(DECLARE_METHOD) |
||||
#undef DECLARE_METHOD |
||||
}; |
||||
|
||||
kj::StringPtr KJ_STRINGIFY(HttpMethod method); |
||||
kj::Maybe<HttpMethod> tryParseHttpMethod(kj::StringPtr name); |
||||
|
||||
class HttpHeaderTable; |
||||
|
||||
class HttpHeaderId { |
||||
// Identifies an HTTP header by numeric ID that indexes into an HttpHeaderTable.
|
||||
//
|
||||
// The KJ HTTP API prefers that headers be identified by these IDs for a few reasons:
|
||||
// - Integer lookups are much more efficient than string lookups.
|
||||
// - Case-insensitivity is awkward to deal with when const strings are being passed to the lookup
|
||||
// method.
|
||||
// - Writing out strings less often means fewer typos.
|
||||
//
|
||||
// See HttpHeaderTable for usage hints.
|
||||
|
||||
public: |
||||
HttpHeaderId() = default; |
||||
|
||||
inline bool operator==(const HttpHeaderId& other) const { return id == other.id; } |
||||
inline bool operator!=(const HttpHeaderId& other) const { return id != other.id; } |
||||
inline bool operator< (const HttpHeaderId& other) const { return id < other.id; } |
||||
inline bool operator> (const HttpHeaderId& other) const { return id > other.id; } |
||||
inline bool operator<=(const HttpHeaderId& other) const { return id <= other.id; } |
||||
inline bool operator>=(const HttpHeaderId& other) const { return id >= other.id; } |
||||
|
||||
inline size_t hashCode() const { return id; } |
||||
|
||||
kj::StringPtr toString() const; |
||||
|
||||
void requireFrom(HttpHeaderTable& table) const; |
||||
// In debug mode, throws an exception if the HttpHeaderId is not from the given table.
|
||||
//
|
||||
// In opt mode, no-op.
|
||||
|
||||
#define KJ_HTTP_FOR_EACH_BUILTIN_HEADER(MACRO) \ |
||||
MACRO(HOST, "Host") \
|
||||
MACRO(DATE, "Date") \
|
||||
MACRO(LOCATION, "Location") \
|
||||
MACRO(CONTENT_TYPE, "Content-Type") |
||||
// For convenience, these very-common headers are valid for all HttpHeaderTables. You can refer
|
||||
// to them like:
|
||||
//
|
||||
// HttpHeaderId::HOST
|
||||
//
|
||||
// TODO(0.7): Fill this out with more common headers.
|
||||
|
||||
#define DECLARE_HEADER(id, name) \ |
||||
static const HttpHeaderId id; |
||||
// Declare a constant for each builtin header, e.g.: HttpHeaderId::CONNECTION
|
||||
|
||||
KJ_HTTP_FOR_EACH_BUILTIN_HEADER(DECLARE_HEADER); |
||||
#undef DECLARE_HEADER |
||||
|
||||
private: |
||||
HttpHeaderTable* table; |
||||
uint id; |
||||
|
||||
inline explicit constexpr HttpHeaderId(HttpHeaderTable* table, uint id): table(table), id(id) {} |
||||
friend class HttpHeaderTable; |
||||
friend class HttpHeaders; |
||||
}; |
||||
|
||||
class HttpHeaderTable { |
||||
// Construct an HttpHeaderTable to declare which headers you'll be interested in later on, and
|
||||
// to manufacture IDs for them.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Build a header table with the headers we are interested in.
|
||||
// kj::HttpHeaderTable::Builder builder;
|
||||
// const HttpHeaderId accept = builder.add("Accept");
|
||||
// const HttpHeaderId contentType = builder.add("Content-Type");
|
||||
// kj::HttpHeaderTable table(kj::mv(builder));
|
||||
//
|
||||
// // Create an HTTP client.
|
||||
// auto client = kj::newHttpClient(table, network);
|
||||
//
|
||||
// // Get http://example.com.
|
||||
// HttpHeaders headers(table);
|
||||
// headers.set(accept, "text/html");
|
||||
// auto response = client->send(kj::HttpMethod::GET, "http://example.com", headers)
|
||||
// .wait(waitScope);
|
||||
// auto msg = kj::str("Response content type: ", response.headers.get(contentType));
|
||||
|
||||
struct IdsByNameMap; |
||||
|
||||
public: |
||||
HttpHeaderTable(); |
||||
// Constructs a table that only contains the builtin headers.
|
||||
|
||||
class Builder { |
||||
public: |
||||
Builder(); |
||||
HttpHeaderId add(kj::StringPtr name); |
||||
Own<HttpHeaderTable> build(); |
||||
|
||||
HttpHeaderTable& getFutureTable(); |
||||
// Get the still-unbuilt header table. You cannot actually use it until build() has been
|
||||
// called.
|
||||
//
|
||||
// This method exists to help when building a shared header table -- the Builder may be passed
|
||||
// to several components, each of which will register the headers they need and get a reference
|
||||
// to the future table.
|
||||
|
||||
private: |
||||
kj::Own<HttpHeaderTable> table; |
||||
}; |
||||
|
||||
KJ_DISALLOW_COPY(HttpHeaderTable); // Can't copy because HttpHeaderId points to the table.
|
||||
~HttpHeaderTable() noexcept(false); |
||||
|
||||
uint idCount(); |
||||
// Return the number of IDs in the table.
|
||||
|
||||
kj::Maybe<HttpHeaderId> stringToId(kj::StringPtr name); |
||||
// Try to find an ID for the given name. The matching is case-insensitive, per the HTTP spec.
|
||||
//
|
||||
// Note: if `name` contains characters that aren't allowed in HTTP header names, this may return
|
||||
// a bogus value rather than null, due to optimizations used in case-insensitive matching.
|
||||
|
||||
kj::StringPtr idToString(HttpHeaderId id); |
||||
// Get the canonical string name for the given ID.
|
||||
|
||||
private: |
||||
kj::Vector<kj::StringPtr> namesById; |
||||
kj::Own<IdsByNameMap> idsByName; |
||||
}; |
||||
|
||||
class HttpHeaders { |
||||
// Represents a set of HTTP headers.
|
||||
//
|
||||
// This class guards against basic HTTP header injection attacks: Trying to set a header name or
|
||||
// value containing a newline, carriage return, or other invalid character will throw an
|
||||
// exception.
|
||||
|
||||
public: |
||||
explicit HttpHeaders(HttpHeaderTable& table); |
||||
|
||||
KJ_DISALLOW_COPY(HttpHeaders); |
||||
HttpHeaders(HttpHeaders&&) = default; |
||||
HttpHeaders& operator=(HttpHeaders&&) = default; |
||||
|
||||
void clear(); |
||||
// Clears all contents, as if the object was freshly-allocated. However, calling this rather
|
||||
// than actually re-allocating the object may avoid re-allocation of internal objects.
|
||||
|
||||
HttpHeaders clone() const; |
||||
// Creates a deep clone of the HttpHeaders. The returned object owns all strings it references.
|
||||
|
||||
HttpHeaders cloneShallow() const; |
||||
// Creates a shallow clone of the HttpHeaders. The returned object references the same strings
|
||||
// as the original, owning none of them.
|
||||
|
||||
kj::Maybe<kj::StringPtr> get(HttpHeaderId id) const; |
||||
// Read a header.
|
||||
|
||||
template <typename Func> |
||||
void forEach(Func&& func) const; |
||||
// Calls `func(name, value)` for each header in the set -- including headers that aren't mapped
|
||||
// to IDs in the header table. Both inputs are of type kj::StringPtr.
|
||||
|
||||
void set(HttpHeaderId id, kj::StringPtr value); |
||||
void set(HttpHeaderId id, kj::String&& value); |
||||
// Sets a header value, overwriting the existing value.
|
||||
//
|
||||
// The String&& version is equivalent to calling the other version followed by takeOwnership().
|
||||
//
|
||||
// WARNING: It is the caller's responsibility to ensure that `value` remains valid until the
|
||||
// HttpHeaders object is destroyed. This allows string literals to be passed without making a
|
||||
// copy, but complicates the use of dynamic values. Hint: Consider using `takeOwnership()`.
|
||||
|
||||
void add(kj::StringPtr name, kj::StringPtr value); |
||||
void add(kj::StringPtr name, kj::String&& value); |
||||
void add(kj::String&& name, kj::String&& value); |
||||
// Append a header. `name` will be looked up in the header table, but if it's not mapped, the
|
||||
// header will be added to the list of unmapped headers.
|
||||
//
|
||||
// The String&& versions are equivalent to calling the other version followed by takeOwnership().
|
||||
//
|
||||
// WARNING: It is the caller's responsibility to ensure that `name` and `value` remain valid
|
||||
// until the HttpHeaders object is destroyed. This allows string literals to be passed without
|
||||
// making a copy, but complicates the use of dynamic values. Hint: Consider using
|
||||
// `takeOwnership()`.
|
||||
|
||||
void unset(HttpHeaderId id); |
||||
// Removes a header.
|
||||
//
|
||||
// It's not possible to remove a header by string name because non-indexed headers would take
|
||||
// O(n) time to remove. Instead, construct a new HttpHeaders object and copy contents.
|
||||
|
||||
void takeOwnership(kj::String&& string); |
||||
void takeOwnership(kj::Array<char>&& chars); |
||||
void takeOwnership(HttpHeaders&& otherHeaders); |
||||
// Takes overship of a string so that it lives until the HttpHeaders object is destroyed. Useful
|
||||
// when you've passed a dynamic value to set() or add() or parse*().
|
||||
|
||||
struct ConnectionHeaders { |
||||
// These headers govern details of the specific HTTP connection or framing of the content.
|
||||
// Hence, they are managed internally within the HTTP library, and never appear in an
|
||||
// HttpHeaders structure.
|
||||
|
||||
#define DECLARE_HEADER(id, name) \ |
||||
kj::StringPtr id; |
||||
KJ_HTTP_FOR_EACH_CONNECTION_HEADER(DECLARE_HEADER) |
||||
#undef DECLARE_HEADER |
||||
}; |
||||
|
||||
struct Request { |
||||
HttpMethod method; |
||||
kj::StringPtr url; |
||||
ConnectionHeaders connectionHeaders; |
||||
}; |
||||
struct Response { |
||||
uint statusCode; |
||||
kj::StringPtr statusText; |
||||
ConnectionHeaders connectionHeaders; |
||||
}; |
||||
|
||||
kj::Maybe<Request> tryParseRequest(kj::ArrayPtr<char> content); |
||||
kj::Maybe<Response> tryParseResponse(kj::ArrayPtr<char> content); |
||||
// Parse an HTTP header blob and add all the headers to this object.
|
||||
//
|
||||
// `content` should be all text from the start of the request to the first occurrance of two
|
||||
// newlines in a row -- including the first of these two newlines, but excluding the second.
|
||||
//
|
||||
// The parse is performed with zero copies: The callee clobbers `content` with '\0' characters
|
||||
// to split it into a bunch of shorter strings. The caller must keep `content` valid until the
|
||||
// `HttpHeaders` is destroyed, or pass it to `takeOwnership()`.
|
||||
|
||||
kj::String serializeRequest(HttpMethod method, kj::StringPtr url, |
||||
const ConnectionHeaders& connectionHeaders) const; |
||||
kj::String serializeResponse(uint statusCode, kj::StringPtr statusText, |
||||
const ConnectionHeaders& connectionHeaders) const; |
||||
// Serialize the headers as a complete request or response blob. The blob uses '\r\n' newlines
|
||||
// and includes the double-newline to indicate the end of the headers.
|
||||
|
||||
kj::String toString() const; |
||||
|
||||
private: |
||||
HttpHeaderTable* table; |
||||
|
||||
kj::Array<kj::StringPtr> indexedHeaders; |
||||
// Size is always table->idCount().
|
||||
|
||||
struct Header { |
||||
kj::StringPtr name; |
||||
kj::StringPtr value; |
||||
}; |
||||
kj::Vector<Header> unindexedHeaders; |
||||
|
||||
kj::Vector<kj::Array<char>> ownedStrings; |
||||
|
||||
kj::Maybe<uint> addNoCheck(kj::StringPtr name, kj::StringPtr value); |
||||
|
||||
kj::StringPtr cloneToOwn(kj::StringPtr str); |
||||
|
||||
kj::String serialize(kj::ArrayPtr<const char> word1, |
||||
kj::ArrayPtr<const char> word2, |
||||
kj::ArrayPtr<const char> word3, |
||||
const ConnectionHeaders& connectionHeaders) const; |
||||
|
||||
bool parseHeaders(char* ptr, char* end, ConnectionHeaders& connectionHeaders); |
||||
|
||||
// TODO(perf): Arguably we should store a map, but header sets are never very long
|
||||
// TODO(perf): We could optimize for common headers by storing them directly as fields. We could
|
||||
// also add direct accessors for those headers.
|
||||
}; |
||||
|
||||
class WebSocket { |
||||
public: |
||||
WebSocket(kj::Own<kj::AsyncIoStream> stream); |
||||
// Create a WebSocket wrapping the given I/O stream.
|
||||
|
||||
kj::Promise<void> send(kj::ArrayPtr<const byte> message); |
||||
kj::Promise<void> send(kj::ArrayPtr<const char> message); |
||||
}; |
||||
|
||||
class HttpClient { |
||||
// Interface to the client end of an HTTP connection.
|
||||
//
|
||||
// There are two kinds of clients:
|
||||
// * Host clients are used when talking to a specific host. The `url` specified in a request
|
||||
// is actually just a path. (A `Host` header is still required in all requests.)
|
||||
// * Proxy clients are used when the target could be any arbitrary host on the internet.
|
||||
// The `url` specified in a request is a full URL including protocol and hostname.
|
||||
|
||||
public: |
||||
struct Response { |
||||
uint statusCode; |
||||
kj::StringPtr statusText; |
||||
const HttpHeaders* headers; |
||||
kj::Own<kj::AsyncInputStream> body; |
||||
// `statusText` and `headers` remain valid until `body` is dropped.
|
||||
}; |
||||
|
||||
struct Request { |
||||
kj::Own<kj::AsyncOutputStream> body; |
||||
// Write the request entity body to this stream, then drop it when done.
|
||||
//
|
||||
// May be null for GET and HEAD requests (which have no body) and requests that have
|
||||
// Content-Length: 0.
|
||||
|
||||
kj::Promise<Response> response; |
||||
// Promise for the eventual respnose.
|
||||
}; |
||||
|
||||
virtual Request request(HttpMethod method, kj::StringPtr url, const HttpHeaders& headers, |
||||
kj::Maybe<uint64_t> expectedBodySize = nullptr) = 0; |
||||
// Perform an HTTP request.
|
||||
//
|
||||
// `url` may be a full URL (with protocol and host) or it may be only the path part of the URL,
|
||||
// depending on whether the client is a proxy client or a host client.
|
||||
//
|
||||
// `url` and `headers` need only remain valid until `request()` returns (they can be
|
||||
// stack-allocated).
|
||||
//
|
||||
// `expectedBodySize`, if provided, must be exactly the number of bytes that will be written to
|
||||
// the body. This will trigger use of the `Content-Length` connection header. Otherwise,
|
||||
// `Transfer-Encoding: chunked` will be used.
|
||||
|
||||
struct WebSocketResponse { |
||||
uint statusCode; |
||||
kj::StringPtr statusText; |
||||
const HttpHeaders* headers; |
||||
kj::OneOf<kj::Own<kj::AsyncInputStream>, kj::Own<WebSocket>> upstreamOrBody; |
||||
// `statusText` and `headers` remain valid until `upstreamOrBody` is dropped.
|
||||
}; |
||||
virtual kj::Promise<WebSocketResponse> openWebSocket( |
||||
kj::StringPtr url, const HttpHeaders& headers, kj::Own<WebSocket> downstream); |
||||
// Tries to open a WebSocket. Default implementation calls send() and never returns a WebSocket.
|
||||
//
|
||||
// `url` and `headers` are invalidated when the returned promise resolves.
|
||||
|
||||
virtual kj::Promise<kj::Own<kj::AsyncIoStream>> connect(kj::String host); |
||||
// Handles CONNECT requests. Only relevant for proxy clients. Default implementation throws
|
||||
// UNIMPLEMENTED.
|
||||
}; |
||||
|
||||
class HttpService { |
||||
// Interface which HTTP services should implement.
|
||||
//
|
||||
// This interface is functionally equivalent to HttpClient, but is intended for applications to
|
||||
// implement rather than call. The ergonomics and performance of the method signatures are
|
||||
// optimized for the serving end.
|
||||
//
|
||||
// As with clients, there are two kinds of services:
|
||||
// * Host services are used when talking to a specific host. The `url` specified in a request
|
||||
// is actually just a path. (A `Host` header is still required in all requests, and the service
|
||||
// may in fact serve multiple origins via this header.)
|
||||
// * Proxy services are used when the target could be any arbitrary host on the internet, i.e. to
|
||||
// implement an HTTP proxy. The `url` specified in a request is a full URL including protocol
|
||||
// and hostname.
|
||||
|
||||
public: |
||||
class Response { |
||||
public: |
||||
virtual kj::Own<kj::AsyncOutputStream> send( |
||||
uint statusCode, kj::StringPtr statusText, const HttpHeaders& headers, |
||||
kj::Maybe<uint64_t> expectedBodySize = nullptr) = 0; |
||||
// Begin the response.
|
||||
//
|
||||
// `statusText` and `headers` need only remain valid until send() returns (they can be
|
||||
// stack-allocated).
|
||||
}; |
||||
|
||||
virtual kj::Promise<void> request( |
||||
HttpMethod method, kj::StringPtr url, const HttpHeaders& headers, |
||||
kj::AsyncInputStream& requestBody, Response& response) = 0; |
||||
// Perform an HTTP request.
|
||||
//
|
||||
// `url` may be a full URL (with protocol and host) or it may be only the path part of the URL,
|
||||
// depending on whether the service is a proxy service or a host service.
|
||||
//
|
||||
// `url` and `headers` are invalidated on the first read from `requestBody` or when the returned
|
||||
// promise resolves, whichever comes first.
|
||||
|
||||
class WebSocketResponse: public Response { |
||||
public: |
||||
kj::Own<WebSocket> startWebSocket( |
||||
uint statusCode, kj::StringPtr statusText, const HttpHeaders& headers, |
||||
WebSocket& upstream); |
||||
// Begin the response.
|
||||
//
|
||||
// `statusText` and `headers` need only remain valid until startWebSocket() returns (they can
|
||||
// be stack-allocated).
|
||||
}; |
||||
|
||||
virtual kj::Promise<void> openWebSocket( |
||||
kj::StringPtr url, const HttpHeaders& headers, WebSocketResponse& response); |
||||
// Tries to open a WebSocket. Default implementation calls request() and never returns a
|
||||
// WebSocket.
|
||||
//
|
||||
// `url` and `headers` are invalidated when the returned promise resolves.
|
||||
|
||||
virtual kj::Promise<kj::Own<kj::AsyncIoStream>> connect(kj::String host); |
||||
// Handles CONNECT requests. Only relevant for proxy services. Default implementation throws
|
||||
// UNIMPLEMENTED.
|
||||
}; |
||||
|
||||
kj::Own<HttpClient> newHttpClient(HttpHeaderTable& responseHeaderTable, kj::Network& network, |
||||
kj::Maybe<kj::Network&> tlsNetwork = nullptr); |
||||
// Creates a proxy HttpClient that connects to hosts over the given network.
|
||||
//
|
||||
// `responseHeaderTable` is used when parsing HTTP responses. Requests can use any header table.
|
||||
//
|
||||
// `tlsNetwork` is required to support HTTPS destination URLs. Otherwise, only HTTP URLs can be
|
||||
// fetched.
|
||||
|
||||
kj::Own<HttpClient> newHttpClient(HttpHeaderTable& responseHeaderTable, kj::AsyncIoStream& stream); |
||||
// Creates an HttpClient that speaks over the given pre-established connection. The client may
|
||||
// be used as a proxy client or a host client depending on whether the peer is operating as
|
||||
// a proxy.
|
||||
//
|
||||
// Note that since this client has only one stream to work with, it will try to pipeline all
|
||||
// requests on this stream. If one request or response has an I/O failure, all subsequent requests
|
||||
// fail as well. If the destination server chooses to close the connection after a response,
|
||||
// subsequent requests will fail. If a response takes a long time, it blocks subsequent responses.
|
||||
// If a WebSocket is opened successfully, all subsequent requests fail.
|
||||
|
||||
kj::Own<HttpClient> newHttpClient(HttpService& service); |
||||
kj::Own<HttpService> newHttpService(HttpClient& client); |
||||
// Adapts an HttpClient to an HttpService and vice versa.
|
||||
|
||||
struct HttpServerSettings { |
||||
kj::Duration headerTimeout = 15 * kj::SECONDS; |
||||
// After initial connection open, or after receiving the first byte of a pipelined request,
|
||||
// the client must send the complete request within this time.
|
||||
|
||||
kj::Duration pipelineTimeout = 5 * kj::SECONDS; |
||||
// After one request/response completes, we'll wait up to this long for a pipelined request to
|
||||
// arrive.
|
||||
}; |
||||
|
||||
class HttpServer: private kj::TaskSet::ErrorHandler { |
||||
// Class which listens for requests on ports or connections and sends them to an HttpService.
|
||||
|
||||
public: |
||||
typedef HttpServerSettings Settings; |
||||
|
||||
HttpServer(kj::Timer& timer, HttpHeaderTable& requestHeaderTable, HttpService& service, |
||||
Settings settings = Settings()); |
||||
// Set up an HttpServer that directs incoming connections to the given service. The service
|
||||
// may be a host service or a proxy service depending on whether you are intending to implement
|
||||
// an HTTP server or an HTTP proxy.
|
||||
|
||||
kj::Promise<void> drain(); |
||||
// Stop accepting new connections or new requests on existing connections. Finish any requests
|
||||
// that are already executing, then close the connections. Returns once no more requests are
|
||||
// in-flight.
|
||||
|
||||
kj::Promise<void> listenHttp(kj::ConnectionReceiver& port); |
||||
// Accepts HTTP connections on the given port and directs them to the handler.
|
||||
//
|
||||
// The returned promise never completes normally. It may throw if port.accept() throws. Dropping
|
||||
// the returned promise will cause the server to stop listening on the port, but already-open
|
||||
// connections will continue to be served. Destroy the whole HttpServer to cancel all I/O.
|
||||
|
||||
kj::Promise<void> listenHttp(kj::Own<kj::AsyncIoStream> connection); |
||||
// Reads HTTP requests from the given connection and directs them to the handler. A successful
|
||||
// completion of the promise indicates that all requests received on the connection resulted in
|
||||
// a complete response, and the client closed the connection gracefully or drain() was called.
|
||||
// The promise throws if an unparseable request is received or if some I/O error occurs. Dropping
|
||||
// the returned promise will cancel all I/O on the connection and cancel any in-flight requests.
|
||||
|
||||
private: |
||||
class Connection; |
||||
|
||||
kj::Timer& timer; |
||||
HttpHeaderTable& requestHeaderTable; |
||||
HttpService& service; |
||||
Settings settings; |
||||
|
||||
bool draining = false; |
||||
kj::ForkedPromise<void> onDrain; |
||||
kj::Own<kj::PromiseFulfiller<void>> drainFulfiller; |
||||
|
||||
uint connectionCount = 0; |
||||
kj::Maybe<kj::Own<kj::PromiseFulfiller<void>>> zeroConnectionsFulfiller; |
||||
|
||||
kj::TaskSet tasks; |
||||
|
||||
HttpServer(kj::Timer& timer, HttpHeaderTable& requestHeaderTable, HttpService& service, |
||||
Settings settings, kj::PromiseFulfillerPair<void> paf); |
||||
|
||||
kj::Promise<void> listenLoop(kj::ConnectionReceiver& port); |
||||
|
||||
void taskFailed(kj::Exception&& exception) override; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// inline implementation
|
||||
|
||||
inline void HttpHeaderId::requireFrom(HttpHeaderTable& table) const { |
||||
KJ_IREQUIRE(this->table == nullptr || this->table == &table, |
||||
"the provided HttpHeaderId is from the wrong HttpHeaderTable"); |
||||
} |
||||
|
||||
inline kj::Own<HttpHeaderTable> HttpHeaderTable::Builder::build() { return kj::mv(table); } |
||||
inline HttpHeaderTable& HttpHeaderTable::Builder::getFutureTable() { return *table; } |
||||
|
||||
inline uint HttpHeaderTable::idCount() { return namesById.size(); } |
||||
|
||||
inline kj::StringPtr HttpHeaderTable::idToString(HttpHeaderId id) { |
||||
id.requireFrom(*this); |
||||
return namesById[id.id]; |
||||
} |
||||
|
||||
inline kj::Maybe<kj::StringPtr> HttpHeaders::get(HttpHeaderId id) const { |
||||
id.requireFrom(*table); |
||||
auto result = indexedHeaders[id.id]; |
||||
return result == nullptr ? kj::Maybe<kj::StringPtr>(nullptr) : result; |
||||
} |
||||
|
||||
inline void HttpHeaders::unset(HttpHeaderId id) { |
||||
id.requireFrom(*table); |
||||
indexedHeaders[id.id] = nullptr; |
||||
} |
||||
|
||||
template <typename Func> |
||||
inline void HttpHeaders::forEach(Func&& func) const { |
||||
for (auto i: kj::indices(indexedHeaders)) { |
||||
if (indexedHeaders[i] != nullptr) { |
||||
func(table->idToString(HttpHeaderId(table, i)), indexedHeaders[i]); |
||||
} |
||||
} |
||||
|
||||
for (auto& header: unindexedHeaders) { |
||||
func(header.name, header.value); |
||||
} |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_COMPAT_HTTP_H_
|
@ -0,0 +1,555 @@ |
||||
// 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 declares convenient macros for debug logging and error handling. The macros make
|
||||
// it excessively easy to extract useful context information from code. Example:
|
||||
//
|
||||
// KJ_ASSERT(a == b, a, b, "a and b must be the same.");
|
||||
//
|
||||
// On failure, this will throw an exception whose description looks like:
|
||||
//
|
||||
// myfile.c++:43: bug in code: expected a == b; a = 14; b = 72; a and b must be the same.
|
||||
//
|
||||
// As you can see, all arguments after the first provide additional context.
|
||||
//
|
||||
// The macros available are:
|
||||
//
|
||||
// * `KJ_LOG(severity, ...)`: Just writes a log message, to stderr by default (but you can
|
||||
// intercept messages by implementing an ExceptionCallback). `severity` is `INFO`, `WARNING`,
|
||||
// `ERROR`, or `FATAL`. By default, `INFO` logs are not written, but for command-line apps the
|
||||
// user should be able to pass a flag like `--verbose` to enable them. Other log levels are
|
||||
// enabled by default. Log messages -- like exceptions -- can be intercepted by registering an
|
||||
// ExceptionCallback.
|
||||
//
|
||||
// * `KJ_DBG(...)`: Like `KJ_LOG`, but intended specifically for temporary log lines added while
|
||||
// debugging a particular problem. Calls to `KJ_DBG` should always be deleted before committing
|
||||
// code. It is suggested that you set up a pre-commit hook that checks for this.
|
||||
//
|
||||
// * `KJ_ASSERT(condition, ...)`: Throws an exception if `condition` is false, or aborts if
|
||||
// exceptions are disabled. This macro should be used to check for bugs in the surrounding code
|
||||
// and its dependencies, but NOT to check for invalid input. The macro may be followed by a
|
||||
// brace-delimited code block; if so, the block will be executed in the case where the assertion
|
||||
// fails, before throwing the exception. If control jumps out of the block (e.g. with "break",
|
||||
// "return", or "goto"), then the error is considered "recoverable" -- in this case, if
|
||||
// exceptions are disabled, execution will continue normally rather than aborting (but if
|
||||
// exceptions are enabled, an exception will still be thrown on exiting the block). A "break"
|
||||
// statement in particular will jump to the code immediately after the block (it does not break
|
||||
// any surrounding loop or switch). Example:
|
||||
//
|
||||
// KJ_ASSERT(value >= 0, "Value cannot be negative.", value) {
|
||||
// // Assertion failed. Set value to zero to "recover".
|
||||
// value = 0;
|
||||
// // Don't abort if exceptions are disabled. Continue normally.
|
||||
// // (Still throw an exception if they are enabled, though.)
|
||||
// break;
|
||||
// }
|
||||
// // When exceptions are disabled, we'll get here even if the assertion fails.
|
||||
// // Otherwise, we get here only if the assertion passes.
|
||||
//
|
||||
// * `KJ_REQUIRE(condition, ...)`: Like `KJ_ASSERT` but used to check preconditions -- e.g. to
|
||||
// validate parameters passed from a caller. A failure indicates that the caller is buggy.
|
||||
//
|
||||
// * `KJ_SYSCALL(code, ...)`: Executes `code` assuming it makes a system call. A negative result
|
||||
// is considered an error, with error code reported via `errno`. EINTR is handled by retrying.
|
||||
// Other errors are handled by throwing an exception. If you need to examine the return code,
|
||||
// assign it to a variable like so:
|
||||
//
|
||||
// int fd;
|
||||
// KJ_SYSCALL(fd = open(filename, O_RDONLY), filename);
|
||||
//
|
||||
// `KJ_SYSCALL` can be followed by a recovery block, just like `KJ_ASSERT`.
|
||||
//
|
||||
// * `KJ_NONBLOCKING_SYSCALL(code, ...)`: Like KJ_SYSCALL, but will not throw an exception on
|
||||
// EAGAIN/EWOULDBLOCK. The calling code should check the syscall's return value to see if it
|
||||
// indicates an error; in this case, it can assume the error was EAGAIN because any other error
|
||||
// would have caused an exception to be thrown.
|
||||
//
|
||||
// * `KJ_CONTEXT(...)`: Notes additional contextual information relevant to any exceptions thrown
|
||||
// from within the current scope. That is, until control exits the block in which KJ_CONTEXT()
|
||||
// is used, if any exception is generated, it will contain the given information in its context
|
||||
// chain. This is helpful because it can otherwise be very difficult to come up with error
|
||||
// messages that make sense within low-level helper code. Note that the parameters to
|
||||
// KJ_CONTEXT() are only evaluated if an exception is thrown. This implies that any variables
|
||||
// used must remain valid until the end of the scope.
|
||||
//
|
||||
// Notes:
|
||||
// * Do not write expressions with side-effects in the message content part of the macro, as the
|
||||
// message will not necessarily be evaluated.
|
||||
// * For every macro `FOO` above except `LOG`, there is also a `FAIL_FOO` macro used to report
|
||||
// failures that already happened. For the macros that check a boolean condition, `FAIL_FOO`
|
||||
// omits the first parameter and behaves like it was `false`. `FAIL_SYSCALL` and
|
||||
// `FAIL_RECOVERABLE_SYSCALL` take a string and an OS error number as the first two parameters.
|
||||
// The string should be the name of the failed system call.
|
||||
// * For every macro `FOO` above, there is a `DFOO` version (or `RECOVERABLE_DFOO`) which is only
|
||||
// executed in debug mode, i.e. when KJ_DEBUG is defined. KJ_DEBUG is defined automatically
|
||||
// by common.h when compiling without optimization (unless NDEBUG is defined), but you can also
|
||||
// define it explicitly (e.g. -DKJ_DEBUG). Generally, production builds should NOT use KJ_DEBUG
|
||||
// as it may enable expensive checks that are unlikely to fail.
|
||||
|
||||
#ifndef KJ_DEBUG_H_ |
||||
#define KJ_DEBUG_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "string.h" |
||||
#include "exception.h" |
||||
|
||||
#ifdef ERROR |
||||
// This is problematic because windows.h #defines ERROR, which we use in an enum here.
|
||||
#error "Make sure to to undefine ERROR (or just #include <kj/windows-sanity.h>) before this file" |
||||
#endif |
||||
|
||||
namespace kj { |
||||
|
||||
#if _MSC_VER |
||||
// MSVC does __VA_ARGS__ differently from GCC:
|
||||
// - A trailing comma before an empty __VA_ARGS__ is removed automatically, whereas GCC wants
|
||||
// you to request this behavior with "##__VA_ARGS__".
|
||||
// - If __VA_ARGS__ is passed directly as an argument to another macro, it will be treated as a
|
||||
// *single* argument rather than an argument list. This can be worked around by wrapping the
|
||||
// outer macro call in KJ_EXPAND(), which appraently forces __VA_ARGS__ to be expanded before
|
||||
// the macro is evaluated. I don't understand the C preprocessor.
|
||||
// - Using "#__VA_ARGS__" to stringify __VA_ARGS__ expands to zero tokens when __VA_ARGS__ is
|
||||
// empty, rather than expanding to an empty string literal. We can work around by concatenating
|
||||
// with an empty string literal.
|
||||
|
||||
#define KJ_EXPAND(X) X |
||||
|
||||
#define KJ_LOG(severity, ...) \ |
||||
if (!::kj::_::Debug::shouldLog(::kj::LogSeverity::severity)) {} else \
|
||||
::kj::_::Debug::log(__FILE__, __LINE__, ::kj::LogSeverity::severity, \
|
||||
"" #__VA_ARGS__, __VA_ARGS__) |
||||
|
||||
#define KJ_DBG(...) KJ_EXPAND(KJ_LOG(DBG, __VA_ARGS__)) |
||||
|
||||
#define KJ_REQUIRE(cond, ...) \ |
||||
if (KJ_LIKELY(cond)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
#cond, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_FAIL_REQUIRE(...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
nullptr, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_SYSCALL(call, ...) \ |
||||
if (auto _kjSyscallResult = ::kj::_::Debug::syscall([&](){return (call);}, false)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
_kjSyscallResult.getErrorNumber(), #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_NONBLOCKING_SYSCALL(call, ...) \ |
||||
if (auto _kjSyscallResult = ::kj::_::Debug::syscall([&](){return (call);}, true)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
_kjSyscallResult.getErrorNumber(), #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_FAIL_SYSCALL(code, errorNumber, ...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
errorNumber, code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#if _WIN32 |
||||
|
||||
#define KJ_WIN32(call, ...) \ |
||||
if (::kj::_::Debug::isWin32Success(call)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::getWin32Error(), #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_WINSOCK(call, ...) \ |
||||
if ((call) != SOCKET_ERROR) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::getWin32Error(), #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_FAIL_WIN32(code, errorNumber, ...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::Win32Error(errorNumber), code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
#endif |
||||
|
||||
#define KJ_UNIMPLEMENTED(...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::UNIMPLEMENTED, \
|
||||
nullptr, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) |
||||
|
||||
// TODO(msvc): MSVC mis-deduces `ContextImpl<decltype(func)>` as `ContextImpl<int>` in some edge
|
||||
// cases, such as inside nested lambdas inside member functions. Wrapping the type in
|
||||
// `decltype(instance<...>())` helps it deduce the context function's type correctly.
|
||||
#define KJ_CONTEXT(...) \ |
||||
auto KJ_UNIQUE_NAME(_kjContextFunc) = [&]() -> ::kj::_::Debug::Context::Value { \
|
||||
return ::kj::_::Debug::Context::Value(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::makeDescription("" #__VA_ARGS__, __VA_ARGS__)); \
|
||||
}; \
|
||||
decltype(::kj::instance<::kj::_::Debug::ContextImpl<decltype(KJ_UNIQUE_NAME(_kjContextFunc))>>()) \
|
||||
KJ_UNIQUE_NAME(_kjContext)(KJ_UNIQUE_NAME(_kjContextFunc)) |
||||
|
||||
#define KJ_REQUIRE_NONNULL(value, ...) \ |
||||
(*[&] { \
|
||||
auto _kj_result = ::kj::_::readMaybe(value); \
|
||||
if (KJ_UNLIKELY(!_kj_result)) { \
|
||||
::kj::_::Debug::Fault(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
#value " != nullptr", "" #__VA_ARGS__, __VA_ARGS__).fatal(); \ |
||||
} \
|
||||
return _kj_result; \
|
||||
}()) |
||||
|
||||
#define KJ_EXCEPTION(type, ...) \ |
||||
::kj::Exception(::kj::Exception::Type::type, __FILE__, __LINE__, \
|
||||
::kj::_::Debug::makeDescription("" #__VA_ARGS__, __VA_ARGS__)) |
||||
|
||||
#else |
||||
|
||||
#define KJ_LOG(severity, ...) \ |
||||
if (!::kj::_::Debug::shouldLog(::kj::LogSeverity::severity)) {} else \
|
||||
::kj::_::Debug::log(__FILE__, __LINE__, ::kj::LogSeverity::severity, \
|
||||
#__VA_ARGS__, ##__VA_ARGS__) |
||||
|
||||
#define KJ_DBG(...) KJ_LOG(DBG, ##__VA_ARGS__) |
||||
|
||||
#define KJ_REQUIRE(cond, ...) \ |
||||
if (KJ_LIKELY(cond)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
#cond, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_FAIL_REQUIRE(...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
nullptr, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_SYSCALL(call, ...) \ |
||||
if (auto _kjSyscallResult = ::kj::_::Debug::syscall([&](){return (call);}, false)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
_kjSyscallResult.getErrorNumber(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_NONBLOCKING_SYSCALL(call, ...) \ |
||||
if (auto _kjSyscallResult = ::kj::_::Debug::syscall([&](){return (call);}, true)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
_kjSyscallResult.getErrorNumber(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_FAIL_SYSCALL(code, errorNumber, ...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
errorNumber, code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#if _WIN32 |
||||
|
||||
#define KJ_WIN32(call, ...) \ |
||||
if (::kj::_::Debug::isWin32Success(call)) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::getWin32Error(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_WINSOCK(call, ...) \ |
||||
if ((call) != SOCKET_ERROR) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::getWin32Error(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_FAIL_WIN32(code, errorNumber, ...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::Win32Error(errorNumber), code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#endif |
||||
|
||||
#define KJ_UNIMPLEMENTED(...) \ |
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::UNIMPLEMENTED, \
|
||||
nullptr, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) |
||||
|
||||
#define KJ_CONTEXT(...) \ |
||||
auto KJ_UNIQUE_NAME(_kjContextFunc) = [&]() -> ::kj::_::Debug::Context::Value { \
|
||||
return ::kj::_::Debug::Context::Value(__FILE__, __LINE__, \
|
||||
::kj::_::Debug::makeDescription(#__VA_ARGS__, ##__VA_ARGS__)); \
|
||||
}; \
|
||||
::kj::_::Debug::ContextImpl<decltype(KJ_UNIQUE_NAME(_kjContextFunc))> \
|
||||
KJ_UNIQUE_NAME(_kjContext)(KJ_UNIQUE_NAME(_kjContextFunc)) |
||||
|
||||
#define KJ_REQUIRE_NONNULL(value, ...) \ |
||||
(*({ \
|
||||
auto _kj_result = ::kj::_::readMaybe(value); \
|
||||
if (KJ_UNLIKELY(!_kj_result)) { \
|
||||
::kj::_::Debug::Fault(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
#value " != nullptr", #__VA_ARGS__, ##__VA_ARGS__).fatal(); \ |
||||
} \
|
||||
kj::mv(_kj_result); \
|
||||
})) |
||||
|
||||
#define KJ_EXCEPTION(type, ...) \ |
||||
::kj::Exception(::kj::Exception::Type::type, __FILE__, __LINE__, \
|
||||
::kj::_::Debug::makeDescription(#__VA_ARGS__, ##__VA_ARGS__)) |
||||
|
||||
#endif |
||||
|
||||
#define KJ_SYSCALL_HANDLE_ERRORS(call) \ |
||||
if (int _kjSyscallError = ::kj::_::Debug::syscallError([&](){return (call);}, false)) \
|
||||
switch (int error = _kjSyscallError) |
||||
// Like KJ_SYSCALL, but doesn't throw. Instead, the block after the macro is a switch block on the
|
||||
// error. Additionally, the int value `error` is defined within the block. So you can do:
|
||||
//
|
||||
// KJ_SYSCALL_HANDLE_ERRORS(foo()) {
|
||||
// case ENOENT:
|
||||
// handleNoSuchFile();
|
||||
// break;
|
||||
// case EEXIST:
|
||||
// handleExists();
|
||||
// break;
|
||||
// default:
|
||||
// KJ_FAIL_SYSCALL("foo()", error);
|
||||
// } else {
|
||||
// handleSuccessCase();
|
||||
// }
|
||||
|
||||
#define KJ_ASSERT KJ_REQUIRE |
||||
#define KJ_FAIL_ASSERT KJ_FAIL_REQUIRE |
||||
#define KJ_ASSERT_NONNULL KJ_REQUIRE_NONNULL |
||||
// Use "ASSERT" in place of "REQUIRE" when the problem is local to the immediate surrounding code.
|
||||
// That is, if the assert ever fails, it indicates that the immediate surrounding code is broken.
|
||||
|
||||
#ifdef KJ_DEBUG |
||||
#define KJ_DLOG KJ_LOG |
||||
#define KJ_DASSERT KJ_ASSERT |
||||
#define KJ_DREQUIRE KJ_REQUIRE |
||||
#else |
||||
#define KJ_DLOG(...) do {} while (false) |
||||
#define KJ_DASSERT(...) do {} while (false) |
||||
#define KJ_DREQUIRE(...) do {} while (false) |
||||
#endif |
||||
|
||||
namespace _ { // private
|
||||
|
||||
class Debug { |
||||
public: |
||||
Debug() = delete; |
||||
|
||||
typedef LogSeverity Severity; // backwards-compatibility
|
||||
|
||||
#if _WIN32 |
||||
struct Win32Error { |
||||
// Hack for overloading purposes.
|
||||
uint number; |
||||
inline explicit Win32Error(uint number): number(number) {} |
||||
}; |
||||
#endif |
||||
|
||||
static inline bool shouldLog(LogSeverity severity) { return severity >= minSeverity; } |
||||
// Returns whether messages of the given severity should be logged.
|
||||
|
||||
static inline void setLogLevel(LogSeverity severity) { minSeverity = severity; } |
||||
// Set the minimum message severity which will be logged.
|
||||
//
|
||||
// TODO(someday): Expose publicly.
|
||||
|
||||
template <typename... Params> |
||||
static void log(const char* file, int line, LogSeverity severity, const char* macroArgs, |
||||
Params&&... params); |
||||
|
||||
class Fault { |
||||
public: |
||||
template <typename Code, typename... Params> |
||||
Fault(const char* file, int line, Code code, |
||||
const char* condition, const char* macroArgs, Params&&... params); |
||||
Fault(const char* file, int line, Exception::Type type, |
||||
const char* condition, const char* macroArgs); |
||||
Fault(const char* file, int line, int osErrorNumber, |
||||
const char* condition, const char* macroArgs); |
||||
#if _WIN32 |
||||
Fault(const char* file, int line, Win32Error osErrorNumber, |
||||
const char* condition, const char* macroArgs); |
||||
#endif |
||||
~Fault() noexcept(false); |
||||
|
||||
KJ_NOINLINE KJ_NORETURN(void fatal()); |
||||
// Throw the exception.
|
||||
|
||||
private: |
||||
void init(const char* file, int line, Exception::Type type, |
||||
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); |
||||
void init(const char* file, int line, int osErrorNumber, |
||||
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); |
||||
#if _WIN32 |
||||
void init(const char* file, int line, Win32Error osErrorNumber, |
||||
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); |
||||
#endif |
||||
|
||||
Exception* exception; |
||||
}; |
||||
|
||||
class SyscallResult { |
||||
public: |
||||
inline SyscallResult(int errorNumber): errorNumber(errorNumber) {} |
||||
inline operator void*() { return errorNumber == 0 ? this : nullptr; } |
||||
inline int getErrorNumber() { return errorNumber; } |
||||
|
||||
private: |
||||
int errorNumber; |
||||
}; |
||||
|
||||
template <typename Call> |
||||
static SyscallResult syscall(Call&& call, bool nonblocking); |
||||
template <typename Call> |
||||
static int syscallError(Call&& call, bool nonblocking); |
||||
|
||||
#if _WIN32 |
||||
static bool isWin32Success(int boolean); |
||||
static bool isWin32Success(void* handle); |
||||
static Win32Error getWin32Error(); |
||||
#endif |
||||
|
||||
class Context: public ExceptionCallback { |
||||
public: |
||||
Context(); |
||||
KJ_DISALLOW_COPY(Context); |
||||
virtual ~Context() noexcept(false); |
||||
|
||||
struct Value { |
||||
const char* file; |
||||
int line; |
||||
String description; |
||||
|
||||
inline Value(const char* file, int line, String&& description) |
||||
: file(file), line(line), description(mv(description)) {} |
||||
}; |
||||
|
||||
virtual Value evaluate() = 0; |
||||
|
||||
virtual void onRecoverableException(Exception&& exception) override; |
||||
virtual void onFatalException(Exception&& exception) override; |
||||
virtual void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, |
||||
String&& text) override; |
||||
|
||||
private: |
||||
bool logged; |
||||
Maybe<Value> value; |
||||
|
||||
Value ensureInitialized(); |
||||
}; |
||||
|
||||
template <typename Func> |
||||
class ContextImpl: public Context { |
||||
public: |
||||
inline ContextImpl(Func& func): func(func) {} |
||||
KJ_DISALLOW_COPY(ContextImpl); |
||||
|
||||
Value evaluate() override { |
||||
return func(); |
||||
} |
||||
private: |
||||
Func& func; |
||||
}; |
||||
|
||||
template <typename... Params> |
||||
static String makeDescription(const char* macroArgs, Params&&... params); |
||||
|
||||
private: |
||||
static LogSeverity minSeverity; |
||||
|
||||
static void logInternal(const char* file, int line, LogSeverity severity, const char* macroArgs, |
||||
ArrayPtr<String> argValues); |
||||
static String makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues); |
||||
|
||||
static int getOsErrorNumber(bool nonblocking); |
||||
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
|
||||
}; |
||||
|
||||
template <typename... Params> |
||||
void Debug::log(const char* file, int line, LogSeverity severity, const char* macroArgs, |
||||
Params&&... params) { |
||||
String argValues[sizeof...(Params)] = {str(params)...}; |
||||
logInternal(file, line, severity, macroArgs, arrayPtr(argValues, sizeof...(Params))); |
||||
} |
||||
|
||||
template <> |
||||
inline void Debug::log<>(const char* file, int line, LogSeverity severity, const char* macroArgs) { |
||||
logInternal(file, line, severity, macroArgs, nullptr); |
||||
} |
||||
|
||||
template <typename Code, typename... Params> |
||||
Debug::Fault::Fault(const char* file, int line, Code code, |
||||
const char* condition, const char* macroArgs, Params&&... params) |
||||
: exception(nullptr) { |
||||
String argValues[sizeof...(Params)] = {str(params)...}; |
||||
init(file, line, code, condition, macroArgs, |
||||
arrayPtr(argValues, sizeof...(Params))); |
||||
} |
||||
|
||||
inline Debug::Fault::Fault(const char* file, int line, int osErrorNumber, |
||||
const char* condition, const char* macroArgs) |
||||
: exception(nullptr) { |
||||
init(file, line, osErrorNumber, condition, macroArgs, nullptr); |
||||
} |
||||
|
||||
inline Debug::Fault::Fault(const char* file, int line, kj::Exception::Type type, |
||||
const char* condition, const char* macroArgs) |
||||
: exception(nullptr) { |
||||
init(file, line, type, condition, macroArgs, nullptr); |
||||
} |
||||
|
||||
#if _WIN32 |
||||
inline Debug::Fault::Fault(const char* file, int line, Win32Error osErrorNumber, |
||||
const char* condition, const char* macroArgs) |
||||
: exception(nullptr) { |
||||
init(file, line, osErrorNumber, condition, macroArgs, nullptr); |
||||
} |
||||
|
||||
inline bool Debug::isWin32Success(int boolean) { |
||||
return boolean; |
||||
} |
||||
inline bool Debug::isWin32Success(void* handle) { |
||||
// Assume null and INVALID_HANDLE_VALUE mean failure.
|
||||
return handle != nullptr && handle != (void*)-1; |
||||
} |
||||
#endif |
||||
|
||||
template <typename Call> |
||||
Debug::SyscallResult Debug::syscall(Call&& call, bool nonblocking) { |
||||
while (call() < 0) { |
||||
int errorNum = getOsErrorNumber(nonblocking); |
||||
// getOsErrorNumber() returns -1 to indicate EINTR.
|
||||
// Also, if nonblocking is true, then it returns 0 on EAGAIN, which will then be treated as a
|
||||
// non-error.
|
||||
if (errorNum != -1) { |
||||
return SyscallResult(errorNum); |
||||
} |
||||
} |
||||
return SyscallResult(0); |
||||
} |
||||
|
||||
template <typename Call> |
||||
int Debug::syscallError(Call&& call, bool nonblocking) { |
||||
while (call() < 0) { |
||||
int errorNum = getOsErrorNumber(nonblocking); |
||||
// getOsErrorNumber() returns -1 to indicate EINTR.
|
||||
// Also, if nonblocking is true, then it returns 0 on EAGAIN, which will then be treated as a
|
||||
// non-error.
|
||||
if (errorNum != -1) { |
||||
return errorNum; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
template <typename... Params> |
||||
String Debug::makeDescription(const char* macroArgs, Params&&... params) { |
||||
String argValues[sizeof...(Params)] = {str(params)...}; |
||||
return makeDescriptionInternal(macroArgs, arrayPtr(argValues, sizeof...(Params))); |
||||
} |
||||
|
||||
template <> |
||||
inline String Debug::makeDescription<>(const char* macroArgs) { |
||||
return makeDescriptionInternal(macroArgs, nullptr); |
||||
} |
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_DEBUG_H_
|
@ -0,0 +1,363 @@ |
||||
// 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 KJ_EXCEPTION_H_ |
||||
#define KJ_EXCEPTION_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "memory.h" |
||||
#include "array.h" |
||||
#include "string.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class ExceptionImpl; |
||||
|
||||
class Exception { |
||||
// Exception thrown in case of fatal errors.
|
||||
//
|
||||
// Actually, a subclass of this which also implements std::exception will be thrown, but we hide
|
||||
// that fact from the interface to avoid #including <exception>.
|
||||
|
||||
public: |
||||
enum class Type { |
||||
// What kind of failure?
|
||||
|
||||
FAILED = 0, |
||||
// Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this
|
||||
// error type.
|
||||
|
||||
OVERLOADED = 1, |
||||
// The call failed because of a temporary lack of resources. This could be space resources
|
||||
// (out of memory, out of disk space) or time resources (request queue overflow, operation
|
||||
// timed out).
|
||||
//
|
||||
// The operation might work if tried again, but it should NOT be repeated immediately as this
|
||||
// may simply exacerbate the problem.
|
||||
|
||||
DISCONNECTED = 2, |
||||
// The call required communication over a connection that has been lost. The callee will need
|
||||
// to re-establish connections and try again.
|
||||
|
||||
UNIMPLEMENTED = 3 |
||||
// The requested method is not implemented. The caller may wish to revert to a fallback
|
||||
// approach based on other methods.
|
||||
|
||||
// IF YOU ADD A NEW VALUE:
|
||||
// - Update the stringifier.
|
||||
// - Update Cap'n Proto's RPC protocol's Exception.Type enum.
|
||||
}; |
||||
|
||||
Exception(Type type, const char* file, int line, String description = nullptr) noexcept; |
||||
Exception(Type type, String file, int line, String description = nullptr) noexcept; |
||||
Exception(const Exception& other) noexcept; |
||||
Exception(Exception&& other) = default; |
||||
~Exception() noexcept; |
||||
|
||||
const char* getFile() const { return file; } |
||||
int getLine() const { return line; } |
||||
Type getType() const { return type; } |
||||
StringPtr getDescription() const { return description; } |
||||
ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); } |
||||
|
||||
struct Context { |
||||
// Describes a bit about what was going on when the exception was thrown.
|
||||
|
||||
const char* file; |
||||
int line; |
||||
String description; |
||||
Maybe<Own<Context>> next; |
||||
|
||||
Context(const char* file, int line, String&& description, Maybe<Own<Context>>&& next) |
||||
: file(file), line(line), description(mv(description)), next(mv(next)) {} |
||||
Context(const Context& other) noexcept; |
||||
}; |
||||
|
||||
inline Maybe<const Context&> getContext() const { |
||||
KJ_IF_MAYBE(c, context) { |
||||
return **c; |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
void wrapContext(const char* file, int line, String&& description); |
||||
// Wraps the context in a new node. This becomes the head node returned by getContext() -- it
|
||||
// is expected that contexts will be added in reverse order as the exception passes up the
|
||||
// callback stack.
|
||||
|
||||
KJ_NOINLINE void extendTrace(uint ignoreCount); |
||||
// Append the current stack trace to the exception's trace, ignoring the first `ignoreCount`
|
||||
// frames (see `getStackTrace()` for discussion of `ignoreCount`).
|
||||
|
||||
KJ_NOINLINE void truncateCommonTrace(); |
||||
// Remove the part of the stack trace which the exception shares with the caller of this method.
|
||||
// This is used by the async library to remove the async infrastructure from the stack trace
|
||||
// before replacing it with the async trace.
|
||||
|
||||
void addTrace(void* ptr); |
||||
// Append the given pointer to the backtrace, if it is not already full. This is used by the
|
||||
// async library to trace through the promise chain that led to the exception.
|
||||
|
||||
private: |
||||
String ownFile; |
||||
const char* file; |
||||
int line; |
||||
Type type; |
||||
String description; |
||||
Maybe<Own<Context>> context; |
||||
void* trace[32]; |
||||
uint traceCount; |
||||
|
||||
friend class ExceptionImpl; |
||||
}; |
||||
|
||||
StringPtr KJ_STRINGIFY(Exception::Type type); |
||||
String KJ_STRINGIFY(const Exception& e); |
||||
|
||||
// =======================================================================================
|
||||
|
||||
enum class LogSeverity { |
||||
INFO, // Information describing what the code is up to, which users may request to see
|
||||
// with a flag like `--verbose`. Does not indicate a problem. Not printed by
|
||||
// default; you must call setLogLevel(INFO) to enable.
|
||||
WARNING, // A problem was detected but execution can continue with correct output.
|
||||
ERROR, // Something is wrong, but execution can continue with garbage output.
|
||||
FATAL, // Something went wrong, and execution cannot continue.
|
||||
DBG // Temporary debug logging. See KJ_DBG.
|
||||
|
||||
// Make sure to update the stringifier if you add a new severity level.
|
||||
}; |
||||
|
||||
StringPtr KJ_STRINGIFY(LogSeverity severity); |
||||
|
||||
class ExceptionCallback { |
||||
// If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
|
||||
// to perform your own exception handling. For example, a reasonable thing to do is to have
|
||||
// onRecoverableException() set a flag indicating that an error occurred, and then check for that
|
||||
// flag just before writing to storage and/or returning results to the user. If the flag is set,
|
||||
// discard whatever you have and return an error instead.
|
||||
//
|
||||
// ExceptionCallbacks must always be allocated on the stack. When an exception is thrown, the
|
||||
// newest ExceptionCallback on the calling thread's stack is called. The default implementation
|
||||
// of each method calls the next-oldest ExceptionCallback for that thread. Thus the callbacks
|
||||
// behave a lot like try/catch blocks, except that they are called before any stack unwinding
|
||||
// occurs.
|
||||
|
||||
public: |
||||
ExceptionCallback(); |
||||
KJ_DISALLOW_COPY(ExceptionCallback); |
||||
virtual ~ExceptionCallback() noexcept(false); |
||||
|
||||
virtual void onRecoverableException(Exception&& exception); |
||||
// Called when an exception has been raised, but the calling code has the ability to continue by
|
||||
// producing garbage output. This method _should_ throw the exception, but is allowed to simply
|
||||
// return if garbage output is acceptable.
|
||||
//
|
||||
// The global default implementation throws an exception unless the library was compiled with
|
||||
// -fno-exceptions, in which case it logs an error and returns.
|
||||
|
||||
virtual void onFatalException(Exception&& exception); |
||||
// Called when an exception has been raised and the calling code cannot continue. If this method
|
||||
// returns normally, abort() will be called. The method must throw the exception to avoid
|
||||
// aborting.
|
||||
//
|
||||
// The global default implementation throws an exception unless the library was compiled with
|
||||
// -fno-exceptions, in which case it logs an error and returns.
|
||||
|
||||
virtual void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, |
||||
String&& text); |
||||
// Called when something wants to log some debug text. `contextDepth` indicates how many levels
|
||||
// of context the message passed through; it may make sense to indent the message accordingly.
|
||||
//
|
||||
// The global default implementation writes the text to stderr.
|
||||
|
||||
enum class StackTraceMode { |
||||
FULL, |
||||
// Stringifying a stack trace will attempt to determine source file and line numbers. This may
|
||||
// be expensive. For example, on Linux, this shells out to `addr2line`.
|
||||
//
|
||||
// This is the default in debug builds.
|
||||
|
||||
ADDRESS_ONLY, |
||||
// Stringifying a stack trace will only generate a list of code addresses.
|
||||
//
|
||||
// This is the default in release builds.
|
||||
|
||||
NONE |
||||
// Generating a stack trace will always return an empty array.
|
||||
//
|
||||
// This avoids ever unwinding the stack. On Windows in particular, the stack unwinding library
|
||||
// has been observed to be pretty slow, so exception-heavy code might benefit significantly
|
||||
// from this setting. (But exceptions should be rare...)
|
||||
}; |
||||
|
||||
virtual StackTraceMode stackTraceMode(); |
||||
// Returns the current preferred stack trace mode.
|
||||
|
||||
protected: |
||||
ExceptionCallback& next; |
||||
|
||||
private: |
||||
ExceptionCallback(ExceptionCallback& next); |
||||
|
||||
class RootExceptionCallback; |
||||
friend ExceptionCallback& getExceptionCallback(); |
||||
}; |
||||
|
||||
ExceptionCallback& getExceptionCallback(); |
||||
// Returns the current exception callback.
|
||||
|
||||
KJ_NOINLINE KJ_NORETURN(void throwFatalException(kj::Exception&& exception, uint ignoreCount = 0)); |
||||
// Invoke the exception callback to throw the given fatal exception. If the exception callback
|
||||
// returns, abort.
|
||||
|
||||
KJ_NOINLINE void throwRecoverableException(kj::Exception&& exception, uint ignoreCount = 0); |
||||
// Invoke the exception callback to throw the given recoverable exception. If the exception
|
||||
// callback returns, return normally.
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace _ { class Runnable; } |
||||
|
||||
template <typename Func> |
||||
Maybe<Exception> runCatchingExceptions(Func&& func) noexcept; |
||||
// Executes the given function (usually, a lambda returning nothing) catching any exceptions that
|
||||
// are thrown. Returns the Exception if there was one, or null if the operation completed normally.
|
||||
// Non-KJ exceptions will be wrapped.
|
||||
//
|
||||
// If exception are disabled (e.g. with -fno-exceptions), this will still detect whether any
|
||||
// recoverable exceptions occurred while running the function and will return those.
|
||||
|
||||
class UnwindDetector { |
||||
// Utility for detecting when a destructor is called due to unwind. Useful for:
|
||||
// - Avoiding throwing exceptions in this case, which would terminate the program.
|
||||
// - Detecting whether to commit or roll back a transaction.
|
||||
//
|
||||
// To use this class, either inherit privately from it or declare it as a member. The detector
|
||||
// works by comparing the exception state against that when the constructor was called, so for
|
||||
// an object that was actually constructed during exception unwind, it will behave as if no
|
||||
// unwind is taking place. This is usually the desired behavior.
|
||||
|
||||
public: |
||||
UnwindDetector(); |
||||
|
||||
bool isUnwinding() const; |
||||
// Returns true if the current thread is in a stack unwind that it wasn't in at the time the
|
||||
// object was constructed.
|
||||
|
||||
template <typename Func> |
||||
void catchExceptionsIfUnwinding(Func&& func) const; |
||||
// Runs the given function (e.g., a lambda). If isUnwinding() is true, any exceptions are
|
||||
// caught and treated as secondary faults, meaning they are considered to be side-effects of the
|
||||
// exception that is unwinding the stack. Otherwise, exceptions are passed through normally.
|
||||
|
||||
private: |
||||
uint uncaughtCount; |
||||
|
||||
void catchExceptionsAsSecondaryFaults(_::Runnable& runnable) const; |
||||
}; |
||||
|
||||
namespace _ { // private
|
||||
|
||||
class Runnable { |
||||
public: |
||||
virtual void run() = 0; |
||||
}; |
||||
|
||||
template <typename Func> |
||||
class RunnableImpl: public Runnable { |
||||
public: |
||||
RunnableImpl(Func&& func): func(kj::mv(func)) {} |
||||
void run() override { |
||||
func(); |
||||
} |
||||
private: |
||||
Func func; |
||||
}; |
||||
|
||||
Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename Func> |
||||
Maybe<Exception> runCatchingExceptions(Func&& func) noexcept { |
||||
_::RunnableImpl<Decay<Func>> runnable(kj::fwd<Func>(func)); |
||||
return _::runCatchingExceptions(runnable); |
||||
} |
||||
|
||||
template <typename Func> |
||||
void UnwindDetector::catchExceptionsIfUnwinding(Func&& func) const { |
||||
if (isUnwinding()) { |
||||
_::RunnableImpl<Decay<Func>> runnable(kj::fwd<Func>(func)); |
||||
catchExceptionsAsSecondaryFaults(runnable); |
||||
} else { |
||||
func(); |
||||
} |
||||
} |
||||
|
||||
#define KJ_ON_SCOPE_SUCCESS(code) \ |
||||
::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \
|
||||
KJ_DEFER(if (!KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; }) |
||||
// Runs `code` if the current scope is exited normally (not due to an exception).
|
||||
|
||||
#define KJ_ON_SCOPE_FAILURE(code) \ |
||||
::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \
|
||||
KJ_DEFER(if (KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; }) |
||||
// Runs `code` if the current scope is exited due to an exception.
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
KJ_NOINLINE ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount); |
||||
// Attempt to get the current stack trace, returning a list of pointers to instructions. The
|
||||
// returned array is a slice of `space`. Provide a larger `space` to get a deeper stack trace.
|
||||
// If the platform doesn't support stack traces, returns an empty array.
|
||||
//
|
||||
// `ignoreCount` items will be truncated from the front of the trace. This is useful for chopping
|
||||
// off a prefix of the trace that is uninteresting to the developer because it's just locations
|
||||
// inside the debug infrastructure that is requesting the trace. Be careful to mark functions as
|
||||
// KJ_NOINLINE if you intend to count them in `ignoreCount`. Note that, unfortunately, the
|
||||
// ignored entries will still waste space in the `space` array (and the returned array's `begin()`
|
||||
// is never exactly equal to `space.begin()` due to this effect, even if `ignoreCount` is zero
|
||||
// since `getStackTrace()` needs to ignore its own internal frames).
|
||||
|
||||
String stringifyStackTrace(ArrayPtr<void* const>); |
||||
// Convert the stack trace to a string with file names and line numbers. This may involve executing
|
||||
// suprocesses.
|
||||
|
||||
String getStackTrace(); |
||||
// Get a stack trace right now and stringify it. Useful for debugging.
|
||||
|
||||
void printStackTraceOnCrash(); |
||||
// Registers signal handlers on common "crash" signals like SIGSEGV that will (attempt to) print
|
||||
// a stack trace. You should call this as early as possible on program startup. Programs using
|
||||
// KJ_MAIN get this automatically.
|
||||
|
||||
kj::StringPtr trimSourceFilename(kj::StringPtr filename); |
||||
// Given a source code file name, trim off noisy prefixes like "src/" or
|
||||
// "/ekam-provider/canonical/".
|
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_EXCEPTION_H_
|
@ -0,0 +1,277 @@ |
||||
// 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 KJ_FUNCTION_H_ |
||||
#define KJ_FUNCTION_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "memory.h" |
||||
|
||||
namespace kj { |
||||
|
||||
template <typename Signature> |
||||
class Function; |
||||
// Function wrapper using virtual-based polymorphism. Use this when template polymorphism is
|
||||
// not possible. You can, for example, accept a Function as a parameter:
|
||||
//
|
||||
// void setFilter(Function<bool(const Widget&)> filter);
|
||||
//
|
||||
// The caller of `setFilter()` may then pass any callable object as the parameter. The callable
|
||||
// object does not have to have the exact signature specified, just one that is "compatible" --
|
||||
// i.e. the return type is covariant and the parameters are contravariant.
|
||||
//
|
||||
// Unlike `std::function`, `kj::Function`s are movable but not copyable, just like `kj::Own`. This
|
||||
// is to avoid unexpected heap allocation or slow atomic reference counting.
|
||||
//
|
||||
// When a `Function` is constructed from an lvalue, it captures only a reference to the value.
|
||||
// When constructed from an rvalue, it invokes the value's move constructor. So, for example:
|
||||
//
|
||||
// struct AddN {
|
||||
// int n;
|
||||
// int operator(int i) { return i + n; }
|
||||
// }
|
||||
//
|
||||
// Function<int(int, int)> f1 = AddN{2};
|
||||
// // f1 owns an instance of AddN. It may safely be moved out
|
||||
// // of the local scope.
|
||||
//
|
||||
// AddN adder(2);
|
||||
// Function<int(int, int)> f2 = adder;
|
||||
// // f2 contains a reference to `adder`. Thus, it becomes invalid
|
||||
// // when `adder` goes out-of-scope.
|
||||
//
|
||||
// AddN adder2(2);
|
||||
// Function<int(int, int)> f3 = kj::mv(adder2);
|
||||
// // f3 owns an insatnce of AddN moved from `adder2`. f3 may safely
|
||||
// // be moved out of the local scope.
|
||||
//
|
||||
// Additionally, a Function may be bound to a class method using KJ_BIND_METHOD(object, methodName).
|
||||
// For example:
|
||||
//
|
||||
// class Printer {
|
||||
// public:
|
||||
// void print(int i);
|
||||
// void print(kj::StringPtr s);
|
||||
// };
|
||||
//
|
||||
// Printer p;
|
||||
//
|
||||
// Function<void(uint)> intPrinter = KJ_BIND_METHOD(p, print);
|
||||
// // Will call Printer::print(int).
|
||||
//
|
||||
// Function<void(const char*)> strPrinter = KJ_BIND_METHOD(p, print);
|
||||
// // Will call Printer::print(kj::StringPtr).
|
||||
//
|
||||
// Notice how KJ_BIND_METHOD is able to figure out which overload to use depending on the kind of
|
||||
// Function it is binding to.
|
||||
|
||||
template <typename Signature> |
||||
class ConstFunction; |
||||
// Like Function, but wraps a "const" (i.e. thread-safe) call.
|
||||
|
||||
template <typename Return, typename... Params> |
||||
class Function<Return(Params...)> { |
||||
public: |
||||
template <typename F> |
||||
inline Function(F&& f): impl(heap<Impl<F>>(kj::fwd<F>(f))) {} |
||||
Function() = default; |
||||
|
||||
// Make sure people don't accidentally end up wrapping a reference when they meant to return
|
||||
// a function.
|
||||
KJ_DISALLOW_COPY(Function); |
||||
Function(Function&) = delete; |
||||
Function& operator=(Function&) = delete; |
||||
template <typename T> Function(const Function<T>&) = delete; |
||||
template <typename T> Function& operator=(const Function<T>&) = delete; |
||||
template <typename T> Function(const ConstFunction<T>&) = delete; |
||||
template <typename T> Function& operator=(const ConstFunction<T>&) = delete; |
||||
Function(Function&&) = default; |
||||
Function& operator=(Function&&) = default; |
||||
|
||||
inline Return operator()(Params... params) { |
||||
return (*impl)(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
Function reference() { |
||||
// Forms a new Function of the same type that delegates to this Function by reference.
|
||||
// Therefore, this Function must outlive the returned Function, but otherwise they behave
|
||||
// exactly the same.
|
||||
|
||||
return *impl; |
||||
} |
||||
|
||||
private: |
||||
class Iface { |
||||
public: |
||||
virtual Return operator()(Params... params) = 0; |
||||
}; |
||||
|
||||
template <typename F> |
||||
class Impl final: public Iface { |
||||
public: |
||||
explicit Impl(F&& f): f(kj::fwd<F>(f)) {} |
||||
|
||||
Return operator()(Params... params) override { |
||||
return f(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
private: |
||||
F f; |
||||
}; |
||||
|
||||
Own<Iface> impl; |
||||
}; |
||||
|
||||
template <typename Return, typename... Params> |
||||
class ConstFunction<Return(Params...)> { |
||||
public: |
||||
template <typename F> |
||||
inline ConstFunction(F&& f): impl(heap<Impl<F>>(kj::fwd<F>(f))) {} |
||||
ConstFunction() = default; |
||||
|
||||
// Make sure people don't accidentally end up wrapping a reference when they meant to return
|
||||
// a function.
|
||||
KJ_DISALLOW_COPY(ConstFunction); |
||||
ConstFunction(ConstFunction&) = delete; |
||||
ConstFunction& operator=(ConstFunction&) = delete; |
||||
template <typename T> ConstFunction(const ConstFunction<T>&) = delete; |
||||
template <typename T> ConstFunction& operator=(const ConstFunction<T>&) = delete; |
||||
template <typename T> ConstFunction(const Function<T>&) = delete; |
||||
template <typename T> ConstFunction& operator=(const Function<T>&) = delete; |
||||
ConstFunction(ConstFunction&&) = default; |
||||
ConstFunction& operator=(ConstFunction&&) = default; |
||||
|
||||
inline Return operator()(Params... params) const { |
||||
return (*impl)(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
ConstFunction reference() const { |
||||
// Forms a new ConstFunction of the same type that delegates to this ConstFunction by reference.
|
||||
// Therefore, this ConstFunction must outlive the returned ConstFunction, but otherwise they
|
||||
// behave exactly the same.
|
||||
|
||||
return *impl; |
||||
} |
||||
|
||||
private: |
||||
class Iface { |
||||
public: |
||||
virtual Return operator()(Params... params) const = 0; |
||||
}; |
||||
|
||||
template <typename F> |
||||
class Impl final: public Iface { |
||||
public: |
||||
explicit Impl(F&& f): f(kj::fwd<F>(f)) {} |
||||
|
||||
Return operator()(Params... params) const override { |
||||
return f(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
private: |
||||
F f; |
||||
}; |
||||
|
||||
Own<Iface> impl; |
||||
}; |
||||
|
||||
#if 1 |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, typename Signature, Signature method> |
||||
class BoundMethod; |
||||
|
||||
template <typename T, typename Return, typename... Params, Return (Decay<T>::*method)(Params...)> |
||||
class BoundMethod<T, Return (Decay<T>::*)(Params...), method> { |
||||
public: |
||||
BoundMethod(T&& t): t(kj::fwd<T>(t)) {} |
||||
|
||||
Return operator()(Params&&... params) { |
||||
return (t.*method)(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
private: |
||||
T t; |
||||
}; |
||||
|
||||
template <typename T, typename Return, typename... Params, |
||||
Return (Decay<T>::*method)(Params...) const> |
||||
class BoundMethod<T, Return (Decay<T>::*)(Params...) const, method> { |
||||
public: |
||||
BoundMethod(T&& t): t(kj::fwd<T>(t)) {} |
||||
|
||||
Return operator()(Params&&... params) const { |
||||
return (t.*method)(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
private: |
||||
T t; |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#define KJ_BIND_METHOD(obj, method) \ |
||||
::kj::_::BoundMethod<KJ_DECLTYPE_REF(obj), \
|
||||
decltype(&::kj::Decay<decltype(obj)>::method), \
|
||||
&::kj::Decay<decltype(obj)>::method>(obj) |
||||
// Macro that produces a functor object which forwards to the method `obj.name`. If `obj` is an
|
||||
// lvalue, the functor will hold a reference to it. If `obj` is an rvalue, the functor will
|
||||
// contain a copy (by move) of it.
|
||||
//
|
||||
// The current implementation requires that the method is not overloaded.
|
||||
//
|
||||
// TODO(someday): C++14's generic lambdas may be able to simplify this code considerably, and
|
||||
// probably make it work with overloaded methods.
|
||||
|
||||
#else |
||||
// Here's a better implementation of the above that doesn't work with GCC (but does with Clang)
|
||||
// because it uses a local class with a template method. Sigh. This implementation supports
|
||||
// overloaded methods.
|
||||
|
||||
#define KJ_BIND_METHOD(obj, method) \ |
||||
({ \
|
||||
typedef KJ_DECLTYPE_REF(obj) T; \
|
||||
class F { \
|
||||
public: \
|
||||
inline F(T&& t): t(::kj::fwd<T>(t)) {} \
|
||||
template <typename... Params> \
|
||||
auto operator()(Params&&... params) \
|
||||
-> decltype(::kj::instance<T>().method(::kj::fwd<Params>(params)...)) { \
|
||||
return t.method(::kj::fwd<Params>(params)...); \
|
||||
} \
|
||||
private: \
|
||||
T t; \
|
||||
}; \
|
||||
(F(obj)); \
|
||||
}) |
||||
// Macro that produces a functor object which forwards to the method `obj.name`. If `obj` is an
|
||||
// lvalue, the functor will hold a reference to it. If `obj` is an rvalue, the functor will
|
||||
// contain a copy (by move) of it.
|
||||
|
||||
#endif |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_FUNCTION_H_
|
@ -0,0 +1,419 @@ |
||||
// 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 KJ_IO_H_ |
||||
#define KJ_IO_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include <stddef.h> |
||||
#include "common.h" |
||||
#include "array.h" |
||||
#include "exception.h" |
||||
|
||||
namespace kj { |
||||
|
||||
// =======================================================================================
|
||||
// Abstract interfaces
|
||||
|
||||
class InputStream { |
||||
public: |
||||
virtual ~InputStream() noexcept(false); |
||||
|
||||
size_t read(void* buffer, size_t minBytes, size_t maxBytes); |
||||
// Reads at least minBytes and at most maxBytes, copying them into the given buffer. Returns
|
||||
// the size read. Throws an exception on errors. Implemented in terms of tryRead().
|
||||
//
|
||||
// maxBytes is the number of bytes the caller really wants, but minBytes is the minimum amount
|
||||
// needed by the caller before it can start doing useful processing. If the stream returns less
|
||||
// than maxBytes, the caller will usually call read() again later to get the rest. Returning
|
||||
// less than maxBytes is useful when it makes sense for the caller to parallelize processing
|
||||
// with I/O.
|
||||
//
|
||||
// Never blocks if minBytes is zero. If minBytes is zero and maxBytes is non-zero, this may
|
||||
// attempt a non-blocking read or may just return zero. To force a read, use a non-zero minBytes.
|
||||
// To detect EOF without throwing an exception, use tryRead().
|
||||
//
|
||||
// If the InputStream can't produce minBytes, it MUST throw an exception, as the caller is not
|
||||
// expected to understand how to deal with partial reads.
|
||||
|
||||
virtual size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) = 0; |
||||
// Like read(), but may return fewer than minBytes on EOF.
|
||||
|
||||
inline void read(void* buffer, size_t bytes) { read(buffer, bytes, bytes); } |
||||
// Convenience method for reading an exact number of bytes.
|
||||
|
||||
virtual void skip(size_t bytes); |
||||
// Skips past the given number of bytes, discarding them. The default implementation read()s
|
||||
// into a scratch buffer.
|
||||
}; |
||||
|
||||
class OutputStream { |
||||
public: |
||||
virtual ~OutputStream() noexcept(false); |
||||
|
||||
virtual void write(const void* buffer, size_t size) = 0; |
||||
// Always writes the full size. Throws exception on error.
|
||||
|
||||
virtual void write(ArrayPtr<const ArrayPtr<const byte>> pieces); |
||||
// Equivalent to write()ing each byte array in sequence, which is what the default implementation
|
||||
// does. Override if you can do something better, e.g. use writev() to do the write in a single
|
||||
// syscall.
|
||||
}; |
||||
|
||||
class BufferedInputStream: public InputStream { |
||||
// An input stream which buffers some bytes in memory to reduce system call overhead.
|
||||
// - OR -
|
||||
// An input stream that actually reads from some in-memory data structure and wants to give its
|
||||
// caller a direct pointer to that memory to potentially avoid a copy.
|
||||
|
||||
public: |
||||
virtual ~BufferedInputStream() noexcept(false); |
||||
|
||||
ArrayPtr<const byte> getReadBuffer(); |
||||
// Get a direct pointer into the read buffer, which contains the next bytes in the input. If the
|
||||
// caller consumes any bytes, it should then call skip() to indicate this. This always returns a
|
||||
// non-empty buffer or throws an exception. Implemented in terms of tryGetReadBuffer().
|
||||
|
||||
virtual ArrayPtr<const byte> tryGetReadBuffer() = 0; |
||||
// Like getReadBuffer() but may return an empty buffer on EOF.
|
||||
}; |
||||
|
||||
class BufferedOutputStream: public OutputStream { |
||||
// An output stream which buffers some bytes in memory to reduce system call overhead.
|
||||
// - OR -
|
||||
// An output stream that actually writes into some in-memory data structure and wants to give its
|
||||
// caller a direct pointer to that memory to potentially avoid a copy.
|
||||
|
||||
public: |
||||
virtual ~BufferedOutputStream() noexcept(false); |
||||
|
||||
virtual ArrayPtr<byte> getWriteBuffer() = 0; |
||||
// Get a direct pointer into the write buffer. The caller may choose to fill in some prefix of
|
||||
// this buffer and then pass it to write(), in which case write() may avoid a copy. It is
|
||||
// incorrect to pass to write any slice of this buffer which is not a prefix.
|
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Buffered streams implemented as wrappers around regular streams
|
||||
|
||||
class BufferedInputStreamWrapper: public BufferedInputStream { |
||||
// Implements BufferedInputStream in terms of an InputStream.
|
||||
//
|
||||
// Note that the underlying stream's position is unpredictable once the wrapper is destroyed,
|
||||
// unless the entire stream was consumed. To read a predictable number of bytes in a buffered
|
||||
// way without going over, you'd need this wrapper to wrap some other wrapper which itself
|
||||
// implements an artificial EOF at the desired point. Such a stream should be trivial to write
|
||||
// but is not provided by the library at this time.
|
||||
|
||||
public: |
||||
explicit BufferedInputStreamWrapper(InputStream& inner, ArrayPtr<byte> buffer = nullptr); |
||||
// Creates a buffered stream wrapping the given non-buffered stream. No guarantee is made about
|
||||
// the position of the inner stream after a buffered wrapper has been created unless the entire
|
||||
// input is read.
|
||||
//
|
||||
// If the second parameter is non-null, the stream uses the given buffer instead of allocating
|
||||
// its own. This may improve performance if the buffer can be reused.
|
||||
|
||||
KJ_DISALLOW_COPY(BufferedInputStreamWrapper); |
||||
~BufferedInputStreamWrapper() noexcept(false); |
||||
|
||||
// implements BufferedInputStream ----------------------------------
|
||||
ArrayPtr<const byte> tryGetReadBuffer() override; |
||||
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override; |
||||
void skip(size_t bytes) override; |
||||
|
||||
private: |
||||
InputStream& inner; |
||||
Array<byte> ownedBuffer; |
||||
ArrayPtr<byte> buffer; |
||||
ArrayPtr<byte> bufferAvailable; |
||||
}; |
||||
|
||||
class BufferedOutputStreamWrapper: public BufferedOutputStream { |
||||
// Implements BufferedOutputStream in terms of an OutputStream. Note that writes to the
|
||||
// underlying stream may be delayed until flush() is called or the wrapper is destroyed.
|
||||
|
||||
public: |
||||
explicit BufferedOutputStreamWrapper(OutputStream& inner, ArrayPtr<byte> buffer = nullptr); |
||||
// Creates a buffered stream wrapping the given non-buffered stream.
|
||||
//
|
||||
// If the second parameter is non-null, the stream uses the given buffer instead of allocating
|
||||
// its own. This may improve performance if the buffer can be reused.
|
||||
|
||||
KJ_DISALLOW_COPY(BufferedOutputStreamWrapper); |
||||
~BufferedOutputStreamWrapper() noexcept(false); |
||||
|
||||
void flush(); |
||||
// Force the wrapper to write any remaining bytes in its buffer to the inner stream. Note that
|
||||
// this only flushes this object's buffer; this object has no idea how to flush any other buffers
|
||||
// that may be present in the underlying stream.
|
||||
|
||||
// implements BufferedOutputStream ---------------------------------
|
||||
ArrayPtr<byte> getWriteBuffer() override; |
||||
void write(const void* buffer, size_t size) override; |
||||
|
||||
private: |
||||
OutputStream& inner; |
||||
Array<byte> ownedBuffer; |
||||
ArrayPtr<byte> buffer; |
||||
byte* bufferPos; |
||||
UnwindDetector unwindDetector; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Array I/O
|
||||
|
||||
class ArrayInputStream: public BufferedInputStream { |
||||
public: |
||||
explicit ArrayInputStream(ArrayPtr<const byte> array); |
||||
KJ_DISALLOW_COPY(ArrayInputStream); |
||||
~ArrayInputStream() noexcept(false); |
||||
|
||||
// implements BufferedInputStream ----------------------------------
|
||||
ArrayPtr<const byte> tryGetReadBuffer() override; |
||||
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override; |
||||
void skip(size_t bytes) override; |
||||
|
||||
private: |
||||
ArrayPtr<const byte> array; |
||||
}; |
||||
|
||||
class ArrayOutputStream: public BufferedOutputStream { |
||||
public: |
||||
explicit ArrayOutputStream(ArrayPtr<byte> array); |
||||
KJ_DISALLOW_COPY(ArrayOutputStream); |
||||
~ArrayOutputStream() noexcept(false); |
||||
|
||||
ArrayPtr<byte> getArray() { |
||||
// Get the portion of the array which has been filled in.
|
||||
return arrayPtr(array.begin(), fillPos); |
||||
} |
||||
|
||||
// implements BufferedInputStream ----------------------------------
|
||||
ArrayPtr<byte> getWriteBuffer() override; |
||||
void write(const void* buffer, size_t size) override; |
||||
|
||||
private: |
||||
ArrayPtr<byte> array; |
||||
byte* fillPos; |
||||
}; |
||||
|
||||
class VectorOutputStream: public BufferedOutputStream { |
||||
public: |
||||
explicit VectorOutputStream(size_t initialCapacity = 4096); |
||||
KJ_DISALLOW_COPY(VectorOutputStream); |
||||
~VectorOutputStream() noexcept(false); |
||||
|
||||
ArrayPtr<byte> getArray() { |
||||
// Get the portion of the array which has been filled in.
|
||||
return arrayPtr(vector.begin(), fillPos); |
||||
} |
||||
|
||||
// implements BufferedInputStream ----------------------------------
|
||||
ArrayPtr<byte> getWriteBuffer() override; |
||||
void write(const void* buffer, size_t size) override; |
||||
|
||||
private: |
||||
Array<byte> vector; |
||||
byte* fillPos; |
||||
|
||||
void grow(size_t minSize); |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// File descriptor I/O
|
||||
|
||||
class AutoCloseFd { |
||||
// A wrapper around a file descriptor which automatically closes the descriptor when destroyed.
|
||||
// The wrapper supports move construction for transferring ownership of the descriptor. If
|
||||
// close() returns an error, the destructor throws an exception, UNLESS the destructor is being
|
||||
// called during unwind from another exception, in which case the close error is ignored.
|
||||
//
|
||||
// If your code is not exception-safe, you should not use AutoCloseFd. In this case you will
|
||||
// have to call close() yourself and handle errors appropriately.
|
||||
|
||||
public: |
||||
inline AutoCloseFd(): fd(-1) {} |
||||
inline AutoCloseFd(decltype(nullptr)): fd(-1) {} |
||||
inline explicit AutoCloseFd(int fd): fd(fd) {} |
||||
inline AutoCloseFd(AutoCloseFd&& other) noexcept: fd(other.fd) { other.fd = -1; } |
||||
KJ_DISALLOW_COPY(AutoCloseFd); |
||||
~AutoCloseFd() noexcept(false); |
||||
|
||||
inline AutoCloseFd& operator=(AutoCloseFd&& other) { |
||||
AutoCloseFd old(kj::mv(*this)); |
||||
fd = other.fd; |
||||
other.fd = -1; |
||||
return *this; |
||||
} |
||||
|
||||
inline AutoCloseFd& operator=(decltype(nullptr)) { |
||||
AutoCloseFd old(kj::mv(*this)); |
||||
return *this; |
||||
} |
||||
|
||||
inline operator int() const { return fd; } |
||||
inline int get() const { return fd; } |
||||
|
||||
operator bool() const = delete; |
||||
// Deleting this operator prevents accidental use in boolean contexts, which
|
||||
// the int conversion operator above would otherwise allow.
|
||||
|
||||
inline bool operator==(decltype(nullptr)) { return fd < 0; } |
||||
inline bool operator!=(decltype(nullptr)) { return fd >= 0; } |
||||
|
||||
private: |
||||
int fd; |
||||
UnwindDetector unwindDetector; |
||||
}; |
||||
|
||||
inline auto KJ_STRINGIFY(const AutoCloseFd& fd) |
||||
-> decltype(kj::toCharSequence(implicitCast<int>(fd))) { |
||||
return kj::toCharSequence(implicitCast<int>(fd)); |
||||
} |
||||
|
||||
class FdInputStream: public InputStream { |
||||
// An InputStream wrapping a file descriptor.
|
||||
|
||||
public: |
||||
explicit FdInputStream(int fd): fd(fd) {} |
||||
explicit FdInputStream(AutoCloseFd fd): fd(fd), autoclose(mv(fd)) {} |
||||
KJ_DISALLOW_COPY(FdInputStream); |
||||
~FdInputStream() noexcept(false); |
||||
|
||||
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override; |
||||
|
||||
inline int getFd() const { return fd; } |
||||
|
||||
private: |
||||
int fd; |
||||
AutoCloseFd autoclose; |
||||
}; |
||||
|
||||
class FdOutputStream: public OutputStream { |
||||
// An OutputStream wrapping a file descriptor.
|
||||
|
||||
public: |
||||
explicit FdOutputStream(int fd): fd(fd) {} |
||||
explicit FdOutputStream(AutoCloseFd fd): fd(fd), autoclose(mv(fd)) {} |
||||
KJ_DISALLOW_COPY(FdOutputStream); |
||||
~FdOutputStream() noexcept(false); |
||||
|
||||
void write(const void* buffer, size_t size) override; |
||||
void write(ArrayPtr<const ArrayPtr<const byte>> pieces) override; |
||||
|
||||
inline int getFd() const { return fd; } |
||||
|
||||
private: |
||||
int fd; |
||||
AutoCloseFd autoclose; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Win32 Handle I/O
|
||||
|
||||
#ifdef _WIN32 |
||||
|
||||
class AutoCloseHandle { |
||||
// A wrapper around a Win32 HANDLE which automatically closes the handle when destroyed.
|
||||
// The wrapper supports move construction for transferring ownership of the handle. If
|
||||
// CloseHandle() returns an error, the destructor throws an exception, UNLESS the destructor is
|
||||
// being called during unwind from another exception, in which case the close error is ignored.
|
||||
//
|
||||
// If your code is not exception-safe, you should not use AutoCloseHandle. In this case you will
|
||||
// have to call close() yourself and handle errors appropriately.
|
||||
|
||||
public: |
||||
inline AutoCloseHandle(): handle((void*)-1) {} |
||||
inline AutoCloseHandle(decltype(nullptr)): handle((void*)-1) {} |
||||
inline explicit AutoCloseHandle(void* handle): handle(handle) {} |
||||
inline AutoCloseHandle(AutoCloseHandle&& other) noexcept: handle(other.handle) { |
||||
other.handle = (void*)-1; |
||||
} |
||||
KJ_DISALLOW_COPY(AutoCloseHandle); |
||||
~AutoCloseHandle() noexcept(false); |
||||
|
||||
inline AutoCloseHandle& operator=(AutoCloseHandle&& other) { |
||||
AutoCloseHandle old(kj::mv(*this)); |
||||
handle = other.handle; |
||||
other.handle = (void*)-1; |
||||
return *this; |
||||
} |
||||
|
||||
inline AutoCloseHandle& operator=(decltype(nullptr)) { |
||||
AutoCloseHandle old(kj::mv(*this)); |
||||
return *this; |
||||
} |
||||
|
||||
inline operator void*() const { return handle; } |
||||
inline void* get() const { return handle; } |
||||
|
||||
operator bool() const = delete; |
||||
// Deleting this operator prevents accidental use in boolean contexts, which
|
||||
// the void* conversion operator above would otherwise allow.
|
||||
|
||||
inline bool operator==(decltype(nullptr)) { return handle != (void*)-1; } |
||||
inline bool operator!=(decltype(nullptr)) { return handle == (void*)-1; } |
||||
|
||||
private: |
||||
void* handle; // -1 (aka INVALID_HANDLE_VALUE) if not valid.
|
||||
}; |
||||
|
||||
class HandleInputStream: public InputStream { |
||||
// An InputStream wrapping a Win32 HANDLE.
|
||||
|
||||
public: |
||||
explicit HandleInputStream(void* handle): handle(handle) {} |
||||
explicit HandleInputStream(AutoCloseHandle handle): handle(handle), autoclose(mv(handle)) {} |
||||
KJ_DISALLOW_COPY(HandleInputStream); |
||||
~HandleInputStream() noexcept(false); |
||||
|
||||
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override; |
||||
|
||||
private: |
||||
void* handle; |
||||
AutoCloseHandle autoclose; |
||||
}; |
||||
|
||||
class HandleOutputStream: public OutputStream { |
||||
// An OutputStream wrapping a Win32 HANDLE.
|
||||
|
||||
public: |
||||
explicit HandleOutputStream(void* handle): handle(handle) {} |
||||
explicit HandleOutputStream(AutoCloseHandle handle): handle(handle), autoclose(mv(handle)) {} |
||||
KJ_DISALLOW_COPY(HandleOutputStream); |
||||
~HandleOutputStream() noexcept(false); |
||||
|
||||
void write(const void* buffer, size_t size) override; |
||||
|
||||
private: |
||||
void* handle; |
||||
AutoCloseHandle autoclose; |
||||
}; |
||||
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_IO_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.
|
||||
|
||||
#ifndef KJ_MAIN_H_ |
||||
#define KJ_MAIN_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "array.h" |
||||
#include "string.h" |
||||
#include "vector.h" |
||||
#include "function.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class ProcessContext { |
||||
// Context for command-line programs.
|
||||
|
||||
public: |
||||
virtual StringPtr getProgramName() = 0; |
||||
// Get argv[0] as passed to main().
|
||||
|
||||
KJ_NORETURN(virtual void exit()) = 0; |
||||
// Indicates program completion. The program is considered successful unless `error()` was
|
||||
// called. Typically this exits with _Exit(), meaning that the stack is not unwound, buffers
|
||||
// are not flushed, etc. -- it is the responsibility of the caller to flush any buffers that
|
||||
// matter. However, an alternate context implementation e.g. for unit testing purposes could
|
||||
// choose to throw an exception instead.
|
||||
//
|
||||
// At first this approach may sound crazy. Isn't it much better to shut down cleanly? What if
|
||||
// you lose data? However, it turns out that if you look at each common class of program, _Exit()
|
||||
// is almost always preferable. Let's break it down:
|
||||
//
|
||||
// * Commands: A typical program you might run from the command line is single-threaded and
|
||||
// exits quickly and deterministically. Commands often use buffered I/O and need to flush
|
||||
// those buffers before exit. However, most of the work performed by destructors is not
|
||||
// flushing buffers, but rather freeing up memory, placing objects into freelists, and closing
|
||||
// file descriptors. All of this is irrelevant if the process is about to exit anyway, and
|
||||
// for a command that runs quickly, time wasted freeing heap space may make a real difference
|
||||
// in the overall runtime of a script. Meanwhile, it is usually easy to determine exactly what
|
||||
// resources need to be flushed before exit, and easy to tell if they are not being flushed
|
||||
// (because the command fails to produce the expected output). Therefore, it is reasonably
|
||||
// easy for commands to explicitly ensure all output is flushed before exiting, and it is
|
||||
// probably a good idea for them to do so anyway, because write failures should be detected
|
||||
// and handled. For commands, a good strategy is to allocate any objects that require clean
|
||||
// destruction on the stack, and allow them to go out of scope before the command exits.
|
||||
// Meanwhile, any resources which do not need to be cleaned up should be allocated as members
|
||||
// of the command's main class, whose destructor normally will not be called.
|
||||
//
|
||||
// * Interactive apps: Programs that interact with the user (whether they be graphical apps
|
||||
// with windows or console-based apps like emacs) generally exit only when the user asks them
|
||||
// to. Such applications may store large data structures in memory which need to be synced
|
||||
// to disk, such as documents or user preferences. However, relying on stack unwind or global
|
||||
// destructors as the mechanism for ensuring such syncing occurs is probably wrong. First of
|
||||
// all, it's 2013, and applications ought to be actively syncing changes to non-volatile
|
||||
// storage the moment those changes are made. Applications can crash at any time and a crash
|
||||
// should never lose data that is more than half a second old. Meanwhile, if a user actually
|
||||
// does try to close an application while unsaved changes exist, the application UI should
|
||||
// prompt the user to decide what to do. Such a UI mechanism is obviously too high level to
|
||||
// be implemented via destructors, so KJ's use of _Exit() shouldn't make a difference here.
|
||||
//
|
||||
// * Servers: A good server is fault-tolerant, prepared for the possibility that at any time
|
||||
// it could crash, the OS could decide to kill it off, or the machine it is running on could
|
||||
// just die. So, using _Exit() should be no problem. In fact, servers generally never even
|
||||
// call exit anyway; they are killed externally.
|
||||
//
|
||||
// * Batch jobs: A long-running batch job is something between a command and a server. It
|
||||
// probably knows exactly what needs to be flushed before exiting, and it probably should be
|
||||
// fault-tolerant.
|
||||
//
|
||||
// Meanwhile, regardless of program type, if you are adhering to KJ style, then the use of
|
||||
// _Exit() shouldn't be a problem anyway:
|
||||
//
|
||||
// * KJ style forbids global mutable state (singletons) in general and global constructors and
|
||||
// destructors in particular. Therefore, everything that could possibly need cleanup either
|
||||
// lives on the stack or is transitively owned by something living on the stack.
|
||||
//
|
||||
// * Calling exit() simply means "Don't clean up anything older than this stack frame.". If you
|
||||
// have resources that require cleanup before exit, make sure they are owned by stack frames
|
||||
// beyond the one that eventually calls exit(). To be as safe as possible, don't place any
|
||||
// state in your program's main class, and don't call exit() yourself. Then, runMainAndExit()
|
||||
// will do it, and the only thing on the stack at that time will be your main class, which
|
||||
// has no state anyway.
|
||||
//
|
||||
// TODO(someday): Perhaps we should use the new std::quick_exit(), so that at_quick_exit() is
|
||||
// available for those who really think they need it. Unfortunately, it is not yet available
|
||||
// on many platforms.
|
||||
|
||||
virtual void warning(StringPtr message) = 0; |
||||
// Print the given message to standard error. A newline is printed after the message if it
|
||||
// doesn't already have one.
|
||||
|
||||
virtual void error(StringPtr message) = 0; |
||||
// Like `warning()`, but also sets a flag indicating that the process has failed, and that when
|
||||
// it eventually exits it should indicate an error status.
|
||||
|
||||
KJ_NORETURN(virtual void exitError(StringPtr message)) = 0; |
||||
// Equivalent to `error(message)` followed by `exit()`.
|
||||
|
||||
KJ_NORETURN(virtual void exitInfo(StringPtr message)) = 0; |
||||
// Displays the given non-error message to the user and then calls `exit()`. This is used to
|
||||
// implement things like --help.
|
||||
|
||||
virtual void increaseLoggingVerbosity() = 0; |
||||
// Increase the level of detail produced by the debug logging system. `MainBuilder` invokes
|
||||
// this if the caller uses the -v flag.
|
||||
|
||||
// TODO(someday): Add interfaces representing standard OS resources like the filesystem, so that
|
||||
// these things can be mocked out.
|
||||
}; |
||||
|
||||
class TopLevelProcessContext final: public ProcessContext { |
||||
// A ProcessContext implementation appropriate for use at the actual entry point of a process
|
||||
// (as opposed to when you are trying to call a program's main function from within some other
|
||||
// program). This implementation writes errors to stderr, and its `exit()` method actually
|
||||
// calls the C `quick_exit()` function.
|
||||
|
||||
public: |
||||
explicit TopLevelProcessContext(StringPtr programName); |
||||
|
||||
struct CleanShutdownException { int exitCode; }; |
||||
// If the environment variable KJ_CLEAN_SHUTDOWN is set, then exit() will actually throw this
|
||||
// exception rather than exiting. `kj::runMain()` catches this exception and returns normally.
|
||||
// This is useful primarily for testing purposes, to assist tools like memory leak checkers that
|
||||
// are easily confused by quick_exit().
|
||||
|
||||
StringPtr getProgramName() override; |
||||
KJ_NORETURN(void exit() override); |
||||
void warning(StringPtr message) override; |
||||
void error(StringPtr message) override; |
||||
KJ_NORETURN(void exitError(StringPtr message) override); |
||||
KJ_NORETURN(void exitInfo(StringPtr message) override); |
||||
void increaseLoggingVerbosity() override; |
||||
|
||||
private: |
||||
StringPtr programName; |
||||
bool cleanShutdown; |
||||
bool hadErrors = false; |
||||
}; |
||||
|
||||
typedef Function<void(StringPtr programName, ArrayPtr<const StringPtr> params)> MainFunc; |
||||
|
||||
int runMainAndExit(ProcessContext& context, MainFunc&& func, int argc, char* argv[]); |
||||
// Runs the given main function and then exits using the given context. If an exception is thrown,
|
||||
// this will catch it, report it via the context and exit with an error code.
|
||||
//
|
||||
// Normally this function does not return, because returning would probably lead to wasting time
|
||||
// on cleanup when the process is just going to exit anyway. However, to facilitate memory leak
|
||||
// checkers and other tools that require a clean shutdown to do their job, if the environment
|
||||
// variable KJ_CLEAN_SHUTDOWN is set, the function will in fact return an exit code, which should
|
||||
// then be returned from main().
|
||||
//
|
||||
// Most users will use the KJ_MAIN() macro rather than call this function directly.
|
||||
|
||||
#define KJ_MAIN(MainClass) \ |
||||
int main(int argc, char* argv[]) { \
|
||||
::kj::TopLevelProcessContext context(argv[0]); \
|
||||
MainClass mainObject(context); \
|
||||
return ::kj::runMainAndExit(context, mainObject.getMain(), argc, argv); \
|
||||
} |
||||
// Convenience macro for declaring a main function based on the given class. The class must have
|
||||
// a constructor that accepts a ProcessContext& and a method getMain() which returns
|
||||
// kj::MainFunc (probably building it using a MainBuilder).
|
||||
|
||||
class MainBuilder { |
||||
// Builds a main() function with nice argument parsing. As options and arguments are parsed,
|
||||
// corresponding callbacks are called, so that you never have to write a massive switch()
|
||||
// statement to interpret arguments. Additionally, this approach encourages you to write
|
||||
// main classes that have a reasonable API that can be used as an alternative to their
|
||||
// command-line interface.
|
||||
//
|
||||
// All StringPtrs passed to MainBuilder must remain valid until option parsing completes. The
|
||||
// assumption is that these strings will all be literals, making this an easy requirement. If
|
||||
// not, consider allocating them in an Arena.
|
||||
//
|
||||
// Some flags are automatically recognized by the main functions built by this class:
|
||||
// --help: Prints help text and exits. The help text is constructed based on the
|
||||
// information you provide to the builder as you define each flag.
|
||||
// --verbose: Increase logging verbosity.
|
||||
// --version: Print version information and exit.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// class FooMain {
|
||||
// public:
|
||||
// FooMain(kj::ProcessContext& context): context(context) {}
|
||||
//
|
||||
// bool setAll() { all = true; return true; }
|
||||
// // Enable the --all flag.
|
||||
//
|
||||
// kj::MainBuilder::Validity setOutput(kj::StringPtr name) {
|
||||
// // Set the output file.
|
||||
//
|
||||
// if (name.endsWith(".foo")) {
|
||||
// outputFile = name;
|
||||
// return true;
|
||||
// } else {
|
||||
// return "Output file must have extension .foo.";
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// kj::MainBuilder::Validity processInput(kj::StringPtr name) {
|
||||
// // Process an input file.
|
||||
//
|
||||
// if (!exists(name)) {
|
||||
// return kj::str(name, ": file not found");
|
||||
// }
|
||||
// // ... process the input file ...
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// kj::MainFunc getMain() {
|
||||
// return MainBuilder(context, "Foo Builder v1.5", "Reads <source>s and builds a Foo.")
|
||||
// .addOption({'a', "all"}, KJ_BIND_METHOD(*this, setAll),
|
||||
// "Frob all the widgets. Otherwise, only some widgets are frobbed.")
|
||||
// .addOptionWithArg({'o', "output"}, KJ_BIND_METHOD(*this, setOutput),
|
||||
// "<filename>", "Output to <filename>. Must be a .foo file.")
|
||||
// .expectOneOrMoreArgs("<source>", KJ_BIND_METHOD(*this, processInput))
|
||||
// .build();
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// bool all = false;
|
||||
// kj::StringPtr outputFile;
|
||||
// kj::ProcessContext& context;
|
||||
// };
|
||||
|
||||
public: |
||||
MainBuilder(ProcessContext& context, StringPtr version, |
||||
StringPtr briefDescription, StringPtr extendedDescription = nullptr); |
||||
~MainBuilder() noexcept(false); |
||||
|
||||
class OptionName { |
||||
public: |
||||
OptionName() = default; |
||||
inline OptionName(char shortName): isLong(false), shortName(shortName) {} |
||||
inline OptionName(const char* longName): isLong(true), longName(longName) {} |
||||
|
||||
private: |
||||
bool isLong; |
||||
union { |
||||
char shortName; |
||||
const char* longName; |
||||
}; |
||||
friend class MainBuilder; |
||||
}; |
||||
|
||||
class Validity { |
||||
public: |
||||
inline Validity(bool valid) { |
||||
if (!valid) errorMessage = heapString("invalid argument"); |
||||
} |
||||
inline Validity(const char* errorMessage) |
||||
: errorMessage(heapString(errorMessage)) {} |
||||
inline Validity(String&& errorMessage) |
||||
: errorMessage(kj::mv(errorMessage)) {} |
||||
|
||||
inline const Maybe<String>& getError() const { return errorMessage; } |
||||
inline Maybe<String> releaseError() { return kj::mv(errorMessage); } |
||||
|
||||
private: |
||||
Maybe<String> errorMessage; |
||||
friend class MainBuilder; |
||||
}; |
||||
|
||||
MainBuilder& addOption(std::initializer_list<OptionName> names, Function<Validity()> callback, |
||||
StringPtr helpText); |
||||
// Defines a new option (flag). `names` is a list of characters and strings that can be used to
|
||||
// specify the option on the command line. Single-character names are used with "-" while string
|
||||
// names are used with "--". `helpText` is a natural-language description of the flag.
|
||||
//
|
||||
// `callback` is called when the option is seen. Its return value indicates whether the option
|
||||
// was accepted. If not, further option processing stops, and error is written, and the process
|
||||
// exits.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// builder.addOption({'a', "all"}, KJ_BIND_METHOD(*this, showAll), "Show all files.");
|
||||
//
|
||||
// This option could be specified in the following ways:
|
||||
//
|
||||
// -a
|
||||
// --all
|
||||
//
|
||||
// Note that single-character option names can be combined into a single argument. For example,
|
||||
// `-abcd` is equivalent to `-a -b -c -d`.
|
||||
//
|
||||
// The help text for this option would look like:
|
||||
//
|
||||
// -a, --all
|
||||
// Show all files.
|
||||
//
|
||||
// Note that help text is automatically word-wrapped.
|
||||
|
||||
MainBuilder& addOptionWithArg(std::initializer_list<OptionName> names, |
||||
Function<Validity(StringPtr)> callback, |
||||
StringPtr argumentTitle, StringPtr helpText); |
||||
// Like `addOption()`, but adds an option which accepts an argument. `argumentTitle` is used in
|
||||
// the help text. The argument text is passed to the callback.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// builder.addOptionWithArg({'o', "output"}, KJ_BIND_METHOD(*this, setOutput),
|
||||
// "<filename>", "Output to <filename>.");
|
||||
//
|
||||
// This option could be specified with an argument of "foo" in the following ways:
|
||||
//
|
||||
// -ofoo
|
||||
// -o foo
|
||||
// --output=foo
|
||||
// --output foo
|
||||
//
|
||||
// Note that single-character option names can be combined, but only the last option can have an
|
||||
// argument, since the characters after the option letter are interpreted as the argument. E.g.
|
||||
// `-abofoo` would be equivalent to `-a -b -o foo`.
|
||||
//
|
||||
// The help text for this option would look like:
|
||||
//
|
||||
// -o FILENAME, --output=FILENAME
|
||||
// Output to FILENAME.
|
||||
|
||||
MainBuilder& addSubCommand(StringPtr name, Function<MainFunc()> getSubParser, |
||||
StringPtr briefHelpText); |
||||
// If exactly the given name is seen as an argument, invoke getSubParser() and then pass all
|
||||
// remaining arguments to the parser it returns. This is useful for implementing commands which
|
||||
// have lots of sub-commands, like "git" (which has sub-commands "checkout", "branch", "pull",
|
||||
// etc.).
|
||||
//
|
||||
// `getSubParser` is only called if the command is seen. This avoids building main functions
|
||||
// for commands that aren't used.
|
||||
//
|
||||
// `briefHelpText` should be brief enough to show immediately after the command name on a single
|
||||
// line. It will not be wrapped. Users can use the built-in "help" command to get extended
|
||||
// help on a particular command.
|
||||
|
||||
MainBuilder& expectArg(StringPtr title, Function<Validity(StringPtr)> callback); |
||||
MainBuilder& expectOptionalArg(StringPtr title, Function<Validity(StringPtr)> callback); |
||||
MainBuilder& expectZeroOrMoreArgs(StringPtr title, Function<Validity(StringPtr)> callback); |
||||
MainBuilder& expectOneOrMoreArgs(StringPtr title, Function<Validity(StringPtr)> callback); |
||||
// Set callbacks to handle arguments. `expectArg()` and `expectOptionalArg()` specify positional
|
||||
// arguments with special handling, while `expect{Zero,One}OrMoreArgs()` specifies a handler for
|
||||
// an argument list (the handler is called once for each argument in the list). `title`
|
||||
// specifies how the argument should be represented in the usage text.
|
||||
//
|
||||
// All options callbacks are called before argument callbacks, regardless of their ordering on
|
||||
// the command line. This matches GNU getopt's behavior of permuting non-flag arguments to the
|
||||
// end of the argument list. Also matching getopt, the special option "--" indicates that the
|
||||
// rest of the command line is all arguments, not options, even if they start with '-'.
|
||||
//
|
||||
// The interpretation of positional arguments is fairly flexible. The non-optional arguments can
|
||||
// be expected at the beginning, end, or in the middle. If more arguments are specified than
|
||||
// the number of non-optional args, they are assigned to the optional argument handlers in the
|
||||
// order of registration.
|
||||
//
|
||||
// For example, say you called:
|
||||
// builder.expectArg("<foo>", ...);
|
||||
// builder.expectOptionalArg("<bar>", ...);
|
||||
// builder.expectArg("<baz>", ...);
|
||||
// builder.expectZeroOrMoreArgs("<qux>", ...);
|
||||
// builder.expectArg("<corge>", ...);
|
||||
//
|
||||
// This command requires at least three arguments: foo, baz, and corge. If four arguments are
|
||||
// given, the second is assigned to bar. If five or more arguments are specified, then the
|
||||
// arguments between the third and last are assigned to qux. Note that it never makes sense
|
||||
// to call `expect*OrMoreArgs()` more than once since only the first call would ever be used.
|
||||
//
|
||||
// In practice, you probably shouldn't create such complicated commands as in the above example.
|
||||
// But, this flexibility seems necessary to support commands where the first argument is special
|
||||
// as well as commands (like `cp`) where the last argument is special.
|
||||
|
||||
MainBuilder& callAfterParsing(Function<Validity()> callback); |
||||
// Call the given function after all arguments have been parsed.
|
||||
|
||||
MainFunc build(); |
||||
// Build the "main" function, which simply parses the arguments. Once this returns, the
|
||||
// `MainBuilder` is no longer valid.
|
||||
|
||||
private: |
||||
struct Impl; |
||||
Own<Impl> impl; |
||||
|
||||
class MainImpl; |
||||
}; |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_MAIN_H_
|
@ -0,0 +1,406 @@ |
||||
// 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 KJ_MEMORY_H_ |
||||
#define KJ_MEMORY_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "common.h" |
||||
|
||||
namespace kj { |
||||
|
||||
// =======================================================================================
|
||||
// Disposer -- Implementation details.
|
||||
|
||||
class Disposer { |
||||
// Abstract interface for a thing that "disposes" of objects, where "disposing" usually means
|
||||
// calling the destructor followed by freeing the underlying memory. `Own<T>` encapsulates an
|
||||
// object pointer with corresponding Disposer.
|
||||
//
|
||||
// Few developers will ever touch this interface. It is primarily useful for those implementing
|
||||
// custom memory allocators.
|
||||
|
||||
protected: |
||||
// Do not declare a destructor, as doing so will force a global initializer for each HeapDisposer
|
||||
// instance. Eww!
|
||||
|
||||
virtual void disposeImpl(void* pointer) const = 0; |
||||
// Disposes of the object, given a pointer to the beginning of the object. If the object is
|
||||
// polymorphic, this pointer is determined by dynamic_cast<void*>(). For non-polymorphic types,
|
||||
// Own<T> does not allow any casting, so the pointer exactly matches the original one given to
|
||||
// Own<T>.
|
||||
|
||||
public: |
||||
|
||||
template <typename T> |
||||
void dispose(T* object) const; |
||||
// Helper wrapper around disposeImpl().
|
||||
//
|
||||
// If T is polymorphic, calls `disposeImpl(dynamic_cast<void*>(object))`, otherwise calls
|
||||
// `disposeImpl(implicitCast<void*>(object))`.
|
||||
//
|
||||
// Callers must not call dispose() on the same pointer twice, even if the first call throws
|
||||
// an exception.
|
||||
|
||||
private: |
||||
template <typename T, bool polymorphic = __is_polymorphic(T)> |
||||
struct Dispose_; |
||||
}; |
||||
|
||||
template <typename T> |
||||
class DestructorOnlyDisposer: public Disposer { |
||||
// A disposer that merely calls the type's destructor and nothing else.
|
||||
|
||||
public: |
||||
static const DestructorOnlyDisposer instance; |
||||
|
||||
void disposeImpl(void* pointer) const override { |
||||
reinterpret_cast<T*>(pointer)->~T(); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
const DestructorOnlyDisposer<T> DestructorOnlyDisposer<T>::instance = DestructorOnlyDisposer<T>(); |
||||
|
||||
class NullDisposer: public Disposer { |
||||
// A disposer that does nothing.
|
||||
|
||||
public: |
||||
static const NullDisposer instance; |
||||
|
||||
void disposeImpl(void* pointer) const override {} |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Own<T> -- An owned pointer.
|
||||
|
||||
template <typename T> |
||||
class Own { |
||||
// A transferrable title to a T. When an Own<T> goes out of scope, the object's Disposer is
|
||||
// called to dispose of it. An Own<T> can be efficiently passed by move, without relocating the
|
||||
// underlying object; this transfers ownership.
|
||||
//
|
||||
// This is much like std::unique_ptr, except:
|
||||
// - You cannot release(). An owned object is not necessarily allocated with new (see next
|
||||
// point), so it would be hard to use release() correctly.
|
||||
// - The deleter is made polymorphic by virtual call rather than by template. This is much
|
||||
// more powerful -- it allows the use of custom allocators, freelists, etc. This could
|
||||
// _almost_ be accomplished with unique_ptr by forcing everyone to use something like
|
||||
// std::unique_ptr<T, kj::Deleter>, except that things get hairy in the presence of multiple
|
||||
// inheritance and upcasting, and anyway if you force everyone to use a custom deleter
|
||||
// then you've lost any benefit to interoperating with the "standard" unique_ptr.
|
||||
|
||||
public: |
||||
KJ_DISALLOW_COPY(Own); |
||||
inline Own(): disposer(nullptr), ptr(nullptr) {} |
||||
inline Own(Own&& other) noexcept |
||||
: disposer(other.disposer), ptr(other.ptr) { other.ptr = nullptr; } |
||||
inline Own(Own<RemoveConstOrDisable<T>>&& other) noexcept |
||||
: disposer(other.disposer), ptr(other.ptr) { other.ptr = nullptr; } |
||||
template <typename U, typename = EnableIf<canConvert<U*, T*>()>> |
||||
inline Own(Own<U>&& other) noexcept |
||||
: disposer(other.disposer), ptr(other.ptr) { |
||||
static_assert(__is_polymorphic(T), |
||||
"Casting owned pointers requires that the target type is polymorphic."); |
||||
other.ptr = nullptr; |
||||
} |
||||
inline Own(T* ptr, const Disposer& disposer) noexcept: disposer(&disposer), ptr(ptr) {} |
||||
|
||||
~Own() noexcept(false) { dispose(); } |
||||
|
||||
inline Own& operator=(Own&& other) { |
||||
// Move-assingnment operator.
|
||||
|
||||
// Careful, this might own `other`. Therefore we have to transfer the pointers first, then
|
||||
// dispose.
|
||||
const Disposer* disposerCopy = disposer; |
||||
T* ptrCopy = ptr; |
||||
disposer = other.disposer; |
||||
ptr = other.ptr; |
||||
other.ptr = nullptr; |
||||
if (ptrCopy != nullptr) { |
||||
disposerCopy->dispose(const_cast<RemoveConst<T>*>(ptrCopy)); |
||||
} |
||||
return *this; |
||||
} |
||||
|
||||
inline Own& operator=(decltype(nullptr)) { |
||||
dispose(); |
||||
return *this; |
||||
} |
||||
|
||||
template <typename U> |
||||
Own<U> downcast() { |
||||
// Downcast the pointer to Own<U>, destroying the original pointer. If this pointer does not
|
||||
// actually point at an instance of U, the results are undefined (throws an exception in debug
|
||||
// mode if RTTI is enabled, otherwise you're on your own).
|
||||
|
||||
Own<U> result; |
||||
if (ptr != nullptr) { |
||||
result.ptr = &kj::downcast<U>(*ptr); |
||||
result.disposer = disposer; |
||||
ptr = nullptr; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
#define NULLCHECK KJ_IREQUIRE(ptr != nullptr, "null Own<> dereference") |
||||
inline T* operator->() { NULLCHECK; return ptr; } |
||||
inline const T* operator->() const { NULLCHECK; return ptr; } |
||||
inline T& operator*() { NULLCHECK; return *ptr; } |
||||
inline const T& operator*() const { NULLCHECK; return *ptr; } |
||||
#undef NULLCHECK |
||||
inline T* get() { return ptr; } |
||||
inline const T* get() const { return ptr; } |
||||
inline operator T*() { return ptr; } |
||||
inline operator const T*() const { return ptr; } |
||||
|
||||
private: |
||||
const Disposer* disposer; // Only valid if ptr != nullptr.
|
||||
T* ptr; |
||||
|
||||
inline explicit Own(decltype(nullptr)): disposer(nullptr), ptr(nullptr) {} |
||||
|
||||
inline bool operator==(decltype(nullptr)) { return ptr == nullptr; } |
||||
inline bool operator!=(decltype(nullptr)) { return ptr != nullptr; } |
||||
// Only called by Maybe<Own<T>>.
|
||||
|
||||
inline void dispose() { |
||||
// Make sure that if an exception is thrown, we are left with a null ptr, so we won't possibly
|
||||
// dispose again.
|
||||
T* ptrCopy = ptr; |
||||
if (ptrCopy != nullptr) { |
||||
ptr = nullptr; |
||||
disposer->dispose(const_cast<RemoveConst<T>*>(ptrCopy)); |
||||
} |
||||
} |
||||
|
||||
template <typename U> |
||||
friend class Own; |
||||
friend class Maybe<Own<T>>; |
||||
}; |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> |
||||
class OwnOwn { |
||||
public: |
||||
inline OwnOwn(Own<T>&& value) noexcept: value(kj::mv(value)) {} |
||||
|
||||
inline Own<T>& operator*() & { return value; } |
||||
inline const Own<T>& operator*() const & { return value; } |
||||
inline Own<T>&& operator*() && { return kj::mv(value); } |
||||
inline const Own<T>&& operator*() const && { return kj::mv(value); } |
||||
inline Own<T>* operator->() { return &value; } |
||||
inline const Own<T>* operator->() const { return &value; } |
||||
inline operator Own<T>*() { return value ? &value : nullptr; } |
||||
inline operator const Own<T>*() const { return value ? &value : nullptr; } |
||||
|
||||
private: |
||||
Own<T> value; |
||||
}; |
||||
|
||||
template <typename T> |
||||
OwnOwn<T> readMaybe(Maybe<Own<T>>&& maybe) { return OwnOwn<T>(kj::mv(maybe.ptr)); } |
||||
template <typename T> |
||||
Own<T>* readMaybe(Maybe<Own<T>>& maybe) { return maybe.ptr ? &maybe.ptr : nullptr; } |
||||
template <typename T> |
||||
const Own<T>* readMaybe(const Maybe<Own<T>>& maybe) { return maybe.ptr ? &maybe.ptr : nullptr; } |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T> |
||||
class Maybe<Own<T>> { |
||||
public: |
||||
inline Maybe(): ptr(nullptr) {} |
||||
inline Maybe(Own<T>&& t) noexcept: ptr(kj::mv(t)) {} |
||||
inline Maybe(Maybe&& other) noexcept: ptr(kj::mv(other.ptr)) {} |
||||
|
||||
template <typename U> |
||||
inline Maybe(Maybe<Own<U>>&& other): ptr(mv(other.ptr)) {} |
||||
template <typename U> |
||||
inline Maybe(Own<U>&& other): ptr(mv(other)) {} |
||||
|
||||
inline Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {} |
||||
|
||||
inline operator Maybe<T&>() { return ptr.get(); } |
||||
inline operator Maybe<const T&>() const { return ptr.get(); } |
||||
|
||||
inline Maybe& operator=(Maybe&& other) { ptr = kj::mv(other.ptr); return *this; } |
||||
|
||||
inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; } |
||||
inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; } |
||||
|
||||
Own<T>& orDefault(Own<T>& defaultValue) { |
||||
if (ptr == nullptr) { |
||||
return defaultValue; |
||||
} else { |
||||
return ptr; |
||||
} |
||||
} |
||||
const Own<T>& orDefault(const Own<T>& defaultValue) const { |
||||
if (ptr == nullptr) { |
||||
return defaultValue; |
||||
} else { |
||||
return ptr; |
||||
} |
||||
} |
||||
|
||||
template <typename Func> |
||||
auto map(Func&& f) & -> Maybe<decltype(f(instance<Own<T>&>()))> { |
||||
if (ptr == nullptr) { |
||||
return nullptr; |
||||
} else { |
||||
return f(ptr); |
||||
} |
||||
} |
||||
|
||||
template <typename Func> |
||||
auto map(Func&& f) const & -> Maybe<decltype(f(instance<const Own<T>&>()))> { |
||||
if (ptr == nullptr) { |
||||
return nullptr; |
||||
} else { |
||||
return f(ptr); |
||||
} |
||||
} |
||||
|
||||
template <typename Func> |
||||
auto map(Func&& f) && -> Maybe<decltype(f(instance<Own<T>&&>()))> { |
||||
if (ptr == nullptr) { |
||||
return nullptr; |
||||
} else { |
||||
return f(kj::mv(ptr)); |
||||
} |
||||
} |
||||
|
||||
template <typename Func> |
||||
auto map(Func&& f) const && -> Maybe<decltype(f(instance<const Own<T>&&>()))> { |
||||
if (ptr == nullptr) { |
||||
return nullptr; |
||||
} else { |
||||
return f(kj::mv(ptr)); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
Own<T> ptr; |
||||
|
||||
template <typename U> |
||||
friend class Maybe; |
||||
template <typename U> |
||||
friend _::OwnOwn<U> _::readMaybe(Maybe<Own<U>>&& maybe); |
||||
template <typename U> |
||||
friend Own<U>* _::readMaybe(Maybe<Own<U>>& maybe); |
||||
template <typename U> |
||||
friend const Own<U>* _::readMaybe(const Maybe<Own<U>>& maybe); |
||||
}; |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> |
||||
class HeapDisposer final: public Disposer { |
||||
public: |
||||
virtual void disposeImpl(void* pointer) const override { delete reinterpret_cast<T*>(pointer); } |
||||
|
||||
static const HeapDisposer instance; |
||||
}; |
||||
|
||||
template <typename T> |
||||
const HeapDisposer<T> HeapDisposer<T>::instance = HeapDisposer<T>(); |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T, typename... Params> |
||||
Own<T> heap(Params&&... params) { |
||||
// heap<T>(...) allocates a T on the heap, forwarding the parameters to its constructor. The
|
||||
// exact heap implementation is unspecified -- for now it is operator new, but you should not
|
||||
// assume this. (Since we know the object size at delete time, we could actually implement an
|
||||
// allocator that is more efficient than operator new.)
|
||||
|
||||
return Own<T>(new T(kj::fwd<Params>(params)...), _::HeapDisposer<T>::instance); |
||||
} |
||||
|
||||
template <typename T> |
||||
Own<Decay<T>> heap(T&& orig) { |
||||
// Allocate a copy (or move) of the argument on the heap.
|
||||
//
|
||||
// The purpose of this overload is to allow you to omit the template parameter as there is only
|
||||
// one argument and the purpose is to copy it.
|
||||
|
||||
typedef Decay<T> T2; |
||||
return Own<T2>(new T2(kj::fwd<T>(orig)), _::HeapDisposer<T2>::instance); |
||||
} |
||||
|
||||
// =======================================================================================
|
||||
// SpaceFor<T> -- assists in manual allocation
|
||||
|
||||
template <typename T> |
||||
class SpaceFor { |
||||
// A class which has the same size and alignment as T but does not call its constructor or
|
||||
// destructor automatically. Instead, call construct() to construct a T in the space, which
|
||||
// returns an Own<T> which will take care of calling T's destructor later.
|
||||
|
||||
public: |
||||
inline SpaceFor() {} |
||||
inline ~SpaceFor() {} |
||||
|
||||
template <typename... Params> |
||||
Own<T> construct(Params&&... params) { |
||||
ctor(value, kj::fwd<Params>(params)...); |
||||
return Own<T>(&value, DestructorOnlyDisposer<T>::instance); |
||||
} |
||||
|
||||
private: |
||||
union { |
||||
T value; |
||||
}; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
template <typename T> |
||||
struct Disposer::Dispose_<T, true> { |
||||
static void dispose(T* object, const Disposer& disposer) { |
||||
// Note that dynamic_cast<void*> does not require RTTI to be enabled, because the offset to
|
||||
// the top of the object is in the vtable -- as it obviously needs to be to correctly implement
|
||||
// operator delete.
|
||||
disposer.disposeImpl(dynamic_cast<void*>(object)); |
||||
} |
||||
}; |
||||
template <typename T> |
||||
struct Disposer::Dispose_<T, false> { |
||||
static void dispose(T* object, const Disposer& disposer) { |
||||
disposer.disposeImpl(static_cast<void*>(object)); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
void Disposer::dispose(T* object) const { |
||||
Dispose_<T>::dispose(object, *this); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_MEMORY_H_
|
@ -0,0 +1,369 @@ |
||||
// 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 KJ_MUTEX_H_ |
||||
#define KJ_MUTEX_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "memory.h" |
||||
#include <inttypes.h> |
||||
|
||||
#if __linux__ && !defined(KJ_USE_FUTEX) |
||||
#define KJ_USE_FUTEX 1 |
||||
#endif |
||||
|
||||
#if !KJ_USE_FUTEX && !_WIN32 |
||||
// On Linux we use futex. On other platforms we wrap pthreads.
|
||||
// TODO(someday): Write efficient low-level locking primitives for other platforms.
|
||||
#include <pthread.h> |
||||
#endif |
||||
|
||||
namespace kj { |
||||
|
||||
// =======================================================================================
|
||||
// Private details -- public interfaces follow below.
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class Mutex { |
||||
// Internal implementation details. See `MutexGuarded<T>`.
|
||||
|
||||
public: |
||||
Mutex(); |
||||
~Mutex(); |
||||
KJ_DISALLOW_COPY(Mutex); |
||||
|
||||
enum Exclusivity { |
||||
EXCLUSIVE, |
||||
SHARED |
||||
}; |
||||
|
||||
void lock(Exclusivity exclusivity); |
||||
void unlock(Exclusivity exclusivity); |
||||
|
||||
void assertLockedByCaller(Exclusivity exclusivity); |
||||
// In debug mode, assert that the mutex is locked by the calling thread, or if that is
|
||||
// non-trivial, assert that the mutex is locked (which should be good enough to catch problems
|
||||
// in unit tests). In non-debug builds, do nothing.
|
||||
|
||||
private: |
||||
#if KJ_USE_FUTEX |
||||
uint futex; |
||||
// bit 31 (msb) = set if exclusive lock held
|
||||
// bit 30 (msb) = set if threads are waiting for exclusive lock
|
||||
// bits 0-29 = count of readers; If an exclusive lock is held, this is the count of threads
|
||||
// waiting for a read lock, otherwise it is the count of threads that currently hold a read
|
||||
// lock.
|
||||
|
||||
static constexpr uint EXCLUSIVE_HELD = 1u << 31; |
||||
static constexpr uint EXCLUSIVE_REQUESTED = 1u << 30; |
||||
static constexpr uint SHARED_COUNT_MASK = EXCLUSIVE_REQUESTED - 1; |
||||
|
||||
#elif _WIN32 |
||||
uintptr_t srwLock; // Actually an SRWLOCK, but don't want to #include <windows.h> in header.
|
||||
|
||||
#else |
||||
mutable pthread_rwlock_t mutex; |
||||
#endif |
||||
}; |
||||
|
||||
class Once { |
||||
// Internal implementation details. See `Lazy<T>`.
|
||||
|
||||
public: |
||||
#if KJ_USE_FUTEX |
||||
inline Once(bool startInitialized = false) |
||||
: futex(startInitialized ? INITIALIZED : UNINITIALIZED) {} |
||||
#else |
||||
Once(bool startInitialized = false); |
||||
~Once(); |
||||
#endif |
||||
KJ_DISALLOW_COPY(Once); |
||||
|
||||
class Initializer { |
||||
public: |
||||
virtual void run() = 0; |
||||
}; |
||||
|
||||
void runOnce(Initializer& init); |
||||
|
||||
#if _WIN32 // TODO(perf): Can we make this inline on win32 somehow?
|
||||
bool isInitialized() noexcept; |
||||
|
||||
#else |
||||
inline bool isInitialized() noexcept { |
||||
// Fast path check to see if runOnce() would simply return immediately.
|
||||
#if KJ_USE_FUTEX |
||||
return __atomic_load_n(&futex, __ATOMIC_ACQUIRE) == INITIALIZED; |
||||
#else |
||||
return __atomic_load_n(&state, __ATOMIC_ACQUIRE) == INITIALIZED; |
||||
#endif |
||||
} |
||||
#endif |
||||
|
||||
void reset(); |
||||
// Returns the state from initialized to uninitialized. It is an error to call this when
|
||||
// not already initialized, or when runOnce() or isInitialized() might be called concurrently in
|
||||
// another thread.
|
||||
|
||||
private: |
||||
#if KJ_USE_FUTEX |
||||
uint futex; |
||||
|
||||
enum State { |
||||
UNINITIALIZED, |
||||
INITIALIZING, |
||||
INITIALIZING_WITH_WAITERS, |
||||
INITIALIZED |
||||
}; |
||||
|
||||
#elif _WIN32 |
||||
uintptr_t initOnce; // Actually an INIT_ONCE, but don't want to #include <windows.h> in header.
|
||||
|
||||
#else |
||||
enum State { |
||||
UNINITIALIZED, |
||||
INITIALIZED |
||||
}; |
||||
State state; |
||||
pthread_mutex_t mutex; |
||||
#endif |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
// =======================================================================================
|
||||
// Public interface
|
||||
|
||||
template <typename T> |
||||
class Locked { |
||||
// Return type for `MutexGuarded<T>::lock()`. `Locked<T>` provides access to the bounded object
|
||||
// and unlocks the mutex when it goes out of scope.
|
||||
|
||||
public: |
||||
KJ_DISALLOW_COPY(Locked); |
||||
inline Locked(): mutex(nullptr), ptr(nullptr) {} |
||||
inline Locked(Locked&& other): mutex(other.mutex), ptr(other.ptr) { |
||||
other.mutex = nullptr; |
||||
other.ptr = nullptr; |
||||
} |
||||
inline ~Locked() { |
||||
if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); |
||||
} |
||||
|
||||
inline Locked& operator=(Locked&& other) { |
||||
if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); |
||||
mutex = other.mutex; |
||||
ptr = other.ptr; |
||||
other.mutex = nullptr; |
||||
other.ptr = nullptr; |
||||
return *this; |
||||
} |
||||
|
||||
inline void release() { |
||||
if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); |
||||
mutex = nullptr; |
||||
ptr = nullptr; |
||||
} |
||||
|
||||
inline T* operator->() { return ptr; } |
||||
inline const T* operator->() const { return ptr; } |
||||
inline T& operator*() { return *ptr; } |
||||
inline const T& operator*() const { return *ptr; } |
||||
inline T* get() { return ptr; } |
||||
inline const T* get() const { return ptr; } |
||||
inline operator T*() { return ptr; } |
||||
inline operator const T*() const { return ptr; } |
||||
|
||||
private: |
||||
_::Mutex* mutex; |
||||
T* ptr; |
||||
|
||||
inline Locked(_::Mutex& mutex, T& value): mutex(&mutex), ptr(&value) {} |
||||
|
||||
template <typename U> |
||||
friend class MutexGuarded; |
||||
}; |
||||
|
||||
template <typename T> |
||||
class MutexGuarded { |
||||
// An object of type T, bounded by a mutex. In order to access the object, you must lock it.
|
||||
//
|
||||
// Write locks are not "recursive" -- trying to lock again in a thread that already holds a lock
|
||||
// will deadlock. Recursive write locks are usually a sign of bad design.
|
||||
//
|
||||
// Unfortunately, **READ LOCKS ARE NOT RECURSIVE** either. Common sense says they should be.
|
||||
// But on many operating systems (BSD, OSX), recursively read-locking a pthread_rwlock is
|
||||
// actually unsafe. The problem is that writers are "prioritized" over readers, so a read lock
|
||||
// request will block if any write lock requests are outstanding. So, if thread A takes a read
|
||||
// lock, thread B requests a write lock (and starts waiting), and then thread A tries to take
|
||||
// another read lock recursively, the result is deadlock.
|
||||
|
||||
public: |
||||
template <typename... Params> |
||||
explicit MutexGuarded(Params&&... params); |
||||
// Initialize the mutex-bounded object by passing the given parameters to its constructor.
|
||||
|
||||
Locked<T> lockExclusive() const; |
||||
// Exclusively locks the object and returns it. The returned `Locked<T>` can be passed by
|
||||
// move, similar to `Own<T>`.
|
||||
//
|
||||
// This method is declared `const` in accordance with KJ style rules which say that constness
|
||||
// should be used to indicate thread-safety. It is safe to share a const pointer between threads,
|
||||
// but it is not safe to share a mutable pointer. Since the whole point of MutexGuarded is to
|
||||
// be shared between threads, its methods should be const, even though locking it produces a
|
||||
// non-const pointer to the contained object.
|
||||
|
||||
Locked<const T> lockShared() const; |
||||
// Lock the value for shared access. Multiple shared locks can be taken concurrently, but cannot
|
||||
// be held at the same time as a non-shared lock.
|
||||
|
||||
inline const T& getWithoutLock() const { return value; } |
||||
inline T& getWithoutLock() { return value; } |
||||
// Escape hatch for cases where some external factor guarantees that it's safe to get the
|
||||
// value. You should treat these like const_cast -- be highly suspicious of any use.
|
||||
|
||||
inline const T& getAlreadyLockedShared() const; |
||||
inline T& getAlreadyLockedShared(); |
||||
inline T& getAlreadyLockedExclusive() const; |
||||
// Like `getWithoutLock()`, but asserts that the lock is already held by the calling thread.
|
||||
|
||||
private: |
||||
mutable _::Mutex mutex; |
||||
mutable T value; |
||||
}; |
||||
|
||||
template <typename T> |
||||
class MutexGuarded<const T> { |
||||
// MutexGuarded cannot guard a const type. This would be pointless anyway, and would complicate
|
||||
// the implementation of Locked<T>, which uses constness to decide what kind of lock it holds.
|
||||
static_assert(sizeof(T) < 0, "MutexGuarded's type cannot be const."); |
||||
}; |
||||
|
||||
template <typename T> |
||||
class Lazy { |
||||
// A lazily-initialized value.
|
||||
|
||||
public: |
||||
template <typename Func> |
||||
T& get(Func&& init); |
||||
template <typename Func> |
||||
const T& get(Func&& init) const; |
||||
// The first thread to call get() will invoke the given init function to construct the value.
|
||||
// Other threads will block until construction completes, then return the same value.
|
||||
//
|
||||
// `init` is a functor(typically a lambda) which takes `SpaceFor<T>&` as its parameter and returns
|
||||
// `Own<T>`. If `init` throws an exception, the exception is propagated out of that thread's
|
||||
// call to `get()`, and subsequent calls behave as if `get()` hadn't been called at all yet --
|
||||
// in other words, subsequent calls retry initialization until it succeeds.
|
||||
|
||||
private: |
||||
mutable _::Once once; |
||||
mutable SpaceFor<T> space; |
||||
mutable Own<T> value; |
||||
|
||||
template <typename Func> |
||||
class InitImpl; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
template <typename T> |
||||
template <typename... Params> |
||||
inline MutexGuarded<T>::MutexGuarded(Params&&... params) |
||||
: value(kj::fwd<Params>(params)...) {} |
||||
|
||||
template <typename T> |
||||
inline Locked<T> MutexGuarded<T>::lockExclusive() const { |
||||
mutex.lock(_::Mutex::EXCLUSIVE); |
||||
return Locked<T>(mutex, value); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline Locked<const T> MutexGuarded<T>::lockShared() const { |
||||
mutex.lock(_::Mutex::SHARED); |
||||
return Locked<const T>(mutex, value); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline const T& MutexGuarded<T>::getAlreadyLockedShared() const { |
||||
#ifdef KJ_DEBUG |
||||
mutex.assertLockedByCaller(_::Mutex::SHARED); |
||||
#endif |
||||
return value; |
||||
} |
||||
template <typename T> |
||||
inline T& MutexGuarded<T>::getAlreadyLockedShared() { |
||||
#ifdef KJ_DEBUG |
||||
mutex.assertLockedByCaller(_::Mutex::SHARED); |
||||
#endif |
||||
return value; |
||||
} |
||||
template <typename T> |
||||
inline T& MutexGuarded<T>::getAlreadyLockedExclusive() const { |
||||
#ifdef KJ_DEBUG |
||||
mutex.assertLockedByCaller(_::Mutex::EXCLUSIVE); |
||||
#endif |
||||
return const_cast<T&>(value); |
||||
} |
||||
|
||||
template <typename T> |
||||
template <typename Func> |
||||
class Lazy<T>::InitImpl: public _::Once::Initializer { |
||||
public: |
||||
inline InitImpl(const Lazy<T>& lazy, Func&& func): lazy(lazy), func(kj::fwd<Func>(func)) {} |
||||
|
||||
void run() override { |
||||
lazy.value = func(lazy.space); |
||||
} |
||||
|
||||
private: |
||||
const Lazy<T>& lazy; |
||||
Func func; |
||||
}; |
||||
|
||||
template <typename T> |
||||
template <typename Func> |
||||
inline T& Lazy<T>::get(Func&& init) { |
||||
if (!once.isInitialized()) { |
||||
InitImpl<Func> initImpl(*this, kj::fwd<Func>(init)); |
||||
once.runOnce(initImpl); |
||||
} |
||||
return *value; |
||||
} |
||||
|
||||
template <typename T> |
||||
template <typename Func> |
||||
inline const T& Lazy<T>::get(Func&& init) const { |
||||
if (!once.isInitialized()) { |
||||
InitImpl<Func> initImpl(*this, kj::fwd<Func>(init)); |
||||
once.runOnce(initImpl); |
||||
} |
||||
return *value; |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_MUTEX_H_
|
@ -0,0 +1,155 @@ |
||||
// 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 KJ_ONE_OF_H_ |
||||
#define KJ_ONE_OF_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "common.h" |
||||
|
||||
namespace kj { |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <uint i, typename Key, typename First, typename... Rest> |
||||
struct TypeIndex_ { static constexpr uint value = TypeIndex_<i + 1, Key, Rest...>::value; }; |
||||
template <uint i, typename Key, typename... Rest> |
||||
struct TypeIndex_<i, Key, Key, Rest...> { static constexpr uint value = i; }; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename... Variants> |
||||
class OneOf { |
||||
template <typename Key> |
||||
static inline constexpr uint typeIndex() { return _::TypeIndex_<1, Key, Variants...>::value; } |
||||
// Get the 1-based index of Key within the type list Types.
|
||||
|
||||
public: |
||||
inline OneOf(): tag(0) {} |
||||
OneOf(const OneOf& other) { copyFrom(other); } |
||||
OneOf(OneOf&& other) { moveFrom(other); } |
||||
~OneOf() { destroy(); } |
||||
|
||||
OneOf& operator=(const OneOf& other) { if (tag != 0) destroy(); copyFrom(other); return *this; } |
||||
OneOf& operator=(OneOf&& other) { if (tag != 0) destroy(); moveFrom(other); return *this; } |
||||
|
||||
inline bool operator==(decltype(nullptr)) const { return tag == 0; } |
||||
inline bool operator!=(decltype(nullptr)) const { return tag != 0; } |
||||
|
||||
template <typename T> |
||||
bool is() const { |
||||
return tag == typeIndex<T>(); |
||||
} |
||||
|
||||
template <typename T> |
||||
T& get() { |
||||
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>()."); |
||||
return *reinterpret_cast<T*>(space); |
||||
} |
||||
template <typename T> |
||||
const T& get() const { |
||||
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>()."); |
||||
return *reinterpret_cast<const T*>(space); |
||||
} |
||||
|
||||
template <typename T, typename... Params> |
||||
T& init(Params&&... params) { |
||||
if (tag != 0) destroy(); |
||||
ctor(*reinterpret_cast<T*>(space), kj::fwd<Params>(params)...); |
||||
tag = typeIndex<T>(); |
||||
return *reinterpret_cast<T*>(space); |
||||
} |
||||
|
||||
private: |
||||
uint tag; |
||||
|
||||
static inline constexpr size_t maxSize(size_t a) { |
||||
return a; |
||||
} |
||||
template <typename... Rest> |
||||
static inline constexpr size_t maxSize(size_t a, size_t b, Rest... rest) { |
||||
return maxSize(kj::max(a, b), rest...); |
||||
} |
||||
// Returns the maximum of all the parameters.
|
||||
// TODO(someday): Generalize the above template and make it common. I tried, but C++ decided to
|
||||
// be difficult so I cut my losses.
|
||||
|
||||
static constexpr auto spaceSize = maxSize(sizeof(Variants)...); |
||||
// TODO(msvc): This constant could just as well go directly inside space's bracket's, where it's
|
||||
// used, but MSVC suffers a parse error on `...`.
|
||||
|
||||
union { |
||||
byte space[spaceSize]; |
||||
|
||||
void* forceAligned; |
||||
// TODO(someday): Use C++11 alignas() once we require GCC 4.8 / Clang 3.3.
|
||||
}; |
||||
|
||||
template <typename... T> |
||||
inline void doAll(T... t) {} |
||||
|
||||
template <typename T> |
||||
inline bool destroyVariant() { |
||||
if (tag == typeIndex<T>()) { |
||||
tag = 0; |
||||
dtor(*reinterpret_cast<T*>(space)); |
||||
} |
||||
return false; |
||||
} |
||||
void destroy() { |
||||
doAll(destroyVariant<Variants>()...); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline bool copyVariantFrom(const OneOf& other) { |
||||
if (other.is<T>()) { |
||||
ctor(*reinterpret_cast<T*>(space), other.get<T>()); |
||||
} |
||||
return false; |
||||
} |
||||
void copyFrom(const OneOf& other) { |
||||
// Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
|
||||
// is invalid.
|
||||
tag = other.tag; |
||||
doAll(copyVariantFrom<Variants>(other)...); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline bool moveVariantFrom(OneOf& other) { |
||||
if (other.is<T>()) { |
||||
ctor(*reinterpret_cast<T*>(space), kj::mv(other.get<T>())); |
||||
} |
||||
return false; |
||||
} |
||||
void moveFrom(OneOf& other) { |
||||
// Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
|
||||
// is invalid.
|
||||
tag = other.tag; |
||||
doAll(moveVariantFrom<Variants>(other)...); |
||||
} |
||||
}; |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_ONE_OF_H_
|
@ -0,0 +1,361 @@ |
||||
// 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 parsers useful for character stream inputs, including parsers to parse
|
||||
// common kinds of tokens like identifiers, numbers, and quoted strings.
|
||||
|
||||
#ifndef KJ_PARSE_CHAR_H_ |
||||
#define KJ_PARSE_CHAR_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "common.h" |
||||
#include "../string.h" |
||||
#include <inttypes.h> |
||||
|
||||
namespace kj { |
||||
namespace parse { |
||||
|
||||
// =======================================================================================
|
||||
// Exact char/string.
|
||||
|
||||
class ExactString_ { |
||||
public: |
||||
constexpr inline ExactString_(const char* str): str(str) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<Tuple<>> operator()(Input& input) const { |
||||
const char* ptr = str; |
||||
|
||||
while (*ptr != '\0') { |
||||
if (input.atEnd() || input.current() != *ptr) return nullptr; |
||||
input.next(); |
||||
++ptr; |
||||
} |
||||
|
||||
return Tuple<>(); |
||||
} |
||||
|
||||
private: |
||||
const char* str; |
||||
}; |
||||
|
||||
constexpr inline ExactString_ exactString(const char* str) { |
||||
return ExactString_(str); |
||||
} |
||||
|
||||
template <char c> |
||||
constexpr ExactlyConst_<char, c> exactChar() { |
||||
// Returns a parser that matches exactly the character given by the template argument (returning
|
||||
// no result).
|
||||
return ExactlyConst_<char, c>(); |
||||
} |
||||
|
||||
// =======================================================================================
|
||||
// Char ranges / sets
|
||||
|
||||
class CharGroup_ { |
||||
public: |
||||
constexpr inline CharGroup_(): bits{0, 0, 0, 0} {} |
||||
|
||||
constexpr inline CharGroup_ orRange(unsigned char first, unsigned char last) const { |
||||
return CharGroup_(bits[0] | (oneBits(last + 1) & ~oneBits(first )), |
||||
bits[1] | (oneBits(last - 63) & ~oneBits(first - 64)), |
||||
bits[2] | (oneBits(last - 127) & ~oneBits(first - 128)), |
||||
bits[3] | (oneBits(last - 191) & ~oneBits(first - 192))); |
||||
} |
||||
|
||||
constexpr inline CharGroup_ orAny(const char* chars) const { |
||||
return *chars == 0 ? *this : orChar(*chars).orAny(chars + 1); |
||||
} |
||||
|
||||
constexpr inline CharGroup_ orChar(unsigned char c) const { |
||||
return CharGroup_(bits[0] | bit(c), |
||||
bits[1] | bit(c - 64), |
||||
bits[2] | bit(c - 128), |
||||
bits[3] | bit(c - 256)); |
||||
} |
||||
|
||||
constexpr inline CharGroup_ orGroup(CharGroup_ other) const { |
||||
return CharGroup_(bits[0] | other.bits[0], |
||||
bits[1] | other.bits[1], |
||||
bits[2] | other.bits[2], |
||||
bits[3] | other.bits[3]); |
||||
} |
||||
|
||||
constexpr inline CharGroup_ invert() const { |
||||
return CharGroup_(~bits[0], ~bits[1], ~bits[2], ~bits[3]); |
||||
} |
||||
|
||||
constexpr inline bool contains(unsigned char c) const { |
||||
return (bits[c / 64] & (1ll << (c % 64))) != 0; |
||||
} |
||||
|
||||
template <typename Input> |
||||
Maybe<char> operator()(Input& input) const { |
||||
if (input.atEnd()) return nullptr; |
||||
unsigned char c = input.current(); |
||||
if (contains(c)) { |
||||
input.next(); |
||||
return c; |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
typedef unsigned long long Bits64; |
||||
|
||||
constexpr inline CharGroup_(Bits64 a, Bits64 b, Bits64 c, Bits64 d): bits{a, b, c, d} {} |
||||
Bits64 bits[4]; |
||||
|
||||
static constexpr inline Bits64 oneBits(int count) { |
||||
return count <= 0 ? 0ll : count >= 64 ? -1ll : ((1ll << count) - 1); |
||||
} |
||||
static constexpr inline Bits64 bit(int index) { |
||||
return index < 0 ? 0 : index >= 64 ? 0 : (1ll << index); |
||||
} |
||||
}; |
||||
|
||||
constexpr inline CharGroup_ charRange(char first, char last) { |
||||
// Create a parser which accepts any character in the range from `first` to `last`, inclusive.
|
||||
// For example: `charRange('a', 'z')` matches all lower-case letters. The parser's result is the
|
||||
// character matched.
|
||||
//
|
||||
// The returned object has methods which can be used to match more characters. The following
|
||||
// produces a parser which accepts any letter as well as '_', '+', '-', and '.'.
|
||||
//
|
||||
// charRange('a', 'z').orRange('A', 'Z').orChar('_').orAny("+-.")
|
||||
//
|
||||
// You can also use `.invert()` to match the opposite set of characters.
|
||||
|
||||
return CharGroup_().orRange(first, last); |
||||
} |
||||
|
||||
#if _MSC_VER |
||||
#define anyOfChars(chars) CharGroup_().orAny(chars) |
||||
// TODO(msvc): MSVC ICEs on the proper definition of `anyOfChars()`, which in turn prevents us from
|
||||
// building the compiler or schema parser. We don't know why this happens, but Harris found that
|
||||
// this horrible, horrible hack makes things work. This is awful, but it's better than nothing.
|
||||
// Hopefully, MSVC will get fixed soon and we'll be able to remove this.
|
||||
#else |
||||
constexpr inline CharGroup_ anyOfChars(const char* chars) { |
||||
// Returns a parser that accepts any of the characters in the given string (which should usually
|
||||
// be a literal). The returned parser is of the same type as returned by `charRange()` -- see
|
||||
// that function for more info.
|
||||
|
||||
return CharGroup_().orAny(chars); |
||||
} |
||||
#endif |
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
struct ArrayToString { |
||||
inline String operator()(const Array<char>& arr) const { |
||||
return heapString(arr); |
||||
} |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename SubParser> |
||||
constexpr inline auto charsToString(SubParser&& subParser) |
||||
-> decltype(transform(kj::fwd<SubParser>(subParser), _::ArrayToString())) { |
||||
// Wraps a parser that returns Array<char> such that it returns String instead.
|
||||
return parse::transform(kj::fwd<SubParser>(subParser), _::ArrayToString()); |
||||
} |
||||
|
||||
// =======================================================================================
|
||||
// Basic character classes.
|
||||
|
||||
constexpr auto alpha = charRange('a', 'z').orRange('A', 'Z'); |
||||
constexpr auto digit = charRange('0', '9'); |
||||
constexpr auto alphaNumeric = alpha.orGroup(digit); |
||||
constexpr auto nameStart = alpha.orChar('_'); |
||||
constexpr auto nameChar = alphaNumeric.orChar('_'); |
||||
constexpr auto hexDigit = charRange('0', '9').orRange('a', 'f').orRange('A', 'F'); |
||||
constexpr auto octDigit = charRange('0', '7'); |
||||
constexpr auto whitespaceChar = anyOfChars(" \f\n\r\t\v"); |
||||
constexpr auto controlChar = charRange(0, 0x1f).invert().orGroup(whitespaceChar).invert(); |
||||
|
||||
constexpr auto whitespace = many(anyOfChars(" \f\n\r\t\v")); |
||||
|
||||
constexpr auto discardWhitespace = discard(many(discard(anyOfChars(" \f\n\r\t\v")))); |
||||
// Like discard(whitespace) but avoids some memory allocation.
|
||||
|
||||
// =======================================================================================
|
||||
// Identifiers
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
struct IdentifierToString { |
||||
inline String operator()(char first, const Array<char>& rest) const { |
||||
String result = heapString(rest.size() + 1); |
||||
result[0] = first; |
||||
memcpy(result.begin() + 1, rest.begin(), rest.size()); |
||||
return result; |
||||
} |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
constexpr auto identifier = transform(sequence(nameStart, many(nameChar)), _::IdentifierToString()); |
||||
// Parses an identifier (e.g. a C variable name).
|
||||
|
||||
// =======================================================================================
|
||||
// Integers
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
inline char parseDigit(char c) { |
||||
if (c < 'A') return c - '0'; |
||||
if (c < 'a') return c - 'A' + 10; |
||||
return c - 'a' + 10; |
||||
} |
||||
|
||||
template <uint base> |
||||
struct ParseInteger { |
||||
inline uint64_t operator()(const Array<char>& digits) const { |
||||
return operator()('0', digits); |
||||
} |
||||
uint64_t operator()(char first, const Array<char>& digits) const { |
||||
uint64_t result = parseDigit(first); |
||||
for (char digit: digits) { |
||||
result = result * base + parseDigit(digit); |
||||
} |
||||
return result; |
||||
} |
||||
}; |
||||
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
constexpr auto integer = sequence( |
||||
oneOf( |
||||
transform(sequence(exactChar<'0'>(), exactChar<'x'>(), oneOrMore(hexDigit)), _::ParseInteger<16>()), |
||||
transform(sequence(exactChar<'0'>(), many(octDigit)), _::ParseInteger<8>()), |
||||
transform(sequence(charRange('1', '9'), many(digit)), _::ParseInteger<10>())), |
||||
notLookingAt(alpha.orAny("_."))); |
||||
|
||||
// =======================================================================================
|
||||
// Numbers (i.e. floats)
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
struct ParseFloat { |
||||
double operator()(const Array<char>& digits, |
||||
const Maybe<Array<char>>& fraction, |
||||
const Maybe<Tuple<Maybe<char>, Array<char>>>& exponent) const; |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
constexpr auto number = transform( |
||||
sequence( |
||||
oneOrMore(digit), |
||||
optional(sequence(exactChar<'.'>(), many(digit))), |
||||
optional(sequence(discard(anyOfChars("eE")), optional(anyOfChars("+-")), many(digit))), |
||||
notLookingAt(alpha.orAny("_."))), |
||||
_::ParseFloat()); |
||||
|
||||
// =======================================================================================
|
||||
// Quoted strings
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
struct InterpretEscape { |
||||
char operator()(char c) const { |
||||
switch (c) { |
||||
case 'a': return '\a'; |
||||
case 'b': return '\b'; |
||||
case 'f': return '\f'; |
||||
case 'n': return '\n'; |
||||
case 'r': return '\r'; |
||||
case 't': return '\t'; |
||||
case 'v': return '\v'; |
||||
default: return c; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
struct ParseHexEscape { |
||||
inline char operator()(char first, char second) const { |
||||
return (parseDigit(first) << 4) | parseDigit(second); |
||||
} |
||||
}; |
||||
|
||||
struct ParseHexByte { |
||||
inline byte operator()(char first, char second) const { |
||||
return (parseDigit(first) << 4) | parseDigit(second); |
||||
} |
||||
}; |
||||
|
||||
struct ParseOctEscape { |
||||
inline char operator()(char first, Maybe<char> second, Maybe<char> third) const { |
||||
char result = first - '0'; |
||||
KJ_IF_MAYBE(digit1, second) { |
||||
result = (result << 3) | (*digit1 - '0'); |
||||
KJ_IF_MAYBE(digit2, third) { |
||||
result = (result << 3) | (*digit2 - '0'); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
constexpr auto escapeSequence = |
||||
sequence(exactChar<'\\'>(), oneOf( |
||||
transform(anyOfChars("abfnrtv'\"\\\?"), _::InterpretEscape()), |
||||
transform(sequence(exactChar<'x'>(), hexDigit, hexDigit), _::ParseHexEscape()), |
||||
transform(sequence(octDigit, optional(octDigit), optional(octDigit)), |
||||
_::ParseOctEscape()))); |
||||
// A parser that parses a C-string-style escape sequence (starting with a backslash). Returns
|
||||
// a char.
|
||||
|
||||
constexpr auto doubleQuotedString = charsToString(sequence( |
||||
exactChar<'\"'>(), |
||||
many(oneOf(anyOfChars("\\\n\"").invert(), escapeSequence)), |
||||
exactChar<'\"'>())); |
||||
// Parses a C-style double-quoted string.
|
||||
|
||||
constexpr auto singleQuotedString = charsToString(sequence( |
||||
exactChar<'\''>(), |
||||
many(oneOf(anyOfChars("\\\n\'").invert(), escapeSequence)), |
||||
exactChar<'\''>())); |
||||
// Parses a C-style single-quoted string.
|
||||
|
||||
constexpr auto doubleQuotedHexBinary = sequence( |
||||
exactChar<'0'>(), exactChar<'x'>(), exactChar<'\"'>(), |
||||
oneOrMore(transform(sequence(discardWhitespace, hexDigit, hexDigit), _::ParseHexByte())), |
||||
discardWhitespace, |
||||
exactChar<'\"'>()); |
||||
// Parses a double-quoted hex binary literal. Returns Array<byte>.
|
||||
|
||||
} // namespace parse
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_PARSE_CHAR_H_
|
@ -0,0 +1,824 @@ |
||||
// 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.
|
||||
|
||||
// Parser combinator framework!
|
||||
//
|
||||
// This file declares several functions which construct parsers, usually taking other parsers as
|
||||
// input, thus making them parser combinators.
|
||||
//
|
||||
// A valid parser is any functor which takes a reference to an input cursor (defined below) as its
|
||||
// input and returns a Maybe. The parser returns null on parse failure, or returns the parsed
|
||||
// result on success.
|
||||
//
|
||||
// An "input cursor" is any type which implements the same interface as IteratorInput, below. Such
|
||||
// a type acts as a pointer to the current input location. When a parser returns successfully, it
|
||||
// will have updated the input cursor to point to the position just past the end of what was parsed.
|
||||
// On failure, the cursor position is unspecified.
|
||||
|
||||
#ifndef KJ_PARSE_COMMON_H_ |
||||
#define KJ_PARSE_COMMON_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "../common.h" |
||||
#include "../memory.h" |
||||
#include "../array.h" |
||||
#include "../tuple.h" |
||||
#include "../vector.h" |
||||
#if _MSC_VER |
||||
#include <type_traits> // result_of_t |
||||
#endif |
||||
|
||||
namespace kj { |
||||
namespace parse { |
||||
|
||||
template <typename Element, typename Iterator> |
||||
class IteratorInput { |
||||
// A parser input implementation based on an iterator range.
|
||||
|
||||
public: |
||||
IteratorInput(Iterator begin, Iterator end) |
||||
: parent(nullptr), pos(begin), end(end), best(begin) {} |
||||
explicit IteratorInput(IteratorInput& parent) |
||||
: parent(&parent), pos(parent.pos), end(parent.end), best(parent.pos) {} |
||||
~IteratorInput() { |
||||
if (parent != nullptr) { |
||||
parent->best = kj::max(kj::max(pos, best), parent->best); |
||||
} |
||||
} |
||||
KJ_DISALLOW_COPY(IteratorInput); |
||||
|
||||
void advanceParent() { |
||||
parent->pos = pos; |
||||
} |
||||
void forgetParent() { |
||||
parent = nullptr; |
||||
} |
||||
|
||||
bool atEnd() { return pos == end; } |
||||
auto current() -> decltype(*instance<Iterator>()) { |
||||
KJ_IREQUIRE(!atEnd()); |
||||
return *pos; |
||||
} |
||||
auto consume() -> decltype(*instance<Iterator>()) { |
||||
KJ_IREQUIRE(!atEnd()); |
||||
return *pos++; |
||||
} |
||||
void next() { |
||||
KJ_IREQUIRE(!atEnd()); |
||||
++pos; |
||||
} |
||||
|
||||
Iterator getBest() { return kj::max(pos, best); } |
||||
|
||||
Iterator getPosition() { return pos; } |
||||
|
||||
private: |
||||
IteratorInput* parent; |
||||
Iterator pos; |
||||
Iterator end; |
||||
Iterator best; // furthest we got with any sub-input
|
||||
}; |
||||
|
||||
template <typename T> struct OutputType_; |
||||
template <typename T> struct OutputType_<Maybe<T>> { typedef T Type; }; |
||||
template <typename Parser, typename Input> |
||||
using OutputType = typename OutputType_< |
||||
#if _MSC_VER |
||||
std::result_of_t<Parser(Input)> |
||||
// The instance<T&>() based version below results in:
|
||||
// C2064: term does not evaluate to a function taking 1 arguments
|
||||
#else |
||||
decltype(instance<Parser&>()(instance<Input&>())) |
||||
#endif |
||||
>::Type; |
||||
// Synonym for the output type of a parser, given the parser type and the input type.
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
template <typename Input, typename Output> |
||||
class ParserRef { |
||||
// Acts as a reference to some other parser, with simplified type. The referenced parser
|
||||
// is polymorphic by virtual call rather than templates. For grammars of non-trivial size,
|
||||
// it is important to inject refs into the grammar here and there to prevent the parser types
|
||||
// from becoming ridiculous. Using too many of them can hurt performance, though.
|
||||
|
||||
public: |
||||
ParserRef(): parser(nullptr), wrapper(nullptr) {} |
||||
ParserRef(const ParserRef&) = default; |
||||
ParserRef(ParserRef&&) = default; |
||||
ParserRef& operator=(const ParserRef& other) = default; |
||||
ParserRef& operator=(ParserRef&& other) = default; |
||||
|
||||
template <typename Other> |
||||
constexpr ParserRef(Other&& other) |
||||
: parser(&other), wrapper(&WrapperImplInstance<Decay<Other>>::instance) { |
||||
static_assert(kj::isReference<Other>(), "ParserRef should not be assigned to a temporary."); |
||||
} |
||||
|
||||
template <typename Other> |
||||
inline ParserRef& operator=(Other&& other) { |
||||
static_assert(kj::isReference<Other>(), "ParserRef should not be assigned to a temporary."); |
||||
parser = &other; |
||||
wrapper = &WrapperImplInstance<Decay<Other>>::instance; |
||||
return *this; |
||||
} |
||||
|
||||
KJ_ALWAYS_INLINE(Maybe<Output> operator()(Input& input) const) { |
||||
// Always inline in the hopes that this allows branch prediction to kick in so the virtual call
|
||||
// doesn't hurt so much.
|
||||
return wrapper->parse(parser, input); |
||||
} |
||||
|
||||
private: |
||||
struct Wrapper { |
||||
virtual Maybe<Output> parse(const void* parser, Input& input) const = 0; |
||||
}; |
||||
template <typename ParserImpl> |
||||
struct WrapperImpl: public Wrapper { |
||||
Maybe<Output> parse(const void* parser, Input& input) const override { |
||||
return (*reinterpret_cast<const ParserImpl*>(parser))(input); |
||||
} |
||||
}; |
||||
template <typename ParserImpl> |
||||
struct WrapperImplInstance { |
||||
#if _MSC_VER |
||||
// TODO(msvc): MSVC currently fails to initialize vtable pointers for constexpr values so
|
||||
// we have to make this just const instead.
|
||||
static const WrapperImpl<ParserImpl> instance; |
||||
#else |
||||
static constexpr WrapperImpl<ParserImpl> instance = WrapperImpl<ParserImpl>(); |
||||
#endif |
||||
}; |
||||
|
||||
const void* parser; |
||||
const Wrapper* wrapper; |
||||
}; |
||||
|
||||
template <typename Input, typename Output> |
||||
template <typename ParserImpl> |
||||
#if _MSC_VER |
||||
const typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl> |
||||
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance = WrapperImpl<ParserImpl>(); |
||||
#else |
||||
constexpr typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl> |
||||
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance; |
||||
#endif |
||||
|
||||
template <typename Input, typename ParserImpl> |
||||
constexpr ParserRef<Input, OutputType<ParserImpl, Input>> ref(ParserImpl& impl) { |
||||
// Constructs a ParserRef. You must specify the input type explicitly, e.g.
|
||||
// `ref<MyInput>(myParser)`.
|
||||
|
||||
return ParserRef<Input, OutputType<ParserImpl, Input>>(impl); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// any
|
||||
// Output = one token
|
||||
|
||||
class Any_ { |
||||
public: |
||||
template <typename Input> |
||||
Maybe<Decay<decltype(instance<Input>().consume())>> operator()(Input& input) const { |
||||
if (input.atEnd()) { |
||||
return nullptr; |
||||
} else { |
||||
return input.consume(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
constexpr Any_ any = Any_(); |
||||
// A parser which matches any token and simply returns it.
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// exactly()
|
||||
// Output = Tuple<>
|
||||
|
||||
template <typename T> |
||||
class Exactly_ { |
||||
public: |
||||
explicit constexpr Exactly_(T&& expected): expected(expected) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<Tuple<>> operator()(Input& input) const { |
||||
if (input.atEnd() || input.current() != expected) { |
||||
return nullptr; |
||||
} else { |
||||
input.next(); |
||||
return Tuple<>(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
T expected; |
||||
}; |
||||
|
||||
template <typename T> |
||||
constexpr Exactly_<T> exactly(T&& expected) { |
||||
// Constructs a parser which succeeds when the input is exactly the token specified. The
|
||||
// result is always the empty tuple.
|
||||
|
||||
return Exactly_<T>(kj::fwd<T>(expected)); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// exactlyConst()
|
||||
// Output = Tuple<>
|
||||
|
||||
template <typename T, T expected> |
||||
class ExactlyConst_ { |
||||
public: |
||||
explicit constexpr ExactlyConst_() {} |
||||
|
||||
template <typename Input> |
||||
Maybe<Tuple<>> operator()(Input& input) const { |
||||
if (input.atEnd() || input.current() != expected) { |
||||
return nullptr; |
||||
} else { |
||||
input.next(); |
||||
return Tuple<>(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
template <typename T, T expected> |
||||
constexpr ExactlyConst_<T, expected> exactlyConst() { |
||||
// Constructs a parser which succeeds when the input is exactly the token specified. The
|
||||
// result is always the empty tuple. This parser is templated on the token value which may cause
|
||||
// it to perform better -- or worse. Be sure to measure.
|
||||
|
||||
return ExactlyConst_<T, expected>(); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// constResult()
|
||||
|
||||
template <typename SubParser, typename Result> |
||||
class ConstResult_ { |
||||
public: |
||||
explicit constexpr ConstResult_(SubParser&& subParser, Result&& result) |
||||
: subParser(kj::fwd<SubParser>(subParser)), result(kj::fwd<Result>(result)) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<Result> operator()(Input& input) const { |
||||
if (subParser(input) == nullptr) { |
||||
return nullptr; |
||||
} else { |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
Result result; |
||||
}; |
||||
|
||||
template <typename SubParser, typename Result> |
||||
constexpr ConstResult_<SubParser, Result> constResult(SubParser&& subParser, Result&& result) { |
||||
// Constructs a parser which returns exactly `result` if `subParser` is successful.
|
||||
return ConstResult_<SubParser, Result>(kj::fwd<SubParser>(subParser), kj::fwd<Result>(result)); |
||||
} |
||||
|
||||
template <typename SubParser> |
||||
constexpr ConstResult_<SubParser, Tuple<>> discard(SubParser&& subParser) { |
||||
// Constructs a parser which wraps `subParser` but discards the result.
|
||||
return constResult(kj::fwd<SubParser>(subParser), Tuple<>()); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// sequence()
|
||||
// Output = Flattened Tuple of outputs of sub-parsers.
|
||||
|
||||
template <typename... SubParsers> class Sequence_; |
||||
|
||||
template <typename FirstSubParser, typename... SubParsers> |
||||
class Sequence_<FirstSubParser, SubParsers...> { |
||||
public: |
||||
template <typename T, typename... U> |
||||
explicit constexpr Sequence_(T&& firstSubParser, U&&... rest) |
||||
: first(kj::fwd<T>(firstSubParser)), rest(kj::fwd<U>(rest)...) {} |
||||
|
||||
// TODO(msvc): The trailing return types on `operator()` and `parseNext()` expose at least two
|
||||
// bugs in MSVC:
|
||||
//
|
||||
// 1. An ICE.
|
||||
// 2. 'error C2672: 'operator __surrogate_func': no matching overloaded function found)',
|
||||
// which crops up in numerous places when trying to build the capnp command line tools.
|
||||
//
|
||||
// The only workaround I found for both bugs is to omit the trailing return types and instead
|
||||
// rely on C++14's return type deduction.
|
||||
|
||||
template <typename Input> |
||||
auto operator()(Input& input) const |
||||
#ifndef _MSC_VER |
||||
-> Maybe<decltype(tuple( |
||||
instance<OutputType<FirstSubParser, Input>>(), |
||||
instance<OutputType<SubParsers, Input>>()...))> |
||||
#endif |
||||
{ |
||||
return parseNext(input); |
||||
} |
||||
|
||||
template <typename Input, typename... InitialParams> |
||||
auto parseNext(Input& input, InitialParams&&... initialParams) const |
||||
#ifndef _MSC_VER |
||||
-> Maybe<decltype(tuple( |
||||
kj::fwd<InitialParams>(initialParams)..., |
||||
instance<OutputType<FirstSubParser, Input>>(), |
||||
instance<OutputType<SubParsers, Input>>()...))> |
||||
#endif |
||||
{ |
||||
KJ_IF_MAYBE(firstResult, first(input)) { |
||||
return rest.parseNext(input, kj::fwd<InitialParams>(initialParams)..., |
||||
kj::mv(*firstResult)); |
||||
} else { |
||||
// TODO(msvc): MSVC depends on return type deduction to compile this function, so we need to
|
||||
// help it deduce the right type on this code path.
|
||||
return Maybe<decltype(tuple( |
||||
kj::fwd<InitialParams>(initialParams)..., |
||||
instance<OutputType<FirstSubParser, Input>>(), |
||||
instance<OutputType<SubParsers, Input>>()...))>{nullptr}; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
FirstSubParser first; |
||||
Sequence_<SubParsers...> rest; |
||||
}; |
||||
|
||||
template <> |
||||
class Sequence_<> { |
||||
public: |
||||
template <typename Input> |
||||
Maybe<Tuple<>> operator()(Input& input) const { |
||||
return parseNext(input); |
||||
} |
||||
|
||||
template <typename Input, typename... Params> |
||||
auto parseNext(Input& input, Params&&... params) const -> |
||||
Maybe<decltype(tuple(kj::fwd<Params>(params)...))> { |
||||
return tuple(kj::fwd<Params>(params)...); |
||||
} |
||||
}; |
||||
|
||||
template <typename... SubParsers> |
||||
constexpr Sequence_<SubParsers...> sequence(SubParsers&&... subParsers) { |
||||
// Constructs a parser that executes each of the parameter parsers in sequence and returns a
|
||||
// tuple of their results.
|
||||
|
||||
return Sequence_<SubParsers...>(kj::fwd<SubParsers>(subParsers)...); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// many()
|
||||
// Output = Array of output of sub-parser, or just a uint count if the sub-parser returns Tuple<>.
|
||||
|
||||
template <typename SubParser, bool atLeastOne> |
||||
class Many_ { |
||||
template <typename Input, typename Output = OutputType<SubParser, Input>> |
||||
struct Impl; |
||||
public: |
||||
explicit constexpr Many_(SubParser&& subParser) |
||||
: subParser(kj::fwd<SubParser>(subParser)) {} |
||||
|
||||
template <typename Input> |
||||
auto operator()(Input& input) const |
||||
-> decltype(Impl<Input>::apply(instance<const SubParser&>(), input)); |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
}; |
||||
|
||||
template <typename SubParser, bool atLeastOne> |
||||
template <typename Input, typename Output> |
||||
struct Many_<SubParser, atLeastOne>::Impl { |
||||
static Maybe<Array<Output>> apply(const SubParser& subParser, Input& input) { |
||||
typedef Vector<OutputType<SubParser, Input>> Results; |
||||
Results results; |
||||
|
||||
while (!input.atEnd()) { |
||||
Input subInput(input); |
||||
|
||||
KJ_IF_MAYBE(subResult, subParser(subInput)) { |
||||
subInput.advanceParent(); |
||||
results.add(kj::mv(*subResult)); |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (atLeastOne && results.empty()) { |
||||
return nullptr; |
||||
} |
||||
|
||||
return results.releaseAsArray(); |
||||
} |
||||
}; |
||||
|
||||
template <typename SubParser, bool atLeastOne> |
||||
template <typename Input> |
||||
struct Many_<SubParser, atLeastOne>::Impl<Input, Tuple<>> { |
||||
// If the sub-parser output is Tuple<>, just return a count.
|
||||
|
||||
static Maybe<uint> apply(const SubParser& subParser, Input& input) { |
||||
uint count = 0; |
||||
|
||||
while (!input.atEnd()) { |
||||
Input subInput(input); |
||||
|
||||
KJ_IF_MAYBE(subResult, subParser(subInput)) { |
||||
subInput.advanceParent(); |
||||
++count; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (atLeastOne && count == 0) { |
||||
return nullptr; |
||||
} |
||||
|
||||
return count; |
||||
} |
||||
}; |
||||
|
||||
template <typename SubParser, bool atLeastOne> |
||||
template <typename Input> |
||||
auto Many_<SubParser, atLeastOne>::operator()(Input& input) const |
||||
-> decltype(Impl<Input>::apply(instance<const SubParser&>(), input)) { |
||||
return Impl<Input, OutputType<SubParser, Input>>::apply(subParser, input); |
||||
} |
||||
|
||||
template <typename SubParser> |
||||
constexpr Many_<SubParser, false> many(SubParser&& subParser) { |
||||
// Constructs a parser that repeatedly executes the given parser until it fails, returning an
|
||||
// Array of the results (or a uint count if `subParser` returns an empty tuple).
|
||||
return Many_<SubParser, false>(kj::fwd<SubParser>(subParser)); |
||||
} |
||||
|
||||
template <typename SubParser> |
||||
constexpr Many_<SubParser, true> oneOrMore(SubParser&& subParser) { |
||||
// Like `many()` but the parser must parse at least one item to be successful.
|
||||
return Many_<SubParser, true>(kj::fwd<SubParser>(subParser)); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// times()
|
||||
// Output = Array of output of sub-parser, or Tuple<> if sub-parser returns Tuple<>.
|
||||
|
||||
template <typename SubParser> |
||||
class Times_ { |
||||
template <typename Input, typename Output = OutputType<SubParser, Input>> |
||||
struct Impl; |
||||
public: |
||||
explicit constexpr Times_(SubParser&& subParser, uint count) |
||||
: subParser(kj::fwd<SubParser>(subParser)), count(count) {} |
||||
|
||||
template <typename Input> |
||||
auto operator()(Input& input) const |
||||
-> decltype(Impl<Input>::apply(instance<const SubParser&>(), instance<uint>(), input)); |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
uint count; |
||||
}; |
||||
|
||||
template <typename SubParser> |
||||
template <typename Input, typename Output> |
||||
struct Times_<SubParser>::Impl { |
||||
static Maybe<Array<Output>> apply(const SubParser& subParser, uint count, Input& input) { |
||||
auto results = heapArrayBuilder<OutputType<SubParser, Input>>(count); |
||||
|
||||
while (results.size() < count) { |
||||
if (input.atEnd()) { |
||||
return nullptr; |
||||
} else KJ_IF_MAYBE(subResult, subParser(input)) { |
||||
results.add(kj::mv(*subResult)); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
return results.finish(); |
||||
} |
||||
}; |
||||
|
||||
template <typename SubParser> |
||||
template <typename Input> |
||||
struct Times_<SubParser>::Impl<Input, Tuple<>> { |
||||
// If the sub-parser output is Tuple<>, just return a count.
|
||||
|
||||
static Maybe<Tuple<>> apply(const SubParser& subParser, uint count, Input& input) { |
||||
uint actualCount = 0; |
||||
|
||||
while (actualCount < count) { |
||||
if (input.atEnd()) { |
||||
return nullptr; |
||||
} else KJ_IF_MAYBE(subResult, subParser(input)) { |
||||
++actualCount; |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
return tuple(); |
||||
} |
||||
}; |
||||
|
||||
template <typename SubParser> |
||||
template <typename Input> |
||||
auto Times_<SubParser>::operator()(Input& input) const |
||||
-> decltype(Impl<Input>::apply(instance<const SubParser&>(), instance<uint>(), input)) { |
||||
return Impl<Input, OutputType<SubParser, Input>>::apply(subParser, count, input); |
||||
} |
||||
|
||||
template <typename SubParser> |
||||
constexpr Times_<SubParser> times(SubParser&& subParser, uint count) { |
||||
// Constructs a parser that repeats the subParser exactly `count` times.
|
||||
return Times_<SubParser>(kj::fwd<SubParser>(subParser), count); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// optional()
|
||||
// Output = Maybe<output of sub-parser>
|
||||
|
||||
template <typename SubParser> |
||||
class Optional_ { |
||||
public: |
||||
explicit constexpr Optional_(SubParser&& subParser) |
||||
: subParser(kj::fwd<SubParser>(subParser)) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<Maybe<OutputType<SubParser, Input>>> operator()(Input& input) const { |
||||
typedef Maybe<OutputType<SubParser, Input>> Result; |
||||
|
||||
Input subInput(input); |
||||
KJ_IF_MAYBE(subResult, subParser(subInput)) { |
||||
subInput.advanceParent(); |
||||
return Result(kj::mv(*subResult)); |
||||
} else { |
||||
return Result(nullptr); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
}; |
||||
|
||||
template <typename SubParser> |
||||
constexpr Optional_<SubParser> optional(SubParser&& subParser) { |
||||
// Constructs a parser that accepts zero or one of the given sub-parser, returning a Maybe
|
||||
// of the sub-parser's result.
|
||||
return Optional_<SubParser>(kj::fwd<SubParser>(subParser)); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// oneOf()
|
||||
// All SubParsers must have same output type, which becomes the output type of the
|
||||
// OneOfParser.
|
||||
|
||||
template <typename... SubParsers> |
||||
class OneOf_; |
||||
|
||||
template <typename FirstSubParser, typename... SubParsers> |
||||
class OneOf_<FirstSubParser, SubParsers...> { |
||||
public: |
||||
explicit constexpr OneOf_(FirstSubParser&& firstSubParser, SubParsers&&... rest) |
||||
: first(kj::fwd<FirstSubParser>(firstSubParser)), rest(kj::fwd<SubParsers>(rest)...) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<OutputType<FirstSubParser, Input>> operator()(Input& input) const { |
||||
{ |
||||
Input subInput(input); |
||||
Maybe<OutputType<FirstSubParser, Input>> firstResult = first(subInput); |
||||
|
||||
if (firstResult != nullptr) { |
||||
subInput.advanceParent(); |
||||
return kj::mv(firstResult); |
||||
} |
||||
} |
||||
|
||||
// Hoping for some tail recursion here...
|
||||
return rest(input); |
||||
} |
||||
|
||||
private: |
||||
FirstSubParser first; |
||||
OneOf_<SubParsers...> rest; |
||||
}; |
||||
|
||||
template <> |
||||
class OneOf_<> { |
||||
public: |
||||
template <typename Input> |
||||
decltype(nullptr) operator()(Input& input) const { |
||||
return nullptr; |
||||
} |
||||
}; |
||||
|
||||
template <typename... SubParsers> |
||||
constexpr OneOf_<SubParsers...> oneOf(SubParsers&&... parsers) { |
||||
// Constructs a parser that accepts one of a set of options. The parser behaves as the first
|
||||
// sub-parser in the list which returns successfully. All of the sub-parsers must return the
|
||||
// same type.
|
||||
return OneOf_<SubParsers...>(kj::fwd<SubParsers>(parsers)...); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// transform()
|
||||
// Output = Result of applying transform functor to input value. If input is a tuple, it is
|
||||
// unpacked to form the transformation parameters.
|
||||
|
||||
template <typename Position> |
||||
struct Span { |
||||
public: |
||||
inline const Position& begin() const { return begin_; } |
||||
inline const Position& end() const { return end_; } |
||||
|
||||
Span() = default; |
||||
inline constexpr Span(Position&& begin, Position&& end): begin_(mv(begin)), end_(mv(end)) {} |
||||
|
||||
private: |
||||
Position begin_; |
||||
Position end_; |
||||
}; |
||||
|
||||
template <typename Position> |
||||
constexpr Span<Decay<Position>> span(Position&& start, Position&& end) { |
||||
return Span<Decay<Position>>(kj::fwd<Position>(start), kj::fwd<Position>(end)); |
||||
} |
||||
|
||||
template <typename SubParser, typename TransformFunc> |
||||
class Transform_ { |
||||
public: |
||||
explicit constexpr Transform_(SubParser&& subParser, TransformFunc&& transform) |
||||
: subParser(kj::fwd<SubParser>(subParser)), transform(kj::fwd<TransformFunc>(transform)) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<decltype(kj::apply(instance<TransformFunc&>(), |
||||
instance<OutputType<SubParser, Input>&&>()))> |
||||
operator()(Input& input) const { |
||||
KJ_IF_MAYBE(subResult, subParser(input)) { |
||||
return kj::apply(transform, kj::mv(*subResult)); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
TransformFunc transform; |
||||
}; |
||||
|
||||
template <typename SubParser, typename TransformFunc> |
||||
class TransformOrReject_ { |
||||
public: |
||||
explicit constexpr TransformOrReject_(SubParser&& subParser, TransformFunc&& transform) |
||||
: subParser(kj::fwd<SubParser>(subParser)), transform(kj::fwd<TransformFunc>(transform)) {} |
||||
|
||||
template <typename Input> |
||||
decltype(kj::apply(instance<TransformFunc&>(), instance<OutputType<SubParser, Input>&&>())) |
||||
operator()(Input& input) const { |
||||
KJ_IF_MAYBE(subResult, subParser(input)) { |
||||
return kj::apply(transform, kj::mv(*subResult)); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
TransformFunc transform; |
||||
}; |
||||
|
||||
template <typename SubParser, typename TransformFunc> |
||||
class TransformWithLocation_ { |
||||
public: |
||||
explicit constexpr TransformWithLocation_(SubParser&& subParser, TransformFunc&& transform) |
||||
: subParser(kj::fwd<SubParser>(subParser)), transform(kj::fwd<TransformFunc>(transform)) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<decltype(kj::apply(instance<TransformFunc&>(), |
||||
instance<Span<Decay<decltype(instance<Input&>().getPosition())>>>(), |
||||
instance<OutputType<SubParser, Input>&&>()))> |
||||
operator()(Input& input) const { |
||||
auto start = input.getPosition(); |
||||
KJ_IF_MAYBE(subResult, subParser(input)) { |
||||
return kj::apply(transform, Span<decltype(start)>(kj::mv(start), input.getPosition()), |
||||
kj::mv(*subResult)); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
TransformFunc transform; |
||||
}; |
||||
|
||||
template <typename SubParser, typename TransformFunc> |
||||
constexpr Transform_<SubParser, TransformFunc> transform( |
||||
SubParser&& subParser, TransformFunc&& functor) { |
||||
// Constructs a parser which executes some other parser and then transforms the result by invoking
|
||||
// `functor` on it. Typically `functor` is a lambda. It is invoked using `kj::apply`,
|
||||
// meaning tuples will be unpacked as arguments.
|
||||
return Transform_<SubParser, TransformFunc>( |
||||
kj::fwd<SubParser>(subParser), kj::fwd<TransformFunc>(functor)); |
||||
} |
||||
|
||||
template <typename SubParser, typename TransformFunc> |
||||
constexpr TransformOrReject_<SubParser, TransformFunc> transformOrReject( |
||||
SubParser&& subParser, TransformFunc&& functor) { |
||||
// Like `transform()` except that `functor` returns a `Maybe`. If it returns null, parsing fails,
|
||||
// otherwise the parser's result is the content of the `Maybe`.
|
||||
return TransformOrReject_<SubParser, TransformFunc>( |
||||
kj::fwd<SubParser>(subParser), kj::fwd<TransformFunc>(functor)); |
||||
} |
||||
|
||||
template <typename SubParser, typename TransformFunc> |
||||
constexpr TransformWithLocation_<SubParser, TransformFunc> transformWithLocation( |
||||
SubParser&& subParser, TransformFunc&& functor) { |
||||
// Like `transform` except that `functor` also takes a `Span` as its first parameter specifying
|
||||
// the location of the parsed content. The span's position type is whatever the parser input's
|
||||
// getPosition() returns.
|
||||
return TransformWithLocation_<SubParser, TransformFunc>( |
||||
kj::fwd<SubParser>(subParser), kj::fwd<TransformFunc>(functor)); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// notLookingAt()
|
||||
// Fails if the given parser succeeds at the current location.
|
||||
|
||||
template <typename SubParser> |
||||
class NotLookingAt_ { |
||||
public: |
||||
explicit constexpr NotLookingAt_(SubParser&& subParser) |
||||
: subParser(kj::fwd<SubParser>(subParser)) {} |
||||
|
||||
template <typename Input> |
||||
Maybe<Tuple<>> operator()(Input& input) const { |
||||
Input subInput(input); |
||||
subInput.forgetParent(); |
||||
if (subParser(subInput) == nullptr) { |
||||
return Tuple<>(); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
SubParser subParser; |
||||
}; |
||||
|
||||
template <typename SubParser> |
||||
constexpr NotLookingAt_<SubParser> notLookingAt(SubParser&& subParser) { |
||||
// Constructs a parser which fails at any position where the given parser succeeds. Otherwise,
|
||||
// it succeeds without consuming any input and returns an empty tuple.
|
||||
return NotLookingAt_<SubParser>(kj::fwd<SubParser>(subParser)); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------
|
||||
// endOfInput()
|
||||
// Output = Tuple<>, only succeeds if at end-of-input
|
||||
|
||||
class EndOfInput_ { |
||||
public: |
||||
template <typename Input> |
||||
Maybe<Tuple<>> operator()(Input& input) const { |
||||
if (input.atEnd()) { |
||||
return Tuple<>(); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
constexpr EndOfInput_ endOfInput = EndOfInput_(); |
||||
// A parser that succeeds only if it is called with no input.
|
||||
|
||||
} // namespace parse
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_PARSE_COMMON_H_
|
@ -0,0 +1,107 @@ |
||||
// 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.
|
||||
|
||||
#include "memory.h" |
||||
|
||||
#ifndef KJ_REFCOUNT_H_ |
||||
#define KJ_REFCOUNT_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
namespace kj { |
||||
|
||||
class Refcounted: private Disposer { |
||||
// Subclass this to create a class that contains a reference count. Then, use
|
||||
// `kj::refcounted<T>()` to allocate a new refcounted pointer.
|
||||
//
|
||||
// Do NOT use this lightly. Refcounting is a crutch. Good designs should strive to make object
|
||||
// ownership clear, so that refcounting is not necessary. All that said, reference counting can
|
||||
// sometimes simplify code that would otherwise become convoluted with explicit ownership, even
|
||||
// when ownership relationships are clear at an abstract level.
|
||||
//
|
||||
// NOT THREADSAFE: This refcounting implementation assumes that an object's references are
|
||||
// manipulated only in one thread, because atomic (thread-safe) refcounting is surprisingly slow.
|
||||
//
|
||||
// In general, abstract classes should _not_ subclass this. The concrete class at the bottom
|
||||
// of the hierarchy should be the one to decide how it implements refcounting. Interfaces should
|
||||
// expose only an `addRef()` method that returns `Own<InterfaceType>`. There are two reasons for
|
||||
// this rule:
|
||||
// 1. Interfaces would need to virtually inherit Refcounted, otherwise two refcounted interfaces
|
||||
// could not be inherited by the same subclass. Virtual inheritance is awkward and
|
||||
// inefficient.
|
||||
// 2. An implementation may decide that it would rather return a copy than a refcount, or use
|
||||
// some other strategy.
|
||||
//
|
||||
// TODO(cleanup): Rethink above. Virtual inheritance is not necessarily that bad. OTOH, a
|
||||
// virtual function call for every refcount is sad in its own way. A Ref<T> type to replace
|
||||
// Own<T> could also be nice.
|
||||
|
||||
public: |
||||
virtual ~Refcounted() noexcept(false); |
||||
|
||||
inline bool isShared() const { return refcount > 1; } |
||||
// Check if there are multiple references to this object. This is sometimes useful for deciding
|
||||
// whether it's safe to modify the object vs. make a copy.
|
||||
|
||||
private: |
||||
mutable uint refcount = 0; |
||||
// "mutable" because disposeImpl() is const. Bleh.
|
||||
|
||||
void disposeImpl(void* pointer) const override; |
||||
template <typename T> |
||||
static Own<T> addRefInternal(T* object); |
||||
|
||||
template <typename T> |
||||
friend Own<T> addRef(T& object); |
||||
template <typename T, typename... Params> |
||||
friend Own<T> refcounted(Params&&... params); |
||||
}; |
||||
|
||||
template <typename T, typename... Params> |
||||
inline Own<T> refcounted(Params&&... params) { |
||||
// Allocate a new refcounted instance of T, passing `params` to its constructor. Returns an
|
||||
// initial reference to the object. More references can be created with `kj::addRef()`.
|
||||
|
||||
return Refcounted::addRefInternal(new T(kj::fwd<Params>(params)...)); |
||||
} |
||||
|
||||
template <typename T> |
||||
Own<T> addRef(T& object) { |
||||
// Return a new reference to `object`, which must subclass Refcounted and have been allocated
|
||||
// using `kj::refcounted<>()`. It is suggested that subclasses implement a non-static addRef()
|
||||
// method which wraps this and returns the appropriate type.
|
||||
|
||||
KJ_IREQUIRE(object.Refcounted::refcount > 0, "Object not allocated with kj::refcounted()."); |
||||
return Refcounted::addRefInternal(&object); |
||||
} |
||||
|
||||
template <typename T> |
||||
Own<T> Refcounted::addRefInternal(T* object) { |
||||
Refcounted* refcounted = object; |
||||
++refcounted->refcount; |
||||
return Own<T>(object, *refcounted); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_REFCOUNT_H_
|
@ -0,0 +1,88 @@ |
||||
// 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.
|
||||
|
||||
/*
|
||||
* Compatibility layer for stdlib iostream |
||||
*/ |
||||
|
||||
#ifndef KJ_STD_IOSTREAM_H_ |
||||
#define KJ_STD_IOSTREAM_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "../io.h" |
||||
#include <iostream> |
||||
|
||||
namespace kj { |
||||
namespace std { |
||||
|
||||
class StdOutputStream: public kj::OutputStream { |
||||
|
||||
public: |
||||
explicit StdOutputStream(::std::ostream& stream) : stream_(stream) {} |
||||
~StdOutputStream() noexcept(false) {} |
||||
|
||||
virtual void write(const void* src, size_t size) override { |
||||
// Always writes the full size.
|
||||
|
||||
stream_.write((char*)src, size); |
||||
} |
||||
|
||||
virtual void write(ArrayPtr<const ArrayPtr<const byte>> pieces) override { |
||||
// Equivalent to write()ing each byte array in sequence, which is what the
|
||||
// default implementation does. Override if you can do something better,
|
||||
// e.g. use writev() to do the write in a single syscall.
|
||||
|
||||
for (auto piece : pieces) { |
||||
write(piece.begin(), piece.size()); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
::std::ostream& stream_; |
||||
|
||||
}; |
||||
|
||||
class StdInputStream: public kj::InputStream { |
||||
|
||||
public: |
||||
explicit StdInputStream(::std::istream& stream) : stream_(stream) {} |
||||
~StdInputStream() noexcept(false) {} |
||||
|
||||
virtual size_t tryRead( |
||||
void* buffer, size_t minBytes, size_t maxBytes) override { |
||||
// Like read(), but may return fewer than minBytes on EOF.
|
||||
|
||||
stream_.read((char*)buffer, maxBytes); |
||||
return stream_.gcount(); |
||||
} |
||||
|
||||
private: |
||||
::std::istream& stream_; |
||||
|
||||
}; |
||||
|
||||
} // namespace std
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_STD_IOSTREAM_H_
|
@ -0,0 +1,212 @@ |
||||
// 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 KJ_STRING_TREE_H_ |
||||
#define KJ_STRING_TREE_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "string.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class StringTree { |
||||
// A long string, represented internally as a tree of strings. This data structure is like a
|
||||
// String, but optimized for concatenation and iteration at the expense of seek time. The
|
||||
// structure is intended to be used for building large text blobs from many small pieces, where
|
||||
// repeatedly concatenating smaller strings into larger ones would waste copies. This structure
|
||||
// is NOT intended for use cases requiring random access or computing substrings. For those,
|
||||
// you should use a Rope, which is a much more complicated data structure.
|
||||
//
|
||||
// The proper way to construct a StringTree is via kj::strTree(...), which works just like
|
||||
// kj::str(...) but returns a StringTree rather than a String.
|
||||
//
|
||||
// KJ_STRINGIFY() functions that construct large strings from many smaller strings are encouraged
|
||||
// to return StringTree rather than a flat char container.
|
||||
|
||||
public: |
||||
inline StringTree(): size_(0) {} |
||||
inline StringTree(String&& text): size_(text.size()), text(kj::mv(text)) {} |
||||
|
||||
StringTree(Array<StringTree>&& pieces, StringPtr delim); |
||||
// Build a StringTree by concatenating the given pieces, delimited by the given delimiter
|
||||
// (e.g. ", ").
|
||||
|
||||
inline size_t size() const { return size_; } |
||||
|
||||
template <typename Func> |
||||
void visit(Func&& func) const; |
||||
|
||||
String flatten() const; |
||||
// Return the contents as a string.
|
||||
|
||||
// TODO(someday): flatten() when *this is an rvalue and when branches.size() == 0 could simply
|
||||
// return `kj::mv(text)`. Requires reference qualifiers (Clang 3.3 / GCC 4.8).
|
||||
|
||||
void flattenTo(char* __restrict__ target) const; |
||||
// Copy the contents to the given character array. Does not add a NUL terminator.
|
||||
|
||||
private: |
||||
size_t size_; |
||||
String text; |
||||
|
||||
struct Branch; |
||||
Array<Branch> branches; // In order.
|
||||
|
||||
inline void fill(char* pos, size_t branchIndex); |
||||
template <typename First, typename... Rest> |
||||
void fill(char* pos, size_t branchIndex, First&& first, Rest&&... rest); |
||||
template <typename... Rest> |
||||
void fill(char* pos, size_t branchIndex, StringTree&& first, Rest&&... rest); |
||||
template <typename... Rest> |
||||
void fill(char* pos, size_t branchIndex, Array<char>&& first, Rest&&... rest); |
||||
template <typename... Rest> |
||||
void fill(char* pos, size_t branchIndex, String&& first, Rest&&... rest); |
||||
|
||||
template <typename... Params> |
||||
static StringTree concat(Params&&... params); |
||||
static StringTree&& concat(StringTree&& param) { return kj::mv(param); } |
||||
|
||||
template <typename T> |
||||
static inline size_t flatSize(const T& t) { return t.size(); } |
||||
static inline size_t flatSize(String&& s) { return 0; } |
||||
static inline size_t flatSize(StringTree&& s) { return 0; } |
||||
|
||||
template <typename T> |
||||
static inline size_t branchCount(const T& t) { return 0; } |
||||
static inline size_t branchCount(String&& s) { return 1; } |
||||
static inline size_t branchCount(StringTree&& s) { return 1; } |
||||
|
||||
template <typename... Params> |
||||
friend StringTree strTree(Params&&... params); |
||||
}; |
||||
|
||||
inline StringTree&& KJ_STRINGIFY(StringTree&& tree) { return kj::mv(tree); } |
||||
inline const StringTree& KJ_STRINGIFY(const StringTree& tree) { return tree; } |
||||
|
||||
inline StringTree KJ_STRINGIFY(Array<StringTree>&& trees) { return StringTree(kj::mv(trees), ""); } |
||||
|
||||
template <typename... Params> |
||||
StringTree strTree(Params&&... params); |
||||
// Build a StringTree by stringifying the given parameters and concatenating the results.
|
||||
// If any of the parameters stringify to StringTree rvalues, they will be incorporated as
|
||||
// branches to avoid a copy.
|
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename... Rest> |
||||
char* fill(char* __restrict__ target, const StringTree& first, Rest&&... rest) { |
||||
// Make str() work with stringifiers that return StringTree by patching fill().
|
||||
|
||||
first.flattenTo(target); |
||||
return fill(target + first.size(), kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename T> constexpr bool isStringTree() { return false; } |
||||
template <> constexpr bool isStringTree<StringTree>() { return true; } |
||||
|
||||
inline StringTree&& toStringTreeOrCharSequence(StringTree&& tree) { return kj::mv(tree); } |
||||
inline StringTree toStringTreeOrCharSequence(String&& str) { return StringTree(kj::mv(str)); } |
||||
|
||||
template <typename T> |
||||
inline auto toStringTreeOrCharSequence(T&& value) |
||||
-> decltype(toCharSequence(kj::fwd<T>(value))) { |
||||
static_assert(!isStringTree<Decay<T>>(), |
||||
"When passing a StringTree into kj::strTree(), either pass it by rvalue " |
||||
"(use kj::mv(value)) or explicitly call value.flatten() to make a copy."); |
||||
|
||||
return toCharSequence(kj::fwd<T>(value)); |
||||
} |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
struct StringTree::Branch { |
||||
size_t index; |
||||
// Index in `text` where this branch should be inserted.
|
||||
|
||||
StringTree content; |
||||
}; |
||||
|
||||
template <typename Func> |
||||
void StringTree::visit(Func&& func) const { |
||||
size_t pos = 0; |
||||
for (auto& branch: branches) { |
||||
if (branch.index > pos) { |
||||
func(text.slice(pos, branch.index)); |
||||
pos = branch.index; |
||||
} |
||||
branch.content.visit(func); |
||||
} |
||||
if (text.size() > pos) { |
||||
func(text.slice(pos, text.size())); |
||||
} |
||||
} |
||||
|
||||
inline void StringTree::fill(char* pos, size_t branchIndex) { |
||||
KJ_IREQUIRE(pos == text.end() && branchIndex == branches.size(), |
||||
kj::str(text.end() - pos, ' ', branches.size() - branchIndex).cStr()); |
||||
} |
||||
|
||||
template <typename First, typename... Rest> |
||||
void StringTree::fill(char* pos, size_t branchIndex, First&& first, Rest&&... rest) { |
||||
pos = _::fill(pos, kj::fwd<First>(first)); |
||||
fill(pos, branchIndex, kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename... Rest> |
||||
void StringTree::fill(char* pos, size_t branchIndex, StringTree&& first, Rest&&... rest) { |
||||
branches[branchIndex].index = pos - text.begin(); |
||||
branches[branchIndex].content = kj::mv(first); |
||||
fill(pos, branchIndex + 1, kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename... Rest> |
||||
void StringTree::fill(char* pos, size_t branchIndex, String&& first, Rest&&... rest) { |
||||
branches[branchIndex].index = pos - text.begin(); |
||||
branches[branchIndex].content = StringTree(kj::mv(first)); |
||||
fill(pos, branchIndex + 1, kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename... Params> |
||||
StringTree StringTree::concat(Params&&... params) { |
||||
StringTree result; |
||||
result.size_ = _::sum({params.size()...}); |
||||
result.text = heapString( |
||||
_::sum({StringTree::flatSize(kj::fwd<Params>(params))...})); |
||||
result.branches = heapArray<StringTree::Branch>( |
||||
_::sum({StringTree::branchCount(kj::fwd<Params>(params))...})); |
||||
result.fill(result.text.begin(), 0, kj::fwd<Params>(params)...); |
||||
return result; |
||||
} |
||||
|
||||
template <typename... Params> |
||||
StringTree strTree(Params&&... params) { |
||||
return StringTree::concat(_::toStringTreeOrCharSequence(kj::fwd<Params>(params))...); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_STRING_TREE_H_
|
@ -0,0 +1,534 @@ |
||||
// 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 KJ_STRING_H_ |
||||
#define KJ_STRING_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include <initializer_list> |
||||
#include "array.h" |
||||
#include <string.h> |
||||
|
||||
namespace kj { |
||||
|
||||
class StringPtr; |
||||
class String; |
||||
|
||||
class StringTree; // string-tree.h
|
||||
|
||||
// Our STL string SFINAE trick does not work with GCC 4.7, but it works with Clang and GCC 4.8, so
|
||||
// we'll just preprocess it out if not supported.
|
||||
#if __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || _MSC_VER |
||||
#define KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP 1 |
||||
#endif |
||||
|
||||
// =======================================================================================
|
||||
// StringPtr -- A NUL-terminated ArrayPtr<const char> containing UTF-8 text.
|
||||
//
|
||||
// NUL bytes are allowed to appear before the end of the string. The only requirement is that
|
||||
// a NUL byte appear immediately after the last byte of the content. This terminator byte is not
|
||||
// counted in the string's size.
|
||||
|
||||
class StringPtr { |
||||
public: |
||||
inline StringPtr(): content("", 1) {} |
||||
inline StringPtr(decltype(nullptr)): content("", 1) {} |
||||
inline StringPtr(const char* value): content(value, strlen(value) + 1) {} |
||||
inline StringPtr(const char* value, size_t size): content(value, size + 1) { |
||||
KJ_IREQUIRE(value[size] == '\0', "StringPtr must be NUL-terminated."); |
||||
} |
||||
inline StringPtr(const char* begin, const char* end): StringPtr(begin, end - begin) {} |
||||
inline StringPtr(const String& value); |
||||
|
||||
#if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP |
||||
template <typename T, typename = decltype(instance<T>().c_str())> |
||||
inline StringPtr(const T& t): StringPtr(t.c_str()) {} |
||||
// 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.
|
||||
|
||||
template <typename T, typename = decltype(instance<T>().c_str())> |
||||
inline operator T() const { return cStr(); } |
||||
// Allow implicit conversion to 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 |
||||
|
||||
inline operator ArrayPtr<const char>() const; |
||||
inline ArrayPtr<const char> asArray() const; |
||||
inline ArrayPtr<const byte> asBytes() const { return asArray().asBytes(); } |
||||
// Result does not include NUL terminator.
|
||||
|
||||
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 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==(const StringPtr& other) const; |
||||
inline bool operator!=(const StringPtr& other) const { return !(*this == other); } |
||||
inline bool operator< (const StringPtr& other) const; |
||||
inline bool operator> (const StringPtr& other) const { return other < *this; } |
||||
inline bool operator<=(const StringPtr& other) const { return !(other < *this); } |
||||
inline bool operator>=(const StringPtr& other) const { return !(*this < other); } |
||||
|
||||
inline StringPtr slice(size_t start) const; |
||||
inline ArrayPtr<const char> slice(size_t start, size_t end) const; |
||||
// A string slice is only NUL-terminated if it is a suffix, so slice() has a one-parameter
|
||||
// version that assumes end = size().
|
||||
|
||||
inline bool startsWith(const StringPtr& other) const; |
||||
inline bool endsWith(const StringPtr& other) const; |
||||
|
||||
inline Maybe<size_t> findFirst(char c) const; |
||||
inline Maybe<size_t> findLast(char c) const; |
||||
|
||||
template <typename T> |
||||
T parseAs() const; |
||||
// Parse string as template number type.
|
||||
// Integer numbers prefixed by "0x" and "0X" are parsed in base 16 (like strtoi with base 0).
|
||||
// Integer numbers prefixed by "0" are parsed in base 10 (unlike strtoi with base 0).
|
||||
// Overflowed integer numbers throw exception.
|
||||
// Overflowed floating numbers return inf.
|
||||
|
||||
private: |
||||
inline StringPtr(ArrayPtr<const char> content): content(content) {} |
||||
|
||||
ArrayPtr<const char> content; |
||||
}; |
||||
|
||||
inline bool operator==(const char* a, const StringPtr& b) { return b == a; } |
||||
inline bool operator!=(const char* a, const StringPtr& b) { return b != a; } |
||||
|
||||
template <> char StringPtr::parseAs<char>() const; |
||||
template <> signed char StringPtr::parseAs<signed char>() const; |
||||
template <> unsigned char StringPtr::parseAs<unsigned char>() const; |
||||
template <> short StringPtr::parseAs<short>() const; |
||||
template <> unsigned short StringPtr::parseAs<unsigned short>() const; |
||||
template <> int StringPtr::parseAs<int>() const; |
||||
template <> unsigned StringPtr::parseAs<unsigned>() const; |
||||
template <> long StringPtr::parseAs<long>() const; |
||||
template <> unsigned long StringPtr::parseAs<unsigned long>() const; |
||||
template <> long long StringPtr::parseAs<long long>() const; |
||||
template <> unsigned long long StringPtr::parseAs<unsigned long long>() const; |
||||
template <> float StringPtr::parseAs<float>() const; |
||||
template <> double StringPtr::parseAs<double>() const; |
||||
|
||||
// =======================================================================================
|
||||
// String -- A NUL-terminated Array<char> containing UTF-8 text.
|
||||
//
|
||||
// NUL bytes are allowed to appear before the end of the string. The only requirement is that
|
||||
// a NUL byte appear immediately after the last byte of the content. This terminator byte is not
|
||||
// counted in the string's size.
|
||||
//
|
||||
// To allocate a String, you must call kj::heapString(). We do not implement implicit copying to
|
||||
// the heap because this hides potential inefficiency from the developer.
|
||||
|
||||
class String { |
||||
public: |
||||
String() = default; |
||||
inline String(decltype(nullptr)): content(nullptr) {} |
||||
inline String(char* value, size_t size, const ArrayDisposer& disposer); |
||||
// Does not copy. `size` does not include NUL terminator, but `value` must be NUL-terminated.
|
||||
inline explicit String(Array<char> buffer); |
||||
// Does not copy. Requires `buffer` ends with `\0`.
|
||||
|
||||
inline operator ArrayPtr<char>(); |
||||
inline operator ArrayPtr<const char>() const; |
||||
inline ArrayPtr<char> asArray(); |
||||
inline ArrayPtr<const char> asArray() const; |
||||
inline ArrayPtr<byte> asBytes() { return asArray().asBytes(); } |
||||
inline ArrayPtr<const byte> asBytes() const { return asArray().asBytes(); } |
||||
// Result does not include NUL terminator.
|
||||
|
||||
inline Array<char> releaseArray() { return kj::mv(content); } |
||||
// Disowns the backing array (which includes the NUL terminator) and returns it. The String value
|
||||
// is clobbered (as if moved away).
|
||||
|
||||
inline const char* cStr() const; |
||||
|
||||
inline size_t size() const; |
||||
// Result does not include NUL terminator.
|
||||
|
||||
inline char operator[](size_t index) const; |
||||
inline char& operator[](size_t index); |
||||
|
||||
inline char* begin(); |
||||
inline char* end(); |
||||
inline const char* begin() const; |
||||
inline const char* end() const; |
||||
|
||||
inline bool operator==(decltype(nullptr)) const { return content.size() <= 1; } |
||||
inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; } |
||||
|
||||
inline bool operator==(const StringPtr& other) const { return StringPtr(*this) == other; } |
||||
inline bool operator!=(const StringPtr& other) const { return StringPtr(*this) != other; } |
||||
inline bool operator< (const StringPtr& other) const { return StringPtr(*this) < other; } |
||||
inline bool operator> (const StringPtr& other) const { return StringPtr(*this) > other; } |
||||
inline bool operator<=(const StringPtr& other) const { return StringPtr(*this) <= other; } |
||||
inline bool operator>=(const StringPtr& other) const { return StringPtr(*this) >= other; } |
||||
|
||||
inline bool startsWith(const StringPtr& other) const { return StringPtr(*this).startsWith(other);} |
||||
inline bool endsWith(const StringPtr& other) const { return StringPtr(*this).endsWith(other); } |
||||
|
||||
inline StringPtr slice(size_t start) const { return StringPtr(*this).slice(start); } |
||||
inline ArrayPtr<const char> slice(size_t start, size_t end) const { |
||||
return StringPtr(*this).slice(start, end); |
||||
} |
||||
|
||||
inline Maybe<size_t> findFirst(char c) const { return StringPtr(*this).findFirst(c); } |
||||
inline Maybe<size_t> findLast(char c) const { return StringPtr(*this).findLast(c); } |
||||
|
||||
template <typename T> |
||||
T parseAs() const { return StringPtr(*this).parseAs<T>(); } |
||||
// Parse as number
|
||||
|
||||
private: |
||||
Array<char> content; |
||||
}; |
||||
|
||||
inline bool operator==(const char* a, const String& b) { return b == a; } |
||||
inline bool operator!=(const char* a, const String& b) { return b != a; } |
||||
|
||||
String heapString(size_t size); |
||||
// Allocate a String of the given size on the heap, not including NUL terminator. The NUL
|
||||
// terminator will be initialized automatically but the rest of the content is not initialized.
|
||||
|
||||
String heapString(const char* value); |
||||
String heapString(const char* value, size_t size); |
||||
String heapString(StringPtr value); |
||||
String heapString(const String& value); |
||||
String heapString(ArrayPtr<const char> value); |
||||
// Allocates a copy of the given value on the heap.
|
||||
|
||||
// =======================================================================================
|
||||
// Magic str() function which transforms parameters to text and concatenates them into one big
|
||||
// String.
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
inline size_t sum(std::initializer_list<size_t> nums) { |
||||
size_t result = 0; |
||||
for (auto num: nums) { |
||||
result += num; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline char* fill(char* ptr) { return ptr; } |
||||
|
||||
template <typename... Rest> |
||||
char* fill(char* __restrict__ target, const StringTree& first, Rest&&... rest); |
||||
// Make str() work with stringifiers that return StringTree by patching fill().
|
||||
//
|
||||
// Defined in string-tree.h.
|
||||
|
||||
template <typename First, typename... Rest> |
||||
char* fill(char* __restrict__ target, const First& first, Rest&&... rest) { |
||||
auto i = first.begin(); |
||||
auto end = first.end(); |
||||
while (i != end) { |
||||
*target++ = *i++; |
||||
} |
||||
return fill(target, kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename... Params> |
||||
String concat(Params&&... params) { |
||||
// Concatenate a bunch of containers into a single Array. The containers can be anything that
|
||||
// is iterable and whose elements can be converted to `char`.
|
||||
|
||||
String result = heapString(sum({params.size()...})); |
||||
fill(result.begin(), kj::fwd<Params>(params)...); |
||||
return result; |
||||
} |
||||
|
||||
inline String concat(String&& arr) { |
||||
return kj::mv(arr); |
||||
} |
||||
|
||||
struct Stringifier { |
||||
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
|
||||
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
|
||||
// The container type must have a `size()` method. Be sure to declare the operator in the same
|
||||
// namespace as `T` **or** in the global scope.
|
||||
//
|
||||
// A more usual way to accomplish what we're doing here would be to require that you define
|
||||
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
|
||||
// the problem that it pollutes other people's namespaces and even the global namespace. For
|
||||
// example, some other project may already have functions called `toString` which do something
|
||||
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
|
||||
// anything.
|
||||
|
||||
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; } |
||||
inline ArrayPtr<const char> operator*(ArrayPtr<char> s) const { return s; } |
||||
inline ArrayPtr<const char> operator*(const Array<const char>& s) const { return s; } |
||||
inline ArrayPtr<const char> operator*(const Array<char>& s) const { return s; } |
||||
template<size_t n> |
||||
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; } |
||||
template<size_t n> |
||||
inline ArrayPtr<const char> operator*(const FixedArray<char, n>& s) const { return s; } |
||||
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); } |
||||
inline ArrayPtr<const char> operator*(const String& s) const { return s.asArray(); } |
||||
inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); } |
||||
|
||||
inline Range<char> operator*(const Range<char>& r) const { return r; } |
||||
inline Repeat<char> operator*(const Repeat<char>& r) const { return r; } |
||||
|
||||
inline FixedArray<char, 1> operator*(char c) const { |
||||
FixedArray<char, 1> result; |
||||
result[0] = c; |
||||
return result; |
||||
} |
||||
|
||||
StringPtr operator*(decltype(nullptr)) const; |
||||
StringPtr operator*(bool b) const; |
||||
|
||||
CappedArray<char, 5> operator*(signed char i) const; |
||||
CappedArray<char, 5> operator*(unsigned char i) const; |
||||
CappedArray<char, sizeof(short) * 3 + 2> operator*(short i) const; |
||||
CappedArray<char, sizeof(unsigned short) * 3 + 2> operator*(unsigned short i) const; |
||||
CappedArray<char, sizeof(int) * 3 + 2> operator*(int i) const; |
||||
CappedArray<char, sizeof(unsigned int) * 3 + 2> operator*(unsigned int i) const; |
||||
CappedArray<char, sizeof(long) * 3 + 2> operator*(long i) const; |
||||
CappedArray<char, sizeof(unsigned long) * 3 + 2> operator*(unsigned long i) const; |
||||
CappedArray<char, sizeof(long long) * 3 + 2> operator*(long long i) const; |
||||
CappedArray<char, sizeof(unsigned long long) * 3 + 2> operator*(unsigned long long i) const; |
||||
CappedArray<char, 24> operator*(float f) const; |
||||
CappedArray<char, 32> operator*(double f) const; |
||||
CappedArray<char, sizeof(const void*) * 3 + 2> operator*(const void* s) const; |
||||
|
||||
template <typename T> |
||||
String operator*(ArrayPtr<T> arr) const; |
||||
template <typename T> |
||||
String operator*(const Array<T>& arr) const; |
||||
|
||||
#if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP // supports expression SFINAE?
|
||||
template <typename T, typename Result = decltype(instance<T>().toString())> |
||||
inline Result operator*(T&& value) const { return kj::fwd<T>(value).toString(); } |
||||
#endif |
||||
}; |
||||
static KJ_CONSTEXPR(const) Stringifier STR = Stringifier(); |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T> |
||||
auto toCharSequence(T&& value) -> decltype(_::STR * kj::fwd<T>(value)) { |
||||
// Returns an iterable of chars that represent a textual representation of the value, suitable
|
||||
// for debugging.
|
||||
//
|
||||
// Most users should use str() instead, but toCharSequence() may occasionally be useful to avoid
|
||||
// heap allocation overhead that str() implies.
|
||||
//
|
||||
// To specialize this function for your type, see KJ_STRINGIFY.
|
||||
|
||||
return _::STR * kj::fwd<T>(value); |
||||
} |
||||
|
||||
CappedArray<char, sizeof(unsigned char) * 2 + 1> hex(unsigned char i); |
||||
CappedArray<char, sizeof(unsigned short) * 2 + 1> hex(unsigned short i); |
||||
CappedArray<char, sizeof(unsigned int) * 2 + 1> hex(unsigned int i); |
||||
CappedArray<char, sizeof(unsigned long) * 2 + 1> hex(unsigned long i); |
||||
CappedArray<char, sizeof(unsigned long long) * 2 + 1> hex(unsigned long long i); |
||||
|
||||
template <typename... Params> |
||||
String str(Params&&... params) { |
||||
// Magic function which builds a string from a bunch of arbitrary values. Example:
|
||||
// str(1, " / ", 2, " = ", 0.5)
|
||||
// returns:
|
||||
// "1 / 2 = 0.5"
|
||||
// To teach `str` how to stringify a type, see `Stringifier`.
|
||||
|
||||
return _::concat(toCharSequence(kj::fwd<Params>(params))...); |
||||
} |
||||
|
||||
inline String str(String&& s) { return mv(s); } |
||||
// Overload to prevent redundant allocation.
|
||||
|
||||
template <typename T> |
||||
String strArray(T&& arr, const char* delim) { |
||||
size_t delimLen = strlen(delim); |
||||
KJ_STACK_ARRAY(decltype(_::STR * arr[0]), pieces, kj::size(arr), 8, 32); |
||||
size_t size = 0; |
||||
for (size_t i = 0; i < kj::size(arr); i++) { |
||||
if (i > 0) size += delimLen; |
||||
pieces[i] = _::STR * arr[i]; |
||||
size += pieces[i].size(); |
||||
} |
||||
|
||||
String result = heapString(size); |
||||
char* pos = result.begin(); |
||||
for (size_t i = 0; i < kj::size(arr); i++) { |
||||
if (i > 0) { |
||||
memcpy(pos, delim, delimLen); |
||||
pos += delimLen; |
||||
} |
||||
pos = _::fill(pos, pieces[i]); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> |
||||
inline String Stringifier::operator*(ArrayPtr<T> arr) const { |
||||
return strArray(arr, ", "); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline String Stringifier::operator*(const Array<T>& arr) const { |
||||
return strArray(arr, ", "); |
||||
} |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#define KJ_STRINGIFY(...) operator*(::kj::_::Stringifier, __VA_ARGS__) |
||||
// Defines a stringifier for a custom type. Example:
|
||||
//
|
||||
// class Foo {...};
|
||||
// inline StringPtr KJ_STRINGIFY(const Foo& foo) { return foo.name(); }
|
||||
//
|
||||
// This allows Foo to be passed to str().
|
||||
//
|
||||
// The function should be declared either in the same namespace as the target type or in the global
|
||||
// namespace. It can return any type which is an iterable container of chars.
|
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details.
|
||||
|
||||
inline StringPtr::StringPtr(const String& value): content(value.begin(), value.size() + 1) {} |
||||
|
||||
inline StringPtr::operator ArrayPtr<const char>() const { |
||||
return content.slice(0, content.size() - 1); |
||||
} |
||||
|
||||
inline ArrayPtr<const char> StringPtr::asArray() const { |
||||
return content.slice(0, content.size() - 1); |
||||
} |
||||
|
||||
inline bool StringPtr::operator==(const StringPtr& other) const { |
||||
return content.size() == other.content.size() && |
||||
memcmp(content.begin(), other.content.begin(), content.size() - 1) == 0; |
||||
} |
||||
|
||||
inline bool StringPtr::operator<(const StringPtr& other) const { |
||||
bool shorter = content.size() < other.content.size(); |
||||
int cmp = memcmp(content.begin(), other.content.begin(), |
||||
shorter ? content.size() : other.content.size()); |
||||
return cmp < 0 || (cmp == 0 && shorter); |
||||
} |
||||
|
||||
inline StringPtr StringPtr::slice(size_t start) const { |
||||
return StringPtr(content.slice(start, content.size())); |
||||
} |
||||
inline ArrayPtr<const char> StringPtr::slice(size_t start, size_t end) const { |
||||
return content.slice(start, end); |
||||
} |
||||
|
||||
inline bool StringPtr::startsWith(const StringPtr& other) const { |
||||
return other.content.size() <= content.size() && |
||||
memcmp(content.begin(), other.content.begin(), other.size()) == 0; |
||||
} |
||||
inline bool StringPtr::endsWith(const StringPtr& other) const { |
||||
return other.content.size() <= content.size() && |
||||
memcmp(end() - other.size(), other.content.begin(), other.size()) == 0; |
||||
} |
||||
|
||||
inline Maybe<size_t> StringPtr::findFirst(char c) const { |
||||
const char* pos = reinterpret_cast<const char*>(memchr(content.begin(), c, size())); |
||||
if (pos == nullptr) { |
||||
return nullptr; |
||||
} else { |
||||
return pos - content.begin(); |
||||
} |
||||
} |
||||
|
||||
inline Maybe<size_t> StringPtr::findLast(char c) const { |
||||
for (size_t i = size(); i > 0; --i) { |
||||
if (content[i-1] == c) { |
||||
return i-1; |
||||
} |
||||
} |
||||
return nullptr; |
||||
} |
||||
|
||||
inline String::operator ArrayPtr<char>() { |
||||
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1); |
||||
} |
||||
inline String::operator ArrayPtr<const char>() const { |
||||
return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1); |
||||
} |
||||
|
||||
inline ArrayPtr<char> String::asArray() { |
||||
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1); |
||||
} |
||||
inline ArrayPtr<const char> String::asArray() const { |
||||
return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1); |
||||
} |
||||
|
||||
inline const char* String::cStr() const { return content == nullptr ? "" : content.begin(); } |
||||
|
||||
inline size_t String::size() const { return content == nullptr ? 0 : content.size() - 1; } |
||||
|
||||
inline char String::operator[](size_t index) const { return content[index]; } |
||||
inline char& String::operator[](size_t index) { return content[index]; } |
||||
|
||||
inline char* String::begin() { return content == nullptr ? nullptr : content.begin(); } |
||||
inline char* String::end() { return content == nullptr ? nullptr : content.end() - 1; } |
||||
inline const char* String::begin() const { return content == nullptr ? nullptr : content.begin(); } |
||||
inline const char* String::end() const { return content == nullptr ? nullptr : content.end() - 1; } |
||||
|
||||
inline String::String(char* value, size_t size, const ArrayDisposer& disposer) |
||||
: content(value, size + 1, disposer) { |
||||
KJ_IREQUIRE(value[size] == '\0', "String must be NUL-terminated."); |
||||
} |
||||
|
||||
inline String::String(Array<char> buffer): content(kj::mv(buffer)) { |
||||
KJ_IREQUIRE(content.size() > 0 && content.back() == '\0', "String must be NUL-terminated."); |
||||
} |
||||
|
||||
inline String heapString(const char* value) { |
||||
return heapString(value, strlen(value)); |
||||
} |
||||
inline String heapString(StringPtr value) { |
||||
return heapString(value.begin(), value.size()); |
||||
} |
||||
inline String heapString(const String& value) { |
||||
return heapString(value.begin(), value.size()); |
||||
} |
||||
inline String heapString(ArrayPtr<const char> value) { |
||||
return heapString(value.begin(), value.size()); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_STRING_H_
|
@ -0,0 +1,167 @@ |
||||
// 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 KJ_TEST_H_ |
||||
#define KJ_TEST_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "debug.h" |
||||
#include "vector.h" |
||||
#include "function.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class TestRunner; |
||||
|
||||
class TestCase { |
||||
public: |
||||
TestCase(const char* file, uint line, const char* description); |
||||
~TestCase(); |
||||
|
||||
virtual void run() = 0; |
||||
|
||||
private: |
||||
const char* file; |
||||
uint line; |
||||
const char* description; |
||||
TestCase* next; |
||||
TestCase** prev; |
||||
bool matchedFilter; |
||||
|
||||
friend class TestRunner; |
||||
}; |
||||
|
||||
#define KJ_TEST(description) \ |
||||
/* Make sure the linker fails if tests are not in anonymous namespaces. */ \
|
||||
extern int KJ_CONCAT(YouMustWrapTestsInAnonymousNamespace, __COUNTER__) KJ_UNUSED; \
|
||||
class KJ_UNIQUE_NAME(TestCase): public ::kj::TestCase { \
|
||||
public: \
|
||||
KJ_UNIQUE_NAME(TestCase)(): ::kj::TestCase(__FILE__, __LINE__, description) {} \
|
||||
void run() override; \
|
||||
} KJ_UNIQUE_NAME(testCase); \
|
||||
void KJ_UNIQUE_NAME(TestCase)::run() |
||||
|
||||
#if _MSC_VER |
||||
#define KJ_INDIRECT_EXPAND(m, vargs) m vargs |
||||
#define KJ_FAIL_EXPECT(...) \ |
||||
KJ_INDIRECT_EXPAND(KJ_LOG, (ERROR , __VA_ARGS__)); |
||||
#define KJ_EXPECT(cond, ...) \ |
||||
if (cond); else KJ_INDIRECT_EXPAND(KJ_FAIL_EXPECT, ("failed: expected " #cond , __VA_ARGS__)) |
||||
#else |
||||
#define KJ_FAIL_EXPECT(...) \ |
||||
KJ_LOG(ERROR, ##__VA_ARGS__); |
||||
#define KJ_EXPECT(cond, ...) \ |
||||
if (cond); else KJ_FAIL_EXPECT("failed: expected " #cond, ##__VA_ARGS__) |
||||
#endif |
||||
|
||||
#define KJ_EXPECT_THROW_RECOVERABLE(type, code) \ |
||||
do { \
|
||||
KJ_IF_MAYBE(e, ::kj::runCatchingExceptions([&]() { code; })) { \
|
||||
KJ_EXPECT(e->getType() == ::kj::Exception::Type::type, \
|
||||
"code threw wrong exception type: " #code, e->getType()); \
|
||||
} else { \
|
||||
KJ_FAIL_EXPECT("code did not throw: " #code); \
|
||||
} \
|
||||
} while (false) |
||||
|
||||
#define KJ_EXPECT_THROW_RECOVERABLE_MESSAGE(message, code) \ |
||||
do { \
|
||||
KJ_IF_MAYBE(e, ::kj::runCatchingExceptions([&]() { code; })) { \
|
||||
KJ_EXPECT(::kj::_::hasSubstring(e->getDescription(), message), \
|
||||
"exception description didn't contain expected substring", e->getDescription()); \
|
||||
} else { \
|
||||
KJ_FAIL_EXPECT("code did not throw: " #code); \
|
||||
} \
|
||||
} while (false) |
||||
|
||||
#if KJ_NO_EXCEPTIONS |
||||
#define KJ_EXPECT_THROW(type, code) \ |
||||
do { \
|
||||
KJ_EXPECT(::kj::_::expectFatalThrow(type, nullptr, [&]() { code; })); \
|
||||
} while (false) |
||||
#define KJ_EXPECT_THROW_MESSAGE(message, code) \ |
||||
do { \
|
||||
KJ_EXPECT(::kj::_::expectFatalThrow(nullptr, kj::StringPtr(message), [&]() { code; })); \
|
||||
} while (false) |
||||
#else |
||||
#define KJ_EXPECT_THROW KJ_EXPECT_THROW_RECOVERABLE |
||||
#define KJ_EXPECT_THROW_MESSAGE KJ_EXPECT_THROW_RECOVERABLE_MESSAGE |
||||
#endif |
||||
|
||||
#define KJ_EXPECT_LOG(level, substring) \ |
||||
::kj::_::LogExpectation KJ_UNIQUE_NAME(_kjLogExpectation)(::kj::LogSeverity::level, substring) |
||||
// Expects that a log message with the given level and substring text will be printed within
|
||||
// the current scope. This message will not cause the test to fail, even if it is an error.
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
bool hasSubstring(kj::StringPtr haystack, kj::StringPtr needle); |
||||
|
||||
#if KJ_NO_EXCEPTIONS |
||||
bool expectFatalThrow(Maybe<Exception::Type> type, Maybe<StringPtr> message, |
||||
Function<void()> code); |
||||
// Expects that the given code will throw a fatal exception matching the given type and/or message.
|
||||
// Since exceptions are disabled, the test will fork() and run in a subprocess. On Windows, where
|
||||
// fork() is not available, this always returns true.
|
||||
#endif |
||||
|
||||
class LogExpectation: public ExceptionCallback { |
||||
public: |
||||
LogExpectation(LogSeverity severity, StringPtr substring); |
||||
~LogExpectation(); |
||||
|
||||
void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, |
||||
String&& text) override; |
||||
|
||||
private: |
||||
LogSeverity severity; |
||||
StringPtr substring; |
||||
bool seen; |
||||
UnwindDetector unwindDetector; |
||||
}; |
||||
|
||||
class GlobFilter { |
||||
// Implements glob filters for the --filter flag.
|
||||
//
|
||||
// Exposed in header only for testing.
|
||||
|
||||
public: |
||||
explicit GlobFilter(const char* pattern); |
||||
explicit GlobFilter(ArrayPtr<const char> pattern); |
||||
|
||||
bool matches(StringPtr name); |
||||
|
||||
private: |
||||
String pattern; |
||||
Vector<uint> states; |
||||
|
||||
void applyState(char c, int state); |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_TEST_H_
|
@ -0,0 +1,82 @@ |
||||
// 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 KJ_THREAD_H_ |
||||
#define KJ_THREAD_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "common.h" |
||||
#include "function.h" |
||||
#include "exception.h" |
||||
|
||||
namespace kj { |
||||
|
||||
class Thread { |
||||
// A thread! Pass a lambda to the constructor, and it runs in the thread. The destructor joins
|
||||
// the thread. If the function throws an exception, it is rethrown from the thread's destructor
|
||||
// (if not unwinding from another exception).
|
||||
|
||||
public: |
||||
explicit Thread(Function<void()> func); |
||||
KJ_DISALLOW_COPY(Thread); |
||||
|
||||
~Thread() noexcept(false); |
||||
|
||||
#if !_WIN32 |
||||
void sendSignal(int signo); |
||||
// Send a Unix signal to the given thread, using pthread_kill or an equivalent.
|
||||
#endif |
||||
|
||||
void detach(); |
||||
// Don't join the thread in ~Thread().
|
||||
|
||||
private: |
||||
struct ThreadState { |
||||
Function<void()> func; |
||||
kj::Maybe<kj::Exception> exception; |
||||
|
||||
unsigned int refcount; |
||||
// Owned by the parent thread and the child thread.
|
||||
|
||||
void unref(); |
||||
}; |
||||
ThreadState* state; |
||||
|
||||
#if _WIN32 |
||||
void* threadHandle; |
||||
#else |
||||
unsigned long long threadId; // actually pthread_t
|
||||
#endif |
||||
bool detached = false; |
||||
|
||||
#if _WIN32 |
||||
static unsigned long __stdcall runThread(void* ptr); |
||||
#else |
||||
static void* runThread(void* ptr); |
||||
#endif |
||||
}; |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_THREAD_H_
|
@ -0,0 +1,136 @@ |
||||
// Copyright (c) 2014, Jason Choy <jjwchoy@gmail.com>
|
||||
// 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 KJ_THREADLOCAL_H_ |
||||
#define KJ_THREADLOCAL_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
// This file declares a macro `KJ_THREADLOCAL_PTR` for declaring thread-local pointer-typed
|
||||
// variables. Use like:
|
||||
// KJ_THREADLOCAL_PTR(MyType) foo = nullptr;
|
||||
// This is equivalent to:
|
||||
// thread_local MyType* foo = nullptr;
|
||||
// This can only be used at the global scope.
|
||||
//
|
||||
// AVOID USING THIS. Use of thread-locals is discouraged because they often have many of the same
|
||||
// properties as singletons: http://www.object-oriented-security.org/lets-argue/singletons
|
||||
//
|
||||
// Also, thread-locals tend to be hostile to event-driven code, which can be particularly
|
||||
// surprising when using fibers (all fibers in the same thread will share the same threadlocals,
|
||||
// even though they do not share a stack).
|
||||
//
|
||||
// That said, thread-locals are sometimes needed for runtime logistics in the KJ framework. For
|
||||
// example, the current exception callback and current EventLoop are stored as thread-local
|
||||
// pointers. Since KJ only ever needs to store pointers, not values, we avoid the question of
|
||||
// whether these values' destructors need to be run, and we avoid the need for heap allocation.
|
||||
|
||||
#include "common.h" |
||||
|
||||
#if !defined(KJ_USE_PTHREAD_THREADLOCAL) && defined(__APPLE__) |
||||
#include "TargetConditionals.h" |
||||
#if TARGET_OS_IPHONE |
||||
// iOS apparently does not support __thread (nor C++11 thread_local).
|
||||
#define KJ_USE_PTHREAD_TLS 1 |
||||
#endif |
||||
#endif |
||||
|
||||
#if KJ_USE_PTHREAD_TLS |
||||
#include <pthread.h> |
||||
#endif |
||||
|
||||
namespace kj { |
||||
|
||||
#if KJ_USE_PTHREAD_TLS |
||||
// If __thread is unavailable, we'll fall back to pthreads.
|
||||
|
||||
#define KJ_THREADLOCAL_PTR(type) \ |
||||
namespace { struct KJ_UNIQUE_NAME(_kj_TlpTag); } \
|
||||
static ::kj::_::ThreadLocalPtr< type, KJ_UNIQUE_NAME(_kj_TlpTag)> |
||||
// Hack: In order to ensure each thread-local results in a unique template instance, we declare
|
||||
// a one-off dummy type to use as the second type parameter.
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, typename> |
||||
class ThreadLocalPtr { |
||||
// Hacky type to emulate __thread T*. We need a separate instance of the ThreadLocalPtr template
|
||||
// for every thread-local variable, because we don't want to require a global constructor, and in
|
||||
// order to initialize the TLS on first use we need to use a local static variable (in getKey()).
|
||||
// Each template instance will get a separate such local static variable, fulfilling our need.
|
||||
|
||||
public: |
||||
ThreadLocalPtr() = default; |
||||
constexpr ThreadLocalPtr(decltype(nullptr)) {} |
||||
// Allow initialization to nullptr without a global constructor.
|
||||
|
||||
inline ThreadLocalPtr& operator=(T* val) { |
||||
pthread_setspecific(getKey(), val); |
||||
return *this; |
||||
} |
||||
|
||||
inline operator T*() const { |
||||
return get(); |
||||
} |
||||
|
||||
inline T& operator*() const { |
||||
return *get(); |
||||
} |
||||
|
||||
inline T* operator->() const { |
||||
return get(); |
||||
} |
||||
|
||||
private: |
||||
inline T* get() const { |
||||
return reinterpret_cast<T*>(pthread_getspecific(getKey())); |
||||
} |
||||
|
||||
inline static pthread_key_t getKey() { |
||||
static pthread_key_t key = createKey(); |
||||
return key; |
||||
} |
||||
|
||||
static pthread_key_t createKey() { |
||||
pthread_key_t key; |
||||
pthread_key_create(&key, 0); |
||||
return key; |
||||
} |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#elif __GNUC__ |
||||
|
||||
#define KJ_THREADLOCAL_PTR(type) static __thread type* |
||||
// GCC's __thread is lighter-weight than thread_local and is good enough for our purposes.
|
||||
|
||||
#else |
||||
|
||||
#define KJ_THREADLOCAL_PTR(type) static thread_local type* |
||||
|
||||
#endif // KJ_USE_PTHREAD_TLS
|
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_THREADLOCAL_H_
|
@ -0,0 +1,174 @@ |
||||
// Copyright (c) 2014 Google Inc. (contributed by Remy Blank <rblank@google.com>)
|
||||
// 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 KJ_TIME_H_ |
||||
#define KJ_TIME_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "async.h" |
||||
#include "units.h" |
||||
#include <inttypes.h> |
||||
|
||||
namespace kj { |
||||
namespace _ { // private
|
||||
|
||||
class NanosecondLabel; |
||||
class TimeLabel; |
||||
class DateLabel; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
using Duration = Quantity<int64_t, _::NanosecondLabel>; |
||||
// A time value, in nanoseconds.
|
||||
|
||||
constexpr Duration NANOSECONDS = unit<Duration>(); |
||||
constexpr Duration MICROSECONDS = 1000 * NANOSECONDS; |
||||
constexpr Duration MILLISECONDS = 1000 * MICROSECONDS; |
||||
constexpr Duration SECONDS = 1000 * MILLISECONDS; |
||||
constexpr Duration MINUTES = 60 * SECONDS; |
||||
constexpr Duration HOURS = 60 * MINUTES; |
||||
constexpr Duration DAYS = 24 * HOURS; |
||||
|
||||
using TimePoint = Absolute<Duration, _::TimeLabel>; |
||||
// An absolute time measured by some particular instance of `Timer`. `Time`s from two different
|
||||
// `Timer`s may be measured from different origins and so are not necessarily compatible.
|
||||
|
||||
using Date = Absolute<Duration, _::DateLabel>; |
||||
// A point in real-world time, measured relative to the Unix epoch (Jan 1, 1970 00:00:00 UTC).
|
||||
|
||||
constexpr Date UNIX_EPOCH = origin<Date>(); |
||||
// The `Date` representing Jan 1, 1970 00:00:00 UTC.
|
||||
|
||||
class Clock { |
||||
// Interface to read the current date and time.
|
||||
public: |
||||
virtual Date now() = 0; |
||||
}; |
||||
|
||||
Clock& nullClock(); |
||||
// A clock which always returns UNIX_EPOCH as the current time. Useful when you don't care about
|
||||
// time.
|
||||
|
||||
class Timer { |
||||
// Interface to time and timer functionality.
|
||||
//
|
||||
// Each `Timer` may have a different origin, and some `Timer`s may in fact tick at a different
|
||||
// rate than real time (e.g. a `Timer` could represent CPU time consumed by a thread). However,
|
||||
// all `Timer`s are monotonic: time will never appear to move backwards, even if the calendar
|
||||
// date as tracked by the system is manually modified.
|
||||
|
||||
public: |
||||
virtual TimePoint now() = 0; |
||||
// Returns the current value of a clock that moves steadily forward, independent of any
|
||||
// changes in the wall clock. The value is updated every time the event loop waits,
|
||||
// and is constant in-between waits.
|
||||
|
||||
virtual Promise<void> atTime(TimePoint time) = 0; |
||||
// Returns a promise that returns as soon as now() >= time.
|
||||
|
||||
virtual Promise<void> afterDelay(Duration delay) = 0; |
||||
// Equivalent to atTime(now() + delay).
|
||||
|
||||
template <typename T> |
||||
Promise<T> timeoutAt(TimePoint time, Promise<T>&& promise) KJ_WARN_UNUSED_RESULT; |
||||
// Return a promise equivalent to `promise` but which throws an exception (and cancels the
|
||||
// original promise) if it hasn't completed by `time`. The thrown exception is of type
|
||||
// "OVERLOADED".
|
||||
|
||||
template <typename T> |
||||
Promise<T> timeoutAfter(Duration delay, Promise<T>&& promise) KJ_WARN_UNUSED_RESULT; |
||||
// Return a promise equivalent to `promise` but which throws an exception (and cancels the
|
||||
// original promise) if it hasn't completed after `delay` from now. The thrown exception is of
|
||||
// type "OVERLOADED".
|
||||
|
||||
private: |
||||
static kj::Exception makeTimeoutException(); |
||||
}; |
||||
|
||||
class TimerImpl final: public Timer { |
||||
// Implementation of Timer that expects an external caller -- usually, the EventPort
|
||||
// implementation -- to tell it when time has advanced.
|
||||
|
||||
public: |
||||
TimerImpl(TimePoint startTime); |
||||
~TimerImpl() noexcept(false); |
||||
|
||||
Maybe<TimePoint> nextEvent(); |
||||
// Returns the time at which the next scheduled timer event will occur, or null if no timer
|
||||
// events are scheduled.
|
||||
|
||||
Maybe<uint64_t> timeoutToNextEvent(TimePoint start, Duration unit, uint64_t max); |
||||
// Convenience method which computes a timeout value to pass to an event-waiting system call to
|
||||
// cause it to time out when the next timer event occurs.
|
||||
//
|
||||
// `start` is the time at which the timeout starts counting. This is typically not the same as
|
||||
// now() since some time may have passed since the last time advanceTo() was called.
|
||||
//
|
||||
// `unit` is the time unit in which the timeout is measured. This is often MILLISECONDS. Note
|
||||
// that this method will fractional values *up*, to guarantee that the returned timeout waits
|
||||
// until just *after* the time the event is scheduled.
|
||||
//
|
||||
// The timeout will be clamped to `max`. Use this to avoid an overflow if e.g. the OS wants a
|
||||
// 32-bit value or a signed value.
|
||||
//
|
||||
// Returns nullptr if there are no future events.
|
||||
|
||||
void advanceTo(TimePoint newTime); |
||||
// Set the time to `time` and fire any at() events that have been passed.
|
||||
|
||||
// implements Timer ----------------------------------------------------------
|
||||
TimePoint now() override; |
||||
Promise<void> atTime(TimePoint time) override; |
||||
Promise<void> afterDelay(Duration delay) override; |
||||
|
||||
private: |
||||
struct Impl; |
||||
class TimerPromiseAdapter; |
||||
TimePoint time; |
||||
Own<Impl> impl; |
||||
}; |
||||
|
||||
// =======================================================================================
|
||||
// inline implementation details
|
||||
|
||||
template <typename T> |
||||
Promise<T> Timer::timeoutAt(TimePoint time, Promise<T>&& promise) { |
||||
return promise.exclusiveJoin(atTime(time).then([]() -> kj::Promise<T> { |
||||
return makeTimeoutException(); |
||||
})); |
||||
} |
||||
|
||||
template <typename T> |
||||
Promise<T> Timer::timeoutAfter(Duration delay, Promise<T>&& promise) { |
||||
return promise.exclusiveJoin(afterDelay(delay).then([]() -> kj::Promise<T> { |
||||
return makeTimeoutException(); |
||||
})); |
||||
} |
||||
|
||||
inline TimePoint TimerImpl::now() { return time; } |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_TIME_H_
|
@ -0,0 +1,364 @@ |
||||
// 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 defines a notion of tuples that is simpler that `std::tuple`. It works as follows:
|
||||
// - `kj::Tuple<A, B, C> is the type of a tuple of an A, a B, and a C.
|
||||
// - `kj::tuple(a, b, c)` returns a tuple containing a, b, and c. If any of these are themselves
|
||||
// tuples, they are flattened, so `tuple(a, tuple(b, c), d)` is equivalent to `tuple(a, b, c, d)`.
|
||||
// - `kj::get<n>(myTuple)` returns the element of `myTuple` at index n.
|
||||
// - `kj::apply(func, ...)` calls func on the following arguments after first expanding any tuples
|
||||
// in the argument list. So `kj::apply(foo, a, tuple(b, c), d)` would call `foo(a, b, c, d)`.
|
||||
//
|
||||
// Note that:
|
||||
// - The type `Tuple<T>` is a synonym for T. This is why `get` and `apply` are not members of the
|
||||
// type.
|
||||
// - It is illegal for an element of `Tuple` to itself be a tuple, as tuples are meant to be
|
||||
// flattened.
|
||||
// - It is illegal for an element of `Tuple` to be a reference, due to problems this would cause
|
||||
// with type inference and `tuple()`.
|
||||
|
||||
#ifndef KJ_TUPLE_H_ |
||||
#define KJ_TUPLE_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "common.h" |
||||
|
||||
namespace kj { |
||||
namespace _ { // private
|
||||
|
||||
template <size_t index, typename... T> |
||||
struct TypeByIndex_; |
||||
template <typename First, typename... Rest> |
||||
struct TypeByIndex_<0, First, Rest...> { |
||||
typedef First Type; |
||||
}; |
||||
template <size_t index, typename First, typename... Rest> |
||||
struct TypeByIndex_<index, First, Rest...> |
||||
: public TypeByIndex_<index - 1, Rest...> {}; |
||||
template <size_t index> |
||||
struct TypeByIndex_<index> { |
||||
static_assert(index != index, "Index out-of-range."); |
||||
}; |
||||
template <size_t index, typename... T> |
||||
using TypeByIndex = typename TypeByIndex_<index, T...>::Type; |
||||
// Chose a particular type out of a list of types, by index.
|
||||
|
||||
template <size_t... s> |
||||
struct Indexes {}; |
||||
// Dummy helper type that just encapsulates a sequential list of indexes, so that we can match
|
||||
// templates against them and unpack them with '...'.
|
||||
|
||||
template <size_t end, size_t... prefix> |
||||
struct MakeIndexes_: public MakeIndexes_<end - 1, end - 1, prefix...> {}; |
||||
template <size_t... prefix> |
||||
struct MakeIndexes_<0, prefix...> { |
||||
typedef Indexes<prefix...> Type; |
||||
}; |
||||
template <size_t end> |
||||
using MakeIndexes = typename MakeIndexes_<end>::Type; |
||||
// Equivalent to Indexes<0, 1, 2, ..., end>.
|
||||
|
||||
template <typename... T> |
||||
class Tuple; |
||||
template <size_t index, typename... U> |
||||
inline TypeByIndex<index, U...>& getImpl(Tuple<U...>& tuple); |
||||
template <size_t index, typename... U> |
||||
inline TypeByIndex<index, U...>&& getImpl(Tuple<U...>&& tuple); |
||||
template <size_t index, typename... U> |
||||
inline const TypeByIndex<index, U...>& getImpl(const Tuple<U...>& tuple); |
||||
|
||||
template <uint index, typename T> |
||||
struct TupleElement { |
||||
// Encapsulates one element of a tuple. The actual tuple implementation multiply-inherits
|
||||
// from a TupleElement for each element, which is more efficient than a recursive definition.
|
||||
|
||||
T value; |
||||
TupleElement() = default; |
||||
constexpr inline TupleElement(const T& value): value(value) {} |
||||
constexpr inline TupleElement(T&& value): value(kj::mv(value)) {} |
||||
}; |
||||
|
||||
template <uint index, typename T> |
||||
struct TupleElement<index, T&> { |
||||
// If tuples contained references, one of the following would have to be true:
|
||||
// - `auto x = tuple(y, z)` would cause x to be a tuple of references to y and z, which is
|
||||
// probably not what you expected.
|
||||
// - `Tuple<Foo&, Bar&> x = tuple(a, b)` would not work, because `tuple()` returned
|
||||
// Tuple<Foo, Bar>.
|
||||
static_assert(sizeof(T*) == 0, "Sorry, tuples cannot contain references."); |
||||
}; |
||||
|
||||
template <uint index, typename... T> |
||||
struct TupleElement<index, Tuple<T...>> { |
||||
static_assert(sizeof(Tuple<T...>*) == 0, |
||||
"Tuples cannot contain other tuples -- they should be flattened."); |
||||
}; |
||||
|
||||
template <typename Indexes, typename... Types> |
||||
struct TupleImpl; |
||||
|
||||
template <size_t... indexes, typename... Types> |
||||
struct TupleImpl<Indexes<indexes...>, Types...> |
||||
: public TupleElement<indexes, Types>... { |
||||
// Implementation of Tuple. The only reason we need this rather than rolling this into class
|
||||
// Tuple (below) is so that we can get "indexes" as an unpackable list.
|
||||
|
||||
static_assert(sizeof...(indexes) == sizeof...(Types), "Incorrect use of TupleImpl."); |
||||
|
||||
template <typename... Params> |
||||
inline TupleImpl(Params&&... params) |
||||
: TupleElement<indexes, Types>(kj::fwd<Params>(params))... { |
||||
// Work around Clang 3.2 bug 16303 where this is not detected. (Unfortunately, Clang sometimes
|
||||
// segfaults instead.)
|
||||
static_assert(sizeof...(params) == sizeof...(indexes), |
||||
"Wrong number of parameters to Tuple constructor."); |
||||
} |
||||
|
||||
template <typename... U> |
||||
constexpr inline TupleImpl(Tuple<U...>&& other) |
||||
: TupleElement<indexes, Types>(kj::mv(getImpl<indexes>(other)))... {} |
||||
template <typename... U> |
||||
constexpr inline TupleImpl(Tuple<U...>& other) |
||||
: TupleElement<indexes, Types>(getImpl<indexes>(other))... {} |
||||
template <typename... U> |
||||
constexpr inline TupleImpl(const Tuple<U...>& other) |
||||
: TupleElement<indexes, Types>(getImpl<indexes>(other))... {} |
||||
}; |
||||
|
||||
struct MakeTupleFunc; |
||||
|
||||
template <typename... T> |
||||
class Tuple { |
||||
// The actual Tuple class (used for tuples of size other than 1).
|
||||
|
||||
public: |
||||
template <typename... U> |
||||
constexpr inline Tuple(Tuple<U...>&& other): impl(kj::mv(other)) {} |
||||
template <typename... U> |
||||
constexpr inline Tuple(Tuple<U...>& other): impl(other) {} |
||||
template <typename... U> |
||||
constexpr inline Tuple(const Tuple<U...>& other): impl(other) {} |
||||
|
||||
private: |
||||
template <typename... Params> |
||||
constexpr Tuple(Params&&... params): impl(kj::fwd<Params>(params)...) {} |
||||
|
||||
TupleImpl<MakeIndexes<sizeof...(T)>, T...> impl; |
||||
|
||||
template <size_t index, typename... U> |
||||
friend inline TypeByIndex<index, U...>& getImpl(Tuple<U...>& tuple); |
||||
template <size_t index, typename... U> |
||||
friend inline TypeByIndex<index, U...>&& getImpl(Tuple<U...>&& tuple); |
||||
template <size_t index, typename... U> |
||||
friend inline const TypeByIndex<index, U...>& getImpl(const Tuple<U...>& tuple); |
||||
friend struct MakeTupleFunc; |
||||
}; |
||||
|
||||
template <> |
||||
class Tuple<> { |
||||
// Simplified zero-member version of Tuple. In particular this is important to make sure that
|
||||
// Tuple<>() is constexpr.
|
||||
}; |
||||
|
||||
template <typename T> |
||||
class Tuple<T>; |
||||
// Single-element tuple should never be used. The public API should ensure this.
|
||||
|
||||
template <size_t index, typename... T> |
||||
inline TypeByIndex<index, T...>& getImpl(Tuple<T...>& tuple) { |
||||
// Get member of a Tuple by index, e.g. `get<2>(myTuple)`.
|
||||
static_assert(index < sizeof...(T), "Tuple element index out-of-bounds."); |
||||
return implicitCast<TupleElement<index, TypeByIndex<index, T...>>&>(tuple.impl).value; |
||||
} |
||||
template <size_t index, typename... T> |
||||
inline TypeByIndex<index, T...>&& getImpl(Tuple<T...>&& tuple) { |
||||
// Get member of a Tuple by index, e.g. `get<2>(myTuple)`.
|
||||
static_assert(index < sizeof...(T), "Tuple element index out-of-bounds."); |
||||
return kj::mv(implicitCast<TupleElement<index, TypeByIndex<index, T...>>&>(tuple.impl).value); |
||||
} |
||||
template <size_t index, typename... T> |
||||
inline const TypeByIndex<index, T...>& getImpl(const Tuple<T...>& tuple) { |
||||
// Get member of a Tuple by index, e.g. `get<2>(myTuple)`.
|
||||
static_assert(index < sizeof...(T), "Tuple element index out-of-bounds."); |
||||
return implicitCast<const TupleElement<index, TypeByIndex<index, T...>>&>(tuple.impl).value; |
||||
} |
||||
template <size_t index, typename T> |
||||
inline T&& getImpl(T&& value) { |
||||
// Get member of a Tuple by index, e.g. `getImpl<2>(myTuple)`.
|
||||
|
||||
// Non-tuples are equivalent to one-element tuples.
|
||||
static_assert(index == 0, "Tuple element index out-of-bounds."); |
||||
return kj::fwd<T>(value); |
||||
} |
||||
|
||||
|
||||
template <typename Func, typename SoFar, typename... T> |
||||
struct ExpandAndApplyResult_; |
||||
// Template which computes the return type of applying Func to T... after flattening tuples.
|
||||
// SoFar starts as Tuple<> and accumulates the flattened parameter types -- so after this template
|
||||
// is recursively expanded, T... is empty and SoFar is a Tuple containing all the parameters.
|
||||
|
||||
template <typename Func, typename First, typename... Rest, typename... T> |
||||
struct ExpandAndApplyResult_<Func, Tuple<T...>, First, Rest...> |
||||
: public ExpandAndApplyResult_<Func, Tuple<T..., First>, Rest...> {}; |
||||
template <typename Func, typename... FirstTypes, typename... Rest, typename... T> |
||||
struct ExpandAndApplyResult_<Func, Tuple<T...>, Tuple<FirstTypes...>, Rest...> |
||||
: public ExpandAndApplyResult_<Func, Tuple<T...>, FirstTypes&&..., Rest...> {}; |
||||
template <typename Func, typename... FirstTypes, typename... Rest, typename... T> |
||||
struct ExpandAndApplyResult_<Func, Tuple<T...>, Tuple<FirstTypes...>&, Rest...> |
||||
: public ExpandAndApplyResult_<Func, Tuple<T...>, FirstTypes&..., Rest...> {}; |
||||
template <typename Func, typename... FirstTypes, typename... Rest, typename... T> |
||||
struct ExpandAndApplyResult_<Func, Tuple<T...>, const Tuple<FirstTypes...>&, Rest...> |
||||
: public ExpandAndApplyResult_<Func, Tuple<T...>, const FirstTypes&..., Rest...> {}; |
||||
template <typename Func, typename... T> |
||||
struct ExpandAndApplyResult_<Func, Tuple<T...>> { |
||||
typedef decltype(instance<Func>()(instance<T&&>()...)) Type; |
||||
}; |
||||
template <typename Func, typename... T> |
||||
using ExpandAndApplyResult = typename ExpandAndApplyResult_<Func, Tuple<>, T...>::Type; |
||||
// Computes the expected return type of `expandAndApply()`.
|
||||
|
||||
template <typename Func> |
||||
inline auto expandAndApply(Func&& func) -> ExpandAndApplyResult<Func> { |
||||
return func(); |
||||
} |
||||
|
||||
template <typename Func, typename First, typename... Rest> |
||||
struct ExpandAndApplyFunc { |
||||
Func&& func; |
||||
First&& first; |
||||
ExpandAndApplyFunc(Func&& func, First&& first) |
||||
: func(kj::fwd<Func>(func)), first(kj::fwd<First>(first)) {} |
||||
template <typename... T> |
||||
auto operator()(T&&... params) |
||||
-> decltype(this->func(kj::fwd<First>(first), kj::fwd<T>(params)...)) { |
||||
return this->func(kj::fwd<First>(first), kj::fwd<T>(params)...); |
||||
} |
||||
}; |
||||
|
||||
template <typename Func, typename First, typename... Rest> |
||||
inline auto expandAndApply(Func&& func, First&& first, Rest&&... rest) |
||||
-> ExpandAndApplyResult<Func, First, Rest...> { |
||||
|
||||
return expandAndApply( |
||||
ExpandAndApplyFunc<Func, First, Rest...>(kj::fwd<Func>(func), kj::fwd<First>(first)), |
||||
kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename Func, typename... FirstTypes, typename... Rest> |
||||
inline auto expandAndApply(Func&& func, Tuple<FirstTypes...>&& first, Rest&&... rest) |
||||
-> ExpandAndApplyResult<Func, FirstTypes&&..., Rest...> { |
||||
return expandAndApplyWithIndexes(MakeIndexes<sizeof...(FirstTypes)>(), |
||||
kj::fwd<Func>(func), kj::mv(first), kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename Func, typename... FirstTypes, typename... Rest> |
||||
inline auto expandAndApply(Func&& func, Tuple<FirstTypes...>& first, Rest&&... rest) |
||||
-> ExpandAndApplyResult<Func, FirstTypes..., Rest...> { |
||||
return expandAndApplyWithIndexes(MakeIndexes<sizeof...(FirstTypes)>(), |
||||
kj::fwd<Func>(func), first, kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename Func, typename... FirstTypes, typename... Rest> |
||||
inline auto expandAndApply(Func&& func, const Tuple<FirstTypes...>& first, Rest&&... rest) |
||||
-> ExpandAndApplyResult<Func, FirstTypes..., Rest...> { |
||||
return expandAndApplyWithIndexes(MakeIndexes<sizeof...(FirstTypes)>(), |
||||
kj::fwd<Func>(func), first, kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename Func, typename... FirstTypes, typename... Rest, size_t... indexes> |
||||
inline auto expandAndApplyWithIndexes( |
||||
Indexes<indexes...>, Func&& func, Tuple<FirstTypes...>&& first, Rest&&... rest) |
||||
-> ExpandAndApplyResult<Func, FirstTypes&&..., Rest...> { |
||||
return expandAndApply(kj::fwd<Func>(func), kj::mv(getImpl<indexes>(first))..., |
||||
kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
template <typename Func, typename... FirstTypes, typename... Rest, size_t... indexes> |
||||
inline auto expandAndApplyWithIndexes( |
||||
Indexes<indexes...>, Func&& func, const Tuple<FirstTypes...>& first, Rest&&... rest) |
||||
-> ExpandAndApplyResult<Func, FirstTypes..., Rest...> { |
||||
return expandAndApply(kj::fwd<Func>(func), getImpl<indexes>(first)..., |
||||
kj::fwd<Rest>(rest)...); |
||||
} |
||||
|
||||
struct MakeTupleFunc { |
||||
template <typename... Params> |
||||
Tuple<Decay<Params>...> operator()(Params&&... params) { |
||||
return Tuple<Decay<Params>...>(kj::fwd<Params>(params)...); |
||||
} |
||||
template <typename Param> |
||||
Decay<Param> operator()(Param&& param) { |
||||
return kj::fwd<Param>(param); |
||||
} |
||||
}; |
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename... T> struct Tuple_ { typedef _::Tuple<T...> Type; }; |
||||
template <typename T> struct Tuple_<T> { typedef T Type; }; |
||||
|
||||
template <typename... T> using Tuple = typename Tuple_<T...>::Type; |
||||
// Tuple type. `Tuple<T>` (i.e. a single-element tuple) is a synonym for `T`. Tuples of size
|
||||
// other than 1 expand to an internal type. Either way, you can construct a Tuple using
|
||||
// `kj::tuple(...)`, get an element by index `i` using `kj::get<i>(myTuple)`, and expand the tuple
|
||||
// as arguments to a function using `kj::apply(func, myTuple)`.
|
||||
//
|
||||
// Tuples are always flat -- that is, no element of a Tuple is ever itself a Tuple. If you
|
||||
// construct a tuple from other tuples, the elements are flattened and concatenated.
|
||||
|
||||
template <typename... Params> |
||||
inline auto tuple(Params&&... params) |
||||
-> decltype(_::expandAndApply(_::MakeTupleFunc(), kj::fwd<Params>(params)...)) { |
||||
// Construct a new tuple from the given values. Any tuples in the argument list will be
|
||||
// flattened into the result.
|
||||
return _::expandAndApply(_::MakeTupleFunc(), kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
template <size_t index, typename Tuple> |
||||
inline auto get(Tuple&& tuple) -> decltype(_::getImpl<index>(kj::fwd<Tuple>(tuple))) { |
||||
// Unpack and return the tuple element at the given index. The index is specified as a template
|
||||
// parameter, e.g. `kj::get<3>(myTuple)`.
|
||||
return _::getImpl<index>(kj::fwd<Tuple>(tuple)); |
||||
} |
||||
|
||||
template <typename Func, typename... Params> |
||||
inline auto apply(Func&& func, Params&&... params) |
||||
-> decltype(_::expandAndApply(kj::fwd<Func>(func), kj::fwd<Params>(params)...)) { |
||||
// Apply a function to some arguments, expanding tuples into separate arguments.
|
||||
return _::expandAndApply(kj::fwd<Func>(func), kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
template <typename T> struct TupleSize_ { static constexpr size_t size = 1; }; |
||||
template <typename... T> struct TupleSize_<_::Tuple<T...>> { |
||||
static constexpr size_t size = sizeof...(T); |
||||
}; |
||||
|
||||
template <typename T> |
||||
constexpr size_t tupleSize() { return TupleSize_<T>::size; } |
||||
// Returns size of the tuple T.
|
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_TUPLE_H_
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,144 @@ |
||||
// 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 KJ_VECTOR_H_ |
||||
#define KJ_VECTOR_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#include "array.h" |
||||
|
||||
namespace kj { |
||||
|
||||
template <typename T> |
||||
class Vector { |
||||
// Similar to std::vector, but based on KJ framework.
|
||||
//
|
||||
// This implementation always uses move constructors when growing the backing array. If the
|
||||
// move constructor throws, the Vector is left in an inconsistent state. This is acceptable
|
||||
// under KJ exception theory which assumes that exceptions leave things in inconsistent states.
|
||||
|
||||
// TODO(someday): Allow specifying a custom allocator.
|
||||
|
||||
public: |
||||
inline Vector() = default; |
||||
inline explicit Vector(size_t capacity): builder(heapArrayBuilder<T>(capacity)) {} |
||||
|
||||
inline operator ArrayPtr<T>() { return builder; } |
||||
inline operator ArrayPtr<const T>() const { return builder; } |
||||
inline ArrayPtr<T> asPtr() { return builder.asPtr(); } |
||||
inline ArrayPtr<const T> asPtr() const { return builder.asPtr(); } |
||||
|
||||
inline size_t size() const { return builder.size(); } |
||||
inline bool empty() const { return size() == 0; } |
||||
inline size_t capacity() const { return builder.capacity(); } |
||||
inline T& operator[](size_t index) const { return builder[index]; } |
||||
|
||||
inline const T* begin() const { return builder.begin(); } |
||||
inline const T* end() const { return builder.end(); } |
||||
inline const T& front() const { return builder.front(); } |
||||
inline const T& back() const { return builder.back(); } |
||||
inline T* begin() { return builder.begin(); } |
||||
inline T* end() { return builder.end(); } |
||||
inline T& front() { return builder.front(); } |
||||
inline T& back() { return builder.back(); } |
||||
|
||||
inline Array<T> releaseAsArray() { |
||||
// TODO(perf): Avoid a copy/move by allowing Array<T> to point to incomplete space?
|
||||
if (!builder.isFull()) { |
||||
setCapacity(size()); |
||||
} |
||||
return builder.finish(); |
||||
} |
||||
|
||||
template <typename... Params> |
||||
inline T& add(Params&&... params) { |
||||
if (builder.isFull()) grow(); |
||||
return builder.add(kj::fwd<Params>(params)...); |
||||
} |
||||
|
||||
template <typename Iterator> |
||||
inline void addAll(Iterator begin, Iterator end) { |
||||
size_t needed = builder.size() + (end - begin); |
||||
if (needed > builder.capacity()) grow(needed); |
||||
builder.addAll(begin, end); |
||||
} |
||||
|
||||
template <typename Container> |
||||
inline void addAll(Container&& container) { |
||||
addAll(container.begin(), container.end()); |
||||
} |
||||
|
||||
inline void removeLast() { |
||||
builder.removeLast(); |
||||
} |
||||
|
||||
inline void resize(size_t size) { |
||||
if (size > builder.capacity()) grow(size); |
||||
builder.resize(size); |
||||
} |
||||
|
||||
inline void operator=(decltype(nullptr)) { |
||||
builder = nullptr; |
||||
} |
||||
|
||||
inline void clear() { |
||||
while (builder.size() > 0) { |
||||
builder.removeLast(); |
||||
} |
||||
} |
||||
|
||||
inline void truncate(size_t size) { |
||||
builder.truncate(size); |
||||
} |
||||
|
||||
inline void reserve(size_t size) { |
||||
if (size > builder.capacity()) { |
||||
setCapacity(size); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
ArrayBuilder<T> builder; |
||||
|
||||
void grow(size_t minCapacity = 0) { |
||||
setCapacity(kj::max(minCapacity, capacity() == 0 ? 4 : capacity() * 2)); |
||||
} |
||||
void setCapacity(size_t newSize) { |
||||
if (builder.size() > newSize) { |
||||
builder.truncate(newSize); |
||||
} |
||||
ArrayBuilder<T> newBuilder = heapArrayBuilder<T>(newSize); |
||||
newBuilder.addAll(kj::mv(builder)); |
||||
builder = kj::mv(newBuilder); |
||||
} |
||||
}; |
||||
|
||||
template <typename T> |
||||
inline auto KJ_STRINGIFY(const Vector<T>& v) -> decltype(toCharSequence(v.asPtr())) { |
||||
return toCharSequence(v.asPtr()); |
||||
} |
||||
|
||||
} // namespace kj
|
||||
|
||||
#endif // KJ_VECTOR_H_
|
@ -0,0 +1,41 @@ |
||||
// 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 KJ_WINDOWS_SANITY_H_ |
||||
#define KJ_WINDOWS_SANITY_H_ |
||||
|
||||
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS |
||||
#pragma GCC system_header |
||||
#endif |
||||
|
||||
#ifndef _INC_WINDOWS |
||||
#error "windows.h needs to be included before kj/windows-sanity.h (or perhaps you don't need either?)" |
||||
#endif |
||||
|
||||
namespace win32 { |
||||
const auto ERROR_ = ERROR; |
||||
#undef ERROR |
||||
const auto ERROR = ERROR_; |
||||
} |
||||
|
||||
using win32::ERROR; |
||||
|
||||
#endif // KJ_WINDOWS_SANITY_H_
|
@ -0,0 +1,41 @@ |
||||
# libcapnp-json.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libcapnp-json-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libcapnp-json-0.6.1.so libcapnp-json-0.6.1.so libcapnp-json.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libcapnp-json.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libcapnp.la /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libcapnp-json. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libcapnp-rpc.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libcapnp-rpc-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libcapnp-rpc-0.6.1.so libcapnp-rpc-0.6.1.so libcapnp-rpc.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libcapnp-rpc.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libcapnp.la /home/batman/openpilot/external/capnp/lib/libkj-async.la /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libcapnp-rpc. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libcapnp.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libcapnp-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libcapnp-0.6.1.so libcapnp-0.6.1.so libcapnp.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libcapnp.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libcapnp. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libcapnp_c.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-0.1 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libcapnp_c.so.0' |
||||
|
||||
# Names of this library. |
||||
library_names='libcapnp_c.so.0.0.0 libcapnp_c.so.0 libcapnp_c.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libcapnp_c.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags='' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs='' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libcapnp_c. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1 @@ |
||||
libcapnp_c.so.0.0.0 |
Binary file not shown.
@ -0,0 +1,41 @@ |
||||
# libcapnpc.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libcapnpc-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libcapnpc-0.6.1.so libcapnpc-0.6.1.so libcapnpc.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libcapnpc.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libcapnp.la /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libcapnpc. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libkj-async.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libkj-async-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libkj-async-0.6.1.so libkj-async-0.6.1.so libkj-async.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libkj-async.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libkj-async. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libkj-http.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libkj-http-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libkj-http-0.6.1.so libkj-http-0.6.1.so libkj-http.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libkj-http.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libkj-async.la /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libkj-http. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libkj-test.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libkj-test-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libkj-test-0.6.1.so libkj-test-0.6.1.so libkj-test.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libkj-test.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' /home/batman/openpilot/external/capnp/lib/libkj.la -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libkj-test. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,41 @@ |
||||
# libkj.la - a libtool library file |
||||
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 |
||||
# |
||||
# Please DO NOT delete this file! |
||||
# It is necessary for linking the library. |
||||
|
||||
# The name that we can dlopen(3). |
||||
dlname='libkj-0.6.1.so' |
||||
|
||||
# Names of this library. |
||||
library_names='libkj-0.6.1.so libkj-0.6.1.so libkj.so' |
||||
|
||||
# The name of the static archive. |
||||
old_library='libkj.a' |
||||
|
||||
# Linker flags that cannot go in dependency_libs. |
||||
inherited_linker_flags=' -pthread' |
||||
|
||||
# Libraries that this one depends upon. |
||||
dependency_libs=' -lpthread' |
||||
|
||||
# Names of additional weak libraries provided by this library |
||||
weak_library_names='' |
||||
|
||||
# Version information for libkj. |
||||
current=0 |
||||
age=0 |
||||
revision=0 |
||||
|
||||
# Is this an already installed library? |
||||
installed=yes |
||||
|
||||
# Should we warn about portability when linking against -modules? |
||||
shouldnotlink=no |
||||
|
||||
# Files to dlopen/dlpreopen |
||||
dlopen='' |
||||
dlpreopen='' |
||||
|
||||
# Directory that this library needs to be installed in: |
||||
libdir='/home/batman/openpilot/external/capnp/lib' |
@ -0,0 +1,12 @@ |
||||
prefix=/home/batman/openpilot/external/capnp |
||||
exec_prefix=${prefix} |
||||
libdir=${exec_prefix}/lib |
||||
bindir=${exec_prefix}/bin |
||||
includedir=${prefix}/include |
||||
codegen=${bindir}/capnpc-c |
||||
|
||||
Name: c-capnproto |
||||
Description: Cap'n Proto C bindings |
||||
Version: 0.2 |
||||
Libs: -L${libdir} -lcapnp_c |
||||
Cflags: -I${includedir} |
@ -0,0 +1,11 @@ |
||||
prefix=/home/batman/openpilot/external/capnp |
||||
exec_prefix=${prefix} |
||||
libdir=${exec_prefix}/lib |
||||
includedir=${prefix}/include |
||||
|
||||
Name: Cap'n Proto RPC |
||||
Description: Fast object-oriented RPC system |
||||
Version: 0.6.1 |
||||
Libs: -L${libdir} -lcapnp-rpc |
||||
Requires: capnp = 0.6.1 kj-async = 0.6.1 |
||||
Cflags: -I${includedir} |
@ -0,0 +1,12 @@ |
||||
prefix=/home/batman/openpilot/external/capnp |
||||
exec_prefix=${prefix} |
||||
libdir=${exec_prefix}/lib |
||||
includedir=${prefix}/include |
||||
|
||||
Name: Cap'n Proto |
||||
Description: Insanely fast serialization system |
||||
Version: 0.6.1 |
||||
Libs: -L${libdir} -lcapnp -pthread -lpthread |
||||
Libs.private: -lpthread |
||||
Requires: kj = 0.6.1 |
||||
Cflags: -I${includedir} -pthread |
@ -0,0 +1,11 @@ |
||||
prefix=/home/batman/openpilot/external/capnp |
||||
exec_prefix=${prefix} |
||||
libdir=${exec_prefix}/lib |
||||
includedir=${prefix}/include |
||||
|
||||
Name: KJ Async Framework Library |
||||
Description: Basic utility library called KJ (async part) |
||||
Version: 0.6.1 |
||||
Libs: -L${libdir} -lkj-async -pthread -lpthread |
||||
Requires: kj = 0.6.1 |
||||
Cflags: -I${includedir} -pthread |
@ -0,0 +1,10 @@ |
||||
prefix=/home/batman/openpilot/external/capnp |
||||
exec_prefix=${prefix} |
||||
libdir=${exec_prefix}/lib |
||||
includedir=${prefix}/include |
||||
|
||||
Name: KJ Framework Library |
||||
Description: Basic utility library called KJ |
||||
Version: 0.6.1 |
||||
Libs: -L${libdir} -lkj -pthread -lpthread |
||||
Cflags: -I${includedir} -pthread |
Loading…
Reference in new issue