Vendor minimal capnproto

This commit is contained in:
Adeeb Shihadeh
2026-09-21 20:05:46 -07:00
parent 384040e094
commit caee18aeef
114 changed files with 67532 additions and 290 deletions

View File

@@ -23,7 +23,6 @@ jobs:
CIBW_ARCHS: ${{ matrix.arch }} CIBW_ARCHS: ${{ matrix.arch }}
CIBW_TEST_REQUIRES: pytest CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: python -m pytest {project}/test CIBW_TEST_COMMAND: python -m pytest {project}/test
CIBW_CONFIG_SETTINGS: force-bundled-libcapnp=true
CMAKE_OSX_ARCHITECTURES: "${{ runner.os == 'macOS' && matrix.arch || '' }}" CMAKE_OSX_ARCHITECTURES: "${{ runner.os == 'macOS' && matrix.arch || '' }}"
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v6
with: with:

6
.gitignore vendored
View File

@@ -8,8 +8,6 @@
*.egg-info *.egg-info
dist dist
build build
build32
build64
eggs eggs
parts parts
bin bin
@@ -47,7 +45,6 @@ docs/_build
capnp/lib/capnp.cpp capnp/lib/capnp.cpp
capnp/lib/capnp.h capnp/lib/capnp.h
capnp/lib/capnp_api.h capnp/lib/capnp_api.h
bundled/
example example
*.iml *.iml
@@ -57,3 +54,6 @@ example
.venv/ .venv/
.pytest_cache/ .pytest_cache/
.ruff_cache/ .ruff_cache/
# Source for the trimmed bootstrap schema.
!vendor/capnproto/src/**/*.capnp

View File

