parent
28e3543ec4
commit
c6df34f55b
|
@ -148,7 +148,7 @@ Want to get paid to work on openpilot? [comma.ai is hiring](http://comma.ai/posi
|
|||
Licensing
|
||||
------
|
||||
|
||||
openpilot is released under the MIT license.
|
||||
openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
||||
|
||||
Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
Version 0.4.6 (2018-05-18)
|
||||
==========================
|
||||
* NEOSv6 required! Will autoupdate
|
||||
* Stability improvements
|
||||
* Fix all memory leaks
|
||||
* Update C++ compiler to clang6
|
||||
* Improve front camera exposure
|
||||
|
||||
Version 0.4.5 (2018-04-27)
|
||||
==========================
|
||||
* Release notes added to the update popup
|
||||
|
|
|
@ -57,6 +57,7 @@ struct CarEvent @0x9b1657f34caf3ad3 {
|
|||
plannerError @32;
|
||||
ipasOverride @33;
|
||||
debugAlert @34;
|
||||
steerTempUnavailableMute @35;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,4 +331,10 @@ struct CarParams {
|
|||
stoppingControl @34 :Bool; # Does the car allows full control even at lows speeds when stopping
|
||||
startAccel @35 :Float32; # Required acceleraton to overcome creep braking
|
||||
steerRateCost @40 :Float32; # Lateral MPC cost on steering rate
|
||||
steerControlType @46 :SteerControlType;
|
||||
|
||||
enum SteerControlType {
|
||||
torque @0;
|
||||
angle @1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ class TOYOTA:
|
|||
|
||||
class GM:
|
||||
VOLT = "CHEVROLET VOLT PREMIER 2017"
|
||||
CADILLAC_CT6 = "CADILLAC CT6 SUPERCRUISE 2018"
|
||||
|
||||
class FORD:
|
||||
FUSION = "FORD FUSION 2018"
|
||||
|
||||
_DEBUG_ADDRESS = {1880: 8} # reserved for debug purposes
|
||||
|
||||
|
@ -82,6 +86,12 @@ _FINGERPRINTS = {
|
|||
GM.VOLT: [{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 289: 8, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
}],
|
||||
GM.CADILLAC_CT6: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 336: 1, 338: 6, 340: 6, 352: 5, 354: 5, 356: 8, 368: 3, 372: 5, 381: 8, 386: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 458: 5, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 723: 2, 753: 5, 761: 7, 800: 6, 801: 8, 804: 3, 810: 8, 832: 8, 833: 8, 834: 8, 835: 6, 836: 5, 837: 8, 838: 8, 839: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 884: 8, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 1, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1918: 7, 1919: 7, 1934: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
FORD.FUSION: [{
|
||||
71: 8, 74: 8, 75: 8, 76: 8, 90: 8, 92: 8, 93: 8, 118: 8, 119: 8, 120: 8, 125: 8, 129: 8, 130: 8, 131: 8, 132: 8, 133: 8, 145: 8, 146: 8, 357: 8, 359: 8, 360: 8, 361: 8, 376: 8, 390: 8, 391: 8, 392: 8, 394: 8, 512: 8, 514: 8, 516: 8, 531: 8, 532: 8, 534: 8, 535: 8, 560: 8, 578: 8, 604: 8, 613: 8, 673: 8, 827: 8, 848: 8, 934: 8, 935: 8, 936: 8, 947: 8, 963: 8, 970: 8, 972: 8, 973: 8, 984: 8, 992: 8, 994: 8, 997: 8, 998: 8, 1003: 8, 1034: 8, 1045: 8, 1046: 8, 1053: 8, 1054: 8, 1058: 8, 1059: 8, 1068: 8, 1072: 8, 1073: 8, 1082: 8, 1107: 8, 1108: 8, 1109: 8, 1110: 8, 1200: 8, 1427: 8, 1430: 8, 1438: 8, 1459: 8
|
||||
}],
|
||||
}
|
||||
|
||||
# support additional internal only fingerprints
|
||||
|
|
|
@ -18,7 +18,7 @@ def json_robust_dumps(obj):
|
|||
|
||||
class NiceOrderedDict(OrderedDict):
|
||||
def __str__(self):
|
||||
return '{'+', '.join("%r: %r" % p for p in self.iteritems())+'}'
|
||||
return json_robust_dumps(self)
|
||||
|
||||
class SwagFormatter(logging.Formatter):
|
||||
def __init__(self, swaglogger):
|
||||
|
@ -62,6 +62,10 @@ class SwagFormatter(logging.Formatter):
|
|||
def format(self, record):
|
||||
return json_robust_dumps(self.format_dict(record))
|
||||
|
||||
class SwagErrorFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
return record.levelno < logging.ERROR
|
||||
|
||||
_tmpfunc = lambda: 0
|
||||
_srcfile = os.path.normcase(_tmpfunc.__code__.co_filename)
|
||||
|
||||
|
@ -128,15 +132,40 @@ class SwagLogger(logging.Logger):
|
|||
if args:
|
||||
evt['args'] = args
|
||||
evt.update(kwargs)
|
||||
self.info(evt)
|
||||
if 'error' in kwargs:
|
||||
self.error(evt)
|
||||
else:
|
||||
self.info(evt)
|
||||
|
||||
if __name__ == "__main__":
|
||||
log = SwagLogger()
|
||||
|
||||
stdout_handler = logging.StreamHandler(sys.stdout)
|
||||
stdout_handler.setLevel(logging.INFO)
|
||||
stdout_handler.addFilter(SwagErrorFilter())
|
||||
log.addHandler(stdout_handler)
|
||||
|
||||
stderr_handler = logging.StreamHandler(sys.stderr)
|
||||
stderr_handler.setLevel(logging.ERROR)
|
||||
log.addHandler(stderr_handler)
|
||||
|
||||
log.info("asdasd %s", "a")
|
||||
log.info({'wut': 1})
|
||||
log.warning("warning")
|
||||
log.error("error")
|
||||
log.critical("critical")
|
||||
log.event("test", x="y")
|
||||
|
||||
with log.ctx():
|
||||
stdout_handler.setFormatter(SwagFormatter(log))
|
||||
stderr_handler.setFormatter(SwagFormatter(log))
|
||||
log.bind(user="some user")
|
||||
log.info("in req")
|
||||
log.event("do_req")
|
||||
print("")
|
||||
log.warning("warning")
|
||||
print("")
|
||||
log.error("error")
|
||||
print("")
|
||||
log.critical("critical")
|
||||
print("")
|
||||
log.event("do_req", a=1, b="c")
|
||||
|
|
Binary file not shown.
|
@ -22,18 +22,6 @@ function launch {
|
|||
echo 0-3 > /dev/cpuset/foreground/cpus
|
||||
echo 0-3 > /dev/cpuset/android/cpus
|
||||
|
||||
# check if NEOS update is required
|
||||
while [ "$(cat /VERSION)" -lt 4 ] && [ ! -e /data/media/0/noupdate ]; do
|
||||
# wait for network
|
||||
(cd selfdrive/ui/spinner && exec ./spinner 'waiting for network...') & spin_pid=$!
|
||||
until ping -W 1 -c 1 8.8.8.8; do sleep 1; done
|
||||
kill $spin_pid
|
||||
|
||||
# update NEOS
|
||||
curl -o /tmp/updater https://neos.comma.ai/updater && chmod +x /tmp/updater && /tmp/updater
|
||||
sleep 10
|
||||
done
|
||||
|
||||
export PYTHONPATH="$PWD"
|
||||
|
||||
# start manager
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
capnp
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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,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_
|
|
@ -2,7 +2,7 @@ Cython==0.24.1
|
|||
bitstring==3.1.5
|
||||
fastcluster==1.1.21
|
||||
libusb1==1.5.0
|
||||
pycapnp==0.5.9
|
||||
pycapnp==0.6.3
|
||||
pyzmq==15.4.0
|
||||
raven==5.23.0
|
||||
requests==2.10.0
|
||||
|
@ -16,3 +16,4 @@ filterpy==1.0.0
|
|||
smbus2==0.2.0
|
||||
pyflakes==1.5.0
|
||||
-e git+https://github.com/commaai/le_python.git#egg=Logentries
|
||||
Flask==1.0.1
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
|
@ -37,6 +37,7 @@
|
|||
#define SAFETY_ELM327 0xE327
|
||||
#define SAFETY_GM 3
|
||||
#define SAFETY_HONDA_BOSCH 4
|
||||
#define SAFETY_FORD 5
|
||||
#define SAFETY_TOYOTA_NOLIMITS 0x1336
|
||||
#define SAFETY_ALLOUTPUT 0x1337
|
||||
|
||||
|
@ -105,6 +106,9 @@ void *safety_setter_thread(void *s) {
|
|||
case (int)cereal::CarParams::SafetyModels::HONDA_BOSCH:
|
||||
safety_setting = SAFETY_HONDA_BOSCH;
|
||||
break;
|
||||
case (int)cereal::CarParams::SafetyModels::FORD:
|
||||
safety_setting = SAFETY_FORD;
|
||||
break;
|
||||
default:
|
||||
LOGE("unknown safety model: %d", safety_model);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ else ifeq ($(UNAME_M),x86_64)
|
|||
else ifeq ($(UNAME_M),aarch64)
|
||||
ZMQ_FLAGS = -I$(PHONELIBS)/zmq/aarch64/include
|
||||
ZMQ_LIBS = -L$(PHONELIBS)/zmq/aarch64/lib -l:libzmq.a
|
||||
CXXFLAGS += -lgnustl_shared
|
||||
endif
|
||||
|
||||
OPENDBC_PATH := $(shell python -c 'import opendbc; print opendbc.DBC_PATH')
|
||||
|
|
|
@ -5,7 +5,7 @@ from common.fingerprints import eliminate_incompatible_cars, all_known_cars
|
|||
|
||||
from selfdrive.swaglog import cloudlog
|
||||
import selfdrive.messaging as messaging
|
||||
from common.fingerprints import HONDA, TOYOTA, GM
|
||||
from common.fingerprints import HONDA, TOYOTA, GM, FORD
|
||||
|
||||
def load_interfaces(x):
|
||||
ret = {}
|
||||
|
@ -22,7 +22,8 @@ def load_interfaces(x):
|
|||
interfaces = load_interfaces({
|
||||
'honda': [HONDA.CIVIC, HONDA.ACURA_ILX, HONDA.CRV, HONDA.ODYSSEY, HONDA.ACURA_RDX, HONDA.PILOT, HONDA.RIDGELINE],
|
||||
'toyota': [TOYOTA.PRIUS, TOYOTA.RAV4, TOYOTA.RAV4H, TOYOTA.COROLLA, TOYOTA.LEXUS_RXH],
|
||||
'gm': [GM.VOLT],
|
||||
'gm': [GM.VOLT, GM.CADILLAC_CT6],
|
||||
'ford': [FORD.FUSION],
|
||||
'simulator2': ['simulator2'],
|
||||
'mock': ['mock']})
|
||||
|
||||
|
|
|
@ -257,6 +257,7 @@ class CarInterface(object):
|
|||
raise ValueError("unsupported car %s" % candidate)
|
||||
|
||||
ret.steerKf = 0. # TODO: investigate FF steer control for Honda
|
||||
ret.steerControlType = car.CarParams.SteerControlType.torque
|
||||
|
||||
# min speed to enable ACC. if car can do stop and go, then set enabling speed
|
||||
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
|
||||
|
|
|
@ -67,6 +67,7 @@ def process_hud_alert(hud_alert, audible_alert):
|
|||
|
||||
return steer, fcw, sound1, sound2
|
||||
|
||||
|
||||
def ipas_state_transition(steer_angle_enabled, enabled, ipas_active, ipas_reset_counter):
|
||||
|
||||
if enabled and not steer_angle_enabled:
|
||||
|
|
|
@ -136,6 +136,7 @@ class CarInterface(object):
|
|||
|
||||
# no rear steering, at least on the listed cars above
|
||||
ret.steerRatioRear = 0.
|
||||
ret.steerControlType = car.CarParams.SteerControlType.torque
|
||||
|
||||
# steer, gas, brake limitations VS speed
|
||||
ret.steerMaxBP = [16. * CV.KPH_TO_MS, 45. * CV.KPH_TO_MS] # breakpoints at 1 and 40 kph
|
||||
|
|
|
@ -89,9 +89,9 @@ int touch_poll(TouchState *s, int* out_x, int* out_y) {
|
|||
}
|
||||
}
|
||||
if (up) {
|
||||
// adjust for landscape
|
||||
*out_x = 1920 - s->last_y;
|
||||
*out_y = s->last_x;
|
||||
// adjust for flippening
|
||||
*out_x = s->last_y;
|
||||
*out_y = 1080 - s->last_x;
|
||||
}
|
||||
return up;
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
#define COMMA_VERSION "0.4.5.1-release"
|
||||
#define COMMA_VERSION "0.4.6-release"
|
||||
|
|
|
@ -88,6 +88,12 @@ class AlertManager(object):
|
|||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, "steerRequired", "chimeDouble", .4, 2., 3.),
|
||||
|
||||
"steerTempUnavailableMute": Alert(
|
||||
"TAKE CONTROL",
|
||||
"Steering Temporarily Unavailable",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, None, None, .2, .2, .2),
|
||||
|
||||
"preDriverDistracted": Alert(
|
||||
"TAKE CONTROL",
|
||||
"User Appears Distracted",
|
||||
|
@ -124,6 +130,12 @@ class AlertManager(object):
|
|||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, None, None, 0., 0., .2),
|
||||
|
||||
"debugAlert": Alert(
|
||||
"DEBUG ALERT",
|
||||
"",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, None, None, .1, .1, .1),
|
||||
|
||||
# Non-entry only alerts
|
||||
"wrongCarModeNoEntry": Alert(
|
||||
"openpilot Unavailable",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue