Vendor minimal capnproto
This commit is contained in:
1
.github/workflows/wheels.yml
vendored
1
.github/workflows/wheels.yml
vendored
@@ -23,7 +23,6 @@ jobs:
|
||||
CIBW_ARCHS: ${{ matrix.arch }}
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: python -m pytest {project}/test
|
||||
CIBW_CONFIG_SETTINGS: force-bundled-libcapnp=true
|
||||
CMAKE_OSX_ARCHITECTURES: "${{ runner.os == 'macOS' && matrix.arch || '' }}"
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,8 +8,6 @@
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
build32
|
||||
build64
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
@@ -47,7 +45,6 @@ docs/_build
|
||||
capnp/lib/capnp.cpp
|
||||
capnp/lib/capnp.h
|
||||
capnp/lib/capnp_api.h
|
||||
bundled/
|
||||
example
|
||||
*.iml
|
||||
|
||||
@@ -57,3 +54,6 @@ example
|
||||
.venv/
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
|
||||
# Source for the trimmed bootstrap schema.
|
||||
!vendor/capnproto/src/**/*.capnp
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
include README.md LICENSE.md
|
||||
include buildutils/*.py
|
||||
include _custom_build/*.py
|
||||
recursive-include vendor/capnproto *.h *.c++ *.txt *.md *.capnp
|
||||
recursive-include capnp *.py *.pyx *.pxd *.h *.cpp
|
||||
recursive-include test *.py *.capnp *.binary *.txt
|
||||
exclude capnp/lib/capnp.cpp capnp/lib/capnp.h capnp/lib/capnp_api.h
|
||||
|
||||
21
README.md
21
README.md
@@ -34,22 +34,23 @@ not be installed alongside another distribution providing `capnp`.
|
||||
|
||||
## Build and test
|
||||
|
||||
Targets: CPython 3.12, Linux x86_64/aarch64, and macOS arm64. A C++14 compiler and
|
||||
CMake are required for a bundled build.
|
||||
Targets: CPython 3.12, Linux x86_64/aarch64, and macOS arm64. A C++17 compiler and
|
||||
CMake are required to build the vendored library.
|
||||
|
||||
```sh
|
||||
uv venv --python 3.12
|
||||
uv pip install cython setuptools wheel pkgconfig pytest build
|
||||
.venv/bin/python setup.py build_ext --inplace --force-bundled-libcapnp
|
||||
uv pip install cython setuptools wheel pytest build
|
||||
.venv/bin/python setup.py build_ext --inplace
|
||||
.venv/bin/python -m pytest
|
||||
.venv/bin/python -m build -Cforce-bundled-libcapnp=true
|
||||
.venv/bin/python -m build
|
||||
```
|
||||
|
||||
The existing build fallback downloads Cap'n Proto 1.4.0. The extension links only
|
||||
`capnpc`, `capnp`, and `kj`; it does not link `capnp-rpc` or `kj-async`. `capnpc` is
|
||||
needed for runtime schema parsing. Owning/vendoring the C++ source itself is a
|
||||
separate step. To use a system installation, pass `--force-system-libcapnp` to
|
||||
`build_ext` (or `-Cforce-system-libcapnp=true` to the wheel build).
|
||||
The C++ library is vendored under [`vendor/capnproto`](vendor/capnproto), based on
|
||||
Cap'n Proto 1.4.0. Every build compiles and statically links this copy; it does not
|
||||
search for a system Cap'n Proto installation or download sources. The source
|
||||
archive includes the vendored files, and wheels contain the compiled library.
|
||||
Runtime schema parsing remains included, so cereal schemas need no generated
|
||||
Python bindings. See the vendor README for provenance and the removed features.
|
||||
|
||||
The retained upstream tests cover message construction, schema loading,
|
||||
reflection, binary fixtures, serialization, and exceptions. Added lifetime tests
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import sys
|
||||
|
||||
from setuptools.build_meta import * # noqa: F401, F403
|
||||
from setuptools.build_meta import build_wheel
|
||||
|
||||
backend_class = build_wheel.__self__.__class__
|
||||
|
||||
|
||||
class _CustomBuildMetaBackend(backend_class):
|
||||
def run_setup(self, setup_script="setup.py"):
|
||||
if self.config_settings:
|
||||
flags = []
|
||||
if self.config_settings.get("force-bundled-libcapnp"):
|
||||
flags.append("--force-bundled-libcapnp")
|
||||
if self.config_settings.get("force-system-libcapnp"):
|
||||
flags.append("--force-system-libcapnp")
|
||||
if self.config_settings.get("libcapnp-url"):
|
||||
flags.append("--libcapnp-url")
|
||||
flags.append(self.config_settings["libcapnp-url"])
|
||||
if flags:
|
||||
sys.argv = sys.argv[:1] + ["build_ext"] + flags + sys.argv[1:]
|
||||
return super().run_setup(setup_script)
|
||||
|
||||
def build_wheel(self, wheel_directory, config_settings=None, metadata_directory=None):
|
||||
self.config_settings = config_settings
|
||||
return super().build_wheel(wheel_directory, config_settings, metadata_directory)
|
||||
|
||||
|
||||
build_wheel = _CustomBuildMetaBackend().build_wheel
|
||||
@@ -1,70 +0,0 @@
|
||||
"Build the bundled capnp distribution"
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
|
||||
def build_libcapnp(bundle_dir, build_dir): # noqa: C901
|
||||
"""
|
||||
Build capnproto
|
||||
"""
|
||||
bundle_dir = os.path.abspath(bundle_dir)
|
||||
capnp_dir = os.path.join(bundle_dir, "capnproto-c++")
|
||||
build_dir = os.path.abspath(build_dir)
|
||||
tmp_dir = os.path.join(capnp_dir, "build")
|
||||
|
||||
# Clean the tmp build directory every time
|
||||
if os.path.exists(tmp_dir):
|
||||
shutil.rmtree(tmp_dir)
|
||||
os.mkdir(tmp_dir)
|
||||
|
||||
cxxflags = os.environ.get("CXXFLAGS", None)
|
||||
ldflags = os.environ.get("LDFLAGS", None)
|
||||
os.environ["CXXFLAGS"] = (cxxflags or "") + " -O2 -DNDEBUG"
|
||||
os.environ["LDFLAGS"] = ldflags or ""
|
||||
|
||||
# Enable ninja for compilation if available
|
||||
build_type = []
|
||||
if shutil.which("ninja"):
|
||||
build_type = ["-G", "Ninja"]
|
||||
|
||||
if not shutil.which("cmake"):
|
||||
raise RuntimeError("Could not find cmake in your path!")
|
||||
|
||||
args = [
|
||||
"cmake",
|
||||
"-DCMAKE_POSITION_INDEPENDENT_CODE=1",
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DBUILD_SHARED_LIBS=OFF",
|
||||
"-DWITH_OPENSSL=OFF",
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH={}".format(build_dir),
|
||||
capnp_dir,
|
||||
]
|
||||
args.extend(build_type)
|
||||
conf = subprocess.Popen(args, cwd=tmp_dir, stdout=sys.stdout)
|
||||
returncode = conf.wait()
|
||||
if returncode != 0:
|
||||
raise RuntimeError("CMake failed {}".format(returncode))
|
||||
|
||||
# Run build through cmake
|
||||
args = [
|
||||
"cmake",
|
||||
"--build",
|
||||
".",
|
||||
"--target",
|
||||
"install",
|
||||
]
|
||||
build = subprocess.Popen(args, cwd=tmp_dir, stdout=sys.stdout)
|
||||
returncode = build.wait()
|
||||
if cxxflags is None:
|
||||
del os.environ["CXXFLAGS"]
|
||||
else:
|
||||
os.environ["CXXFLAGS"] = cxxflags
|
||||
if ldflags is None:
|
||||
del os.environ["LDFLAGS"]
|
||||
else:
|
||||
os.environ["LDFLAGS"] = ldflags
|
||||
if returncode != 0:
|
||||
raise RuntimeError("capnproto compilation failed: {}".format(returncode))
|
||||
@@ -1,76 +0,0 @@
|
||||
"""utilities for fetching build dependencies."""
|
||||
|
||||
#
|
||||
# Copyright (C) PyZMQ Developers
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
#
|
||||
# This bundling code is largely adapted from pyzmq-static's get.sh by
|
||||
# Brandon Craig-Rhodes, which is itself BSD licensed.
|
||||
#
|
||||
# Adapted for use in pycapnp from pyzmq. See https://github.com/zeromq/pyzmq
|
||||
# for original project.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
|
||||
from urllib.request import urlopen
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
|
||||
|
||||
bundled_version = (1, 4, 0)
|
||||
libcapnp_name = "capnproto-c++-%i.%i.%i.tar.gz" % (bundled_version)
|
||||
libcapnp_url = "https://capnproto.org/" + libcapnp_name
|
||||
|
||||
|
||||
def fetch_archive(savedir, url):
|
||||
"""download an archive to a specific location"""
|
||||
req = urlopen(url)
|
||||
# Lookup filename
|
||||
fname = req.info().get_filename()
|
||||
if not fname:
|
||||
fname = os.path.basename(url)
|
||||
dest = pjoin(savedir, fname)
|
||||
if os.path.exists(dest):
|
||||
print("already have %s" % fname)
|
||||
return dest
|
||||
print("fetching %s into %s" % (url, savedir))
|
||||
if not os.path.exists(savedir):
|
||||
os.makedirs(savedir)
|
||||
with open(dest, "wb") as f:
|
||||
f.write(req.read())
|
||||
return dest
|
||||
|
||||
|
||||
#
|
||||
# libcapnp
|
||||
#
|
||||
|
||||
|
||||
def fetch_libcapnp(savedir, url=None):
|
||||
"""download and extract libcapnp"""
|
||||
is_preconfigured = False
|
||||
if url is None:
|
||||
url = libcapnp_url
|
||||
is_preconfigured = True
|
||||
dest = pjoin(savedir, "capnproto-c++")
|
||||
if os.path.exists(dest):
|
||||
print("already have %s" % dest)
|
||||
return
|
||||
fname = fetch_archive(savedir, url)
|
||||
tf = tarfile.open(fname)
|
||||
with_version = pjoin(savedir, tf.firstmember.path)
|
||||
tf.extractall(savedir)
|
||||
tf.close()
|
||||
# remove version suffix:
|
||||
if is_preconfigured:
|
||||
shutil.move(with_version, dest)
|
||||
else:
|
||||
cpp_dir = os.path.join(with_version, "c++")
|
||||
shutil.move(cpp_dir, dest)
|
||||
@@ -136,7 +136,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
bint has(char *) except +reraise_kj_exception
|
||||
StructSchema getSchema()
|
||||
Maybe[StructSchema.Field] which()
|
||||
MessageSize totalSize()
|
||||
MessageSize totalSize() except +reraise_kj_exception
|
||||
|
||||
cdef cppclass DynamicStruct_Builder" ::capnp::DynamicStruct::Builder" nogil:
|
||||
# Need to flatten this class out, since nested C++ classes cause havoc with cython fused types
|
||||
@@ -152,7 +152,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
StructSchema getSchema()
|
||||
Maybe[StructSchema.Field] which()
|
||||
DynamicStruct.Reader asReader()
|
||||
MessageSize totalSize()
|
||||
MessageSize totalSize() except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicEnum nogil:
|
||||
@@ -214,4 +214,4 @@ cdef extern from "capnp/schema-parser.h" namespace " ::capnp":
|
||||
ParsedSchema getNested(char * name) except +reraise_kj_exception
|
||||
cdef cppclass SchemaParser nogil:
|
||||
SchemaParser()
|
||||
ParsedSchema parseDiskFile(char * displayName, char * diskPath, ArrayPtr[StringPtr] importPath)
|
||||
ParsedSchema parseDiskFile(char * displayName, char * diskPath, ArrayPtr[StringPtr] importPath) except +reraise_kj_exception
|
||||
|
||||
@@ -72,7 +72,7 @@ cdef extern from "capnp/message.h" namespace " ::capnp":
|
||||
cdef cppclass MessageBuilder nogil:
|
||||
DynamicStruct_Builder getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
|
||||
DynamicStruct_Builder initRootDynamicStruct 'initRoot< ::capnp::DynamicStruct>'(StructSchema)
|
||||
void setRootDynamicStruct 'setRoot< ::capnp::DynamicStruct::Reader>'(DynamicStruct.Reader)
|
||||
void setRootDynamicStruct 'setRoot< ::capnp::DynamicStruct::Reader>'(DynamicStruct.Reader) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass MessageReader nogil:
|
||||
DynamicStruct.Reader getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# capnp.pyx
|
||||
# distutils: language = c++
|
||||
# distutils: libraries = capnpc capnp kj
|
||||
# distutils: include_dirs = .
|
||||
# cython: c_string_type = str
|
||||
# cython: c_string_encoding = default
|
||||
@@ -1382,7 +1381,6 @@ cdef class SchemaParser:
|
||||
self._last_import_array = importArray
|
||||
|
||||
ret = _ParsedSchema()
|
||||
# TODO (HaaTa): Convert to parseFromDirectory() as per deprecation note
|
||||
ret._init_child(self.thisptr.parseDiskFile(displayName, diskPath, importArray.asArrayPtr()))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel", "pkgconfig", "cython>=3.0"]
|
||||
build-backend = "backend"
|
||||
backend-path = ["_custom_build"]
|
||||
requires = ["setuptools", "wheel", "cython>=3.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pycapnp"
|
||||
@@ -18,7 +17,7 @@ dynamic = [
|
||||
dependencies = []
|
||||
|
||||
[dependency-groups]
|
||||
dev = ["cython>=3.0", "setuptools", "wheel", "pkgconfig", "build", {include-group = "test"}, {include-group = "lint"}]
|
||||
dev = ["cython>=3.0", "setuptools", "wheel", "build", {include-group = "test"}, {include-group = "lint"}]
|
||||
test = ["pytest"]
|
||||
lint = ["ruff==0.16.8"]
|
||||
|
||||
@@ -28,7 +27,7 @@ testpaths = ["test"]
|
||||
[tool.ruff]
|
||||
target-version = "py312"
|
||||
line-length = 120
|
||||
exclude = ["build", "build64", "bundled"]
|
||||
exclude = ["build", "vendor"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[metadata]
|
||||
description_file = README.md
|
||||
license_files = LICENSE.md
|
||||
108
setup.py
108
setup.py
@@ -3,24 +3,17 @@
|
||||
pycapnp distutils setup.py
|
||||
"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import pkgconfig
|
||||
import setuptools # noqa: F401
|
||||
|
||||
from distutils.command.clean import clean as _clean
|
||||
|
||||
from setuptools import setup, Extension
|
||||
|
||||
_this_dir = os.path.dirname(__file__)
|
||||
sys.path.insert(1, _this_dir)
|
||||
|
||||
from buildutils.build import build_libcapnp
|
||||
from buildutils.bundle import fetch_libcapnp
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
MAJOR = 2
|
||||
MINOR = 2
|
||||
@@ -72,10 +65,7 @@ class clean(_clean):
|
||||
os.path.join("capnp", "lib", "capnp.h"),
|
||||
os.path.join("capnp", "version.py"),
|
||||
"build",
|
||||
"build32",
|
||||
"build64",
|
||||
"bundled",
|
||||
] + glob.glob(os.path.join("capnp", "*.capnp")):
|
||||
]:
|
||||
print("removing %s" % x)
|
||||
try:
|
||||
os.remove(x)
|
||||
@@ -91,76 +81,32 @@ class build_libcapnp_ext(build_ext_c):
|
||||
Build capnproto library
|
||||
"""
|
||||
|
||||
user_options = build_ext_c.user_options + [
|
||||
("force-bundled-libcapnp", None, "Bundle capnp library into the installer"),
|
||||
("force-system-libcapnp", None, "Use system capnp library"),
|
||||
("libcapnp-url=", "u", "URL to download libcapnp from (only if bundled)"),
|
||||
def run(self):
|
||||
source = Path(_this_dir, "vendor", "capnproto").resolve()
|
||||
build = Path(self.build_temp, "capnproto").resolve()
|
||||
args = [
|
||||
"cmake",
|
||||
"-S",
|
||||
str(source),
|
||||
"-B",
|
||||
str(build),
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
build_ext_c.initialize_options(self)
|
||||
self.force_bundled_libcapnp = None
|
||||
self.force_system_libcapnp = None
|
||||
self.libcapnp_url = None
|
||||
|
||||
def run(self): # noqa: C901
|
||||
if self.force_bundled_libcapnp:
|
||||
need_build = True
|
||||
elif self.force_system_libcapnp:
|
||||
need_build = False
|
||||
else:
|
||||
# Try to use capnp executable to find include and lib path
|
||||
capnp_executable = shutil.which("capnp")
|
||||
if capnp_executable:
|
||||
capnp_dir = os.path.dirname(capnp_executable)
|
||||
self.include_dirs += [os.path.join(capnp_dir, "..", "include")]
|
||||
self.library_dirs += [os.path.join(capnp_dir, "..", "lib{}".format(8 * struct.calcsize("P")))]
|
||||
self.library_dirs += [os.path.join(capnp_dir, "..", "lib")]
|
||||
|
||||
# Look for capnproto using pkg-config (and minimum version)
|
||||
try:
|
||||
if pkgconfig.installed("capnp", ">= 0.7.0"):
|
||||
need_build = False
|
||||
else:
|
||||
need_build = True
|
||||
except EnvironmentError:
|
||||
# pkg-config not available in path
|
||||
need_build = True
|
||||
|
||||
if need_build:
|
||||
print(
|
||||
"*WARNING* no libcapnp detected or rebuild forced. "
|
||||
"Attempting to build it from source now. "
|
||||
"If you have C++ Cap'n Proto installed, it may be out of date or is not being detected. "
|
||||
"This may take a while..."
|
||||
)
|
||||
bundle_dir = os.path.join(_this_dir, "bundled")
|
||||
if not os.path.exists(bundle_dir):
|
||||
os.mkdir(bundle_dir)
|
||||
build_dir = os.path.join(_this_dir, "build{}".format(8 * struct.calcsize("P")))
|
||||
if not os.path.exists(build_dir):
|
||||
os.mkdir(build_dir)
|
||||
|
||||
# Check if we've already built capnproto
|
||||
capnp_bin = os.path.join(build_dir, "bin", "capnp")
|
||||
|
||||
if not os.path.exists(capnp_bin):
|
||||
# Not built, fetch and build
|
||||
fetch_libcapnp(bundle_dir, self.libcapnp_url)
|
||||
build_libcapnp(bundle_dir, build_dir)
|
||||
else:
|
||||
print("capnproto already built at {}".format(build_dir))
|
||||
|
||||
self.include_dirs = [os.path.join(build_dir, "include")] + self.include_dirs
|
||||
self.library_dirs = [
|
||||
os.path.join(build_dir, "lib{}".format(8 * struct.calcsize("P"))),
|
||||
os.path.join(build_dir, "lib"),
|
||||
] + self.library_dirs
|
||||
|
||||
if os.environ.get("CMAKE_OSX_ARCHITECTURES"):
|
||||
args.append("-DCMAKE_OSX_ARCHITECTURES=" + os.environ["CMAKE_OSX_ARCHITECTURES"])
|
||||
if os.environ.get("MACOSX_DEPLOYMENT_TARGET"):
|
||||
args.append("-DCMAKE_OSX_DEPLOYMENT_TARGET=" + os.environ["MACOSX_DEPLOYMENT_TARGET"])
|
||||
subprocess.run(args, check=True)
|
||||
subprocess.run(["cmake", "--build", str(build), "--parallel", str(self.parallel or 2)], check=True)
|
||||
archive = str(build / "libcapnp-vendored.a")
|
||||
for extension in self.extensions:
|
||||
extension.include_dirs.insert(0, str(source / "src"))
|
||||
extension.extra_objects = [archive]
|
||||
extension.depends = [str(p) for p in source.rglob("*") if p.is_file()] + [archive]
|
||||
return build_ext_c.run(self)
|
||||
|
||||
|
||||
extra_compile_args = ["--std=c++14"]
|
||||
extra_compile_args = ["-std=c++17", "-pthread"]
|
||||
import Cython.Build # noqa: E402
|
||||
import Cython # noqa: E402
|
||||
|
||||
@@ -172,6 +118,7 @@ extensions = [
|
||||
"capnp/lib/*.pyx",
|
||||
],
|
||||
extra_compile_args=extra_compile_args,
|
||||
extra_link_args=["-pthread"],
|
||||
language="c++",
|
||||
)
|
||||
]
|
||||
@@ -203,7 +150,8 @@ setup(
|
||||
description="A cython wrapping of the C++ Cap'n Proto library",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
license="BSD-2-Clause",
|
||||
license="BSD-2-Clause AND MIT",
|
||||
license_files=["LICENSE.md", "vendor/capnproto/LICENSE.txt"],
|
||||
# (setup.py only supports 1 author...)
|
||||
author="Jacob Alexander", # <- Current maintainer; Original author -> Jason Paryani
|
||||
author_email="haata@kiibohd.com",
|
||||
|
||||
@@ -14,7 +14,3 @@ struct Baz{
|
||||
struct Qux{
|
||||
id @0 :UInt64;
|
||||
}
|
||||
|
||||
interface Wrapper {
|
||||
wrapped @0 (object :AnyPointer);
|
||||
}
|
||||
|
||||
30
test/test_vendor.py
Normal file
30
test/test_vendor.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Validation of malformed wire data through the trimmed C++ core."""
|
||||
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
import capnp
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pointer", [3, 7, 0xFFFFFFFF00000003])
|
||||
def test_capability_and_reserved_pointers_rejected(pointer):
|
||||
schema = capnp.load(str(Path(__file__).with_name("addressbook.capnp")))
|
||||
# Single segment: AddressBook root (zero data words, one pointer) followed
|
||||
# by a capability/reserved pointer where the people list should be.
|
||||
data = struct.pack("<IIQQ", 0, 2, 1 << 48, pointer)
|
||||
with schema.AddressBook.from_bytes(data) as reader:
|
||||
with pytest.raises(capnp.KjException):
|
||||
_ = reader.people
|
||||
with pytest.raises(capnp.KjException):
|
||||
reader.as_builder()
|
||||
if pointer == 7:
|
||||
with pytest.raises(capnp.KjException):
|
||||
_ = reader.total_size
|
||||
|
||||
|
||||
def test_interface_schema_is_rejected(tmp_path):
|
||||
schema = tmp_path / "unsupported.capnp"
|
||||
schema.write_text("@0xdeadbeefdeadbeef; interface Unsupported { ping @0 () -> (); }")
|
||||
with pytest.raises(capnp.KjException, match="Interfaces are not supported"):
|
||||
capnp.load(str(schema))
|
||||
47
vendor/capnproto/CMakeLists.txt
vendored
Normal file
47
vendor/capnproto/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(pycapnp_vendor LANGUAGES CXX)
|
||||
|
||||
add_library(capnp-vendored STATIC
|
||||
src/capnp/blob.c++
|
||||
src/capnp/arena.c++
|
||||
src/capnp/layout.c++
|
||||
src/capnp/message.c++
|
||||
src/capnp/schema.capnp.c++
|
||||
src/capnp/serialize.c++
|
||||
src/capnp/schema.c++
|
||||
src/capnp/schema-loader.c++
|
||||
src/capnp/dynamic.c++
|
||||
src/capnp/stringify.c++
|
||||
src/capnp/compiler/type-id.c++
|
||||
src/capnp/compiler/lexer.c++
|
||||
src/capnp/compiler/grammar.capnp.c++
|
||||
src/capnp/compiler/parser.c++
|
||||
src/capnp/compiler/generics.c++
|
||||
src/capnp/compiler/node-translator.c++
|
||||
src/capnp/compiler/compiler.c++
|
||||
src/capnp/schema-parser.c++
|
||||
src/kj/array.c++
|
||||
src/kj/common.c++
|
||||
src/kj/debug.c++
|
||||
src/kj/exception.c++
|
||||
src/kj/io.c++
|
||||
src/kj/mutex.c++
|
||||
src/kj/string.c++
|
||||
src/kj/source-location.c++
|
||||
src/kj/hash.c++
|
||||
src/kj/table.c++
|
||||
src/kj/arena.c++
|
||||
src/kj/units.c++
|
||||
src/kj/encoding.c++
|
||||
src/kj/refcount.c++
|
||||
src/kj/string-tree.c++
|
||||
src/kj/time.c++
|
||||
src/kj/filesystem.c++
|
||||
src/kj/filesystem-disk-unix.c++
|
||||
src/kj/parse/char.c++
|
||||
)
|
||||
target_compile_features(capnp-vendored PUBLIC cxx_std_17)
|
||||
target_include_directories(capnp-vendored PUBLIC src)
|
||||
set_target_properties(capnp-vendored PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(capnp-vendored PUBLIC Threads::Threads)
|
||||
23
vendor/capnproto/LICENSE.txt
vendored
Normal file
23
vendor/capnproto/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2013-2017 Sandstorm Development Group, Inc.; Cloudflare, Inc.;
|
||||
and other contributors. Each commit is copyright by its respective author or
|
||||
author's employer.
|
||||
|
||||
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.
|
||||
44
vendor/capnproto/README.md
vendored
Normal file
44
vendor/capnproto/README.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# Vendored Cap'n Proto
|
||||
|
||||
Based on Cap'n Proto 1.4.0, the version previously downloaded by this fork.
|
||||
|
||||
Source: https://capnproto.org/capnproto-c++-1.4.0.tar.gz
|
||||
|
||||
Archive SHA-256: `fa02378ad522b318916b9ad928d1372fc9abd43dd1f4f0392e50450f5c87828f`
|
||||
|
||||
See LICENSE.txt and individual files for upstream licenses.
|
||||
|
||||
This is an internal serialization/schema-parser subset for pycapnp, not a full
|
||||
Cap'n Proto distribution. Generated schema sources are checked in; no compiler
|
||||
executables or network downloads are required to build it.
|
||||
|
||||
Removed: RPC/capabilities, promises/async I/O, networking/TLS/HTTP, JSON,
|
||||
compression, packed/text/stream/fd message serialization, command-line tools and
|
||||
code generators, upstream build systems/examples/tests, compiler export helpers,
|
||||
unused KJ encodings/stream classes/clocks, and opt-in crash handlers. Also removed
|
||||
are canonicalization, borrowed-memory builders, orphan resizing/concatenation,
|
||||
schema doc-comment storage, interface-schema support, Windows code, and the
|
||||
optional lite build. Interface declarations are rejected with a parser error.
|
||||
|
||||
The retained core includes message validation, flat serialization, dynamic
|
||||
struct/list/enum values, runtime schema compilation (including generics), and
|
||||
its filesystem, allocation, synchronization, and diagnostic dependencies.
|
||||
Generated schema reader/builder metadata is retained; pipeline classes are not.
|
||||
This source tree is for the Python binding, not a drop-in C++ SDK or schema
|
||||
compiler for openpilot's separate C++ build.
|
||||
|
||||
The retained `src/capnp/schema.capnp` omits compiler-request and documentation
|
||||
metadata. Its generated C++ was rebuilt with upstream 1.4.0's `capnp` and
|
||||
`capnpc-c++`, then unused generated pipeline APIs were removed. Regeneration:
|
||||
|
||||
```sh
|
||||
capnp compile -I<upstream>/src --src-prefix=vendor/capnproto/src/capnp \
|
||||
-o<capnpc-c++>:<output> vendor/capnproto/src/capnp/schema.capnp
|
||||
```
|
||||
|
||||
Regeneration must also apply the vendor's removal of Pipeline declarations,
|
||||
classes and accessors, and Windows-only includes. The unused `LexedTokens` type
|
||||
was removed from the shipped generated lexer files; the runtime parser uses
|
||||
`LexedStatements`. Lexer reflection metadata is also omitted; its generated
|
||||
static reader/builder layouts remain. Generated wire type tags and active field
|
||||
ordinals are kept.
|
||||
862
vendor/capnproto/src/capnp/any.h
vendored
Normal file
862
vendor/capnproto/src/capnp/any.h
vendored
Normal file
@@ -0,0 +1,862 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "layout.h"
|
||||
#include "pointer-helpers.h"
|
||||
#include "orphan.h"
|
||||
#include "list.h"
|
||||
#include <kj/hash.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
|
||||
class StructSchema;
|
||||
class ListSchema;
|
||||
class Orphanage;
|
||||
struct AnyPointer;
|
||||
|
||||
struct AnyList {
|
||||
AnyList() = delete;
|
||||
|
||||
class Reader;
|
||||
class Builder;
|
||||
};
|
||||
|
||||
struct AnyStruct {
|
||||
AnyStruct() = delete;
|
||||
|
||||
class Reader;
|
||||
class Builder;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct List<AnyStruct, Kind::OTHER> {
|
||||
List() = delete;
|
||||
|
||||
class Reader;
|
||||
class Builder;
|
||||
};
|
||||
|
||||
namespace _ { // private
|
||||
template <> struct Kind_<AnyPointer> { static constexpr Kind kind = Kind::OTHER; };
|
||||
template <> struct Kind_<AnyStruct> { static constexpr Kind kind = Kind::OTHER; };
|
||||
template <> struct Kind_<AnyList> { static constexpr Kind kind = Kind::OTHER; };
|
||||
} // namespace _ (private)
|
||||
|
||||
// =======================================================================================
|
||||
// AnyPointer!
|
||||
|
||||
struct AnyPointer {
|
||||
// Reader/Builder for the `AnyPointer` field type, i.e. a pointer that can point to an arbitrary
|
||||
// object.
|
||||
|
||||
AnyPointer() = delete;
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
typedef AnyPointer Reads;
|
||||
|
||||
Reader() = default;
|
||||
inline Reader(_::PointerReader reader): reader(reader) {}
|
||||
|
||||
inline MessageSize targetSize() const;
|
||||
// Get the total size of the target object and all its children.
|
||||
|
||||
inline PointerType getPointerType() const;
|
||||
|
||||
inline bool isNull() const { return getPointerType() == PointerType::NULL_; }
|
||||
inline bool isStruct() const { return getPointerType() == PointerType::STRUCT; }
|
||||
inline bool isList() const { return getPointerType() == PointerType::LIST; }
|
||||
inline bool isCapability() const { return getPointerType() == PointerType::CAPABILITY; }
|
||||
|
||||
template <typename T>
|
||||
inline ReaderFor<T> getAs() const;
|
||||
// Valid for T = any generated struct type, List<U>, Text, or Data.
|
||||
|
||||
template <typename T>
|
||||
inline ReaderFor<T> getAs(StructSchema schema) const;
|
||||
// Only valid for T = DynamicStruct. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
template <typename T>
|
||||
inline ReaderFor<T> getAs(ListSchema schema) const;
|
||||
// Only valid for T = DynamicList. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
private:
|
||||
_::PointerReader reader;
|
||||
friend struct AnyPointer;
|
||||
friend class Orphanage;
|
||||
friend struct _::PointerHelpers<AnyPointer>;
|
||||
};
|
||||
|
||||
class Builder {
|
||||
public:
|
||||
typedef AnyPointer Builds;
|
||||
|
||||
Builder() = delete;
|
||||
inline Builder(decltype(nullptr)) {}
|
||||
inline Builder(_::PointerBuilder builder): builder(builder) {}
|
||||
|
||||
inline MessageSize targetSize() const;
|
||||
// Get the total size of the target object and all its children.
|
||||
|
||||
inline PointerType getPointerType();
|
||||
|
||||
inline bool isNull() { return getPointerType() == PointerType::NULL_; }
|
||||
inline bool isStruct() { return getPointerType() == PointerType::STRUCT; }
|
||||
inline bool isList() { return getPointerType() == PointerType::LIST; }
|
||||
inline bool isCapability() { return getPointerType() == PointerType::CAPABILITY; }
|
||||
|
||||
inline void clear();
|
||||
// Set to null.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> getAs();
|
||||
// Valid for T = any generated struct type, List<U>, Text, or Data.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> getAs(StructSchema schema);
|
||||
// Only valid for T = DynamicStruct. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> getAs(ListSchema schema);
|
||||
// Only valid for T = DynamicList. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> initAs();
|
||||
// Valid for T = any generated struct type.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> initAs(uint elementCount);
|
||||
// Valid for T = List<U>, Text, or Data.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> initAs(StructSchema schema);
|
||||
// Only valid for T = DynamicStruct. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> initAs(ListSchema schema, uint elementCount);
|
||||
// Only valid for T = DynamicList. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
inline AnyList::Builder initAsAnyList(ElementSize elementSize, uint elementCount);
|
||||
// Note: Does not accept INLINE_COMPOSITE for elementSize.
|
||||
|
||||
inline List<AnyStruct>::Builder initAsListOfAnyStruct(
|
||||
uint16_t dataWordCount, uint16_t pointerCount, uint elementCount);
|
||||
|
||||
inline AnyStruct::Builder initAsAnyStruct(uint16_t dataWordCount, uint16_t pointerCount);
|
||||
|
||||
template <typename T>
|
||||
inline void setAs(ReaderFor<T> value);
|
||||
// Valid for ReaderType = T::Reader for T = any generated struct type, List<U>, Text, Data,
|
||||
// DynamicStruct, or DynamicList (the dynamic types require `#include <capnp/dynamic.h>`).
|
||||
|
||||
template <typename T>
|
||||
inline void setAs(std::initializer_list<ReaderFor<ListElementType<T>>> list);
|
||||
// Valid for T = List<?>.
|
||||
|
||||
inline void set(Reader value) { builder.copyFrom(value.reader); }
|
||||
// Set to a copy of another AnyPointer.
|
||||
|
||||
template <typename T>
|
||||
inline void adopt(Orphan<T>&& orphan);
|
||||
// Valid for T = any generated struct type, List<U>, Text, Data, DynamicList, DynamicStruct,
|
||||
// or DynamicValue (the dynamic types require `#include <capnp/dynamic.h>`).
|
||||
|
||||
template <typename T>
|
||||
inline Orphan<T> disownAs();
|
||||
// Valid for T = any generated struct type, List<U>, Text, Data.
|
||||
|
||||
template <typename T>
|
||||
inline Orphan<T> disownAs(StructSchema schema);
|
||||
// Only valid for T = DynamicStruct. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
template <typename T>
|
||||
inline Orphan<T> disownAs(ListSchema schema);
|
||||
// Only valid for T = DynamicList. Requires `#include <capnp/dynamic.h>`.
|
||||
|
||||
inline Orphan<AnyPointer> disown();
|
||||
// Disown without a type.
|
||||
|
||||
inline Reader asReader() const { return Reader(builder.asReader()); }
|
||||
inline operator Reader() const { return Reader(builder.asReader()); }
|
||||
|
||||
private:
|
||||
_::PointerBuilder builder;
|
||||
friend class Orphanage;
|
||||
friend struct _::PointerHelpers<AnyPointer>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
class Orphan<AnyPointer> {
|
||||
// An orphaned object of unknown type.
|
||||
|
||||
public:
|
||||
Orphan() = default;
|
||||
KJ_DISALLOW_COPY(Orphan);
|
||||
Orphan(Orphan&&) = default;
|
||||
inline Orphan(_::OrphanBuilder&& builder)
|
||||
: builder(kj::mv(builder)) {}
|
||||
|
||||
Orphan& operator=(Orphan&&) = default;
|
||||
|
||||
template <typename T>
|
||||
inline Orphan(Orphan<T>&& other): builder(kj::mv(other.builder)) {}
|
||||
template <typename T>
|
||||
inline Orphan& operator=(Orphan<T>&& other) { builder = kj::mv(other.builder); return *this; }
|
||||
// Cast from typed orphan.
|
||||
|
||||
// It's not possible to get an AnyPointer::{Reader,Builder} directly since there is no
|
||||
// underlying pointer (the pointer would normally live in the parent, but this object is
|
||||
// orphaned). It is possible, however, to request typed readers/builders.
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> getAs();
|
||||
template <typename T>
|
||||
inline BuilderFor<T> getAs(StructSchema schema);
|
||||
template <typename T>
|
||||
inline BuilderFor<T> getAs(ListSchema schema);
|
||||
template <typename T>
|
||||
inline ReaderFor<T> getAsReader() const;
|
||||
template <typename T>
|
||||
inline ReaderFor<T> getAsReader(StructSchema schema) const;
|
||||
template <typename T>
|
||||
inline ReaderFor<T> getAsReader(ListSchema schema) const;
|
||||
|
||||
template <typename T>
|
||||
inline Orphan<T> releaseAs();
|
||||
template <typename T>
|
||||
inline Orphan<T> releaseAs(StructSchema schema);
|
||||
template <typename T>
|
||||
inline Orphan<T> releaseAs(ListSchema schema);
|
||||
// Down-cast the orphan to a specific type.
|
||||
|
||||
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
|
||||
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
|
||||
|
||||
private:
|
||||
_::OrphanBuilder builder;
|
||||
|
||||
template <typename, Kind>
|
||||
friend struct _::PointerHelpers;
|
||||
friend class Orphanage;
|
||||
template <typename U>
|
||||
friend class Orphan;
|
||||
friend class AnyPointer::Builder;
|
||||
};
|
||||
|
||||
template <Kind k> struct AnyTypeFor_;
|
||||
template <> struct AnyTypeFor_<Kind::STRUCT> { typedef AnyStruct Type; };
|
||||
template <> struct AnyTypeFor_<Kind::LIST> { typedef AnyList Type; };
|
||||
|
||||
template <typename T>
|
||||
using AnyTypeFor = typename AnyTypeFor_<CAPNP_KIND(T)>::Type;
|
||||
|
||||
template <typename T>
|
||||
inline ReaderFor<AnyTypeFor<FromReader<T>>> toAny(T&& value) {
|
||||
return ReaderFor<AnyTypeFor<FromReader<T>>>(
|
||||
_::PointerHelpers<FromReader<T>>::getInternalReader(value));
|
||||
}
|
||||
template <typename T>
|
||||
inline BuilderFor<AnyTypeFor<FromBuilder<T>>> toAny(T&& value) {
|
||||
return BuilderFor<AnyTypeFor<FromBuilder<T>>>(
|
||||
_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::mv(value)));
|
||||
}
|
||||
|
||||
template <>
|
||||
struct List<AnyPointer, Kind::OTHER> {
|
||||
// Note: This cannot be used for a list of structs, since such lists are not encoded as pointer
|
||||
// lists! Use List<AnyStruct>.
|
||||
|
||||
List() = delete;
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
typedef List<AnyPointer> Reads;
|
||||
|
||||
inline Reader(): reader(ElementSize::POINTER) {}
|
||||
inline explicit Reader(_::ListReader reader): reader(reader) {}
|
||||
|
||||
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
|
||||
inline AnyPointer::Reader operator[](uint index) const {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return AnyPointer::Reader(reader.getPointerElement(bounded(index) * ELEMENTS));
|
||||
}
|
||||
|
||||
typedef _::IndexingIterator<const Reader, typename AnyPointer::Reader> Iterator;
|
||||
inline Iterator begin() const { return Iterator(this, 0); }
|
||||
inline Iterator end() const { return Iterator(this, size()); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
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<AnyPointer> Builds;
|
||||
|
||||
Builder() = delete;
|
||||
inline Builder(decltype(nullptr)): builder(ElementSize::POINTER) {}
|
||||
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 AnyPointer::Builder operator[](uint index) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return AnyPointer::Builder(builder.getPointerElement(bounded(index) * ELEMENTS));
|
||||
}
|
||||
|
||||
typedef _::IndexingIterator<Builder, typename AnyPointer::Builder> Iterator;
|
||||
inline Iterator begin() { return Iterator(this, 0); }
|
||||
inline Iterator end() { return Iterator(this, size()); }
|
||||
|
||||
private:
|
||||
_::ListBuilder builder;
|
||||
template <typename, Kind>
|
||||
friend struct _::PointerHelpers;
|
||||
friend class Orphanage;
|
||||
template <typename, Kind>
|
||||
friend struct ToDynamic_;
|
||||
};
|
||||
};
|
||||
|
||||
class AnyStruct::Reader {
|
||||
public:
|
||||
typedef AnyStruct Reads;
|
||||
|
||||
Reader() = default;
|
||||
inline Reader(_::StructReader reader): _reader(reader) {}
|
||||
|
||||
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromReader<T>) == Kind::STRUCT>>
|
||||
inline Reader(T&& value)
|
||||
: _reader(_::PointerHelpers<FromReader<T>>::getInternalReader(kj::fwd<T>(value))) {}
|
||||
|
||||
inline MessageSize totalSize() const { return _reader.totalSize().asPublic(); }
|
||||
|
||||
kj::ArrayPtr<const byte> getDataSection() const {
|
||||
return _reader.getDataSectionAsBlob();
|
||||
}
|
||||
List<AnyPointer>::Reader getPointerSection() const {
|
||||
return List<AnyPointer>::Reader(_reader.getPointerSectionAsList());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReaderFor<T> as() const {
|
||||
// T must be a struct type.
|
||||
return typename T::Reader(_reader);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReaderFor<T> as(StructSchema schema) const;
|
||||
// T must be DynamicStruct. Defined in dynamic.h.
|
||||
|
||||
private:
|
||||
_::StructReader _reader;
|
||||
|
||||
template <typename, Kind>
|
||||
friend struct _::PointerHelpers;
|
||||
friend class Orphanage;
|
||||
};
|
||||
|
||||
class AnyStruct::Builder {
|
||||
public:
|
||||
typedef AnyStruct Builds;
|
||||
|
||||
inline Builder(decltype(nullptr)) {}
|
||||
inline Builder(_::StructBuilder builder): _builder(builder) {}
|
||||
|
||||
#if !_MSC_VER || defined(__clang__) // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
|
||||
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromBuilder<T>) == Kind::STRUCT>>
|
||||
inline Builder(T&& value)
|
||||
: _builder(_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::fwd<T>(value))) {}
|
||||
#endif
|
||||
|
||||
inline kj::ArrayPtr<byte> getDataSection() {
|
||||
return _builder.getDataSectionAsBlob();
|
||||
}
|
||||
List<AnyPointer>::Builder getPointerSection() {
|
||||
return List<AnyPointer>::Builder(_builder.getPointerSectionAsList());
|
||||
}
|
||||
|
||||
inline operator Reader() const { return Reader(_builder.asReader()); }
|
||||
inline Reader asReader() const { return Reader(_builder.asReader()); }
|
||||
|
||||
template <typename T>
|
||||
BuilderFor<T> as() {
|
||||
// T must be a struct type.
|
||||
return typename T::Builder(_builder);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BuilderFor<T> as(StructSchema schema);
|
||||
// T must be DynamicStruct. Defined in dynamic.h.
|
||||
|
||||
private:
|
||||
_::StructBuilder _builder;
|
||||
friend class Orphanage;
|
||||
};
|
||||
|
||||
class List<AnyStruct, Kind::OTHER>::Reader {
|
||||
public:
|
||||
typedef List<AnyStruct> Reads;
|
||||
|
||||
inline Reader(): reader(ElementSize::INLINE_COMPOSITE) {}
|
||||
inline explicit Reader(_::ListReader reader): reader(reader) {}
|
||||
|
||||
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
|
||||
inline AnyStruct::Reader operator[](uint index) const {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return AnyStruct::Reader(reader.getStructElement(bounded(index) * ELEMENTS));
|
||||
}
|
||||
|
||||
typedef _::IndexingIterator<const Reader, typename AnyStruct::Reader> Iterator;
|
||||
inline Iterator begin() const { return Iterator(this, 0); }
|
||||
inline Iterator end() const { return Iterator(this, size()); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
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 List<AnyStruct, Kind::OTHER>::Builder {
|
||||
public:
|
||||
typedef List<AnyStruct> Builds;
|
||||
|
||||
Builder() = delete;
|
||||
inline Builder(decltype(nullptr)): builder(ElementSize::INLINE_COMPOSITE) {}
|
||||
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 AnyStruct::Builder operator[](uint index) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return AnyStruct::Builder(builder.getStructElement(bounded(index) * ELEMENTS));
|
||||
}
|
||||
|
||||
typedef _::IndexingIterator<Builder, typename AnyStruct::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 AnyList::Reader {
|
||||
public:
|
||||
typedef AnyList Reads;
|
||||
|
||||
inline Reader(): _reader(ElementSize::VOID) {}
|
||||
inline Reader(_::ListReader reader): _reader(reader) {}
|
||||
|
||||
#if !_MSC_VER || defined(__clang__) // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
|
||||
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromReader<T>) == Kind::LIST>>
|
||||
inline Reader(T&& value)
|
||||
: _reader(_::PointerHelpers<FromReader<T>>::getInternalReader(kj::fwd<T>(value))) {}
|
||||
#endif
|
||||
|
||||
inline ElementSize getElementSize() const { return _reader.getElementSize(); }
|
||||
inline uint size() const { return unbound(_reader.size() / ELEMENTS); }
|
||||
|
||||
inline kj::ArrayPtr<const byte> getRawBytes() const { return _reader.asRawBytes(); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return _reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
template <typename T> ReaderFor<T> as() const {
|
||||
// T must be List<U>.
|
||||
return ReaderFor<T>(_reader);
|
||||
}
|
||||
private:
|
||||
_::ListReader _reader;
|
||||
|
||||
template <typename, Kind>
|
||||
friend struct _::PointerHelpers;
|
||||
friend class Orphanage;
|
||||
};
|
||||
|
||||
class AnyList::Builder {
|
||||
public:
|
||||
typedef AnyList Builds;
|
||||
|
||||
inline Builder(decltype(nullptr)): _builder(ElementSize::VOID) {}
|
||||
inline Builder(_::ListBuilder builder): _builder(builder) {}
|
||||
|
||||
#if !_MSC_VER || defined(__clang__) // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
|
||||
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromBuilder<T>) == Kind::LIST>>
|
||||
inline Builder(T&& value)
|
||||
: _builder(_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::fwd<T>(value))) {}
|
||||
#endif
|
||||
|
||||
inline ElementSize getElementSize() { return _builder.getElementSize(); }
|
||||
inline uint size() { return unbound(_builder.size() / ELEMENTS); }
|
||||
|
||||
template <typename T> BuilderFor<T> as() {
|
||||
// T must be List<U>.
|
||||
return BuilderFor<T>(_builder);
|
||||
}
|
||||
|
||||
inline operator Reader() const { return Reader(_builder.asReader()); }
|
||||
inline Reader asReader() const { return Reader(_builder.asReader()); }
|
||||
|
||||
private:
|
||||
_::ListBuilder _builder;
|
||||
|
||||
friend class Orphanage;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
inline MessageSize AnyPointer::Reader::targetSize() const {
|
||||
return reader.targetSize().asPublic();
|
||||
}
|
||||
|
||||
inline PointerType AnyPointer::Reader::getPointerType() const {
|
||||
return reader.getPointerType();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReaderFor<T> AnyPointer::Reader::getAs() const {
|
||||
return _::PointerHelpers<T>::get(reader);
|
||||
}
|
||||
|
||||
inline MessageSize AnyPointer::Builder::targetSize() const {
|
||||
return asReader().targetSize();
|
||||
}
|
||||
|
||||
inline PointerType AnyPointer::Builder::getPointerType() {
|
||||
return builder.getPointerType();
|
||||
}
|
||||
|
||||
inline void AnyPointer::Builder::clear() {
|
||||
return builder.clear();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> AnyPointer::Builder::getAs() {
|
||||
return _::PointerHelpers<T>::get(builder);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> AnyPointer::Builder::initAs() {
|
||||
return _::PointerHelpers<T>::init(builder);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> AnyPointer::Builder::initAs(uint elementCount) {
|
||||
return _::PointerHelpers<T>::init(builder, elementCount);
|
||||
}
|
||||
|
||||
inline AnyList::Builder AnyPointer::Builder::initAsAnyList(
|
||||
ElementSize elementSize, uint elementCount) {
|
||||
return AnyList::Builder(builder.initList(elementSize, bounded(elementCount) * ELEMENTS));
|
||||
}
|
||||
|
||||
inline List<AnyStruct>::Builder AnyPointer::Builder::initAsListOfAnyStruct(
|
||||
uint16_t dataWordCount, uint16_t pointerCount, uint elementCount) {
|
||||
return List<AnyStruct>::Builder(builder.initStructList(bounded(elementCount) * ELEMENTS,
|
||||
_::StructSize(bounded(dataWordCount) * WORDS,
|
||||
bounded(pointerCount) * POINTERS)));
|
||||
}
|
||||
|
||||
inline AnyStruct::Builder AnyPointer::Builder::initAsAnyStruct(
|
||||
uint16_t dataWordCount, uint16_t pointerCount) {
|
||||
return AnyStruct::Builder(builder.initStruct(
|
||||
_::StructSize(bounded(dataWordCount) * WORDS,
|
||||
bounded(pointerCount) * POINTERS)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void AnyPointer::Builder::setAs(ReaderFor<T> value) {
|
||||
return _::PointerHelpers<T>::set(builder, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void AnyPointer::Builder::setAs(
|
||||
std::initializer_list<ReaderFor<ListElementType<T>>> list) {
|
||||
return _::PointerHelpers<T>::set(builder, list);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void AnyPointer::Builder::adopt(Orphan<T>&& orphan) {
|
||||
_::PointerHelpers<T>::adopt(builder, kj::mv(orphan));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Orphan<T> AnyPointer::Builder::disownAs() {
|
||||
return _::PointerHelpers<T>::disown(builder);
|
||||
}
|
||||
|
||||
inline Orphan<AnyPointer> AnyPointer::Builder::disown() {
|
||||
return Orphan<AnyPointer>(builder.disown());
|
||||
}
|
||||
|
||||
template <> struct ReaderFor_ <AnyPointer, Kind::OTHER> { typedef AnyPointer::Reader Type; };
|
||||
template <> struct BuilderFor_<AnyPointer, Kind::OTHER> { typedef AnyPointer::Builder Type; };
|
||||
template <> struct ReaderFor_ <AnyStruct, Kind::OTHER> { typedef AnyStruct::Reader Type; };
|
||||
template <> struct BuilderFor_<AnyStruct, Kind::OTHER> { typedef AnyStruct::Builder Type; };
|
||||
|
||||
template <>
|
||||
struct Orphanage::GetInnerReader<AnyPointer, Kind::OTHER> {
|
||||
static inline _::PointerReader apply(const AnyPointer::Reader& t) {
|
||||
return t.reader;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::GetInnerBuilder<AnyPointer, Kind::OTHER> {
|
||||
static inline _::PointerBuilder apply(AnyPointer::Builder& t) {
|
||||
return t.builder;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::GetInnerReader<AnyStruct, Kind::OTHER> {
|
||||
static inline _::StructReader apply(const AnyStruct::Reader& t) {
|
||||
return t._reader;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::GetInnerBuilder<AnyStruct, Kind::OTHER> {
|
||||
static inline _::StructBuilder apply(AnyStruct::Builder& t) {
|
||||
return t._builder;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::GetInnerReader<AnyList, Kind::OTHER> {
|
||||
static inline _::ListReader apply(const AnyList::Reader& t) {
|
||||
return t._reader;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::GetInnerBuilder<AnyList, Kind::OTHER> {
|
||||
static inline _::ListBuilder apply(AnyList::Builder& t) {
|
||||
return t._builder;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline BuilderFor<T> Orphan<AnyPointer>::getAs() {
|
||||
return _::OrphanGetImpl<T>::apply(builder);
|
||||
}
|
||||
template <typename T>
|
||||
inline ReaderFor<T> Orphan<AnyPointer>::getAsReader() const {
|
||||
return _::OrphanGetImpl<T>::applyReader(builder);
|
||||
}
|
||||
template <typename T>
|
||||
inline Orphan<T> Orphan<AnyPointer>::releaseAs() {
|
||||
return Orphan<T>(kj::mv(builder));
|
||||
}
|
||||
|
||||
// Using AnyPointer as the template type should work...
|
||||
|
||||
template <>
|
||||
inline typename AnyPointer::Reader AnyPointer::Reader::getAs<AnyPointer>() const {
|
||||
return *this;
|
||||
}
|
||||
template <>
|
||||
inline typename AnyPointer::Builder AnyPointer::Builder::getAs<AnyPointer>() {
|
||||
return *this;
|
||||
}
|
||||
template <>
|
||||
inline typename AnyPointer::Builder AnyPointer::Builder::initAs<AnyPointer>() {
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
template <>
|
||||
inline void AnyPointer::Builder::setAs<AnyPointer>(AnyPointer::Reader value) {
|
||||
return builder.copyFrom(value.reader);
|
||||
}
|
||||
template <>
|
||||
inline void AnyPointer::Builder::adopt<AnyPointer>(Orphan<AnyPointer>&& orphan) {
|
||||
builder.adopt(kj::mv(orphan.builder));
|
||||
}
|
||||
template <>
|
||||
inline Orphan<AnyPointer> AnyPointer::Builder::disownAs<AnyPointer>() {
|
||||
return Orphan<AnyPointer>(builder.disown());
|
||||
}
|
||||
template <>
|
||||
inline Orphan<AnyPointer> Orphan<AnyPointer>::releaseAs() {
|
||||
return kj::mv(*this);
|
||||
}
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
// Specialize PointerHelpers for AnyPointer.
|
||||
|
||||
template <>
|
||||
struct PointerHelpers<AnyPointer, Kind::OTHER> {
|
||||
static inline AnyPointer::Reader get(PointerReader reader,
|
||||
const void* defaultValue = nullptr,
|
||||
uint defaultBytes = 0) {
|
||||
return AnyPointer::Reader(reader);
|
||||
}
|
||||
static inline AnyPointer::Builder get(PointerBuilder builder,
|
||||
const void* defaultValue = nullptr,
|
||||
uint defaultBytes = 0) {
|
||||
return AnyPointer::Builder(builder);
|
||||
}
|
||||
static inline void set(PointerBuilder builder, AnyPointer::Reader value) {
|
||||
AnyPointer::Builder(builder).set(value);
|
||||
}
|
||||
static inline void adopt(PointerBuilder builder, Orphan<AnyPointer>&& value) {
|
||||
builder.adopt(kj::mv(value.builder));
|
||||
}
|
||||
static inline Orphan<AnyPointer> disown(PointerBuilder builder) {
|
||||
return Orphan<AnyPointer>(builder.disown());
|
||||
}
|
||||
static inline _::PointerReader getInternalReader(const AnyPointer::Reader& reader) {
|
||||
return reader.reader;
|
||||
}
|
||||
static inline _::PointerBuilder getInternalBuilder(AnyPointer::Builder&& builder) {
|
||||
return builder.builder;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PointerHelpers<AnyStruct, Kind::OTHER> {
|
||||
static inline AnyStruct::Reader get(
|
||||
PointerReader reader, const word* defaultValue = nullptr) {
|
||||
return AnyStruct::Reader(reader.getStruct(defaultValue));
|
||||
}
|
||||
static inline AnyStruct::Builder get(
|
||||
PointerBuilder builder, const word* defaultValue = nullptr) {
|
||||
// TODO(someday): Allow specifying the size somehow?
|
||||
return AnyStruct::Builder(builder.getStruct(
|
||||
_::StructSize(ZERO * WORDS, ZERO * POINTERS), defaultValue));
|
||||
}
|
||||
static inline void set(PointerBuilder builder, AnyStruct::Reader value) {
|
||||
builder.setStruct(value._reader);
|
||||
}
|
||||
static inline AnyStruct::Builder init(
|
||||
PointerBuilder builder, uint16_t dataWordCount, uint16_t pointerCount) {
|
||||
return AnyStruct::Builder(builder.initStruct(
|
||||
StructSize(bounded(dataWordCount) * WORDS,
|
||||
bounded(pointerCount) * POINTERS)));
|
||||
}
|
||||
|
||||
static void adopt(PointerBuilder builder, Orphan<AnyStruct>&& value) {
|
||||
builder.adopt(kj::mv(value.builder));
|
||||
}
|
||||
static Orphan<AnyStruct> disown(PointerBuilder builder) {
|
||||
return Orphan<AnyStruct>(builder.disown());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PointerHelpers<AnyList, Kind::OTHER> {
|
||||
static inline AnyList::Reader get(
|
||||
PointerReader reader, const word* defaultValue = nullptr) {
|
||||
return AnyList::Reader(reader.getListAnySize(defaultValue));
|
||||
}
|
||||
static inline AnyList::Builder get(
|
||||
PointerBuilder builder, const word* defaultValue = nullptr) {
|
||||
return AnyList::Builder(builder.getListAnySize(defaultValue));
|
||||
}
|
||||
static inline void set(PointerBuilder builder, AnyList::Reader value) {
|
||||
builder.setList(value._reader);
|
||||
}
|
||||
static inline AnyList::Builder init(
|
||||
PointerBuilder builder, ElementSize elementSize, uint elementCount) {
|
||||
return AnyList::Builder(builder.initList(
|
||||
elementSize, bounded(elementCount) * ELEMENTS));
|
||||
}
|
||||
static inline AnyList::Builder init(
|
||||
PointerBuilder builder, uint16_t dataWordCount, uint16_t pointerCount, uint elementCount) {
|
||||
return AnyList::Builder(builder.initStructList(
|
||||
bounded(elementCount) * ELEMENTS,
|
||||
StructSize(bounded(dataWordCount) * WORDS,
|
||||
bounded(pointerCount) * POINTERS)));
|
||||
}
|
||||
|
||||
static void adopt(PointerBuilder builder, Orphan<AnyList>&& value) {
|
||||
builder.adopt(kj::mv(value.builder));
|
||||
}
|
||||
static Orphan<AnyList> disown(PointerBuilder builder) {
|
||||
return Orphan<AnyList>(builder.disown());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct OrphanGetImpl<AnyStruct, Kind::OTHER> {
|
||||
static inline AnyStruct::Builder apply(_::OrphanBuilder& builder) {
|
||||
return AnyStruct::Builder(builder.asStruct(_::StructSize(ZERO * WORDS, ZERO * POINTERS)));
|
||||
}
|
||||
static inline AnyStruct::Reader applyReader(const _::OrphanBuilder& builder) {
|
||||
return AnyStruct::Reader(builder.asStructReader(_::StructSize(ZERO * WORDS, ZERO * POINTERS)));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct OrphanGetImpl<AnyList, Kind::OTHER> {
|
||||
static inline AnyList::Builder apply(_::OrphanBuilder& builder) {
|
||||
return AnyList::Builder(builder.asListAnySize());
|
||||
}
|
||||
static inline AnyList::Reader applyReader(const _::OrphanBuilder& builder) {
|
||||
return AnyList::Reader(builder.asListReaderAnySize());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
286
vendor/capnproto/src/capnp/arena.c++
vendored
Normal file
286
vendor/capnproto/src/capnp/arena.c++
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
// 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.
|
||||
|
||||
#define CAPNP_PRIVATE
|
||||
#include "arena.h"
|
||||
#include "message.h"
|
||||
#include <kj/debug.h>
|
||||
#include <kj/refcount.h>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace capnp {
|
||||
namespace _ { // private
|
||||
|
||||
Arena::~Arena() noexcept(false) {}
|
||||
|
||||
void ReadLimiter::unread(WordCount64 amount) {
|
||||
// Be careful not to overflow here. Since ReadLimiter has no thread-safety, it's possible that
|
||||
// the limit value was not updated correctly for one or more reads, and therefore unread() could
|
||||
// overflow it even if it is only unreading bytes that were actually read.
|
||||
uint64_t oldValue = readLimit();
|
||||
uint64_t newValue = oldValue + unbound(amount / WORDS);
|
||||
if (newValue > oldValue) {
|
||||
setLimit(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
void SegmentReader::abortCheckObjectFault() {
|
||||
KJ_LOG(FATAL, "checkObject()'s parameter is not in-range; this would segfault in opt mode",
|
||||
"this is a serious bug in Cap'n Proto; please notify security@sandstorm.io");
|
||||
abort();
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
static SegmentWordCount verifySegmentSize(size_t size) {
|
||||
auto gsize = bounded(size) * WORDS;
|
||||
return assertMaxBits<SEGMENT_WORD_COUNT_BITS>(gsize, [&]() {
|
||||
KJ_FAIL_REQUIRE("segment is too large", size);
|
||||
});
|
||||
}
|
||||
|
||||
static SegmentWordCount verifySegment(kj::ArrayPtr<const word> segment) {
|
||||
#if !CAPNP_ALLOW_UNALIGNED
|
||||
KJ_REQUIRE(reinterpret_cast<uintptr_t>(segment.begin()) % sizeof(void*) == 0,
|
||||
"Detected unaligned data in Cap'n Proto message. Messages must be aligned to the "
|
||||
"architecture's word size. Yes, even on x86: Unaligned access is undefined behavior "
|
||||
"under the C/C++ language standard, and compilers can and do assume alignment for the "
|
||||
"purpose of optimizations. Unaligned access may lead to crashes or subtle corruption. "
|
||||
"For example, GCC will use SIMD instructions in optimizations, and those instrsuctions "
|
||||
"require alignment. If you really insist on taking your changes with unaligned data, "
|
||||
"compile the Cap'n Proto library with -DCAPNP_ALLOW_UNALIGNED to remove this check.") {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return verifySegmentSize(segment.size());
|
||||
}
|
||||
|
||||
inline ReaderArena::ReaderArena(MessageReader* message, const word* firstSegment,
|
||||
SegmentWordCount firstSegmentSize)
|
||||
: message(message),
|
||||
readLimiter(bounded(message->getOptions().traversalLimitInWords) * WORDS),
|
||||
segment0(this, SegmentId(0), firstSegment, firstSegmentSize, &readLimiter) {}
|
||||
|
||||
inline ReaderArena::ReaderArena(MessageReader* message, kj::ArrayPtr<const word> firstSegment)
|
||||
: ReaderArena(message, firstSegment.begin(), verifySegment(firstSegment)) {}
|
||||
|
||||
ReaderArena::ReaderArena(MessageReader* message)
|
||||
: ReaderArena(message, message->getSegment(0)) {}
|
||||
|
||||
ReaderArena::~ReaderArena() noexcept(false) {}
|
||||
|
||||
SegmentReader* ReaderArena::tryGetSegment(SegmentId id) {
|
||||
if (id == SegmentId(0)) {
|
||||
if (segment0.getArray() == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return &segment0;
|
||||
}
|
||||
}
|
||||
|
||||
auto lock = moreSegments.lockExclusive();
|
||||
|
||||
SegmentMap* segments = nullptr;
|
||||
KJ_IF_MAYBE(s, *lock) {
|
||||
KJ_IF_MAYBE(segment, s->find(id.value)) {
|
||||
return *segment;
|
||||
}
|
||||
segments = s;
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const word> newSegment = message->getSegment(id.value);
|
||||
if (newSegment == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SegmentWordCount newSegmentSize = verifySegment(newSegment);
|
||||
|
||||
if (*lock == nullptr) {
|
||||
// OK, the segment exists, so allocate the map.
|
||||
segments = &lock->emplace();
|
||||
}
|
||||
|
||||
auto segment = kj::heap<SegmentReader>(
|
||||
this, id, newSegment.begin(), newSegmentSize, &readLimiter);
|
||||
SegmentReader* result = segment;
|
||||
segments->insert(id.value, kj::mv(segment));
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReaderArena::reportReadLimitReached() {
|
||||
KJ_FAIL_REQUIRE("Exceeded message traversal limit. See capnp::ReaderOptions.") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
BuilderArena::BuilderArena(MessageBuilder* message)
|
||||
: message(message), segment0(nullptr, SegmentId(0), nullptr, nullptr) {}
|
||||
|
||||
BuilderArena::~BuilderArena() noexcept(false) {}
|
||||
|
||||
SegmentBuilder* BuilderArena::getSegment(SegmentId id) {
|
||||
// This method is allowed to fail if the segment ID is not valid.
|
||||
if (id == SegmentId(0)) {
|
||||
return &segment0;
|
||||
} else {
|
||||
KJ_IF_MAYBE(s, moreSegments) {
|
||||
KJ_REQUIRE(id.value - 1 < s->get()->builders.size(), "invalid segment id", id.value);
|
||||
return const_cast<SegmentBuilder*>(s->get()->builders[id.value - 1].get());
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("invalid segment id", id.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BuilderArena::AllocateResult BuilderArena::allocate(SegmentWordCount amount) {
|
||||
if (segment0.getArena() == nullptr) {
|
||||
// We're allocating the first segment.
|
||||
kj::ArrayPtr<word> ptr = message->allocateSegment(unbound(amount / WORDS));
|
||||
auto actualSize = verifySegment(ptr);
|
||||
|
||||
// Re-allocate segment0 in-place. This is a bit of a hack, but we have not returned any
|
||||
// pointers to this segment yet, so it should be fine.
|
||||
kj::dtor(segment0);
|
||||
kj::ctor(segment0, this, SegmentId(0), ptr.begin(), actualSize, &this->dummyLimiter);
|
||||
|
||||
segmentWithSpace = &segment0;
|
||||
return AllocateResult { &segment0, segment0.allocate(amount) };
|
||||
} else {
|
||||
if (segmentWithSpace != nullptr) {
|
||||
// Check if there is space in an existing segment.
|
||||
// TODO(perf): Check for available space in more than just the last segment. We don't
|
||||
// want this to be O(n), though, so we'll need to maintain some sort of table. Complicating
|
||||
// matters, we want SegmentBuilders::allocate() to be fast, so we can't update any such
|
||||
// table when allocation actually happens. Instead, we could have a priority queue based
|
||||
// on the last-known available size, and then re-check the size when we pop segments off it
|
||||
// and shove them to the back of the queue if they have become too small.
|
||||
word* attempt = segmentWithSpace->allocate(amount);
|
||||
if (attempt != nullptr) {
|
||||
return AllocateResult { segmentWithSpace, attempt };
|
||||
}
|
||||
}
|
||||
|
||||
// Need to allocate a new segment.
|
||||
SegmentBuilder* result = addSegmentInternal(message->allocateSegment(unbound(amount / WORDS)));
|
||||
|
||||
// Check this new segment first the next time we need to allocate.
|
||||
segmentWithSpace = result;
|
||||
|
||||
// Allocating from the new segment is guaranteed to succeed since we made it big enough.
|
||||
return AllocateResult { result, result->allocate(amount) };
|
||||
}
|
||||
}
|
||||
|
||||
SegmentBuilder* BuilderArena::addSegmentInternal(kj::ArrayPtr<word> content) {
|
||||
// This check should never fail in practice, since you can't get an Orphanage without allocating
|
||||
// the root segment.
|
||||
KJ_REQUIRE(segment0.getArena() != nullptr,
|
||||
"Can't allocate segments before allocating the root segment.");
|
||||
|
||||
auto contentSize = verifySegmentSize(content.size());
|
||||
|
||||
MultiSegmentState* segmentState;
|
||||
KJ_IF_MAYBE(s, moreSegments) {
|
||||
segmentState = *s;
|
||||
} else {
|
||||
auto newSegmentState = kj::heap<MultiSegmentState>();
|
||||
segmentState = newSegmentState;
|
||||
moreSegments = kj::mv(newSegmentState);
|
||||
}
|
||||
|
||||
kj::Own<SegmentBuilder> newBuilder = kj::heap<SegmentBuilder>(
|
||||
this, SegmentId(segmentState->builders.size() + 1),
|
||||
content.begin(), contentSize, &this->dummyLimiter);
|
||||
SegmentBuilder* result = newBuilder.get();
|
||||
segmentState->builders.add(kj::mv(newBuilder));
|
||||
|
||||
// Keep forOutput the right size so that we don't have to re-allocate during
|
||||
// getSegmentsForOutput(), which callers might reasonably expect is a thread-safe method.
|
||||
segmentState->forOutput.resize(segmentState->builders.size() + 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const kj::ArrayPtr<const word>> BuilderArena::getSegmentsForOutput() {
|
||||
// Although this is a read-only method, we shouldn't need to lock a mutex here because if this
|
||||
// is called multiple times simultaneously, we should only be overwriting the array with the
|
||||
// exact same data. If the number or size of segments is actually changing due to an activity
|
||||
// in another thread, then the caller has a problem regardless of locking here.
|
||||
|
||||
KJ_IF_MAYBE(segmentState, moreSegments) {
|
||||
KJ_DASSERT(segmentState->get()->forOutput.size() == segmentState->get()->builders.size() + 1,
|
||||
"segmentState->forOutput wasn't resized correctly when the last builder was added.",
|
||||
segmentState->get()->forOutput.size(), segmentState->get()->builders.size());
|
||||
|
||||
kj::ArrayPtr<kj::ArrayPtr<const word>> result(
|
||||
&segmentState->get()->forOutput[0], segmentState->get()->forOutput.size());
|
||||
uint i = 0;
|
||||
result[i++] = segment0.currentlyAllocated();
|
||||
for (auto& builder: segmentState->get()->builders) {
|
||||
result[i++] = builder->currentlyAllocated();
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (segment0.getArena() == nullptr) {
|
||||
// We haven't actually allocated any segments yet.
|
||||
return nullptr;
|
||||
} else {
|
||||
// We have only one segment so far.
|
||||
segment0ForOutput = segment0.currentlyAllocated();
|
||||
return kj::arrayPtr(&segment0ForOutput, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SegmentReader* BuilderArena::tryGetSegment(SegmentId id) {
|
||||
if (id == SegmentId(0)) {
|
||||
if (segment0.getArena() == nullptr) {
|
||||
// We haven't allocated any segments yet.
|
||||
return nullptr;
|
||||
} else {
|
||||
return &segment0;
|
||||
}
|
||||
} else {
|
||||
KJ_IF_MAYBE(segmentState, moreSegments) {
|
||||
if (id.value <= segmentState->get()->builders.size()) {
|
||||
// TODO(cleanup): Return a const SegmentReader and tediously constify all SegmentBuilder
|
||||
// pointers throughout the codebase.
|
||||
return const_cast<SegmentReader*>(kj::implicitCast<const SegmentReader*>(
|
||||
segmentState->get()->builders[id.value - 1].get()));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BuilderArena::reportReadLimitReached() {
|
||||
KJ_FAIL_ASSERT("Read limit reached for BuilderArena, but it should have been unlimited.") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
||||
391
vendor/capnproto/src/capnp/arena.h
vendored
Normal file
391
vendor/capnproto/src/capnp/arena.h
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CAPNP_PRIVATE
|
||||
#error "This header is only meant to be included by Cap'n Proto's own source code."
|
||||
#endif
|
||||
|
||||
#include <kj/common.h>
|
||||
#include <kj/mutex.h>
|
||||
#include <kj/exception.h>
|
||||
#include <kj/vector.h>
|
||||
#include <kj/units.h>
|
||||
#include "common.h"
|
||||
#include "message.h"
|
||||
#include "layout.h"
|
||||
#include <kj/map.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class SegmentReader;
|
||||
class SegmentBuilder;
|
||||
class Arena;
|
||||
class BuilderArena;
|
||||
class ReadLimiter;
|
||||
|
||||
class Segment;
|
||||
typedef kj::Id<uint32_t, Segment> SegmentId;
|
||||
|
||||
class ReadLimiter {
|
||||
// Used to keep track of how much data has been processed from a message, and cut off further
|
||||
// processing if and when a particular limit is reached. This is primarily intended to guard
|
||||
// against maliciously-crafted messages which contain cycles or overlapping structures. Cycles
|
||||
// and overlapping are not permitted by the Cap'n Proto format because in many cases they could
|
||||
// be used to craft a deceptively small message which could consume excessive server resources to
|
||||
// process, perhaps even sending it into an infinite loop. Actually detecting overlaps would be
|
||||
// time-consuming, so instead we just keep track of how many words worth of data structures the
|
||||
// receiver has actually dereferenced and error out if this gets too high.
|
||||
//
|
||||
// This counting takes place as you call getters (for non-primitive values) on the message
|
||||
// readers. If you call the same getter twice, the data it returns may be double-counted. This
|
||||
// should not be a big deal in most cases -- just set the read limit high enough that it will
|
||||
// only trigger in unreasonable cases.
|
||||
//
|
||||
// This class is "safe" to use from multiple threads for its intended use case. Threads may
|
||||
// overwrite each others' changes to the counter, but this is OK because it only means that the
|
||||
// limit is enforced a bit less strictly -- it will still kick in eventually.
|
||||
|
||||
public:
|
||||
inline explicit ReadLimiter(); // No limit.
|
||||
inline explicit ReadLimiter(WordCount64 limit); // Limit to the given number of words.
|
||||
|
||||
KJ_ALWAYS_INLINE(bool canRead(WordCount64 amount, Arena* arena));
|
||||
|
||||
void unread(WordCount64 amount);
|
||||
// Adds back some words to the limit. Useful when the caller knows they are double-reading
|
||||
// some data.
|
||||
|
||||
private:
|
||||
alignas(8) volatile uint64_t limit;
|
||||
// Current limit, decremented each time catRead() is called. We modify this variable using atomics
|
||||
// with "relaxed" thread safety to make TSAN happy (on ARM & x86 this is no different from a
|
||||
// regular read/write of the variable). See the class comment for why this is OK (previously we
|
||||
// used a regular volatile variable - this is just to make ASAN happy).
|
||||
//
|
||||
// alignas(8) is the default on 64-bit systems, but needed on 32-bit to avoid an expensive
|
||||
// unaligned atomic operation.
|
||||
|
||||
KJ_DISALLOW_COPY_AND_MOVE(ReadLimiter);
|
||||
|
||||
KJ_ALWAYS_INLINE(void setLimit(uint64_t newLimit)) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
__atomic_store_n(&limit, newLimit, __ATOMIC_RELAXED);
|
||||
#else
|
||||
limit = newLimit;
|
||||
#endif
|
||||
}
|
||||
|
||||
KJ_ALWAYS_INLINE(uint64_t readLimit() const) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __atomic_load_n(&limit, __ATOMIC_RELAXED);
|
||||
#else
|
||||
return limit;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class SegmentReader {
|
||||
public:
|
||||
inline SegmentReader(Arena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
|
||||
ReadLimiter* readLimiter);
|
||||
|
||||
KJ_ALWAYS_INLINE(const word* checkOffset(const word* from, ptrdiff_t offset));
|
||||
// Adds the given offset to the given pointer, checks that it is still within the bounds of the
|
||||
// segment, then returns it. Note that the "end" pointer of the segment (which technically points
|
||||
// to the word after the last in the segment) is considered in-bounds for this purpose, so you
|
||||
// can't necessarily dereference it. You must call checkObject() next to check that the object
|
||||
// you want to read is entirely in-bounds.
|
||||
//
|
||||
// If `from + offset` is out-of-range, this returns a pointer to the end of the segment. Thus,
|
||||
// any non-zero-sized object will fail `checkObject()`. We do this instead of throwing to save
|
||||
// some code footprint.
|
||||
|
||||
KJ_ALWAYS_INLINE(bool checkObject(const word* start, WordCountN<31> size));
|
||||
// Assuming that `start` is in-bounds for this segment (probably checked using `checkOffset()`),
|
||||
// check that `start + size` is also in-bounds, and hence the whole area in-between is valid.
|
||||
|
||||
KJ_ALWAYS_INLINE(bool amplifiedRead(WordCount virtualAmount));
|
||||
// Indicates that the reader should pretend that `virtualAmount` additional data was read even
|
||||
// though no actual pointer was traversed. This is used e.g. when reading a struct list pointer
|
||||
// where the element sizes are zero -- the sender could set the list size arbitrarily high and
|
||||
// cause the receiver to iterate over this list even though the message itself is small, so we
|
||||
// need to defend against DoS attacks based on this.
|
||||
|
||||
inline Arena* getArena();
|
||||
inline SegmentId getSegmentId();
|
||||
|
||||
inline const word* getStartPtr();
|
||||
inline SegmentWordCount getOffsetTo(const word* ptr);
|
||||
inline SegmentWordCount getSize();
|
||||
|
||||
inline kj::ArrayPtr<const word> getArray();
|
||||
|
||||
inline void unread(WordCount64 amount);
|
||||
// Add back some words to the ReadLimiter.
|
||||
|
||||
private:
|
||||
Arena* arena;
|
||||
SegmentId id;
|
||||
kj::ArrayPtr<const word> ptr; // size guaranteed to fit in SEGMENT_WORD_COUNT_BITS bits
|
||||
ReadLimiter* readLimiter;
|
||||
|
||||
KJ_DISALLOW_COPY_AND_MOVE(SegmentReader);
|
||||
|
||||
friend class SegmentBuilder;
|
||||
|
||||
[[noreturn]] static void abortCheckObjectFault();
|
||||
// Called in debug mode in cases that would segfault in opt mode. (Should be impossible!)
|
||||
};
|
||||
|
||||
class SegmentBuilder: public SegmentReader {
|
||||
public:
|
||||
inline SegmentBuilder(BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
|
||||
ReadLimiter* readLimiter);
|
||||
inline SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
|
||||
ReadLimiter* readLimiter);
|
||||
|
||||
KJ_ALWAYS_INLINE(word* allocate(SegmentWordCount amount));
|
||||
|
||||
KJ_ALWAYS_INLINE(word* getPtrUnchecked(SegmentWordCount offset));
|
||||
// Get a writable pointer into the segment.
|
||||
|
||||
inline BuilderArena* getArena();
|
||||
|
||||
inline kj::ArrayPtr<const word> currentlyAllocated();
|
||||
|
||||
private:
|
||||
word* pos;
|
||||
// Pointer to a pointer to the current end point of the segment, i.e. the location where the
|
||||
// next object should be allocated.
|
||||
|
||||
KJ_DISALLOW_COPY_AND_MOVE(SegmentBuilder);
|
||||
};
|
||||
|
||||
class Arena {
|
||||
public:
|
||||
virtual ~Arena() noexcept(false);
|
||||
|
||||
virtual SegmentReader* tryGetSegment(SegmentId id) = 0;
|
||||
// Gets the segment with the given ID, or return nullptr if no such segment exists.
|
||||
|
||||
virtual void reportReadLimitReached() = 0;
|
||||
// Called to report that the read limit has been reached. See ReadLimiter, below. This invokes
|
||||
// the VALIDATE_INPUT() macro which may throw an exception; if it returns normally, the caller
|
||||
// will need to continue with default values.
|
||||
};
|
||||
|
||||
class ReaderArena final: public Arena {
|
||||
public:
|
||||
explicit ReaderArena(MessageReader* message);
|
||||
~ReaderArena() noexcept(false);
|
||||
KJ_DISALLOW_COPY_AND_MOVE(ReaderArena);
|
||||
|
||||
// implements Arena ------------------------------------------------
|
||||
SegmentReader* tryGetSegment(SegmentId id) override;
|
||||
void reportReadLimitReached() override;
|
||||
|
||||
private:
|
||||
MessageReader* message;
|
||||
ReadLimiter readLimiter;
|
||||
|
||||
// Optimize for single-segment messages so that small messages are handled quickly.
|
||||
SegmentReader segment0;
|
||||
|
||||
typedef kj::HashMap<uint, kj::Own<SegmentReader>> SegmentMap;
|
||||
kj::MutexGuarded<kj::Maybe<SegmentMap>> moreSegments;
|
||||
// We need to mutex-guard the segment map because we lazily initialize segments when they are
|
||||
// first requested, but a Reader is allowed to be used concurrently in multiple threads. Luckily
|
||||
// this only applies to large messages.
|
||||
//
|
||||
// TODO(perf): Thread-local thing instead? Some kind of lockless map? Or do sharing of data
|
||||
// in a different way, where you have to construct a new MessageReader in each thread (but
|
||||
// possibly backed by the same data)?
|
||||
|
||||
ReaderArena(MessageReader* message, kj::ArrayPtr<const word> firstSegment);
|
||||
ReaderArena(MessageReader* message, const word* firstSegment, SegmentWordCount firstSegmentSize);
|
||||
};
|
||||
|
||||
class BuilderArena final: public Arena {
|
||||
// A BuilderArena that does not allow the injection of capabilities.
|
||||
|
||||
public:
|
||||
explicit BuilderArena(MessageBuilder* message);
|
||||
~BuilderArena() noexcept(false);
|
||||
KJ_DISALLOW_COPY_AND_MOVE(BuilderArena);
|
||||
|
||||
inline SegmentBuilder* getRootSegment() { return &segment0; }
|
||||
|
||||
kj::ArrayPtr<const kj::ArrayPtr<const word>> getSegmentsForOutput();
|
||||
// Get an array of all the segments, suitable for writing out. This only returns the allocated
|
||||
// portion of each segment, whereas tryGetSegment() returns something that includes
|
||||
// not-yet-allocated space.
|
||||
|
||||
SegmentBuilder* getSegment(SegmentId id);
|
||||
// Get the segment with the given id. Crashes or throws an exception if no such segment exists.
|
||||
|
||||
struct AllocateResult {
|
||||
SegmentBuilder* segment;
|
||||
word* words;
|
||||
};
|
||||
|
||||
AllocateResult allocate(SegmentWordCount amount);
|
||||
// Find a segment with at least the given amount of space available and allocate the space.
|
||||
// Note that allocating directly from a particular segment is much faster, but allocating from
|
||||
// the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific
|
||||
// segment first if there is one, then fall back to the arena.
|
||||
|
||||
// implements Arena ------------------------------------------------
|
||||
SegmentReader* tryGetSegment(SegmentId id) override;
|
||||
void reportReadLimitReached() override;
|
||||
|
||||
private:
|
||||
MessageBuilder* message;
|
||||
ReadLimiter dummyLimiter;
|
||||
|
||||
SegmentBuilder segment0;
|
||||
kj::ArrayPtr<const word> segment0ForOutput;
|
||||
|
||||
struct MultiSegmentState {
|
||||
kj::Vector<kj::Own<SegmentBuilder>> builders;
|
||||
kj::Vector<kj::ArrayPtr<const word>> forOutput;
|
||||
};
|
||||
kj::Maybe<kj::Own<MultiSegmentState>> moreSegments;
|
||||
|
||||
SegmentBuilder* segmentWithSpace = nullptr;
|
||||
// When allocating, look for space in this segment first before resorting to allocating a new
|
||||
// segment.
|
||||
|
||||
SegmentBuilder* addSegmentInternal(kj::ArrayPtr<word> content);
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
inline ReadLimiter::ReadLimiter()
|
||||
: limit(kj::maxValue) {}
|
||||
|
||||
inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(unbound(limit / WORDS)) {}
|
||||
|
||||
inline bool ReadLimiter::canRead(WordCount64 amount, Arena* arena) {
|
||||
// Be careful not to store an underflowed value into `limit`, even if multiple threads are
|
||||
// decrementing it.
|
||||
uint64_t current = readLimit();
|
||||
if (KJ_UNLIKELY(unbound(amount / WORDS) > current)) {
|
||||
arena->reportReadLimitReached();
|
||||
return false;
|
||||
} else {
|
||||
setLimit(current - unbound(amount / WORDS));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
inline SegmentReader::SegmentReader(Arena* arena, SegmentId id, const word* ptr,
|
||||
SegmentWordCount size, ReadLimiter* readLimiter)
|
||||
: arena(arena), id(id), ptr(kj::arrayPtr(ptr, unbound(size / WORDS))),
|
||||
readLimiter(readLimiter) {}
|
||||
|
||||
inline const word* SegmentReader::checkOffset(const word* from, ptrdiff_t offset) {
|
||||
ptrdiff_t min = ptr.begin() - from;
|
||||
ptrdiff_t max = ptr.end() - from;
|
||||
if (offset >= min && offset <= max) {
|
||||
return from + offset;
|
||||
} else {
|
||||
return ptr.end();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool SegmentReader::checkObject(const word* start, WordCountN<31> size) {
|
||||
auto startOffset = intervalLength(ptr.begin(), start, MAX_SEGMENT_WORDS);
|
||||
#ifdef KJ_DEBUG
|
||||
if (startOffset > bounded(ptr.size()) * WORDS) {
|
||||
abortCheckObjectFault();
|
||||
}
|
||||
#endif
|
||||
return startOffset + size <= bounded(ptr.size()) * WORDS &&
|
||||
readLimiter->canRead(size, arena);
|
||||
}
|
||||
|
||||
inline bool SegmentReader::amplifiedRead(WordCount virtualAmount) {
|
||||
return readLimiter->canRead(virtualAmount, arena);
|
||||
}
|
||||
|
||||
inline Arena* SegmentReader::getArena() { return arena; }
|
||||
inline SegmentId SegmentReader::getSegmentId() { return id; }
|
||||
inline const word* SegmentReader::getStartPtr() { return ptr.begin(); }
|
||||
inline SegmentWordCount SegmentReader::getOffsetTo(const word* ptr) {
|
||||
KJ_IREQUIRE(this->ptr.begin() <= ptr && ptr <= this->ptr.end());
|
||||
return intervalLength(this->ptr.begin(), ptr, MAX_SEGMENT_WORDS);
|
||||
}
|
||||
inline SegmentWordCount SegmentReader::getSize() {
|
||||
return assumeBits<SEGMENT_WORD_COUNT_BITS>(ptr.size()) * WORDS;
|
||||
}
|
||||
inline kj::ArrayPtr<const word> SegmentReader::getArray() { return ptr; }
|
||||
inline void SegmentReader::unread(WordCount64 amount) { readLimiter->unread(amount); }
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
inline SegmentBuilder::SegmentBuilder(
|
||||
BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
|
||||
ReadLimiter* readLimiter)
|
||||
: SegmentReader(arena, id, ptr, size, readLimiter),
|
||||
pos(ptr) {}
|
||||
inline SegmentBuilder::SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
|
||||
ReadLimiter* readLimiter)
|
||||
: SegmentReader(arena, id, nullptr, ZERO * WORDS, readLimiter),
|
||||
pos(nullptr) {}
|
||||
|
||||
inline word* SegmentBuilder::allocate(SegmentWordCount amount) {
|
||||
if (intervalLength(pos, ptr.end(), MAX_SEGMENT_WORDS) < amount) {
|
||||
// Not enough space in the segment for this allocation.
|
||||
return nullptr;
|
||||
} else {
|
||||
// Success.
|
||||
word* result = pos;
|
||||
pos = pos + amount;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
inline word* SegmentBuilder::getPtrUnchecked(SegmentWordCount offset) {
|
||||
return const_cast<word*>(ptr.begin() + offset);
|
||||
}
|
||||
|
||||
inline BuilderArena* SegmentBuilder::getArena() {
|
||||
// Down-cast safe because SegmentBuilder's constructor always initializes its SegmentReader base
|
||||
// class with an Arena pointer that actually points to a BuilderArena.
|
||||
return static_cast<BuilderArena*>(arena);
|
||||
}
|
||||
|
||||
inline kj::ArrayPtr<const word> SegmentBuilder::currentlyAllocated() {
|
||||
return kj::arrayPtr(ptr.begin(), pos - ptr.begin());
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
28
vendor/capnproto/src/capnp/blob.c++
vendored
Normal file
28
vendor/capnproto/src/capnp/blob.c++
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 "blob.h"
|
||||
|
||||
namespace capnp {
|
||||
|
||||
char Text::Builder::nulstr[1] = "";
|
||||
|
||||
} // namespace capnp
|
||||
221
vendor/capnproto/src/capnp/blob.h
vendored
Normal file
221
vendor/capnproto/src/capnp/blob.h
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kj/common.h>
|
||||
#include <kj/string.h>
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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 = kj::EnableIf<kj::canConvert<decltype(kj::instance<T>().c_str()), const char*>()>>
|
||||
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(kj::implicitCast<const kj::ArrayPtr<byte>&>(*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 b.asString() == a; }
|
||||
inline bool operator!=(const char* a, const Text::Builder& b) { return b.asString() != a; }
|
||||
|
||||
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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
684
vendor/capnproto/src/capnp/common.h
vendored
Normal file
684
vendor/capnproto/src/capnp/common.h
vendored
Normal file
@@ -0,0 +1,684 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/memory.h>
|
||||
|
||||
#if CAPNP_DEBUG_TYPES
|
||||
#include <kj/units.h>
|
||||
#endif
|
||||
|
||||
#if !defined(CAPNP_HEADER_WARNINGS) || !CAPNP_HEADER_WARNINGS
|
||||
#define CAPNP_BEGIN_HEADER KJ_BEGIN_SYSTEM_HEADER
|
||||
#define CAPNP_END_HEADER KJ_END_SYSTEM_HEADER
|
||||
#else
|
||||
#define CAPNP_BEGIN_HEADER
|
||||
#define CAPNP_END_HEADER
|
||||
#endif
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
|
||||
#define CAPNP_VERSION_MAJOR 1
|
||||
#define CAPNP_VERSION_MINOR 4
|
||||
#define CAPNP_VERSION_MICRO 0
|
||||
|
||||
#define CAPNP_VERSION \
|
||||
(CAPNP_VERSION_MAJOR * 1000000 + CAPNP_VERSION_MINOR * 1000 + CAPNP_VERSION_MICRO)
|
||||
|
||||
|
||||
#if CAPNP_TESTING_CAPNP // defined in Cap'n Proto's own unit tests; others should not define this
|
||||
#define CAPNP_DEPRECATED(reason)
|
||||
#else
|
||||
#define CAPNP_DEPRECATED KJ_DEPRECATED
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
#define CAPNP_KIND(T) ::capnp::kind<T>()
|
||||
// Use this macro rather than kind<T>() in any code which must work in MSVC.
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)>
|
||||
struct List;
|
||||
|
||||
|
||||
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> 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> using BuilderFor = typename BuilderFor_<T>::Type;
|
||||
// The type returned by List<T>::Builder::operator[].
|
||||
|
||||
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, 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::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
|
||||
// uint32_t -> uint32_t
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)>
|
||||
struct PointerHelpers;
|
||||
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
struct MessageSize {
|
||||
// Size of a message. Every struct and list type has a method `.totalSize()` that returns this.
|
||||
uint64_t wordCount;
|
||||
uint capCount;
|
||||
|
||||
inline constexpr MessageSize operator+(const MessageSize& other) const {
|
||||
return { wordCount + other.wordCount, capCount + other.capCount };
|
||||
}
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Raw memory types and measures
|
||||
|
||||
using kj::byte;
|
||||
|
||||
class word {
|
||||
// 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.
|
||||
public:
|
||||
word() = default;
|
||||
private:
|
||||
uint64_t content KJ_UNUSED_MEMBER;
|
||||
#if __GNUC__ < 8 || __clang__
|
||||
// GCC 8's -Wclass-memaccess complains whenever we try to memcpy() a `word` if we've disallowed
|
||||
// the copy constructor. We don't want to disable the warning because it's a useful warning and
|
||||
// we'd have to disable it for all applications that include this header. Instead we allow `word`
|
||||
// to be copyable on GCC.
|
||||
KJ_DISALLOW_COPY_AND_MOVE(word);
|
||||
#endif
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
47
vendor/capnproto/src/capnp/compat/std-iterator.h
vendored
Normal file
47
vendor/capnproto/src/capnp/compat/std-iterator.h
vendored
Normal file
@@ -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.
|
||||
|
||||
#pragma once
|
||||
|
||||
// This exposes IndexingIterator as something compatible with std::iterator so that things like
|
||||
// std::copy work with List::begin/List::end.
|
||||
|
||||
// Make sure that if this header is before list.h by the user it includes it to make
|
||||
// IndexingIterator visible to avoid brittle header problems.
|
||||
#include "../list.h"
|
||||
#include <iterator>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename Container, typename Element>
|
||||
struct iterator_traits<capnp::_::IndexingIterator<Container, Element>> {
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = Element;
|
||||
using difference_type = int;
|
||||
using pointer = Element*;
|
||||
using reference = Element;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
CAPNP_END_HEADER
|
||||
1126
vendor/capnproto/src/capnp/compiler/compiler.c++
vendored
Normal file
1126
vendor/capnproto/src/capnp/compiler/compiler.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
188
vendor/capnproto/src/capnp/compiler/compiler.h
vendored
Normal file
188
vendor/capnproto/src/capnp/compiler/compiler.h
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/compiler/grammar.capnp.h>
|
||||
#include <capnp/schema.capnp.h>
|
||||
#include <capnp/schema-loader.h>
|
||||
#include "error-reporter.h"
|
||||
#include "generics.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
class Module: public ErrorReporter {
|
||||
public:
|
||||
virtual kj::StringPtr getSourceName() = 0;
|
||||
// The name of the module file relative to the source tree. Used to decide where to output
|
||||
// generated code and to form the `displayName` in the schema.
|
||||
|
||||
virtual Orphan<ParsedFile> loadContent(Orphanage orphanage) = 0;
|
||||
// Loads the module content, using the given orphanage to allocate objects if necessary.
|
||||
|
||||
virtual kj::Maybe<Module&> importRelative(kj::StringPtr importPath) = 0;
|
||||
// Find another module, relative to this one. Importing the same logical module twice should
|
||||
// produce the exact same object, comparable by identity. These objects are owned by some
|
||||
// outside pool that outlives the Compiler instance.
|
||||
|
||||
virtual kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr embedPath) = 0;
|
||||
// Read and return the content of a file specified using `embed`.
|
||||
|
||||
};
|
||||
|
||||
class Compiler final: private SchemaLoader::LazyLoadCallback {
|
||||
// Cross-links separate modules (schema files) and translates them into schema nodes.
|
||||
//
|
||||
// This class is thread-safe, hence all its methods are const.
|
||||
|
||||
class Node;
|
||||
|
||||
public:
|
||||
Compiler();
|
||||
~Compiler() noexcept(false);
|
||||
KJ_DISALLOW_COPY_AND_MOVE(Compiler);
|
||||
|
||||
class ModuleScope {
|
||||
public:
|
||||
uint64_t getId() { return id; }
|
||||
|
||||
private:
|
||||
uint64_t id;
|
||||
explicit ModuleScope(uint64_t id): id(id) {}
|
||||
friend class Compiler;
|
||||
};
|
||||
|
||||
ModuleScope add(Module& module) const;
|
||||
// Add a module to the Compiler, returning the ID of the top-level scope of
|
||||
// the module. The module is parsed at the time `add()` is called, but not fully compiled --
|
||||
// individual schema nodes are compiled lazily. If you want to force eager compilation,
|
||||
// see `eagerlyCompile()`, below.
|
||||
|
||||
kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName) const;
|
||||
// Given the type ID of a schema node, find the ID of a node nested within it. Throws an
|
||||
// exception if the parent ID is not recognized; returns null if the parent has no child of the
|
||||
// given name. Neither the parent nor the child schema node is actually compiled.
|
||||
//
|
||||
// This interface does not handle generic specializations.
|
||||
|
||||
enum Eagerness: uint32_t {
|
||||
// Flags specifying how eager to be about compilation. These are intended to be bitwise OR'd.
|
||||
// Used with the method `eagerlyCompile()`.
|
||||
//
|
||||
// Schema declarations can be compiled upfront, or they can be compiled lazily as they are
|
||||
// needed. Usually, the difference is not observable, but it is not a perfect abstraction.
|
||||
// The difference has the following effects:
|
||||
// * `getLoader().getAllLoaded()` only returns the schema nodes which have been compiled so
|
||||
// far.
|
||||
// * `getLoader().get()` (i.e. searching for a schema by ID) can only find schema nodes that
|
||||
// have either been compiled already, or which are referenced by schema nodes which have been
|
||||
// compiled already. This means that if the ID you pass in came from another schema node
|
||||
// compiled with the same compiler, there should be no observable difference, but if you
|
||||
// have an ID from elsewhere which you _a priori_ expect is defined in a particular schema
|
||||
// file, you will need to compile that file eagerly before you look up the node by ID.
|
||||
// * Errors are reported when they are encountered, so some errors will not be reported until
|
||||
// the node is actually compiled.
|
||||
// * If an imported file is not needed, it will never even be read from disk.
|
||||
//
|
||||
// The last point is the main reason why you might want to prefer lazy compilation: it allows
|
||||
// you to use a schema file with missing imports, so long as those missing imports are not
|
||||
// actually needed.
|
||||
//
|
||||
// For example, the flag combo:
|
||||
// EAGER_NODE | EAGER_CHILDREN | EAGER_DEPENDENCIES | EAGER_DEPENDENCY_PARENTS
|
||||
// will compile the entire given module, plus all direct dependencies of anything in that
|
||||
// module, plus all lexical ancestors of those dependencies. This is what the Cap'n Proto
|
||||
// compiler uses when building initial code generator requests.
|
||||
|
||||
ALL_RELATED_NODES = ~0u,
|
||||
// Compile everything that is in any way related to the target node, including its entire
|
||||
// containing file and everything transitively imported by it.
|
||||
|
||||
NODE = 1 << 0,
|
||||
// Eagerly compile the requested node, but not necessarily any of its parents, children, or
|
||||
// dependencies.
|
||||
|
||||
PARENTS = 1 << 1,
|
||||
// Eagerly compile all lexical parents of the requested node. Only meaningful in conjunction
|
||||
// with NODE.
|
||||
|
||||
CHILDREN = 1 << 2,
|
||||
// Eagerly compile all of the node's lexically nested nodes. Only meaningful in conjunction
|
||||
// with NODE.
|
||||
|
||||
DEPENDENCIES = NODE << 15,
|
||||
// For all nodes compiled as a result of the above flags, also compile their direct
|
||||
// dependencies. E.g. if Foo is a struct which contains a field of type Bar, and Foo is
|
||||
// compiled, then also compile Bar. "Dependencies" are defined as field types, method
|
||||
// parameter and return types, and annotation types. Nested types and outer types are not
|
||||
// considered dependencies.
|
||||
|
||||
DEPENDENCY_PARENTS = PARENTS * DEPENDENCIES,
|
||||
DEPENDENCY_CHILDREN = CHILDREN * DEPENDENCIES,
|
||||
DEPENDENCY_DEPENDENCIES = DEPENDENCIES * DEPENDENCIES,
|
||||
// Like PARENTS, CHILDREN, and DEPENDENCIES, but applies relative to dependency nodes rather
|
||||
// than the original requested node. Note that DEPENDENCY_DEPENDENCIES causes all transitive
|
||||
// dependencies of the requested node to be compiled.
|
||||
//
|
||||
// These flags are defined as multiples of the original flag and DEPENDENCIES so that we
|
||||
// can form the flags to use when traversing a dependency by shifting bits.
|
||||
};
|
||||
|
||||
void eagerlyCompile(uint64_t id, uint eagerness) const;
|
||||
// Force eager compilation of schema nodes related to the given ID. `eagerness` specifies which
|
||||
// related nodes should be compiled before returning. It is a bitwise OR of the possible values
|
||||
// of the `Eagerness` enum.
|
||||
//
|
||||
// If this returns and no errors have been reported, then it is guaranteed that the compiled
|
||||
// nodes can be found in the SchemaLoader returned by `getLoader()`.
|
||||
|
||||
const SchemaLoader& getLoader() const { return loader; }
|
||||
SchemaLoader& getLoader() { return loader; }
|
||||
// Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you
|
||||
// traverse them using this loader.
|
||||
|
||||
void clearWorkspace() const;
|
||||
// The compiler builds a lot of temporary tables and data structures while it works. It's
|
||||
// useful to keep these around if more work is expected (especially if you are using lazy
|
||||
// compilation and plan to look up Schema nodes that haven't already been seen), but once
|
||||
// the SchemaLoader has everything you need, you can call clearWorkspace() to free up the
|
||||
// temporary space. Note that it's safe to call clearWorkspace() even if you do expect to
|
||||
// compile more nodes in the future; it may simply lead to redundant work if the discarded
|
||||
// structures are needed again.
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
kj::MutexGuarded<kj::Own<Impl>> impl;
|
||||
SchemaLoader loader;
|
||||
|
||||
class CompiledModule;
|
||||
class Alias;
|
||||
|
||||
void load(const SchemaLoader& loader, uint64_t id) const override;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
61
vendor/capnproto/src/capnp/compiler/error-reporter.h
vendored
Normal file
61
vendor/capnproto/src/capnp/compiler/error-reporter.h
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/common.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/exception.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
class ErrorReporter {
|
||||
// Callback for reporting errors within a particular file.
|
||||
|
||||
public:
|
||||
virtual void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) = 0;
|
||||
// Report an error at the given location in the input text. `startByte` and `endByte` indicate
|
||||
// the span of text that is erroneous. They may be equal, in which case the parser was only
|
||||
// able to identify where the error begins, not where it ends.
|
||||
|
||||
template <typename T>
|
||||
inline void addErrorOn(T&& decl, kj::StringPtr message) {
|
||||
// Works for any `T` that defines `getStartByte()` and `getEndByte()` methods, which many
|
||||
// of the Cap'n Proto types defined in `grammar.capnp` do.
|
||||
|
||||
addError(decl.getStartByte(), decl.getEndByte(), message);
|
||||
}
|
||||
|
||||
virtual bool hadErrors() = 0;
|
||||
// Return true if any errors have been reported, globally. The main use case for this callback
|
||||
// is to inhibit the reporting of errors which may have been caused by previous errors, or to
|
||||
// allow the compiler to bail out entirely if it gets confused and thinks this could be because
|
||||
// of previous errors.
|
||||
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
627
vendor/capnproto/src/capnp/compiler/generics.c++
vendored
Normal file
627
vendor/capnproto/src/capnp/compiler/generics.c++
vendored
Normal file
@@ -0,0 +1,627 @@
|
||||
// Copyright (c) 2013-2020 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 "generics.h"
|
||||
#include "parser.h" // for expressionString()
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
BrandedDecl::BrandedDecl(BrandedDecl& other)
|
||||
: body(other.body),
|
||||
source(other.source) {
|
||||
if (body.is<Resolver::ResolvedDecl>()) {
|
||||
brand = kj::addRef(*other.brand);
|
||||
}
|
||||
}
|
||||
|
||||
BrandedDecl& BrandedDecl::operator=(BrandedDecl& other) {
|
||||
body = other.body;
|
||||
source = other.source;
|
||||
if (body.is<Resolver::ResolvedDecl>()) {
|
||||
brand = kj::addRef(*other.brand);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
kj::Maybe<BrandedDecl> BrandedDecl::applyParams(
|
||||
kj::Array<BrandedDecl> params, Expression::Reader subSource) {
|
||||
if (body.is<Resolver::ResolvedParameter>()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return brand->setParams(kj::mv(params), body.get<Resolver::ResolvedDecl>().kind, subSource)
|
||||
.map([&](kj::Own<BrandScope>&& scope) {
|
||||
BrandedDecl result = *this;
|
||||
result.brand = kj::mv(scope);
|
||||
result.source = subSource;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<BrandedDecl> BrandedDecl::getMember(
|
||||
kj::StringPtr memberName, Expression::Reader subSource) {
|
||||
if (body.is<Resolver::ResolvedParameter>()) {
|
||||
return nullptr;
|
||||
} else KJ_IF_MAYBE(r, body.get<Resolver::ResolvedDecl>().resolver->resolveMember(memberName)) {
|
||||
return brand->interpretResolve(*body.get<Resolver::ResolvedDecl>().resolver, *r, subSource);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<Declaration::Which> BrandedDecl::getKind() {
|
||||
if (body.is<Resolver::ResolvedParameter>()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return body.get<Resolver::ResolvedDecl>().kind;
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<BrandedDecl&> BrandedDecl::getListParam() {
|
||||
KJ_REQUIRE(body.is<Resolver::ResolvedDecl>());
|
||||
|
||||
auto& decl = body.get<Resolver::ResolvedDecl>();
|
||||
KJ_REQUIRE(decl.kind == Declaration::BUILTIN_LIST);
|
||||
|
||||
auto params = KJ_ASSERT_NONNULL(brand->getParams(decl.id));
|
||||
if (params.size() != 1) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return params[0];
|
||||
}
|
||||
}
|
||||
|
||||
Resolver::ResolvedParameter BrandedDecl::asVariable() {
|
||||
KJ_REQUIRE(body.is<Resolver::ResolvedParameter>());
|
||||
|
||||
return body.get<Resolver::ResolvedParameter>();
|
||||
}
|
||||
|
||||
bool BrandedDecl::compileAsType(
|
||||
ErrorReporter& errorReporter, schema::Type::Builder target) {
|
||||
KJ_IF_MAYBE(kind, getKind()) {
|
||||
switch (*kind) {
|
||||
case Declaration::ENUM: {
|
||||
auto enum_ = target.initEnum();
|
||||
enum_.setTypeId(getIdAndFillBrand([&]() { return enum_.initBrand(); }));
|
||||
return true;
|
||||
}
|
||||
|
||||
case Declaration::STRUCT: {
|
||||
auto struct_ = target.initStruct();
|
||||
struct_.setTypeId(getIdAndFillBrand([&]() { return struct_.initBrand(); }));
|
||||
return true;
|
||||
}
|
||||
|
||||
case Declaration::INTERFACE: {
|
||||
auto interface = target.initInterface();
|
||||
interface.setTypeId(getIdAndFillBrand([&]() { return interface.initBrand(); }));
|
||||
return true;
|
||||
}
|
||||
|
||||
case Declaration::BUILTIN_LIST: {
|
||||
auto elementType = target.initList().initElementType();
|
||||
|
||||
KJ_IF_MAYBE(param, getListParam()) {
|
||||
if (!param->compileAsType(errorReporter, elementType)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
addError(errorReporter, "'List' requires exactly one parameter.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elementType.isAnyPointer()) {
|
||||
auto unconstrained = elementType.getAnyPointer().getUnconstrained();
|
||||
|
||||
if (unconstrained.isAnyKind()) {
|
||||
addError(errorReporter, "'List(AnyPointer)' is not supported.");
|
||||
// Seeing List(AnyPointer) later can mess things up, so change the type to Void.
|
||||
elementType.setVoid();
|
||||
return false;
|
||||
} else if (unconstrained.isStruct()) {
|
||||
addError(errorReporter, "'List(AnyStruct)' is not supported.");
|
||||
// Seeing List(AnyStruct) later can mess things up, so change the type to Void.
|
||||
elementType.setVoid();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case Declaration::BUILTIN_VOID: target.setVoid(); return true;
|
||||
case Declaration::BUILTIN_BOOL: target.setBool(); return true;
|
||||
case Declaration::BUILTIN_INT8: target.setInt8(); return true;
|
||||
case Declaration::BUILTIN_INT16: target.setInt16(); return true;
|
||||
case Declaration::BUILTIN_INT32: target.setInt32(); return true;
|
||||
case Declaration::BUILTIN_INT64: target.setInt64(); return true;
|
||||
case Declaration::BUILTIN_U_INT8: target.setUint8(); return true;
|
||||
case Declaration::BUILTIN_U_INT16: target.setUint16(); return true;
|
||||
case Declaration::BUILTIN_U_INT32: target.setUint32(); return true;
|
||||
case Declaration::BUILTIN_U_INT64: target.setUint64(); return true;
|
||||
case Declaration::BUILTIN_FLOAT32: target.setFloat32(); return true;
|
||||
case Declaration::BUILTIN_FLOAT64: target.setFloat64(); return true;
|
||||
case Declaration::BUILTIN_TEXT: target.setText(); return true;
|
||||
case Declaration::BUILTIN_DATA: target.setData(); return true;
|
||||
|
||||
case Declaration::BUILTIN_OBJECT:
|
||||
addError(errorReporter,
|
||||
"As of Cap'n Proto 0.4, 'Object' has been renamed to 'AnyPointer'. Sorry for the "
|
||||
"inconvenience, and thanks for being an early adopter. :)");
|
||||
KJ_FALLTHROUGH;
|
||||
case Declaration::BUILTIN_ANY_POINTER:
|
||||
target.initAnyPointer().initUnconstrained().setAnyKind();
|
||||
return true;
|
||||
case Declaration::BUILTIN_ANY_STRUCT:
|
||||
target.initAnyPointer().initUnconstrained().setStruct();
|
||||
return true;
|
||||
case Declaration::BUILTIN_ANY_LIST:
|
||||
target.initAnyPointer().initUnconstrained().setList();
|
||||
return true;
|
||||
case Declaration::BUILTIN_CAPABILITY:
|
||||
target.initAnyPointer().initUnconstrained().setCapability();
|
||||
return true;
|
||||
|
||||
case Declaration::FILE:
|
||||
case Declaration::USING:
|
||||
case Declaration::CONST:
|
||||
case Declaration::ENUMERANT:
|
||||
case Declaration::FIELD:
|
||||
case Declaration::UNION:
|
||||
case Declaration::GROUP:
|
||||
case Declaration::METHOD:
|
||||
case Declaration::ANNOTATION:
|
||||
case Declaration::NAKED_ID:
|
||||
case Declaration::NAKED_ANNOTATION:
|
||||
addError(errorReporter, kj::str("'", toString(), "' is not a type."));
|
||||
return false;
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
} else {
|
||||
// Oh, this is a type variable.
|
||||
auto var = asVariable();
|
||||
auto builder = target.initAnyPointer().initParameter();
|
||||
builder.setScopeId(var.id);
|
||||
builder.setParameterIndex(var.index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Resolver::ResolveResult BrandedDecl::asResolveResult(
|
||||
uint64_t scopeId, schema::Brand::Builder brandBuilder) {
|
||||
auto result = body;
|
||||
if (result.is<Resolver::ResolvedDecl>()) {
|
||||
// May need to compile our context as the "brand".
|
||||
|
||||
result.get<Resolver::ResolvedDecl>().scopeId = scopeId;
|
||||
|
||||
getIdAndFillBrand([&]() {
|
||||
result.get<Resolver::ResolvedDecl>().brand = brandBuilder.asReader();
|
||||
return brandBuilder;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
kj::String BrandedDecl::toString() {
|
||||
return expressionString(source);
|
||||
}
|
||||
|
||||
BrandScope::BrandScope(ErrorReporter& errorReporter, uint64_t startingScopeId,
|
||||
uint startingScopeParamCount, Resolver& startingScope)
|
||||
: errorReporter(errorReporter), parent(nullptr), leafId(startingScopeId),
|
||||
leafParamCount(startingScopeParamCount), inherited(true) {
|
||||
// Create all lexical parent scopes, all with no brand bindings.
|
||||
KJ_IF_MAYBE(p, startingScope.getParent()) {
|
||||
parent = kj::refcounted<BrandScope>(
|
||||
errorReporter, p->id, p->genericParamCount, *p->resolver);
|
||||
}
|
||||
}
|
||||
|
||||
bool BrandScope::isGeneric() {
|
||||
if (leafParamCount > 0) return true;
|
||||
|
||||
KJ_IF_MAYBE(p, parent) {
|
||||
return p->get()->isGeneric();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
kj::Own<BrandScope> BrandScope::push(uint64_t typeId, uint paramCount) {
|
||||
return kj::refcounted<BrandScope>(kj::addRef(*this), typeId, paramCount);
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Own<BrandScope>> BrandScope::setParams(
|
||||
kj::Array<BrandedDecl> params, Declaration::Which genericType, Expression::Reader source) {
|
||||
if (this->params.size() != 0) {
|
||||
errorReporter.addErrorOn(source, "Double-application of generic parameters.");
|
||||
return nullptr;
|
||||
} else if (params.size() > leafParamCount) {
|
||||
if (leafParamCount == 0) {
|
||||
errorReporter.addErrorOn(source, "Declaration does not accept generic parameters.");
|
||||
} else {
|
||||
errorReporter.addErrorOn(source, "Too many generic parameters.");
|
||||
}
|
||||
return nullptr;
|
||||
} else if (params.size() < leafParamCount) {
|
||||
errorReporter.addErrorOn(source, "Not enough generic parameters.");
|
||||
return nullptr;
|
||||
} else {
|
||||
if (genericType != Declaration::BUILTIN_LIST) {
|
||||
for (auto& param: params) {
|
||||
KJ_IF_MAYBE(kind, param.getKind()) {
|
||||
switch (*kind) {
|
||||
case Declaration::BUILTIN_LIST:
|
||||
case Declaration::BUILTIN_TEXT:
|
||||
case Declaration::BUILTIN_DATA:
|
||||
case Declaration::BUILTIN_ANY_POINTER:
|
||||
case Declaration::STRUCT:
|
||||
case Declaration::INTERFACE:
|
||||
break;
|
||||
|
||||
default:
|
||||
param.addError(errorReporter,
|
||||
"Sorry, only pointer types can be used as generic parameters.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kj::refcounted<BrandScope>(*this, kj::mv(params));
|
||||
}
|
||||
}
|
||||
|
||||
kj::Own<BrandScope> BrandScope::pop(uint64_t newLeafId) {
|
||||
if (leafId == newLeafId) {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
KJ_IF_MAYBE(p, parent) {
|
||||
return (*p)->pop(newLeafId);
|
||||
} else {
|
||||
// Looks like we're moving into a whole top-level scope.
|
||||
return kj::refcounted<BrandScope>(errorReporter, newLeafId);
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<BrandedDecl> BrandScope::lookupParameter(
|
||||
Resolver& resolver, uint64_t scopeId, uint index) {
|
||||
// Returns null if the param should be inherited from the client scope.
|
||||
|
||||
if (scopeId == leafId) {
|
||||
if (index < params.size()) {
|
||||
return params[index];
|
||||
} else if (inherited) {
|
||||
return nullptr;
|
||||
} else {
|
||||
// Unbound and not inherited, so return AnyPointer.
|
||||
auto decl = resolver.resolveBuiltin(Declaration::BUILTIN_ANY_POINTER);
|
||||
return BrandedDecl(decl,
|
||||
evaluateBrand(resolver, decl, List<schema::Brand::Scope>::Reader()),
|
||||
Expression::Reader());
|
||||
}
|
||||
} else KJ_IF_MAYBE(p, parent) {
|
||||
return p->get()->lookupParameter(resolver, scopeId, index);
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("scope is not a parent");
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<kj::ArrayPtr<BrandedDecl>> BrandScope::getParams(uint64_t scopeId) {
|
||||
// Returns null if params at the requested scope should be inherited from the client scope.
|
||||
|
||||
if (scopeId == leafId) {
|
||||
if (inherited) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return params.asPtr();
|
||||
}
|
||||
} else KJ_IF_MAYBE(p, parent) {
|
||||
return p->get()->getParams(scopeId);
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("scope is not a parent");
|
||||
}
|
||||
}
|
||||
|
||||
BrandedDecl BrandScope::interpretResolve(
|
||||
Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source) {
|
||||
if (result.is<Resolver::ResolvedDecl>()) {
|
||||
auto& decl = result.get<Resolver::ResolvedDecl>();
|
||||
|
||||
auto scope = pop(decl.scopeId);
|
||||
KJ_IF_MAYBE(brand, decl.brand) {
|
||||
scope = scope->evaluateBrand(resolver, decl, brand->getScopes());
|
||||
} else {
|
||||
scope = scope->push(decl.id, decl.genericParamCount);
|
||||
}
|
||||
|
||||
return BrandedDecl(decl, kj::mv(scope), source);
|
||||
} else {
|
||||
auto& param = result.get<Resolver::ResolvedParameter>();
|
||||
KJ_IF_MAYBE(p, lookupParameter(resolver, param.id, param.index)) {
|
||||
return *p;
|
||||
} else {
|
||||
return BrandedDecl(param, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kj::Own<BrandScope> BrandScope::evaluateBrand(
|
||||
Resolver& resolver, Resolver::ResolvedDecl decl,
|
||||
List<schema::Brand::Scope>::Reader brand, uint index) {
|
||||
auto result = kj::refcounted<BrandScope>(errorReporter, decl.id);
|
||||
result->leafParamCount = decl.genericParamCount;
|
||||
|
||||
// Fill in `params`.
|
||||
if (index < brand.size()) {
|
||||
auto nextScope = brand[index];
|
||||
if (decl.id == nextScope.getScopeId()) {
|
||||
// Initialize our parameters.
|
||||
|
||||
switch (nextScope.which()) {
|
||||
case schema::Brand::Scope::BIND: {
|
||||
auto bindings = nextScope.getBind();
|
||||
auto params = kj::heapArrayBuilder<BrandedDecl>(bindings.size());
|
||||
for (auto binding: bindings) {
|
||||
switch (binding.which()) {
|
||||
case schema::Brand::Binding::UNBOUND: {
|
||||
// Build an AnyPointer-equivalent.
|
||||
auto anyPointerDecl = resolver.resolveBuiltin(Declaration::BUILTIN_ANY_POINTER);
|
||||
params.add(BrandedDecl(anyPointerDecl,
|
||||
kj::refcounted<BrandScope>(errorReporter, anyPointerDecl.scopeId),
|
||||
Expression::Reader()));
|
||||
break;
|
||||
}
|
||||
|
||||
case schema::Brand::Binding::TYPE:
|
||||
// Reverse this schema::Type back into a BrandedDecl.
|
||||
params.add(decompileType(resolver, binding.getType()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
result->params = params.finish();
|
||||
break;
|
||||
}
|
||||
|
||||
case schema::Brand::Scope::INHERIT:
|
||||
KJ_IF_MAYBE(p, getParams(decl.id)) {
|
||||
result->params = kj::heapArray(*p);
|
||||
} else {
|
||||
result->inherited = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Parent should start one level deeper in the list.
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in `parent`.
|
||||
KJ_IF_MAYBE(parent, decl.resolver->getParent()) {
|
||||
result->parent = evaluateBrand(resolver, *parent, brand, index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BrandedDecl BrandScope::decompileType(
|
||||
Resolver& resolver, schema::Type::Reader type) {
|
||||
auto builtin = [&](Declaration::Which which) -> BrandedDecl {
|
||||
auto decl = resolver.resolveBuiltin(which);
|
||||
return BrandedDecl(decl,
|
||||
evaluateBrand(resolver, decl, List<schema::Brand::Scope>::Reader()),
|
||||
Expression::Reader());
|
||||
};
|
||||
|
||||
switch (type.which()) {
|
||||
case schema::Type::VOID: return builtin(Declaration::BUILTIN_VOID);
|
||||
case schema::Type::BOOL: return builtin(Declaration::BUILTIN_BOOL);
|
||||
case schema::Type::INT8: return builtin(Declaration::BUILTIN_INT8);
|
||||
case schema::Type::INT16: return builtin(Declaration::BUILTIN_INT16);
|
||||
case schema::Type::INT32: return builtin(Declaration::BUILTIN_INT32);
|
||||
case schema::Type::INT64: return builtin(Declaration::BUILTIN_INT64);
|
||||
case schema::Type::UINT8: return builtin(Declaration::BUILTIN_U_INT8);
|
||||
case schema::Type::UINT16: return builtin(Declaration::BUILTIN_U_INT16);
|
||||
case schema::Type::UINT32: return builtin(Declaration::BUILTIN_U_INT32);
|
||||
case schema::Type::UINT64: return builtin(Declaration::BUILTIN_U_INT64);
|
||||
case schema::Type::FLOAT32: return builtin(Declaration::BUILTIN_FLOAT32);
|
||||
case schema::Type::FLOAT64: return builtin(Declaration::BUILTIN_FLOAT64);
|
||||
case schema::Type::TEXT: return builtin(Declaration::BUILTIN_TEXT);
|
||||
case schema::Type::DATA: return builtin(Declaration::BUILTIN_DATA);
|
||||
|
||||
case schema::Type::ENUM: {
|
||||
auto enumType = type.getEnum();
|
||||
Resolver::ResolvedDecl decl = resolver.resolveId(enumType.getTypeId());
|
||||
return BrandedDecl(decl,
|
||||
evaluateBrand(resolver, decl, enumType.getBrand().getScopes()),
|
||||
Expression::Reader());
|
||||
}
|
||||
|
||||
case schema::Type::INTERFACE: {
|
||||
auto interfaceType = type.getInterface();
|
||||
Resolver::ResolvedDecl decl = resolver.resolveId(interfaceType.getTypeId());
|
||||
return BrandedDecl(decl,
|
||||
evaluateBrand(resolver, decl, interfaceType.getBrand().getScopes()),
|
||||
Expression::Reader());
|
||||
}
|
||||
|
||||
case schema::Type::STRUCT: {
|
||||
auto structType = type.getStruct();
|
||||
Resolver::ResolvedDecl decl = resolver.resolveId(structType.getTypeId());
|
||||
return BrandedDecl(decl,
|
||||
evaluateBrand(resolver, decl, structType.getBrand().getScopes()),
|
||||
Expression::Reader());
|
||||
}
|
||||
|
||||
case schema::Type::LIST: {
|
||||
auto elementType = decompileType(resolver, type.getList().getElementType());
|
||||
return KJ_ASSERT_NONNULL(builtin(Declaration::BUILTIN_LIST)
|
||||
.applyParams(kj::heapArray(&elementType, 1), Expression::Reader()));
|
||||
}
|
||||
|
||||
case schema::Type::ANY_POINTER: {
|
||||
auto anyPointer = type.getAnyPointer();
|
||||
switch (anyPointer.which()) {
|
||||
case schema::Type::AnyPointer::UNCONSTRAINED:
|
||||
return builtin(Declaration::BUILTIN_ANY_POINTER);
|
||||
|
||||
case schema::Type::AnyPointer::PARAMETER: {
|
||||
auto param = anyPointer.getParameter();
|
||||
auto id = param.getScopeId();
|
||||
uint index = param.getParameterIndex();
|
||||
KJ_IF_MAYBE(binding, lookupParameter(resolver, id, index)) {
|
||||
return *binding;
|
||||
} else {
|
||||
return BrandedDecl(Resolver::ResolvedParameter {id, index}, Expression::Reader());
|
||||
}
|
||||
}
|
||||
|
||||
case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER:
|
||||
KJ_FAIL_ASSERT("Alias pointed to implicit method type parameter?");
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
kj::Maybe<BrandedDecl> BrandScope::compileDeclExpression(
|
||||
Expression::Reader source, Resolver& resolver) {
|
||||
switch (source.which()) {
|
||||
case Expression::UNKNOWN:
|
||||
// Error reported earlier.
|
||||
return nullptr;
|
||||
|
||||
case Expression::POSITIVE_INT:
|
||||
case Expression::NEGATIVE_INT:
|
||||
case Expression::FLOAT:
|
||||
case Expression::STRING:
|
||||
case Expression::BINARY:
|
||||
case Expression::LIST:
|
||||
case Expression::TUPLE:
|
||||
case Expression::EMBED:
|
||||
errorReporter.addErrorOn(source, "Expected name.");
|
||||
return nullptr;
|
||||
|
||||
case Expression::RELATIVE_NAME: {
|
||||
auto name = source.getRelativeName();
|
||||
auto nameValue = name.getValue();
|
||||
|
||||
KJ_IF_MAYBE(r, resolver.resolve(nameValue)) {
|
||||
auto result = interpretResolve(resolver, *r, source);
|
||||
return kj::mv(result);
|
||||
} else {
|
||||
errorReporter.addErrorOn(name, kj::str("Not defined: ", nameValue));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
case Expression::ABSOLUTE_NAME: {
|
||||
auto name = source.getAbsoluteName();
|
||||
KJ_IF_MAYBE(r, resolver.getTopScope().resolver->resolveMember(name.getValue())) {
|
||||
auto result = interpretResolve(resolver, *r, source);
|
||||
return kj::mv(result);
|
||||
} else {
|
||||
errorReporter.addErrorOn(name, kj::str("Not defined: ", name.getValue()));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
case Expression::IMPORT: {
|
||||
auto filename = source.getImport();
|
||||
KJ_IF_MAYBE(decl, resolver.resolveImport(filename.getValue())) {
|
||||
// Import is always a root scope, so create a fresh BrandScope.
|
||||
return BrandedDecl(*decl, kj::refcounted<BrandScope>(
|
||||
errorReporter, decl->id, decl->genericParamCount, *decl->resolver), source);
|
||||
} else {
|
||||
errorReporter.addErrorOn(filename, kj::str("Import failed: ", filename.getValue()));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
case Expression::APPLICATION: {
|
||||
auto app = source.getApplication();
|
||||
KJ_IF_MAYBE(decl, compileDeclExpression(app.getFunction(), resolver)) {
|
||||
// Compile all params.
|
||||
auto params = app.getParams();
|
||||
auto compiledParams = kj::heapArrayBuilder<BrandedDecl>(params.size());
|
||||
bool paramFailed = false;
|
||||
for (auto param: params) {
|
||||
if (param.isNamed()) {
|
||||
errorReporter.addErrorOn(param.getNamed(), "Named parameter not allowed here.");
|
||||
}
|
||||
|
||||
KJ_IF_MAYBE(d, compileDeclExpression(param.getValue(), resolver)) {
|
||||
compiledParams.add(kj::mv(*d));
|
||||
} else {
|
||||
// Param failed to compile. Error was already reported.
|
||||
paramFailed = true;
|
||||
}
|
||||
};
|
||||
|
||||
if (paramFailed) {
|
||||
return kj::mv(*decl);
|
||||
}
|
||||
|
||||
// Add the parameters to the brand.
|
||||
KJ_IF_MAYBE(applied, decl->applyParams(compiledParams.finish(), source)) {
|
||||
return kj::mv(*applied);
|
||||
} else {
|
||||
// Error already reported. Ignore parameters.
|
||||
return kj::mv(*decl);
|
||||
}
|
||||
} else {
|
||||
// error already reported
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
case Expression::MEMBER: {
|
||||
auto member = source.getMember();
|
||||
KJ_IF_MAYBE(decl, compileDeclExpression(member.getParent(), resolver)) {
|
||||
auto name = member.getName();
|
||||
KJ_IF_MAYBE(memberDecl, decl->getMember(name.getValue(), source)) {
|
||||
return kj::mv(*memberDecl);
|
||||
} else {
|
||||
errorReporter.addErrorOn(name, kj::str(
|
||||
"'", expressionString(member.getParent()),
|
||||
"' has no member named '", name.getValue(), "'"));
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// error already reported
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
273
vendor/capnproto/src/capnp/compiler/generics.h
vendored
Normal file
273
vendor/capnproto/src/capnp/compiler/generics.h
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
// Copyright (c) 2013-2020 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kj/refcount.h>
|
||||
#include <capnp/orphan.h>
|
||||
#include <capnp/compiler/grammar.capnp.h>
|
||||
#include <capnp/schema.capnp.h>
|
||||
#include <capnp/dynamic.h>
|
||||
#include <kj/vector.h>
|
||||
#include <kj/one-of.h>
|
||||
#include "error-reporter.h"
|
||||
#include "resolver.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
class BrandedDecl;
|
||||
class BrandScope;
|
||||
|
||||
class BrandedDecl {
|
||||
// Represents a declaration possibly with generic parameter bindings.
|
||||
|
||||
public:
|
||||
inline BrandedDecl(Resolver::ResolvedDecl decl,
|
||||
kj::Own<BrandScope>&& brand,
|
||||
Expression::Reader source)
|
||||
: brand(kj::mv(brand)), source(source) {
|
||||
// `source`, is the expression which specified this branded decl. It is provided so that errors
|
||||
// can be reported against it. It is acceptable to pass a default-initialized reader if there's
|
||||
// no source expression; errors will then be reported at 0, 0.
|
||||
|
||||
body.init<Resolver::ResolvedDecl>(kj::mv(decl));
|
||||
}
|
||||
inline BrandedDecl(Resolver::ResolvedParameter variable, Expression::Reader source)
|
||||
: source(source) {
|
||||
body.init<Resolver::ResolvedParameter>(kj::mv(variable));
|
||||
}
|
||||
inline BrandedDecl(decltype(nullptr)) {}
|
||||
inline BrandedDecl() {} // exists only for ExternalMutexGuarded<BrandedDecl> to work...
|
||||
|
||||
BrandedDecl(BrandedDecl& other);
|
||||
BrandedDecl(BrandedDecl&& other) = default;
|
||||
|
||||
BrandedDecl& operator=(BrandedDecl& other);
|
||||
BrandedDecl& operator=(BrandedDecl&& other) = default;
|
||||
|
||||
kj::Maybe<BrandedDecl> applyParams(kj::Array<BrandedDecl> params, Expression::Reader subSource);
|
||||
// Treat the declaration as a generic and apply it to the given parameter list.
|
||||
|
||||
kj::Maybe<BrandedDecl> getMember(kj::StringPtr memberName, Expression::Reader subSource);
|
||||
// Get a member of this declaration.
|
||||
|
||||
kj::Maybe<Declaration::Which> getKind();
|
||||
// Returns the kind of declaration, or null if this is an unbound generic variable.
|
||||
|
||||
template <typename InitBrandFunc>
|
||||
uint64_t getIdAndFillBrand(InitBrandFunc&& initBrand);
|
||||
// Returns the type ID of this node. `initBrand` is a zero-arg functor which returns
|
||||
// schema::Brand::Builder; this will be called if this decl has brand bindings, and
|
||||
// the returned builder filled in to reflect those bindings.
|
||||
//
|
||||
// It is an error to call this when `getKind()` returns null.
|
||||
|
||||
kj::Maybe<BrandedDecl&> getListParam();
|
||||
// Only if the kind is BUILTIN_LIST: Get the list's type parameter.
|
||||
|
||||
Resolver::ResolvedParameter asVariable();
|
||||
// If this is an unbound generic variable (i.e. `getKind()` returns null), return information
|
||||
// about the variable.
|
||||
//
|
||||
// It is an error to call this when `getKind()` does not return null.
|
||||
|
||||
bool compileAsType(ErrorReporter& errorReporter, schema::Type::Builder target);
|
||||
// Compile this decl to a schema::Type.
|
||||
|
||||
inline void addError(ErrorReporter& errorReporter, kj::StringPtr message) {
|
||||
errorReporter.addErrorOn(source, message);
|
||||
}
|
||||
|
||||
Resolver::ResolveResult asResolveResult(uint64_t scopeId, schema::Brand::Builder brandBuilder);
|
||||
// Reverse this into a ResolveResult. If necessary, use `brandBuilder` to fill in
|
||||
// ResolvedDecl.brand.
|
||||
|
||||
kj::String toString();
|
||||
|
||||
private:
|
||||
Resolver::ResolveResult body;
|
||||
kj::Own<BrandScope> brand; // null if parameter
|
||||
Expression::Reader source;
|
||||
};
|
||||
|
||||
class BrandScope: public kj::Refcounted {
|
||||
// Tracks the brand parameter bindings affecting the scope specified by some expression. For
|
||||
// example, if we are interpreting the type expression "Foo(Text).Bar", we would start with the
|
||||
// current scope's BrandScope, create a new child BrandScope representing "Foo", add the "(Text)"
|
||||
// parameter bindings to it, then create a further child scope for "Bar". Thus the BrandScope for
|
||||
// Bar knows that Foo's parameter list has been bound to "(Text)".
|
||||
|
||||
public:
|
||||
BrandScope(ErrorReporter& errorReporter, uint64_t startingScopeId,
|
||||
uint startingScopeParamCount, Resolver& startingScope);
|
||||
// TODO(bug): Passing an `errorReporter` to the constructor of `BrandScope` turns out not to
|
||||
// make a ton of sense, as an `errorReporter` is meant to report errors in a specific module,
|
||||
// but `BrandScope` might be constructed while compiling one module but then used when
|
||||
// compiling a different module, or not compiling a module at all. Note, though, that it DOES
|
||||
// make sense for BrandedDecl to have an ErrorReporter, specifically associated with its
|
||||
// `source` expression.
|
||||
|
||||
bool isGeneric();
|
||||
// Returns true if this scope or any parent scope is a generic (has brand parameters).
|
||||
|
||||
kj::Own<BrandScope> push(uint64_t typeId, uint paramCount);
|
||||
// Creates a new child scope with the given type ID and number of brand parameters.
|
||||
|
||||
kj::Maybe<kj::Own<BrandScope>> setParams(
|
||||
kj::Array<BrandedDecl> params, Declaration::Which genericType, Expression::Reader source);
|
||||
// Create a new BrandScope representing the same scope, but with parameters filled in.
|
||||
//
|
||||
// This should only be called on the generic version of the scope. If called on a branded
|
||||
// version, an error will be reported.
|
||||
//
|
||||
// Returns null if an error occurred that prevented creating the BrandScope; the error will have
|
||||
// been reported to the ErrorReporter.
|
||||
|
||||
kj::Own<BrandScope> pop(uint64_t newLeafId);
|
||||
// Return the parent scope.
|
||||
|
||||
kj::Maybe<BrandedDecl> lookupParameter(Resolver& resolver, uint64_t scopeId, uint index);
|
||||
// Search up the scope chain for the scope matching `scopeId`, and return its `index`th parameter
|
||||
// binding. Returns null if the parameter is from a scope that we are currently compiling, and
|
||||
// hasn't otherwise been bound to any argument (see Brand.Scope.inherit in schema.capnp).
|
||||
//
|
||||
// In the case that a parameter wasn't specified, but isn't part of the current scope, this
|
||||
// returns the declaration for `AnyPointer`.
|
||||
//
|
||||
// TODO(cleanup): Should be called lookupArgument()?
|
||||
|
||||
kj::Maybe<kj::ArrayPtr<BrandedDecl>> getParams(uint64_t scopeId);
|
||||
// Get the whole list of parameter bindings at the given scope. Returns null if the scope is
|
||||
// currently be compiled and the parameters are unbound.
|
||||
//
|
||||
// Note that it's possible that not all declared parameters were actually specified for a given
|
||||
// scope. For example, if you declare a generic `Foo(T, U)`, and then you intiantiate it
|
||||
// somewhere as `Foo(Text)`, then `U` is unspecified -- this is not an error, because Cap'n
|
||||
// Proto allows new type parameters to be added over time. `U` should be treated as `AnyPointer`
|
||||
// in this case, but `getParams()` doesn't know how many parameters are expected, so it will
|
||||
// return an array that only contains one item. Use `lookupParameter()` if you want unspecified
|
||||
// parameters to be filled in with `AnyPointer` automatically.
|
||||
//
|
||||
// TODO(cleanup): Should be called getArguments()?
|
||||
|
||||
template <typename InitBrandFunc>
|
||||
void compile(InitBrandFunc&& initBrand);
|
||||
// Constructs the schema::Brand corresponding to this brand scope.
|
||||
//
|
||||
// `initBrand` is a zero-arg functor which returns an empty schema::Brand::Builder, into which
|
||||
// the brand is constructed. If no generics are present, then `initBrand` is never called.
|
||||
//
|
||||
// TODO(cleanup): Should this return Maybe<Orphan<schema::Brand>> instead?
|
||||
|
||||
kj::Maybe<BrandedDecl> compileDeclExpression(
|
||||
Expression::Reader source, Resolver& resolver);
|
||||
// Interpret a type expression within this branded scope.
|
||||
|
||||
BrandedDecl interpretResolve(
|
||||
Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source);
|
||||
// After using a Resolver to resolve a symbol, call interpretResolve() to interpret the result
|
||||
// within the current brand scope. For example, if a name resolved to a brand parameter, this
|
||||
// replaces it with the appropriate argument from the scope.
|
||||
|
||||
inline uint64_t getScopeId() { return leafId; }
|
||||
|
||||
private:
|
||||
ErrorReporter& errorReporter;
|
||||
kj::Maybe<kj::Own<BrandScope>> parent;
|
||||
uint64_t leafId; // zero = this is the root
|
||||
uint leafParamCount; // number of generic parameters on this leaf
|
||||
bool inherited;
|
||||
kj::Array<BrandedDecl> params;
|
||||
|
||||
BrandScope(kj::Own<BrandScope> parent, uint64_t leafId, uint leafParamCount)
|
||||
: errorReporter(parent->errorReporter),
|
||||
parent(kj::mv(parent)), leafId(leafId), leafParamCount(leafParamCount),
|
||||
inherited(false) {}
|
||||
BrandScope(BrandScope& base, kj::Array<BrandedDecl> params)
|
||||
: errorReporter(base.errorReporter),
|
||||
leafId(base.leafId), leafParamCount(base.leafParamCount),
|
||||
inherited(false), params(kj::mv(params)) {
|
||||
KJ_IF_MAYBE(p, base.parent) {
|
||||
parent = kj::addRef(**p);
|
||||
}
|
||||
}
|
||||
BrandScope(ErrorReporter& errorReporter, uint64_t scopeId)
|
||||
: errorReporter(errorReporter), leafId(scopeId), leafParamCount(0), inherited(false) {}
|
||||
|
||||
kj::Own<BrandScope> evaluateBrand(
|
||||
Resolver& resolver, Resolver::ResolvedDecl decl,
|
||||
List<schema::Brand::Scope>::Reader brand, uint index = 0);
|
||||
|
||||
BrandedDecl decompileType(Resolver& resolver, schema::Type::Reader type);
|
||||
|
||||
template <typename T, typename... Params>
|
||||
friend kj::Own<T> kj::refcounted(Params&&... params);
|
||||
friend class BrandedDecl;
|
||||
};
|
||||
|
||||
template <typename InitBrandFunc>
|
||||
uint64_t BrandedDecl::getIdAndFillBrand(InitBrandFunc&& initBrand) {
|
||||
KJ_REQUIRE(body.is<Resolver::ResolvedDecl>());
|
||||
|
||||
brand->compile(kj::fwd<InitBrandFunc>(initBrand));
|
||||
return body.get<Resolver::ResolvedDecl>().id;
|
||||
}
|
||||
|
||||
template <typename InitBrandFunc>
|
||||
void BrandScope::compile(InitBrandFunc&& initBrand) {
|
||||
kj::Vector<BrandScope*> levels;
|
||||
BrandScope* ptr = this;
|
||||
for (;;) {
|
||||
if (ptr->params.size() > 0 || (ptr->inherited && ptr->leafParamCount > 0)) {
|
||||
levels.add(ptr);
|
||||
}
|
||||
KJ_IF_MAYBE(p, ptr->parent) {
|
||||
ptr = *p;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (levels.size() > 0) {
|
||||
auto scopes = initBrand().initScopes(levels.size());
|
||||
for (uint i: kj::indices(levels)) {
|
||||
auto scope = scopes[i];
|
||||
scope.setScopeId(levels[i]->leafId);
|
||||
|
||||
if (levels[i]->inherited) {
|
||||
scope.setInherit();
|
||||
} else {
|
||||
auto bindings = scope.initBind(levels[i]->params.size());
|
||||
for (uint j: kj::indices(bindings)) {
|
||||
levels[i]->params[j].compileAsType(errorReporter, bindings[j].initType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
2978
vendor/capnproto/src/capnp/compiler/grammar.capnp.c++
vendored
Normal file
2978
vendor/capnproto/src/capnp/compiler/grammar.capnp.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6202
vendor/capnproto/src/capnp/compiler/grammar.capnp.h
vendored
Normal file
6202
vendor/capnproto/src/capnp/compiler/grammar.capnp.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
259
vendor/capnproto/src/capnp/compiler/lexer.c++
vendored
Normal file
259
vendor/capnproto/src/capnp/compiler/lexer.c++
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
// 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 "lexer.h"
|
||||
#include <kj/parse/char.h>
|
||||
#include <kj/debug.h>
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
namespace p = kj::parse;
|
||||
|
||||
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
|
||||
ErrorReporter& errorReporter) {
|
||||
Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
|
||||
|
||||
auto parser = p::sequence(lexer.getParsers().statementSequence, p::endOfInput);
|
||||
|
||||
Lexer::ParserInput parserInput(input.begin(), input.end());
|
||||
kj::Maybe<kj::Array<Orphan<Statement>>> parseOutput = parser(parserInput);
|
||||
|
||||
KJ_IF_MAYBE(output, parseOutput) {
|
||||
auto l = result.initStatements(output->size());
|
||||
for (uint i = 0; i < output->size(); i++) {
|
||||
l.adoptWithCaveats(i, kj::mv((*output)[i]));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
uint32_t best = parserInput.getBest();
|
||||
errorReporter.addError(best, best, kj::str("Parse error."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
typedef p::Span<uint32_t> Location;
|
||||
|
||||
Token::Builder initTok(Orphan<Token>& t, const Location& loc) {
|
||||
auto builder = t.get();
|
||||
builder.setStartByte(loc.begin());
|
||||
builder.setEndByte(loc.end());
|
||||
return builder;
|
||||
}
|
||||
|
||||
void buildTokenSequenceList(List<List<Token>>::Builder builder,
|
||||
kj::Array<kj::Array<Orphan<Token>>>&& items) {
|
||||
for (uint i = 0; i < items.size(); i++) {
|
||||
auto& item = items[i];
|
||||
auto itemBuilder = builder.init(i, item.size());
|
||||
for (uint j = 0; j < item.size(); j++) {
|
||||
itemBuilder.adoptWithCaveats(j, kj::mv(item[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto discardComment =
|
||||
sequence(p::exactChar<'#'>(), p::discard(p::many(p::discard(p::anyOfChars("\n").invert()))),
|
||||
p::oneOf(p::exactChar<'\n'>(), p::endOfInput));
|
||||
constexpr auto utf8Bom =
|
||||
sequence(p::exactChar<'\xef'>(), p::exactChar<'\xbb'>(), p::exactChar<'\xbf'>());
|
||||
|
||||
constexpr auto bomsAndWhitespace =
|
||||
sequence(p::discardWhitespace,
|
||||
p::discard(p::many(sequence(utf8Bom, p::discardWhitespace))));
|
||||
|
||||
constexpr auto commentsAndWhitespace =
|
||||
sequence(bomsAndWhitespace,
|
||||
p::discard(p::many(sequence(discardComment, bomsAndWhitespace))));
|
||||
|
||||
constexpr auto discardLineWhitespace =
|
||||
p::discard(p::many(p::discard(p::whitespaceChar.invert().orAny("\r\n").invert())));
|
||||
constexpr auto newline = p::oneOf(
|
||||
p::exactChar<'\n'>(),
|
||||
sequence(p::exactChar<'\r'>(), p::discard(p::optional(p::exactChar<'\n'>()))));
|
||||
|
||||
constexpr auto docComment = p::discard(p::optional(p::sequence(
|
||||
discardLineWhitespace,
|
||||
p::discard(p::optional(newline)),
|
||||
p::oneOrMore(p::sequence(discardLineWhitespace, discardComment)))));
|
||||
// Parses a set of comment lines preceded by at most one newline and with no intervening blank
|
||||
// lines.
|
||||
|
||||
} // namespace
|
||||
|
||||
Lexer::Lexer(Orphanage orphanageParam, ErrorReporter& errorReporter)
|
||||
: orphanage(orphanageParam) {
|
||||
|
||||
// Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe
|
||||
// for us to use parsers.tokenSequence even though we haven't yet constructed it.
|
||||
auto& tokenSequence = parsers.tokenSequence;
|
||||
|
||||
auto& commaDelimitedList = arena.copy(p::transform(
|
||||
p::sequence(tokenSequence, p::many(p::sequence(p::exactChar<','>(), tokenSequence))),
|
||||
[](kj::Array<Orphan<Token>>&& first, kj::Array<kj::Array<Orphan<Token>>>&& rest)
|
||||
-> kj::Array<kj::Array<Orphan<Token>>> {
|
||||
if (first == nullptr && rest == nullptr) {
|
||||
// Completely empty list.
|
||||
return nullptr;
|
||||
} else {
|
||||
uint restSize = rest.size();
|
||||
if (restSize > 0 && rest[restSize - 1] == nullptr) {
|
||||
// Allow for trailing commas by shortening the list by one item if the final token is
|
||||
// nullptr
|
||||
restSize--;
|
||||
}
|
||||
auto result = kj::heapArrayBuilder<kj::Array<Orphan<Token>>>(1 + restSize); // first+rest
|
||||
result.add(kj::mv(first));
|
||||
for (uint i = 0; i < restSize ; i++) {
|
||||
result.add(kj::mv(rest[i]));
|
||||
}
|
||||
return result.finish();
|
||||
}
|
||||
}));
|
||||
|
||||
auto& token = arena.copy(p::oneOf(
|
||||
p::transformWithLocation(p::identifier,
|
||||
[this](Location loc, kj::String name) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
initTok(t, loc).setIdentifier(name);
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(p::doubleQuotedString,
|
||||
[this](Location loc, kj::String text) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
initTok(t, loc).setStringLiteral(text);
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(
|
||||
sequence(p::exactChar<'`'>(), p::many(p::anyOfChars("\r\n").invert())),
|
||||
[this](Location loc, kj::Array<char> text) -> Orphan<Token> {
|
||||
// Backtick-quoted line. Note that we assume either `\r` or `\n` is a valid line
|
||||
// ending (to cover all known line ending formats) but we replace the line ending
|
||||
// with `\n`. This way, changing the line endings of your source code doesn't affect
|
||||
// the compiled code.
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
// Append '\n' to the text.
|
||||
auto out = initTok(t, loc).initStringLiteral(text.size() + 1);
|
||||
memcpy(out.begin(), text.begin(), text.size());
|
||||
out[out.size() - 1] = '\n';
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(p::doubleQuotedHexBinary,
|
||||
[this](Location loc, kj::Array<byte> data) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
initTok(t, loc).setBinaryLiteral(data);
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(p::integer,
|
||||
[this](Location loc, uint64_t i) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
initTok(t, loc).setIntegerLiteral(i);
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(p::number,
|
||||
[this](Location loc, double x) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
initTok(t, loc).setFloatLiteral(x);
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(
|
||||
p::charsToString(p::oneOrMore(p::anyOfChars("!$%&*+-./:<=>?@^|~"))),
|
||||
[this](Location loc, kj::String text) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
initTok(t, loc).setOperator(text);
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(
|
||||
sequence(p::exactChar<'('>(), commaDelimitedList, p::exactChar<')'>()),
|
||||
[this](Location loc, kj::Array<kj::Array<Orphan<Token>>>&& items) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
buildTokenSequenceList(
|
||||
initTok(t, loc).initParenthesizedList(items.size()), kj::mv(items));
|
||||
return t;
|
||||
}),
|
||||
p::transformWithLocation(
|
||||
sequence(p::exactChar<'['>(), commaDelimitedList, p::exactChar<']'>()),
|
||||
[this](Location loc, kj::Array<kj::Array<Orphan<Token>>>&& items) -> Orphan<Token> {
|
||||
auto t = orphanage.newOrphan<Token>();
|
||||
buildTokenSequenceList(
|
||||
initTok(t, loc).initBracketedList(items.size()), kj::mv(items));
|
||||
return t;
|
||||
}),
|
||||
p::transformOrReject(p::transformWithLocation(
|
||||
p::oneOf(sequence(p::exactChar<'\xff'>(), p::exactChar<'\xfe'>()),
|
||||
sequence(p::exactChar<'\xfe'>(), p::exactChar<'\xff'>()),
|
||||
sequence(p::exactChar<'\x00'>())),
|
||||
[&errorReporter](Location loc) -> kj::Maybe<Orphan<Token>> {
|
||||
errorReporter.addError(loc.begin(), loc.end(),
|
||||
"Non-UTF-8 input detected. Cap'n Proto schema files must be UTF-8 text.");
|
||||
return nullptr;
|
||||
}), [](kj::Maybe<Orphan<Token>> param) { return param; })));
|
||||
parsers.tokenSequence = arena.copy(p::sequence(
|
||||
commentsAndWhitespace, p::many(p::sequence(token, commentsAndWhitespace))));
|
||||
|
||||
auto& statementSequence = parsers.statementSequence;
|
||||
|
||||
auto& statementEnd = arena.copy(p::oneOf(
|
||||
transform(p::sequence(p::exactChar<';'>(), docComment),
|
||||
[this]() -> Orphan<Statement> {
|
||||
auto result = orphanage.newOrphan<Statement>();
|
||||
auto builder = result.get();
|
||||
builder.setLine();
|
||||
return result;
|
||||
}),
|
||||
transform(
|
||||
p::sequence(p::exactChar<'{'>(), docComment, statementSequence, p::exactChar<'}'>(),
|
||||
docComment),
|
||||
[this](kj::Array<Orphan<Statement>>&& statements)
|
||||
-> Orphan<Statement> {
|
||||
auto result = orphanage.newOrphan<Statement>();
|
||||
auto builder = result.get();
|
||||
auto list = builder.initBlock(statements.size());
|
||||
for (uint i = 0; i < statements.size(); i++) {
|
||||
list.adoptWithCaveats(i, kj::mv(statements[i]));
|
||||
}
|
||||
return result;
|
||||
})
|
||||
));
|
||||
|
||||
auto& statement = arena.copy(p::transformWithLocation(p::sequence(tokenSequence, statementEnd),
|
||||
[](Location loc, kj::Array<Orphan<Token>>&& tokens, Orphan<Statement>&& statement) {
|
||||
auto builder = statement.get();
|
||||
auto tokensBuilder = builder.initTokens(tokens.size());
|
||||
for (uint i = 0; i < tokens.size(); i++) {
|
||||
tokensBuilder.adoptWithCaveats(i, kj::mv(tokens[i]));
|
||||
}
|
||||
builder.setStartByte(loc.begin());
|
||||
builder.setEndByte(loc.end());
|
||||
return kj::mv(statement);
|
||||
}));
|
||||
|
||||
parsers.statementSequence = arena.copy(sequence(
|
||||
commentsAndWhitespace, many(sequence(statement, commentsAndWhitespace))));
|
||||
|
||||
}
|
||||
|
||||
Lexer::~Lexer() noexcept(false) {}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
1022
vendor/capnproto/src/capnp/compiler/lexer.capnp.h
vendored
Normal file
1022
vendor/capnproto/src/capnp/compiler/lexer.capnp.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
91
vendor/capnproto/src/capnp/compiler/lexer.h
vendored
Normal file
91
vendor/capnproto/src/capnp/compiler/lexer.h
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/compiler/lexer.capnp.h>
|
||||
#include <kj/parse/common.h>
|
||||
#include <kj/arena.h>
|
||||
#include "error-reporter.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
|
||||
ErrorReporter& errorReporter);
|
||||
// Lex the given source code, placing the results in `result`. Returns true if there
|
||||
// were no errors, false if there were. Even when errors are present, the file may have partial
|
||||
// content which can be fed into later stages of parsing in order to find more errors.
|
||||
|
||||
class Lexer {
|
||||
// Advanced lexer interface. This interface exposes the inner parsers so that you can embed them
|
||||
// into your own parsers.
|
||||
|
||||
public:
|
||||
Lexer(Orphanage orphanage, ErrorReporter& errorReporter);
|
||||
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
|
||||
// a pointer to the beginning of the input, used to compute byte offsets.
|
||||
|
||||
~Lexer() noexcept(false);
|
||||
|
||||
class ParserInput: public kj::parse::IteratorInput<char, const char*> {
|
||||
// Like IteratorInput<char, const char*> except that positions are measured as byte offsets
|
||||
// rather than pointers.
|
||||
|
||||
public:
|
||||
ParserInput(const char* begin, const char* end)
|
||||
: IteratorInput<char, const char*>(begin, end), begin(begin) {}
|
||||
explicit ParserInput(ParserInput& parent)
|
||||
: IteratorInput<char, const char*>(parent), begin(parent.begin) {}
|
||||
|
||||
inline uint32_t getBest() {
|
||||
return IteratorInput<char, const char*>::getBest() - begin;
|
||||
}
|
||||
inline uint32_t getPosition() {
|
||||
return IteratorInput<char, const char*>::getPosition() - begin;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* begin;
|
||||
};
|
||||
|
||||
template <typename Output>
|
||||
using Parser = kj::parse::ParserRef<ParserInput, Output>;
|
||||
|
||||
struct Parsers {
|
||||
Parser<kj::Array<Orphan<Token>>> tokenSequence;
|
||||
Parser<kj::Array<Orphan<Statement>>> statementSequence;
|
||||
};
|
||||
|
||||
const Parsers& getParsers() { return parsers; }
|
||||
|
||||
private:
|
||||
Orphanage orphanage;
|
||||
kj::Arena arena;
|
||||
Parsers parsers;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
2086
vendor/capnproto/src/capnp/compiler/node-translator.c++
vendored
Normal file
2086
vendor/capnproto/src/capnp/compiler/node-translator.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
205
vendor/capnproto/src/capnp/compiler/node-translator.h
vendored
Normal file
205
vendor/capnproto/src/capnp/compiler/node-translator.h
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/orphan.h>
|
||||
#include <capnp/compiler/grammar.capnp.h>
|
||||
#include <capnp/schema.capnp.h>
|
||||
#include <capnp/dynamic.h>
|
||||
#include <kj/vector.h>
|
||||
#include <kj/one-of.h>
|
||||
#include "error-reporter.h"
|
||||
#include "resolver.h"
|
||||
#include "generics.h"
|
||||
#include <map>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
class NodeTranslator {
|
||||
// Translates one node in the schema from AST form to final schema form. A "node" is anything
|
||||
// that has a unique ID, such as structs, enums, constants, and annotations, but not fields,
|
||||
// unions, enumerants, or methods (the latter set have 16-bit ordinals but not 64-bit global IDs).
|
||||
public:
|
||||
NodeTranslator(Resolver& resolver, ErrorReporter& errorReporter,
|
||||
const Declaration::Reader& decl, Orphan<schema::Node> wipNode);
|
||||
// Construct a NodeTranslator to translate the given declaration. The wipNode starts out with
|
||||
// `displayName`, `id`, `scopeId`, and `nestedNodes` already initialized. The `NodeTranslator`
|
||||
// fills in the rest.
|
||||
|
||||
~NodeTranslator() noexcept(false);
|
||||
|
||||
struct NodeSet {
|
||||
schema::Node::Reader node;
|
||||
// The main node.
|
||||
|
||||
kj::Array<schema::Node::Reader> auxNodes;
|
||||
// Auxiliary nodes that were produced when translating this node and should be loaded along
|
||||
// with it. In particular, structs that contain groups (or named unions) spawn extra nodes
|
||||
// representing those.
|
||||
|
||||
};
|
||||
|
||||
NodeSet getBootstrapNode();
|
||||
// Get an incomplete version of the node in which pointer-typed value expressions have not yet
|
||||
// been translated. Instead, for all `schema.Value` objects representing pointer-type values,
|
||||
// the value is set to an appropriate "empty" value. This version of the schema can be used to
|
||||
// bootstrap the dynamic API which can then in turn be used to encode the missing complex values.
|
||||
//
|
||||
// If the final node has already been built, this will actually return the final node (in fact,
|
||||
// it's the same node object).
|
||||
|
||||
NodeSet finish(Schema selfUnboundBootstrap);
|
||||
// Finish translating the node (including filling in all the pieces that are missing from the
|
||||
// bootstrap node) and return it.
|
||||
//
|
||||
// `selfUnboundBootstrap` is a Schema build using the Node returned by getBootstrapNode(), and
|
||||
// with generic parameters "unbound", i.e. it was returned by SchemaLoader::getUnbound().
|
||||
|
||||
static kj::Maybe<Resolver::ResolveResult> compileDecl(
|
||||
uint64_t scopeId, uint scopeParameterCount, Resolver& resolver, ErrorReporter& errorReporter,
|
||||
Expression::Reader expression, schema::Brand::Builder brandBuilder);
|
||||
// Compile a one-off declaration expression without building a NodeTranslator. Used for
|
||||
// evaluating aliases.
|
||||
//
|
||||
// `brandBuilder` may be used to construct a message which will fill in ResolvedDecl::brand in
|
||||
// the result.
|
||||
|
||||
private:
|
||||
class DuplicateNameDetector;
|
||||
class DuplicateOrdinalDetector;
|
||||
class StructLayout;
|
||||
class StructTranslator;
|
||||
|
||||
Resolver& resolver;
|
||||
ErrorReporter& errorReporter;
|
||||
Orphanage orphanage;
|
||||
kj::Own<BrandScope> localBrand;
|
||||
|
||||
Orphan<schema::Node> wipNode;
|
||||
// The work-in-progress schema node.
|
||||
|
||||
kj::Vector<Orphan<schema::Node>> groups;
|
||||
// If this is a struct node and it contains groups, these are the nodes for those groups, which
|
||||
// must be loaded together with the top-level node.
|
||||
|
||||
|
||||
|
||||
struct UnfinishedValue {
|
||||
Expression::Reader source;
|
||||
schema::Type::Reader type;
|
||||
kj::Maybe<Schema> typeScope;
|
||||
schema::Value::Builder target;
|
||||
};
|
||||
kj::Vector<UnfinishedValue> unfinishedValues;
|
||||
// List of values in `wipNode` which have not yet been interpreted, because they are structs
|
||||
// or lists and as such interpreting them require using the types' schemas (to take advantage
|
||||
// of the dynamic API). Once bootstrap schemas have been built, they can be used to interpret
|
||||
// these values.
|
||||
|
||||
void compileNode(Declaration::Reader decl, schema::Node::Builder builder);
|
||||
|
||||
void compileConst(Declaration::Const::Reader decl, schema::Node::Const::Builder builder);
|
||||
void compileAnnotation(Declaration::Annotation::Reader decl,
|
||||
schema::Node::Annotation::Builder builder);
|
||||
|
||||
void compileEnum(Void decl, List<Declaration>::Reader members,
|
||||
schema::Node::Builder builder);
|
||||
void compileStruct(Void decl, List<Declaration>::Reader members,
|
||||
schema::Node::Builder builder);
|
||||
// The `members` arrays contain only members with ordinal numbers, in code order. Other members
|
||||
// are handled elsewhere.
|
||||
|
||||
kj::Maybe<BrandedDecl> compileDeclExpression(
|
||||
Expression::Reader source);
|
||||
// Compile an expression which is expected to resolve to a declaration or type expression.
|
||||
|
||||
bool compileType(Expression::Reader source, schema::Type::Builder target);
|
||||
// Returns false if there was a problem, in which case value expressions of this type should
|
||||
// not be parsed.
|
||||
|
||||
void compileDefaultDefaultValue(schema::Type::Reader type, schema::Value::Builder target);
|
||||
// Initializes `target` to contain the "default default" value for `type`.
|
||||
|
||||
void compileBootstrapValue(
|
||||
Expression::Reader source, schema::Type::Reader type, schema::Value::Builder target,
|
||||
kj::Maybe<Schema> typeScope = nullptr);
|
||||
// Calls compileValue() if this value should be interpreted at bootstrap time. Otherwise,
|
||||
// adds the value to `unfinishedValues` for later evaluation.
|
||||
//
|
||||
// If `type` comes from some other node, `typeScope` is the schema for that node. Otherwise the
|
||||
// scope of the type expression is assumed to be this node (meaning, in particular, that no
|
||||
// generic type parameters are bound).
|
||||
|
||||
void compileValue(Expression::Reader source, schema::Type::Reader type,
|
||||
Schema typeScope, schema::Value::Builder target, bool isBootstrap);
|
||||
// Interprets the value expression and initializes `target` with the result.
|
||||
|
||||
kj::Maybe<DynamicValue::Reader> readConstant(Expression::Reader name, bool isBootstrap);
|
||||
// Get the value of the given constant. May return null if some error occurs, which will already
|
||||
// have been reported.
|
||||
|
||||
kj::Maybe<kj::Array<const byte>> readEmbed(LocatedText::Reader filename);
|
||||
// Read a raw file for embedding.
|
||||
|
||||
Orphan<List<schema::Annotation>> compileAnnotationApplications(
|
||||
List<Declaration::AnnotationApplication>::Reader annotations,
|
||||
kj::StringPtr targetsFlagName);
|
||||
};
|
||||
|
||||
class ValueTranslator {
|
||||
public:
|
||||
class Resolver {
|
||||
public:
|
||||
virtual kj::Maybe<DynamicValue::Reader> resolveConstant(Expression::Reader name) = 0;
|
||||
virtual kj::Maybe<kj::Array<const byte>> readEmbed(LocatedText::Reader filename) = 0;
|
||||
};
|
||||
|
||||
ValueTranslator(Resolver& resolver, ErrorReporter& errorReporter, Orphanage orphanage)
|
||||
: resolver(resolver), errorReporter(errorReporter), orphanage(orphanage) {}
|
||||
|
||||
kj::Maybe<Orphan<DynamicValue>> compileValue(Expression::Reader src, Type type);
|
||||
|
||||
void fillStructValue(DynamicStruct::Builder builder,
|
||||
List<Expression::Param>::Reader assignments);
|
||||
// Interprets the given assignments and uses them to fill in the given struct builder.
|
||||
|
||||
private:
|
||||
Resolver& resolver;
|
||||
ErrorReporter& errorReporter;
|
||||
Orphanage orphanage;
|
||||
|
||||
Orphan<DynamicValue> compileValueInner(Expression::Reader src, Type type);
|
||||
bool matchesType(Expression::Reader src, Type type, Orphan<DynamicValue>& result);
|
||||
// Helpers for compileValue().
|
||||
|
||||
kj::String makeNodeName(Schema node);
|
||||
kj::String makeTypeName(Type type);
|
||||
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
1160
vendor/capnproto/src/capnp/compiler/parser.c++
vendored
Normal file
1160
vendor/capnproto/src/capnp/compiler/parser.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
146
vendor/capnproto/src/capnp/compiler/parser.h
vendored
Normal file
146
vendor/capnproto/src/capnp/compiler/parser.h
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/compiler/grammar.capnp.h>
|
||||
#include <capnp/compiler/lexer.capnp.h>
|
||||
#include <kj/parse/common.h>
|
||||
#include <kj/arena.h>
|
||||
#include "error-reporter.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
|
||||
ErrorReporter& errorReporter);
|
||||
// Parse a list of statements to build a ParsedFile.
|
||||
//
|
||||
// If any errors are reported, then the output is not usable. However, it may be passed on through
|
||||
// later stages of compilation in order to detect additional errors.
|
||||
|
||||
uint64_t generateRandomId();
|
||||
// Generate a new random unique ID. This lives here mostly for lack of a better location.
|
||||
|
||||
uint64_t generateChildId(uint64_t parentId, kj::StringPtr childName);
|
||||
// Generate the ID for a child node given its parent ID and name.
|
||||
|
||||
uint64_t generateGroupId(uint64_t parentId, uint16_t groupIndex);
|
||||
// Generate the ID for a group within a struct.
|
||||
|
||||
// TODO(cleanup): Move generate*Id() somewhere more sensible.
|
||||
|
||||
class CapnpParser {
|
||||
// Advanced parser interface. This interface exposes the inner parsers so that you can embed
|
||||
// them into your own parsers.
|
||||
|
||||
public:
|
||||
CapnpParser(Orphanage orphanage, ErrorReporter& errorReporter);
|
||||
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
|
||||
// a pointer to the beginning of the input, used to compute byte offsets.
|
||||
|
||||
~CapnpParser() noexcept(false);
|
||||
|
||||
KJ_DISALLOW_COPY_AND_MOVE(CapnpParser);
|
||||
|
||||
using ParserInput = kj::parse::IteratorInput<Token::Reader, List<Token>::Reader::Iterator>;
|
||||
struct DeclParserResult;
|
||||
template <typename Output>
|
||||
using Parser = kj::parse::ParserRef<ParserInput, Output>;
|
||||
using DeclParser = Parser<DeclParserResult>;
|
||||
|
||||
kj::Maybe<Orphan<Declaration>> parseStatement(
|
||||
Statement::Reader statement, const DeclParser& parser);
|
||||
// Parse a statement using the given parser. In addition to parsing the token sequence itself,
|
||||
// this takes care of parsing the block (if any).
|
||||
|
||||
struct DeclParserResult {
|
||||
// DeclParser parses a sequence of tokens representing just the "line" part of the statement --
|
||||
// i.e. everything up to the semicolon or opening curly brace.
|
||||
//
|
||||
// Use `parseStatement()` to avoid having to deal with this struct.
|
||||
|
||||
Orphan<Declaration> decl;
|
||||
// The declaration parsed so far, with nestedDecls still empty.
|
||||
|
||||
kj::Maybe<DeclParser> memberParser;
|
||||
// If null, the statement should not have a block. If non-null, the statement should have a
|
||||
// block containing statements parseable by this parser.
|
||||
|
||||
DeclParserResult(Orphan<Declaration>&& decl, const DeclParser& memberParser)
|
||||
: decl(kj::mv(decl)), memberParser(memberParser) {}
|
||||
explicit DeclParserResult(Orphan<Declaration>&& decl)
|
||||
: decl(kj::mv(decl)), memberParser(nullptr) {}
|
||||
};
|
||||
|
||||
struct Parsers {
|
||||
DeclParser genericDecl;
|
||||
// Parser that matches any declaration type except those that have ordinals (since they are
|
||||
// context-dependent).
|
||||
|
||||
DeclParser fileLevelDecl;
|
||||
DeclParser enumLevelDecl;
|
||||
DeclParser structLevelDecl;
|
||||
DeclParser interfaceLevelDecl;
|
||||
// Parsers that match genericDecl *and* the ordinal-based declaration types valid in the given
|
||||
// contexts. Note that these may match declarations that are not actually allowed in the given
|
||||
// contexts, as long as the grammar is unambiguous. E.g. nested types are not allowed in
|
||||
// enums, but they'll be accepted by enumLevelDecl. A later stage of compilation should report
|
||||
// these as errors.
|
||||
|
||||
Parser<Orphan<Expression>> expression;
|
||||
Parser<Orphan<Declaration::AnnotationApplication>> annotation;
|
||||
Parser<Orphan<LocatedInteger>> uid;
|
||||
Parser<Orphan<LocatedInteger>> ordinal;
|
||||
Parser<Orphan<Declaration::Param>> param;
|
||||
|
||||
DeclParser usingDecl;
|
||||
DeclParser constDecl;
|
||||
DeclParser enumDecl;
|
||||
DeclParser enumerantDecl;
|
||||
DeclParser structDecl;
|
||||
DeclParser fieldDecl;
|
||||
DeclParser unionDecl;
|
||||
DeclParser groupDecl;
|
||||
DeclParser interfaceDecl;
|
||||
DeclParser methodDecl;
|
||||
DeclParser annotationDecl;
|
||||
// Parsers for individual declaration types.
|
||||
};
|
||||
|
||||
const Parsers& getParsers() { return parsers; }
|
||||
|
||||
private:
|
||||
Orphanage orphanage;
|
||||
ErrorReporter& errorReporter;
|
||||
kj::Arena arena;
|
||||
Parsers parsers;
|
||||
};
|
||||
|
||||
kj::String expressionString(Expression::Reader name);
|
||||
// Stringify the expression as code.
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
132
vendor/capnproto/src/capnp/compiler/resolver.h
vendored
Normal file
132
vendor/capnproto/src/capnp/compiler/resolver.h
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright (c) 2013-2020 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/compiler/grammar.capnp.h>
|
||||
#include <capnp/schema.capnp.h>
|
||||
#include <capnp/schema.h>
|
||||
#include <kj/one-of.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
class Resolver {
|
||||
// Callback class used to find other nodes relative to some existing node.
|
||||
//
|
||||
// `Resolver` is used when compiling one declaration requires inspecting the compiled versions
|
||||
// of other declarations it depends on. For example, if struct type Foo contains a field of type
|
||||
// Bar, and specifies a default value for that field, then to parse that default value we need
|
||||
// the compiled version of `Bar`. Or, more commonly, if a struct type Foo refers to some other
|
||||
// type `Bar.Baz`, this requires doing a lookup that depends on at least partial compilation of
|
||||
// `Bar`, in order to discover its nested type `Baz`.
|
||||
//
|
||||
// Note that declarations are often compiled just-in-time the first time they are resolved. So,
|
||||
// the methods of Resolver may recurse back into other parts of the compiler. It must detect when
|
||||
// a dependency cycle occurs and report an error in order to prevent an infinite loop.
|
||||
|
||||
public:
|
||||
struct ResolvedDecl {
|
||||
// Information about a resolved declaration.
|
||||
|
||||
uint64_t id;
|
||||
// Type ID / node ID of the resolved declaration.
|
||||
|
||||
uint genericParamCount;
|
||||
// If non-zero, the declaration is a generic with the given number of parameters.
|
||||
|
||||
uint64_t scopeId;
|
||||
// The ID of the parent scope of this declaration.
|
||||
|
||||
Declaration::Which kind;
|
||||
// What basic kind of declaration is this? E.g. struct, interface, const, etc.
|
||||
|
||||
Resolver* resolver;
|
||||
// `Resolver` instance that can be used to further resolve other declarations relative to this
|
||||
// one.
|
||||
|
||||
kj::Maybe<schema::Brand::Reader> brand;
|
||||
// If present, then it is necessary to replace the brand scope with the given brand before
|
||||
// using the target type. This happens when the decl resolved to an alias; all other fields
|
||||
// of `ResolvedDecl` refer to the target of the alias, except for `scopeId` which is the
|
||||
// scope that contained the alias.
|
||||
};
|
||||
|
||||
struct ResolvedParameter {
|
||||
uint64_t id; // ID of the node declaring the parameter.
|
||||
uint index; // Index of the parameter.
|
||||
};
|
||||
|
||||
typedef kj::OneOf<ResolvedDecl, ResolvedParameter> ResolveResult;
|
||||
|
||||
virtual kj::Maybe<ResolveResult> resolve(kj::StringPtr name) = 0;
|
||||
// Look up the given name, relative to this node, and return basic information about the
|
||||
// target.
|
||||
|
||||
virtual kj::Maybe<ResolveResult> resolveMember(kj::StringPtr name) = 0;
|
||||
// Look up a member of this node.
|
||||
|
||||
virtual ResolvedDecl resolveBuiltin(Declaration::Which which) = 0;
|
||||
virtual ResolvedDecl resolveId(uint64_t id) = 0;
|
||||
|
||||
virtual kj::Maybe<ResolvedDecl> getParent() = 0;
|
||||
// Returns the parent of this scope, or null if this is the top scope.
|
||||
|
||||
virtual ResolvedDecl getTopScope() = 0;
|
||||
// Get the top-level scope containing this node.
|
||||
|
||||
virtual kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id, schema::Brand::Reader brand) = 0;
|
||||
// Get the schema for the given ID. If a schema is returned, it must be safe to traverse its
|
||||
// dependencies via the Schema API. A schema that is only at the bootstrap stage is
|
||||
// acceptable.
|
||||
//
|
||||
// Throws an exception if the id is not one that was found by calling resolve() or by
|
||||
// traversing other schemas. Returns null if the ID is recognized, but the corresponding
|
||||
// schema node failed to be built for reasons that were already reported.
|
||||
|
||||
virtual kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) = 0;
|
||||
// Get the final schema for the given ID. A bootstrap schema is not acceptable. A raw
|
||||
// node reader is returned rather than a Schema object because using a Schema object built
|
||||
// by the final schema loader could trigger lazy initialization of dependencies which could
|
||||
// lead to a cycle and deadlock.
|
||||
//
|
||||
// Throws an exception if the id is not one that was found by calling resolve() or by
|
||||
// traversing other schemas. Returns null if the ID is recognized, but the corresponding
|
||||
// schema node failed to be built for reasons that were already reported.
|
||||
|
||||
virtual kj::Maybe<ResolvedDecl> resolveImport(kj::StringPtr name) = 0;
|
||||
// Get the ID of an imported file given the import path.
|
||||
|
||||
virtual kj::Maybe<kj::Array<const byte>> readEmbed(kj::StringPtr name) = 0;
|
||||
// Read and return the contents of a file for an `embed` expression.
|
||||
|
||||
virtual kj::Maybe<Type> resolveBootstrapType(schema::Type::Reader type, Schema scope) = 0;
|
||||
// Compile a schema::Type into a Type whose dependencies may safely be traversed via the schema
|
||||
// API. These dependencies may have only bootstrap schemas. Returns null if the type could not
|
||||
// be constructed due to already-reported errors.
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
414
vendor/capnproto/src/capnp/compiler/type-id.c++
vendored
Normal file
414
vendor/capnproto/src/capnp/compiler/type-id.c++
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2013-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.
|
||||
|
||||
#include "type-id.h"
|
||||
#include <kj/debug.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
class TypeIdGenerator {
|
||||
// A non-cryptographic deterministic random number generator used to generate type IDs when the
|
||||
// developer did not specify one themselves.
|
||||
//
|
||||
// The underlying algorithm is MD5. MD5 is safe to use here because this is not intended to be a
|
||||
// cryptographic random number generator. In retrospect it would have been nice to use something
|
||||
// else just to avoid people freaking out about it, but changing the algorithm now would break
|
||||
// backwards-compatibility.
|
||||
|
||||
public:
|
||||
TypeIdGenerator();
|
||||
|
||||
void update(kj::ArrayPtr<const kj::byte> data);
|
||||
inline void update(kj::ArrayPtr<const char> data) {
|
||||
return update(data.asBytes());
|
||||
}
|
||||
inline void update(kj::StringPtr data) {
|
||||
return update(data.asArray());
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const kj::byte> finish();
|
||||
|
||||
private:
|
||||
bool finished = false;
|
||||
|
||||
struct {
|
||||
uint lo, hi;
|
||||
uint a, b, c, d;
|
||||
kj::byte buffer[64];
|
||||
uint block[16];
|
||||
} ctx;
|
||||
|
||||
const kj::byte* body(const kj::byte* ptr, size_t size);
|
||||
};
|
||||
|
||||
uint64_t generateChildId(uint64_t parentId, kj::StringPtr childName) {
|
||||
// Compute ID by hashing the concatenation of the parent ID and the declaration name, and
|
||||
// then taking the first 8 bytes.
|
||||
|
||||
kj::byte parentIdBytes[sizeof(uint64_t)];
|
||||
for (uint i = 0; i < sizeof(uint64_t); i++) {
|
||||
parentIdBytes[i] = (parentId >> (i * 8)) & 0xff;
|
||||
}
|
||||
|
||||
TypeIdGenerator generator;
|
||||
generator.update(kj::arrayPtr(parentIdBytes, kj::size(parentIdBytes)));
|
||||
generator.update(childName);
|
||||
|
||||
kj::ArrayPtr<const kj::byte> resultBytes = generator.finish();
|
||||
|
||||
uint64_t result = 0;
|
||||
for (uint i = 0; i < sizeof(uint64_t); i++) {
|
||||
result = (result << 8) | resultBytes[i];
|
||||
}
|
||||
|
||||
return result | (1ull << 63);
|
||||
}
|
||||
|
||||
uint64_t generateGroupId(uint64_t parentId, uint16_t groupIndex) {
|
||||
// Compute ID by hashing the concatenation of the parent ID and the group index, and
|
||||
// then taking the first 8 bytes.
|
||||
|
||||
kj::byte bytes[sizeof(uint64_t) + sizeof(uint16_t)];
|
||||
for (uint i = 0; i < sizeof(uint64_t); i++) {
|
||||
bytes[i] = (parentId >> (i * 8)) & 0xff;
|
||||
}
|
||||
for (uint i = 0; i < sizeof(uint16_t); i++) {
|
||||
bytes[sizeof(uint64_t) + i] = (groupIndex >> (i * 8)) & 0xff;
|
||||
}
|
||||
|
||||
TypeIdGenerator generator;
|
||||
generator.update(bytes);
|
||||
|
||||
kj::ArrayPtr<const kj::byte> resultBytes = generator.finish();
|
||||
|
||||
uint64_t result = 0;
|
||||
for (uint i = 0; i < sizeof(uint64_t); i++) {
|
||||
result = (result << 8) | resultBytes[i];
|
||||
}
|
||||
|
||||
return result | (1ull << 63);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// The remainder of this file was derived from code placed in the public domain.
|
||||
// The original code bore the following notice:
|
||||
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
|
||||
* MD5 Message-Digest Algorithm (RFC 1321).
|
||||
*
|
||||
* Homepage:
|
||||
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
|
||||
*
|
||||
* Author:
|
||||
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
|
||||
*
|
||||
* This software was written by Alexander Peslyak in 2001. No copyright is
|
||||
* claimed, and the software is hereby placed in the public domain.
|
||||
* In case this attempt to disclaim copyright and place the software in the
|
||||
* public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* (This is a heavily cut-down "BSD license".)
|
||||
*
|
||||
* This differs from Colin Plumb's older public domain implementation in that
|
||||
* no exactly 32-bit integer data type is required (any 32-bit or wider
|
||||
* unsigned integer data type will do), there's no compile-time endianness
|
||||
* configuration, and the function prototypes match OpenSSL's. No code from
|
||||
* Colin Plumb's implementation has been reused; this comment merely compares
|
||||
* the properties of the two independent implementations.
|
||||
*
|
||||
* The primary goals of this implementation are portability and ease of use.
|
||||
* It is meant to be fast, but not as fast as possible. Some known
|
||||
* optimizations are not included to reduce source code size and avoid
|
||||
* compile-time configuration.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* F and G are optimized compared to their RFC 1321 definitions for
|
||||
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
|
||||
* implementation.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them
|
||||
* in a properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures that tolerate unaligned
|
||||
* memory accesses is just an optimization. Nothing will break if it
|
||||
* doesn't work.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
|
||||
#define SET(n) \
|
||||
(*(uint *)&ptr[(n) * 4])
|
||||
#define GET(n) \
|
||||
SET(n)
|
||||
#else
|
||||
#define SET(n) \
|
||||
(ctx.block[(n)] = \
|
||||
(uint)ptr[(n) * 4] | \
|
||||
((uint)ptr[(n) * 4 + 1] << 8) | \
|
||||
((uint)ptr[(n) * 4 + 2] << 16) | \
|
||||
((uint)ptr[(n) * 4 + 3] << 24))
|
||||
#define GET(n) \
|
||||
(ctx.block[(n)])
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update
|
||||
* the bit counters. There are no alignment requirements.
|
||||
*/
|
||||
const kj::byte* TypeIdGenerator::body(const kj::byte* ptr, size_t size)
|
||||
{
|
||||
uint a, b, c, d;
|
||||
uint saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
a = ctx.a;
|
||||
b = ctx.b;
|
||||
c = ctx.c;
|
||||
d = ctx.d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
|
||||
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
|
||||
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
|
||||
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
|
||||
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
|
||||
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
|
||||
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
|
||||
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
|
||||
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
|
||||
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
|
||||
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
|
||||
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
|
||||
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
|
||||
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
|
||||
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
|
||||
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
|
||||
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
|
||||
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
|
||||
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
|
||||
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
|
||||
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
|
||||
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
|
||||
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
|
||||
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
|
||||
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
|
||||
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
|
||||
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
|
||||
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
|
||||
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
|
||||
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
|
||||
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
|
||||
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
|
||||
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
|
||||
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
|
||||
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
|
||||
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
|
||||
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
|
||||
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
|
||||
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
|
||||
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
|
||||
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
|
||||
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
|
||||
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
|
||||
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
|
||||
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
|
||||
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
|
||||
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
|
||||
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
|
||||
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
|
||||
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
|
||||
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
|
||||
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
|
||||
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
|
||||
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
|
||||
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
|
||||
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
|
||||
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
|
||||
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
|
||||
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
|
||||
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
|
||||
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx.a = a;
|
||||
ctx.b = b;
|
||||
ctx.c = c;
|
||||
ctx.d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
TypeIdGenerator::TypeIdGenerator()
|
||||
{
|
||||
ctx.a = 0x67452301;
|
||||
ctx.b = 0xefcdab89;
|
||||
ctx.c = 0x98badcfe;
|
||||
ctx.d = 0x10325476;
|
||||
|
||||
ctx.lo = 0;
|
||||
ctx.hi = 0;
|
||||
}
|
||||
|
||||
void TypeIdGenerator::update(kj::ArrayPtr<const kj::byte> dataArray)
|
||||
{
|
||||
KJ_REQUIRE(!finished, "already called TypeIdGenerator::finish()");
|
||||
|
||||
const kj::byte* data = dataArray.begin();
|
||||
unsigned long size = dataArray.size();
|
||||
|
||||
uint saved_lo;
|
||||
unsigned long used, free;
|
||||
|
||||
saved_lo = ctx.lo;
|
||||
if ((ctx.lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
|
||||
ctx.hi++;
|
||||
ctx.hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
free = 64 - used;
|
||||
|
||||
if (size < free) {
|
||||
memcpy(&ctx.buffer[used], data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ctx.buffer[used], data, free);
|
||||
data = data + free;
|
||||
size -= free;
|
||||
body(ctx.buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(data, size & ~(unsigned long)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
memcpy(ctx.buffer, data, size);
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const kj::byte> TypeIdGenerator::finish()
|
||||
{
|
||||
if (!finished) {
|
||||
unsigned long used, free;
|
||||
|
||||
used = ctx.lo & 0x3f;
|
||||
|
||||
ctx.buffer[used++] = 0x80;
|
||||
|
||||
free = 64 - used;
|
||||
|
||||
if (free < 8) {
|
||||
memset(&ctx.buffer[used], 0, free);
|
||||
body(ctx.buffer, 64);
|
||||
used = 0;
|
||||
free = 64;
|
||||
}
|
||||
|
||||
memset(&ctx.buffer[used], 0, free - 8);
|
||||
|
||||
ctx.lo <<= 3;
|
||||
ctx.buffer[56] = ctx.lo;
|
||||
ctx.buffer[57] = ctx.lo >> 8;
|
||||
ctx.buffer[58] = ctx.lo >> 16;
|
||||
ctx.buffer[59] = ctx.lo >> 24;
|
||||
ctx.buffer[60] = ctx.hi;
|
||||
ctx.buffer[61] = ctx.hi >> 8;
|
||||
ctx.buffer[62] = ctx.hi >> 16;
|
||||
ctx.buffer[63] = ctx.hi >> 24;
|
||||
|
||||
body(ctx.buffer, 64);
|
||||
|
||||
// Store final result into ctx.buffer.
|
||||
ctx.buffer[0] = ctx.a;
|
||||
ctx.buffer[1] = ctx.a >> 8;
|
||||
ctx.buffer[2] = ctx.a >> 16;
|
||||
ctx.buffer[3] = ctx.a >> 24;
|
||||
ctx.buffer[4] = ctx.b;
|
||||
ctx.buffer[5] = ctx.b >> 8;
|
||||
ctx.buffer[6] = ctx.b >> 16;
|
||||
ctx.buffer[7] = ctx.b >> 24;
|
||||
ctx.buffer[8] = ctx.c;
|
||||
ctx.buffer[9] = ctx.c >> 8;
|
||||
ctx.buffer[10] = ctx.c >> 16;
|
||||
ctx.buffer[11] = ctx.c >> 24;
|
||||
ctx.buffer[12] = ctx.d;
|
||||
ctx.buffer[13] = ctx.d >> 8;
|
||||
ctx.buffer[14] = ctx.d >> 16;
|
||||
ctx.buffer[15] = ctx.d >> 24;
|
||||
|
||||
finished = true;
|
||||
}
|
||||
|
||||
return kj::arrayPtr(ctx.buffer, 16);
|
||||
}
|
||||
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
45
vendor/capnproto/src/capnp/compiler/type-id.h
vendored
Normal file
45
vendor/capnproto/src/capnp/compiler/type-id.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kj/string.h>
|
||||
#include <kj/array.h>
|
||||
#include <capnp/common.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
namespace compiler {
|
||||
|
||||
uint64_t generateChildId(uint64_t parentId, kj::StringPtr childName);
|
||||
uint64_t generateGroupId(uint64_t parentId, uint16_t groupIndex);
|
||||
// Generate a default type ID for various symbols. These are used only if the developer did not
|
||||
// specify an ID explicitly.
|
||||
//
|
||||
// The returned ID always has the most-significant bit set. The remaining bits are generated
|
||||
// pseudo-randomly from the input using an algorithm that should produce a uniform distribution of
|
||||
// IDs.
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
1830
vendor/capnproto/src/capnp/dynamic.c++
vendored
Normal file
1830
vendor/capnproto/src/capnp/dynamic.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1202
vendor/capnproto/src/capnp/dynamic.h
vendored
Normal file
1202
vendor/capnproto/src/capnp/dynamic.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
284
vendor/capnproto/src/capnp/endian.h
vendored
Normal file
284
vendor/capnproto/src/capnp/endian.h
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <inttypes.h>
|
||||
#include <string.h> // memcpy
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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 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
|
||||
#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 // !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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
337
vendor/capnproto/src/capnp/generated-header-support.h
vendored
Normal file
337
vendor/capnproto/src/capnp/generated-header-support.h
vendored
Normal file
@@ -0,0 +1,337 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#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>
|
||||
#include <kj/hash.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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.
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
|
||||
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, &rawSchema<T>().defaultBrand };
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BrandBindingFor_<T, Kind::STRUCT> {
|
||||
static constexpr RawBrandedSchema::Binding get(uint16_t listDepth) {
|
||||
return { 16, 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 <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>());
|
||||
}
|
||||
|
||||
|
||||
// TODO(cleanup): Unify ConstStruct and ConstList.
|
||||
template <typename T>
|
||||
class ConstStruct {
|
||||
public:
|
||||
ConstStruct() = delete;
|
||||
KJ_DISALLOW_COPY_AND_MOVE(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_AND_MOVE(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_AND_MOVE(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_AND_MOVE(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.
|
||||
|
||||
} // namespace capnp
|
||||
|
||||
#define CAPNP_NON_INT_CONSTEXPR_DECL_INIT(value) = value
|
||||
#define CAPNP_NON_INT_CONSTEXPR_DEF_INIT(value)
|
||||
|
||||
#define CAPNP_AUTO_IF_MSVC(...) __VA_ARGS__
|
||||
|
||||
// TODO(msvc): MSVC does not even expect constexprs to have definitions below C++17.
|
||||
#if (KJ_CPP_STD < 201703L) && !(defined(_MSC_VER) && !defined(__clang__))
|
||||
#define CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL 1
|
||||
#else
|
||||
#define CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL 0
|
||||
#endif
|
||||
|
||||
|
||||
#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; \
|
||||
}
|
||||
|
||||
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
#define CAPNP_DEFINE_ENUM(type, id) \
|
||||
constexpr uint64_t EnumInfo<type>::typeId; \
|
||||
constexpr ::capnp::_::RawSchema const* EnumInfo<type>::schema
|
||||
#else
|
||||
#define CAPNP_DEFINE_ENUM(type, id)
|
||||
#endif
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
CAPNP_END_HEADER
|
||||
3073
vendor/capnproto/src/capnp/layout.c++
vendored
Normal file
3073
vendor/capnproto/src/capnp/layout.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1105
vendor/capnproto/src/capnp/layout.h
vendored
Normal file
1105
vendor/capnproto/src/capnp/layout.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
548
vendor/capnproto/src/capnp/list.h
vendored
Normal file
548
vendor/capnproto/src/capnp/list.h
vendored
Normal file
@@ -0,0 +1,548 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "layout.h"
|
||||
#include "orphan.h"
|
||||
#include <initializer_list>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// By default this isn't compatible with STL algorithms. To add STL support either define
|
||||
// KJ_STD_COMPAT at the top of your compilation unit or include capnp/compat/std-iterator.h.
|
||||
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()); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
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()); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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()); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
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<List<T>>&& value) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(value.builder));
|
||||
}
|
||||
inline Orphan<List<T>> disown(uint index) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return Orphan<List<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()); }
|
||||
|
||||
inline MessageSize totalSize() const {
|
||||
return reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
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
|
||||
#include "compat/std-iterator.h"
|
||||
#endif // KJ_STD_COMPAT
|
||||
|
||||
CAPNP_END_HEADER
|
||||
211
vendor/capnproto/src/capnp/message.c++
vendored
Normal file
211
vendor/capnproto/src/capnp/message.c++
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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.
|
||||
|
||||
#define CAPNP_PRIVATE
|
||||
#include "message.h"
|
||||
#include <kj/debug.h>
|
||||
#include "arena.h"
|
||||
#include "orphan.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
MessageReader::MessageReader(ReaderOptions options): options(options), allocatedArena(false) {}
|
||||
MessageReader::~MessageReader() noexcept(false) {
|
||||
if (allocatedArena) {
|
||||
arena()->~ReaderArena();
|
||||
}
|
||||
}
|
||||
|
||||
AnyPointer::Reader MessageReader::getRootInternal() {
|
||||
if (!allocatedArena) {
|
||||
static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
|
||||
"arenaSpace is too small to hold a ReaderArena. Please increase it. This will break "
|
||||
"ABI compatibility.");
|
||||
kj::ctor(*arena(), this);
|
||||
allocatedArena = true;
|
||||
}
|
||||
|
||||
_::SegmentReader* segment = arena()->tryGetSegment(_::SegmentId(0));
|
||||
KJ_REQUIRE(segment != nullptr &&
|
||||
segment->checkObject(segment->getStartPtr(), ONE * WORDS),
|
||||
"Message did not contain a root pointer.") {
|
||||
return AnyPointer::Reader();
|
||||
}
|
||||
|
||||
return AnyPointer::Reader(_::PointerReader::getRoot(
|
||||
segment,
|
||||
segment->getStartPtr(), options.nestingLimit));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
MessageBuilder::MessageBuilder(): allocatedArena(false) {}
|
||||
|
||||
MessageBuilder::~MessageBuilder() noexcept(false) {
|
||||
if (allocatedArena) {
|
||||
kj::dtor(*arena());
|
||||
}
|
||||
}
|
||||
|
||||
_::SegmentBuilder* MessageBuilder::getRootSegment() {
|
||||
if (allocatedArena) {
|
||||
return arena()->getSegment(_::SegmentId(0));
|
||||
} else {
|
||||
static_assert(sizeof(_::BuilderArena) <= sizeof(arenaSpace),
|
||||
"arenaSpace is too small to hold a BuilderArena. Please increase it.");
|
||||
kj::ctor(*arena(), this);
|
||||
allocatedArena = true;
|
||||
|
||||
auto allocation = arena()->allocate(POINTER_SIZE_IN_WORDS);
|
||||
|
||||
KJ_ASSERT(allocation.segment->getSegmentId() == _::SegmentId(0),
|
||||
"First allocated word of new arena was not in segment ID 0.");
|
||||
KJ_ASSERT(allocation.words == allocation.segment->getPtrUnchecked(ZERO * WORDS),
|
||||
"First allocated word of new arena was not the first word in its segment.");
|
||||
return allocation.segment;
|
||||
}
|
||||
}
|
||||
|
||||
AnyPointer::Builder MessageBuilder::getRootInternal() {
|
||||
_::SegmentBuilder* rootSegment = getRootSegment();
|
||||
return AnyPointer::Builder(_::PointerBuilder::getRoot(
|
||||
rootSegment, rootSegment->getPtrUnchecked(ZERO * WORDS)));
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const kj::ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
|
||||
if (allocatedArena) {
|
||||
return arena()->getSegmentsForOutput();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Orphanage MessageBuilder::getOrphanage() {
|
||||
// We must ensure that the arena and root pointer have been allocated before the Orphanage
|
||||
// can be used.
|
||||
if (!allocatedArena) getRootSegment();
|
||||
|
||||
return Orphanage(arena());
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
MallocMessageBuilder::MallocMessageBuilder(
|
||||
uint firstSegmentWords, AllocationStrategy allocationStrategy)
|
||||
: nextSize(firstSegmentWords), allocationStrategy(allocationStrategy),
|
||||
ownFirstSegment(true), returnedFirstSegment(false), firstSegment(nullptr) {}
|
||||
|
||||
MallocMessageBuilder::MallocMessageBuilder(
|
||||
kj::ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy)
|
||||
: nextSize(firstSegment.size()), allocationStrategy(allocationStrategy),
|
||||
ownFirstSegment(false), returnedFirstSegment(false), firstSegment(firstSegment.begin()) {
|
||||
KJ_REQUIRE(firstSegment.size() > 0, "First segment size must be non-zero.");
|
||||
|
||||
// Checking just the first word should catch most cases of failing to zero the segment.
|
||||
KJ_REQUIRE(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
|
||||
"First segment must be zeroed.");
|
||||
}
|
||||
|
||||
MallocMessageBuilder::~MallocMessageBuilder() noexcept(false) {
|
||||
if (returnedFirstSegment) {
|
||||
if (ownFirstSegment) {
|
||||
free(firstSegment);
|
||||
} else {
|
||||
// Must zero first segment.
|
||||
kj::ArrayPtr<const kj::ArrayPtr<const word>> segments = getSegmentsForOutput();
|
||||
if (segments.size() > 0) {
|
||||
KJ_ASSERT(segments[0].begin() == firstSegment,
|
||||
"First segment in getSegmentsForOutput() is not the first segment allocated?");
|
||||
memset(firstSegment, 0, segments[0].size() * sizeof(word));
|
||||
}
|
||||
}
|
||||
|
||||
for (void* ptr: moreSegments) {
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kj::ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
|
||||
KJ_REQUIRE(bounded(minimumSize) * WORDS <= MAX_SEGMENT_WORDS,
|
||||
"MallocMessageBuilder asked to allocate segment above maximum serializable size.");
|
||||
KJ_ASSERT(bounded(nextSize) * WORDS <= MAX_SEGMENT_WORDS,
|
||||
"MallocMessageBuilder nextSize out of bounds.");
|
||||
|
||||
if (!returnedFirstSegment && !ownFirstSegment) {
|
||||
kj::ArrayPtr<word> result = kj::arrayPtr(reinterpret_cast<word*>(firstSegment), nextSize);
|
||||
if (result.size() >= minimumSize) {
|
||||
returnedFirstSegment = true;
|
||||
return result;
|
||||
}
|
||||
// If the provided first segment wasn't big enough, we discard it and proceed to allocate
|
||||
// our own. This never happens in practice since minimumSize is always 1 for the first
|
||||
// segment.
|
||||
ownFirstSegment = true;
|
||||
}
|
||||
|
||||
uint size = kj::max(minimumSize, nextSize);
|
||||
|
||||
void* result = calloc(size, sizeof(word));
|
||||
if (result == nullptr) {
|
||||
KJ_FAIL_SYSCALL("calloc(size, sizeof(word))", ENOMEM, size);
|
||||
}
|
||||
|
||||
if (!returnedFirstSegment) {
|
||||
firstSegment = result;
|
||||
returnedFirstSegment = true;
|
||||
|
||||
// After the first segment, we want nextSize to equal the total size allocated so far.
|
||||
if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) nextSize = size;
|
||||
} else {
|
||||
moreSegments.add(result);
|
||||
if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) {
|
||||
// set nextSize = min(nextSize+size, MAX_SEGMENT_WORDS)
|
||||
// while protecting against possible overflow of (nextSize+size)
|
||||
nextSize = (size <= unbound(MAX_SEGMENT_WORDS / WORDS) - nextSize)
|
||||
? nextSize + size : unbound(MAX_SEGMENT_WORDS / WORDS);
|
||||
}
|
||||
}
|
||||
|
||||
return kj::arrayPtr(reinterpret_cast<word*>(result), size);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
FlatMessageBuilder::FlatMessageBuilder(kj::ArrayPtr<word> array): array(array), allocated(false) {}
|
||||
FlatMessageBuilder::~FlatMessageBuilder() noexcept(false) {}
|
||||
|
||||
void FlatMessageBuilder::requireFilled() {
|
||||
KJ_REQUIRE(getSegmentsForOutput()[0].end() == array.end(),
|
||||
"FlatMessageBuilder's buffer was too large.");
|
||||
}
|
||||
|
||||
kj::ArrayPtr<word> FlatMessageBuilder::allocateSegment(uint minimumSize) {
|
||||
KJ_REQUIRE(!allocated, "FlatMessageBuilder's buffer was not large enough.");
|
||||
allocated = true;
|
||||
return array;
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
464
vendor/capnproto/src/capnp/message.h
vendored
Normal file
464
vendor/capnproto/src/capnp/message.h
vendored
Normal file
@@ -0,0 +1,464 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kj/common.h>
|
||||
#include <kj/memory.h>
|
||||
#include <kj/mutex.h>
|
||||
#include <kj/debug.h>
|
||||
#include <kj/vector.h>
|
||||
#include "common.h"
|
||||
#include "layout.h"
|
||||
#include "any.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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.
|
||||
//
|
||||
// A message reader obtains segments through getSegment().
|
||||
|
||||
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.
|
||||
|
||||
private:
|
||||
ReaderOptions options;
|
||||
|
||||
#if defined(__EMSCRIPTEN__) || (defined(__APPLE__) && (defined(__ppc__) || defined(__i386__)))
|
||||
static constexpr size_t arenaSpacePadding = 19;
|
||||
#else
|
||||
static constexpr size_t arenaSpacePadding = 18;
|
||||
#endif
|
||||
|
||||
// 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
|
||||
// other 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.
|
||||
alignas(8) void* arenaSpace[arenaSpacePadding + 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_AND_MOVE(MessageBuilder);
|
||||
|
||||
virtual kj::ArrayPtr<word> allocateSegment(uint minimumSize) = 0;
|
||||
// Allocates an array of at least the given number of zero'd 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.
|
||||
//
|
||||
// allocateSegment() is responsible for zeroing the memory before returning. This is required
|
||||
// because otherwise the Cap'n Proto implementation would have to zero the memory anyway, and
|
||||
// many allocators are able to provide already-zero'd memory more efficiently.
|
||||
|
||||
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();
|
||||
|
||||
private:
|
||||
alignas(8) 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?
|
||||
|
||||
template <typename Reader, typename = FromReader<Reader>>
|
||||
kj::Own<kj::Decay<Reader>> clone(Reader&& reader);
|
||||
// Make a deep copy of the given Reader on the heap, producing an owned pointer.
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
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_AND_MOVE(MallocMessageBuilder);
|
||||
virtual ~MallocMessageBuilder() noexcept(false);
|
||||
|
||||
virtual kj::ArrayPtr<word> allocateSegment(uint minimumSize) override;
|
||||
|
||||
private:
|
||||
uint nextSize;
|
||||
AllocationStrategy allocationStrategy;
|
||||
|
||||
bool ownFirstSegment;
|
||||
bool returnedFirstSegment;
|
||||
|
||||
void* firstSegment;
|
||||
kj::Vector<void*> 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_AND_MOVE(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 Reader, typename>
|
||||
kj::Own<kj::Decay<Reader>> clone(Reader&& reader) {
|
||||
auto size = reader.totalSize();
|
||||
auto buffer = kj::heapArray<capnp::word>(size.wordCount + 1);
|
||||
memset(buffer.asBytes().begin(), 0, buffer.asBytes().size());
|
||||
copyToUnchecked(reader, buffer);
|
||||
auto result = readMessageUnchecked<FromReader<Reader>>(buffer.begin());
|
||||
return kj::attachVal(result, kj::mv(buffer));
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
308
vendor/capnproto/src/capnp/orphan.h
vendored
Normal file
308
vendor/capnproto/src/capnp/orphan.h
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "layout.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
|
||||
class StructSchema;
|
||||
class ListSchema;
|
||||
struct DynamicStruct;
|
||||
struct DynamicList;
|
||||
|
||||
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; }
|
||||
|
||||
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.
|
||||
|
||||
private:
|
||||
_::BuilderArena* arena;
|
||||
|
||||
inline explicit Orphanage(_::BuilderArena* arena)
|
||||
: arena(arena) {}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details.
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, Kind = CAPNP_KIND(T)>
|
||||
struct OrphanGetImpl;
|
||||
|
||||
template <typename T>
|
||||
struct OrphanGetImpl<T, Kind::PRIMITIVE> {
|
||||
|
||||
};
|
||||
|
||||
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>()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // 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>
|
||||
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());
|
||||
}
|
||||
|
||||
template <typename RootType>
|
||||
Orphan<RootType> Orphanage::newOrphan() const {
|
||||
return Orphan<RootType>(_::OrphanBuilder::initStruct(arena, _::structSize<RootType>()));
|
||||
}
|
||||
|
||||
template <typename T, Kind k>
|
||||
struct Orphanage::NewOrphanListImpl<List<T, k>> {
|
||||
static inline _::OrphanBuilder apply(
|
||||
_::BuilderArena* arena, uint size) {
|
||||
return _::OrphanBuilder::initList(
|
||||
arena, bounded(size) * ELEMENTS, _::ElementSizeForType<T>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Orphanage::NewOrphanListImpl<List<T, Kind::STRUCT>> {
|
||||
static inline _::OrphanBuilder apply(
|
||||
_::BuilderArena* arena, uint size) {
|
||||
return _::OrphanBuilder::initStructList(
|
||||
arena, bounded(size) * ELEMENTS, _::structSize<T>());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::NewOrphanListImpl<Text> {
|
||||
static inline _::OrphanBuilder apply(
|
||||
_::BuilderArena* arena, uint size) {
|
||||
return _::OrphanBuilder::initText(arena, bounded(size) * BYTES);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Orphanage::NewOrphanListImpl<Data> {
|
||||
static inline _::OrphanBuilder apply(
|
||||
_::BuilderArena* arena, uint size) {
|
||||
return _::OrphanBuilder::initData(arena, bounded(size) * BYTES);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RootType>
|
||||
Orphan<RootType> Orphanage::newOrphan(uint size) const {
|
||||
return Orphan<RootType>(NewOrphanListImpl<RootType>::apply(arena, 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, GetInnerReader<FromReader<Reader>>::apply(copyFrom)));
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
151
vendor/capnproto/src/capnp/pointer-helpers.h
vendored
Normal file
151
vendor/capnproto/src/capnp/pointer-helpers.h
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "layout.h"
|
||||
#include "list.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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 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 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 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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
44
vendor/capnproto/src/capnp/pretty-print.h
vendored
Normal file
44
vendor/capnproto/src/capnp/pretty-print.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dynamic.h"
|
||||
#include <kj/string-tree.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
223
vendor/capnproto/src/capnp/raw-schema.h
vendored
Normal file
223
vendor/capnproto/src/capnp/raw-schema.h
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h" // for uint and friends
|
||||
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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!).
|
||||
|
||||
CONST_TYPE = 5
|
||||
// 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__ || defined(__clang__)
|
||||
const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_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__ || defined(__clang__)
|
||||
const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_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`.
|
||||
|
||||
bool mayContainCapabilities = true;
|
||||
// See StructSchema::mayContainCapabilities.
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
2099
vendor/capnproto/src/capnp/schema-loader.c++
vendored
Normal file
2099
vendor/capnproto/src/capnp/schema-loader.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
183
vendor/capnproto/src/capnp/schema-loader.h
vendored
Normal file
183
vendor/capnproto/src/capnp/schema-loader.h
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "schema.h"
|
||||
#include <kj/memory.h>
|
||||
#include <kj/mutex.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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_AND_MOVE(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.
|
||||
|
||||
void computeOptimizationHints();
|
||||
// Call after all interesting schemas have been loaded to compute optimization hints. In
|
||||
// particular, this initializes `hasNoCapabilities` for every struct type. Before this is called,
|
||||
// that value is initialized to false for all types (which ensures correct behavior but does not
|
||||
// allow the optimization).
|
||||
//
|
||||
// If any loaded struct types contain fields of types for which no schema has been loaded, they
|
||||
// will be presumed to possibly contain capabilities. `LazyLoadCallback` will NOT be invoked to
|
||||
// load any types that haven't been loaded yet.
|
||||
//
|
||||
// TODO(someday): Perhaps we could dynamically initialize the hints on-demand, but it would be
|
||||
// much more work to implement.
|
||||
|
||||
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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
406
vendor/capnproto/src/capnp/schema-parser.c++
vendored
Normal file
406
vendor/capnproto/src/capnp/schema-parser.c++
vendored
Normal file
@@ -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.
|
||||
|
||||
#include "schema-parser.h"
|
||||
#include "message.h"
|
||||
#include <capnp/compiler/compiler.h>
|
||||
#include <capnp/compiler/lexer.capnp.h>
|
||||
#include <capnp/compiler/lexer.h>
|
||||
#include <capnp/compiler/grammar.capnp.h>
|
||||
#include <capnp/compiler/parser.h>
|
||||
#include <unordered_map>
|
||||
#include <kj/mutex.h>
|
||||
#include <kj/vector.h>
|
||||
#include <kj/debug.h>
|
||||
#include <kj/io.h>
|
||||
#include <map>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
size_t findLargestElementBefore(const kj::Vector<T>& vec, const T& key) {
|
||||
KJ_REQUIRE(vec.size() > 0 && vec[0] <= key);
|
||||
|
||||
size_t lower = 0;
|
||||
size_t upper = vec.size();
|
||||
|
||||
while (upper - lower > 1) {
|
||||
size_t mid = (lower + upper) / 2;
|
||||
if (vec[mid] > key) {
|
||||
upper = mid;
|
||||
} else {
|
||||
lower = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return lower;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
class SchemaParser::ModuleImpl final: public compiler::Module {
|
||||
public:
|
||||
ModuleImpl(const SchemaParser& parser, kj::Own<const SchemaFile>&& file)
|
||||
: parser(parser), file(kj::mv(file)) {}
|
||||
|
||||
kj::StringPtr getSourceName() override {
|
||||
return file->getDisplayName();
|
||||
}
|
||||
|
||||
Orphan<compiler::ParsedFile> loadContent(Orphanage orphanage) override {
|
||||
kj::Array<const char> content = file->readContent();
|
||||
|
||||
lineBreaks.get([&](kj::SpaceFor<kj::Vector<uint>>& space) {
|
||||
auto vec = space.construct(content.size() / 40);
|
||||
vec->add(0);
|
||||
for (const char* pos = content.begin(); pos < content.end(); ++pos) {
|
||||
if (*pos == '\n') {
|
||||
vec->add(pos + 1 - content.begin());
|
||||
}
|
||||
}
|
||||
return vec;
|
||||
});
|
||||
|
||||
MallocMessageBuilder lexedBuilder;
|
||||
auto statements = lexedBuilder.initRoot<compiler::LexedStatements>();
|
||||
compiler::lex(content, statements, *this);
|
||||
|
||||
auto parsed = orphanage.newOrphan<compiler::ParsedFile>();
|
||||
compiler::parseFile(statements.getStatements(), parsed.get(), *this);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
kj::Maybe<Module&> importRelative(kj::StringPtr importPath) override {
|
||||
KJ_IF_MAYBE(importedFile, file->import(importPath)) {
|
||||
return parser.getModuleImpl(kj::mv(*importedFile));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr embedPath) override {
|
||||
KJ_IF_MAYBE(importedFile, file->import(embedPath)) {
|
||||
return importedFile->get()->readContent().releaseAsBytes();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
|
||||
auto& lines = lineBreaks.get(
|
||||
[](kj::SpaceFor<kj::Vector<uint>>& space) {
|
||||
KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called.");
|
||||
return space.construct();
|
||||
});
|
||||
|
||||
// TODO(someday): This counts tabs as single characters. Do we care?
|
||||
uint startLine = findLargestElementBefore(lines, startByte);
|
||||
uint startCol = startByte - lines[startLine];
|
||||
uint endLine = findLargestElementBefore(lines, endByte);
|
||||
uint endCol = endByte - lines[endLine];
|
||||
|
||||
file->reportError(
|
||||
SchemaFile::SourcePos { startByte, startLine, startCol },
|
||||
SchemaFile::SourcePos { endByte, endLine, endCol },
|
||||
message);
|
||||
|
||||
// We intentionally only set hadErrors true if reportError() didn't throw.
|
||||
parser.hadErrors = true;
|
||||
}
|
||||
|
||||
bool hadErrors() override {
|
||||
return parser.hadErrors;
|
||||
}
|
||||
|
||||
private:
|
||||
const SchemaParser& parser;
|
||||
kj::Own<const SchemaFile> file;
|
||||
|
||||
kj::Lazy<kj::Vector<uint>> lineBreaks;
|
||||
// Byte offsets of the first byte in each source line. The first element is always zero.
|
||||
// Initialized the first time the module is loaded.
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
struct SchemaFileHash {
|
||||
inline size_t operator()(const SchemaFile* f) const {
|
||||
return f->hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
struct SchemaFileEq {
|
||||
inline bool operator()(const SchemaFile* a, const SchemaFile* b) const {
|
||||
return *a == *b;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct SchemaParser::DiskFileCompat {
|
||||
// Stuff we only create if parseDiskFile() is ever called, in order to translate that call into
|
||||
// KJ filesystem API calls.
|
||||
|
||||
kj::Own<kj::Filesystem> ownFs;
|
||||
kj::Filesystem& fs;
|
||||
|
||||
struct ImportDir {
|
||||
kj::String pathStr;
|
||||
kj::Path path;
|
||||
kj::Own<const kj::ReadableDirectory> dir;
|
||||
};
|
||||
std::map<kj::StringPtr, ImportDir> cachedImportDirs;
|
||||
|
||||
std::map<std::pair<const kj::StringPtr*, size_t>, kj::Array<const kj::ReadableDirectory*>>
|
||||
cachedImportPaths;
|
||||
|
||||
DiskFileCompat(): ownFs(kj::newDiskFilesystem()), fs(*ownFs) {}
|
||||
};
|
||||
|
||||
struct SchemaParser::Impl {
|
||||
typedef std::unordered_map<
|
||||
const SchemaFile*, kj::Own<ModuleImpl>, SchemaFileHash, SchemaFileEq> FileMap;
|
||||
kj::MutexGuarded<FileMap> fileMap;
|
||||
compiler::Compiler compiler;
|
||||
|
||||
kj::MutexGuarded<kj::Maybe<DiskFileCompat>> compat;
|
||||
};
|
||||
|
||||
SchemaParser::SchemaParser(): impl(kj::heap<Impl>()) {}
|
||||
SchemaParser::~SchemaParser() noexcept(false) {}
|
||||
|
||||
ParsedSchema SchemaParser::parseDiskFile(
|
||||
kj::StringPtr displayName, kj::StringPtr diskPath,
|
||||
kj::ArrayPtr<const kj::StringPtr> importPath) const {
|
||||
auto lock = impl->compat.lockExclusive();
|
||||
DiskFileCompat* compat;
|
||||
KJ_IF_MAYBE(c, *lock) {
|
||||
compat = c;
|
||||
} else {
|
||||
compat = &lock->emplace();
|
||||
}
|
||||
|
||||
auto& root = compat->fs.getRoot();
|
||||
auto cwd = compat->fs.getCurrentPath();
|
||||
|
||||
const kj::ReadableDirectory* baseDir = &root;
|
||||
kj::Path path = cwd.evalNative(diskPath);
|
||||
|
||||
kj::ArrayPtr<const kj::ReadableDirectory* const> translatedImportPath = nullptr;
|
||||
|
||||
if (importPath.size() > 0) {
|
||||
auto importPathKey = std::make_pair(importPath.begin(), importPath.size());
|
||||
auto& slot = compat->cachedImportPaths[importPathKey];
|
||||
|
||||
if (slot == nullptr) {
|
||||
slot = KJ_MAP(path, importPath) -> const kj::ReadableDirectory* {
|
||||
auto iter = compat->cachedImportDirs.find(path);
|
||||
if (iter != compat->cachedImportDirs.end()) {
|
||||
return iter->second.dir;
|
||||
}
|
||||
|
||||
auto parsed = cwd.evalNative(path);
|
||||
kj::Own<const kj::ReadableDirectory> dir;
|
||||
KJ_IF_MAYBE(d, root.tryOpenSubdir(parsed)) {
|
||||
dir = kj::mv(*d);
|
||||
} else {
|
||||
// Ignore paths that don't exist.
|
||||
dir = kj::newInMemoryDirectory(kj::nullClock());
|
||||
}
|
||||
|
||||
const kj::ReadableDirectory* result = dir;
|
||||
|
||||
kj::StringPtr pathRef = path;
|
||||
KJ_ASSERT(compat->cachedImportDirs.insert(std::make_pair(pathRef,
|
||||
DiskFileCompat::ImportDir { kj::str(path), kj::mv(parsed), kj::mv(dir) })).second);
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
translatedImportPath = slot;
|
||||
|
||||
// Check if `path` appears to be inside any of the import path directories. If so, adjust
|
||||
// to be relative to that directory rather than absolute.
|
||||
kj::Maybe<DiskFileCompat::ImportDir&> matchedImportDir;
|
||||
size_t bestMatchLength = 0;
|
||||
for (auto importDir: importPath) {
|
||||
auto iter = compat->cachedImportDirs.find(importDir);
|
||||
KJ_ASSERT(iter != compat->cachedImportDirs.end());
|
||||
|
||||
if (path.startsWith(iter->second.path)) {
|
||||
// Looks like we're trying to load a file from inside this import path. Treat the import
|
||||
// path as the base directory.
|
||||
if (iter->second.path.size() > bestMatchLength) {
|
||||
bestMatchLength = iter->second.path.size();
|
||||
matchedImportDir = iter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KJ_IF_MAYBE(match, matchedImportDir) {
|
||||
baseDir = match->dir;
|
||||
path = path.slice(match->path.size(), path.size()).clone();
|
||||
}
|
||||
}
|
||||
|
||||
return parseFile(SchemaFile::newFromDirectory(
|
||||
*baseDir, kj::mv(path), translatedImportPath, kj::str(displayName)));
|
||||
}
|
||||
|
||||
ParsedSchema SchemaParser::parseFile(kj::Own<SchemaFile>&& file) const {
|
||||
KJ_DEFER(impl->compiler.clearWorkspace());
|
||||
uint64_t id = impl->compiler.add(getModuleImpl(kj::mv(file))).getId();
|
||||
impl->compiler.eagerlyCompile(id,
|
||||
compiler::Compiler::NODE | compiler::Compiler::CHILDREN |
|
||||
compiler::Compiler::DEPENDENCIES | compiler::Compiler::DEPENDENCY_DEPENDENCIES);
|
||||
return ParsedSchema(impl->compiler.getLoader().get(id), *this);
|
||||
}
|
||||
|
||||
SchemaParser::ModuleImpl& SchemaParser::getModuleImpl(kj::Own<SchemaFile>&& file) const {
|
||||
auto lock = impl->fileMap.lockExclusive();
|
||||
|
||||
auto insertResult = lock->insert(std::make_pair(file.get(), kj::Own<ModuleImpl>()));
|
||||
if (insertResult.second) {
|
||||
// This is a newly-inserted entry. Construct the ModuleImpl.
|
||||
insertResult.first->second = kj::heap<ModuleImpl>(*this, kj::mv(file));
|
||||
}
|
||||
return *insertResult.first->second;
|
||||
}
|
||||
|
||||
kj::Maybe<ParsedSchema> ParsedSchema::findNested(kj::StringPtr name) const {
|
||||
return parser->impl->compiler.lookup(getProto().getId(), name).map(
|
||||
[this](uint64_t childId) {
|
||||
return ParsedSchema(parser->impl->compiler.getLoader().get(childId), *parser);
|
||||
});
|
||||
}
|
||||
|
||||
ParsedSchema ParsedSchema::getNested(kj::StringPtr nestedName) const {
|
||||
KJ_IF_MAYBE(nested, findNested(nestedName)) {
|
||||
return *nested;
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("no such nested declaration", getProto().getDisplayName(), nestedName);
|
||||
}
|
||||
}
|
||||
|
||||
class SchemaFile::DiskSchemaFile final: public SchemaFile {
|
||||
public:
|
||||
DiskSchemaFile(const kj::ReadableDirectory& baseDir, kj::Path pathParam,
|
||||
kj::ArrayPtr<const kj::ReadableDirectory* const> importPath,
|
||||
kj::Own<const kj::ReadableFile> file,
|
||||
kj::Maybe<kj::String> displayNameOverride)
|
||||
: baseDir(baseDir), path(kj::mv(pathParam)), importPath(importPath), file(kj::mv(file)) {
|
||||
KJ_IF_MAYBE(dn, displayNameOverride) {
|
||||
displayName = kj::mv(*dn);
|
||||
displayNameOverridden = true;
|
||||
} else {
|
||||
displayName = path.toString();
|
||||
displayNameOverridden = false;
|
||||
}
|
||||
}
|
||||
|
||||
kj::StringPtr getDisplayName() const override {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
kj::Array<const char> readContent() const override {
|
||||
return file->mmap(0, file->stat().size).releaseAsChars();
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Own<SchemaFile>> import(kj::StringPtr target) const override {
|
||||
if (target.startsWith("/")) {
|
||||
auto parsed = kj::Path::parse(target.slice(1));
|
||||
for (auto candidate: importPath) {
|
||||
KJ_IF_MAYBE(newFile, candidate->tryOpenFile(parsed)) {
|
||||
return kj::implicitCast<kj::Own<SchemaFile>>(kj::heap<DiskSchemaFile>(
|
||||
*candidate, kj::mv(parsed), importPath, kj::mv(*newFile), nullptr));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
} else {
|
||||
auto parsed = path.parent().eval(target);
|
||||
|
||||
kj::Maybe<kj::String> displayNameOverride;
|
||||
if (displayNameOverridden) {
|
||||
// Try to create a consistent display name override for the imported file. This is for
|
||||
// backwards-compatibility only -- display names are only overridden when using the
|
||||
// deprecated parseDiskFile() interface.
|
||||
kj::runCatchingExceptions([&]() {
|
||||
displayNameOverride = kj::Path::parse(displayName).parent().eval(target).toString();
|
||||
});
|
||||
}
|
||||
|
||||
KJ_IF_MAYBE(newFile, baseDir.tryOpenFile(parsed)) {
|
||||
return kj::implicitCast<kj::Own<SchemaFile>>(kj::heap<DiskSchemaFile>(
|
||||
baseDir, kj::mv(parsed), importPath, kj::mv(*newFile), kj::mv(displayNameOverride)));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const SchemaFile& other) const override {
|
||||
auto& other2 = kj::downcast<const DiskSchemaFile>(other);
|
||||
return &baseDir == &other2.baseDir && path == other2.path;
|
||||
}
|
||||
size_t hashCode() const override {
|
||||
// djb hash with xor
|
||||
// TODO(someday): Add hashing library to KJ.
|
||||
size_t result = reinterpret_cast<uintptr_t>(&baseDir);
|
||||
for (auto& part: path) {
|
||||
for (char c: part) {
|
||||
result = (result * 33) ^ c;
|
||||
}
|
||||
result = (result * 33) ^ '/';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void reportError(SourcePos start, SourcePos end, kj::StringPtr message) const override {
|
||||
kj::getExceptionCallback().onRecoverableException(kj::Exception(
|
||||
kj::Exception::Type::FAILED, path.toString(), start.line,
|
||||
kj::heapString(message)));
|
||||
}
|
||||
|
||||
private:
|
||||
const kj::ReadableDirectory& baseDir;
|
||||
kj::Path path;
|
||||
kj::ArrayPtr<const kj::ReadableDirectory* const> importPath;
|
||||
kj::Own<const kj::ReadableFile> file;
|
||||
kj::String displayName;
|
||||
bool displayNameOverridden;
|
||||
};
|
||||
|
||||
kj::Own<SchemaFile> SchemaFile::newFromDirectory(
|
||||
const kj::ReadableDirectory& baseDir, kj::Path path,
|
||||
kj::ArrayPtr<const kj::ReadableDirectory* const> importPath,
|
||||
kj::Maybe<kj::String> displayNameOverride) {
|
||||
return kj::heap<DiskSchemaFile>(baseDir, kj::mv(path), importPath, baseDir.openFile(path),
|
||||
kj::mv(displayNameOverride));
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
153
vendor/capnproto/src/capnp/schema-parser.h
vendored
Normal file
153
vendor/capnproto/src/capnp/schema-parser.h
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "schema-loader.h"
|
||||
#include <kj/string.h>
|
||||
#include <kj/filesystem.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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 from disk. Relative imports and embeds can access paths outside its directory.
|
||||
// Absolute schema imports are searched in importPath.
|
||||
|
||||
ParsedSchema parseFile(kj::Own<SchemaFile>&& file) const;
|
||||
// Advanced interface for parsing a file that may or may not be located in any global namespace.
|
||||
//
|
||||
// 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.
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
struct DiskFileCompat;
|
||||
class ModuleImpl;
|
||||
kj::Own<Impl> impl;
|
||||
mutable bool hadErrors = false;
|
||||
|
||||
ModuleImpl& getModuleImpl(kj::Own<SchemaFile>&& file) const;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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:
|
||||
// Note: Cap'n Proto 0.6.x and below had classes FileReader and DiskFileReader and a method
|
||||
// newDiskFile() defined here. These were removed when SchemaParser was transitioned to use the
|
||||
// KJ filesystem API. You should be able to get the same effect by subclassing
|
||||
// kj::ReadableDirectory, or using kj::newInMemoryDirectory().
|
||||
|
||||
static kj::Own<SchemaFile> newFromDirectory(
|
||||
const kj::ReadableDirectory& baseDir, kj::Path path,
|
||||
kj::ArrayPtr<const kj::ReadableDirectory* const> importPath,
|
||||
kj::Maybe<kj::String> displayNameOverride = nullptr);
|
||||
// Construct a SchemaFile representing a file in a kj::ReadableDirectory. This is used to
|
||||
// resolve imports relative to a filesystem directory.
|
||||
//
|
||||
// The SchemaFile compares equal to any other SchemaFile that has exactly the same `baseDir`
|
||||
// object (by identity) and `path` (by value).
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// 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 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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
796
vendor/capnproto/src/capnp/schema.c++
vendored
Normal file
796
vendor/capnproto/src/capnp/schema.c++
vendored
Normal file
@@ -0,0 +1,796 @@
|
||||
// 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 "schema.h"
|
||||
#include "message.h"
|
||||
#include <kj/debug.h>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
namespace schema {
|
||||
uint KJ_HASHCODE(Type::Which w) { return kj::hashCode(static_cast<uint16_t>(w)); }
|
||||
// TODO(cleanup): Cap'n Proto does not declare stringifiers nor hashers for `Which` enums, unlike
|
||||
// all other enums. Fix that and remove this.
|
||||
}
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
// Null schemas generated using the below schema file with:
|
||||
//
|
||||
// capnp eval -Isrc null-schemas.capnp node --flat |
|
||||
// hexdump -v -e '8/1 "0x%02x, "' -e '1/8 "\n"'; echo
|
||||
//
|
||||
// I totally don't understand hexdump format strings and came up with this command based on trial
|
||||
// and error.
|
||||
//
|
||||
// @0x879863d4b2cc4a1e;
|
||||
//
|
||||
// using Node = import "/capnp/schema.capnp".Node;
|
||||
//
|
||||
// const node :Node = (
|
||||
// id = 0x0000000000000000,
|
||||
// displayName = "(null schema)");
|
||||
//
|
||||
// const struct :Node = (
|
||||
// id = 0x0000000000000001,
|
||||
// displayName = "(null struct schema)",
|
||||
// struct = (
|
||||
// dataWordCount = 0,
|
||||
// pointerCount = 0,
|
||||
// preferredListEncoding = empty));
|
||||
//
|
||||
// const enum :Node = (
|
||||
// id = 0x0000000000000002,
|
||||
// displayName = "(null enum schema)",
|
||||
// enum = ());
|
||||
//
|
||||
// const interface :Node = (
|
||||
// id = 0x0000000000000003,
|
||||
// displayName = "(null interface schema)",
|
||||
// interface = ());
|
||||
//
|
||||
// const const :Node = (
|
||||
// id = 0x0000000000000004,
|
||||
// displayName = "(null const schema)",
|
||||
// const = (type = (void = void), value = (void = void)));
|
||||
|
||||
static const AlignedData<13> NULL_SCHEMA_BYTES = {{
|
||||
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, // union discriminant intentionally mangled
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x63,
|
||||
0x68, 0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
const RawSchema NULL_SCHEMA = {
|
||||
0x0000000000000000, NULL_SCHEMA_BYTES.words, 13,
|
||||
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
||||
{ &NULL_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
|
||||
static const AlignedData<14> NULL_STRUCT_SCHEMA_BYTES = {{
|
||||
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x74,
|
||||
0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x63, 0x68,
|
||||
0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
const RawSchema NULL_STRUCT_SCHEMA = {
|
||||
0x0000000000000001, NULL_STRUCT_SCHEMA_BYTES.words, 14,
|
||||
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
||||
{ &NULL_STRUCT_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
|
||||
static const AlignedData<14> NULL_ENUM_SCHEMA_BYTES = {{
|
||||
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x65, 0x6e,
|
||||
0x75, 0x6d, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
|
||||
0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
const RawSchema NULL_ENUM_SCHEMA = {
|
||||
0x0000000000000002, NULL_ENUM_SCHEMA_BYTES.words, 14,
|
||||
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
||||
{ &NULL_ENUM_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
|
||||
static const AlignedData<20> NULL_CONST_SCHEMA_BYTES = {{
|
||||
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
|
||||
0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
|
||||
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x63, 0x6f,
|
||||
0x6e, 0x73, 0x74, 0x20, 0x73, 0x63, 0x68, 0x65,
|
||||
0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
const RawSchema NULL_CONST_SCHEMA = {
|
||||
0x0000000000000004, NULL_CONST_SCHEMA_BYTES.words, 20,
|
||||
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
||||
{ &NULL_CONST_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
schema::Node::Reader Schema::getProto() const {
|
||||
return readMessageUnchecked<schema::Node>(raw->generic->encodedNode);
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const word> Schema::asUncheckedMessage() const {
|
||||
return kj::arrayPtr(raw->generic->encodedNode, raw->generic->encodedSize);
|
||||
}
|
||||
|
||||
Schema Schema::getDependency(uint64_t id, uint location) const {
|
||||
{
|
||||
// Binary search dependency list.
|
||||
uint lower = 0;
|
||||
uint upper = raw->dependencyCount;
|
||||
|
||||
while (lower < upper) {
|
||||
uint mid = (lower + upper) / 2;
|
||||
|
||||
auto candidate = raw->dependencies[mid];
|
||||
if (candidate.location == location) {
|
||||
candidate.schema->ensureInitialized();
|
||||
return Schema(candidate.schema);
|
||||
} else if (candidate.location < location) {
|
||||
lower = mid + 1;
|
||||
} else {
|
||||
upper = mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint lower = 0;
|
||||
uint upper = raw->generic->dependencyCount;
|
||||
|
||||
while (lower < upper) {
|
||||
uint mid = (lower + upper) / 2;
|
||||
|
||||
const _::RawSchema* candidate = raw->generic->dependencies[mid];
|
||||
|
||||
uint64_t candidateId = candidate->id;
|
||||
if (candidateId == id) {
|
||||
candidate->ensureInitialized();
|
||||
return Schema(&candidate->defaultBrand);
|
||||
} else if (candidateId < id) {
|
||||
lower = mid + 1;
|
||||
} else {
|
||||
upper = mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KJ_FAIL_REQUIRE("Requested ID not found in dependency table.", kj::hex(id)) {
|
||||
return Schema();
|
||||
}
|
||||
}
|
||||
|
||||
Schema::BrandArgumentList Schema::getBrandArgumentsAtScope(uint64_t scopeId) const {
|
||||
KJ_REQUIRE(getProto().getIsGeneric(), "Not a generic type.", getProto().getDisplayName());
|
||||
|
||||
for (auto scope: kj::range(raw->scopes, raw->scopes + raw->scopeCount)) {
|
||||
if (scope->typeId == scopeId) {
|
||||
// OK, this scope matches the scope we're looking for.
|
||||
if (scope->isUnbound) {
|
||||
return BrandArgumentList(scopeId, true);
|
||||
} else {
|
||||
return BrandArgumentList(scopeId, scope->bindingCount, scope->bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This scope is not listed in the scopes list.
|
||||
return BrandArgumentList(scopeId, raw->isUnbound());
|
||||
}
|
||||
|
||||
kj::Array<uint64_t> Schema::getGenericScopeIds() const {
|
||||
if (!getProto().getIsGeneric())
|
||||
return nullptr;
|
||||
|
||||
auto result = kj::heapArray<uint64_t>(raw->scopeCount);
|
||||
for (auto iScope: kj::indices(result)) {
|
||||
result[iScope] = raw->scopes[iScope].typeId;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
StructSchema Schema::asStruct() const {
|
||||
KJ_REQUIRE(getProto().isStruct(), "Tried to use non-struct schema as a struct.",
|
||||
getProto().getDisplayName()) {
|
||||
return StructSchema();
|
||||
}
|
||||
return StructSchema(*this);
|
||||
}
|
||||
|
||||
EnumSchema Schema::asEnum() const {
|
||||
KJ_REQUIRE(getProto().isEnum(), "Tried to use non-enum schema as an enum.",
|
||||
getProto().getDisplayName()) {
|
||||
return EnumSchema();
|
||||
}
|
||||
return EnumSchema(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ConstSchema Schema::asConst() const {
|
||||
KJ_REQUIRE(getProto().isConst(), "Tried to use non-constant schema as a constant.",
|
||||
getProto().getDisplayName()) {
|
||||
return ConstSchema();
|
||||
}
|
||||
return ConstSchema(*this);
|
||||
}
|
||||
|
||||
kj::StringPtr Schema::getShortDisplayName() const {
|
||||
auto proto = getProto();
|
||||
return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
|
||||
}
|
||||
|
||||
const kj::StringPtr Schema::getUnqualifiedName() const {
|
||||
auto proto = getProto();
|
||||
return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
|
||||
}
|
||||
|
||||
void Schema::requireUsableAs(const _::RawSchema* expected) const {
|
||||
KJ_REQUIRE(raw->generic == expected ||
|
||||
(expected != nullptr && raw->generic->canCastTo == expected),
|
||||
"This schema is not compatible with the requested native type.");
|
||||
}
|
||||
|
||||
uint32_t Schema::getSchemaOffset(const schema::Value::Reader& value) const {
|
||||
const word* ptr;
|
||||
|
||||
switch (value.which()) {
|
||||
case schema::Value::TEXT:
|
||||
ptr = reinterpret_cast<const word*>(value.getText().begin());
|
||||
break;
|
||||
case schema::Value::DATA:
|
||||
ptr = reinterpret_cast<const word*>(value.getData().begin());
|
||||
break;
|
||||
case schema::Value::STRUCT:
|
||||
ptr = value.getStruct().getAs<_::UncheckedMessage>();
|
||||
break;
|
||||
case schema::Value::LIST:
|
||||
ptr = value.getList().getAs<_::UncheckedMessage>();
|
||||
break;
|
||||
case schema::Value::ANY_POINTER:
|
||||
ptr = value.getAnyPointer().getAs<_::UncheckedMessage>();
|
||||
break;
|
||||
default:
|
||||
KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
|
||||
"and any-pointer fields.");
|
||||
}
|
||||
|
||||
return ptr - raw->generic->encodedNode;
|
||||
}
|
||||
|
||||
Type Schema::getBrandBinding(uint64_t scopeId, uint index) const {
|
||||
return getBrandArgumentsAtScope(scopeId)[index];
|
||||
}
|
||||
|
||||
Type Schema::interpretType(schema::Type::Reader proto, uint location) const {
|
||||
switch (proto.which()) {
|
||||
case schema::Type::VOID:
|
||||
case schema::Type::BOOL:
|
||||
case schema::Type::INT8:
|
||||
case schema::Type::INT16:
|
||||
case schema::Type::INT32:
|
||||
case schema::Type::INT64:
|
||||
case schema::Type::UINT8:
|
||||
case schema::Type::UINT16:
|
||||
case schema::Type::UINT32:
|
||||
case schema::Type::UINT64:
|
||||
case schema::Type::FLOAT32:
|
||||
case schema::Type::FLOAT64:
|
||||
case schema::Type::TEXT:
|
||||
case schema::Type::DATA:
|
||||
return proto.which();
|
||||
|
||||
case schema::Type::STRUCT: {
|
||||
auto structType = proto.getStruct();
|
||||
return getDependency(structType.getTypeId(), location).asStruct();
|
||||
}
|
||||
|
||||
case schema::Type::ENUM: {
|
||||
auto enumType = proto.getEnum();
|
||||
return getDependency(enumType.getTypeId(), location).asEnum();
|
||||
}
|
||||
|
||||
case schema::Type::INTERFACE:
|
||||
KJ_FAIL_REQUIRE("Interfaces are not supported.");
|
||||
|
||||
case schema::Type::LIST:
|
||||
return ListSchema::of(interpretType(proto.getList().getElementType(), location));
|
||||
|
||||
case schema::Type::ANY_POINTER: {
|
||||
auto anyPointer = proto.getAnyPointer();
|
||||
switch (anyPointer.which()) {
|
||||
case schema::Type::AnyPointer::UNCONSTRAINED:
|
||||
return anyPointer.getUnconstrained().which();
|
||||
case schema::Type::AnyPointer::PARAMETER: {
|
||||
auto param = anyPointer.getParameter();
|
||||
return getBrandBinding(param.getScopeId(), param.getParameterIndex());
|
||||
}
|
||||
case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER:
|
||||
return Type(Type::ImplicitParameter {
|
||||
anyPointer.getImplicitMethodParameter().getParameterIndex() });
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
Type Schema::BrandArgumentList::operator[](uint index) const {
|
||||
if (isUnbound) {
|
||||
return Type::BrandParameter { scopeId, index };
|
||||
}
|
||||
|
||||
if (index >= size_) {
|
||||
// Binding index out-of-range. Treat as AnyPointer. This is important to allow new
|
||||
// type parameters to be added to existing types without breaking dependent
|
||||
// schemas.
|
||||
return schema::Type::ANY_POINTER;
|
||||
}
|
||||
|
||||
auto& binding = bindings[index];
|
||||
Type result;
|
||||
if (binding.which == (uint)schema::Type::ANY_POINTER) {
|
||||
if (binding.scopeId != 0) {
|
||||
result = Type::BrandParameter { binding.scopeId, binding.paramIndex };
|
||||
} else if (binding.isImplicitParameter) {
|
||||
result = Type::ImplicitParameter { binding.paramIndex };
|
||||
} else {
|
||||
result = static_cast<schema::Type::AnyPointer::Unconstrained::Which>(binding.paramIndex);
|
||||
}
|
||||
} else if (binding.schema == nullptr) {
|
||||
// Builtin / primitive type.
|
||||
result = static_cast<schema::Type::Which>(binding.which);
|
||||
} else {
|
||||
binding.schema->ensureInitialized();
|
||||
result = Type(static_cast<schema::Type::Which>(binding.which), binding.schema);
|
||||
}
|
||||
|
||||
return result.wrapInList(binding.listDepth);
|
||||
}
|
||||
|
||||
kj::StringPtr KJ_STRINGIFY(const Schema& schema) {
|
||||
return schema.getProto().getDisplayName();
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename List>
|
||||
auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, List&& list)
|
||||
-> kj::Maybe<decltype(list[0])> {
|
||||
uint lower = 0;
|
||||
uint upper = raw->memberCount;
|
||||
|
||||
while (lower < upper) {
|
||||
uint mid = (lower + upper) / 2;
|
||||
|
||||
uint16_t memberIndex = raw->membersByName[mid];
|
||||
|
||||
auto candidate = list[memberIndex];
|
||||
kj::StringPtr candidateName = candidate.getProto().getName();
|
||||
if (candidateName == name) {
|
||||
return candidate;
|
||||
} else if (candidateName < name) {
|
||||
lower = mid + 1;
|
||||
} else {
|
||||
upper = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
StructSchema::FieldList StructSchema::getFields() const {
|
||||
return FieldList(*this, getProto().getStruct().getFields());
|
||||
}
|
||||
|
||||
StructSchema::FieldSubset StructSchema::getUnionFields() const {
|
||||
auto proto = getProto().getStruct();
|
||||
return FieldSubset(*this, proto.getFields(),
|
||||
raw->generic->membersByDiscriminant, proto.getDiscriminantCount());
|
||||
}
|
||||
|
||||
StructSchema::FieldSubset StructSchema::getNonUnionFields() const {
|
||||
auto proto = getProto().getStruct();
|
||||
auto fields = proto.getFields();
|
||||
auto offset = proto.getDiscriminantCount();
|
||||
auto size = fields.size() - offset;
|
||||
return FieldSubset(*this, fields, raw->generic->membersByDiscriminant + offset, size);
|
||||
}
|
||||
|
||||
kj::Maybe<StructSchema::Field> StructSchema::findFieldByName(kj::StringPtr name) const {
|
||||
return findSchemaMemberByName(raw->generic, name, getFields());
|
||||
}
|
||||
|
||||
StructSchema::Field StructSchema::getFieldByName(kj::StringPtr name) const {
|
||||
KJ_IF_MAYBE(member, findFieldByName(name)) {
|
||||
return *member;
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("struct has no such member", name);
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t discriminant) const {
|
||||
auto unionFields = getUnionFields();
|
||||
|
||||
if (discriminant >= unionFields.size()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return unionFields[discriminant];
|
||||
}
|
||||
}
|
||||
|
||||
Type StructSchema::Field::getType() const {
|
||||
auto proto = getProto();
|
||||
uint location = _::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::FIELD, index);
|
||||
|
||||
switch (proto.which()) {
|
||||
case schema::Field::SLOT:
|
||||
return parent.interpretType(proto.getSlot().getType(), location);
|
||||
|
||||
case schema::Field::GROUP:
|
||||
return parent.getDependency(proto.getGroup().getTypeId(), location).asStruct();
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
|
||||
return parent.getSchemaOffset(proto.getSlot().getDefaultValue());
|
||||
}
|
||||
|
||||
kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field) {
|
||||
return field.getProto().getName();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
EnumSchema::EnumerantList EnumSchema::getEnumerants() const {
|
||||
return EnumerantList(*this, getProto().getEnum().getEnumerants());
|
||||
}
|
||||
|
||||
kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(kj::StringPtr name) const {
|
||||
return findSchemaMemberByName(raw->generic, name, getEnumerants());
|
||||
}
|
||||
|
||||
EnumSchema::Enumerant EnumSchema::getEnumerantByName(kj::StringPtr name) const {
|
||||
KJ_IF_MAYBE(enumerant, findEnumerantByName(name)) {
|
||||
return *enumerant;
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("enum has no such enumerant", name);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
uint32_t ConstSchema::getValueSchemaOffset() const {
|
||||
return getSchemaOffset(getProto().getConst().getValue());
|
||||
}
|
||||
|
||||
Type ConstSchema::getType() const {
|
||||
return interpretType(getProto().getConst().getType(),
|
||||
_::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::CONST_TYPE, 0));
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
ListSchema ListSchema::of(schema::Type::Which primitiveType) {
|
||||
switch (primitiveType) {
|
||||
case schema::Type::VOID:
|
||||
case schema::Type::BOOL:
|
||||
case schema::Type::INT8:
|
||||
case schema::Type::INT16:
|
||||
case schema::Type::INT32:
|
||||
case schema::Type::INT64:
|
||||
case schema::Type::UINT8:
|
||||
case schema::Type::UINT16:
|
||||
case schema::Type::UINT32:
|
||||
case schema::Type::UINT64:
|
||||
case schema::Type::FLOAT32:
|
||||
case schema::Type::FLOAT64:
|
||||
case schema::Type::TEXT:
|
||||
case schema::Type::DATA:
|
||||
break;
|
||||
|
||||
case schema::Type::STRUCT:
|
||||
case schema::Type::ENUM:
|
||||
case schema::Type::INTERFACE:
|
||||
case schema::Type::LIST:
|
||||
KJ_FAIL_REQUIRE("Must use one of the other ListSchema::of() overloads for complex types.");
|
||||
break;
|
||||
|
||||
case schema::Type::ANY_POINTER:
|
||||
KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
|
||||
break;
|
||||
}
|
||||
|
||||
return ListSchema(primitiveType);
|
||||
}
|
||||
|
||||
ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) {
|
||||
// This method is deprecated because it can only be implemented in terms of other deprecated
|
||||
// methods. Temporarily disable warnings for those other deprecated methods.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
switch (elementType.which()) {
|
||||
case schema::Type::VOID:
|
||||
case schema::Type::BOOL:
|
||||
case schema::Type::INT8:
|
||||
case schema::Type::INT16:
|
||||
case schema::Type::INT32:
|
||||
case schema::Type::INT64:
|
||||
case schema::Type::UINT8:
|
||||
case schema::Type::UINT16:
|
||||
case schema::Type::UINT32:
|
||||
case schema::Type::UINT64:
|
||||
case schema::Type::FLOAT32:
|
||||
case schema::Type::FLOAT64:
|
||||
case schema::Type::TEXT:
|
||||
case schema::Type::DATA:
|
||||
return of(elementType.which());
|
||||
|
||||
case schema::Type::STRUCT:
|
||||
return of(context.getDependency(elementType.getStruct().getTypeId()).asStruct());
|
||||
|
||||
case schema::Type::ENUM:
|
||||
return of(context.getDependency(elementType.getEnum().getTypeId()).asEnum());
|
||||
|
||||
case schema::Type::INTERFACE:
|
||||
KJ_FAIL_REQUIRE("Interfaces are not supported.");
|
||||
|
||||
case schema::Type::LIST:
|
||||
return of(of(elementType.getList().getElementType(), context));
|
||||
|
||||
case schema::Type::ANY_POINTER:
|
||||
KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
|
||||
return ListSchema();
|
||||
}
|
||||
|
||||
// Unknown type is acceptable.
|
||||
return ListSchema(elementType.which());
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
StructSchema Type::asStruct() const {
|
||||
KJ_REQUIRE(isStruct(), "Tried to interpret a non-struct type as a struct.") {
|
||||
return StructSchema();
|
||||
}
|
||||
KJ_ASSERT(schema != nullptr);
|
||||
return StructSchema(Schema(schema));
|
||||
}
|
||||
EnumSchema Type::asEnum() const {
|
||||
KJ_REQUIRE(isEnum(), "Tried to interpret a non-enum type as an enum.") {
|
||||
return EnumSchema();
|
||||
}
|
||||
KJ_ASSERT(schema != nullptr);
|
||||
return EnumSchema(Schema(schema));
|
||||
}
|
||||
|
||||
ListSchema Type::asList() const {
|
||||
KJ_REQUIRE(isList(), "Type::asList(): Not a list.") {
|
||||
return ListSchema::of(schema::Type::VOID);
|
||||
}
|
||||
Type elementType = *this;
|
||||
--elementType.listDepth;
|
||||
return ListSchema::of(elementType);
|
||||
}
|
||||
|
||||
kj::Maybe<Type::BrandParameter> Type::getBrandParameter() const {
|
||||
KJ_REQUIRE(isAnyPointer(), "Type::getBrandParameter() can only be called on AnyPointer types.");
|
||||
|
||||
if (scopeId == 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return BrandParameter { scopeId, paramIndex };
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<Type::ImplicitParameter> Type::getImplicitParameter() const {
|
||||
KJ_REQUIRE(isAnyPointer(),
|
||||
"Type::getImplicitParameter() can only be called on AnyPointer types.");
|
||||
|
||||
if (isImplicitParam) {
|
||||
return ImplicitParameter { paramIndex };
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Type::operator==(const Type& other) const {
|
||||
if (baseType != other.baseType || listDepth != other.listDepth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (baseType) {
|
||||
case schema::Type::VOID:
|
||||
case schema::Type::BOOL:
|
||||
case schema::Type::INT8:
|
||||
case schema::Type::INT16:
|
||||
case schema::Type::INT32:
|
||||
case schema::Type::INT64:
|
||||
case schema::Type::UINT8:
|
||||
case schema::Type::UINT16:
|
||||
case schema::Type::UINT32:
|
||||
case schema::Type::UINT64:
|
||||
case schema::Type::FLOAT32:
|
||||
case schema::Type::FLOAT64:
|
||||
case schema::Type::TEXT:
|
||||
case schema::Type::DATA:
|
||||
return true;
|
||||
|
||||
case schema::Type::STRUCT:
|
||||
case schema::Type::ENUM:
|
||||
case schema::Type::INTERFACE:
|
||||
return schema == other.schema;
|
||||
|
||||
case schema::Type::LIST:
|
||||
KJ_UNREACHABLE;
|
||||
|
||||
case schema::Type::ANY_POINTER:
|
||||
return scopeId == other.scopeId && isImplicitParam == other.isImplicitParam &&
|
||||
// Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
|
||||
// both branches compile to the same instructions and can optimize it away.
|
||||
(scopeId != 0 || isImplicitParam ? paramIndex == other.paramIndex
|
||||
: anyPointerKind == other.anyPointerKind);
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
uint Type::hashCode() const {
|
||||
switch (baseType) {
|
||||
case schema::Type::VOID:
|
||||
case schema::Type::BOOL:
|
||||
case schema::Type::INT8:
|
||||
case schema::Type::INT16:
|
||||
case schema::Type::INT32:
|
||||
case schema::Type::INT64:
|
||||
case schema::Type::UINT8:
|
||||
case schema::Type::UINT16:
|
||||
case schema::Type::UINT32:
|
||||
case schema::Type::UINT64:
|
||||
case schema::Type::FLOAT32:
|
||||
case schema::Type::FLOAT64:
|
||||
case schema::Type::TEXT:
|
||||
case schema::Type::DATA:
|
||||
if (listDepth == 0) {
|
||||
// Make sure that hashCode(Type(baseType)) == hashCode(baseType), otherwise HashMap lookups
|
||||
// keyed by `Type` won't work when the caller passes `baseType` as the key.
|
||||
return kj::hashCode(baseType);
|
||||
} else {
|
||||
return kj::hashCode(baseType, listDepth);
|
||||
}
|
||||
|
||||
case schema::Type::STRUCT:
|
||||
case schema::Type::ENUM:
|
||||
case schema::Type::INTERFACE:
|
||||
if (listDepth == 0) {
|
||||
// Make sure that hashCode(Type(schema)) == hashCode(schema), otherwise HashMap lookups
|
||||
// keyed by `Type` won't work when the caller passes `schema` as the key.
|
||||
return kj::hashCode(schema);
|
||||
} else {
|
||||
return kj::hashCode(schema, listDepth);
|
||||
}
|
||||
|
||||
case schema::Type::LIST:
|
||||
KJ_UNREACHABLE;
|
||||
|
||||
case schema::Type::ANY_POINTER: {
|
||||
// Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
|
||||
// both branches compile to the same instructions and can optimize it away.
|
||||
uint16_t val = scopeId != 0 || isImplicitParam ?
|
||||
paramIndex : static_cast<uint16_t>(anyPointerKind);
|
||||
return kj::hashCode(val, isImplicitParam, scopeId, listDepth);
|
||||
}
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
void Type::requireUsableAs(Type expected) const {
|
||||
KJ_REQUIRE(baseType == expected.baseType && listDepth == expected.listDepth,
|
||||
"This type is not compatible with the requested native type.");
|
||||
|
||||
switch (baseType) {
|
||||
case schema::Type::VOID:
|
||||
case schema::Type::BOOL:
|
||||
case schema::Type::INT8:
|
||||
case schema::Type::INT16:
|
||||
case schema::Type::INT32:
|
||||
case schema::Type::INT64:
|
||||
case schema::Type::UINT8:
|
||||
case schema::Type::UINT16:
|
||||
case schema::Type::UINT32:
|
||||
case schema::Type::UINT64:
|
||||
case schema::Type::FLOAT32:
|
||||
case schema::Type::FLOAT64:
|
||||
case schema::Type::TEXT:
|
||||
case schema::Type::DATA:
|
||||
case schema::Type::ANY_POINTER:
|
||||
break;
|
||||
|
||||
case schema::Type::STRUCT:
|
||||
case schema::Type::ENUM:
|
||||
case schema::Type::INTERFACE:
|
||||
Schema(schema).requireUsableAs(expected.schema->generic);
|
||||
break;
|
||||
|
||||
case schema::Type::LIST:
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
468
vendor/capnproto/src/capnp/schema.capnp
vendored
Normal file
468
vendor/capnproto/src/capnp/schema.capnp
vendored
Normal file
@@ -0,0 +1,468 @@
|
||||
# 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;
|
||||
}
|
||||
}
|
||||
|
||||
startByte @34 :UInt32;
|
||||
endByte @35 :UInt32;
|
||||
|
||||
|
||||
}
|
||||
|
||||
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 is always either the
|
||||
# current scope's type ID or one of its ancestors' IDs.
|
||||
|
||||
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 the Brand appears is within this scope or a sub-scope, and bindings
|
||||
# for this scope are deferred to later Brand applications. This is equivalent to a
|
||||
# pass-through binding list, where each of this scope's parameters is bound to itself.
|
||||
# For example:
|
||||
#
|
||||
# struct Outer(T) {
|
||||
# struct Inner {
|
||||
# value @0 :T;
|
||||
# }
|
||||
# innerInherit @0 :Inner; # Outer Brand.Scope is `inherit`.
|
||||
# innerBindSelf @1 :Outer(T).Inner; # Outer Brand.Scope explicitly binds T to T.
|
||||
# }
|
||||
#
|
||||
# The innerInherit and innerBindSelf fields have equivalent types, but different Brand
|
||||
# styles.
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
3357
vendor/capnproto/src/capnp/schema.capnp.c++
vendored
Normal file
3357
vendor/capnproto/src/capnp/schema.capnp.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6430
vendor/capnproto/src/capnp/schema.capnp.h
vendored
Normal file
6430
vendor/capnproto/src/capnp/schema.capnp.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
877
vendor/capnproto/src/capnp/schema.h
vendored
Normal file
877
vendor/capnproto/src/capnp/schema.h
vendored
Normal file
@@ -0,0 +1,877 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#undef CONST
|
||||
// For some ridiculous reason, Windows defines CONST to const. We have an enum value called CONST
|
||||
// in schema.capnp.h, so if this is defined, compilation is gonna fail. So we undef it because
|
||||
// that seems strictly better than failing entirely. But this could cause trouble for people later
|
||||
// on if they, say, include windows.h, then include schema.h, then include another windows API
|
||||
// header that uses CONST. I suppose they may have to re-#define CONST in between, or change the
|
||||
// header ordering. Sorry.
|
||||
//
|
||||
// Please don't file a bug report telling us to change our enum naming style. You are at least
|
||||
// seven years too late.
|
||||
|
||||
#include <capnp/schema.capnp.h>
|
||||
#include <kj/hash.h>
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
namespace capnp {
|
||||
|
||||
class Schema;
|
||||
class StructSchema;
|
||||
class EnumSchema;
|
||||
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::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_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 CAPNP_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.
|
||||
|
||||
kj::Array<uint64_t> getGenericScopeIds() const;
|
||||
// Returns the type IDs of all parent scopes that have generic parameters, to which this type is
|
||||
// subject.
|
||||
|
||||
StructSchema asStruct() const;
|
||||
EnumSchema asEnum() 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.
|
||||
|
||||
inline uint hashCode() const { return kj::hashCode(raw); }
|
||||
|
||||
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.
|
||||
|
||||
const kj::StringPtr getUnqualifiedName() const;
|
||||
// Get the display name "nickname" of this node minus the prefix
|
||||
|
||||
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 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.)
|
||||
|
||||
|
||||
bool mayContainCapabilities() const { return raw->generic->mayContainCapabilities; }
|
||||
// Returns true if a struct of this type may transitively contain any capabilities. I.e., are
|
||||
// any of the fields an interface type, or a struct type that may in turn contain capabilities?
|
||||
//
|
||||
// This is meant for optimizations where various bookkeeping can possibly be skipped if it is
|
||||
// known in advance that there are no capabilities. Note that this may conservatively return true
|
||||
// spuriously, e.g. if it would be inconvenient to compute the correct answer. A false positive
|
||||
// should never cause incorrect behavior, just potentially hurt performance.
|
||||
//
|
||||
// It's important to keep in mind that even if a schema has no capability-typed fields today,
|
||||
// they could always be added in future versions of the schema. So, just because the schema
|
||||
// doesn't contain capabilities does NOT necessarily mean that an instance of the struct can't
|
||||
// contain capabilities. However, it is a pretty good hint that the application won't plan to
|
||||
// use such capabilities -- for example, if there are no caps in an RPC call's response type
|
||||
// according to the client's version of the schema, then the client clearly isn't going to try
|
||||
// to make any pipelined calls. The server could be operating with a new version of the schema
|
||||
// and could actually return capabilities, but for the client to make a pipelined call, the
|
||||
// client would have to know in advance that capabilities could be returned.
|
||||
|
||||
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); }
|
||||
inline uint hashCode() const;
|
||||
|
||||
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); }
|
||||
inline uint hashCode() const;
|
||||
|
||||
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 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(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();
|
||||
template <typename T>
|
||||
inline static Type from(T&& value);
|
||||
|
||||
inline schema::Type::Which which() const;
|
||||
|
||||
StructSchema asStruct() const;
|
||||
EnumSchema asEnum() 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); }
|
||||
|
||||
uint 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;
|
||||
|
||||
template <typename T, Kind k>
|
||||
struct FromValueImpl;
|
||||
|
||||
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(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)
|
||||
CAPNP_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;
|
||||
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 uint StructSchema::Field::hashCode() const {
|
||||
return kj::hashCode(parent, index);
|
||||
}
|
||||
inline uint EnumSchema::Enumerant::hashCode() const {
|
||||
return kj::hashCode(parent, 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(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 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(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>()); }
|
||||
|
||||
template <typename T, Kind k>
|
||||
struct Type::FromValueImpl {
|
||||
template <typename U>
|
||||
static inline Type type(U&& value) {
|
||||
return Type::from<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Type::FromValueImpl<T, Kind::OTHER> {
|
||||
template <typename U>
|
||||
static inline Type type(U&& value) {
|
||||
// All dynamic types have getSchema().
|
||||
return value.getSchema();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline Type Type::from(T&& value) {
|
||||
typedef FromAny<kj::Decay<T>> Base;
|
||||
return Type::FromValueImpl<Base, kind<Base>()>::type(kj::fwd<T>(value));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
CAPNP_END_HEADER
|
||||
135
vendor/capnproto/src/capnp/serialize.c++
vendored
Normal file
135
vendor/capnproto/src/capnp/serialize.c++
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
// 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 "serialize.h"
|
||||
#include "layout.h"
|
||||
#include <kj/debug.h>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
FlatArrayMessageReader::FlatArrayMessageReader(
|
||||
kj::ArrayPtr<const word> array, ReaderOptions options)
|
||||
: MessageReader(options), end(array.end()) {
|
||||
if (array.size() < 1) {
|
||||
// Assume empty message.
|
||||
return;
|
||||
}
|
||||
|
||||
const _::WireValue<uint32_t>* table =
|
||||
reinterpret_cast<const _::WireValue<uint32_t>*>(array.begin());
|
||||
|
||||
uint segmentCount = table[0].get() + 1;
|
||||
size_t offset = segmentCount / 2u + 1u;
|
||||
|
||||
KJ_REQUIRE(segmentCount != 0, "Message segment count too large, caused overflow.") {
|
||||
return;
|
||||
}
|
||||
|
||||
KJ_REQUIRE(array.size() >= offset, "Message ends prematurely in segment table.") {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
uint segmentSize = table[1].get();
|
||||
|
||||
KJ_REQUIRE(array.size() >= offset + segmentSize,
|
||||
"Message ends prematurely in first segment.") {
|
||||
return;
|
||||
}
|
||||
|
||||
segment0 = array.slice(offset, offset + segmentSize);
|
||||
offset += segmentSize;
|
||||
}
|
||||
|
||||
if (segmentCount > 1) {
|
||||
moreSegments = kj::heapArray<kj::ArrayPtr<const word>>(segmentCount - 1);
|
||||
|
||||
for (uint i = 1; i < segmentCount; i++) {
|
||||
uint segmentSize = table[i + 1].get();
|
||||
|
||||
KJ_REQUIRE(array.size() >= offset + segmentSize, "Message ends prematurely.") {
|
||||
moreSegments = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
moreSegments[i - 1] = array.slice(offset, offset + segmentSize);
|
||||
offset += segmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
end = array.begin() + offset;
|
||||
}
|
||||
|
||||
kj::ArrayPtr<const word> FlatArrayMessageReader::getSegment(uint id) {
|
||||
if (id == 0) {
|
||||
return segment0;
|
||||
} else if (id <= moreSegments.size()) {
|
||||
return moreSegments[id - 1];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
kj::Array<word> messageToFlatArray(kj::ArrayPtr<const kj::ArrayPtr<const word>> segments) {
|
||||
kj::Array<word> result = kj::heapArray<word>(computeSerializedSizeInWords(segments));
|
||||
|
||||
_::WireValue<uint32_t>* table =
|
||||
reinterpret_cast<_::WireValue<uint32_t>*>(result.begin());
|
||||
|
||||
// We write the segment count - 1 because this makes the first word zero for single-segment
|
||||
// messages, improving compression. We don't bother doing this with segment sizes because
|
||||
// one-word segments are rare anyway.
|
||||
table[0].set(segments.size() - 1);
|
||||
|
||||
for (uint i = 0; i < segments.size(); i++) {
|
||||
table[i + 1].set(segments[i].size());
|
||||
}
|
||||
|
||||
if (segments.size() % 2 == 0) {
|
||||
// Set padding byte.
|
||||
table[segments.size() + 1].set(0);
|
||||
}
|
||||
|
||||
word* dst = result.begin() + segments.size() / 2 + 1;
|
||||
|
||||
for (auto& segment: segments) {
|
||||
memcpy(dst, segment.begin(), segment.size() * sizeof(word));
|
||||
dst += segment.size();
|
||||
}
|
||||
|
||||
KJ_DASSERT(dst == result.end(), "Buffer overrun/underrun bug in code above.");
|
||||
|
||||
return kj::mv(result);
|
||||
}
|
||||
|
||||
size_t computeSerializedSizeInWords(kj::ArrayPtr<const kj::ArrayPtr<const word>> segments) {
|
||||
KJ_REQUIRE(segments.size() > 0, "Tried to serialize uninitialized message.");
|
||||
|
||||
size_t totalSize = segments.size() / 2 + 1;
|
||||
|
||||
for (auto& segment: segments) {
|
||||
totalSize += segment.size();
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
95
vendor/capnproto/src/capnp/serialize.h
vendored
Normal file
95
vendor/capnproto/src/capnp/serialize.h
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "message.h"
|
||||
|
||||
CAPNP_BEGIN_HEADER
|
||||
|
||||
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::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(kj::ArrayPtr<const kj::ArrayPtr<const word>> segments);
|
||||
// Version of computeSerializedSizeInWords that takes a raw segment array.
|
||||
|
||||
inline kj::Array<word> messageToFlatArray(MessageBuilder& builder) {
|
||||
return messageToFlatArray(builder.getSegmentsForOutput());
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
|
||||
CAPNP_END_HEADER
|
||||
267
vendor/capnproto/src/capnp/stringify.c++
vendored
Normal file
267
vendor/capnproto/src/capnp/stringify.c++
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
// 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 "dynamic.h"
|
||||
#include <kj/debug.h>
|
||||
#include <kj/vector.h>
|
||||
#include <kj/encoding.h>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PrintMode {
|
||||
BARE,
|
||||
// The value is planned to be printed on its own line, unless it is very short and contains
|
||||
// no inner newlines.
|
||||
|
||||
PREFIXED,
|
||||
// The value is planned to be printed with a prefix, like "memberName = " (a struct field).
|
||||
|
||||
PARENTHESIZED
|
||||
// The value is printed in parenthesized (a union value).
|
||||
};
|
||||
|
||||
enum class PrintKind {
|
||||
LIST,
|
||||
RECORD
|
||||
};
|
||||
|
||||
class Indent {
|
||||
public:
|
||||
explicit Indent(bool enable): amount(enable ? 1 : 0) {}
|
||||
|
||||
Indent next() {
|
||||
return Indent(amount == 0 ? 0 : amount + 1);
|
||||
}
|
||||
|
||||
kj::StringTree delimit(kj::Array<kj::StringTree> items, PrintMode mode, PrintKind kind) {
|
||||
if (amount == 0 || canPrintAllInline(items, kind)) {
|
||||
return kj::StringTree(kj::mv(items), ", ");
|
||||
} else {
|
||||
KJ_STACK_ARRAY(char, delimArrayPtr, amount * 2 + 3, 32, 256);
|
||||
auto delim = delimArrayPtr.begin();
|
||||
delim[0] = ',';
|
||||
delim[1] = '\n';
|
||||
memset(delim + 2, ' ', amount * 2);
|
||||
delim[amount * 2 + 2] = '\0';
|
||||
|
||||
// If the outer value isn't being printed on its own line, we need to add a newline/indent
|
||||
// before the first item, otherwise we only add a space on the assumption that it is preceded
|
||||
// by an open bracket or parenthesis.
|
||||
return kj::strTree(mode == BARE ? " " : delim + 1,
|
||||
kj::StringTree(kj::mv(items), kj::StringPtr(delim, amount * 2 + 2)), ' ');
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint amount;
|
||||
|
||||
explicit Indent(uint amount): amount(amount) {}
|
||||
|
||||
static constexpr size_t maxInlineValueSize = 24;
|
||||
static constexpr size_t maxInlineRecordSize = 64;
|
||||
|
||||
static bool canPrintInline(const kj::StringTree& text) {
|
||||
if (text.size() > maxInlineValueSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char flat[maxInlineValueSize + 1];
|
||||
text.flattenTo(flat);
|
||||
flat[text.size()] = '\0';
|
||||
if (strchr(flat, '\n') != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool canPrintAllInline(const kj::Array<kj::StringTree>& items, PrintKind kind) {
|
||||
size_t totalSize = 0;
|
||||
for (auto& item: items) {
|
||||
if (!canPrintInline(item)) return false;
|
||||
if (kind == PrintKind::RECORD) {
|
||||
totalSize += item.size();
|
||||
if (totalSize > maxInlineRecordSize) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static schema::Type::Which whichFieldType(const StructSchema::Field& field) {
|
||||
auto proto = field.getProto();
|
||||
switch (proto.which()) {
|
||||
case schema::Field::SLOT:
|
||||
return proto.getSlot().getType().which();
|
||||
case schema::Field::GROUP:
|
||||
return schema::Type::STRUCT;
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
static kj::StringTree print(const DynamicValue::Reader& value,
|
||||
schema::Type::Which which, Indent indent,
|
||||
PrintMode mode) {
|
||||
switch (value.getType()) {
|
||||
case DynamicValue::UNKNOWN:
|
||||
return kj::strTree("?");
|
||||
case DynamicValue::VOID:
|
||||
return kj::strTree("void");
|
||||
case DynamicValue::BOOL:
|
||||
return kj::strTree(value.as<bool>() ? "true" : "false");
|
||||
case DynamicValue::INT:
|
||||
return kj::strTree(value.as<int64_t>());
|
||||
case DynamicValue::UINT:
|
||||
return kj::strTree(value.as<uint64_t>());
|
||||
case DynamicValue::FLOAT:
|
||||
if (which == schema::Type::FLOAT32) {
|
||||
return kj::strTree(value.as<float>());
|
||||
} else {
|
||||
return kj::strTree(value.as<double>());
|
||||
}
|
||||
case DynamicValue::TEXT: {
|
||||
kj::ArrayPtr<const char> chars = value.as<Text>();
|
||||
return kj::strTree('"', kj::encodeCEscape(chars), '"');
|
||||
}
|
||||
case DynamicValue::DATA: {
|
||||
// TODO(someday): Maybe data should be printed as binary literal.
|
||||
kj::ArrayPtr<const byte> bytes = value.as<Data>().asBytes();
|
||||
return kj::strTree('"', kj::encodeCEscape(bytes), '"');
|
||||
}
|
||||
case DynamicValue::LIST: {
|
||||
auto listValue = value.as<DynamicList>();
|
||||
auto which = listValue.getSchema().whichElementType();
|
||||
kj::Array<kj::StringTree> elements = KJ_MAP(element, listValue) {
|
||||
return print(element, which, indent.next(), BARE);
|
||||
};
|
||||
return kj::strTree('[', indent.delimit(kj::mv(elements), mode, PrintKind::LIST), ']');
|
||||
}
|
||||
case DynamicValue::ENUM: {
|
||||
auto enumValue = value.as<DynamicEnum>();
|
||||
KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) {
|
||||
return kj::strTree(enumerant->getProto().getName());
|
||||
} else {
|
||||
// Unknown enum value; output raw number.
|
||||
return kj::strTree('(', enumValue.getRaw(), ')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DynamicValue::STRUCT: {
|
||||
auto structValue = value.as<DynamicStruct>();
|
||||
auto unionFields = structValue.getSchema().getUnionFields();
|
||||
auto nonUnionFields = structValue.getSchema().getNonUnionFields();
|
||||
|
||||
kj::Vector<kj::StringTree> printedFields(nonUnionFields.size() + (unionFields.size() != 0));
|
||||
|
||||
// We try to write the union field, if any, in proper order with the rest.
|
||||
auto which = structValue.which();
|
||||
|
||||
kj::StringTree unionValue;
|
||||
KJ_IF_MAYBE(field, which) {
|
||||
// Even if the union field has its default value, if it is not the default field of the
|
||||
// union then we have to print it anyway.
|
||||
auto fieldProto = field->getProto();
|
||||
if (fieldProto.getDiscriminantValue() != 0 || structValue.has(*field)) {
|
||||
unionValue = kj::strTree(
|
||||
fieldProto.getName(), " = ",
|
||||
print(structValue.get(*field), whichFieldType(*field), indent.next(), PREFIXED));
|
||||
} else {
|
||||
which = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto field: nonUnionFields) {
|
||||
KJ_IF_MAYBE(unionField, which) {
|
||||
if (unionField->getIndex() < field.getIndex()) {
|
||||
printedFields.add(kj::mv(unionValue));
|
||||
which = nullptr;
|
||||
}
|
||||
}
|
||||
if (structValue.has(field)) {
|
||||
printedFields.add(kj::strTree(
|
||||
field.getProto().getName(), " = ",
|
||||
print(structValue.get(field), whichFieldType(field), indent.next(), PREFIXED)));
|
||||
}
|
||||
}
|
||||
if (which != nullptr) {
|
||||
// Union value is last.
|
||||
printedFields.add(kj::mv(unionValue));
|
||||
}
|
||||
|
||||
if (mode == PARENTHESIZED) {
|
||||
return indent.delimit(printedFields.releaseAsArray(), mode, PrintKind::RECORD);
|
||||
} else {
|
||||
return kj::strTree(
|
||||
'(', indent.delimit(printedFields.releaseAsArray(), mode, PrintKind::RECORD), ')');
|
||||
}
|
||||
}
|
||||
case DynamicValue::ANY_POINTER:
|
||||
return kj::strTree("<opaque pointer>");
|
||||
}
|
||||
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
kj::StringTree stringify(DynamicValue::Reader value) {
|
||||
return print(value, schema::Type::STRUCT, Indent(false), BARE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
kj::StringTree prettyPrint(DynamicStruct::Reader value) {
|
||||
return print(value, schema::Type::STRUCT, Indent(true), BARE);
|
||||
}
|
||||
|
||||
kj::StringTree prettyPrint(DynamicList::Reader value) {
|
||||
return print(value, schema::Type::LIST, Indent(true), BARE);
|
||||
}
|
||||
|
||||
kj::StringTree prettyPrint(DynamicStruct::Builder value) { return prettyPrint(value.asReader()); }
|
||||
kj::StringTree prettyPrint(DynamicList::Builder value) { return prettyPrint(value.asReader()); }
|
||||
|
||||
kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value) { return stringify(value); }
|
||||
kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& value) { return stringify(value.asReader()); }
|
||||
kj::StringTree KJ_STRINGIFY(DynamicEnum value) { return stringify(value); }
|
||||
kj::StringTree KJ_STRINGIFY(const DynamicStruct::Reader& value) { return stringify(value); }
|
||||
kj::StringTree KJ_STRINGIFY(const DynamicStruct::Builder& value) { return stringify(value.asReader()); }
|
||||
kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value) { return stringify(value); }
|
||||
kj::StringTree KJ_STRINGIFY(const DynamicList::Builder& value) { return stringify(value.asReader()); }
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
kj::StringTree structString(StructReader reader, const RawBrandedSchema& schema) {
|
||||
return stringify(DynamicStruct::Reader(Schema(&schema).asStruct(), reader));
|
||||
}
|
||||
|
||||
kj::String enumString(uint16_t value, const RawBrandedSchema& schema) {
|
||||
auto enumerants = Schema(&schema).asEnum().getEnumerants();
|
||||
if (value < enumerants.size()) {
|
||||
return kj::heapString(enumerants[value].getProto().getName());
|
||||
} else {
|
||||
return kj::str(value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
} // namespace capnp
|
||||
167
vendor/capnproto/src/kj/arena.c++
vendored
Normal file
167
vendor/capnproto/src/kj/arena.c++
vendored
Normal file
@@ -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.
|
||||
|
||||
#include "arena.h"
|
||||
#include "debug.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace kj {
|
||||
|
||||
Arena::Arena(size_t chunkSizeHint): nextChunkSize(kj::max(sizeof(ChunkHeader), chunkSizeHint)) {}
|
||||
|
||||
Arena::Arena(ArrayPtr<byte> scratch)
|
||||
: nextChunkSize(kj::max(sizeof(ChunkHeader), scratch.size())) {
|
||||
if (scratch.size() > sizeof(ChunkHeader)) {
|
||||
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(scratch.begin());
|
||||
chunk->end = scratch.end();
|
||||
chunk->pos = reinterpret_cast<byte*>(chunk + 1);
|
||||
chunk->next = nullptr; // Never actually observed.
|
||||
|
||||
// Don't place the chunk in the chunk list because it's not ours to delete. Just make it the
|
||||
// current chunk so that we'll allocate from it until it is empty.
|
||||
currentChunk = chunk;
|
||||
}
|
||||
}
|
||||
|
||||
Arena::~Arena() noexcept(false) {
|
||||
// Run cleanup() explicitly, but if it throws an exception, make sure to run it again as part of
|
||||
// unwind. The second call will not throw because destructors are required to guard against
|
||||
// exceptions when already unwinding.
|
||||
KJ_ON_SCOPE_FAILURE(cleanup());
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Arena::cleanup() {
|
||||
while (objectList != nullptr) {
|
||||
void* ptr = objectList + 1;
|
||||
auto destructor = objectList->destructor;
|
||||
objectList = objectList->next;
|
||||
destructor(ptr);
|
||||
}
|
||||
|
||||
while (chunkList != nullptr) {
|
||||
void* ptr = chunkList;
|
||||
chunkList = chunkList->next;
|
||||
operator delete(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool KJ_UNUSED isPowerOfTwo(size_t value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
inline byte* alignTo(byte* p, uint alignment) {
|
||||
// Round the pointer up to the next aligned value.
|
||||
|
||||
KJ_DASSERT(isPowerOfTwo(alignment), alignment);
|
||||
uintptr_t mask = alignment - 1;
|
||||
uintptr_t i = reinterpret_cast<uintptr_t>(p);
|
||||
return reinterpret_cast<byte*>((i + mask) & ~mask);
|
||||
}
|
||||
|
||||
inline size_t alignTo(size_t s, uint alignment) {
|
||||
// Round the pointer up to the next aligned value.
|
||||
|
||||
KJ_DASSERT(isPowerOfTwo(alignment), alignment);
|
||||
size_t mask = alignment - 1;
|
||||
return (s + mask) & ~mask;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void* Arena::allocateBytes(size_t amount, uint alignment, bool hasDisposer) {
|
||||
if (hasDisposer) {
|
||||
alignment = kj::max(alignment, alignof(ObjectHeader));
|
||||
amount += alignTo(sizeof(ObjectHeader), alignment);
|
||||
}
|
||||
|
||||
void* result = allocateBytesInternal(amount, alignment);
|
||||
|
||||
if (hasDisposer) {
|
||||
// Reserve space for the ObjectHeader, but don't add it to the object list yet.
|
||||
result = alignTo(reinterpret_cast<byte*>(result) + sizeof(ObjectHeader), alignment);
|
||||
}
|
||||
|
||||
KJ_DASSERT(reinterpret_cast<uintptr_t>(result) % alignment == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* Arena::allocateBytesInternal(size_t amount, uint alignment) {
|
||||
if (currentChunk != nullptr) {
|
||||
ChunkHeader* chunk = currentChunk;
|
||||
byte* alignedPos = alignTo(chunk->pos, alignment);
|
||||
|
||||
// Careful about overflow here.
|
||||
if (amount + (alignedPos - chunk->pos) <= chunk->end - chunk->pos) {
|
||||
// There's enough space in this chunk.
|
||||
chunk->pos = alignedPos + amount;
|
||||
return alignedPos;
|
||||
}
|
||||
}
|
||||
|
||||
// Not enough space in the current chunk. Allocate a new one.
|
||||
|
||||
// We need to allocate at least enough space for the ChunkHeader and the requested allocation.
|
||||
|
||||
// If the alignment is less than that of the chunk header, we'll need to increase it.
|
||||
alignment = kj::max(alignment, alignof(ChunkHeader));
|
||||
|
||||
// If the ChunkHeader size does not match the alignment, we'll need to pad it up.
|
||||
amount += alignTo(sizeof(ChunkHeader), alignment);
|
||||
|
||||
// Make sure we're going to allocate enough space.
|
||||
while (nextChunkSize < amount) {
|
||||
nextChunkSize *= 2;
|
||||
}
|
||||
|
||||
// Allocate.
|
||||
byte* bytes = reinterpret_cast<byte*>(operator new(nextChunkSize));
|
||||
|
||||
// Set up the ChunkHeader at the beginning of the allocation.
|
||||
ChunkHeader* newChunk = reinterpret_cast<ChunkHeader*>(bytes);
|
||||
newChunk->next = chunkList;
|
||||
newChunk->pos = bytes + amount;
|
||||
newChunk->end = bytes + nextChunkSize;
|
||||
currentChunk = newChunk;
|
||||
chunkList = newChunk;
|
||||
nextChunkSize *= 2;
|
||||
|
||||
// Move past the ChunkHeader to find the position of the allocated object.
|
||||
return alignTo(bytes + sizeof(ChunkHeader), alignment);
|
||||
}
|
||||
|
||||
StringPtr Arena::copyString(StringPtr content) {
|
||||
char* data = reinterpret_cast<char*>(allocateBytes(content.size() + 1, 1, false));
|
||||
memcpy(data, content.cStr(), content.size() + 1);
|
||||
return StringPtr(data, content.size());
|
||||
}
|
||||
|
||||
void Arena::setDestructor(void* ptr, void (*destructor)(void*)) {
|
||||
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(ptr) - 1;
|
||||
KJ_DASSERT(reinterpret_cast<uintptr_t>(header) % alignof(ObjectHeader) == 0);
|
||||
header->destructor = destructor;
|
||||
header->next = objectList;
|
||||
objectList = header;
|
||||
}
|
||||
|
||||
} // namespace kj
|
||||
210
vendor/capnproto/src/kj/arena.h
vendored
Normal file
210
vendor/capnproto/src/kj/arena.h
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "memory.h"
|
||||
#include "array.h"
|
||||
#include "string.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
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_AND_MOVE(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), !KJ_HAS_TRIVIAL_DESTRUCTOR(T)));
|
||||
if (!KJ_HAS_TRIVIAL_CONSTRUCTOR(T) || sizeof...(Params) > 0) {
|
||||
ctor(result, kj::fwd<Params>(params)...);
|
||||
}
|
||||
if (!KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
||||
setDestructor(&result, &destroyObject<T>);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArrayPtr<T> Arena::allocateArray(size_t size) {
|
||||
if (KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
||||
ArrayPtr<T> result =
|
||||
arrayPtr(reinterpret_cast<T*>(allocateBytes(
|
||||
sizeof(T) * size, alignof(T), false)), size);
|
||||
if (!KJ_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 (KJ_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 (!KJ_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
|
||||
|
||||
KJ_END_HEADER
|
||||
109
vendor/capnproto/src/kj/array.c++
vendored
Normal file
109
vendor/capnproto/src/kj/array.c++
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// 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 "array.h"
|
||||
#include "exception.h"
|
||||
|
||||
namespace kj {
|
||||
|
||||
void ExceptionSafeArrayUtil::construct(size_t count, void (*constructElement)(void*)) {
|
||||
while (count > 0) {
|
||||
constructElement(pos);
|
||||
pos += elementSize;
|
||||
++constructedElementCount;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionSafeArrayUtil::destroyAll() {
|
||||
while (constructedElementCount > 0) {
|
||||
pos -= elementSize;
|
||||
--constructedElementCount;
|
||||
destroyElement(pos);
|
||||
}
|
||||
}
|
||||
|
||||
const DestructorOnlyArrayDisposer DestructorOnlyArrayDisposer::instance =
|
||||
DestructorOnlyArrayDisposer();
|
||||
|
||||
void DestructorOnlyArrayDisposer::disposeImpl(
|
||||
void* firstElement, size_t elementSize, size_t elementCount,
|
||||
size_t capacity, void (*destroyElement)(void*)) const {
|
||||
if (destroyElement != nullptr) {
|
||||
ExceptionSafeArrayUtil guard(firstElement, elementSize, elementCount, destroyElement);
|
||||
guard.destroyAll();
|
||||
}
|
||||
}
|
||||
|
||||
const NullArrayDisposer NullArrayDisposer::instance = NullArrayDisposer();
|
||||
|
||||
void NullArrayDisposer::disposeImpl(
|
||||
void* firstElement, size_t elementSize, size_t elementCount,
|
||||
size_t capacity, void (*destroyElement)(void*)) const {}
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
struct AutoDeleter {
|
||||
void* ptr;
|
||||
inline void* release() { void* result = ptr; ptr = nullptr; return result; }
|
||||
inline AutoDeleter(void* ptr): ptr(ptr) {}
|
||||
inline ~AutoDeleter() { operator delete(ptr); }
|
||||
};
|
||||
|
||||
void* HeapArrayDisposer::allocateImpl(size_t elementSize, size_t elementCount, size_t capacity,
|
||||
void (*constructElement)(void*),
|
||||
void (*destroyElement)(void*)) {
|
||||
AutoDeleter result(operator new(elementSize * capacity));
|
||||
|
||||
if (constructElement == nullptr) {
|
||||
// Nothing to do.
|
||||
} else if (destroyElement == nullptr) {
|
||||
byte* pos = reinterpret_cast<byte*>(result.ptr);
|
||||
while (elementCount > 0) {
|
||||
constructElement(pos);
|
||||
pos += elementSize;
|
||||
--elementCount;
|
||||
}
|
||||
} else {
|
||||
ExceptionSafeArrayUtil guard(result.ptr, elementSize, 0, destroyElement);
|
||||
guard.construct(elementCount, constructElement);
|
||||
guard.release();
|
||||
}
|
||||
|
||||
return result.release();
|
||||
}
|
||||
|
||||
void HeapArrayDisposer::disposeImpl(
|
||||
void* firstElement, size_t elementSize, size_t elementCount, size_t capacity,
|
||||
void (*destroyElement)(void*)) const {
|
||||
// Note that capacity is ignored since operator delete() doesn't care about it.
|
||||
AutoDeleter deleter(firstElement);
|
||||
|
||||
if (destroyElement != nullptr) {
|
||||
ExceptionSafeArrayUtil guard(firstElement, elementSize, elementCount, destroyElement);
|
||||
guard.destroyAll();
|
||||
}
|
||||
}
|
||||
|
||||
const HeapArrayDisposer HeapArrayDisposer::instance = HeapArrayDisposer();
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
913
vendor/capnproto/src/kj/array.h
vendored
Normal file
913
vendor/capnproto/src/kj/array.h
vendored
Normal file
@@ -0,0 +1,913 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "memory.h"
|
||||
#include <string.h>
|
||||
#include <initializer_list>
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
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 = KJ_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_AND_MOVE(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 KJ_LIFETIMEBOUND, size_t size, const ArrayDisposer& disposer)
|
||||
: ptr(firstElement), size_(size), disposer(&disposer) {}
|
||||
|
||||
KJ_DISALLOW_COPY(Array);
|
||||
inline ~Array() noexcept { dispose(); }
|
||||
|
||||
inline operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
||||
return ArrayPtr<T>(ptr, size_);
|
||||
}
|
||||
inline operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
||||
return ArrayPtr<T>(ptr, size_);
|
||||
}
|
||||
inline ArrayPtr<T> asPtr() KJ_LIFETIMEBOUND {
|
||||
return ArrayPtr<T>(ptr, size_);
|
||||
}
|
||||
inline ArrayPtr<const T> asPtr() const KJ_LIFETIMEBOUND {
|
||||
return ArrayPtr<T>(ptr, size_);
|
||||
}
|
||||
|
||||
inline size_t size() const { return size_; }
|
||||
inline T& operator[](size_t index) KJ_LIFETIMEBOUND {
|
||||
KJ_IREQUIRE(index < size_, "Out-of-bounds Array access.");
|
||||
return ptr[index];
|
||||
}
|
||||
inline const T& operator[](size_t index) const KJ_LIFETIMEBOUND {
|
||||
KJ_IREQUIRE(index < size_, "Out-of-bounds Array access.");
|
||||
return ptr[index];
|
||||
}
|
||||
|
||||
inline const T* begin() const KJ_LIFETIMEBOUND { return ptr; }
|
||||
inline const T* end() const KJ_LIFETIMEBOUND { return ptr + size_; }
|
||||
inline const T& front() const KJ_LIFETIMEBOUND { return *ptr; }
|
||||
inline const T& back() const KJ_LIFETIMEBOUND { return *(ptr + size_ - 1); }
|
||||
inline T* begin() KJ_LIFETIMEBOUND { return ptr; }
|
||||
inline T* end() KJ_LIFETIMEBOUND { return ptr + size_; }
|
||||
inline T& front() KJ_LIFETIMEBOUND { return *ptr; }
|
||||
inline T& back() KJ_LIFETIMEBOUND { return *(ptr + size_ - 1); }
|
||||
|
||||
template <typename U>
|
||||
inline bool operator==(const U& other) const { return asPtr() == other; }
|
||||
template <typename U>
|
||||
inline bool operator!=(const U& other) const { return asPtr() != other; }
|
||||
|
||||
inline ArrayPtr<T> slice(size_t start, size_t end) KJ_LIFETIMEBOUND {
|
||||
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_LIFETIMEBOUND {
|
||||
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 KJ_LIFETIMEBOUND { return asPtr().asBytes(); }
|
||||
inline ArrayPtr<PropagateConst<T, byte>> asBytes() KJ_LIFETIMEBOUND { return asPtr().asBytes(); }
|
||||
inline ArrayPtr<const char> asChars() const KJ_LIFETIMEBOUND { return asPtr().asChars(); }
|
||||
inline ArrayPtr<PropagateConst<T, char>> asChars() KJ_LIFETIMEBOUND { 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;
|
||||
}
|
||||
|
||||
template <typename... Attachments>
|
||||
Array<T> attach(Attachments&&... attachments) KJ_WARN_UNUSED_RESULT;
|
||||
// Like Own<T>::attach(), but attaches to an Array.
|
||||
|
||||
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;
|
||||
template <typename U>
|
||||
friend class ArrayBuilder;
|
||||
};
|
||||
|
||||
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 = KJ_HAS_TRIVIAL_CONSTRUCTOR(T),
|
||||
bool hasNothrowConstructor = KJ_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;
|
||||
}
|
||||
ArrayBuilder(Array<T>&& other)
|
||||
: ptr(other.ptr), pos(other.ptr + other.size_), endPtr(pos), disposer(other.disposer) {
|
||||
// Create an already-full ArrayBuilder from an Array of the same type. This constructor
|
||||
// primarily exists to enable Vector<T> to be constructed from Array<T>.
|
||||
other.ptr = nullptr;
|
||||
other.size_ = 0;
|
||||
}
|
||||
KJ_DISALLOW_COPY(ArrayBuilder);
|
||||
inline ~ArrayBuilder() noexcept(false) { dispose(); }
|
||||
|
||||
inline operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(ptr, pos);
|
||||
}
|
||||
inline operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(ptr, pos);
|
||||
}
|
||||
inline ArrayPtr<T> asPtr() KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(ptr, pos);
|
||||
}
|
||||
inline ArrayPtr<const T> asPtr() const KJ_LIFETIMEBOUND {
|
||||
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) KJ_LIFETIMEBOUND {
|
||||
KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access.");
|
||||
return ptr[index];
|
||||
}
|
||||
inline const T& operator[](size_t index) const KJ_LIFETIMEBOUND {
|
||||
KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access.");
|
||||
return ptr[index];
|
||||
}
|
||||
|
||||
inline const T* begin() const KJ_LIFETIMEBOUND { return ptr; }
|
||||
inline const T* end() const KJ_LIFETIMEBOUND { return pos; }
|
||||
inline const T& front() const KJ_LIFETIMEBOUND { return *ptr; }
|
||||
inline const T& back() const KJ_LIFETIMEBOUND { return *(pos - 1); }
|
||||
inline T* begin() KJ_LIFETIMEBOUND { return ptr; }
|
||||
inline T* end() KJ_LIFETIMEBOUND { return pos; }
|
||||
inline T& front() KJ_LIFETIMEBOUND { return *ptr; }
|
||||
inline T& back() KJ_LIFETIMEBOUND { 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_LIFETIMEBOUND {
|
||||
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 (KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
||||
pos = target;
|
||||
} else {
|
||||
while (pos > target) {
|
||||
kj::dtor(*--pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
||||
pos = ptr;
|
||||
} else {
|
||||
while (pos > ptr) {
|
||||
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 (KJ_HAS_TRIVIAL_CONSTRUCTOR(T)) {
|
||||
pos = target;
|
||||
} else {
|
||||
while (pos < target) {
|
||||
kj::ctor(*pos++);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// truncate
|
||||
if (KJ_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 the 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 = &NullArrayDisposer::instance;
|
||||
|
||||
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 constexpr size_t size() const { return fixedSize; }
|
||||
inline constexpr T* begin() KJ_LIFETIMEBOUND { return content; }
|
||||
inline constexpr T* end() KJ_LIFETIMEBOUND { return content + fixedSize; }
|
||||
inline constexpr const T* begin() const KJ_LIFETIMEBOUND { return content; }
|
||||
inline constexpr const T* end() const KJ_LIFETIMEBOUND { return content + fixedSize; }
|
||||
|
||||
inline constexpr operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(content, fixedSize);
|
||||
}
|
||||
inline constexpr operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(content, fixedSize);
|
||||
}
|
||||
|
||||
inline constexpr T& operator[](size_t index) KJ_LIFETIMEBOUND { return content[index]; }
|
||||
inline constexpr const T& operator[](size_t index) const KJ_LIFETIMEBOUND {
|
||||
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() KJ_LIFETIMEBOUND { return content; }
|
||||
inline T* end() KJ_LIFETIMEBOUND { return content + currentSize; }
|
||||
inline const T* begin() const KJ_LIFETIMEBOUND { return content; }
|
||||
inline const T* end() const KJ_LIFETIMEBOUND { return content + currentSize; }
|
||||
|
||||
inline operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(content, currentSize);
|
||||
}
|
||||
inline operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
||||
return arrayPtr(content, currentSize);
|
||||
}
|
||||
|
||||
inline T& operator[](size_t index) KJ_LIFETIMEBOUND { return content[index]; }
|
||||
inline const T& operator[](size_t index) const KJ_LIFETIMEBOUND { 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(const_cast<RemoveConst<T>*>(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) {
|
||||
if (end != start) {
|
||||
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) {
|
||||
if (end != start) {
|
||||
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());
|
||||
}
|
||||
|
||||
#if KJ_CPP_STD > 201402L
|
||||
template <typename T, typename... Params>
|
||||
inline Array<Decay<T>> arr(T&& param1, Params&&... params) {
|
||||
ArrayBuilder<Decay<T>> builder = heapArrayBuilder<Decay<T>>(sizeof...(params) + 1);
|
||||
(builder.add(kj::fwd<T>(param1)), ... , builder.add(kj::fwd<Params>(params)));
|
||||
return builder.finish();
|
||||
}
|
||||
template <typename T, typename... Params>
|
||||
inline Array<Decay<T>> arrOf(Params&&... params) {
|
||||
ArrayBuilder<Decay<T>> builder = heapArrayBuilder<Decay<T>>(sizeof...(params));
|
||||
(... , builder.add(kj::fwd<Params>(params)));
|
||||
return builder.finish();
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename... T>
|
||||
struct ArrayDisposableOwnedBundle final: public ArrayDisposer, public OwnedBundle<T...> {
|
||||
ArrayDisposableOwnedBundle(T&&... values): OwnedBundle<T...>(kj::fwd<T>(values)...) {}
|
||||
void disposeImpl(void*, size_t, size_t, size_t, void (*)(void*)) const override { delete this; }
|
||||
};
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T>
|
||||
template <typename... Attachments>
|
||||
Array<T> Array<T>::attach(Attachments&&... attachments) {
|
||||
T* ptrCopy = ptr;
|
||||
auto sizeCopy = size_;
|
||||
|
||||
KJ_IREQUIRE(ptrCopy != nullptr, "cannot attach to null pointer");
|
||||
|
||||
// HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
|
||||
// accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
|
||||
// disposer will still be called when the pointer goes out of scope.
|
||||
if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
|
||||
|
||||
auto bundle = new _::ArrayDisposableOwnedBundle<Array<T>, Attachments...>(
|
||||
kj::mv(*this), kj::fwd<Attachments>(attachments)...);
|
||||
return Array<T>(ptrCopy, sizeCopy, *bundle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename... Attachments>
|
||||
Array<T> ArrayPtr<T>::attach(Attachments&&... attachments) const {
|
||||
T* ptrCopy = ptr;
|
||||
|
||||
KJ_IREQUIRE(ptrCopy != nullptr, "cannot attach to null pointer");
|
||||
|
||||
// HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
|
||||
// accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
|
||||
// disposer will still be called when the pointer goes out of scope.
|
||||
if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
|
||||
|
||||
auto bundle = new _::ArrayDisposableOwnedBundle<Attachments...>(
|
||||
kj::fwd<Attachments>(attachments)...);
|
||||
return Array<T>(ptrCopy, size_, *bundle);
|
||||
}
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
49
vendor/capnproto/src/kj/common.c++
vendored
Normal file
49
vendor/capnproto/src/kj/common.c++
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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 "common.h"
|
||||
#include "debug.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace kj {
|
||||
namespace _ { // private
|
||||
|
||||
void inlineRequireFailure(const char* file, int line, const char* expectation,
|
||||
const char* macroArgs, const char* message) {
|
||||
if (message == nullptr) {
|
||||
Debug::Fault f(file, line, kj::Exception::Type::FAILED, expectation, macroArgs);
|
||||
f.fatal();
|
||||
} else {
|
||||
Debug::Fault f(file, line, kj::Exception::Type::FAILED, expectation, macroArgs, message);
|
||||
f.fatal();
|
||||
}
|
||||
}
|
||||
|
||||
void unreachable() {
|
||||
KJ_FAIL_ASSERT("Supposedly-unreachable branch executed.");
|
||||
|
||||
// Really make sure we abort.
|
||||
KJ_KNOWN_UNREACHABLE(abort());
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
} // namespace kj
|
||||
1929
vendor/capnproto/src/kj/common.h
vendored
Normal file
1929
vendor/capnproto/src/kj/common.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
397
vendor/capnproto/src/kj/debug.c++
vendored
Normal file
397
vendor/capnproto/src/kj/debug.c++
vendored
Normal file
@@ -0,0 +1,397 @@
|
||||
// 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 "debug.h"
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
namespace kj {
|
||||
namespace _ { // private
|
||||
|
||||
LogSeverity Debug::minSeverity = LogSeverity::WARNING;
|
||||
|
||||
namespace {
|
||||
|
||||
Exception::Type typeOfErrno(int error) {
|
||||
switch (error) {
|
||||
#ifdef EDQUOT
|
||||
case EDQUOT:
|
||||
#endif
|
||||
#ifdef EMFILE
|
||||
case EMFILE:
|
||||
#endif
|
||||
#ifdef ENFILE
|
||||
case ENFILE:
|
||||
#endif
|
||||
#ifdef ENOBUFS
|
||||
case ENOBUFS:
|
||||
#endif
|
||||
#ifdef ENOLCK
|
||||
case ENOLCK:
|
||||
#endif
|
||||
#ifdef ENOMEM
|
||||
case ENOMEM:
|
||||
#endif
|
||||
#ifdef ENOSPC
|
||||
case ENOSPC:
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
case ETIMEDOUT:
|
||||
#endif
|
||||
#ifdef EUSERS
|
||||
case EUSERS:
|
||||
#endif
|
||||
return Exception::Type::OVERLOADED;
|
||||
|
||||
#ifdef ENOTCONN
|
||||
case ENOTCONN:
|
||||
#endif
|
||||
#ifdef ECONNABORTED
|
||||
case ECONNABORTED:
|
||||
#endif
|
||||
#ifdef ECONNREFUSED
|
||||
case ECONNREFUSED:
|
||||
#endif
|
||||
#ifdef ECONNRESET
|
||||
case ECONNRESET:
|
||||
#endif
|
||||
#ifdef EHOSTDOWN
|
||||
case EHOSTDOWN:
|
||||
#endif
|
||||
#ifdef EHOSTUNREACH
|
||||
case EHOSTUNREACH:
|
||||
#endif
|
||||
#ifdef ENETDOWN
|
||||
case ENETDOWN:
|
||||
#endif
|
||||
#ifdef ENETRESET
|
||||
case ENETRESET:
|
||||
#endif
|
||||
#ifdef ENETUNREACH
|
||||
case ENETUNREACH:
|
||||
#endif
|
||||
#ifdef ENONET
|
||||
case ENONET:
|
||||
#endif
|
||||
#ifdef EPIPE
|
||||
case EPIPE:
|
||||
#endif
|
||||
return Exception::Type::DISCONNECTED;
|
||||
|
||||
#ifdef ENOSYS
|
||||
case ENOSYS:
|
||||
#endif
|
||||
#ifdef ENOTSUP
|
||||
case ENOTSUP:
|
||||
#endif
|
||||
#if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOTSUP
|
||||
case EOPNOTSUPP:
|
||||
#endif
|
||||
#ifdef ENOPROTOOPT
|
||||
case ENOPROTOOPT:
|
||||
#endif
|
||||
#ifdef ENOTSOCK
|
||||
// This is really saying "syscall not implemented for non-sockets".
|
||||
case ENOTSOCK:
|
||||
#endif
|
||||
return Exception::Type::UNIMPLEMENTED;
|
||||
|
||||
default:
|
||||
return Exception::Type::FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum DescriptionStyle {
|
||||
LOG,
|
||||
ASSERTION,
|
||||
SYSCALL
|
||||
};
|
||||
|
||||
static String makeDescriptionImpl(DescriptionStyle style, const char* code, int errorNumber,
|
||||
const char* sysErrorString, const char* macroArgs,
|
||||
ArrayPtr<String> argValues) {
|
||||
KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64);
|
||||
|
||||
if (argValues.size() > 0) {
|
||||
size_t index = 0;
|
||||
const char* start = macroArgs;
|
||||
while (isspace(*start)) ++start;
|
||||
const char* pos = start;
|
||||
uint depth = 0;
|
||||
bool quoted = false;
|
||||
while (char c = *pos++) {
|
||||
if (quoted) {
|
||||
if (c == '\\' && *pos != '\0') {
|
||||
++pos;
|
||||
} else if (c == '\"') {
|
||||
quoted = false;
|
||||
}
|
||||
} else {
|
||||
if (c == '(') {
|
||||
++depth;
|
||||
} else if (c == ')') {
|
||||
--depth;
|
||||
} else if (c == '\"') {
|
||||
quoted = true;
|
||||
} else if (c == ',' && depth == 0) {
|
||||
if (index < argValues.size()) {
|
||||
argNames[index++] = arrayPtr(start, pos - 1);
|
||||
}
|
||||
while (isspace(*pos)) ++pos;
|
||||
start = pos;
|
||||
if (*pos == '\0') {
|
||||
// ignore trailing comma
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index < argValues.size()) {
|
||||
argNames[index++] = arrayPtr(start, pos - 1);
|
||||
}
|
||||
|
||||
if (index != argValues.size()) {
|
||||
getExceptionCallback().logMessage(LogSeverity::ERROR, __FILE__, __LINE__, 0,
|
||||
str("Failed to parse logging macro args into ",
|
||||
argValues.size(), " names: ", macroArgs, '\n'));
|
||||
}
|
||||
}
|
||||
|
||||
if (style == SYSCALL) {
|
||||
// Strip off leading "foo = " from code, since callers will sometimes write things like:
|
||||
// ssize_t n;
|
||||
// RECOVERABLE_SYSCALL(n = read(fd, buffer, sizeof(buffer))) { return ""; }
|
||||
// return std::string(buffer, n);
|
||||
const char* equalsPos = strchr(code, '=');
|
||||
if (equalsPos != nullptr && equalsPos[1] != '=') {
|
||||
code = equalsPos + 1;
|
||||
while (isspace(*code)) ++code;
|
||||
}
|
||||
}
|
||||
|
||||
if (style == ASSERTION && code == nullptr) {
|
||||
style = LOG;
|
||||
}
|
||||
|
||||
{
|
||||
StringPtr expected = "expected ";
|
||||
StringPtr codeArray = style == LOG ? nullptr : StringPtr(code);
|
||||
StringPtr sep = " = ";
|
||||
StringPtr delim = "; ";
|
||||
StringPtr colon = ": ";
|
||||
StringPtr openBracket = " [";
|
||||
StringPtr closeBracket = "]";
|
||||
|
||||
StringPtr sysErrorArray;
|
||||
// On android before marshmallow only the posix version of stderror_r was
|
||||
// available, even with __USE_GNU.
|
||||
#if __USE_GNU && !(defined(__ANDROID_API__) && __ANDROID_API__ < 23)
|
||||
char buffer[256];
|
||||
if (style == SYSCALL) {
|
||||
if (sysErrorString == nullptr) {
|
||||
sysErrorArray = strerror_r(errorNumber, buffer, sizeof(buffer));
|
||||
} else {
|
||||
sysErrorArray = sysErrorString;
|
||||
}
|
||||
}
|
||||
#else
|
||||
char buffer[256];
|
||||
if (style == SYSCALL) {
|
||||
if (sysErrorString == nullptr) {
|
||||
strerror_r(errorNumber, buffer, sizeof(buffer));
|
||||
sysErrorArray = buffer;
|
||||
} else {
|
||||
sysErrorArray = sysErrorString;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t totalSize = 0;
|
||||
switch (style) {
|
||||
case LOG:
|
||||
break;
|
||||
case ASSERTION:
|
||||
totalSize += expected.size() + codeArray.size();
|
||||
break;
|
||||
case SYSCALL:
|
||||
totalSize += codeArray.size() + colon.size() + sysErrorArray.size();
|
||||
break;
|
||||
}
|
||||
|
||||
auto needsLabel = [](ArrayPtr<const char> &argName) -> bool {
|
||||
return (argName.size() > 0 && argName[0] != '\"' &&
|
||||
!(argName.size() >= 8 && memcmp(argName.begin(), "kj::str(", 8) == 0));
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < argValues.size(); i++) {
|
||||
if (argNames[i] == "_kjCondition"_kj) {
|
||||
// Special handling: don't output delimiter, we want to append this to the previous item,
|
||||
// in brackets. Also, if it's just "[false]" (meaning we didn't manage to extract a
|
||||
// comparison), don't add it at all.
|
||||
if (argValues[i] != "false") {
|
||||
totalSize += openBracket.size() + argValues[i].size() + closeBracket.size();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > 0 || style != LOG) {
|
||||
totalSize += delim.size();
|
||||
}
|
||||
if (needsLabel(argNames[i])) {
|
||||
totalSize += argNames[i].size() + sep.size();
|
||||
}
|
||||
totalSize += argValues[i].size();
|
||||
}
|
||||
|
||||
String result = heapString(totalSize);
|
||||
char* pos = result.begin();
|
||||
|
||||
switch (style) {
|
||||
case LOG:
|
||||
break;
|
||||
case ASSERTION:
|
||||
pos = _::fill(pos, expected, codeArray);
|
||||
break;
|
||||
case SYSCALL:
|
||||
pos = _::fill(pos, codeArray, colon, sysErrorArray);
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < argValues.size(); i++) {
|
||||
if (argNames[i] == "_kjCondition"_kj) {
|
||||
// Special handling: don't output delimiter, we want to append this to the previous item,
|
||||
// in brackets. Also, if it's just "[false]" (meaning we didn't manage to extract a
|
||||
// comparison), don't add it at all.
|
||||
if (argValues[i] != "false") {
|
||||
pos = _::fill(pos, openBracket, argValues[i], closeBracket);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > 0 || style != LOG) {
|
||||
pos = _::fill(pos, delim);
|
||||
}
|
||||
if (needsLabel(argNames[i])) {
|
||||
pos = _::fill(pos, argNames[i], sep);
|
||||
}
|
||||
pos = _::fill(pos, argValues[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Debug::logInternal(const char* file, int line, LogSeverity severity, const char* macroArgs,
|
||||
ArrayPtr<String> argValues) {
|
||||
getExceptionCallback().logMessage(severity, trimSourceFilename(file).cStr(), line, 0,
|
||||
makeDescriptionImpl(LOG, nullptr, 0, nullptr, macroArgs, argValues));
|
||||
}
|
||||
|
||||
Debug::Fault::~Fault() noexcept(false) {
|
||||
if (exception != nullptr) {
|
||||
Exception copy = mv(*exception);
|
||||
delete exception;
|
||||
throwRecoverableException(mv(copy), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Debug::Fault::fatal() {
|
||||
Exception copy = mv(*exception);
|
||||
delete exception;
|
||||
exception = nullptr;
|
||||
throwFatalException(mv(copy), 1);
|
||||
KJ_KNOWN_UNREACHABLE(abort());
|
||||
}
|
||||
|
||||
void Debug::Fault::init(
|
||||
const char* file, int line, Exception::Type type,
|
||||
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
|
||||
exception = new Exception(type, file, line,
|
||||
makeDescriptionImpl(ASSERTION, condition, 0, nullptr, macroArgs, argValues));
|
||||
}
|
||||
|
||||
void Debug::Fault::init(
|
||||
const char* file, int line, int osErrorNumber,
|
||||
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
|
||||
exception = new Exception(typeOfErrno(osErrorNumber), file, line,
|
||||
makeDescriptionImpl(SYSCALL, condition, osErrorNumber, nullptr, macroArgs, argValues));
|
||||
}
|
||||
|
||||
|
||||
String Debug::makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) {
|
||||
return makeDescriptionImpl(LOG, nullptr, 0, nullptr, macroArgs, argValues);
|
||||
}
|
||||
|
||||
int Debug::getOsErrorNumber(bool nonblocking) {
|
||||
int result = errno;
|
||||
|
||||
// On many systems, EAGAIN and EWOULDBLOCK have the same value, but this is not strictly required
|
||||
// by POSIX, so we need to check both.
|
||||
return result == EINTR ? -1
|
||||
: nonblocking && (result == EAGAIN || result == EWOULDBLOCK) ? 0
|
||||
: result;
|
||||
}
|
||||
|
||||
|
||||
Debug::Context::Context(): logged(false) {}
|
||||
Debug::Context::~Context() noexcept(false) {}
|
||||
|
||||
Debug::Context::Value Debug::Context::ensureInitialized() {
|
||||
KJ_IF_MAYBE(v, value) {
|
||||
return Value(v->file, v->line, heapString(v->description));
|
||||
} else {
|
||||
Value result = evaluate();
|
||||
value = Value(result.file, result.line, heapString(result.description));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void Debug::Context::onRecoverableException(Exception&& exception) {
|
||||
Value v = ensureInitialized();
|
||||
exception.wrapContext(v.file, v.line, mv(v.description));
|
||||
next.onRecoverableException(kj::mv(exception));
|
||||
}
|
||||
void Debug::Context::onFatalException(Exception&& exception) {
|
||||
Value v = ensureInitialized();
|
||||
exception.wrapContext(v.file, v.line, mv(v.description));
|
||||
next.onFatalException(kj::mv(exception));
|
||||
}
|
||||
void Debug::Context::logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
|
||||
String&& text) {
|
||||
if (!logged) {
|
||||
Value v = ensureInitialized();
|
||||
next.logMessage(LogSeverity::INFO, trimSourceFilename(v.file).cStr(), v.line, 0,
|
||||
str("context: ", mv(v.description), '\n'));
|
||||
logged = true;
|
||||
}
|
||||
|
||||
next.logMessage(severity, file, line, contextDepth + 1, mv(text));
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
542
vendor/capnproto/src/kj/debug.h
vendored
Normal file
542
vendor/capnproto/src/kj/debug.h
vendored
Normal file
@@ -0,0 +1,542 @@
|
||||
// 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_ASSUME(condition, ...)`: Like `KJ_ASSERT`, but in release mode (if KJ_DEBUG is not
|
||||
// defined; see below) instead warrants to the compiler that the condition can be assumed to
|
||||
// hold, allowing it to optimize accordingly. This can result in undefined behavior, so use
|
||||
// this macro *only* if you can prove to your satisfaction that the condition is guaranteed by
|
||||
// surrounding code, and if the condition failing to hold would in any case result in undefined
|
||||
// behavior in its dependencies.
|
||||
//
|
||||
// * `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 except `ASSUME`, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "string.h"
|
||||
#include "exception.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
|
||||
#define KJ_LOG(severity, ...) \
|
||||
for (bool _kj_shouldLog = ::kj::_::Debug::shouldLog(::kj::LogSeverity::severity); \
|
||||
_kj_shouldLog; _kj_shouldLog = false) \
|
||||
::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 (auto _kjCondition = ::kj::_::MAGIC_ASSERT << cond) {} else \
|
||||
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, ::kj::Exception::Type::FAILED, \
|
||||
#cond, "_kjCondition," #__VA_ARGS__, _kjCondition, ##__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())
|
||||
|
||||
|
||||
#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__))
|
||||
|
||||
|
||||
#define KJ_SYSCALL_HANDLE_ERRORS(call) \
|
||||
if (int _kjSyscallError = ::kj::_::Debug::syscallError([&](){return (call);}, false)) \
|
||||
switch (int error KJ_UNUSED = _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
|
||||
#define KJ_ASSUME KJ_ASSERT
|
||||
#else
|
||||
#define KJ_DLOG(...) do {} while (false)
|
||||
#define KJ_DASSERT(...) do {} while (false)
|
||||
#define KJ_DREQUIRE(...) do {} while (false)
|
||||
#if defined(__GNUC__)
|
||||
#define KJ_ASSUME(cond, ...) do { if (cond) {} else __builtin_unreachable(); } while (false)
|
||||
#elif defined(__clang__)
|
||||
#define KJ_ASSUME(cond, ...) __builtin_assume(cond)
|
||||
#else
|
||||
#define KJ_ASSUME(...) do {} while (false)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class Debug {
|
||||
public:
|
||||
Debug() = delete;
|
||||
|
||||
typedef LogSeverity Severity; // backwards-compatibility
|
||||
|
||||
|
||||
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);
|
||||
~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);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
class Context: public ExceptionCallback {
|
||||
public:
|
||||
Context();
|
||||
KJ_DISALLOW_COPY_AND_MOVE(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_AND_MOVE(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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Magic Asserts!
|
||||
//
|
||||
// When KJ_ASSERT(foo == bar) fails, `foo` and `bar`'s actual values will be stringified in the
|
||||
// error message. How does it work? We use template magic and operator precedence. The assertion
|
||||
// actually evaluates something like this:
|
||||
//
|
||||
// if (auto _kjCondition = kj::_::MAGIC_ASSERT << foo == bar)
|
||||
//
|
||||
// `<<` has operator precedence slightly above `==`, so `kj::_::MAGIC_ASSERT << foo` gets evaluated
|
||||
// first. This wraps `foo` in a little wrapper that captures the comparison operators and keeps
|
||||
// enough information around to be able to stringify the left and right sides of the comparison
|
||||
// independently. As always, the stringification only actually occurs if the assert fails.
|
||||
//
|
||||
// You might ask why we use operator `<<` and not e.g. operator `<=`, since operators of the same
|
||||
// precedence are evaluated left-to-right. The answer is that some compilers trigger all sorts of
|
||||
// warnings when you seem to be using a comparison as the input to another comparison. The
|
||||
// particular warning GCC produces is its general "-Wparentheses" warning which is broadly useful,
|
||||
// so we don't want to disable it. `<<` also produces some warnings, but only on Clang and the
|
||||
// specific warning is one we're comfortable disabling (see below). This does mean that we have to
|
||||
// explicitly overload `operator<<` ourselves to make sure using it in an assert still works.
|
||||
//
|
||||
// You might also ask, if we're using operator `<<` anyway, why not start it from the right, in
|
||||
// which case it would bind after computing any `<<` operators that were actually in the user's
|
||||
// code? I tried this, but it resulted in a somewhat broader warning from clang that I felt worse
|
||||
// about disabling (a warning about `<<` precedence not applying specifically to overloads) and
|
||||
// also created ambiguous overload errors in the KJ units code.
|
||||
|
||||
#if __clang__
|
||||
// We intentionally overload operator << for the specific purpose of evaluating it before
|
||||
// evaluating comparison expressions, so stop Clang from warning about it. Unfortunately this means
|
||||
// eliminating a warning that would otherwise be useful for people using iostreams... sorry.
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
struct DebugExpression;
|
||||
|
||||
template <typename T, typename = decltype(toCharSequence(instance<T&>()))>
|
||||
inline auto tryToCharSequence(T* value) { return kj::toCharSequence(*value); }
|
||||
inline StringPtr tryToCharSequence(...) { return "(can't stringify)"_kj; }
|
||||
// SFINAE to stringify a value if and only if it can be stringified.
|
||||
|
||||
template <typename Left, typename Right>
|
||||
struct DebugComparison {
|
||||
Left left;
|
||||
Right right;
|
||||
StringPtr op;
|
||||
bool result;
|
||||
|
||||
inline operator bool() const { return KJ_LIKELY(result); }
|
||||
|
||||
template <typename T> inline void operator&(T&& other) = delete;
|
||||
template <typename T> inline void operator^(T&& other) = delete;
|
||||
template <typename T> inline void operator|(T&& other) = delete;
|
||||
};
|
||||
|
||||
template <typename Left, typename Right>
|
||||
String KJ_STRINGIFY(DebugComparison<Left, Right>& cmp) {
|
||||
return _::concat(tryToCharSequence(&cmp.left), cmp.op, tryToCharSequence(&cmp.right));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct DebugExpression {
|
||||
DebugExpression(T&& value): value(kj::fwd<T>(value)) {}
|
||||
T value;
|
||||
|
||||
// Handle comparison operations by constructing a DebugComparison value.
|
||||
#define DEFINE_OPERATOR(OP) \
|
||||
template <typename U> \
|
||||
DebugComparison<T, U> operator OP(U&& other) { \
|
||||
bool result = value OP other; \
|
||||
return { kj::fwd<T>(value), kj::fwd<U>(other), " " #OP " "_kj, result }; \
|
||||
}
|
||||
DEFINE_OPERATOR(==);
|
||||
DEFINE_OPERATOR(!=);
|
||||
DEFINE_OPERATOR(<=);
|
||||
DEFINE_OPERATOR(>=);
|
||||
DEFINE_OPERATOR(< );
|
||||
DEFINE_OPERATOR(> );
|
||||
#undef DEFINE_OPERATOR
|
||||
|
||||
// Handle binary operators that have equal or lower precedence than comparisons by performing
|
||||
// the operation and wrapping the result.
|
||||
#define DEFINE_OPERATOR(OP) \
|
||||
template <typename U> inline auto operator OP(U&& other) { \
|
||||
return DebugExpression<decltype(kj::fwd<T>(value) OP kj::fwd<U>(other))>(\
|
||||
kj::fwd<T>(value) OP kj::fwd<U>(other)); \
|
||||
}
|
||||
DEFINE_OPERATOR(<<);
|
||||
DEFINE_OPERATOR(>>);
|
||||
DEFINE_OPERATOR(&);
|
||||
DEFINE_OPERATOR(^);
|
||||
DEFINE_OPERATOR(|);
|
||||
#undef DEFINE_OPERATOR
|
||||
|
||||
inline operator bool() {
|
||||
// No comparison performed, we're just asserting the expression is truthy. This also covers
|
||||
// the case of the logic operators && and || -- we cannot overload those because doing so would
|
||||
// break short-circuiting behavior.
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
StringPtr KJ_STRINGIFY(const DebugExpression<T>& exp) {
|
||||
// Hack: This will only ever be called in cases where the expression's truthiness was asserted
|
||||
// directly, and was determined to be falsy.
|
||||
return "false"_kj;
|
||||
}
|
||||
|
||||
struct DebugExpressionStart {
|
||||
template <typename T>
|
||||
DebugExpression<T> operator<<(T&& value) const {
|
||||
return DebugExpression<T>(kj::fwd<T>(value));
|
||||
}
|
||||
};
|
||||
static constexpr DebugExpressionStart MAGIC_ASSERT;
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
72
vendor/capnproto/src/kj/encoding.c++
vendored
Normal file
72
vendor/capnproto/src/kj/encoding.c++
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2017 Cloudflare, Inc.; 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 "encoding.h"
|
||||
#include "vector.h"
|
||||
|
||||
namespace kj {
|
||||
|
||||
namespace {
|
||||
|
||||
const char HEX_DIGITS[] = "0123456789abcdef";
|
||||
// Maps integer in the range [0,16) to a hex digit.
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
String encodeCEscapeImpl(ArrayPtr<const byte> bytes, bool isBinary) {
|
||||
Vector<char> escaped(bytes.size());
|
||||
|
||||
for (byte b: bytes) {
|
||||
switch (b) {
|
||||
case '\a': escaped.addAll(StringPtr("\\a")); break;
|
||||
case '\b': escaped.addAll(StringPtr("\\b")); break;
|
||||
case '\f': escaped.addAll(StringPtr("\\f")); break;
|
||||
case '\n': escaped.addAll(StringPtr("\\n")); break;
|
||||
case '\r': escaped.addAll(StringPtr("\\r")); break;
|
||||
case '\t': escaped.addAll(StringPtr("\\t")); break;
|
||||
case '\v': escaped.addAll(StringPtr("\\v")); break;
|
||||
case '\'': escaped.addAll(StringPtr("\\\'")); break;
|
||||
case '\"': escaped.addAll(StringPtr("\\\"")); break;
|
||||
case '\\': escaped.addAll(StringPtr("\\\\")); break;
|
||||
default:
|
||||
if (b < 0x20 || b == 0x7f || (isBinary && b > 0x7f)) {
|
||||
// Use octal escape, not hex, because hex escapes technically have no length limit and
|
||||
// so can create ambiguity with subsequent characters.
|
||||
escaped.add('\\');
|
||||
escaped.add(HEX_DIGITS[b / 64]);
|
||||
escaped.add(HEX_DIGITS[(b / 8) % 8]);
|
||||
escaped.add(HEX_DIGITS[b % 8]);
|
||||
} else {
|
||||
escaped.add(b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
escaped.add(0);
|
||||
return String(escaped.releaseAsArray());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace kj
|
||||
69
vendor/capnproto/src/kj/encoding.h
vendored
Normal file
69
vendor/capnproto/src/kj/encoding.h
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2017 Cloudflare, 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.
|
||||
|
||||
#pragma once
|
||||
// C-style string escaping.
|
||||
|
||||
#include "string.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
String encodeCEscape(ArrayPtr<const byte> bytes);
|
||||
String encodeCEscape(ArrayPtr<const char> bytes);
|
||||
|
||||
// =======================================================================================
|
||||
// inline implementation details
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
String encodeCEscapeImpl(ArrayPtr<const byte> bytes, bool isBinary);
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
inline String encodeCEscape(ArrayPtr<const char> text) {
|
||||
return _::encodeCEscapeImpl(text.asBytes(), false);
|
||||
}
|
||||
|
||||
inline String encodeCEscape(ArrayPtr<const byte> bytes) {
|
||||
return _::encodeCEscapeImpl(bytes, true);
|
||||
}
|
||||
|
||||
|
||||
// If you pass a string literal to a function taking ArrayPtr<const char>, it'll include the NUL
|
||||
// terminator. These overloads avoid including it.
|
||||
|
||||
template <size_t s>
|
||||
inline String encodeCEscape(const char (&text)[s]) {
|
||||
return encodeCEscape(arrayPtr(text, s - 1));
|
||||
}
|
||||
|
||||
#if __cpp_char8_t
|
||||
template <size_t s>
|
||||
inline String encodeCEscape(const char8_t (&text)[s]) {
|
||||
return encodeCEscape(arrayPtr(reinterpret_cast<const char*>(text), s - 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
822
vendor/capnproto/src/kj/exception.c++
vendored
Normal file
822
vendor/capnproto/src/kj/exception.c++
vendored
Normal file
@@ -0,0 +1,822 @@
|
||||
// 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 _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include "exception.h"
|
||||
#include "string.h"
|
||||
#include "debug.h"
|
||||
#include "threadlocal.h"
|
||||
#include "miniposix.h"
|
||||
#include <stdlib.h>
|
||||
#include <exception>
|
||||
#include <new>
|
||||
#include <stdint.h>
|
||||
|
||||
#if !KJ_NO_RTTI
|
||||
#include <typeinfo>
|
||||
#endif
|
||||
#if __GNUC__
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#ifndef KJ_USE_BACKTRACE
|
||||
#if (__linux__ && __GLIBC__ && !__UCLIBC__) || __APPLE__
|
||||
#define KJ_USE_BACKTRACE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if KJ_USE_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if (__linux__ || __APPLE__)
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if KJ_HAS_LIBDL
|
||||
#include "dlfcn.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if KJ_HAS_COMPILER_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#else
|
||||
static void __lsan_ignore_object(const void* p) {}
|
||||
#endif
|
||||
// TODO(cleanup): Remove the LSAN stuff per https://github.com/capnproto/capnproto/pull/1255
|
||||
// feedback.
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
inline T* lsanIgnoreObjectAndReturn(T* ptr) {
|
||||
// Defensively lsan_ignore_object since the documentation doesn't explicitly specify what happens
|
||||
// if you call this multiple times on the same object.
|
||||
// TODO(cleanup): Remove this per https://github.com/capnproto/capnproto/pull/1255.
|
||||
__lsan_ignore_object(ptr);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace kj {
|
||||
|
||||
StringPtr KJ_STRINGIFY(LogSeverity severity) {
|
||||
static const char* SEVERITY_STRINGS[] = {
|
||||
"info",
|
||||
"warning",
|
||||
"error",
|
||||
"fatal",
|
||||
"debug"
|
||||
};
|
||||
|
||||
return SEVERITY_STRINGS[static_cast<uint>(severity)];
|
||||
}
|
||||
|
||||
|
||||
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
|
||||
if (getExceptionCallback().stackTraceMode() == ExceptionCallback::StackTraceMode::NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if KJ_USE_BACKTRACE
|
||||
size_t size = backtrace(space.begin(), space.size());
|
||||
for (auto& addr: space.slice(0, size)) {
|
||||
// The addresses produced by backtrace() are return addresses, which means they point to the
|
||||
// instruction immediately after the call. Invoking addr2line on these can be confusing because
|
||||
// it often points to the next line. If the next instruction is inlined from another function,
|
||||
// the trace can be extra-confusing, since now it claims to be in a function that was not
|
||||
// actually on the call stack. If we subtract 1 from each address, though, we get a much more
|
||||
// reasonable trace. This may cause the addresses to be invalid instruction pointers if the
|
||||
// instructions were multi-byte, but it appears addr2line is able to cope with this.
|
||||
addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - 1);
|
||||
}
|
||||
return space.slice(kj::min(ignoreCount + 1, size), size);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __GNUC__ || __clang__
|
||||
// Allow dependents to override the implementation of stack symbolication by making it a weak
|
||||
// symbol. We prefer weak symbols over some sort of callback registration mechanism becasue this
|
||||
// allows an alternate symbolication library to be easily linked into tests without changing the
|
||||
// code of the test.
|
||||
__attribute__((weak))
|
||||
#endif
|
||||
String stringifyStackTrace(ArrayPtr<void* const> trace) {
|
||||
if (trace.size() == 0) return nullptr;
|
||||
if (getExceptionCallback().stackTraceMode() != ExceptionCallback::StackTraceMode::FULL) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if (__linux__ || __APPLE__) && !__ANDROID__
|
||||
// We want to generate a human-readable stack trace.
|
||||
|
||||
// TODO(someday): It would be really great if we could avoid farming out to another process
|
||||
// and do this all in-process, but that may involve onerous requirements like large library
|
||||
// dependencies or using -rdynamic.
|
||||
|
||||
// The environment manipulation is not thread-safe, so lock a mutex. This could still be
|
||||
// problematic if another thread is manipulating the environment in unrelated code, but there's
|
||||
// not much we can do about that. This is debug-only anyway and only an issue when LD_PRELOAD
|
||||
// is in use.
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&mutex);
|
||||
KJ_DEFER(pthread_mutex_unlock(&mutex));
|
||||
|
||||
// Don't heapcheck / intercept syscalls.
|
||||
const char* preload = getenv("LD_PRELOAD");
|
||||
String oldPreload;
|
||||
if (preload != nullptr) {
|
||||
oldPreload = heapString(preload);
|
||||
unsetenv("LD_PRELOAD");
|
||||
}
|
||||
KJ_DEFER(if (oldPreload != nullptr) { setenv("LD_PRELOAD", oldPreload.cStr(), true); });
|
||||
|
||||
String lines[32];
|
||||
FILE* p = nullptr;
|
||||
auto strTrace = strArray(trace, " ");
|
||||
|
||||
#if __linux__
|
||||
if (access("/proc/self/exe", R_OK) < 0) {
|
||||
// Apparently /proc is not available?
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Obtain symbolic stack trace using addr2line.
|
||||
// TODO(cleanup): Use fork() and exec() or maybe our own Subprocess API (once it exists), to
|
||||
// avoid depending on a shell.
|
||||
p = popen(str("addr2line -e /proc/", getpid(), "/exe ", strTrace).cStr(), "r");
|
||||
#elif __APPLE__
|
||||
// The Mac OS X equivalent of addr2line is atos.
|
||||
// (Internally, it uses the private CoreSymbolication.framework library.)
|
||||
p = popen(str("xcrun atos -p ", getpid(), ' ', strTrace).cStr(), "r");
|
||||
#endif
|
||||
|
||||
if (p == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char line[512];
|
||||
size_t i = 0;
|
||||
while (i < kj::size(lines) && fgets(line, sizeof(line), p) != nullptr) {
|
||||
// Don't include exception-handling infrastructure or promise infrastructure in stack trace.
|
||||
// addr2line output matches file names; atos output matches symbol names.
|
||||
if (strstr(line, "kj/common.c++") != nullptr ||
|
||||
strstr(line, "kj/exception.") != nullptr ||
|
||||
strstr(line, "kj/debug.") != nullptr ||
|
||||
strstr(line, "kj/async.") != nullptr ||
|
||||
strstr(line, "kj/async-prelude.h") != nullptr ||
|
||||
strstr(line, "kj/async-inl.h") != nullptr ||
|
||||
strstr(line, "kj::Exception") != nullptr ||
|
||||
strstr(line, "kj::_::Debug") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t len = strlen(line);
|
||||
if (len > 0 && line[len-1] == '\n') line[len-1] = '\0';
|
||||
lines[i++] = str("\n ", trimSourceFilename(line), ": returning here");
|
||||
}
|
||||
|
||||
// Skip remaining input.
|
||||
while (fgets(line, sizeof(line), p) != nullptr) {}
|
||||
|
||||
pclose(p);
|
||||
|
||||
return strArray(arrayPtr(lines, i), "");
|
||||
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
String stringifyStackTraceAddresses(ArrayPtr<void* const> trace) {
|
||||
#if KJ_HAS_LIBDL
|
||||
return strArray(KJ_MAP(addr, trace) {
|
||||
Dl_info info;
|
||||
// Shared libraries are mapped near the end of the address space while the executable is mapped
|
||||
// near the beginning. We want to print addresses in the executable as raw addresses, not
|
||||
// offsets, since that's what addr2line expects for executables. For shared libraries it
|
||||
// expects offsets. In any case, most frames are likely to be in the main executable so it
|
||||
// makes the output cleaner if we don't repeatedly write its name.
|
||||
if (reinterpret_cast<uintptr_t>(addr) >= 0x400000000000ull && dladdr(addr, &info)) {
|
||||
uintptr_t offset = reinterpret_cast<uintptr_t>(addr) -
|
||||
reinterpret_cast<uintptr_t>(info.dli_fbase);
|
||||
return kj::str(info.dli_fname, '@', reinterpret_cast<void*>(offset));
|
||||
} else {
|
||||
return kj::str(addr);
|
||||
}
|
||||
}, " ");
|
||||
#else
|
||||
// TODO(someday): Support other platforms.
|
||||
return kj::strArray(trace, " ");
|
||||
#endif
|
||||
}
|
||||
|
||||
StringPtr stringifyStackTraceAddresses(ArrayPtr<void* const> trace, ArrayPtr<char> scratch) {
|
||||
// Version which writes into a pre-allocated buffer. This is safe for signal handlers to the
|
||||
// extent that dladdr() is safe.
|
||||
//
|
||||
// TODO(cleanup): We should improve the KJ stringification framework so that there's a way to
|
||||
// write this string directly into a larger message buffer with strPreallocated().
|
||||
|
||||
#if KJ_HAS_LIBDL
|
||||
char* ptr = scratch.begin();
|
||||
char* limit = scratch.end() - 1;
|
||||
|
||||
for (auto addr: trace) {
|
||||
Dl_info info;
|
||||
// Shared libraries are mapped near the end of the address space while the executable is mapped
|
||||
// near the beginning. We want to print addresses in the executable as raw addresses, not
|
||||
// offsets, since that's what addr2line expects for executables. For shared libraries it
|
||||
// expects offsets. In any case, most frames are likely to be in the main executable so it
|
||||
// makes the output cleaner if we don't repeatedly write its name.
|
||||
if (reinterpret_cast<uintptr_t>(addr) >= 0x400000000000ull && dladdr(addr, &info)) {
|
||||
uintptr_t offset = reinterpret_cast<uintptr_t>(addr) -
|
||||
reinterpret_cast<uintptr_t>(info.dli_fbase);
|
||||
ptr = _::fillLimited(ptr, limit, kj::StringPtr(info.dli_fname), "@0x"_kj, hex(offset));
|
||||
} else {
|
||||
ptr = _::fillLimited(ptr, limit, toCharSequence(addr));
|
||||
}
|
||||
|
||||
ptr = _::fillLimited(ptr, limit, " "_kj);
|
||||
}
|
||||
*ptr = '\0';
|
||||
return StringPtr(scratch.begin(), ptr);
|
||||
#else
|
||||
// TODO(someday): Support other platforms.
|
||||
return kj::strPreallocated(scratch, kj::delimited(trace, " "));
|
||||
#endif
|
||||
}
|
||||
|
||||
String getStackTrace() {
|
||||
void* space[32];
|
||||
auto trace = getStackTrace(space, 2);
|
||||
return kj::str(stringifyStackTraceAddresses(trace), stringifyStackTrace(trace));
|
||||
}
|
||||
|
||||
kj::StringPtr trimSourceFilename(kj::StringPtr filename) {
|
||||
// Removes noisy prefixes from source code file name.
|
||||
//
|
||||
// The goal here is to produce the "canonical" filename given the filename returned by e.g.
|
||||
// addr2line. addr2line gives us the full path of the file as passed on the compiler
|
||||
// command-line, which in turn is affected by build system and by whether and where we're
|
||||
// performing an out-of-tree build.
|
||||
//
|
||||
// To deal with all this, we look for directory names in the path which we recognize to be
|
||||
// locations that represent roots of the source tree. We strip said root and everything before
|
||||
// it.
|
||||
//
|
||||
// On Windows, we often get filenames containing backslashes. Since we aren't allowed to allocate
|
||||
// a new string here, we can't do much about this, so our returned "canonical" name will
|
||||
// unfortunately end up with backslashes.
|
||||
|
||||
static constexpr const char* ROOTS[] = {
|
||||
"ekam-provider/canonical/", // Ekam source file.
|
||||
"ekam-provider/c++header/", // Ekam include file.
|
||||
"src/", // Non-Ekam source root.
|
||||
"tmp/", // Non-Ekam generated code.
|
||||
};
|
||||
|
||||
retry:
|
||||
for (size_t i: kj::indices(filename)) {
|
||||
if (i == 0 || filename[i-1] == '/'
|
||||
) {
|
||||
// We're at the start of a directory name. Check for valid prefixes.
|
||||
for (kj::StringPtr root: ROOTS) {
|
||||
if (filename.slice(i).startsWith(root)) {
|
||||
filename = filename.slice(i + root.size());
|
||||
|
||||
// We should keep searching to find the last instance of a root name. `i` is no longer
|
||||
// a valid index for `filename` so start the loop over.
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
StringPtr KJ_STRINGIFY(Exception::Type type) {
|
||||
static const char* TYPE_STRINGS[] = {
|
||||
"failed",
|
||||
"overloaded",
|
||||
"disconnected",
|
||||
"unimplemented"
|
||||
};
|
||||
|
||||
return TYPE_STRINGS[static_cast<uint>(type)];
|
||||
}
|
||||
|
||||
String KJ_STRINGIFY(const Exception& e) {
|
||||
uint contextDepth = 0;
|
||||
|
||||
Maybe<const Exception::Context&> contextPtr = e.getContext();
|
||||
for (;;) {
|
||||
KJ_IF_MAYBE(c, contextPtr) {
|
||||
++contextDepth;
|
||||
contextPtr = c->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Array<String> contextText = heapArray<String>(contextDepth);
|
||||
|
||||
contextDepth = 0;
|
||||
contextPtr = e.getContext();
|
||||
for (;;) {
|
||||
KJ_IF_MAYBE(c, contextPtr) {
|
||||
contextText[contextDepth++] =
|
||||
str(trimSourceFilename(c->file), ":", c->line, ": context: ", c->description, "\n");
|
||||
contextPtr = c->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return str(strArray(contextText, ""),
|
||||
e.getFile(), ":", e.getLine(), ": ", e.getType(),
|
||||
e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
|
||||
e.getStackTrace().size() > 0 ? "\nstack: " : "",
|
||||
stringifyStackTraceAddresses(e.getStackTrace()),
|
||||
stringifyStackTrace(e.getStackTrace()));
|
||||
}
|
||||
|
||||
Exception::Exception(Type type, const char* file, int line, String description) noexcept
|
||||
: file(trimSourceFilename(file).cStr()), line(line), type(type), description(mv(description)),
|
||||
traceCount(0) {}
|
||||
|
||||
Exception::Exception(Type type, String file, int line, String description) noexcept
|
||||
: ownFile(kj::mv(file)), file(trimSourceFilename(ownFile).cStr()), line(line), type(type),
|
||||
description(mv(description)), traceCount(0) {}
|
||||
|
||||
Exception::Exception(const Exception& other) noexcept
|
||||
: file(other.file), line(other.line), type(other.type),
|
||||
description(heapString(other.description)), traceCount(other.traceCount) {
|
||||
if (file == other.ownFile.cStr()) {
|
||||
ownFile = heapString(other.ownFile);
|
||||
file = ownFile.cStr();
|
||||
}
|
||||
|
||||
|
||||
memcpy(trace, other.trace, sizeof(trace[0]) * traceCount);
|
||||
|
||||
KJ_IF_MAYBE(c, other.context) {
|
||||
context = heap(**c);
|
||||
}
|
||||
}
|
||||
|
||||
Exception::~Exception() noexcept {}
|
||||
|
||||
Exception::Context::Context(const Context& other) noexcept
|
||||
: file(other.file), line(other.line), description(str(other.description)) {
|
||||
KJ_IF_MAYBE(n, other.next) {
|
||||
next = heap(**n);
|
||||
}
|
||||
}
|
||||
|
||||
void Exception::wrapContext(const char* file, int line, String&& description) {
|
||||
context = heap<Context>(file, line, mv(description), mv(context));
|
||||
}
|
||||
|
||||
void Exception::extendTrace(uint ignoreCount, uint limit) {
|
||||
if (isFullTrace) {
|
||||
// Awkward: extendTrace() was called twice without truncating in between. This should probably
|
||||
// be an error, but historically we didn't check for this so I'm hesitant to make it an error
|
||||
// now. We shouldn't actually extend the trace, though, as our current trace is presumably
|
||||
// rooted in main() and it'd be weird to append frames "above" that.
|
||||
// TODO(cleanup): Abort here and see what breaks?
|
||||
return;
|
||||
}
|
||||
|
||||
KJ_STACK_ARRAY(void*, newTraceSpace, kj::min(kj::size(trace), limit) + ignoreCount + 1,
|
||||
sizeof(trace)/sizeof(trace[0]) + 8, 128);
|
||||
|
||||
auto newTrace = kj::getStackTrace(newTraceSpace, ignoreCount + 1);
|
||||
if (newTrace.size() > ignoreCount + 2) {
|
||||
// Remove suffix that won't fit into our static-sized trace.
|
||||
newTrace = newTrace.slice(0, kj::min(kj::size(trace) - traceCount, newTrace.size()));
|
||||
|
||||
// Copy the rest into our trace.
|
||||
memcpy(trace + traceCount, newTrace.begin(), newTrace.asBytes().size());
|
||||
traceCount += newTrace.size();
|
||||
isFullTrace = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Exception::truncateCommonTrace() {
|
||||
if (isFullTrace) {
|
||||
// We're truncating the common portion of the full trace, turning it back into a limited
|
||||
// trace.
|
||||
isFullTrace = false;
|
||||
} else {
|
||||
// If the trace was never extended in the first place, trying to truncate it is at best a waste
|
||||
// of time and at worst might remove information for no reason. So, don't.
|
||||
//
|
||||
// This comes up in particular in coroutines, when the exception originated from a co_awaited
|
||||
// promise. In that case we manually add the one relevant frame to the trace, rather than
|
||||
// call extendTrace() just to have to truncate most of it again a moment later in the
|
||||
// unhandled_exception() callback.
|
||||
return;
|
||||
}
|
||||
|
||||
if (traceCount > 0) {
|
||||
// Create a "reference" stack trace that is a little bit deeper than the one in the exception.
|
||||
void* refTraceSpace[sizeof(this->trace) / sizeof(this->trace[0]) + 4];
|
||||
auto refTrace = kj::getStackTrace(refTraceSpace, 0);
|
||||
|
||||
// We expect that the deepest frame in the exception's stack trace should be somewhere in our
|
||||
// own trace, since our own trace has a deeper limit. Search for it.
|
||||
for (uint i = refTrace.size(); i > 0; i--) {
|
||||
if (refTrace[i-1] == trace[traceCount-1]) {
|
||||
// See how many frames match.
|
||||
for (uint j = 0; j < i; j++) {
|
||||
if (j >= traceCount) {
|
||||
// We matched the whole trace, apparently?
|
||||
traceCount = 0;
|
||||
return;
|
||||
} else if (refTrace[i-j-1] != trace[traceCount-j-1]) {
|
||||
// Found mismatching entry.
|
||||
|
||||
// If we matched more than half of the reference trace, guess that this is in fact
|
||||
// the prefix we're looking for.
|
||||
if (j > refTrace.size() / 2) {
|
||||
// Delete the matching suffix. Also delete one non-matched entry on the assumption
|
||||
// that both traces contain that stack frame but are simply at different points in
|
||||
// the function.
|
||||
traceCount -= j + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No match. Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
#if !KJ_NO_EXCEPTIONS
|
||||
|
||||
class ExceptionImpl: public Exception, public std::exception {
|
||||
public:
|
||||
inline ExceptionImpl(Exception&& other): Exception(mv(other)) {}
|
||||
ExceptionImpl(const ExceptionImpl& other): Exception(other) {}
|
||||
|
||||
const char* what() const noexcept override;
|
||||
|
||||
private:
|
||||
mutable String whatBuffer;
|
||||
|
||||
};
|
||||
|
||||
const char* ExceptionImpl::what() const noexcept {
|
||||
whatBuffer = str(*this);
|
||||
return whatBuffer.begin();
|
||||
}
|
||||
|
||||
#endif // !KJ_NO_EXCEPTIONS
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
KJ_THREADLOCAL_PTR(ExceptionCallback) threadLocalCallback = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
void requireOnStack(void* ptr, kj::StringPtr description) {
|
||||
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || \
|
||||
KJ_HAS_COMPILER_FEATURE(address_sanitizer) || \
|
||||
KJ_HAS_COMPILER_FEATURE(hwaddress_sanitizer) || \
|
||||
defined(__SANITIZE_ADDRESS__)
|
||||
// When using libfuzzer or ASAN, this sanity check may spurriously fail, so skip it.
|
||||
#else
|
||||
char stackVar;
|
||||
ptrdiff_t offset = reinterpret_cast<char*>(ptr) - &stackVar;
|
||||
KJ_REQUIRE(offset < 65536 && offset > -65536,
|
||||
kj::str(description));
|
||||
#endif
|
||||
}
|
||||
|
||||
ExceptionCallback::ExceptionCallback(): next(getExceptionCallback()) {
|
||||
requireOnStack(this, "ExceptionCallback must be allocated on the stack.");
|
||||
threadLocalCallback = this;
|
||||
}
|
||||
|
||||
ExceptionCallback::ExceptionCallback(ExceptionCallback& next): next(next) {}
|
||||
|
||||
ExceptionCallback::~ExceptionCallback() noexcept(false) {
|
||||
if (&next != this) {
|
||||
threadLocalCallback = &next;
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionCallback::onRecoverableException(Exception&& exception) {
|
||||
next.onRecoverableException(mv(exception));
|
||||
}
|
||||
|
||||
void ExceptionCallback::onFatalException(Exception&& exception) {
|
||||
next.onFatalException(mv(exception));
|
||||
}
|
||||
|
||||
void ExceptionCallback::logMessage(
|
||||
LogSeverity severity, const char* file, int line, int contextDepth, String&& text) {
|
||||
next.logMessage(severity, file, line, contextDepth, mv(text));
|
||||
}
|
||||
|
||||
ExceptionCallback::StackTraceMode ExceptionCallback::stackTraceMode() {
|
||||
return next.stackTraceMode();
|
||||
}
|
||||
|
||||
namespace _ { // private
|
||||
uint uncaughtExceptionCount(); // defined later in this file
|
||||
}
|
||||
|
||||
class ExceptionCallback::RootExceptionCallback: public ExceptionCallback {
|
||||
public:
|
||||
RootExceptionCallback(): ExceptionCallback(*this) {}
|
||||
|
||||
void onRecoverableException(Exception&& exception) override {
|
||||
#if KJ_NO_EXCEPTIONS
|
||||
logException(LogSeverity::ERROR, mv(exception));
|
||||
#else
|
||||
if (_::uncaughtExceptionCount() > 0) {
|
||||
// Bad time to throw an exception. Just log instead.
|
||||
//
|
||||
// TODO(someday): We should really compare uncaughtExceptionCount() against the count at
|
||||
// the innermost runCatchingExceptions() frame in this thread to tell if exceptions are
|
||||
// being caught correctly.
|
||||
logException(LogSeverity::ERROR, mv(exception));
|
||||
} else {
|
||||
throw ExceptionImpl(mv(exception));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void onFatalException(Exception&& exception) override {
|
||||
#if KJ_NO_EXCEPTIONS
|
||||
logException(LogSeverity::FATAL, mv(exception));
|
||||
#else
|
||||
throw ExceptionImpl(mv(exception));
|
||||
#endif
|
||||
}
|
||||
|
||||
void logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
|
||||
String&& text) override {
|
||||
text = str(kj::repeat('_', contextDepth), file, ":", line, ": ", severity, ": ",
|
||||
mv(text), '\n');
|
||||
|
||||
StringPtr textPtr = text;
|
||||
|
||||
while (textPtr != nullptr) {
|
||||
miniposix::ssize_t n = miniposix::write(STDERR_FILENO, textPtr.begin(), textPtr.size());
|
||||
if (n <= 0) {
|
||||
// stderr is broken. Give up.
|
||||
return;
|
||||
}
|
||||
textPtr = textPtr.slice(n);
|
||||
}
|
||||
}
|
||||
|
||||
StackTraceMode stackTraceMode() override {
|
||||
#ifdef KJ_DEBUG
|
||||
return StackTraceMode::FULL;
|
||||
#else
|
||||
return StackTraceMode::ADDRESS_ONLY;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
void logException(LogSeverity severity, Exception&& e) {
|
||||
// We intentionally go back to the top exception callback on the stack because we don't want to
|
||||
// bypass whatever log processing is in effect.
|
||||
//
|
||||
// We intentionally don't log the context since it should get re-added by the exception callback
|
||||
// anyway.
|
||||
getExceptionCallback().logMessage(severity, e.getFile(), e.getLine(), 0, str(
|
||||
e.getType(), e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
|
||||
e.getStackTrace().size() > 0 ? "\nstack: " : "",
|
||||
stringifyStackTraceAddresses(e.getStackTrace()),
|
||||
stringifyStackTrace(e.getStackTrace()), "\n"));
|
||||
}
|
||||
};
|
||||
|
||||
ExceptionCallback& getExceptionCallback() {
|
||||
static auto defaultCallback = lsanIgnoreObjectAndReturn(
|
||||
new ExceptionCallback::RootExceptionCallback());
|
||||
// We allocate on the heap because some objects may throw in their destructors. If those objects
|
||||
// had static storage, they might get fully constructed before the root callback. If they however
|
||||
// then throw an exception during destruction, there would be a lifetime issue because their
|
||||
// destructor would end up getting registered after the root callback's destructor. One solution
|
||||
// is to just leak this pointer & allocate on first-use. The cost is that the initialization is
|
||||
// mildly more expensive (+ we need to annotate sanitizers to ignore the problem). A great
|
||||
// compiler annotation that would simply things would be one that allowed static variables to have
|
||||
// their destruction omitted wholesale. That would allow us to avoid the heap but still have the
|
||||
// same robust safety semantics leaking would give us. A practical alternative that could be
|
||||
// implemented without new compilers would be to define another static root callback in
|
||||
// RootExceptionCallback's destructor (+ a separate pointer to share its value with this
|
||||
// function). Since this would end up getting constructed during exit unwind, it would have the
|
||||
// nice property of effectively being guaranteed to be evicted last.
|
||||
//
|
||||
// All this being said, I came back to leaking the object is the easiest tweak here:
|
||||
// * Can't go wrong
|
||||
// * Easy to maintain
|
||||
// * Throwing exceptions is bound to do be expensive and malloc-happy anyway, so the incremental
|
||||
// cost of 1 heap allocation is minimal.
|
||||
//
|
||||
// TODO(cleanup): Harris has an excellent suggestion in
|
||||
// https://github.com/capnproto/capnproto/pull/1255 that should ensure we initialize the root
|
||||
// callback once on first use as a global & never destroy it.
|
||||
|
||||
ExceptionCallback* scoped = threadLocalCallback;
|
||||
return scoped != nullptr ? *scoped : *defaultCallback;
|
||||
}
|
||||
|
||||
void throwFatalException(kj::Exception&& exception, uint ignoreCount) {
|
||||
if (ignoreCount != (uint)kj::maxValue) exception.extendTrace(ignoreCount + 1);
|
||||
getExceptionCallback().onFatalException(kj::mv(exception));
|
||||
abort();
|
||||
}
|
||||
|
||||
void throwRecoverableException(kj::Exception&& exception, uint ignoreCount) {
|
||||
if (ignoreCount != (uint)kj::maxValue) exception.extendTrace(ignoreCount + 1);
|
||||
getExceptionCallback().onRecoverableException(kj::mv(exception));
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
#if KJ_CPP_STD >= 201703L
|
||||
|
||||
uint uncaughtExceptionCount() {
|
||||
return std::uncaught_exceptions();
|
||||
}
|
||||
|
||||
#elif __GNUC__
|
||||
|
||||
// Horrible -- but working -- hack: We can dig into __cxa_get_globals() in order to extract the
|
||||
// count of uncaught exceptions. This function is part of the C++ ABI implementation used on Linux,
|
||||
// OSX, and probably other platforms that use GCC. Unfortunately, __cxa_get_globals() is only
|
||||
// actually defined in cxxabi.h on some platforms (e.g. Linux, but not OSX), and even where it is
|
||||
// defined, it returns an incomplete type. Here we use the same hack used by Evgeny Panasyuk:
|
||||
// https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp
|
||||
//
|
||||
// Notice that a similar hack is possible on MSVC -- if its C++11 support ever gets to the point of
|
||||
// supporting KJ in the first place.
|
||||
//
|
||||
// It appears likely that a future version of the C++ standard may include an
|
||||
// uncaught_exception_count() function in the standard library, or an equivalent language feature.
|
||||
// Some discussion:
|
||||
// https://groups.google.com/a/isocpp.org/d/msg/std-proposals/HglEslyZFYs/kKdu5jJw5AgJ
|
||||
|
||||
struct FakeEhGlobals {
|
||||
// Fake
|
||||
|
||||
void* caughtExceptions;
|
||||
uint uncaughtExceptions;
|
||||
};
|
||||
|
||||
// LLVM's libstdc++ doesn't declare __cxa_get_globals in its cxxabi.h. GNU does. Because it is
|
||||
// extern "C", the compiler wills get upset if we re-declare it even in a different namespace.
|
||||
#if _LIBCPPABI_VERSION
|
||||
extern "C" void* __cxa_get_globals();
|
||||
#else
|
||||
using abi::__cxa_get_globals;
|
||||
#endif
|
||||
|
||||
uint uncaughtExceptionCount() {
|
||||
return reinterpret_cast<FakeEhGlobals*>(__cxa_get_globals())->uncaughtExceptions;
|
||||
}
|
||||
|
||||
#else
|
||||
#error "This needs to be ported to your compiler / C++ ABI."
|
||||
#endif
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
UnwindDetector::UnwindDetector(): uncaughtCount(_::uncaughtExceptionCount()) {}
|
||||
|
||||
bool UnwindDetector::isUnwinding() const {
|
||||
return _::uncaughtExceptionCount() > uncaughtCount;
|
||||
}
|
||||
|
||||
#if !KJ_NO_EXCEPTIONS
|
||||
void UnwindDetector::catchThrownExceptionAsSecondaryFault() const {
|
||||
// TODO(someday): Attach the secondary exception to whatever primary exception is causing
|
||||
// the unwind. For now we just drop it on the floor as this is probably fine most of the
|
||||
// time.
|
||||
getCaughtExceptionAsKj();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __GNUC__ && !KJ_NO_RTTI
|
||||
static kj::String demangleTypeName(const char* name) {
|
||||
if (name == nullptr) return kj::heapString("(nil)");
|
||||
|
||||
int status;
|
||||
char* buf = abi::__cxa_demangle(name, nullptr, nullptr, &status);
|
||||
kj::String result = kj::heapString(buf == nullptr ? name : buf);
|
||||
free(buf);
|
||||
return kj::mv(result);
|
||||
}
|
||||
|
||||
kj::String getCaughtExceptionType() {
|
||||
return demangleTypeName(abi::__cxa_current_exception_type()->name());
|
||||
}
|
||||
#else
|
||||
kj::String getCaughtExceptionType() {
|
||||
return kj::heapString("(unknown)");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if KJ_NO_EXCEPTIONS
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class RecoverableExceptionCatcher: public ExceptionCallback {
|
||||
// Catches a recoverable exception without using try/catch. Used when compiled with
|
||||
// -fno-exceptions.
|
||||
|
||||
public:
|
||||
virtual ~RecoverableExceptionCatcher() noexcept(false) {}
|
||||
|
||||
void onRecoverableException(Exception&& exception) override {
|
||||
if (caught == nullptr) {
|
||||
caught = mv(exception);
|
||||
} else {
|
||||
// TODO(someday): Consider it a secondary fault?
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<Exception> caught;
|
||||
};
|
||||
|
||||
Maybe<Exception> runCatchingExceptions(Runnable& runnable) {
|
||||
RecoverableExceptionCatcher catcher;
|
||||
runnable.run();
|
||||
KJ_IF_MAYBE(e, catcher.caught) {
|
||||
e->truncateCommonTrace();
|
||||
}
|
||||
return mv(catcher.caught);
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#else // KJ_NO_EXCEPTIONS
|
||||
|
||||
kj::Exception getCaughtExceptionAsKj() {
|
||||
try {
|
||||
throw;
|
||||
} catch (Exception& e) {
|
||||
e.truncateCommonTrace();
|
||||
return kj::mv(e);
|
||||
} catch (CanceledException) {
|
||||
throw;
|
||||
} catch (std::bad_alloc& e) {
|
||||
return Exception(Exception::Type::OVERLOADED,
|
||||
"(unknown)", -1, str("std::bad_alloc: ", e.what()));
|
||||
} catch (std::exception& e) {
|
||||
return Exception(Exception::Type::FAILED,
|
||||
"(unknown)", -1, str("std::exception: ", e.what()));
|
||||
} catch (...) {
|
||||
#if __GNUC__ && !KJ_NO_RTTI
|
||||
return Exception(Exception::Type::FAILED, "(unknown)", -1, str(
|
||||
"unknown non-KJ exception of type: ", getCaughtExceptionType()));
|
||||
#else
|
||||
return Exception(Exception::Type::FAILED, "(unknown)", -1, str("unknown non-KJ exception"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // !KJ_NO_EXCEPTIONS
|
||||
|
||||
} // namespace kj
|
||||
429
vendor/capnproto/src/kj/exception.h
vendored
Normal file
429
vendor/capnproto/src/kj/exception.h
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "memory.h"
|
||||
#include "array.h"
|
||||
#include "string.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
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, uint limit = kj::maxValue);
|
||||
// Append the current stack trace to the exception's trace, ignoring the first `ignoreCount`
|
||||
// frames (see `getStackTrace()` for discussion of `ignoreCount`).
|
||||
//
|
||||
// If `limit` is set, limit the number of frames added to the given number.
|
||||
|
||||
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.
|
||||
|
||||
private:
|
||||
String ownFile;
|
||||
const char* file;
|
||||
int line;
|
||||
Type type;
|
||||
String description;
|
||||
Maybe<Own<Context>> context;
|
||||
void* trace[32];
|
||||
uint traceCount;
|
||||
|
||||
bool isFullTrace = false;
|
||||
// Is `trace` a full trace to the top of the stack (or as close as we could get before we ran
|
||||
// out of space)? If this is false, then `trace` is instead a partial trace covering just the
|
||||
// frames between where the exception was thrown and where it was caught.
|
||||
//
|
||||
// extendTrace() transitions this to true, and truncateCommonTrace() changes it back to false.
|
||||
//
|
||||
// In theory, an exception should only hold a full trace when it is in the process of being
|
||||
// thrown via the C++ exception handling mechanism -- extendTrace() is called before the throw
|
||||
// and truncateCommonTrace() after it is caught. Note that when exceptions propagate through
|
||||
// async promises, the trace is extended one frame at a time instead, so isFullTrace should
|
||||
// remain false.
|
||||
|
||||
friend class ExceptionImpl;
|
||||
};
|
||||
|
||||
struct CanceledException { };
|
||||
// This exception is thrown to force-unwind a stack in order to immediately cancel whatever that
|
||||
// stack was doing. It is used in the implementation of fibers in particular. Application code
|
||||
// should almost never catch this exception, unless you need to modify stack unwinding for some
|
||||
// reason. kj::runCatchingExceptions() does not catch it.
|
||||
|
||||
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_AND_MOVE(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);
|
||||
// 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.
|
||||
|
||||
#if !KJ_NO_EXCEPTIONS
|
||||
|
||||
kj::Exception getCaughtExceptionAsKj();
|
||||
// Call from the catch block of a try/catch to get a `kj::Exception` representing the exception
|
||||
// that was caught, the same way that `kj::runCatchingExceptions` would when catching an exception.
|
||||
// This is sometimes useful if `runCatchingExceptions()` doesn't quite fit your use case. You can
|
||||
// call this from any catch block, including `catch (...)`.
|
||||
//
|
||||
// Some exception types will actually be rethrown by this function, rather than returned. The most
|
||||
// common example is `CanceledException`, whose purpose is to unwind the stack and is not meant to
|
||||
// be caught.
|
||||
|
||||
#endif // !KJ_NO_EXCEPTIONS
|
||||
|
||||
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;
|
||||
|
||||
#if !KJ_NO_EXCEPTIONS
|
||||
void catchThrownExceptionAsSecondaryFault() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if KJ_NO_EXCEPTIONS
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class Runnable {
|
||||
public:
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
template <typename Func>
|
||||
class RunnableImpl: public Runnable {
|
||||
public:
|
||||
RunnableImpl(Func&& func): func(kj::fwd<Func>(func)) {}
|
||||
void run() override {
|
||||
func();
|
||||
}
|
||||
private:
|
||||
Func func;
|
||||
};
|
||||
|
||||
Maybe<Exception> runCatchingExceptions(Runnable& runnable);
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#endif // KJ_NO_EXCEPTIONS
|
||||
|
||||
template <typename Func>
|
||||
Maybe<Exception> runCatchingExceptions(Func&& func) {
|
||||
#if KJ_NO_EXCEPTIONS
|
||||
_::RunnableImpl<Func> runnable(kj::fwd<Func>(func));
|
||||
return _::runCatchingExceptions(runnable);
|
||||
#else
|
||||
try {
|
||||
func();
|
||||
return nullptr;
|
||||
} catch (...) {
|
||||
return getCaughtExceptionAsKj();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void UnwindDetector::catchExceptionsIfUnwinding(Func&& func) const {
|
||||
#if KJ_NO_EXCEPTIONS
|
||||
// Can't possibly be unwinding...
|
||||
func();
|
||||
#else
|
||||
if (isUnwinding()) {
|
||||
try {
|
||||
func();
|
||||
} catch (...) {
|
||||
catchThrownExceptionAsSecondaryFault();
|
||||
}
|
||||
} else {
|
||||
func();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#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 stringifyStackTraceAddresses(ArrayPtr<void* const> trace);
|
||||
StringPtr stringifyStackTraceAddresses(ArrayPtr<void* const> trace, ArrayPtr<char> scratch);
|
||||
// Construct a string containing just enough information about a stack trace to be able to convert
|
||||
// it to file and line numbers later using offline tools. This produces a sequence of
|
||||
// space-separated code location identifiers. Each identifier may be an absolute address
|
||||
// (hex number starting with 0x) or may be a module-relative address "<module>@0x<hex>". The
|
||||
// latter case is preferred when ASLR is in effect and has loaded different modules at different
|
||||
// addresses.
|
||||
|
||||
String getStackTrace();
|
||||
// Get a stack trace right now and stringify it. Useful for debugging.
|
||||
|
||||
kj::StringPtr trimSourceFilename(kj::StringPtr filename);
|
||||
// Given a source code file name, trim off noisy prefixes like "src/" or
|
||||
// "/ekam-provider/canonical/".
|
||||
|
||||
kj::String getCaughtExceptionType();
|
||||
// Utility function which attempts to return the human-readable type name of the exception
|
||||
// currently being thrown. This can be called inside a catch block, including a catch (...) block,
|
||||
// for the purpose of error logging. This function is best-effort; on some platforms it may simply
|
||||
// return "(unknown)".
|
||||
|
||||
void requireOnStack(void* ptr, kj::StringPtr description);
|
||||
// Throw an exception if `ptr` does not appear to point to something near the top of the stack.
|
||||
// Used as a safety check for types that must be stack-allocated, like ExceptionCallback.
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
1801
vendor/capnproto/src/kj/filesystem-disk-unix.c++
vendored
Normal file
1801
vendor/capnproto/src/kj/filesystem-disk-unix.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1496
vendor/capnproto/src/kj/filesystem.c++
vendored
Normal file
1496
vendor/capnproto/src/kj/filesystem.c++
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1028
vendor/capnproto/src/kj/filesystem.h
vendored
Normal file
1028
vendor/capnproto/src/kj/filesystem.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
293
vendor/capnproto/src/kj/function.h
vendored
Normal file
293
vendor/capnproto/src/kj/function.h
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
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 Signature>
|
||||
class FunctionParam;
|
||||
// Like Function, but used specifically as a call parameter type. Does not do any heap allocation.
|
||||
//
|
||||
// This type MUST NOT be used for anything other than a parameter type to a function or method.
|
||||
// This is because if FunctionParam binds to a temporary, it assumes that the temporary will
|
||||
// outlive the FunctionParam instance. This is true when FunctionParam is used as a parameter type,
|
||||
// but not if it is used as a local variable nor a class member variable.
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template <typename Return, typename... Params>
|
||||
class FunctionParam<Return(Params...)> {
|
||||
public:
|
||||
template <typename Func>
|
||||
FunctionParam(Func&& func) {
|
||||
typedef Wrapper<Decay<Func>> WrapperType;
|
||||
|
||||
// All instances of Wrapper<Func> are two pointers in size: a vtable, and a Func&. So if we
|
||||
// allocate space for two pointers, we can construct a Wrapper<Func> in it!
|
||||
static_assert(sizeof(WrapperType) == sizeof(space),
|
||||
"expected WrapperType to be two pointers");
|
||||
|
||||
// Even if `func` is an rvalue reference, it's OK to use it as an lvalue here, because
|
||||
// FunctionParam is used strictly for parameters. If we captured a temporary, we know that
|
||||
// temporary will not be destroyed until after the function call completes.
|
||||
ctor(*reinterpret_cast<WrapperType*>(space), func);
|
||||
}
|
||||
|
||||
FunctionParam(const FunctionParam& other) = default;
|
||||
FunctionParam(FunctionParam&& other) = default;
|
||||
// Magically, a plain copy works.
|
||||
|
||||
inline Return operator()(Params... params) {
|
||||
return (*reinterpret_cast<WrapperBase*>(space))(kj::fwd<Params>(params)...);
|
||||
}
|
||||
|
||||
private:
|
||||
alignas(void*) char space[2 * sizeof(void*)];
|
||||
|
||||
class WrapperBase {
|
||||
public:
|
||||
virtual Return operator()(Params... params) = 0;
|
||||
};
|
||||
|
||||
template <typename Func>
|
||||
class Wrapper: public WrapperBase {
|
||||
public:
|
||||
Wrapper(Func& func): func(func) {}
|
||||
|
||||
inline Return operator()(Params... params) override {
|
||||
return func(kj::fwd<Params>(params)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Func& func;
|
||||
};
|
||||
};
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, typename Func, typename ConstFunc>
|
||||
class BoundMethod {
|
||||
public:
|
||||
BoundMethod(T&& t, Func&& func, ConstFunc&& constFunc)
|
||||
: t(kj::fwd<T>(t)), func(kj::mv(func)), constFunc(kj::mv(constFunc)) {}
|
||||
|
||||
template <typename... Params>
|
||||
auto operator()(Params&&... params) {
|
||||
return func(t, kj::fwd<Params>(params)...);
|
||||
}
|
||||
template <typename... Params>
|
||||
auto operator()(Params&&... params) const {
|
||||
return constFunc(t, kj::fwd<Params>(params)...);
|
||||
}
|
||||
|
||||
private:
|
||||
T t;
|
||||
Func func;
|
||||
ConstFunc constFunc;
|
||||
};
|
||||
|
||||
template <typename T, typename Func, typename ConstFunc>
|
||||
BoundMethod<T, Func, ConstFunc> boundMethod(T&& t, Func&& func, ConstFunc&& constFunc) {
|
||||
return { kj::fwd<T>(t), kj::fwd<Func>(func), kj::fwd<ConstFunc>(constFunc) };
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#define KJ_BIND_METHOD(obj, method) \
|
||||
::kj::_::boundMethod(obj, \
|
||||
[](auto& s, auto&&... p) mutable { return s.method(kj::fwd<decltype(p)>(p)...); }, \
|
||||
[](auto& s, auto&&... p) { return s.method(kj::fwd<decltype(p)>(p)...); })
|
||||
// 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 method is allowed to be overloaded.
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
65
vendor/capnproto/src/kj/hash.c++
vendored
Normal file
65
vendor/capnproto/src/kj/hash.c++
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2018 Kenton Varda 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 "hash.h"
|
||||
|
||||
namespace kj {
|
||||
namespace _ { // private
|
||||
|
||||
uint HashCoder::operator*(ArrayPtr<const byte> s) const {
|
||||
// murmur2 adapted from libc++ source code.
|
||||
//
|
||||
// TODO(perf): Use CityHash or FarmHash on 64-bit machines? They seem optimized for x86-64; what
|
||||
// about ARM? Ask Vlad for advice.
|
||||
|
||||
constexpr uint m = 0x5bd1e995;
|
||||
constexpr uint r = 24;
|
||||
uint h = s.size();
|
||||
const byte* data = s.begin();
|
||||
uint len = s.size();
|
||||
for (; len >= 4; data += 4, len -= 4) {
|
||||
uint k;
|
||||
memcpy(&k, data, sizeof(k));
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
h *= m;
|
||||
h ^= k;
|
||||
}
|
||||
switch (len) {
|
||||
case 3:
|
||||
h ^= data[2] << 16;
|
||||
KJ_FALLTHROUGH;
|
||||
case 2:
|
||||
h ^= data[1] << 8;
|
||||
KJ_FALLTHROUGH;
|
||||
case 1:
|
||||
h ^= data[0];
|
||||
h *= m;
|
||||
}
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
return h;
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
196
vendor/capnproto/src/kj/hash.h
vendored
Normal file
196
vendor/capnproto/src/kj/hash.h
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2018 Kenton Varda 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "string.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
namespace _ { // private
|
||||
|
||||
struct HashCoder {
|
||||
// This is a dummy type with only one instance: HASHCODER (below). To make an arbitrary type
|
||||
// hashable, define `operator*(HashCoder, T)` to return any other type that is already hashable.
|
||||
// Be sure to declare the operator in the same namespace as `T` **or** in the global scope.
|
||||
// You can use the KJ_HASHCODE() macro as syntax sugar for this.
|
||||
//
|
||||
// A more usual way to accomplish what we're doing here would be to require that you define
|
||||
// a function like `hashCode(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 `hashCode` which do something
|
||||
// different. Declaring `operator*` with `HashCoder` as the left operand cannot conflict with
|
||||
// anything.
|
||||
|
||||
uint operator*(ArrayPtr<const byte> s) const;
|
||||
inline uint operator*(ArrayPtr<byte> s) const { return operator*(s.asConst()); }
|
||||
|
||||
inline uint operator*(ArrayPtr<const char> s) const { return operator*(s.asBytes()); }
|
||||
inline uint operator*(ArrayPtr<char> s) const { return operator*(s.asBytes()); }
|
||||
inline uint operator*(const Array<const char>& s) const { return operator*(s.asBytes()); }
|
||||
inline uint operator*(const Array<char>& s) const { return operator*(s.asBytes()); }
|
||||
inline uint operator*(const String& s) const { return operator*(s.asBytes()); }
|
||||
inline uint operator*(const StringPtr& s) const { return operator*(s.asBytes()); }
|
||||
inline uint operator*(const ConstString& s) const { return operator*(s.asBytes()); }
|
||||
|
||||
inline uint operator*(decltype(nullptr)) const { return 0; }
|
||||
inline uint operator*(bool b) const { return b; }
|
||||
inline uint operator*(char i) const { return i; }
|
||||
inline uint operator*(signed char i) const { return i; }
|
||||
inline uint operator*(unsigned char i) const { return i; }
|
||||
inline uint operator*(signed short i) const { return i; }
|
||||
inline uint operator*(unsigned short i) const { return i; }
|
||||
inline uint operator*(signed int i) const { return i; }
|
||||
inline uint operator*(unsigned int i) const { return i; }
|
||||
|
||||
inline uint operator*(signed long i) const {
|
||||
if (sizeof(i) == sizeof(uint)) {
|
||||
return operator*(static_cast<uint>(i));
|
||||
} else {
|
||||
return operator*(static_cast<unsigned long long>(i));
|
||||
}
|
||||
}
|
||||
inline uint operator*(unsigned long i) const {
|
||||
if (sizeof(i) == sizeof(uint)) {
|
||||
return operator*(static_cast<uint>(i));
|
||||
} else {
|
||||
return operator*(static_cast<unsigned long long>(i));
|
||||
}
|
||||
}
|
||||
inline uint operator*(signed long long i) const {
|
||||
return operator*(static_cast<unsigned long long>(i));
|
||||
}
|
||||
inline uint operator*(unsigned long long i) const {
|
||||
// Mix 64 bits to 32 bits in such a way that if our input values differ primarily in the upper
|
||||
// 32 bits, we still get good diffusion. (I.e. we cannot just truncate!)
|
||||
//
|
||||
// 49123 is an arbitrarily-chosen prime that is vaguely close to 2^16.
|
||||
//
|
||||
// TODO(perf): I just made this up. Is it OK?
|
||||
return static_cast<uint>(i) + static_cast<uint>(i >> 32) * 49123;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint operator*(T* ptr) const {
|
||||
static_assert(!isSameType<Decay<T>, char>(), "Wrap in StringPtr if you want to hash string "
|
||||
"contents. If you want to hash the pointer, cast to void*");
|
||||
if (sizeof(ptr) == sizeof(uint)) {
|
||||
// TODO(cleanup): In C++17, make the if() above be `if constexpr ()`, then change this to
|
||||
// reinterpret_cast<uint>(ptr).
|
||||
return reinterpret_cast<unsigned long long>(ptr);
|
||||
} else {
|
||||
return operator*(reinterpret_cast<unsigned long long>(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename = decltype(instance<const HashCoder&>() * instance<const T&>())>
|
||||
uint operator*(ArrayPtr<T> arr) const;
|
||||
template <typename T, typename = decltype(instance<const HashCoder&>() * instance<const T&>())>
|
||||
uint operator*(const Array<T>& arr) const;
|
||||
template <typename T, typename = EnableIf<__is_enum(T)>>
|
||||
inline uint operator*(T e) const;
|
||||
|
||||
template <typename T, typename Result = decltype(instance<T>().hashCode())>
|
||||
inline Result operator*(T&& value) const { return kj::fwd<T>(value).hashCode(); }
|
||||
};
|
||||
static KJ_CONSTEXPR(const) HashCoder HASHCODER = HashCoder();
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
#define KJ_HASHCODE(...) operator*(::kj::_::HashCoder, __VA_ARGS__)
|
||||
// Defines a hash function for a custom type. Example:
|
||||
//
|
||||
// class Foo {...};
|
||||
// inline uint KJ_HASHCODE(const Foo& foo) { return kj::hashCode(foo.x, foo.y); }
|
||||
//
|
||||
// This allows Foo to be passed to hashCode().
|
||||
//
|
||||
// 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 itself is hashable -- that value will be hashed in turn
|
||||
// until a `uint` comes out.
|
||||
|
||||
inline uint hashCode(uint value) { return value; }
|
||||
template <typename T>
|
||||
inline uint hashCode(T&& value) { return hashCode(_::HASHCODER * kj::fwd<T>(value)); }
|
||||
template <typename T, size_t N>
|
||||
inline uint hashCode(T (&arr)[N]) {
|
||||
static_assert(!isSameType<Decay<T>, char>(), "Wrap in StringPtr if you want to hash string "
|
||||
"contents. If you want to hash the pointer, cast to void*");
|
||||
static_assert(isSameType<Decay<T>, char>(), "Wrap in ArrayPtr if you want to hash a C array. "
|
||||
"If you want to hash the pointer, cast to void*");
|
||||
return 0;
|
||||
}
|
||||
template <typename... T>
|
||||
inline uint hashCode(T&&... values) {
|
||||
uint hashes[] = { hashCode(kj::fwd<T>(values))... };
|
||||
return hashCode(kj::ArrayPtr<uint>(hashes).asBytes());
|
||||
}
|
||||
// kj::hashCode() is a universal hashing function, like kj::str() is a universal stringification
|
||||
// function. Throw stuff in, get a hash code.
|
||||
//
|
||||
// Hash codes may differ between different processes, even running exactly the same code.
|
||||
//
|
||||
// NOT SUITABLE FOR CRYPTOGRAPHY. This is for hash tables, not crypto.
|
||||
|
||||
// =======================================================================================
|
||||
// inline implementation details
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, typename>
|
||||
inline uint HashCoder::operator*(ArrayPtr<T> arr) const {
|
||||
// Hash each array element to create a string of hashes, then murmur2 over those.
|
||||
//
|
||||
// TODO(perf): Choose a more-modern hash. (See hash.c++.)
|
||||
|
||||
constexpr uint m = 0x5bd1e995;
|
||||
constexpr uint r = 24;
|
||||
uint h = arr.size() * sizeof(uint);
|
||||
|
||||
for (auto& e: arr) {
|
||||
uint k = kj::hashCode(e);
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
h *= m;
|
||||
h ^= k;
|
||||
}
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
return h;
|
||||
}
|
||||
template <typename T, typename>
|
||||
inline uint HashCoder::operator*(const Array<T>& arr) const {
|
||||
return operator*(arr.asPtr());
|
||||
}
|
||||
|
||||
template <typename T, typename>
|
||||
inline uint HashCoder::operator*(T e) const {
|
||||
return operator*(static_cast<__underlying_type(T)>(e));
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
171
vendor/capnproto/src/kj/io.c++
vendored
Normal file
171
vendor/capnproto/src/kj/io.c++
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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 _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
#include "io.h"
|
||||
#include "debug.h"
|
||||
#include "miniposix.h"
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include "vector.h"
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/uio.h>
|
||||
|
||||
namespace kj {
|
||||
|
||||
OutputStream::~OutputStream() noexcept(false) {}
|
||||
|
||||
void OutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
|
||||
for (auto piece: pieces) {
|
||||
write(piece.begin(), piece.size());
|
||||
}
|
||||
}
|
||||
|
||||
AutoCloseFd::~AutoCloseFd() noexcept(false) {
|
||||
if (fd >= 0) {
|
||||
// Don't use SYSCALL() here because close() should not be repeated on EINTR.
|
||||
if (miniposix::close(fd) < 0) {
|
||||
KJ_FAIL_SYSCALL("close", errno, fd) {
|
||||
// This ensures we don't throw an exception if unwinding.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FdOutputStream::~FdOutputStream() noexcept(false) {}
|
||||
|
||||
#if __APPLE__
|
||||
// macOS cannot handle writes of more than INT_MAX, so we clamp
|
||||
// writes to this size. We use 1GB rather than INT_MAX since INT_MAX is 2GB minus 1 byte. Being
|
||||
// off by one from a big round number feels rude (alignment issues may harm performance), and 1GB
|
||||
// should be plenty in any case.
|
||||
static constexpr size_t WRITE_CLAMP_SIZE = 1u << 30;
|
||||
#endif
|
||||
|
||||
void FdOutputStream::write(const void* buffer, size_t size) {
|
||||
const char* pos = reinterpret_cast<const char*>(buffer);
|
||||
|
||||
while (size > 0) {
|
||||
miniposix::ssize_t n;
|
||||
#if __APPLE__
|
||||
// macOS fails if given more than INT_MAX bytes
|
||||
// in a single write operation. We can just clamp the size since the loop will then handle
|
||||
// writing the rest. I don't know why these platforms don't just do this themselves.
|
||||
KJ_SYSCALL(n = miniposix::write(fd, pos, kj::min(size, WRITE_CLAMP_SIZE)), fd);
|
||||
#else
|
||||
KJ_SYSCALL(n = miniposix::write(fd, pos, size), fd);
|
||||
#endif
|
||||
KJ_ASSERT(n > 0, "write() returned zero.");
|
||||
pos += n;
|
||||
size -= n;
|
||||
}
|
||||
}
|
||||
|
||||
void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
|
||||
const size_t iovmax = miniposix::iovMax();
|
||||
while (pieces.size() > iovmax) {
|
||||
write(pieces.slice(0, iovmax));
|
||||
pieces = pieces.slice(iovmax, pieces.size());
|
||||
}
|
||||
|
||||
KJ_STACK_ARRAY(struct iovec, iov, pieces.size(), 16, 128);
|
||||
|
||||
for (uint i = 0; i < pieces.size(); i++) {
|
||||
// writev() interface is not const-correct. :(
|
||||
iov[i].iov_base = const_cast<byte*>(pieces[i].begin());
|
||||
iov[i].iov_len = pieces[i].size();
|
||||
}
|
||||
|
||||
struct iovec* current = iov.begin();
|
||||
|
||||
// Advance past any leading empty buffers so that a write full of only empty buffers does not
|
||||
// cause a syscall at all.
|
||||
while (current < iov.end() && current->iov_len == 0) {
|
||||
++current;
|
||||
}
|
||||
|
||||
while (current < iov.end()) {
|
||||
size_t iovCount = iov.end() - current;
|
||||
|
||||
#if __APPLE__
|
||||
// MacOS will fail if you give it more than INT_MAX bytes to write at once. We can solve this
|
||||
// by carefully truncating the list to a lesser number. Why the OS doesn't just return a short
|
||||
// write itself, I don't know.
|
||||
size_t totalSize = 0;
|
||||
struct iovec* editedPiece = nullptr;
|
||||
size_t editedPieceOriginalLen = 0;
|
||||
for (auto i: kj::zeroTo(iovCount)) {
|
||||
auto& piece = *(current + i);
|
||||
totalSize += piece.iov_len;
|
||||
if (totalSize >= WRITE_CLAMP_SIZE) {
|
||||
// Truncate the list after this piece.
|
||||
iovCount = i + 1;
|
||||
|
||||
if (totalSize > WRITE_CLAMP_SIZE) {
|
||||
// We also have to truncate this piece. Patch it in-place and plan to fix it later.
|
||||
editedPiece = &piece;
|
||||
editedPieceOriginalLen = piece.iov_len;
|
||||
size_t overage = totalSize - WRITE_CLAMP_SIZE;
|
||||
piece.iov_len -= overage;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Issue the write.
|
||||
ssize_t n = 0;
|
||||
KJ_SYSCALL(n = ::writev(fd, current, iovCount), fd);
|
||||
KJ_ASSERT(n > 0, "writev() returned zero.");
|
||||
|
||||
#if __APPLE__
|
||||
// If we patched the list above, unpatch now.
|
||||
if (editedPiece != nullptr) {
|
||||
editedPiece->iov_len = editedPieceOriginalLen;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Advance past all buffers that were fully-written.
|
||||
while (current < iov.end() && static_cast<size_t>(n) >= current->iov_len) {
|
||||
n -= current->iov_len;
|
||||
++current;
|
||||
}
|
||||
|
||||
// If we only partially-wrote one of the buffers, adjust the pointer and size to include only
|
||||
// the unwritten part.
|
||||
if (n > 0) {
|
||||
current->iov_base = reinterpret_cast<byte*>(current->iov_base) + n;
|
||||
current->iov_len -= n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
|
||||
} // namespace kj
|
||||
129
vendor/capnproto/src/kj/io.h
vendored
Normal file
129
vendor/capnproto/src/kj/io.h
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "common.h"
|
||||
#include "array.h"
|
||||
#include "exception.h"
|
||||
#include <stdint.h>
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
// =======================================================================================
|
||||
// Abstract interfaces
|
||||
|
||||
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.
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// 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; }
|
||||
|
||||
inline int release() {
|
||||
// Release ownership of an FD. Not recommended.
|
||||
int result = fd;
|
||||
fd = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
inline auto KJ_STRINGIFY(const AutoCloseFd& fd)
|
||||
-> decltype(kj::toCharSequence(implicitCast<int>(fd))) {
|
||||
return kj::toCharSequence(implicitCast<int>(fd));
|
||||
}
|
||||
|
||||
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_AND_MOVE(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;
|
||||
};
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
578
vendor/capnproto/src/kj/map.h
vendored
Normal file
578
vendor/capnproto/src/kj/map.h
vendored
Normal file
@@ -0,0 +1,578 @@
|
||||
// Copyright (c) 2018 Kenton Varda 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "table.h"
|
||||
#include "hash.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class HashMap {
|
||||
// A key/value mapping backed by hashing.
|
||||
//
|
||||
// `Key` must be hashable (via a `.hashCode()` method or `KJ_HASHCODE()`; see `hash.h`) and must
|
||||
// implement `operator==()`. Additionally, when performing lookups, you can use key types other
|
||||
// than `Key` as long as the other type is also hashable (producing the same hash codes) and
|
||||
// there is an `operator==` implementation with `Key` on the left and that other type on the
|
||||
// right. For example, if the key type is `String`, you can pass `StringPtr` to `find()`.
|
||||
|
||||
public:
|
||||
void reserve(size_t size);
|
||||
// Pre-allocates space for a map of the given size.
|
||||
|
||||
size_t size() const;
|
||||
size_t capacity() const;
|
||||
void clear();
|
||||
|
||||
struct Entry {
|
||||
Key key;
|
||||
Value value;
|
||||
};
|
||||
|
||||
Entry* begin();
|
||||
Entry* end();
|
||||
const Entry* begin() const;
|
||||
const Entry* end() const;
|
||||
// Deterministic iteration. If you only ever insert(), iteration order will be insertion order.
|
||||
// If you erase(), the erased element is swapped with the last element in the ordering.
|
||||
|
||||
Entry& insert(Key key, Value value);
|
||||
// Inserts a new entry. Throws if the key already exists.
|
||||
|
||||
template <typename Collection>
|
||||
void insertAll(Collection&& collection);
|
||||
// Given an iterable collection of `Entry`s, inserts all of them into this map. If the
|
||||
// input is an rvalue, the entries will be moved rather than copied.
|
||||
|
||||
template <typename UpdateFunc>
|
||||
Entry& upsert(Key key, Value value, UpdateFunc&& update);
|
||||
Entry& upsert(Key key, Value value);
|
||||
// Tries to insert a new entry. However, if a duplicate already exists (according to some index),
|
||||
// then update(Value& existingValue, Value&& newValue) is called to modify the existing value.
|
||||
// If no function is provided, the default is to simply replace the value (but not the key).
|
||||
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<Value&> find(KeyLike&& key);
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const Value&> find(KeyLike&& key) const;
|
||||
// Search for a matching key. The input does not have to be of type `Key`; it merely has to
|
||||
// be something that the Hasher accepts.
|
||||
//
|
||||
// Note that the default hasher for String accepts StringPtr.
|
||||
|
||||
template <typename KeyLike, typename Func>
|
||||
Value& findOrCreate(KeyLike&& key, Func&& createEntry);
|
||||
// Like find() but if the key isn't present then call createEntry() to create the corresponding
|
||||
// entry and insert it. createEntry() must return type `Entry`.
|
||||
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<Entry&> findEntry(KeyLike&& key);
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const Entry&> findEntry(KeyLike&& key) const;
|
||||
template <typename KeyLike, typename Func>
|
||||
Entry& findOrCreateEntry(KeyLike&& key, Func&& createEntry);
|
||||
// Sometimes you need to see the whole matching Entry, not just the Value.
|
||||
|
||||
template <typename KeyLike>
|
||||
bool erase(KeyLike&& key);
|
||||
// Erase the entry with the matching key.
|
||||
//
|
||||
// WARNING: This invalidates all pointers and iterators into the map. Use eraseAll() if you need
|
||||
// to iterate and erase multiple entries.
|
||||
|
||||
void erase(Entry& entry);
|
||||
// Erase an entry by reference.
|
||||
|
||||
Entry release(Entry& row);
|
||||
// Erase an entry and return its content by move.
|
||||
|
||||
template <typename Predicate,
|
||||
typename = decltype(instance<Predicate>()(instance<Key&>(), instance<Value&>()))>
|
||||
size_t eraseAll(Predicate&& predicate);
|
||||
// Erase all values for which predicate(key, value) returns true. This scans over the entire map.
|
||||
|
||||
private:
|
||||
class Callbacks {
|
||||
public:
|
||||
inline const Key& keyForRow(const Entry& entry) const { return entry.key; }
|
||||
inline Key& keyForRow(Entry& entry) const { return entry.key; }
|
||||
|
||||
template <typename KeyLike>
|
||||
inline bool matches(Entry& e, KeyLike&& key) const {
|
||||
return e.key == key;
|
||||
}
|
||||
template <typename KeyLike>
|
||||
inline bool matches(const Entry& e, KeyLike&& key) const {
|
||||
return e.key == key;
|
||||
}
|
||||
template <typename KeyLike>
|
||||
inline auto hashCode(KeyLike&& key) const {
|
||||
return kj::hashCode(key);
|
||||
}
|
||||
};
|
||||
|
||||
kj::Table<Entry, HashIndex<Callbacks>> table;
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class TreeMap {
|
||||
// A key/value mapping backed by a B-tree.
|
||||
//
|
||||
// `Key` must support `operator<` and `operator==` against other Keys, and against any type
|
||||
// which you might want to pass to find() (with `Key` always on the left of the comparison).
|
||||
|
||||
public:
|
||||
void reserve(size_t size);
|
||||
// Pre-allocates space for a map of the given size.
|
||||
|
||||
size_t size() const;
|
||||
size_t capacity() const;
|
||||
void clear();
|
||||
|
||||
struct Entry {
|
||||
Key key;
|
||||
Value value;
|
||||
};
|
||||
|
||||
auto begin();
|
||||
auto end();
|
||||
auto begin() const;
|
||||
auto end() const;
|
||||
// Iteration is in sorted order by key.
|
||||
|
||||
Entry& insert(Key key, Value value);
|
||||
// Inserts a new entry. Throws if the key already exists.
|
||||
|
||||
template <typename Collection>
|
||||
void insertAll(Collection&& collection);
|
||||
// Given an iterable collection of `Entry`s, inserts all of them into this map. If the
|
||||
// input is an rvalue, the entries will be moved rather than copied.
|
||||
|
||||
template <typename UpdateFunc>
|
||||
Entry& upsert(Key key, Value value, UpdateFunc&& update);
|
||||
Entry& upsert(Key key, Value value);
|
||||
// Tries to insert a new entry. However, if a duplicate already exists (according to some index),
|
||||
// then update(Value& existingValue, Value&& newValue) is called to modify the existing value.
|
||||
// If no function is provided, the default is to simply replace the value (but not the key).
|
||||
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<Value&> find(KeyLike&& key);
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const Value&> find(KeyLike&& key) const;
|
||||
// Search for a matching key. The input does not have to be of type `Key`; it merely has to
|
||||
// be something that can be compared against `Key`.
|
||||
|
||||
template <typename KeyLike, typename Func>
|
||||
Value& findOrCreate(KeyLike&& key, Func&& createEntry);
|
||||
// Like find() but if the key isn't present then call createEntry() to create the corresponding
|
||||
// entry and insert it. createEntry() must return type `Entry`.
|
||||
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<Entry&> findEntry(KeyLike&& key);
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const Entry&> findEntry(KeyLike&& key) const;
|
||||
template <typename KeyLike, typename Func>
|
||||
Entry& findOrCreateEntry(KeyLike&& key, Func&& createEntry);
|
||||
// Sometimes you need to see the whole matching Entry, not just the Value.
|
||||
|
||||
template <typename K1, typename K2>
|
||||
auto range(K1&& k1, K2&& k2);
|
||||
template <typename K1, typename K2>
|
||||
auto range(K1&& k1, K2&& k2) const;
|
||||
// Returns an iterable range of entries with keys between k1 (inclusive) and k2 (exclusive).
|
||||
|
||||
template <typename KeyLike>
|
||||
bool erase(KeyLike&& key);
|
||||
// Erase the entry with the matching key.
|
||||
//
|
||||
// WARNING: This invalidates all pointers and iterators into the map. Use eraseAll() if you need
|
||||
// to iterate and erase multiple entries.
|
||||
|
||||
void erase(Entry& entry);
|
||||
// Erase an entry by reference.
|
||||
|
||||
Entry release(Entry& row);
|
||||
// Erase an entry and return its content by move.
|
||||
|
||||
template <typename Predicate,
|
||||
typename = decltype(instance<Predicate>()(instance<Key&>(), instance<Value&>()))>
|
||||
size_t eraseAll(Predicate&& predicate);
|
||||
// Erase all values for which predicate(key, value) returns true. This scans over the entire map.
|
||||
|
||||
template <typename K1, typename K2>
|
||||
size_t eraseRange(K1&& k1, K2&& k2);
|
||||
// Erases all entries with keys between k1 (inclusive) and k2 (exclusive).
|
||||
|
||||
private:
|
||||
class Callbacks {
|
||||
public:
|
||||
inline const Key& keyForRow(const Entry& entry) const { return entry.key; }
|
||||
inline Key& keyForRow(Entry& entry) const { return entry.key; }
|
||||
|
||||
template <typename KeyLike>
|
||||
inline bool matches(Entry& e, KeyLike&& key) const {
|
||||
return e.key == key;
|
||||
}
|
||||
template <typename KeyLike>
|
||||
inline bool matches(const Entry& e, KeyLike&& key) const {
|
||||
return e.key == key;
|
||||
}
|
||||
template <typename KeyLike>
|
||||
inline bool isBefore(Entry& e, KeyLike&& key) const {
|
||||
return e.key < key;
|
||||
}
|
||||
template <typename KeyLike>
|
||||
inline bool isBefore(const Entry& e, KeyLike&& key) const {
|
||||
return e.key < key;
|
||||
}
|
||||
};
|
||||
|
||||
kj::Table<Entry, TreeIndex<Callbacks>> table;
|
||||
};
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class HashSetCallbacks {
|
||||
public:
|
||||
template <typename Row>
|
||||
inline Row& keyForRow(Row& row) const { return row; }
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool matches(T& a, U& b) const { return a == b; }
|
||||
template <typename KeyLike>
|
||||
inline auto hashCode(KeyLike&& key) const {
|
||||
return kj::hashCode(key);
|
||||
}
|
||||
};
|
||||
|
||||
class TreeSetCallbacks {
|
||||
public:
|
||||
template <typename Row>
|
||||
inline Row& keyForRow(Row& row) const { return row; }
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool matches(T& a, U& b) const { return a == b; }
|
||||
template <typename T, typename U>
|
||||
inline bool isBefore(T& a, U& b) const { return a < b; }
|
||||
};
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename Element>
|
||||
class HashSet: public Table<Element, HashIndex<_::HashSetCallbacks>> {
|
||||
// A simple hashtable-based set, using kj::hashCode() and operator==().
|
||||
|
||||
public:
|
||||
// Everything is inherited.
|
||||
|
||||
template <typename... Params>
|
||||
inline bool contains(Params&&... params) const {
|
||||
return this->find(kj::fwd<Params>(params)...) != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Element>
|
||||
class TreeSet: public Table<Element, TreeIndex<_::TreeSetCallbacks>> {
|
||||
// A simple b-tree-based set, using operator<() and operator==().
|
||||
|
||||
public:
|
||||
// Everything is inherited.
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// inline implementation details
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void HashMap<Key, Value>::reserve(size_t size) {
|
||||
table.reserve(size);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
size_t HashMap<Key, Value>::size() const {
|
||||
return table.size();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
size_t HashMap<Key, Value>::capacity() const {
|
||||
return table.capacity();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
void HashMap<Key, Value>::clear() {
|
||||
return table.clear();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename HashMap<Key, Value>::Entry* HashMap<Key, Value>::begin() {
|
||||
return table.begin();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
typename HashMap<Key, Value>::Entry* HashMap<Key, Value>::end() {
|
||||
return table.end();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
const typename HashMap<Key, Value>::Entry* HashMap<Key, Value>::begin() const {
|
||||
return table.begin();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
const typename HashMap<Key, Value>::Entry* HashMap<Key, Value>::end() const {
|
||||
return table.end();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename HashMap<Key, Value>::Entry& HashMap<Key, Value>::insert(Key key, Value value) {
|
||||
return table.insert(Entry { kj::mv(key), kj::mv(value) });
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Collection>
|
||||
void HashMap<Key, Value>::insertAll(Collection&& collection) {
|
||||
return table.insertAll(kj::fwd<Collection>(collection));
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename UpdateFunc>
|
||||
typename HashMap<Key, Value>::Entry& HashMap<Key, Value>::upsert(
|
||||
Key key, Value value, UpdateFunc&& update) {
|
||||
return table.upsert(Entry { kj::mv(key), kj::mv(value) },
|
||||
[&](Entry& existingEntry, Entry&& newEntry) {
|
||||
update(existingEntry.value, kj::mv(newEntry.value));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename HashMap<Key, Value>::Entry& HashMap<Key, Value>::upsert(
|
||||
Key key, Value value) {
|
||||
return table.upsert(Entry { kj::mv(key), kj::mv(value) },
|
||||
[&](Entry& existingEntry, Entry&& newEntry) {
|
||||
existingEntry.value = kj::mv(newEntry.value);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<Value&> HashMap<Key, Value>::find(KeyLike&& key) {
|
||||
return table.find(key).map([](Entry& e) -> Value& { return e.value; });
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const Value&> HashMap<Key, Value>::find(KeyLike&& key) const {
|
||||
return table.find(key).map([](const Entry& e) -> const Value& { return e.value; });
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike, typename Func>
|
||||
Value& HashMap<Key, Value>::findOrCreate(KeyLike&& key, Func&& createEntry) {
|
||||
return table.findOrCreate(key, kj::fwd<Func>(createEntry)).value;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<typename HashMap<Key, Value>::Entry&>
|
||||
HashMap<Key, Value>::findEntry(KeyLike&& key) {
|
||||
return table.find(kj::fwd<KeyLike>(key));
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const typename HashMap<Key, Value>::Entry&>
|
||||
HashMap<Key, Value>::findEntry(KeyLike&& key) const {
|
||||
return table.find(kj::fwd<KeyLike>(key));
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike, typename Func>
|
||||
typename HashMap<Key, Value>::Entry&
|
||||
HashMap<Key, Value>::findOrCreateEntry(KeyLike&& key, Func&& createEntry) {
|
||||
return table.findOrCreate(kj::fwd<KeyLike>(key), kj::fwd<Func>(createEntry));
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
bool HashMap<Key, Value>::erase(KeyLike&& key) {
|
||||
return table.eraseMatch(key);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void HashMap<Key, Value>::erase(Entry& entry) {
|
||||
table.erase(entry);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename HashMap<Key, Value>::Entry HashMap<Key, Value>::release(Entry& entry) {
|
||||
return table.release(entry);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Predicate, typename>
|
||||
size_t HashMap<Key, Value>::eraseAll(Predicate&& predicate) {
|
||||
return table.eraseAll([&](Entry& entry) {
|
||||
return predicate(entry.key, entry.value);
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void TreeMap<Key, Value>::reserve(size_t size) {
|
||||
table.reserve(size);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
size_t TreeMap<Key, Value>::size() const {
|
||||
return table.size();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
size_t TreeMap<Key, Value>::capacity() const {
|
||||
return table.capacity();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
void TreeMap<Key, Value>::clear() {
|
||||
return table.clear();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
auto TreeMap<Key, Value>::begin() {
|
||||
return table.ordered().begin();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
auto TreeMap<Key, Value>::end() {
|
||||
return table.ordered().end();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
auto TreeMap<Key, Value>::begin() const {
|
||||
return table.ordered().begin();
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
auto TreeMap<Key, Value>::end() const {
|
||||
return table.ordered().end();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename TreeMap<Key, Value>::Entry& TreeMap<Key, Value>::insert(Key key, Value value) {
|
||||
return table.insert(Entry { kj::mv(key), kj::mv(value) });
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Collection>
|
||||
void TreeMap<Key, Value>::insertAll(Collection&& collection) {
|
||||
return table.insertAll(kj::fwd<Collection>(collection));
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename UpdateFunc>
|
||||
typename TreeMap<Key, Value>::Entry& TreeMap<Key, Value>::upsert(
|
||||
Key key, Value value, UpdateFunc&& update) {
|
||||
return table.upsert(Entry { kj::mv(key), kj::mv(value) },
|
||||
[&](Entry& existingEntry, Entry&& newEntry) {
|
||||
update(existingEntry.value, kj::mv(newEntry.value));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename TreeMap<Key, Value>::Entry& TreeMap<Key, Value>::upsert(
|
||||
Key key, Value value) {
|
||||
return table.upsert(Entry { kj::mv(key), kj::mv(value) },
|
||||
[&](Entry& existingEntry, Entry&& newEntry) {
|
||||
existingEntry.value = kj::mv(newEntry.value);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<Value&> TreeMap<Key, Value>::find(KeyLike&& key) {
|
||||
return table.find(key).map([](Entry& e) -> Value& { return e.value; });
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const Value&> TreeMap<Key, Value>::find(KeyLike&& key) const {
|
||||
return table.find(key).map([](const Entry& e) -> const Value& { return e.value; });
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike, typename Func>
|
||||
Value& TreeMap<Key, Value>::findOrCreate(KeyLike&& key, Func&& createEntry) {
|
||||
return table.findOrCreate(key, kj::fwd<Func>(createEntry)).value;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<typename TreeMap<Key, Value>::Entry&>
|
||||
TreeMap<Key, Value>::findEntry(KeyLike&& key) {
|
||||
return table.find(kj::fwd<KeyLike>(key));
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
kj::Maybe<const typename TreeMap<Key, Value>::Entry&>
|
||||
TreeMap<Key, Value>::findEntry(KeyLike&& key) const {
|
||||
return table.find(kj::fwd<KeyLike>(key));
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike, typename Func>
|
||||
typename TreeMap<Key, Value>::Entry&
|
||||
TreeMap<Key, Value>::findOrCreateEntry(KeyLike&& key, Func&& createEntry) {
|
||||
return table.findOrCreate(kj::fwd<KeyLike>(key), kj::fwd<Func>(createEntry));
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename K1, typename K2>
|
||||
auto TreeMap<Key, Value>::range(K1&& k1, K2&& k2) {
|
||||
return table.range(kj::fwd<K1>(k1), kj::fwd<K2>(k2));
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
template <typename K1, typename K2>
|
||||
auto TreeMap<Key, Value>::range(K1&& k1, K2&& k2) const {
|
||||
return table.range(kj::fwd<K1>(k1), kj::fwd<K2>(k2));
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename KeyLike>
|
||||
bool TreeMap<Key, Value>::erase(KeyLike&& key) {
|
||||
return table.eraseMatch(key);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void TreeMap<Key, Value>::erase(Entry& entry) {
|
||||
table.erase(entry);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename TreeMap<Key, Value>::Entry TreeMap<Key, Value>::release(Entry& entry) {
|
||||
return table.release(entry);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Predicate, typename>
|
||||
size_t TreeMap<Key, Value>::eraseAll(Predicate&& predicate) {
|
||||
return table.eraseAll([&](Entry& entry) {
|
||||
return predicate(entry.key, entry.value);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename K1, typename K2>
|
||||
size_t TreeMap<Key, Value>::eraseRange(K1&& k1, K2&& k2) {
|
||||
return table.eraseRange(kj::fwd<K1>(k1), kj::fwd<K2>(k2));
|
||||
}
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
776
vendor/capnproto/src/kj/memory.h
vendored
Normal file
776
vendor/capnproto/src/kj/memory.h
vendored
Normal file
@@ -0,0 +1,776 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool _kj_internal_isPolymorphic(T*) {
|
||||
// If you get a compiler error here complaining that T is incomplete, it's because you are trying
|
||||
// to use kj::Own<T> with a type that has only been forward-declared. Since KJ doesn't know if
|
||||
// the type might be involved in inheritance (especially multiple inheritance), it doesn't know
|
||||
// how to correctly call the disposer to destroy the type, since the object's true memory address
|
||||
// may differ from the address used to point to a superclass.
|
||||
//
|
||||
// However, if you know for sure that T is NOT polymorphic (i.e. it doesn't have a vtable and
|
||||
// isn't involved in inheritance), then you can use KJ_DECLARE_NON_POLYMORPHIC(T) to declare this
|
||||
// to KJ without actually completing the type. Place this macro invocation either in the global
|
||||
// scope, or in the same namespace as T is defined.
|
||||
return __is_polymorphic(T);
|
||||
}
|
||||
|
||||
#define KJ_DECLARE_NON_POLYMORPHIC(...) \
|
||||
inline constexpr bool _kj_internal_isPolymorphic(__VA_ARGS__*) { \
|
||||
return false; \
|
||||
}
|
||||
// If you want to use kj::Own<T> for an incomplete type T that you know is not polymorphic, then
|
||||
// write `KJ_DECLARE_NON_POLYMORPHIC(T)` either at the global scope or in the same namespace as
|
||||
// T is declared.
|
||||
//
|
||||
// This also works for templates, e.g.:
|
||||
//
|
||||
// template <typename X, typename Y>
|
||||
// struct MyType;
|
||||
// template <typename X, typename Y>
|
||||
// KJ_DECLARE_NON_POLYMORPHIC(MyType<X, Y>)
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T> struct RefOrVoid_ { typedef T& Type; };
|
||||
template <> struct RefOrVoid_<void> { typedef void Type; };
|
||||
template <> struct RefOrVoid_<const void> { typedef void Type; };
|
||||
|
||||
template <typename T>
|
||||
using RefOrVoid = typename RefOrVoid_<T>::Type;
|
||||
// Evaluates to T&, unless T is `void`, in which case evaluates to `void`.
|
||||
//
|
||||
// This is a hack needed to avoid defining Own<void> as a totally separate class.
|
||||
|
||||
template <typename T, bool isPolymorphic = _kj_internal_isPolymorphic((T*)nullptr)>
|
||||
struct CastToVoid_;
|
||||
|
||||
template <typename T>
|
||||
struct CastToVoid_<T, false> {
|
||||
static void* apply(T* ptr) {
|
||||
return static_cast<void*>(ptr);
|
||||
}
|
||||
static const void* applyConst(T* ptr) {
|
||||
const T* cptr = ptr;
|
||||
return static_cast<const void*>(cptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CastToVoid_<T, true> {
|
||||
static void* apply(T* ptr) {
|
||||
return dynamic_cast<void*>(ptr);
|
||||
}
|
||||
static const void* applyConst(T* ptr) {
|
||||
const T* cptr = ptr;
|
||||
return dynamic_cast<const void*>(cptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void* castToVoid(T* ptr) {
|
||||
return CastToVoid_<T>::apply(ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const void* castToConstVoid(T* ptr) {
|
||||
return CastToVoid_<T>::applyConst(ptr);
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
// =======================================================================================
|
||||
// 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 = _kj_internal_isPolymorphic((T*)nullptr)>
|
||||
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>();
|
||||
|
||||
// =======================================================================================
|
||||
// Own<T> -- An owned pointer.
|
||||
|
||||
template <typename T, typename StaticDisposer = decltype(nullptr)>
|
||||
class Own;
|
||||
|
||||
template <typename T>
|
||||
class Own<T, decltype(nullptr)> {
|
||||
// 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(cast(other.ptr)) {
|
||||
other.ptr = nullptr;
|
||||
}
|
||||
template <typename U, typename StaticDisposer, typename = EnableIf<canConvert<U*, T*>()>>
|
||||
inline Own(Own<U, StaticDisposer>&& other) noexcept;
|
||||
// Convert statically-disposed Own to dynamically-disposed Own.
|
||||
inline Own(T* ptr, const Disposer& disposer) noexcept: disposer(&disposer), ptr(ptr) {}
|
||||
|
||||
~Own() noexcept(false) { dispose(); }
|
||||
|
||||
inline Own& operator=(Own&& other) {
|
||||
// Move-assignnment 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... Attachments>
|
||||
Own<T> attach(Attachments&&... attachments) KJ_WARN_UNUSED_RESULT;
|
||||
// Returns an Own<T> which points to the same object but which also ensures that all values
|
||||
// passed to `attachments` remain alive until after this object is destroyed. Normally
|
||||
// `attachments` are other Own<?>s pointing to objects that this one depends on.
|
||||
//
|
||||
// Note that attachments will eventually be destroyed in the order they are listed. Hence,
|
||||
// foo.attach(bar, baz) is equivalent to (but more efficient than) foo.attach(bar).attach(baz).
|
||||
|
||||
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 _::RefOrVoid<T> operator*() { NULLCHECK; return *ptr; }
|
||||
inline _::RefOrVoid<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>
|
||||
static inline T* cast(U* ptr) {
|
||||
static_assert(_kj_internal_isPolymorphic((T*)nullptr),
|
||||
"Casting owned pointers requires that the target type is polymorphic.");
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename, typename>
|
||||
friend class Own;
|
||||
friend class Maybe<Own<T>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
template <typename U>
|
||||
inline void* Own<void>::cast(U* ptr) {
|
||||
return _::castToVoid(ptr);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <typename U>
|
||||
inline const void* Own<const void>::cast(U* ptr) {
|
||||
return _::castToConstVoid(ptr);
|
||||
}
|
||||
|
||||
template <typename T, typename StaticDisposer>
|
||||
class Own {
|
||||
// If a `StaticDisposer` is specified (which is not the norm), then the object will be deleted
|
||||
// by calling StaticDisposer::dispose(pointer). The pointer passed to `dispose()` could be a
|
||||
// superclass of `T`, if the pointer has been upcast.
|
||||
//
|
||||
// This type can be useful for micro-optimization, if you've found that you are doing excessive
|
||||
// heap allocations to the point where the virtual call on destruction is costing non-negligible
|
||||
// resources. You should avoid this unless you have a specific need, because it precludes a lot
|
||||
// of power.
|
||||
|
||||
public:
|
||||
KJ_DISALLOW_COPY(Own);
|
||||
inline Own(): ptr(nullptr) {}
|
||||
inline Own(Own&& other) noexcept
|
||||
: ptr(other.ptr) { other.ptr = nullptr; }
|
||||
inline Own(Own<RemoveConstOrDisable<T>, StaticDisposer>&& other) noexcept
|
||||
: ptr(other.ptr) { other.ptr = nullptr; }
|
||||
template <typename U, typename = EnableIf<canConvert<U*, T*>()>>
|
||||
inline Own(Own<U, StaticDisposer>&& other) noexcept
|
||||
: ptr(cast(other.ptr)) {
|
||||
other.ptr = nullptr;
|
||||
}
|
||||
inline explicit Own(T* ptr) noexcept: ptr(ptr) {}
|
||||
|
||||
~Own() noexcept(false) { dispose(); }
|
||||
|
||||
inline Own& operator=(Own&& other) {
|
||||
// Move-assignnment operator.
|
||||
|
||||
// Careful, this might own `other`. Therefore we have to transfer the pointers first, then
|
||||
// dispose.
|
||||
T* ptrCopy = ptr;
|
||||
ptr = other.ptr;
|
||||
other.ptr = nullptr;
|
||||
if (ptrCopy != nullptr) {
|
||||
StaticDisposer::dispose(ptrCopy);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Own& operator=(decltype(nullptr)) {
|
||||
dispose();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
Own<U, StaticDisposer> 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, StaticDisposer> result;
|
||||
if (ptr != nullptr) {
|
||||
result.ptr = &kj::downcast<U>(*ptr);
|
||||
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 _::RefOrVoid<T> operator*() { NULLCHECK; return *ptr; }
|
||||
inline _::RefOrVoid<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:
|
||||
T* ptr;
|
||||
|
||||
inline explicit Own(decltype(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;
|
||||
StaticDisposer::dispose(ptrCopy);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static inline T* cast(U* ptr) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename, typename>
|
||||
friend class Own;
|
||||
friend class Maybe<Own<T, StaticDisposer>>;
|
||||
};
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, typename D>
|
||||
class OwnOwn {
|
||||
public:
|
||||
inline OwnOwn(Own<T, D>&& value) noexcept: value(kj::mv(value)) {}
|
||||
|
||||
inline Own<T, D>& operator*() & { return value; }
|
||||
inline const Own<T, D>& operator*() const & { return value; }
|
||||
inline Own<T, D>&& operator*() && { return kj::mv(value); }
|
||||
inline const Own<T, D>&& operator*() const && { return kj::mv(value); }
|
||||
inline Own<T, D>* operator->() { return &value; }
|
||||
inline const Own<T, D>* operator->() const { return &value; }
|
||||
inline operator Own<T, D>*() { return value ? &value : nullptr; }
|
||||
inline operator const Own<T, D>*() const { return value ? &value : nullptr; }
|
||||
|
||||
private:
|
||||
Own<T, D> value;
|
||||
};
|
||||
|
||||
template <typename T, typename D>
|
||||
OwnOwn<T, D> readMaybe(Maybe<Own<T, D>>&& maybe) { return OwnOwn<T, D>(kj::mv(maybe.ptr)); }
|
||||
template <typename T, typename D>
|
||||
Own<T, D>* readMaybe(Maybe<Own<T, D>>& maybe) { return maybe.ptr ? &maybe.ptr : nullptr; }
|
||||
template <typename T, typename D>
|
||||
const Own<T, D>* readMaybe(const Maybe<Own<T, D>>& maybe) {
|
||||
return maybe.ptr ? &maybe.ptr : nullptr;
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T, typename D>
|
||||
class Maybe<Own<T, D>> {
|
||||
public:
|
||||
inline Maybe(): ptr(nullptr) {}
|
||||
inline Maybe(Own<T, D>&& t) noexcept: ptr(kj::mv(t)) {}
|
||||
inline Maybe(Maybe&& other) noexcept: ptr(kj::mv(other.ptr)) {}
|
||||
|
||||
template <typename U>
|
||||
inline Maybe(Maybe<Own<U, D>>&& other): ptr(mv(other.ptr)) {}
|
||||
template <typename U>
|
||||
inline Maybe(Own<U, D>&& other): ptr(mv(other)) {}
|
||||
|
||||
inline Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
|
||||
|
||||
inline Own<T, D>& emplace(Own<T, D> value) {
|
||||
// Assign the Maybe to the given value and return the content. This avoids the need to do a
|
||||
// KJ_ASSERT_NONNULL() immediately after setting the Maybe just to read it back again.
|
||||
ptr = kj::mv(value);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
inline operator NoInfer<Maybe<U&>>() { return ptr.get(); }
|
||||
template <typename U = T>
|
||||
inline operator NoInfer<Maybe<const U&>>() const { return ptr.get(); }
|
||||
// Implicit conversion to `Maybe<U&>`. The weird templating is to make sure that
|
||||
// `Maybe<Own<void>>` can be instantiated with the compiler complaining about forming references
|
||||
// to void -- the use of templates here will cause SFINAE to kick in and hide these, whereas if
|
||||
// they are not templates then SFINAE isn't applied and so they are considered errors.
|
||||
|
||||
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, D>& orDefault(Own<T, D>& defaultValue) {
|
||||
if (ptr == nullptr) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
const Own<T, D>& orDefault(const Own<T, D>& defaultValue) const {
|
||||
if (ptr == nullptr) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F,
|
||||
typename Result = decltype(instance<bool>() ? instance<Own<T, D>>() : instance<F>()())>
|
||||
Result orDefault(F&& lazyDefaultValue) && {
|
||||
if (ptr == nullptr) {
|
||||
return lazyDefaultValue();
|
||||
} else {
|
||||
return kj::mv(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
auto map(Func&& f) & -> Maybe<decltype(f(instance<Own<T, D>&>()))> {
|
||||
if (ptr == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return f(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
auto map(Func&& f) const & -> Maybe<decltype(f(instance<const Own<T, D>&>()))> {
|
||||
if (ptr == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return f(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
auto map(Func&& f) && -> Maybe<decltype(f(instance<Own<T, D>&&>()))> {
|
||||
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, D>&&>()))> {
|
||||
if (ptr == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return f(kj::mv(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Own<T, D> ptr;
|
||||
|
||||
template <typename U>
|
||||
friend class Maybe;
|
||||
template <typename U, typename D2>
|
||||
friend _::OwnOwn<U, D2> _::readMaybe(Maybe<Own<U, D2>>&& maybe);
|
||||
template <typename U, typename D2>
|
||||
friend Own<U, D2>* _::readMaybe(Maybe<Own<U, D2>>& maybe);
|
||||
template <typename U, typename D2>
|
||||
friend const Own<U, D2>* _::readMaybe(const Maybe<Own<U, D2>>& 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>();
|
||||
|
||||
#if KJ_CPP_STD >= 202002L
|
||||
template <typename T, void(*F)(T*)>
|
||||
class CustomDisposer: public Disposer {
|
||||
public:
|
||||
void disposeImpl(void* pointer) const override {
|
||||
(*F)(reinterpret_cast<T*>(pointer));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, void(*F)(T*)>
|
||||
static constexpr CustomDisposer<T, F> CUSTOM_DISPOSER_INSTANCE {};
|
||||
#else
|
||||
template <typename T, void(*F)(T*)>
|
||||
class CustomDisposer: public Disposer {
|
||||
public:
|
||||
static const CustomDisposer instance;
|
||||
|
||||
void disposeImpl(void* pointer) const override {
|
||||
(*F)(reinterpret_cast<T*>(pointer));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, void(*F)(T*)>
|
||||
const CustomDisposer<T, F> CustomDisposer<T, F>::instance = CustomDisposer<T, F>();
|
||||
#endif
|
||||
|
||||
} // 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);
|
||||
}
|
||||
|
||||
#if KJ_CPP_STD > 201402L
|
||||
#if KJ_CPP_STD < 202002L
|
||||
template <auto F, typename T>
|
||||
Own<T> disposeWith(T* ptr) {
|
||||
// Associate a pre-allocated raw pointer with a corresponding disposal function.
|
||||
// The first template parameter should be a function pointer e.g. disposeWith<freeInt>(new int(0)).
|
||||
|
||||
return Own<T>(ptr, _::CustomDisposer<T, F>::instance);
|
||||
}
|
||||
#else
|
||||
template <auto F, typename T>
|
||||
Own<T> disposeWith(T* ptr) {
|
||||
// Associate a pre-allocated raw pointer with a corresponding disposal function.
|
||||
// The first template parameter should be a function pointer e.g. disposeWith<freeInt>(new int(0)).
|
||||
|
||||
return Own<T>(ptr, _::CUSTOM_DISPOSER_INSTANCE<T, F>);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Attachments>
|
||||
Own<Decay<T>> attachVal(T&& value, Attachments&&... attachments);
|
||||
// Returns an Own<T> that takes ownership of `value` and `attachments`, and points to `value`.
|
||||
//
|
||||
// This is equivalent to heap(value).attach(attachments), but only does one allocation rather than
|
||||
// two.
|
||||
|
||||
template <typename T, typename... Attachments>
|
||||
Own<T> attachRef(T& value, Attachments&&... attachments);
|
||||
// Like attach() but `value` is not moved; the resulting Own<T> points to its existing location.
|
||||
// This is preferred if `value` is already owned by one of `attachments`.
|
||||
|
||||
// =======================================================================================
|
||||
// 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 _ { // private
|
||||
|
||||
template <typename... T>
|
||||
struct OwnedBundle;
|
||||
|
||||
template <>
|
||||
struct OwnedBundle<> {};
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct OwnedBundle<First, Rest...>: public OwnedBundle<Rest...> {
|
||||
OwnedBundle(First&& first, Rest&&... rest)
|
||||
: OwnedBundle<Rest...>(kj::fwd<Rest>(rest)...), first(kj::fwd<First>(first)) {}
|
||||
|
||||
// Note that it's intentional that `first` is destroyed before `rest`. This way, doing
|
||||
// ptr.attach(foo, bar, baz) is equivalent to ptr.attach(foo).attach(bar).attach(baz) in terms
|
||||
// of destruction order (although the former does fewer allocations).
|
||||
Decay<First> first;
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
struct DisposableOwnedBundle final: public Disposer, public OwnedBundle<T...> {
|
||||
DisposableOwnedBundle(T&&... values): OwnedBundle<T...>(kj::fwd<T>(values)...) {}
|
||||
void disposeImpl(void* pointer) const override { delete this; }
|
||||
};
|
||||
|
||||
template <typename T, typename StaticDisposer>
|
||||
class StaticDisposerAdapter final: public Disposer {
|
||||
// Adapts a static disposer to be called dynamically.
|
||||
public:
|
||||
virtual void disposeImpl(void* pointer) const override {
|
||||
StaticDisposer::dispose(reinterpret_cast<T*>(pointer));
|
||||
}
|
||||
|
||||
static const StaticDisposerAdapter instance;
|
||||
};
|
||||
|
||||
template <typename T, typename D>
|
||||
const StaticDisposerAdapter<T, D> StaticDisposerAdapter<T, D>::instance =
|
||||
StaticDisposerAdapter<T, D>();
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T>
|
||||
template <typename... Attachments>
|
||||
Own<T> Own<T>::attach(Attachments&&... attachments) {
|
||||
T* ptrCopy = ptr;
|
||||
|
||||
KJ_IREQUIRE(ptrCopy != nullptr, "cannot attach to null pointer");
|
||||
|
||||
// HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
|
||||
// accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
|
||||
// disposer will still be called when the pointer goes out of scope.
|
||||
if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
|
||||
|
||||
auto bundle = new _::DisposableOwnedBundle<Own<T>, Attachments...>(
|
||||
kj::mv(*this), kj::fwd<Attachments>(attachments)...);
|
||||
return Own<T>(ptrCopy, *bundle);
|
||||
}
|
||||
|
||||
template <typename T, typename... Attachments>
|
||||
Own<T> attachRef(T& value, Attachments&&... attachments) {
|
||||
auto bundle = new _::DisposableOwnedBundle<Attachments...>(kj::fwd<Attachments>(attachments)...);
|
||||
return Own<T>(&value, *bundle);
|
||||
}
|
||||
|
||||
template <typename T, typename... Attachments>
|
||||
Own<Decay<T>> attachVal(T&& value, Attachments&&... attachments) {
|
||||
auto bundle = new _::DisposableOwnedBundle<T, Attachments...>(
|
||||
kj::fwd<T>(value), kj::fwd<Attachments>(attachments)...);
|
||||
return Own<Decay<T>>(&bundle->first, *bundle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U, typename StaticDisposer, typename>
|
||||
inline Own<T>::Own(Own<U, StaticDisposer>&& other) noexcept
|
||||
: ptr(cast(other.ptr)) {
|
||||
if (_::castToVoid(other.ptr) != reinterpret_cast<void*>(other.ptr)) {
|
||||
// Oh dangit, there's some sort of multiple inheritance going on and `StaticDisposerAdapter`
|
||||
// won't actually work because it'll receive a pointer pointing to the top of the object, which
|
||||
// isn't exactly the same as the `U*` pointer it wants. We have no choice but to allocate
|
||||
// a dynamic disposer here.
|
||||
disposer = new _::DisposableOwnedBundle<Own<U, StaticDisposer>>(kj::mv(other));
|
||||
} else {
|
||||
disposer = &_::StaticDisposerAdapter<U, StaticDisposer>::instance;
|
||||
other.ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
79
vendor/capnproto/src/kj/miniposix.h
vendored
Normal file
79
vendor/capnproto/src/kj/miniposix.h
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
// This header provides a small subset of the POSIX API.
|
||||
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/uio.h>
|
||||
|
||||
// To get KJ_BEGIN_HEADER/KJ_END_HEADER
|
||||
#include "common.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
namespace miniposix {
|
||||
|
||||
using ::ssize_t;
|
||||
using ::read;
|
||||
using ::write;
|
||||
using ::close;
|
||||
|
||||
|
||||
using ::pipe;
|
||||
using ::mkdir;
|
||||
|
||||
|
||||
// Apparently, there is a maximum number of iovecs allowed per call. I don't understand why.
|
||||
// Most platforms define IOV_MAX but Linux defines only UIO_MAXIOV and others, like Hurd,
|
||||
// define neither.
|
||||
//
|
||||
// On platforms where both IOV_MAX and UIO_MAXIOV are undefined, we poke sysconf(_SC_IOV_MAX),
|
||||
// then try to fall back to the POSIX-mandated minimum of _XOPEN_IOV_MAX if that fails.
|
||||
//
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html#tag_13_23_03_01
|
||||
#if defined(IOV_MAX)
|
||||
// Solaris, MacOS (& all other BSD-variants?) (and others?)
|
||||
static constexpr inline size_t iovMax() {
|
||||
return IOV_MAX;
|
||||
}
|
||||
#elif defined(UIO_MAX_IOV)
|
||||
// Linux
|
||||
static constexpr inline size_t iovMax() {
|
||||
return UIO_MAX_IOV;
|
||||
}
|
||||
#else
|
||||
#error "Please determine the appropriate constant for IOV_MAX on your system."
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace miniposix
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
854
vendor/capnproto/src/kj/mutex.c++
vendored
Normal file
854
vendor/capnproto/src/kj/mutex.c++
vendored
Normal file
@@ -0,0 +1,854 @@
|
||||
// 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 "mutex.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if KJ_USE_FUTEX
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/futex.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef SYS_futex
|
||||
// Missing on Android/Bionic.
|
||||
#ifdef __NR_futex
|
||||
#define SYS_futex __NR_futex
|
||||
#elif defined(SYS_futex_time64)
|
||||
#define SYS_futex SYS_futex_time64
|
||||
#else
|
||||
#error "Need working SYS_futex"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX_WAIT_PRIVATE
|
||||
// Missing on Android/Bionic.
|
||||
#define FUTEX_WAIT_PRIVATE FUTEX_WAIT
|
||||
#define FUTEX_WAKE_PRIVATE FUTEX_WAKE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace kj {
|
||||
#if KJ_TRACK_LOCK_BLOCKING
|
||||
static thread_local const BlockedOnReason* tlsBlockReason __attribute((tls_model("initial-exec")));
|
||||
// The initial-exec model ensures that even if this code is part of a shared library built PIC, then
|
||||
// we still place this variable in the appropriate ELF section so that __tls_get_addr is avoided.
|
||||
// It's unclear if __tls_get_addr is still not async signal safe in glibc. The only negative
|
||||
// downside of this approach is that a shared library built with kj & lock tracking will fail if
|
||||
// dlopen'ed which isn't an intended use-case for the initial implementation.
|
||||
|
||||
Maybe<const BlockedOnReason&> blockedReason() noexcept {
|
||||
if (tlsBlockReason == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return *tlsBlockReason;
|
||||
}
|
||||
|
||||
static void setCurrentThreadIsWaitingFor(const BlockedOnReason* meta) {
|
||||
tlsBlockReason = meta;
|
||||
}
|
||||
|
||||
static void setCurrentThreadIsNoLongerWaiting() {
|
||||
tlsBlockReason = nullptr;
|
||||
}
|
||||
#elif KJ_USE_FUTEX
|
||||
struct BlockedOnMutexAcquisition {
|
||||
constexpr BlockedOnMutexAcquisition(const _::Mutex& mutex, LockSourceLocationArg) {}
|
||||
};
|
||||
|
||||
struct BlockedOnCondVarWait {
|
||||
constexpr BlockedOnCondVarWait(const _::Mutex& mutex, const void *waiter,
|
||||
LockSourceLocationArg) {}
|
||||
};
|
||||
|
||||
struct BlockedOnOnceInit {
|
||||
constexpr BlockedOnOnceInit(const _::Once& once, LockSourceLocationArg) {}
|
||||
};
|
||||
|
||||
struct BlockedOnReason {
|
||||
constexpr BlockedOnReason(const BlockedOnMutexAcquisition&) {}
|
||||
constexpr BlockedOnReason(const BlockedOnCondVarWait&) {}
|
||||
constexpr BlockedOnReason(const BlockedOnOnceInit&) {}
|
||||
};
|
||||
|
||||
static void setCurrentThreadIsWaitingFor(const BlockedOnReason* meta) {}
|
||||
static void setCurrentThreadIsNoLongerWaiting() {}
|
||||
#endif
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
#if KJ_USE_FUTEX
|
||||
constexpr uint Mutex::EXCLUSIVE_HELD;
|
||||
constexpr uint Mutex::EXCLUSIVE_REQUESTED;
|
||||
constexpr uint Mutex::SHARED_COUNT_MASK;
|
||||
#endif
|
||||
|
||||
inline void Mutex::addWaiter(Waiter& waiter) {
|
||||
#ifdef KJ_DEBUG
|
||||
assertLockedByCaller(EXCLUSIVE);
|
||||
#endif
|
||||
*waitersTail = waiter;
|
||||
waitersTail = &waiter.next;
|
||||
}
|
||||
inline void Mutex::removeWaiter(Waiter& waiter) {
|
||||
#ifdef KJ_DEBUG
|
||||
assertLockedByCaller(EXCLUSIVE);
|
||||
#endif
|
||||
*waiter.prev = waiter.next;
|
||||
KJ_IF_MAYBE(next, waiter.next) {
|
||||
next->prev = waiter.prev;
|
||||
} else {
|
||||
KJ_DASSERT(waitersTail == &waiter.next);
|
||||
waitersTail = waiter.prev;
|
||||
}
|
||||
}
|
||||
|
||||
bool Mutex::checkPredicate(Waiter& waiter) {
|
||||
// Run the predicate from a thread other than the waiting thread, returning true if it's time to
|
||||
// signal the waiting thread. This is not only when the predicate passes, but also when it
|
||||
// throws, in which case we want to propagate the exception to the waiting thread.
|
||||
|
||||
if (waiter.exception != nullptr) return true; // don't run again after an exception
|
||||
|
||||
bool result = false;
|
||||
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
|
||||
result = waiter.predicate.check();
|
||||
})) {
|
||||
// Exception thrown.
|
||||
result = true;
|
||||
waiter.exception = kj::heap(kj::mv(*exception));
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
TimePoint toTimePoint(struct timespec ts) {
|
||||
return kj::origin<TimePoint>() + ts.tv_sec * kj::SECONDS + ts.tv_nsec * kj::NANOSECONDS;
|
||||
}
|
||||
TimePoint now() {
|
||||
struct timespec now;
|
||||
KJ_SYSCALL(clock_gettime(CLOCK_MONOTONIC, &now));
|
||||
return toTimePoint(now);
|
||||
}
|
||||
struct timespec toRelativeTimespec(Duration timeout) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = timeout / kj::SECONDS;
|
||||
ts.tv_nsec = timeout % kj::SECONDS / kj::NANOSECONDS;
|
||||
return ts;
|
||||
}
|
||||
struct timespec toAbsoluteTimespec(TimePoint time) {
|
||||
return toRelativeTimespec(time - kj::origin<TimePoint>());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#if KJ_USE_FUTEX
|
||||
// =======================================================================================
|
||||
// Futex-based implementation (Linux-only)
|
||||
|
||||
#if KJ_SAVE_ACQUIRED_LOCK_INFO
|
||||
#if !__GLIBC_PREREQ(2, 30)
|
||||
#ifndef SYS_gettid
|
||||
#error SYS_gettid is unavailable on this system
|
||||
#endif
|
||||
|
||||
#define gettid() ((pid_t)syscall(SYS_gettid))
|
||||
#endif
|
||||
|
||||
static thread_local pid_t tlsTid = gettid();
|
||||
#define TRACK_ACQUIRED_TID() tlsTid
|
||||
|
||||
Mutex::AcquiredMetadata Mutex::lockedInfo() const {
|
||||
auto state = __atomic_load_n(&futex, __ATOMIC_RELAXED);
|
||||
auto tid = lockedExclusivelyByThread;
|
||||
auto location = lockAcquiredLocation;
|
||||
|
||||
if (state & EXCLUSIVE_HELD) {
|
||||
return HoldingExclusively{tid, location};
|
||||
} else {
|
||||
return HoldingShared{location};
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#define TRACK_ACQUIRED_TID() 0
|
||||
#endif
|
||||
|
||||
Mutex::Mutex(): futex(0) {}
|
||||
Mutex::~Mutex() {
|
||||
// This will crash anyway, might as well crash with a nice error message.
|
||||
KJ_ASSERT(futex == 0, "Mutex destroyed while locked.") { break; }
|
||||
}
|
||||
|
||||
bool Mutex::lock(Exclusivity exclusivity, Maybe<Duration> timeout, LockSourceLocationArg location) {
|
||||
BlockedOnReason blockReason = BlockedOnMutexAcquisition{*this, location};
|
||||
KJ_DEFER(setCurrentThreadIsNoLongerWaiting());
|
||||
|
||||
auto spec = timeout.map([](Duration d) { return toRelativeTimespec(d); });
|
||||
struct timespec* specp = nullptr;
|
||||
KJ_IF_MAYBE(s, spec) {
|
||||
specp = s;
|
||||
}
|
||||
|
||||
switch (exclusivity) {
|
||||
case EXCLUSIVE:
|
||||
for (;;) {
|
||||
uint state = 0;
|
||||
if (KJ_LIKELY(__atomic_compare_exchange_n(&futex, &state, EXCLUSIVE_HELD, false,
|
||||
__ATOMIC_ACQUIRE, __ATOMIC_RELAXED))) {
|
||||
|
||||
// Acquired.
|
||||
break;
|
||||
}
|
||||
|
||||
// The mutex is contended. Set the exclusive-requested bit and wait.
|
||||
if ((state & EXCLUSIVE_REQUESTED) == 0) {
|
||||
if (!__atomic_compare_exchange_n(&futex, &state, state | EXCLUSIVE_REQUESTED, false,
|
||||
__ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
// Oops, the state changed before we could set the request bit. Start over.
|
||||
continue;
|
||||
}
|
||||
|
||||
state |= EXCLUSIVE_REQUESTED;
|
||||
}
|
||||
|
||||
setCurrentThreadIsWaitingFor(&blockReason);
|
||||
|
||||
auto result = syscall(SYS_futex, &futex, FUTEX_WAIT_PRIVATE, state, specp, nullptr, 0);
|
||||
if (result < 0) {
|
||||
if (errno == ETIMEDOUT) {
|
||||
setCurrentThreadIsNoLongerWaiting();
|
||||
// We timed out, we can't remove the exclusive request flag (since others might be waiting)
|
||||
// so we just return false.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
acquiredExclusive(TRACK_ACQUIRED_TID(), location);
|
||||
#if KJ_CONTENTION_WARNING_THRESHOLD
|
||||
printContendedReader = false;
|
||||
#endif
|
||||
break;
|
||||
case SHARED: {
|
||||
#if KJ_CONTENTION_WARNING_THRESHOLD
|
||||
kj::Maybe<kj::TimePoint> contentionWaitStart;
|
||||
#endif
|
||||
|
||||
uint state = __atomic_add_fetch(&futex, 1, __ATOMIC_ACQUIRE);
|
||||
|
||||
for (;;) {
|
||||
if (KJ_LIKELY((state & EXCLUSIVE_HELD) == 0)) {
|
||||
// Acquired.
|
||||
break;
|
||||
}
|
||||
|
||||
#if KJ_CONTENTION_WARNING_THRESHOLD
|
||||
if (contentionWaitStart == nullptr) {
|
||||
// We could have the exclusive mutex tell us how long it was holding the lock. That would
|
||||
// be the nicest. However, I'm hesitant to bloat the structure. I suspect having a reader
|
||||
// tell us how long it was waiting for is probably a good proxy.
|
||||
contentionWaitStart = kj::systemPreciseMonotonicClock().now();
|
||||
}
|
||||
#endif
|
||||
|
||||
setCurrentThreadIsWaitingFor(&blockReason);
|
||||
|
||||
// The mutex is exclusively locked by another thread. Since we incremented the counter
|
||||
// already, we just have to wait for it to be unlocked.
|
||||
auto result = syscall(SYS_futex, &futex, FUTEX_WAIT_PRIVATE, state, specp, nullptr, 0);
|
||||
if (result < 0) {
|
||||
// If we timeout though, we need to signal that we're not waiting anymore.
|
||||
if (errno == ETIMEDOUT) {
|
||||
setCurrentThreadIsNoLongerWaiting();
|
||||
state = __atomic_sub_fetch(&futex, 1, __ATOMIC_RELAXED);
|
||||
|
||||
// We may have unlocked since we timed out. So act like we just unlocked the mutex
|
||||
// and maybe send a wait signal if needed. See Mutex::unlock SHARED case.
|
||||
if (KJ_UNLIKELY(state == EXCLUSIVE_REQUESTED)) {
|
||||
if (__atomic_compare_exchange_n(
|
||||
&futex, &state, 0, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
// Wake all exclusive waiters. We have to wake all of them because one of them will
|
||||
// grab the lock while the others will re-establish the exclusive-requested bit.
|
||||
syscall(SYS_futex, &futex, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state = __atomic_load_n(&futex, __ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
#ifdef KJ_CONTENTION_WARNING_THRESHOLD
|
||||
KJ_IF_MAYBE(start, contentionWaitStart) {
|
||||
if (__atomic_load_n(&printContendedReader, __ATOMIC_RELAXED)) {
|
||||
// Double-checked lock avoids the CPU needing to acquire the lock in most cases.
|
||||
if (__atomic_exchange_n(&printContendedReader, false, __ATOMIC_RELAXED)) {
|
||||
auto contentionDuration = kj::systemPreciseMonotonicClock().now() - *start;
|
||||
KJ_LOG(WARNING, "Acquired contended lock", location, contentionDuration,
|
||||
kj::getStackTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// We just want to record the lock being acquired somewhere but the specific location doesn't
|
||||
// matter. This does mean that race conditions could occur where a thread might read this
|
||||
// inconsistently (e.g. filename from 1 lock & function from another). This currently is just
|
||||
// meant to be a debugging aid for manual analysis so it's OK for that purpose. If it's ever
|
||||
// required for this to be used for anything else, then this should probably be changed to
|
||||
// use an additional atomic variable that can ensure only one writer updates this. Or use the
|
||||
// futex variable to ensure that this is only done for the first one to acquire the lock,
|
||||
// although there may be thundering herd problems with that whereby there's a long wallclock
|
||||
// time between when the lock is acquired and when the location is updated (since the first
|
||||
// locker isn't really guaranteed to be the first one unlocked).
|
||||
acquiredShared(location);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mutex::unlock(Exclusivity exclusivity, Waiter* waiterToSkip) {
|
||||
switch (exclusivity) {
|
||||
case EXCLUSIVE: {
|
||||
KJ_DASSERT(futex & EXCLUSIVE_HELD, "Unlocked a mutex that wasn't locked.");
|
||||
|
||||
#ifdef KJ_CONTENTION_WARNING_THRESHOLD
|
||||
auto acquiredLocation = releasingExclusive();
|
||||
#endif
|
||||
|
||||
// First check if there are any conditional waiters. Note we only do this when unlocking an
|
||||
// exclusive lock since under a shared lock the state couldn't have changed.
|
||||
auto nextWaiter = waitersHead;
|
||||
for (;;) {
|
||||
KJ_IF_MAYBE(waiter, nextWaiter) {
|
||||
nextWaiter = waiter->next;
|
||||
|
||||
if (waiter != waiterToSkip && checkPredicate(*waiter)) {
|
||||
// This waiter's predicate now evaluates true, so wake it up.
|
||||
if (waiter->hasTimeout) {
|
||||
// In this case we need to be careful to make sure the target thread isn't already
|
||||
// processing a timeout, so we need to do an atomic CAS rather than just a store.
|
||||
uint expected = 0;
|
||||
if (__atomic_compare_exchange_n(&waiter->futex, &expected, 1, false,
|
||||
__ATOMIC_RELEASE, __ATOMIC_RELAXED)) {
|
||||
// Good, we set it to 1, transferring ownership of the mutex. Continue on below.
|
||||
} else {
|
||||
// Looks like the thread already timed out and set its own futex to 1. In that
|
||||
// case it is going to try to lock the mutex itself, so we should NOT attempt an
|
||||
// ownership transfer as this will deadlock.
|
||||
//
|
||||
// We have two options here: We can continue along the waiter list looking for
|
||||
// another waiter that's ready to be signaled, or we could drop out of the list
|
||||
// immediately since we know that another thread is already waiting for the lock
|
||||
// and will re-evaluate the waiter queue itself when it is done. It feels cleaner
|
||||
// to me to continue.
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
__atomic_store_n(&waiter->futex, 1, __ATOMIC_RELEASE);
|
||||
}
|
||||
syscall(SYS_futex, &waiter->futex, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
|
||||
// We transferred ownership of the lock to this waiter, so we're done now.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// No more waiters.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef KJ_CONTENTION_WARNING_THRESHOLD
|
||||
uint readerCount;
|
||||
{
|
||||
uint oldState = __atomic_load_n(&futex, __ATOMIC_RELAXED);
|
||||
readerCount = oldState & SHARED_COUNT_MASK;
|
||||
if (readerCount >= KJ_CONTENTION_WARNING_THRESHOLD) {
|
||||
// Atomic not needed because we're still holding the exclusive lock.
|
||||
printContendedReader = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Didn't wake any waiters, so wake normally.
|
||||
uint oldState = __atomic_fetch_and(
|
||||
&futex, ~(EXCLUSIVE_HELD | EXCLUSIVE_REQUESTED), __ATOMIC_RELEASE);
|
||||
|
||||
if (KJ_UNLIKELY(oldState & ~EXCLUSIVE_HELD)) {
|
||||
// Other threads are waiting. If there are any shared waiters, they now collectively hold
|
||||
// the lock, and we must wake them up. If there are any exclusive waiters, we must wake
|
||||
// them up even if readers are waiting so that at the very least they may re-establish the
|
||||
// EXCLUSIVE_REQUESTED bit that we just removed.
|
||||
syscall(SYS_futex, &futex, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
|
||||
#ifdef KJ_CONTENTION_WARNING_THRESHOLD
|
||||
if (readerCount >= KJ_CONTENTION_WARNING_THRESHOLD) {
|
||||
KJ_LOG(WARNING, "excessively many readers were waiting on this lock", readerCount,
|
||||
acquiredLocation, kj::getStackTrace());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SHARED: {
|
||||
KJ_DASSERT(futex & SHARED_COUNT_MASK, "Unshared a mutex that wasn't shared.");
|
||||
uint state = __atomic_sub_fetch(&futex, 1, __ATOMIC_RELEASE);
|
||||
|
||||
// The only case where anyone is waiting is if EXCLUSIVE_REQUESTED is set, and the only time
|
||||
// it makes sense to wake up that waiter is if the shared count has reached zero.
|
||||
if (KJ_UNLIKELY(state == EXCLUSIVE_REQUESTED)) {
|
||||
if (__atomic_compare_exchange_n(
|
||||
&futex, &state, 0, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
// Wake all exclusive waiters. We have to wake all of them because one of them will
|
||||
// grab the lock while the others will re-establish the exclusive-requested bit.
|
||||
syscall(SYS_futex, &futex, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::assertLockedByCaller(Exclusivity exclusivity) const {
|
||||
switch (exclusivity) {
|
||||
case EXCLUSIVE:
|
||||
KJ_ASSERT(futex & EXCLUSIVE_HELD,
|
||||
"Tried to call getAlreadyLocked*() but lock is not held.");
|
||||
break;
|
||||
case SHARED:
|
||||
KJ_ASSERT(futex & SHARED_COUNT_MASK,
|
||||
"Tried to call getAlreadyLocked*() but lock is not held.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::wait(Predicate& predicate, Maybe<Duration> timeout, LockSourceLocationArg location) {
|
||||
// Add waiter to list.
|
||||
Waiter waiter { nullptr, waitersTail, predicate, nullptr, 0, timeout != nullptr };
|
||||
addWaiter(waiter);
|
||||
|
||||
BlockedOnReason blockReason = BlockedOnCondVarWait{*this, &waiter, location};
|
||||
KJ_DEFER(setCurrentThreadIsNoLongerWaiting());
|
||||
|
||||
// To guarantee that we've re-locked the mutex before scope exit, keep track of whether it is
|
||||
// currently.
|
||||
bool currentlyLocked = true;
|
||||
KJ_DEFER({
|
||||
// Infinite timeout for re-obtaining the lock is on purpose because the post-condition for this
|
||||
// function has to be that the lock state hasn't changed (& we have to be locked when we enter
|
||||
// since that's how condvars work).
|
||||
if (!currentlyLocked) lock(EXCLUSIVE, nullptr, location);
|
||||
removeWaiter(waiter);
|
||||
});
|
||||
|
||||
if (!predicate.check()) {
|
||||
unlock(EXCLUSIVE, &waiter);
|
||||
currentlyLocked = false;
|
||||
|
||||
struct timespec ts;
|
||||
struct timespec* tsp = nullptr;
|
||||
KJ_IF_MAYBE(t, timeout) {
|
||||
ts = toAbsoluteTimespec(now() + *t);
|
||||
tsp = &ts;
|
||||
}
|
||||
|
||||
setCurrentThreadIsWaitingFor(&blockReason);
|
||||
|
||||
// Wait for someone to set our futex to 1.
|
||||
for (;;) {
|
||||
// Note we use FUTEX_WAIT_BITSET_PRIVATE + FUTEX_BITSET_MATCH_ANY to get the same effect as
|
||||
// FUTEX_WAIT_PRIVATE except that the timeout is specified as an absolute time based on
|
||||
// CLOCK_MONOTONIC. Otherwise, FUTEX_WAIT_PRIVATE interprets it as a relative time, forcing
|
||||
// us to recompute the time after every iteration.
|
||||
KJ_SYSCALL_HANDLE_ERRORS(syscall(SYS_futex,
|
||||
&waiter.futex, FUTEX_WAIT_BITSET_PRIVATE, 0, tsp, nullptr, FUTEX_BITSET_MATCH_ANY)) {
|
||||
case EAGAIN:
|
||||
// Indicates that the futex was already non-zero by the time the kernel looked at it.
|
||||
// Not an error.
|
||||
break;
|
||||
case ETIMEDOUT: {
|
||||
// Wait timed out. This leaves us in a bit of a pickle: Ownership of the mutex was not
|
||||
// transferred to us from another thread. So, we need to lock it ourselves. But, another
|
||||
// thread might be in the process of signaling us and transferring ownership. So, we
|
||||
// first must atomically take control of our destiny.
|
||||
KJ_ASSERT(timeout != nullptr);
|
||||
uint expected = 0;
|
||||
if (__atomic_compare_exchange_n(&waiter.futex, &expected, 1, false,
|
||||
__ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
|
||||
// OK, we set our own futex to 1. That means no other thread will, and so we won't be
|
||||
// receiving a mutex ownership transfer. We have to lock the mutex ourselves.
|
||||
setCurrentThreadIsNoLongerWaiting();
|
||||
lock(EXCLUSIVE, nullptr, location);
|
||||
currentlyLocked = true;
|
||||
return;
|
||||
} else {
|
||||
// Oh, someone else actually did signal us, apparently. Let's move on as if the futex
|
||||
// call told us so.
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
KJ_FAIL_SYSCALL("futex(FUTEX_WAIT_PRIVATE)", error);
|
||||
}
|
||||
|
||||
setCurrentThreadIsNoLongerWaiting();
|
||||
|
||||
if (__atomic_load_n(&waiter.futex, __ATOMIC_ACQUIRE)) {
|
||||
// We received a lock ownership transfer from another thread.
|
||||
currentlyLocked = true;
|
||||
|
||||
// The other thread checked the predicate before the transfer.
|
||||
#ifdef KJ_DEBUG
|
||||
assertLockedByCaller(EXCLUSIVE);
|
||||
#endif
|
||||
|
||||
KJ_IF_MAYBE(exception, waiter.exception) {
|
||||
// The predicate threw an exception, apparently. Propagate it.
|
||||
// TODO(someday): Could we somehow have this be a recoverable exception? Presumably we'd
|
||||
// then want MutexGuarded::when() to skip calling the callback, but then what should it
|
||||
// return, since it normally returns the callback's result? Or maybe people who disable
|
||||
// exceptions just really should not write predicates that can throw.
|
||||
kj::throwFatalException(kj::mv(**exception));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Once::runOnce(Initializer& init, LockSourceLocationArg location) {
|
||||
startOver:
|
||||
uint state = UNINITIALIZED;
|
||||
if (__atomic_compare_exchange_n(&futex, &state, INITIALIZING, false,
|
||||
__ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
// It's our job to initialize!
|
||||
{
|
||||
KJ_ON_SCOPE_FAILURE({
|
||||
// An exception was thrown by the initializer. We have to revert.
|
||||
if (__atomic_exchange_n(&futex, UNINITIALIZED, __ATOMIC_RELEASE) ==
|
||||
INITIALIZING_WITH_WAITERS) {
|
||||
// Someone was waiting for us to finish.
|
||||
syscall(SYS_futex, &futex, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
}
|
||||
});
|
||||
|
||||
init.run();
|
||||
}
|
||||
if (__atomic_exchange_n(&futex, INITIALIZED, __ATOMIC_RELEASE) ==
|
||||
INITIALIZING_WITH_WAITERS) {
|
||||
// Someone was waiting for us to finish.
|
||||
syscall(SYS_futex, &futex, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
}
|
||||
} else {
|
||||
BlockedOnReason blockReason = BlockedOnOnceInit{*this, location};
|
||||
KJ_DEFER(setCurrentThreadIsNoLongerWaiting());
|
||||
|
||||
for (;;) {
|
||||
if (state == INITIALIZED) {
|
||||
break;
|
||||
} else if (state == INITIALIZING) {
|
||||
// Initialization is taking place in another thread. Indicate that we're waiting.
|
||||
if (!__atomic_compare_exchange_n(&futex, &state, INITIALIZING_WITH_WAITERS, true,
|
||||
__ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
|
||||
// State changed, retry.
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
KJ_DASSERT(state == INITIALIZING_WITH_WAITERS);
|
||||
}
|
||||
|
||||
// Wait for initialization.
|
||||
setCurrentThreadIsWaitingFor(&blockReason);
|
||||
syscall(SYS_futex, &futex, FUTEX_WAIT_PRIVATE, INITIALIZING_WITH_WAITERS,
|
||||
nullptr, nullptr, 0);
|
||||
state = __atomic_load_n(&futex, __ATOMIC_ACQUIRE);
|
||||
|
||||
if (state == UNINITIALIZED) {
|
||||
// Oh hey, apparently whoever was trying to initialize gave up. Let's take it from the
|
||||
// top.
|
||||
goto startOver;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Once::reset() {
|
||||
uint state = INITIALIZED;
|
||||
if (!__atomic_compare_exchange_n(&futex, &state, UNINITIALIZED,
|
||||
false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) {
|
||||
KJ_FAIL_REQUIRE("reset() called while not initialized.");
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// =======================================================================================
|
||||
// Generic pthreads-based implementation
|
||||
|
||||
#define KJ_PTHREAD_CALL(code) \
|
||||
{ \
|
||||
int pthreadError = code; \
|
||||
if (pthreadError != 0) { \
|
||||
KJ_FAIL_SYSCALL(#code, pthreadError); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define KJ_PTHREAD_CLEANUP(code) \
|
||||
{ \
|
||||
int pthreadError = code; \
|
||||
if (pthreadError != 0) { \
|
||||
KJ_LOG(ERROR, #code, strerror(pthreadError)); \
|
||||
} \
|
||||
}
|
||||
|
||||
Mutex::Mutex(): mutex(PTHREAD_RWLOCK_INITIALIZER) {
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
|
||||
// In older versions of MacOS, mutexes initialized statically cannot be destroyed,
|
||||
// so we must call the init function.
|
||||
KJ_PTHREAD_CALL(pthread_rwlock_init(&mutex, NULL));
|
||||
#endif
|
||||
}
|
||||
Mutex::~Mutex() {
|
||||
KJ_PTHREAD_CLEANUP(pthread_rwlock_destroy(&mutex));
|
||||
}
|
||||
|
||||
bool Mutex::lock(Exclusivity exclusivity, Maybe<Duration> timeout, NoopSourceLocation) {
|
||||
if (timeout != nullptr) {
|
||||
KJ_UNIMPLEMENTED("Locking a mutex with a timeout is only supported on Linux.");
|
||||
}
|
||||
switch (exclusivity) {
|
||||
case EXCLUSIVE:
|
||||
KJ_PTHREAD_CALL(pthread_rwlock_wrlock(&mutex));
|
||||
break;
|
||||
case SHARED:
|
||||
KJ_PTHREAD_CALL(pthread_rwlock_rdlock(&mutex));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mutex::unlock(Exclusivity exclusivity, Waiter* waiterToSkip) {
|
||||
KJ_DEFER(KJ_PTHREAD_CALL(pthread_rwlock_unlock(&mutex)));
|
||||
|
||||
if (exclusivity == EXCLUSIVE) {
|
||||
// Check if there are any conditional waiters. Note we only do this when unlocking an
|
||||
// exclusive lock since under a shared lock the state couldn't have changed.
|
||||
auto nextWaiter = waitersHead;
|
||||
for (;;) {
|
||||
KJ_IF_MAYBE(waiter, nextWaiter) {
|
||||
nextWaiter = waiter->next;
|
||||
|
||||
if (waiter != waiterToSkip && checkPredicate(*waiter)) {
|
||||
// This waiter's predicate now evaluates true, so wake it up. It doesn't matter if we
|
||||
// use _signal() vs. _broadcast() here since there's always only one thread waiting.
|
||||
KJ_PTHREAD_CALL(pthread_mutex_lock(&waiter->stupidMutex));
|
||||
KJ_PTHREAD_CALL(pthread_cond_signal(&waiter->condvar));
|
||||
KJ_PTHREAD_CALL(pthread_mutex_unlock(&waiter->stupidMutex));
|
||||
|
||||
// We only need to wake one waiter. Note that unlike the futex-based implementation, we
|
||||
// cannot "transfer ownership" of the lock to the waiter, therefore we cannot guarantee
|
||||
// that the condition is still true when that waiter finally awakes. However, if the
|
||||
// condition is no longer true at that point, the waiter will re-check all other waiters'
|
||||
// conditions and possibly wake up any other waiter who is now ready, hence we still only
|
||||
// need to wake one waiter here.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// No more waiters.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::assertLockedByCaller(Exclusivity exclusivity) const {
|
||||
switch (exclusivity) {
|
||||
case EXCLUSIVE:
|
||||
// A read lock should fail if the mutex is already held for writing.
|
||||
if (pthread_rwlock_tryrdlock(&mutex) == 0) {
|
||||
pthread_rwlock_unlock(&mutex);
|
||||
KJ_FAIL_ASSERT("Tried to call getAlreadyLocked*() but lock is not held.");
|
||||
}
|
||||
break;
|
||||
case SHARED:
|
||||
// A write lock should fail if the mutex is already held for reading or writing. We don't
|
||||
// have any way to prove that the lock is held only for reading.
|
||||
if (pthread_rwlock_trywrlock(&mutex) == 0) {
|
||||
pthread_rwlock_unlock(&mutex);
|
||||
KJ_FAIL_ASSERT("Tried to call getAlreadyLocked*() but lock is not held.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::wait(Predicate& predicate, Maybe<Duration> timeout, NoopSourceLocation) {
|
||||
// Add waiter to list.
|
||||
Waiter waiter {
|
||||
nullptr, waitersTail, predicate, nullptr,
|
||||
PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
|
||||
// In older versions of MacOS, mutexes initialized statically cannot be destroyed,
|
||||
// so we must call the init function.
|
||||
KJ_PTHREAD_CALL(pthread_cond_init(&waiter.condvar, NULL));
|
||||
KJ_PTHREAD_CALL(pthread_mutex_init(&waiter.stupidMutex, NULL));
|
||||
#endif
|
||||
|
||||
addWaiter(waiter);
|
||||
|
||||
// To guarantee that we've re-locked the mutex before scope exit, keep track of whether it is
|
||||
// currently.
|
||||
bool currentlyLocked = true;
|
||||
KJ_DEFER({
|
||||
if (!currentlyLocked) lock(EXCLUSIVE, nullptr, NoopSourceLocation{});
|
||||
removeWaiter(waiter);
|
||||
|
||||
// Destroy pthread objects.
|
||||
KJ_PTHREAD_CLEANUP(pthread_mutex_destroy(&waiter.stupidMutex));
|
||||
KJ_PTHREAD_CLEANUP(pthread_cond_destroy(&waiter.condvar));
|
||||
});
|
||||
|
||||
#if !__APPLE__
|
||||
if (timeout != nullptr) {
|
||||
// Oops, the default condvar uses the wall clock, which is dumb... fix it to use the monotonic
|
||||
// clock. (Except not on macOS, where pthread_condattr_setclock() is unimplemented, but there's
|
||||
// a bizarre pthread_cond_timedwait_relative_np() method we can use instead...)
|
||||
pthread_condattr_t attr;
|
||||
KJ_PTHREAD_CALL(pthread_condattr_init(&attr));
|
||||
KJ_PTHREAD_CALL(pthread_condattr_setclock(&attr, CLOCK_MONOTONIC));
|
||||
pthread_cond_init(&waiter.condvar, &attr);
|
||||
KJ_PTHREAD_CALL(pthread_condattr_destroy(&attr));
|
||||
}
|
||||
#endif
|
||||
|
||||
Maybe<struct timespec> endTime = timeout.map([](Duration d) {
|
||||
return toAbsoluteTimespec(now() + d);
|
||||
});
|
||||
|
||||
while (!predicate.check()) {
|
||||
// pthread condvars only work with basic mutexes, not rwlocks. So, we need to lock a basic
|
||||
// mutex before we unlock the real mutex, and the signaling thread also needs to lock this
|
||||
// mutex, in order to ensure that this thread is actually waiting on the condvar before it is
|
||||
// signaled.
|
||||
KJ_PTHREAD_CALL(pthread_mutex_lock(&waiter.stupidMutex));
|
||||
|
||||
// OK, now we can unlock the main mutex.
|
||||
unlock(EXCLUSIVE, &waiter);
|
||||
currentlyLocked = false;
|
||||
|
||||
bool timedOut = false;
|
||||
|
||||
// Wait for someone to signal the condvar.
|
||||
KJ_IF_MAYBE(t, endTime) {
|
||||
#if __APPLE__
|
||||
// On macOS, the absolute timeout can only be specified in wall time, not monotonic time,
|
||||
// which means modifying the system clock will break the wait. However, macOS happens to
|
||||
// provide an alternative relative-time wait function, so I guess we'll use that. It does
|
||||
// require recomputing the time every iteration...
|
||||
struct timespec ts = toRelativeTimespec(kj::max(toTimePoint(*t) - now(), 0 * kj::SECONDS));
|
||||
int error = pthread_cond_timedwait_relative_np(&waiter.condvar, &waiter.stupidMutex, &ts);
|
||||
#else
|
||||
int error = pthread_cond_timedwait(&waiter.condvar, &waiter.stupidMutex, t);
|
||||
#endif
|
||||
if (error != 0) {
|
||||
if (error == ETIMEDOUT) {
|
||||
timedOut = true;
|
||||
} else {
|
||||
KJ_FAIL_SYSCALL("pthread_cond_timedwait", error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
KJ_PTHREAD_CALL(pthread_cond_wait(&waiter.condvar, &waiter.stupidMutex));
|
||||
}
|
||||
|
||||
// We have to be very careful about lock ordering here. We need to unlock stupidMutex before
|
||||
// re-locking the main mutex, because another thread may have a lock on the main mutex already
|
||||
// and be waiting for a lock on stupidMutex. Note that other thread may signal the condvar
|
||||
// right after we unlock stupidMutex but before we re-lock the main mutex. That is fine,
|
||||
// because we've already been signaled.
|
||||
KJ_PTHREAD_CALL(pthread_mutex_unlock(&waiter.stupidMutex));
|
||||
|
||||
lock(EXCLUSIVE, nullptr, NoopSourceLocation{});
|
||||
currentlyLocked = true;
|
||||
|
||||
KJ_IF_MAYBE(exception, waiter.exception) {
|
||||
// The predicate threw an exception, apparently. Propagate it.
|
||||
// TODO(someday): Could we somehow have this be a recoverable exception? Presumably we'd
|
||||
// then want MutexGuarded::when() to skip calling the callback, but then what should it
|
||||
// return, since it normally returns the callback's result? Or maybe people who disable
|
||||
// exceptions just really should not write predicates that can throw.
|
||||
kj::throwFatalException(kj::mv(**exception));
|
||||
}
|
||||
|
||||
if (timedOut) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Once::Once(bool startInitialized)
|
||||
: state(startInitialized ? INITIALIZED : UNINITIALIZED),
|
||||
mutex(PTHREAD_MUTEX_INITIALIZER) {
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
|
||||
// In older versions of MacOS, mutexes initialized statically cannot be destroyed,
|
||||
// so we must call the init function.
|
||||
KJ_PTHREAD_CALL(pthread_mutex_init(&mutex, NULL));
|
||||
#endif
|
||||
}
|
||||
Once::~Once() {
|
||||
KJ_PTHREAD_CLEANUP(pthread_mutex_destroy(&mutex));
|
||||
}
|
||||
|
||||
void Once::runOnce(Initializer& init, NoopSourceLocation) {
|
||||
KJ_PTHREAD_CALL(pthread_mutex_lock(&mutex));
|
||||
KJ_DEFER(KJ_PTHREAD_CALL(pthread_mutex_unlock(&mutex)));
|
||||
|
||||
if (state != UNINITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
init.run();
|
||||
|
||||
__atomic_store_n(&state, INITIALIZED, __ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
void Once::reset() {
|
||||
State oldState = INITIALIZED;
|
||||
if (!__atomic_compare_exchange_n(&state, &oldState, UNINITIALIZED,
|
||||
false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) {
|
||||
KJ_FAIL_REQUIRE("reset() called while not initialized.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
739
vendor/capnproto/src/kj/mutex.h
vendored
Normal file
739
vendor/capnproto/src/kj/mutex.h
vendored
Normal file
@@ -0,0 +1,739 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
#include <inttypes.h>
|
||||
#include "time.h"
|
||||
#include "source-location.h"
|
||||
#include "one-of.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
#if __linux__ && !defined(KJ_USE_FUTEX)
|
||||
#define KJ_USE_FUTEX 1
|
||||
#endif
|
||||
|
||||
#if !KJ_USE_FUTEX
|
||||
// We fall back to pthreads when we don't have a better platform-specific primitive. pthreads
|
||||
// mutexes are bloated, though, so we use futex() on Linux.
|
||||
//
|
||||
// TODO(someday): Write efficient low-level locking primitives for other platforms.
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
// There are 3 macros controlling lock tracking:
|
||||
// KJ_TRACK_LOCK_BLOCKING will set up async signal safe TLS variables that can be used to identify
|
||||
// the KJ primitive blocking the current thread.
|
||||
// KJ_SAVE_ACQUIRED_LOCK_INFO will allow introspection of a Mutex to get information about what is
|
||||
// currently holding the lock.
|
||||
// KJ_TRACK_LOCK_ACQUISITION is automatically enabled by either one of them.
|
||||
|
||||
#if KJ_TRACK_LOCK_BLOCKING
|
||||
// Lock tracking is required to keep track of what blocked.
|
||||
#define KJ_TRACK_LOCK_ACQUISITION 1
|
||||
#endif
|
||||
|
||||
#if KJ_SAVE_ACQUIRED_LOCK_INFO
|
||||
#define KJ_TRACK_LOCK_ACQUISITION 1
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace kj {
|
||||
#if KJ_TRACK_LOCK_ACQUISITION
|
||||
#if !KJ_USE_FUTEX
|
||||
#error Lock tracking is only currently supported for futex-based mutexes.
|
||||
#endif
|
||||
|
||||
#if !KJ_COMPILER_SUPPORTS_SOURCE_LOCATION
|
||||
#error C++20 or newer is required (or the use of clang/gcc).
|
||||
#endif
|
||||
|
||||
using LockSourceLocation = SourceLocation;
|
||||
using LockSourceLocationArg = const SourceLocation&;
|
||||
// On x86-64 the codegen is optimal if the argument has type const& for the location. However,
|
||||
// since this conflicts with the optimal call signature for NoopSourceLocation,
|
||||
// LockSourceLocationArg is used to conditionally select the right type without polluting the usage
|
||||
// themselves. Interestingly this makes no difference on ARM.
|
||||
// https://godbolt.org/z/q6G8ee5a3
|
||||
#else
|
||||
using LockSourceLocation = NoopSourceLocation;
|
||||
using LockSourceLocationArg = NoopSourceLocation;
|
||||
#endif
|
||||
|
||||
|
||||
class Exception;
|
||||
|
||||
// =======================================================================================
|
||||
// Private details -- public interfaces follow below.
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
#if KJ_SAVE_ACQUIRED_LOCK_INFO
|
||||
class HoldingExclusively {
|
||||
// The lock is being held in exclusive mode.
|
||||
public:
|
||||
constexpr HoldingExclusively(pid_t tid, const SourceLocation& location)
|
||||
: heldBy(tid), acquiredAt(location) {}
|
||||
|
||||
pid_t threadHoldingLock() const { return heldBy; }
|
||||
const SourceLocation& lockAcquiredAt() const { return acquiredAt; }
|
||||
|
||||
private:
|
||||
pid_t heldBy;
|
||||
SourceLocation acquiredAt;
|
||||
};
|
||||
|
||||
class HoldingShared {
|
||||
// The lock is being held in shared mode currently. Which threads are holding this lock open
|
||||
// is unknown.
|
||||
public:
|
||||
constexpr HoldingShared(const SourceLocation& location) : acquiredAt(location) {}
|
||||
|
||||
const SourceLocation& lockAcquiredAt() const { return acquiredAt; }
|
||||
|
||||
private:
|
||||
SourceLocation acquiredAt;
|
||||
};
|
||||
#endif
|
||||
|
||||
class Mutex {
|
||||
// Internal implementation details. See `MutexGuarded<T>`.
|
||||
|
||||
struct Waiter;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
KJ_DISALLOW_COPY_AND_MOVE(Mutex);
|
||||
|
||||
enum Exclusivity {
|
||||
EXCLUSIVE,
|
||||
SHARED
|
||||
};
|
||||
|
||||
bool lock(Exclusivity exclusivity, Maybe<Duration> timeout, LockSourceLocationArg location);
|
||||
void unlock(Exclusivity exclusivity, Waiter* waiterToSkip = nullptr);
|
||||
|
||||
void assertLockedByCaller(Exclusivity exclusivity) const;
|
||||
// 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.
|
||||
|
||||
class Predicate {
|
||||
public:
|
||||
virtual bool check() = 0;
|
||||
};
|
||||
|
||||
void wait(Predicate& predicate, Maybe<Duration> timeout, LockSourceLocationArg location);
|
||||
// If predicate.check() returns false, unlock the mutex until predicate.check() returns true, or
|
||||
// when the timeout (if any) expires. The mutex is always re-locked when this returns regardless
|
||||
// of whether the timeout expired, and including if it throws.
|
||||
//
|
||||
// Requires that the mutex is already exclusively locked before calling.
|
||||
|
||||
#if KJ_SAVE_ACQUIRED_LOCK_INFO
|
||||
using AcquiredMetadata = kj::OneOf<HoldingExclusively, HoldingShared>;
|
||||
KJ_DISABLE_TSAN AcquiredMetadata lockedInfo() const;
|
||||
// Returns metadata about this lock when its held. This method is async signal safe. It must also
|
||||
// be called in a state where it's guaranteed that the lock state won't be released by another
|
||||
// thread. In other words this has to be called from the signal handler within the thread that's
|
||||
// holding the lock.
|
||||
#endif
|
||||
|
||||
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.
|
||||
|
||||
#ifdef KJ_CONTENTION_WARNING_THRESHOLD
|
||||
bool printContendedReader = false;
|
||||
#endif
|
||||
|
||||
static constexpr uint EXCLUSIVE_HELD = 1u << 31;
|
||||
static constexpr uint EXCLUSIVE_REQUESTED = 1u << 30;
|
||||
static constexpr uint SHARED_COUNT_MASK = EXCLUSIVE_REQUESTED - 1;
|
||||
|
||||
#else
|
||||
mutable pthread_rwlock_t mutex;
|
||||
#endif
|
||||
|
||||
#if KJ_SAVE_ACQUIRED_LOCK_INFO
|
||||
pid_t lockedExclusivelyByThread = 0;
|
||||
SourceLocation lockAcquiredLocation;
|
||||
|
||||
KJ_DISABLE_TSAN void acquiredExclusive(pid_t tid, const SourceLocation& location) noexcept {
|
||||
lockAcquiredLocation = location;
|
||||
__atomic_store_n(&lockedExclusivelyByThread, tid, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
KJ_DISABLE_TSAN void acquiredShared(const SourceLocation& location) noexcept {
|
||||
lockAcquiredLocation = location;
|
||||
}
|
||||
|
||||
KJ_DISABLE_TSAN SourceLocation releasingExclusive() noexcept {
|
||||
auto tmp = lockAcquiredLocation;
|
||||
lockAcquiredLocation = SourceLocation{};
|
||||
lockedExclusivelyByThread = 0;
|
||||
return tmp;
|
||||
}
|
||||
#else
|
||||
static constexpr void acquiredExclusive(uint, LockSourceLocationArg) {}
|
||||
static constexpr void acquiredShared(LockSourceLocationArg) {}
|
||||
static constexpr NoopSourceLocation releasingExclusive() { return NoopSourceLocation{}; }
|
||||
#endif
|
||||
struct Waiter {
|
||||
kj::Maybe<Waiter&> next;
|
||||
kj::Maybe<Waiter&>* prev;
|
||||
Predicate& predicate;
|
||||
Maybe<Own<Exception>> exception;
|
||||
#if KJ_USE_FUTEX
|
||||
uint futex;
|
||||
bool hasTimeout;
|
||||
#else
|
||||
pthread_cond_t condvar;
|
||||
|
||||
pthread_mutex_t stupidMutex;
|
||||
// pthread condvars are only compatible with basic pthread mutexes, not rwlocks, for no
|
||||
// particularly good reason. To work around this, we need an extra mutex per condvar.
|
||||
#endif
|
||||
};
|
||||
|
||||
kj::Maybe<Waiter&> waitersHead = nullptr;
|
||||
kj::Maybe<Waiter&>* waitersTail = &waitersHead;
|
||||
// linked list of waiters; can only modify under lock
|
||||
|
||||
inline void addWaiter(Waiter& waiter);
|
||||
inline void removeWaiter(Waiter& waiter);
|
||||
bool checkPredicate(Waiter& waiter);
|
||||
};
|
||||
|
||||
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_AND_MOVE(Once);
|
||||
|
||||
class Initializer {
|
||||
public:
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
void runOnce(Initializer& init, LockSourceLocationArg location);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
#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; }
|
||||
|
||||
template <typename Cond>
|
||||
void wait(Cond&& condition, Maybe<Duration> timeout = nullptr,
|
||||
LockSourceLocationArg location = {}) {
|
||||
// Unlocks the lock until `condition(state)` evaluates true (where `state` is type `const T&`
|
||||
// referencing the object protected by the lock).
|
||||
|
||||
// We can't wait on a shared lock because the internal bookkeeping needed for a wait requires
|
||||
// the protection of an exclusive lock.
|
||||
static_assert(!isConst<T>(), "cannot wait() on shared lock");
|
||||
|
||||
struct PredicateImpl final: public _::Mutex::Predicate {
|
||||
bool check() override {
|
||||
return condition(value);
|
||||
}
|
||||
|
||||
Cond&& condition;
|
||||
const T& value;
|
||||
|
||||
PredicateImpl(Cond&& condition, const T& value)
|
||||
: condition(kj::fwd<Cond>(condition)), value(value) {}
|
||||
};
|
||||
|
||||
PredicateImpl impl(kj::fwd<Cond>(condition), *ptr);
|
||||
mutex->wait(impl, timeout, location);
|
||||
}
|
||||
|
||||
private:
|
||||
_::Mutex* mutex;
|
||||
T* ptr;
|
||||
|
||||
inline Locked(_::Mutex& mutex, T& value): mutex(&mutex), ptr(&value) {}
|
||||
|
||||
template <typename U>
|
||||
friend class MutexGuarded;
|
||||
template <typename U>
|
||||
friend class ExternalMutexGuarded;
|
||||
|
||||
|
||||
};
|
||||
|
||||
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(LockSourceLocationArg location = {}) 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(LockSourceLocationArg location = {}) 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.
|
||||
|
||||
Maybe<Locked<T>> lockExclusiveWithTimeout(Duration timeout,
|
||||
LockSourceLocationArg location = {}) const;
|
||||
// Attempts to exclusively lock the object. If the timeout elapses before the lock is acquired,
|
||||
// this returns null.
|
||||
|
||||
Maybe<Locked<const T>> lockSharedWithTimeout(Duration timeout,
|
||||
LockSourceLocationArg location = {}) const;
|
||||
// Attempts to lock the value for shared access. If the timeout elapses before the lock is acquired,
|
||||
// this returns null.
|
||||
|
||||
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.
|
||||
|
||||
template <typename Cond, typename Func>
|
||||
auto when(Cond&& condition, Func&& callback, Maybe<Duration> timeout = nullptr,
|
||||
LockSourceLocationArg location = {}) const
|
||||
-> decltype(callback(instance<T&>())) {
|
||||
// Waits until condition(state) returns true, then calls callback(state) under lock.
|
||||
//
|
||||
// `condition`, when called, receives as its parameter a const reference to the state, which is
|
||||
// locked (either shared or exclusive). `callback` receives a mutable reference, which is
|
||||
// exclusively locked.
|
||||
//
|
||||
// `condition()` may be called multiple times, from multiple threads, while waiting for the
|
||||
// condition to become true. It may even return true once, but then be called more times.
|
||||
// It is guaranteed, though, that at the time `callback()` is finally called, `condition()`
|
||||
// would currently return true (assuming it is a pure function of the guarded data).
|
||||
//
|
||||
// If `timeout` is specified, then after the given amount of time, the callback will be called
|
||||
// regardless of whether the condition is true. In this case, when `callback()` is called,
|
||||
// `condition()` may in fact evaluate false, but *only* if the timeout was reached.
|
||||
//
|
||||
// TODO(cleanup): lock->wait() is a better interface. Can we deprecate this one?
|
||||
|
||||
auto lock = lockExclusive();
|
||||
lock.wait(kj::fwd<Cond>(condition), timeout, location);
|
||||
return callback(value);
|
||||
}
|
||||
|
||||
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 ExternalMutexGuarded {
|
||||
// Holds a value that can only be manipulated while some other mutex is locked.
|
||||
//
|
||||
// The ExternalMutexGuarded<T> lives *outside* the scope of any lock on the mutex, but ensures
|
||||
// that the value it holds can only be accessed under lock by forcing the caller to present a
|
||||
// lock before accessing the value.
|
||||
//
|
||||
// Additionally, ExternalMutexGuarded<T>'s destructor will take an exclusive lock on the mutex
|
||||
// while destroying the held value, unless the value has been release()ed before hand.
|
||||
//
|
||||
// The type T must have the following properties (which probably all movable types satisfy):
|
||||
// - T is movable.
|
||||
// - Immediately after any of the following has happened, T's destructor is effectively a no-op
|
||||
// (hence certainly not requiring locks):
|
||||
// - The value has been default-constructed.
|
||||
// - The value has been initialized by-move from a default-constructed T.
|
||||
// - The value has been moved away.
|
||||
// - If ExternalMutexGuarded<T> is ever moved, then T must have a move constructor and move
|
||||
// assignment operator that do not follow any pointers, therefore do not need to take a lock.
|
||||
//
|
||||
// Inherits from LockSourceLocation to perform an empty base class optimization when lock tracking
|
||||
// is compiled out. Once the minimum C++ standard for the KJ library is C++20, this optimization
|
||||
// could be replaced by a member variable with a [[no_unique_address]] annotation.
|
||||
public:
|
||||
ExternalMutexGuarded(LockSourceLocationArg location = {})
|
||||
: location(location) {}
|
||||
|
||||
template <typename U, typename... Params>
|
||||
ExternalMutexGuarded(Locked<U> lock, Params&&... params, LockSourceLocationArg location = {})
|
||||
: mutex(lock.mutex),
|
||||
value(kj::fwd<Params>(params)...),
|
||||
location(location) {}
|
||||
// Construct the value in-place. This constructor requires passing ownership of the lock into
|
||||
// the constructor. Normally this should be a lock that you take on the line calling the
|
||||
// constructor, like:
|
||||
//
|
||||
// ExternalMutexGuarded<T> foo(someMutexGuarded.lockExclusive());
|
||||
//
|
||||
// The reason this constructor does not accept an lvalue reference to an existing lock is because
|
||||
// this would be deadlock-prone: If an exception were thrown immediately after the constructor
|
||||
// completed, then the destructor would deadlock, because the lock would still be held. An
|
||||
// ExternalMutexGuarded must live outside the scope of any locks to avoid such a deadlock.
|
||||
|
||||
~ExternalMutexGuarded() noexcept(false) {
|
||||
if (mutex != nullptr) {
|
||||
mutex->lock(_::Mutex::EXCLUSIVE, nullptr, location);
|
||||
KJ_DEFER(mutex->unlock(_::Mutex::EXCLUSIVE));
|
||||
value = T();
|
||||
}
|
||||
}
|
||||
|
||||
ExternalMutexGuarded(ExternalMutexGuarded&& other)
|
||||
: mutex(other.mutex), value(kj::mv(other.value)), location(other.location) {
|
||||
other.mutex = nullptr;
|
||||
}
|
||||
ExternalMutexGuarded& operator=(ExternalMutexGuarded&& other) {
|
||||
mutex = other.mutex;
|
||||
value = kj::mv(other.value);
|
||||
location = other.location;
|
||||
other.mutex = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void set(Locked<U>& lock, T&& newValue) {
|
||||
KJ_IREQUIRE(mutex == nullptr);
|
||||
mutex = lock.mutex;
|
||||
value = kj::mv(newValue);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
T& get(Locked<U>& lock) {
|
||||
KJ_IREQUIRE(lock.mutex == mutex);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
const T& get(Locked<const U>& lock) const {
|
||||
KJ_IREQUIRE(lock.mutex == mutex);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
T release(Locked<U>& lock) {
|
||||
// Release (move away) the value. This allows the destructor to skip locking the mutex.
|
||||
KJ_IREQUIRE(lock.mutex == mutex);
|
||||
T result = kj::mv(value);
|
||||
mutex = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
_::Mutex* mutex = nullptr;
|
||||
T value;
|
||||
KJ_NO_UNIQUE_ADDRESS LockSourceLocation location;
|
||||
// When built against C++20 (or clang >= 9.0), the overhead of this is elided. Otherwise this
|
||||
// struct will be 1 byte larger than it would otherwise be.
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Lazy {
|
||||
// A lazily-initialized value.
|
||||
|
||||
public:
|
||||
template <typename Func>
|
||||
T& get(Func&& init, LockSourceLocationArg location = {});
|
||||
template <typename Func>
|
||||
const T& get(Func&& init, LockSourceLocationArg location = {}) 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(LockSourceLocationArg location)
|
||||
const {
|
||||
mutex.lock(_::Mutex::EXCLUSIVE, nullptr, location);
|
||||
return Locked<T>(mutex, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Locked<const T> MutexGuarded<T>::lockShared(LockSourceLocationArg location) const {
|
||||
mutex.lock(_::Mutex::SHARED, nullptr, location);
|
||||
return Locked<const T>(mutex, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Maybe<Locked<T>> MutexGuarded<T>::lockExclusiveWithTimeout(Duration timeout,
|
||||
LockSourceLocationArg location) const {
|
||||
if (mutex.lock(_::Mutex::EXCLUSIVE, timeout, location)) {
|
||||
return Locked<T>(mutex, value);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Maybe<Locked<const T>> MutexGuarded<T>::lockSharedWithTimeout(Duration timeout,
|
||||
LockSourceLocationArg location) const {
|
||||
if (mutex.lock(_::Mutex::SHARED, timeout, location)) {
|
||||
return Locked<const T>(mutex, value);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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, LockSourceLocationArg location) {
|
||||
if (!once.isInitialized()) {
|
||||
InitImpl<Func> initImpl(*this, kj::fwd<Func>(init));
|
||||
once.runOnce(initImpl, location);
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Func>
|
||||
inline const T& Lazy<T>::get(Func&& init, LockSourceLocationArg location) const {
|
||||
if (!once.isInitialized()) {
|
||||
InitImpl<Func> initImpl(*this, kj::fwd<Func>(init));
|
||||
once.runOnce(initImpl, location);
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
#if KJ_TRACK_LOCK_BLOCKING
|
||||
struct BlockedOnMutexAcquisition {
|
||||
const _::Mutex& mutex;
|
||||
// The mutex we are blocked on.
|
||||
|
||||
const SourceLocation& origin;
|
||||
// Where did the blocking operation originate from.
|
||||
};
|
||||
|
||||
struct BlockedOnCondVarWait {
|
||||
const _::Mutex& mutex;
|
||||
// The mutex the condition variable is using (may or may not be locked).
|
||||
|
||||
const void* waiter;
|
||||
// Pointer to the waiter that's being waited on.
|
||||
|
||||
const SourceLocation& origin;
|
||||
// Where did the blocking operation originate from.
|
||||
};
|
||||
|
||||
struct BlockedOnOnceInit {
|
||||
const _::Once& once;
|
||||
|
||||
const SourceLocation& origin;
|
||||
// Where did the blocking operation originate from.
|
||||
};
|
||||
|
||||
using BlockedOnReason = OneOf<BlockedOnMutexAcquisition, BlockedOnCondVarWait, BlockedOnOnceInit>;
|
||||
|
||||
Maybe<const BlockedOnReason&> blockedReason() noexcept;
|
||||
// Returns the information about the reason the current thread is blocked synchronously on KJ
|
||||
// lock primitives. Returns nullptr if the current thread is not currently blocked on such
|
||||
// primitives. This is intended to be called from a signal handler to check whether the current
|
||||
// thread is blocked. Outside of a signal handler there is little value to this function. In those
|
||||
// cases by definition the thread is not blocked. This includes the callable used as part of a
|
||||
// condition variable since that happens after the lock is acquired & the current thread is no
|
||||
// longer blocked). The utility could be made useful for non-signal handler use-cases by being able
|
||||
// to fetch the pointer to the TLS variable directly (i.e. const BlockedOnReason&*). However, there
|
||||
// would have to be additional changes/complexity to try make that work since you'd need
|
||||
// synchronization to ensure that the memory you'd try to reference is still valid. The likely
|
||||
// solution would be to make these mutually exclusive options where you can use either the fast
|
||||
// async-safe option, or a mutex-guarded TLS variable you can get a reference to that isn't
|
||||
// async-safe. That being said, maybe someone can come up with a way to make something that works
|
||||
// in both use-cases which would of course be more preferable.
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
656
vendor/capnproto/src/kj/one-of.h
vendored
Normal file
656
vendor/capnproto/src/kj/one-of.h
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <uint i, template<uint> class Fail, typename Key, typename... Variants>
|
||||
struct TypeIndex_;
|
||||
template <uint i, template<uint> class Fail, typename Key, typename First, typename... Rest>
|
||||
struct TypeIndex_<i, Fail, Key, First, Rest...> {
|
||||
static constexpr uint value = TypeIndex_<i + 1, Fail, Key, Rest...>::value;
|
||||
};
|
||||
template <uint i, template<uint> class Fail, typename Key, typename... Rest>
|
||||
struct TypeIndex_<i, Fail, Key, Key, Rest...> { static constexpr uint value = i; };
|
||||
template <uint i, template<uint> class Fail, typename Key>
|
||||
struct TypeIndex_<i, Fail, Key>: public Fail<i> {};
|
||||
|
||||
template <uint i>
|
||||
struct OneOfFailError_ {
|
||||
static_assert(i == -1, "type does not match any in OneOf");
|
||||
};
|
||||
template <uint i>
|
||||
struct OneOfFailZero_ {
|
||||
static constexpr int value = 0;
|
||||
};
|
||||
|
||||
template <uint i>
|
||||
struct SuccessIfNotZero {
|
||||
typedef int Success;
|
||||
};
|
||||
template <>
|
||||
struct SuccessIfNotZero<0> {};
|
||||
|
||||
enum class Variants0 {};
|
||||
enum class Variants1 { _variant0 };
|
||||
enum class Variants2 { _variant0, _variant1 };
|
||||
enum class Variants3 { _variant0, _variant1, _variant2 };
|
||||
enum class Variants4 { _variant0, _variant1, _variant2, _variant3 };
|
||||
enum class Variants5 { _variant0, _variant1, _variant2, _variant3, _variant4 };
|
||||
enum class Variants6 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5 };
|
||||
enum class Variants7 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6 };
|
||||
enum class Variants8 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7 };
|
||||
enum class Variants9 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8 };
|
||||
enum class Variants10 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9 };
|
||||
enum class Variants11 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10 };
|
||||
enum class Variants12 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11 };
|
||||
enum class Variants13 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12 };
|
||||
enum class Variants14 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13 };
|
||||
enum class Variants15 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14 };
|
||||
enum class Variants16 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15 };
|
||||
enum class Variants17 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16 };
|
||||
enum class Variants18 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17 };
|
||||
enum class Variants19 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18 };
|
||||
enum class Variants20 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19 };
|
||||
enum class Variants21 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20 };
|
||||
enum class Variants22 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21 };
|
||||
enum class Variants23 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22 };
|
||||
enum class Variants24 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23 };
|
||||
enum class Variants25 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24 };
|
||||
enum class Variants26 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25 };
|
||||
enum class Variants27 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26 };
|
||||
enum class Variants28 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27 };
|
||||
enum class Variants29 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28 };
|
||||
enum class Variants30 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29 };
|
||||
enum class Variants31 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30 };
|
||||
enum class Variants32 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31 };
|
||||
enum class Variants33 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32 };
|
||||
enum class Variants34 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33 };
|
||||
enum class Variants35 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34 };
|
||||
enum class Variants36 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35 };
|
||||
enum class Variants37 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36 };
|
||||
enum class Variants38 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37 };
|
||||
enum class Variants39 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38 };
|
||||
enum class Variants40 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39 };
|
||||
enum class Variants41 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40 };
|
||||
enum class Variants42 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41 };
|
||||
enum class Variants43 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42 };
|
||||
enum class Variants44 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43 };
|
||||
enum class Variants45 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43, _variant44 };
|
||||
enum class Variants46 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43, _variant44, _variant45 };
|
||||
enum class Variants47 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43, _variant44, _variant45, _variant46 };
|
||||
enum class Variants48 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43, _variant44, _variant45, _variant46, _variant47 };
|
||||
enum class Variants49 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43, _variant44, _variant45, _variant46, _variant47, _variant48 };
|
||||
enum class Variants50 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
|
||||
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
|
||||
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
|
||||
_variant19, _variant20, _variant21, _variant22, _variant23, _variant24,
|
||||
_variant25, _variant26, _variant27, _variant28, _variant29, _variant30,
|
||||
_variant31, _variant32, _variant33, _variant34, _variant35, _variant36,
|
||||
_variant37, _variant38, _variant39, _variant40, _variant41, _variant42,
|
||||
_variant43, _variant44, _variant45, _variant46, _variant47, _variant48,
|
||||
_variant49 };
|
||||
|
||||
template <uint i> struct Variants_;
|
||||
template <> struct Variants_<0> { typedef Variants0 Type; };
|
||||
template <> struct Variants_<1> { typedef Variants1 Type; };
|
||||
template <> struct Variants_<2> { typedef Variants2 Type; };
|
||||
template <> struct Variants_<3> { typedef Variants3 Type; };
|
||||
template <> struct Variants_<4> { typedef Variants4 Type; };
|
||||
template <> struct Variants_<5> { typedef Variants5 Type; };
|
||||
template <> struct Variants_<6> { typedef Variants6 Type; };
|
||||
template <> struct Variants_<7> { typedef Variants7 Type; };
|
||||
template <> struct Variants_<8> { typedef Variants8 Type; };
|
||||
template <> struct Variants_<9> { typedef Variants9 Type; };
|
||||
template <> struct Variants_<10> { typedef Variants10 Type; };
|
||||
template <> struct Variants_<11> { typedef Variants11 Type; };
|
||||
template <> struct Variants_<12> { typedef Variants12 Type; };
|
||||
template <> struct Variants_<13> { typedef Variants13 Type; };
|
||||
template <> struct Variants_<14> { typedef Variants14 Type; };
|
||||
template <> struct Variants_<15> { typedef Variants15 Type; };
|
||||
template <> struct Variants_<16> { typedef Variants16 Type; };
|
||||
template <> struct Variants_<17> { typedef Variants17 Type; };
|
||||
template <> struct Variants_<18> { typedef Variants18 Type; };
|
||||
template <> struct Variants_<19> { typedef Variants19 Type; };
|
||||
template <> struct Variants_<20> { typedef Variants20 Type; };
|
||||
template <> struct Variants_<21> { typedef Variants21 Type; };
|
||||
template <> struct Variants_<22> { typedef Variants22 Type; };
|
||||
template <> struct Variants_<23> { typedef Variants23 Type; };
|
||||
template <> struct Variants_<24> { typedef Variants24 Type; };
|
||||
template <> struct Variants_<25> { typedef Variants25 Type; };
|
||||
template <> struct Variants_<26> { typedef Variants26 Type; };
|
||||
template <> struct Variants_<27> { typedef Variants27 Type; };
|
||||
template <> struct Variants_<28> { typedef Variants28 Type; };
|
||||
template <> struct Variants_<29> { typedef Variants29 Type; };
|
||||
template <> struct Variants_<30> { typedef Variants30 Type; };
|
||||
template <> struct Variants_<31> { typedef Variants31 Type; };
|
||||
template <> struct Variants_<32> { typedef Variants32 Type; };
|
||||
template <> struct Variants_<33> { typedef Variants33 Type; };
|
||||
template <> struct Variants_<34> { typedef Variants34 Type; };
|
||||
template <> struct Variants_<35> { typedef Variants35 Type; };
|
||||
template <> struct Variants_<36> { typedef Variants36 Type; };
|
||||
template <> struct Variants_<37> { typedef Variants37 Type; };
|
||||
template <> struct Variants_<38> { typedef Variants38 Type; };
|
||||
template <> struct Variants_<39> { typedef Variants39 Type; };
|
||||
template <> struct Variants_<40> { typedef Variants40 Type; };
|
||||
template <> struct Variants_<41> { typedef Variants41 Type; };
|
||||
template <> struct Variants_<42> { typedef Variants42 Type; };
|
||||
template <> struct Variants_<43> { typedef Variants43 Type; };
|
||||
template <> struct Variants_<44> { typedef Variants44 Type; };
|
||||
template <> struct Variants_<45> { typedef Variants45 Type; };
|
||||
template <> struct Variants_<46> { typedef Variants46 Type; };
|
||||
template <> struct Variants_<47> { typedef Variants47 Type; };
|
||||
template <> struct Variants_<48> { typedef Variants48 Type; };
|
||||
template <> struct Variants_<49> { typedef Variants49 Type; };
|
||||
template <> struct Variants_<50> { typedef Variants50 Type; };
|
||||
|
||||
template <uint i>
|
||||
using Variants = typename Variants_<i>::Type;
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename... Variants>
|
||||
class OneOf {
|
||||
template <typename Key>
|
||||
static inline constexpr uint typeIndex() {
|
||||
return _::TypeIndex_<1, _::OneOfFailError_, Key, Variants...>::value;
|
||||
}
|
||||
// Get the 1-based index of Key within the type list Types, or static_assert with a nice error.
|
||||
|
||||
template <typename Key>
|
||||
static inline constexpr uint typeIndexOrZero() {
|
||||
return _::TypeIndex_<1, _::OneOfFailZero_, Key, Variants...>::value;
|
||||
}
|
||||
|
||||
template <uint i, typename... OtherVariants>
|
||||
struct HasAll;
|
||||
// Has a member type called "Success" if and only if all of `OtherVariants` are types that
|
||||
// appear in `Variants`. Used with SFINAE to enable subset constructors.
|
||||
|
||||
public:
|
||||
inline OneOf(): tag(0) {}
|
||||
|
||||
OneOf(const OneOf& other) { copyFrom(other); }
|
||||
OneOf(OneOf& other) { copyFrom(other); }
|
||||
OneOf(OneOf&& other) { moveFrom(other); }
|
||||
// Copy/move from same OneOf type.
|
||||
|
||||
template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success>
|
||||
OneOf(const OneOf<OtherVariants...>& other) { copyFromSubset(other); }
|
||||
template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success>
|
||||
OneOf(OneOf<OtherVariants...>& other) { copyFromSubset(other); }
|
||||
template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success>
|
||||
OneOf(OneOf<OtherVariants...>&& other) { moveFromSubset(other); }
|
||||
// Copy/move from OneOf that contains a subset of the types we do.
|
||||
|
||||
template <typename T, typename = typename HasAll<0, Decay<T>>::Success>
|
||||
OneOf(T&& other): tag(typeIndex<Decay<T>>()) {
|
||||
ctor(*reinterpret_cast<Decay<T>*>(space), kj::fwd<T>(other));
|
||||
}
|
||||
// Copy/move from a value that matches one of the individual types in the OneOf.
|
||||
|
||||
~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>
|
||||
T&& get() && {
|
||||
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
|
||||
return kj::mv(*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>
|
||||
const T&& get() const&& {
|
||||
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
|
||||
return kj::mv(*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);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Maybe<T&> tryGet() {
|
||||
if (is<T>()) {
|
||||
return *reinterpret_cast<T*>(space);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
Maybe<const T&> tryGet() const {
|
||||
if (is<T>()) {
|
||||
return *reinterpret_cast<const T*>(space);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <uint i>
|
||||
KJ_NORETURN(void allHandled());
|
||||
// After a series of if/else blocks handling each variant of the OneOf, have the final else
|
||||
// block call allHandled<n>() where n is the number of variants. This will fail to compile
|
||||
// if new variants are added in the future.
|
||||
|
||||
typedef _::Variants<sizeof...(Variants)> Tag;
|
||||
|
||||
Tag which() const {
|
||||
KJ_IREQUIRE(tag != 0, "Can't KJ_SWITCH_ONEOF() on uninitialized value.");
|
||||
return static_cast<Tag>(tag - 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static constexpr Tag tagFor() {
|
||||
return static_cast<Tag>(typeIndex<T>() - 1);
|
||||
}
|
||||
|
||||
OneOf* _switchSubject() & { return this; }
|
||||
const OneOf* _switchSubject() const& { return this; }
|
||||
_::NullableValue<OneOf> _switchSubject() && { return kj::mv(*this); }
|
||||
|
||||
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 copyVariantFrom(OneOf& other) {
|
||||
if (other.is<T>()) {
|
||||
ctor(*reinterpret_cast<T*>(space), other.get<T>());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void copyFrom(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)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... OtherVariants>
|
||||
inline bool copySubsetVariantFrom(const OneOf<OtherVariants...>& other) {
|
||||
if (other.template is<T>()) {
|
||||
tag = typeIndex<Decay<T>>();
|
||||
ctor(*reinterpret_cast<T*>(space), other.template get<T>());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <typename... OtherVariants>
|
||||
void copyFromSubset(const OneOf<OtherVariants...>& other) {
|
||||
doAll(copySubsetVariantFrom<OtherVariants>(other)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... OtherVariants>
|
||||
inline bool copySubsetVariantFrom(OneOf<OtherVariants...>& other) {
|
||||
if (other.template is<T>()) {
|
||||
tag = typeIndex<Decay<T>>();
|
||||
ctor(*reinterpret_cast<T*>(space), other.template get<T>());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <typename... OtherVariants>
|
||||
void copyFromSubset(OneOf<OtherVariants...>& other) {
|
||||
doAll(copySubsetVariantFrom<OtherVariants>(other)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... OtherVariants>
|
||||
inline bool moveSubsetVariantFrom(OneOf<OtherVariants...>& other) {
|
||||
if (other.template is<T>()) {
|
||||
tag = typeIndex<Decay<T>>();
|
||||
ctor(*reinterpret_cast<T*>(space), kj::mv(other.template get<T>()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <typename... OtherVariants>
|
||||
void moveFromSubset(OneOf<OtherVariants...>& other) {
|
||||
doAll(moveSubsetVariantFrom<OtherVariants>(other)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Variants>
|
||||
template <uint i, typename First, typename... Rest>
|
||||
struct OneOf<Variants...>::HasAll<i, First, Rest...>
|
||||
: public HasAll<typeIndexOrZero<First>(), Rest...> {};
|
||||
template <typename... Variants>
|
||||
template <uint i>
|
||||
struct OneOf<Variants...>::HasAll<i>: public _::SuccessIfNotZero<i> {};
|
||||
|
||||
template <typename... Variants>
|
||||
template <uint i>
|
||||
void OneOf<Variants...>::allHandled() {
|
||||
// After a series of if/else blocks handling each variant of the OneOf, have the final else
|
||||
// block call allHandled<n>() where n is the number of variants. This will fail to compile
|
||||
// if new variants are added in the future.
|
||||
|
||||
static_assert(i == sizeof...(Variants), "new OneOf variants need to be handled here");
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
#if KJ_CPP_STD > 201402L
|
||||
#define KJ_SWITCH_ONEOF(value) \
|
||||
switch (auto _kj_switch_subject = (value)._switchSubject(); _kj_switch_subject->which())
|
||||
#else
|
||||
#define KJ_SWITCH_ONEOF(value) \
|
||||
/* Without C++17, we can only support one switch per containing block. Deal with it. */ \
|
||||
auto _kj_switch_subject = (value)._switchSubject(); \
|
||||
switch (_kj_switch_subject->which())
|
||||
#endif
|
||||
#define KJ_CASE_ONEOF(name, ...) \
|
||||
break; \
|
||||
case ::kj::Decay<decltype(*_kj_switch_subject)>::template tagFor<__VA_ARGS__>(): \
|
||||
for (auto& name = _kj_switch_subject->template get<__VA_ARGS__>(), *_kj_switch_done = &name; \
|
||||
_kj_switch_done; _kj_switch_done = nullptr)
|
||||
#define KJ_CASE_ONEOF_DEFAULT break; default:
|
||||
// Allows switching over a OneOf.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// kj::OneOf<int, float, const char*> variant;
|
||||
// KJ_SWITCH_ONEOF(variant) {
|
||||
// KJ_CASE_ONEOF(i, int) {
|
||||
// doSomethingWithInt(i);
|
||||
// }
|
||||
// KJ_CASE_ONEOF(s, const char*) {
|
||||
// doSomethingWithString(s);
|
||||
// }
|
||||
// KJ_CASE_ONEOF_DEFAULT {
|
||||
// doSomethingElse();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Notes:
|
||||
// - If you don't handle all possible types and don't include a default branch, you'll get a
|
||||
// compiler warning, just like a regular switch() over an enum where one of the enum values is
|
||||
// missing.
|
||||
// - There's no need for a `break` statement in a KJ_CASE_ONEOF; it is implied.
|
||||
// - Under C++11 and C++14, only one KJ_SWITCH_ONEOF() can appear in a block. Wrap the switch in
|
||||
// a pair of braces if you need a second switch in the same block. If C++17 is enabled, this is
|
||||
// not an issue.
|
||||
//
|
||||
// Implementation notes:
|
||||
// - The use of __VA_ARGS__ is to account for template types that have commas separating type
|
||||
// parameters, since macros don't recognize <> as grouping.
|
||||
// - _kj_switch_done is really used as a boolean flag to prevent the for() loop from actually
|
||||
// looping, but it's defined as a pointer since that's all we can define in this context.
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
71
vendor/capnproto/src/kj/parse/char.c++
vendored
Normal file
71
vendor/capnproto/src/kj/parse/char.c++
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 "char.h"
|
||||
#include "../debug.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace kj {
|
||||
namespace parse {
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
double ParseFloat::operator()(const Array<char>& digits,
|
||||
const Maybe<Array<char>>& fraction,
|
||||
const Maybe<Tuple<Maybe<char>, Array<char>>>& exponent) const {
|
||||
size_t bufSize = digits.size();
|
||||
KJ_IF_MAYBE(f, fraction) {
|
||||
bufSize += 1 + f->size();
|
||||
}
|
||||
KJ_IF_MAYBE(e, exponent) {
|
||||
bufSize += 1 + (get<0>(*e) != nullptr) + get<1>(*e).size();
|
||||
}
|
||||
|
||||
KJ_STACK_ARRAY(char, buf, bufSize + 1, 128, 128);
|
||||
|
||||
char* pos = buf.begin();
|
||||
memcpy(pos, digits.begin(), digits.size());
|
||||
pos += digits.size();
|
||||
KJ_IF_MAYBE(f, fraction) {
|
||||
*pos++ = '.';
|
||||
memcpy(pos, f->begin(), f->size());
|
||||
pos += f->size();
|
||||
}
|
||||
KJ_IF_MAYBE(e, exponent) {
|
||||
*pos++ = 'e';
|
||||
KJ_IF_MAYBE(sign, get<0>(*e)) {
|
||||
*pos++ = *sign;
|
||||
}
|
||||
memcpy(pos, get<1>(*e).begin(), get<1>(*e).size());
|
||||
pos += get<1>(*e).size();
|
||||
}
|
||||
|
||||
*pos++ = '\0';
|
||||
KJ_DASSERT(pos == buf.end());
|
||||
|
||||
// The above construction should always produce a valid double, so this should never throw...
|
||||
return StringPtr(buf.begin(), bufSize).parseAs<double>();
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
} // namespace parse
|
||||
} // namespace kj
|
||||
358
vendor/capnproto/src/kj/parse/char.h
vendored
Normal file
358
vendor/capnproto/src/kj/parse/char.h
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "../string.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline bool containsAll(ArrayPtr<const char> text) const {
|
||||
for (char c: text) {
|
||||
if (!contains(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
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 {
|
||||
if (rest.size() == 0) return heapString(&first, 1);
|
||||
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
|
||||
|
||||
KJ_END_HEADER
|
||||
799
vendor/capnproto/src/kj/parse/common.h
vendored
Normal file
799
vendor/capnproto/src/kj/parse/common.h
vendored
Normal file
@@ -0,0 +1,799 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../memory.h"
|
||||
#include "../array.h"
|
||||
#include "../tuple.h"
|
||||
#include "../vector.h"
|
||||
|
||||
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
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_AND_MOVE(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_<
|
||||
decltype(instance<Parser&>()(instance<Input&>()))
|
||||
>::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 {
|
||||
static constexpr WrapperImpl<ParserImpl> instance = WrapperImpl<ParserImpl>();
|
||||
};
|
||||
|
||||
const void* parser;
|
||||
const Wrapper* wrapper;
|
||||
};
|
||||
|
||||
template <typename Input, typename Output>
|
||||
template <typename ParserImpl>
|
||||
constexpr typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl>
|
||||
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance;
|
||||
|
||||
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
|
||||
-> Maybe<decltype(tuple(
|
||||
instance<OutputType<FirstSubParser, Input>>(),
|
||||
instance<OutputType<SubParsers, Input>>()...))>
|
||||
{
|
||||
return parseNext(input);
|
||||
}
|
||||
|
||||
template <typename Input, typename... InitialParams>
|
||||
auto parseNext(Input& input, InitialParams&&... initialParams) const
|
||||
-> Maybe<decltype(tuple(
|
||||
kj::fwd<InitialParams>(initialParams)...,
|
||||
instance<OutputType<FirstSubParser, Input>>(),
|
||||
instance<OutputType<SubParsers, Input>>()...))>
|
||||
{
|
||||
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
|
||||
|
||||
KJ_END_HEADER
|
||||
72
vendor/capnproto/src/kj/refcount.c++
vendored
Normal file
72
vendor/capnproto/src/kj/refcount.c++
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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 "refcount.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
namespace kj {
|
||||
|
||||
// =======================================================================================
|
||||
// Non-atomic (thread-unsafe) refcounting
|
||||
|
||||
Refcounted::~Refcounted() noexcept(false) {
|
||||
KJ_ASSERT(refcount == 0, "Refcounted object deleted with non-zero refcount.");
|
||||
}
|
||||
|
||||
void Refcounted::disposeImpl(void* pointer) const {
|
||||
if (--refcount == 0) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Atomic (thread-safe) refcounting
|
||||
|
||||
AtomicRefcounted::~AtomicRefcounted() noexcept(false) {
|
||||
KJ_ASSERT(refcount == 0, "Refcounted object deleted with non-zero refcount.");
|
||||
}
|
||||
|
||||
void AtomicRefcounted::disposeImpl(void* pointer) const {
|
||||
if (__atomic_sub_fetch(&refcount, 1, __ATOMIC_RELEASE) == 0) {
|
||||
__atomic_thread_fence(__ATOMIC_ACQUIRE);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
bool AtomicRefcounted::addRefWeakInternal() const {
|
||||
uint orig = __atomic_load_n(&refcount, __ATOMIC_RELAXED);
|
||||
|
||||
for (;;) {
|
||||
if (orig == 0) {
|
||||
// Refcount already hit zero. Destructor is already running so we can't revive the object.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__atomic_compare_exchange_n(&refcount, &orig, orig + 1, true,
|
||||
__ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
// Successfully incremented refcount without letting it hit zero.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kj
|
||||
253
vendor/capnproto/src/kj/refcount.h
vendored
Normal file
253
vendor/capnproto/src/kj/refcount.h
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
|
||||
KJ_BEGIN_HEADER
|
||||
|
||||
namespace kj {
|
||||
|
||||
// =======================================================================================
|
||||
// Non-atomic (thread-unsafe) refcounting
|
||||
|
||||
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:
|
||||
Refcounted() = default;
|
||||
virtual ~Refcounted() noexcept(false);
|
||||
KJ_DISALLOW_COPY_AND_MOVE(Refcounted);
|
||||
|
||||
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>
|
||||
friend class RefcountedWrapper;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class RefcountedWrapper: public Refcounted {
|
||||
// Adds refcounting as a wrapper around an existing type, allowing you to construct references
|
||||
// with type Own<T> that appears to point directly to the underlying object.
|
||||
|
||||
public:
|
||||
template <typename... Params>
|
||||
RefcountedWrapper(Params&&... params): wrapped(kj::fwd<Params>(params)...) {}
|
||||
|
||||
T& getWrapped() { return wrapped; }
|
||||
const T& getWrapped() const { return wrapped; }
|
||||
|
||||
Own<T> addWrappedRef() {
|
||||
// Return an owned reference to the wrapped object that is backed by a refcount.
|
||||
++refcount;
|
||||
return Own<T>(&wrapped, *this);
|
||||
}
|
||||
|
||||
private:
|
||||
T wrapped;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RefcountedWrapper<Own<T>>: public Refcounted {
|
||||
// Specialization for when the wrapped type is itself Own<T>. We don't want this to result in
|
||||
// Own<Own<T>>.
|
||||
|
||||
public:
|
||||
RefcountedWrapper(Own<T> wrapped): wrapped(kj::mv(wrapped)) {}
|
||||
|
||||
T& getWrapped() { return *wrapped; }
|
||||
const T& getWrapped() const { return *wrapped; }
|
||||
|
||||
Own<T> addWrappedRef() {
|
||||
// Return an owned reference to the wrapped object that is backed by a refcount.
|
||||
++refcount;
|
||||
return Own<T>(wrapped.get(), *this);
|
||||
}
|
||||
|
||||
private:
|
||||
Own<T> wrapped;
|
||||
};
|
||||
|
||||
template <typename T, typename... Params>
|
||||
Own<RefcountedWrapper<T>> refcountedWrapper(Params&&... params) {
|
||||
return refcounted<RefcountedWrapper<T>>(kj::fwd<Params>(params)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Own<RefcountedWrapper<Own<T>>> refcountedWrapper(Own<T>&& wrapped) {
|
||||
return refcounted<RefcountedWrapper<Own<T>>>(kj::mv(wrapped));
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Atomic (thread-safe) refcounting
|
||||
//
|
||||
// Warning: Atomic ops are SLOW.
|
||||
|
||||
|
||||
class AtomicRefcounted: private kj::Disposer {
|
||||
public:
|
||||
AtomicRefcounted() = default;
|
||||
virtual ~AtomicRefcounted() noexcept(false);
|
||||
KJ_DISALLOW_COPY_AND_MOVE(AtomicRefcounted);
|
||||
|
||||
inline bool isShared() const {
|
||||
return __atomic_load_n(&refcount, __ATOMIC_ACQUIRE) > 1;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable volatile uint refcount = 0;
|
||||
|
||||
bool addRefWeakInternal() const;
|
||||
|
||||
void disposeImpl(void* pointer) const override;
|
||||
template <typename T>
|
||||
static kj::Own<T> addRefInternal(T* object);
|
||||
template <typename T>
|
||||
static kj::Own<const T> addRefInternal(const T* object);
|
||||
|
||||
template <typename T>
|
||||
friend kj::Own<T> atomicAddRef(T& object);
|
||||
template <typename T>
|
||||
friend kj::Own<const T> atomicAddRef(const T& object);
|
||||
template <typename T>
|
||||
friend kj::Maybe<kj::Own<const T>> atomicAddRefWeak(const T& object);
|
||||
template <typename T, typename... Params>
|
||||
friend kj::Own<T> atomicRefcounted(Params&&... params);
|
||||
};
|
||||
|
||||
template <typename T, typename... Params>
|
||||
inline kj::Own<T> atomicRefcounted(Params&&... params) {
|
||||
return AtomicRefcounted::addRefInternal(new T(kj::fwd<Params>(params)...));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
kj::Own<T> atomicAddRef(T& object) {
|
||||
KJ_IREQUIRE(object.AtomicRefcounted::refcount > 0,
|
||||
"Object not allocated with kj::atomicRefcounted().");
|
||||
return AtomicRefcounted::addRefInternal(&object);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
kj::Own<const T> atomicAddRef(const T& object) {
|
||||
KJ_IREQUIRE(object.AtomicRefcounted::refcount > 0,
|
||||
"Object not allocated with kj::atomicRefcounted().");
|
||||
return AtomicRefcounted::addRefInternal(&object);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
kj::Maybe<kj::Own<const T>> atomicAddRefWeak(const T& object) {
|
||||
// Try to addref an object whose refcount could have already reached zero in another thread, and
|
||||
// whose destructor could therefore already have started executing. The destructor must contain
|
||||
// some synchronization that guarantees that said destructor has not yet completed when
|
||||
// attomicAddRefWeak() is called (so that the object is still valid). Since the destructor cannot
|
||||
// be canceled once it has started, in the case that it has already started, this function
|
||||
// returns nullptr.
|
||||
|
||||
const AtomicRefcounted* refcounted = &object;
|
||||
if (refcounted->addRefWeakInternal()) {
|
||||
return kj::Own<const T>(&object, *refcounted);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
kj::Own<T> AtomicRefcounted::addRefInternal(T* object) {
|
||||
AtomicRefcounted* refcounted = object;
|
||||
__atomic_add_fetch(&refcounted->refcount, 1, __ATOMIC_RELAXED);
|
||||
return kj::Own<T>(object, *refcounted);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
kj::Own<const T> AtomicRefcounted::addRefInternal(const T* object) {
|
||||
const AtomicRefcounted* refcounted = object;
|
||||
__atomic_add_fetch(&refcounted->refcount, 1, __ATOMIC_RELAXED);
|
||||
return kj::Own<const T>(object, *refcounted);
|
||||
}
|
||||
|
||||
} // namespace kj
|
||||
|
||||
KJ_END_HEADER
|
||||
28
vendor/capnproto/src/kj/source-location.c++
vendored
Normal file
28
vendor/capnproto/src/kj/source-location.c++
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2021 Cloudflare, 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 "source-location.h"
|
||||
|
||||
namespace kj {
|
||||
kj::String KJ_STRINGIFY(const SourceLocation& l) {
|
||||
return kj::str(l.fileName, ":", l.lineNumber, ":", l.columnNumber, " in ", l.function);
|
||||
}
|
||||
} // namespace kj
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user