@@ -1,6 +1,5 @@
include README.md LICENSE.md include README.md LICENSE.md
include buildutils/*.py recursive-include vendor/capnproto *.h *.c++ *.txt *.md *.capnp
include _custom_build/*.py
recursive-include capnp *.py *.pyx *.pxd *.h *.cpp recursive-include capnp *.py *.pyx *.pxd *.h *.cpp
recursive-include test *.py *.capnp *.binary *.txt recursive-include test *.py *.capnp *.binary *.txt
exclude capnp/lib/capnp.cpp capnp/lib/capnp.h capnp/lib/capnp_api.h exclude capnp/lib/capnp.cpp capnp/lib/capnp.h capnp/lib/capnp_api.h

View File

@@ -34,22 +34,23 @@ not be installed alongside another distribution providing `capnp`.
## Build and test ## Build and test
Targets: CPython 3.12, Linux x86_64/aarch64, and macOS arm64. A C++14 compiler and Targets: CPython 3.12, Linux x86_64/aarch64, and macOS arm64. A C++17 compiler and
CMake are required for a bundled build. CMake are required to build the vendored library.
```sh ```sh
uv venv --python 3.12 uv venv --python 3.12
uv pip install cython setuptools wheel pkgconfig pytest build uv pip install cython setuptools wheel pytest build
.venv/bin/python setup.py build_ext --inplace --force-bundled-libcapnp .venv/bin/python setup.py build_ext --inplace
.venv/bin/python -m pytest .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 The C++ library is vendored under [`vendor/capnproto`](vendor/capnproto), based on
`capnpc`, `capnp`, and `kj`; it does not link `capnp-rpc` or `kj-async`. `capnpc` is Cap'n Proto 1.4.0. Every build compiles and statically links this copy; it does not
needed for runtime schema parsing. Owning/vendoring the C++ source itself is a search for a system Cap'n Proto installation or download sources. The source
separate step. To use a system installation, pass `--force-system-libcapnp` to archive includes the vendored files, and wheels contain the compiled library.
`build_ext` (or `-Cforce-system-libcapnp=true` to the wheel build). 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, The retained upstream tests cover message construction, schema loading,
reflection, binary fixtures, serialization, and exceptions. Added lifetime tests reflection, binary fixtures, serialization, and exceptions. Added lifetime tests

View File

@@ -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

View File

View File

@@ -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))

View File

@@ -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)

View File

@@ -136,7 +136,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
bint has(char *) except +reraise_kj_exception bint has(char *) except +reraise_kj_exception
StructSchema getSchema() StructSchema getSchema()
Maybe[StructSchema.Field] which() Maybe[StructSchema.Field] which()
MessageSize totalSize() MessageSize totalSize() except +reraise_kj_exception
cdef cppclass DynamicStruct_Builder" ::capnp::DynamicStruct::Builder" nogil: cdef cppclass DynamicStruct_Builder" ::capnp::DynamicStruct::Builder" nogil:
# Need to flatten this class out, since nested C++ classes cause havoc with cython fused types # 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() StructSchema getSchema()
Maybe[StructSchema.Field] which() Maybe[StructSchema.Field] which()
DynamicStruct.Reader asReader() DynamicStruct.Reader asReader()
MessageSize totalSize() MessageSize totalSize() except +reraise_kj_exception
cdef extern from "capnp/dynamic.h" namespace " ::capnp": cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicEnum nogil: 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 ParsedSchema getNested(char * name) except +reraise_kj_exception
cdef cppclass SchemaParser nogil: cdef cppclass SchemaParser nogil:
SchemaParser() SchemaParser()
ParsedSchema parseDiskFile(char * displayName, char * diskPath, ArrayPtr[StringPtr] importPath) ParsedSchema parseDiskFile(char * displayName, char * diskPath, ArrayPtr[StringPtr] importPath) except +reraise_kj_exception

View File

@@ -72,7 +72,7 @@ cdef extern from "capnp/message.h" namespace " ::capnp":
cdef cppclass MessageBuilder nogil: cdef cppclass MessageBuilder nogil:
DynamicStruct_Builder getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception DynamicStruct_Builder getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
DynamicStruct_Builder initRootDynamicStruct 'initRoot< ::capnp::DynamicStruct>'(StructSchema) 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: cdef cppclass MessageReader nogil:
DynamicStruct.Reader getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception DynamicStruct.Reader getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception

View File

@@ -1,6 +1,5 @@
# capnp.pyx # capnp.pyx
# distutils: language = c++ # distutils: language = c++
# distutils: libraries = capnpc capnp kj
# distutils: include_dirs = . # distutils: include_dirs = .
# cython: c_string_type = str # cython: c_string_type = str
# cython: c_string_encoding = default # cython: c_string_encoding = default
@@ -1382,7 +1381,6 @@ cdef class SchemaParser:
self._last_import_array = importArray self._last_import_array = importArray
ret = _ParsedSchema() ret = _ParsedSchema()
# TODO (HaaTa): Convert to parseFromDirectory() as per deprecation note
ret._init_child(self.thisptr.parseDiskFile(displayName, diskPath, importArray.asArrayPtr())) ret._init_child(self.thisptr.parseDiskFile(displayName, diskPath, importArray.asArrayPtr()))
return ret return ret

View File

@@ -1,7 +1,6 @@
[build-system] [build-system]
requires = ["setuptools", "wheel", "pkgconfig", "cython>=3.0"] requires = ["setuptools", "wheel", "cython>=3.0"]
build-backend = "backend" build-backend = "setuptools.build_meta"
backend-path = ["_custom_build"]
[project] [project]
name = "pycapnp" name = "pycapnp"
@@ -18,7 +17,7 @@ dynamic = [
dependencies = [] dependencies = []
[dependency-groups] [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"] test = ["pytest"]
lint = ["ruff==0.16.8"] lint = ["ruff==0.16.8"]
@@ -28,7 +27,7 @@ testpaths = ["test"]
[tool.ruff] [tool.ruff]
target-version = "py312" target-version = "py312"
line-length = 120 line-length = 120
exclude = ["build", "build64", "bundled"] exclude = ["build", "vendor"]
[tool.ruff.lint] [tool.ruff.lint]
select = ["E4", "E7", "E9", "F"] select = ["E4", "E7", "E9", "F"]

View File

@@ -1,3 +0,0 @@
[metadata]
description_file = README.md
license_files = LICENSE.md

110
setup.py
View File

@@ -3,24 +3,17 @@
pycapnp distutils setup.py pycapnp distutils setup.py
""" """
import glob
import os import os
import shutil import shutil
import struct
import sys
import pkgconfig
import setuptools # noqa: F401
from distutils.command.clean import clean as _clean from distutils.command.clean import clean as _clean
from setuptools import setup, Extension from setuptools import setup, Extension
_this_dir = os.path.dirname(__file__) _this_dir = os.path.dirname(__file__)
sys.path.insert(1, _this_dir)
from buildutils.build import build_libcapnp import subprocess
from buildutils.bundle import fetch_libcapnp from pathlib import Path
MAJOR = 2 MAJOR = 2
MINOR = 2 MINOR = 2
@@ -72,10 +65,7 @@ class clean(_clean):
os.path.join("capnp", "lib", "capnp.h"), os.path.join("capnp", "lib", "capnp.h"),
os.path.join("capnp", "version.py"), os.path.join("capnp", "version.py"),
"build", "build",
"build32", ]:
"build64",
"bundled",
] + glob.glob(os.path.join("capnp", "*.capnp")):
print("removing %s" % x) print("removing %s" % x)
try: try:
os.remove(x) os.remove(x)
@@ -91,76 +81,32 @@ class build_libcapnp_ext(build_ext_c):
Build capnproto library Build capnproto library
""" """
user_options = build_ext_c.user_options + [ def run(self):
("force-bundled-libcapnp", None, "Bundle capnp library into the installer"), source = Path(_this_dir, "vendor", "capnproto").resolve()
("force-system-libcapnp", None, "Use system capnp library"), build = Path(self.build_temp, "capnproto").resolve()
("libcapnp-url=", "u", "URL to download libcapnp from (only if bundled)"), args = [
] "cmake",
"-S",
def initialize_options(self): str(source),
build_ext_c.initialize_options(self) "-B",
self.force_bundled_libcapnp = None str(build),
self.force_system_libcapnp = None "-DCMAKE_BUILD_TYPE=Release",
self.libcapnp_url = None ]
if os.environ.get("CMAKE_OSX_ARCHITECTURES"):
def run(self): # noqa: C901 args.append("-DCMAKE_OSX_ARCHITECTURES=" + os.environ["CMAKE_OSX_ARCHITECTURES"])
if self.force_bundled_libcapnp: if os.environ.get("MACOSX_DEPLOYMENT_TARGET"):
need_build = True args.append("-DCMAKE_OSX_DEPLOYMENT_TARGET=" + os.environ["MACOSX_DEPLOYMENT_TARGET"])
elif self.force_system_libcapnp: subprocess.run(args, check=True)
need_build = False subprocess.run(["cmake", "--build", str(build), "--parallel", str(self.parallel or 2)], check=True)
else: archive = str(build / "libcapnp-vendored.a")
# Try to use capnp executable to find include and lib path for extension in self.extensions:
capnp_executable = shutil.which("capnp") extension.include_dirs.insert(0, str(source / "src"))
if capnp_executable: extension.extra_objects = [archive]
capnp_dir = os.path.dirname(capnp_executable) extension.depends = [str(p) for p in source.rglob("*") if p.is_file()] + [archive]
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
return build_ext_c.run(self) 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.Build # noqa: E402
import Cython # noqa: E402 import Cython # noqa: E402
@@ -172,6 +118,7 @@ extensions = [
"capnp/lib/*.pyx", "capnp/lib/*.pyx",
], ],
extra_compile_args=extra_compile_args, extra_compile_args=extra_compile_args,
extra_link_args=["-pthread"],
language="c++", language="c++",
) )
] ]
@@ -203,7 +150,8 @@ setup(
description="A cython wrapping of the C++ Cap'n Proto library", description="A cython wrapping of the C++ Cap'n Proto library",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", 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...) # (setup.py only supports 1 author...)
author="Jacob Alexander", # <- Current maintainer; Original author -> Jason Paryani author="Jacob Alexander", # <- Current maintainer; Original author -> Jason Paryani
author_email="haata@kiibohd.com", author_email="haata@kiibohd.com",

View File

@@ -14,7 +14,3 @@ struct Baz{
struct Qux{ struct Qux{
id @0 :UInt64; id @0 :UInt64;
} }
interface Wrapper {
wrapped @0 (object :AnyPointer);
}

30
test/test_vendor.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

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
View 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

View 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

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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
View 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

View 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

View 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
View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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
View 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
View 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;
}

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

397
vendor/capnproto/src/kj/debug.c++ vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

293
vendor/capnproto/src/kj/function.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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