Merge pull request #38 from LNIS-Projects/dev
architecture fix for tutorial purpose
This commit is contained in:
commit
4c9d380161
|
@ -5,7 +5,7 @@
|
|||
.DS_Store
|
||||
|
||||
# ctags
|
||||
tags
|
||||
vpr7_x2p/vpr/src/tags
|
||||
|
||||
# log files
|
||||
**/logs
|
||||
|
@ -45,4 +45,4 @@ tmp/
|
|||
build/
|
||||
|
||||
message.txt
|
||||
deploy_key
|
||||
deploy_key
|
||||
|
|
|
@ -7,7 +7,7 @@ cache:
|
|||
- $TRAVIS_BUILD_DIR/abc
|
||||
- $TRAVIS_BUILD_DIR/yosys
|
||||
- $TRAVIS_BUILD_DIR/ace2
|
||||
- $TRAVIS_BUILD_DIR/libs
|
||||
# - $TRAVIS_BUILD_DIR/libs
|
||||
- $HOME/.ccache
|
||||
# - $HOME/deps
|
||||
|
||||
|
@ -30,6 +30,7 @@ matrix:
|
|||
sources:
|
||||
- ubuntu-toolchain-r-test # For newer GCC
|
||||
- george-edison55-precise-backports # For cmake
|
||||
- llvm_toolchain-trusty-7
|
||||
packages:
|
||||
- autoconf
|
||||
- automake
|
||||
|
@ -58,11 +59,13 @@ matrix:
|
|||
- libxml++2.6-dev
|
||||
- perl
|
||||
- python
|
||||
- python-lxml
|
||||
- texinfo
|
||||
- time
|
||||
- valgrind
|
||||
- zip
|
||||
- qt5-default
|
||||
- clang-format-7
|
||||
# - os: osx
|
||||
# osx_image: xcode10.2 # we target latest MacOS Mojave
|
||||
# sudo: true
|
||||
|
|
|
@ -34,4 +34,7 @@ python3 openfpga_flow/scripts/run_fpga_task.py tileable_routing --debug --show_t
|
|||
echo -e "Testing Verilog generation with explicit port mapping ";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py explicit_verilog --debug --show_thread_logs
|
||||
|
||||
echo -e "Testing Verilog generation with grid pin duplication ";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py duplicate_grid_pin --debug --show_thread_logs
|
||||
|
||||
end_section "OpenFPGA.TaskTun"
|
||||
|
|
|
@ -23,12 +23,28 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||
endif()
|
||||
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||
message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
|
||||
message("CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}")
|
||||
message(FATAL_ERROR "In-source builds not allowed. Use the Makefile wrapper (e.g. make), or create a new build directory and call cmake manually from there (e.g. mkdir -p build && cd build && cmake .. && make). You may need to 'rm -rf CMakeCache.txt CMakeFiles' first.")
|
||||
endif()
|
||||
|
||||
#We install to the source directory by default
|
||||
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set (CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE PATH "default install path" FORCE)
|
||||
endif()
|
||||
|
||||
#Allow the user to configure how much assertion checking should occur
|
||||
set(VTR_ASSERT_LEVEL "2" CACHE STRING "VTR assertion checking level. 0: no assertions, 1: fast assertions, 2: regular assertions, 3: additional assertions with noticable run-time overhead, 4: all assertions (including those with significant run-time cost)")
|
||||
set_property(CACHE VTR_ASSERT_LEVEL PROPERTY STRINGS 0 1 2 3 4)
|
||||
|
||||
#Create the project
|
||||
project("OPENFPGA" C CXX)
|
||||
|
||||
# Options
|
||||
# Option to enable/disable graphic in compilation
|
||||
option(ENABLE_VPR_GRAPHICS "Enables VPR graphics" ON)
|
||||
option(ENABLE_VPR_GRAPHICS "Enables build with librtlnumber" OFF)
|
||||
|
||||
# Version number
|
||||
set(OPENFPGA_VERSION_MAJOR 1)
|
||||
|
@ -40,6 +56,8 @@ set(OPENFPGA_VERSION_PRERELEASE "dev")
|
|||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||
include(FilesToDirs)
|
||||
|
||||
# Set the assertion level
|
||||
add_definitions("-DVTR_ASSERT_LEVEL=${VTR_ASSERT_LEVEL}")
|
||||
|
||||
# compiler flag configuration checks
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
@ -140,11 +158,12 @@ enable_testing()
|
|||
# Sub-projects
|
||||
#
|
||||
#add_subdirectory(iverilog)
|
||||
add_subdirectory(libs)
|
||||
add_subdirectory(yosys)
|
||||
add_subdirectory(abc)
|
||||
add_subdirectory(ace2)
|
||||
add_subdirectory(libs)
|
||||
add_subdirectory(vpr7_x2p)
|
||||
add_subdirectory(vpr)
|
||||
|
||||
# run make to extract compiler options, linker options and list of source files
|
||||
#add_custom_target(
|
||||
|
@ -182,25 +201,11 @@ set_target_properties(libace ace
|
|||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2")
|
||||
|
||||
# Set output locations to be in the main source tree under the relevant folder
|
||||
set_target_properties(libini
|
||||
set_target_properties(libarchfpgavpr7 read_arch_vpr7
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/external/libini"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/external/libini"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/external/libini")
|
||||
|
||||
# Set output locations to be in the main source tree under the relevant folder
|
||||
set_target_properties(libvtrutil
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil")
|
||||
|
||||
set_target_properties(libarchfpga read_arch
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpga"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpga"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpga")
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpgavpr7"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpgavpr7"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpgavpr7")
|
||||
|
||||
set_target_properties(libpcre pcredemo
|
||||
PROPERTIES
|
||||
|
@ -226,6 +231,8 @@ set_target_properties(libvpr vpr_shell
|
|||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/vpr"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/vpr")
|
||||
|
||||
|
||||
|
||||
|
||||
set_target_properties(libvpr8 vpr8
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr")
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
include(ProcessorCount)
|
||||
ProcessorCount(CPU_COUNT)
|
||||
if(CPU_COUNT EQUAL 0)
|
||||
message(FATAL_ERROR "Unknown number of CPUs!?")
|
||||
endif()
|
||||
|
||||
#
|
||||
# ----------------------- Code format --------------------------
|
||||
#
|
||||
|
||||
set(FIND_TO_FORMAT_CPP
|
||||
-name '*.cpp' -print -o -name '*.h' -print
|
||||
-o -name '*.tpp' -print -o -name '*.hpp' -print)
|
||||
|
||||
list(APPEND DIRS_TO_FORMAT_CPP "")
|
||||
|
||||
#
|
||||
# List the files which will be auto-formatted.
|
||||
#
|
||||
add_custom_target(format-cpp-files
|
||||
COMMAND find ${DIRS_TO_FORMAT_CPP} ${FIND_TO_FORMAT_CPP})
|
||||
|
||||
#
|
||||
# Use clang-format-7 for code format
|
||||
#
|
||||
add_custom_target(format-cpp
|
||||
COMMAND find ${DIRS_TO_FORMAT_CPP} ${FIND_TO_FORMAT_CPP} |
|
||||
xargs -P ${CPU_COUNT} clang-format-7 -style=file -i)
|
||||
|
||||
#
|
||||
# Use simple python script for fixing C like boxed comments
|
||||
#
|
||||
add_custom_target(format-cpp-fix-comments DEPENDS format-cpp
|
||||
COMMAND find ${DIRS_TO_FORMAT_CPP} ${FIND_TO_FORMAT_CPP} |
|
||||
xargs -L 1 -P ${CPU_COUNT}
|
||||
python3 ${PROJECT_SOURCE_DIR}/dev/code_format_fixup.py --inplace --fix-comments --input)
|
||||
|
||||
#
|
||||
# Use simple python script for fixing template brackets e.g. <<>
|
||||
#
|
||||
add_custom_target(format-cpp-fix-template-operators DEPENDS format-cpp
|
||||
COMMAND find ${DIRS_TO_FORMAT_CPP} ${FIND_TO_FORMAT_CPP} |
|
||||
xargs -L 1 -P ${CPU_COUNT}
|
||||
python3 ${PROJECT_SOURCE_DIR}/dev/code_format_fixup.py --inplace --fix-template-operators --input)
|
||||
|
||||
add_custom_target(format DEPENDS format-cpp-fix-comments format-cpp-fix-template-operators)
|
|
@ -0,0 +1,303 @@
|
|||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2015 Justus Calvin
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
#
|
||||
# FindTBB
|
||||
# -------
|
||||
#
|
||||
# Find TBB include directories and libraries.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# find_package(TBB [major[.minor]] [EXACT]
|
||||
# [QUIET] [REQUIRED]
|
||||
# [[COMPONENTS] [components...]]
|
||||
# [OPTIONAL_COMPONENTS components...])
|
||||
#
|
||||
# where the allowed components are tbbmalloc and tbb_preview. Users may modify
|
||||
# the behavior of this module with the following variables:
|
||||
#
|
||||
# * TBB_ROOT_DIR - The base directory the of TBB installation.
|
||||
# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files.
|
||||
# * TBB_LIBRARY - The directory that contains the TBB library files.
|
||||
# * TBB_<library>_LIBRARY - The path of the TBB the corresponding TBB library.
|
||||
# These libraries, if specified, override the
|
||||
# corresponding library search results, where <library>
|
||||
# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug,
|
||||
# tbb_preview, or tbb_preview_debug.
|
||||
# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will
|
||||
# be used instead of the release version.
|
||||
#
|
||||
# Users may modify the behavior of this module with the following environment
|
||||
# variables:
|
||||
#
|
||||
# * TBB_INSTALL_DIR
|
||||
# * TBBROOT
|
||||
# * LIBRARY_PATH
|
||||
#
|
||||
# This module will set the following variables:
|
||||
#
|
||||
# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or
|
||||
# don’t want to use TBB.
|
||||
# * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is
|
||||
# not available.
|
||||
# * TBB_VERSION - The full version string
|
||||
# * TBB_VERSION_MAJOR - The major version
|
||||
# * TBB_VERSION_MINOR - The minor version
|
||||
# * TBB_INTERFACE_VERSION - The interface version number defined in
|
||||
# tbb/tbb_stddef.h.
|
||||
# * TBB_<library>_LIBRARY_RELEASE - The path of the TBB release version of
|
||||
# <library>, where <library> may be tbb, tbb_debug,
|
||||
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
|
||||
# tbb_preview_debug.
|
||||
# * TBB_<library>_LIBRARY_DEGUG - The path of the TBB release version of
|
||||
# <library>, where <library> may be tbb, tbb_debug,
|
||||
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
|
||||
# tbb_preview_debug.
|
||||
#
|
||||
# The following varibles should be used to build and link with TBB:
|
||||
#
|
||||
# * TBB_INCLUDE_DIRS - The include directory for TBB.
|
||||
# * TBB_LIBRARIES - The libraries to link against to use TBB.
|
||||
# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB.
|
||||
# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB.
|
||||
# * TBB_DEFINITIONS - Definitions to use when compiling code that uses
|
||||
# TBB.
|
||||
# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that
|
||||
# uses TBB.
|
||||
# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that
|
||||
# uses TBB.
|
||||
#
|
||||
# This module will also create the "tbb" target that may be used when building
|
||||
# executables and libraries.
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(NOT TBB_FOUND)
|
||||
|
||||
##################################
|
||||
# Check the build type
|
||||
##################################
|
||||
|
||||
if(NOT DEFINED TBB_USE_DEBUG_BUILD)
|
||||
if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug|RelWithDebInfo|RELWITHDEBINFO|relwithdebinfo)")
|
||||
set(TBB_BUILD_TYPE DEBUG)
|
||||
else()
|
||||
set(TBB_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
elseif(TBB_USE_DEBUG_BUILD)
|
||||
set(TBB_BUILD_TYPE DEBUG)
|
||||
else()
|
||||
set(TBB_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
##################################
|
||||
# Set the TBB search directories
|
||||
##################################
|
||||
|
||||
# Define search paths based on user input and environment variables
|
||||
set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
|
||||
|
||||
# Define the search directories based on the current platform
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
|
||||
"C:/Program Files (x86)/Intel/TBB")
|
||||
|
||||
# Set the target architecture
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(TBB_ARCHITECTURE "intel64")
|
||||
else()
|
||||
set(TBB_ARCHITECTURE "ia32")
|
||||
endif()
|
||||
|
||||
# Set the TBB search library path search suffix based on the version of VC
|
||||
if(WINDOWS_STORE)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui")
|
||||
elseif(MSVC14)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14")
|
||||
elseif(MSVC12)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12")
|
||||
elseif(MSVC11)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11")
|
||||
elseif(MSVC10)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10")
|
||||
endif()
|
||||
|
||||
# Add the library path search suffix for the VC independent version of TBB
|
||||
list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt")
|
||||
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
# OS X
|
||||
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||
|
||||
# TODO: Check to see which C++ library is being used by the compiler.
|
||||
if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
|
||||
# The default C++ library on OS X 10.9 and later is libc++
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
|
||||
else()
|
||||
set(TBB_LIB_PATH_SUFFIX "lib")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# Linux
|
||||
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||
|
||||
# TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
|
||||
# <arch>/gcc4.1. For now, assume that the compiler is more recent than
|
||||
# gcc 4.4.x or later.
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
##################################
|
||||
# Find the TBB include dir
|
||||
##################################
|
||||
|
||||
find_path(TBB_INCLUDE_DIRS tbb/tbb.h
|
||||
HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
|
||||
PATHS ${TBB_DEFAULT_SEARCH_DIR}
|
||||
PATH_SUFFIXES include)
|
||||
|
||||
##################################
|
||||
# Set version strings
|
||||
##################################
|
||||
|
||||
if(TBB_INCLUDE_DIRS)
|
||||
file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
|
||||
string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
|
||||
TBB_VERSION_MAJOR "${_tbb_version_file}")
|
||||
string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
|
||||
TBB_VERSION_MINOR "${_tbb_version_file}")
|
||||
string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
|
||||
TBB_INTERFACE_VERSION "${_tbb_version_file}")
|
||||
set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
|
||||
endif()
|
||||
|
||||
##################################
|
||||
# Find TBB components
|
||||
##################################
|
||||
|
||||
if(TBB_VERSION VERSION_LESS 4.3)
|
||||
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
|
||||
else()
|
||||
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
|
||||
endif()
|
||||
|
||||
# Find each component
|
||||
foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
|
||||
if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
|
||||
|
||||
# Search for the libraries
|
||||
find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}
|
||||
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||
|
||||
find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}_debug
|
||||
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||
|
||||
if(TBB_${_comp}_LIBRARY_DEBUG)
|
||||
list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
if(TBB_${_comp}_LIBRARY_RELEASE)
|
||||
list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
|
||||
set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
|
||||
endif()
|
||||
|
||||
if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
|
||||
set(TBB_${_comp}_FOUND TRUE)
|
||||
else()
|
||||
set(TBB_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
# Mark internal variables as advanced
|
||||
mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
|
||||
mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
|
||||
mark_as_advanced(TBB_${_comp}_LIBRARY)
|
||||
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
##################################
|
||||
# Set compile flags and libraries
|
||||
##################################
|
||||
|
||||
set(TBB_DEFINITIONS_RELEASE "")
|
||||
set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
|
||||
|
||||
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}")
|
||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
|
||||
elseif(TBB_LIBRARIES_RELEASE)
|
||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}")
|
||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}")
|
||||
elseif(TBB_LIBRARIES_DEBUG)
|
||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}")
|
||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}")
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(TBB
|
||||
REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
|
||||
HANDLE_COMPONENTS
|
||||
VERSION_VAR TBB_VERSION)
|
||||
|
||||
##################################
|
||||
# Create targets
|
||||
##################################
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
|
||||
add_library(tbb SHARED IMPORTED)
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES})
|
||||
if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
|
||||
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
|
||||
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG}
|
||||
IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
|
||||
IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
|
||||
)
|
||||
elseif(TBB_LIBRARIES_RELEASE)
|
||||
set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE})
|
||||
else()
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}"
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES)
|
||||
|
||||
unset(TBB_ARCHITECTURE)
|
||||
unset(TBB_BUILD_TYPE)
|
||||
unset(TBB_LIB_PATH_SUFFIX)
|
||||
unset(TBB_DEFAULT_SEARCH_DIR)
|
||||
|
||||
endif()
|
|
@ -0,0 +1,27 @@
|
|||
# Support ccache for faster builds.
|
||||
#
|
||||
# By default ccache will be used if it is found in your path.
|
||||
#
|
||||
# You can use `BUILD_USING_CCACHE=off` to disable it's usage.
|
||||
# You can use `BUILD_USING_CCACHE=off` to make sure ccache is used.
|
||||
#
|
||||
set(BUILD_USING_CCACHE "auto")
|
||||
if(NOT "$ENV{BUILD_USING_CCACHE}" STREQUAL "")
|
||||
set(BUILD_USING_CCACHE $ENV{BUILD_USING_CCACHE})
|
||||
endif()
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if (BUILD_USING_CCACHE STREQUAL "on")
|
||||
if(NOT CCACHE_PROGRAM)
|
||||
message(FATAL_ERROR "BUILD_USING_CCACHE set to on but ccache binary not found!")
|
||||
endif()
|
||||
endif()
|
||||
if (NOT BUILD_USING_CCACHE STREQUAL "off")
|
||||
if(CCACHE_PROGRAM)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
message(STATUS "Using ccache binary found @ ${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
else()
|
||||
if(CCACHE_PROGRAM)
|
||||
message(STATUS "**Not** using ccache binary found @ ${CCACHE_PROGRAM} (as BUILD_USING_CCACHE is off)")
|
||||
endif()
|
||||
endif()
|
|
@ -377,12 +377,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="input" prefix="D" lib_name="D" size="1"/>
|
||||
<port type="output" prefix="Q" lib_name="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" lib_name="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/SpiceNetlists/io.sp" verilog_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
#VTR developed libraries
|
||||
#add_subdirectory(libarchfpga)
|
||||
#Externally developed libraries
|
||||
add_subdirectory(EXTERNAL)
|
||||
|
||||
# OpenFPGA-related libraries
|
||||
add_subdirectory(libini)
|
||||
|
||||
# VTR developed libraries
|
||||
# Only add warn flags for VPR internal libraries.
|
||||
#add_compile_options(${WARN_FLAGS})
|
||||
add_subdirectory(libarchfpga)
|
||||
add_subdirectory(libvtrutil)
|
||||
add_subdirectory(liblog)
|
||||
add_subdirectory(external)
|
||||
#add_subdirectory(external)
|
||||
#add_subdirectory(libpugiutil)
|
||||
#add_subdirectory(libeasygl)
|
||||
#add_subdirectory(librtlnumber)
|
||||
add_subdirectory(libpugiutil)
|
||||
add_subdirectory(librtlnumber)
|
||||
if(${VTR_ENABLE_CAPNPROTO})
|
||||
add_subdirectory(libvtrcapnproto)
|
||||
endif()
|
||||
|
||||
#Externally developed libraries
|
||||
#add_subdirectory(EXTERNAL)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#Manually synchronized external libraries
|
||||
add_subdirectory(libpugixml)
|
||||
add_subdirectory(libcatch)
|
||||
|
||||
#External libraries synchronized with 'git subtree'
|
||||
add_subdirectory(libargparse)
|
||||
add_subdirectory(libsdcparse)
|
||||
add_subdirectory(libblifparse)
|
||||
add_subdirectory(libtatum)
|
||||
|
||||
#VPR_USE_EZGL is initialized in the root CMakeLists.
|
||||
#compile libezgl only if the user asks for or has its dependencies installed.
|
||||
if(VPR_USE_EZGL STREQUAL "on")
|
||||
add_subdirectory(libezgl)
|
||||
endif()
|
||||
|
||||
if(${VTR_ENABLE_CAPNPROTO})
|
||||
# Override default policy for capnproto (CMake policy version 3.1)
|
||||
# Enable new IPO variables
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
||||
|
||||
# Enable option overrides via variables
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
# Re-enable CXX extensions for capnproto.
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
|
||||
# Disable capnproto tests
|
||||
set(BUILD_TESTING OFF)
|
||||
|
||||
#Since capnproto is an externally developed library/tool, we suppress all compiler warnings
|
||||
CHECK_CXX_COMPILER_FLAG("-w" CXX_COMPILER_SUPPORTS_-w)
|
||||
if(CXX_COMPILER_SUPPORTS_-w)
|
||||
add_compile_options("-w")
|
||||
endif()
|
||||
|
||||
add_subdirectory(capnproto EXCLUDE_FROM_ALL)
|
||||
|
||||
#Some capnproto kj headers (e.g. filesystem.h) generate warnings, treat them as system headers to suppress warnings
|
||||
#We suppress them here since we include the capnproto sub-tree as is and do not modify its CMakeLists.txts
|
||||
target_include_directories(kj SYSTEM INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/capnproto/c++/src>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
endif()
|
|
@ -0,0 +1,5 @@
|
|||
This folder contains libraries developed outside of the VTR repository.
|
||||
|
||||
Some are simply checked in manually (e.g. libpugixml), while others are kept in sync using the 'git subtree' command (see README.developers.md in the repository root for usage).
|
||||
|
||||
These libraries should NOT BE MODIFIED since they would diverge from their mainline versions, making it difficult to update in the future.
|
|
@ -0,0 +1,76 @@
|
|||
# Kenton's personal backup script.
|
||||
/backup.sh
|
||||
|
||||
# Eclipse-generated stuff.
|
||||
.cproject
|
||||
.project
|
||||
.pydevproject
|
||||
.settings
|
||||
.dist-buildwrapper
|
||||
/c++/gen/
|
||||
|
||||
# Code you may want to map in from elsewhere.
|
||||
/c++/src/base
|
||||
/c++/src/capnp/compilerbin
|
||||
/c++/src/ekam
|
||||
/c++/src/os
|
||||
/c++/src/protobuf
|
||||
/c++/src/snappy
|
||||
/c++/src/samples
|
||||
|
||||
# Ekam build artifacts.
|
||||
/c++/tmp/
|
||||
/c++/bin/
|
||||
|
||||
# setup-ekam.sh
|
||||
/c++/.ekam
|
||||
|
||||
# super-test.sh
|
||||
/tmp-staging
|
||||
|
||||
# Jekyll-generated site
|
||||
/doc/_site
|
||||
|
||||
# Checkout of gh-pages made by /doc/push-site.sh
|
||||
/doc/.gh-pages
|
||||
|
||||
# cabal-install artifacts
|
||||
/compiler/dist/
|
||||
|
||||
# Make artefacts
|
||||
/c++/.libs/
|
||||
/c++/Makefile
|
||||
/c++/Makefile.in
|
||||
/c++/**/*.o
|
||||
/c++/**/*.lo
|
||||
/c++/**/.deps/
|
||||
/c++/**/.dirstamp
|
||||
/c++/stamp-h1
|
||||
/c++/**/*.log
|
||||
/c++/test_capnpc_middleman
|
||||
/c++/**/test*.capnp.*
|
||||
/c++/*.la
|
||||
/c++/**/*.trs
|
||||
/c++/aclocal.m4
|
||||
/c++/autom4te.cache/
|
||||
/c++/build-aux/
|
||||
/c++/capnp
|
||||
/c++/capnp-evolution-test
|
||||
/c++/cmake/CapnProtoConfig.cmake
|
||||
/c++/cmake/CapnProtoConfigVersion.cmake
|
||||
/c++/pkgconfig/*.pc
|
||||
/c++/capnp-test
|
||||
/c++/capnpc-c++
|
||||
/c++/capnpc-capnp
|
||||
/c++/config.*
|
||||
/c++/configure
|
||||
/c++/libtool
|
||||
/c++/m4/libtool.m4
|
||||
/c++/m4/ltoptions.m4
|
||||
/c++/m4/ltsugar.m4
|
||||
/c++/m4/ltversion.m4
|
||||
/c++/m4/lt~obsolete.m4
|
||||
/c++/samples/addressbook
|
||||
|
||||
# editor artefacts
|
||||
*~
|
|
@ -0,0 +1,77 @@
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release-.*/
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- automake
|
||||
- autoconf
|
||||
- libtool
|
||||
- pkg-config
|
||||
# limit parallelism due to limited memory on Travis
|
||||
script: CC=$MATRIX_CC CXX=$MATRIX_CXX ./super-test.sh -j2 quick
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# Old GCC
|
||||
- os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
env:
|
||||
- MATRIX_CC=gcc-4.9
|
||||
- MATRIX_CXX=g++-4.9
|
||||
|
||||
# New GCC
|
||||
- os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-7
|
||||
env:
|
||||
- MATRIX_CC=gcc-7
|
||||
- MATRIX_CXX=g++-7
|
||||
|
||||
# Old Clang
|
||||
- os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-3.6
|
||||
packages:
|
||||
- clang-3.6
|
||||
- libc++-dev # clang-3.6 can't compile C++14 against libstdc++, apparently.
|
||||
env:
|
||||
- MATRIX_CC=clang-3.6
|
||||
- MATRIX_CXX=clang++-3.6
|
||||
|
||||
# New Clang
|
||||
- os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-5.0
|
||||
packages:
|
||||
- clang-5.0
|
||||
env:
|
||||
- MATRIX_CC=clang-5.0
|
||||
- MATRIX_CXX=clang++-5.0
|
||||
|
||||
# Mac. We only test Clang because Mac builds are expensive for Travis and probably any
|
||||
# compiler-specific problems will be caught on the Linux matrix anyway.
|
||||
- os: osx
|
||||
osx_image: xcode9.3
|
||||
env:
|
||||
- MATRIX_CC=clang
|
||||
- MATRIX_CXX=clang++
|
|
@ -0,0 +1,3 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
project("Cap'n Proto Root" CXX)
|
||||
add_subdirectory(c++)
|
|
@ -0,0 +1,23 @@
|
|||
The following people have made large code contributions to this repository.
|
||||
Those contributions are copyright the respective authors and licensed by them
|
||||
under the same MIT license terms as the rest of the library.
|
||||
|
||||
Kenton Varda <kenton@sandstorm.io> <kenton@cloudflare.com>: Primary Author
|
||||
Jason Choy <jjwchoy@gmail.com>: kj/threadlocal.h and other iOS tweaks, `name` annotation in C++ code generator
|
||||
Remy Blank <rblank@google.com> (contributions copyright Google Inc.): KJ Timers
|
||||
Joshua Warner <joshuawarner32@gmail.com>: cmake build, AnyStruct/AnyList, other stuff
|
||||
Scott Purdy <scott@fer.io>: kj/std iostream interface
|
||||
Bryan Borham <bjboreham@gmail.com>: Initial MSVC support
|
||||
Philip Quinn <p@partylemon.com>: cmake build and other assorted bits
|
||||
Brian Taylor <el.wubo@gmail.com>: emacs syntax highlighting
|
||||
Ben Laurie <ben@links.org>: discovered and responsibly disclosed security bugs
|
||||
Kamal Marhubi <kamal@marhubi.com>: JSON parser
|
||||
Oliver Kuckertz <oliver.kuckertz@mologie.de>: FdObserver POLLPRI support
|
||||
Harris Hancock <vortrab@gmail.com>: MSVC support
|
||||
Branislav Katreniak <branislav.katreniak@digitalstrom.com>: JSON decode
|
||||
Matthew Maurer <matthew.r.maurer@gmail.com>: Canonicalization Support
|
||||
David Renshaw <david@sandstorm.io>: bugfixes and miscellaneous maintenance
|
||||
Ingvar Stepanyan <me@rreverser.com> <ingvar@cloudflare.com>: Custom handlers for JSON decode
|
||||
|
||||
This file does not list people who maintain their own Cap'n Proto
|
||||
implementations as separate projects. Those people are awesome too! :)
|
|
@ -0,0 +1,24 @@
|
|||
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.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Unix: [![Unix Build Status](https://travis-ci.org/capnproto/capnproto.svg?branch=master)](https://travis-ci.org/capnproto/capnproto) Windows: [![Windows Build Status](https://ci.appveyor.com/api/projects/status/9rxff2tujkae4hte?svg=true)](https://ci.appveyor.com/project/kentonv/capnproto)
|
||||
|
||||
<img src='http://kentonv.github.com/capnproto/images/infinity-times-faster.png' style='width:334px; height:306px; float: right;'>
|
||||
|
||||
Cap'n Proto is an insanely fast data interchange format and capability-based RPC system. Think
|
||||
JSON, except binary. Or think [Protocol Buffers](https://github.com/google/protobuf), except faster.
|
||||
In fact, in benchmarks, Cap'n Proto is INFINITY TIMES faster than Protocol Buffers.
|
||||
|
||||
[Read more...](http://kentonv.github.com/capnproto/)
|
|
@ -0,0 +1,67 @@
|
|||
How to release
|
||||
==============
|
||||
|
||||
**Developing**
|
||||
|
||||
* First, develop some new features to release! As you do, make sure to keep the documentation
|
||||
up-to-date.
|
||||
|
||||
**Testing**
|
||||
|
||||
* Run `super-test.sh` on as many platforms as you have available. Remember that you can easily run
|
||||
on any machine available through ssh using `./super-test.sh remote [hostname]`. Also run in
|
||||
Clang mode. (If you are Kenton and running from Kenton's home machine and network, use
|
||||
`./mega-test.py mega-test.cfg` to run on all supported compilers and platforms.)
|
||||
|
||||
* Manually test Windows/MSVC -- unfortunately this can't be automated by super-test.sh.
|
||||
|
||||
* Manually run the pointer fuzz tests under Valgrind. This will take 40-80 minutes.
|
||||
|
||||
valgrind ./capnp-test -fcapnp/fuzz-test.c++
|
||||
|
||||
* Manually run the AFL fuzz tests by running `afl-fuzz.sh`. There are three test cases, and ideally each should run for 24 hours or more.
|
||||
|
||||
**Documenting**
|
||||
|
||||
* Write a blog post discussing what is new, placing it in doc/_posts.
|
||||
|
||||
* Run jekyll locally and review the blog post and docs.
|
||||
|
||||
**Releasing**
|
||||
|
||||
* Check out the master branch in a fresh directory. Do NOT use your regular repo, as the release
|
||||
script commits changes and if anything goes wrong you'll probably want to trash the whole thing
|
||||
without pushing. DO NOT git clone the repo from an existing local repo -- check it out directly
|
||||
from github. Otherwise, when it pushes its changes back, they'll only be pushed back to your
|
||||
local repo.
|
||||
|
||||
* Run `./release.sh candidate`. This creates a new release branch, updates the version number to
|
||||
`-rc1`, builds release tarballs, copies them to the current directory, then switches back to the
|
||||
master branch and bumps the version number there. After asking for final confirmation, it will
|
||||
upload the tarball to S3 and push all changes back to github.
|
||||
|
||||
* Install your release candidates on your local machine, as if you were a user.
|
||||
|
||||
* Go to `c++/samples` in the git repo and run `./test.sh`. It will try to build against your
|
||||
installed copy.
|
||||
|
||||
* Post the release candidates somewhere public and then send links to the mailing list for people
|
||||
to test. Wait a bit for bug reports.
|
||||
|
||||
* If there are any problems, fix them in master and start a new release candidate by running
|
||||
`./release.sh candidate <commit>...` from the release branch. This will cherry-pick the specified
|
||||
commits into the release branch and create a new candidate. Repeat until all problems are fixed.
|
||||
Be sure that any such fixes include tests or process changes so that they don't happen again.
|
||||
|
||||
* You should now be ready for an official release. Run `./release.sh final`. This will remove the
|
||||
"-rcN" suffix from the version number, update the version number shown on the downloads page,
|
||||
build the final release package, and -- after final confirmation -- upload the binary, push
|
||||
changes to git, and publish the new documentation.
|
||||
|
||||
* Submit the newly-published blog post to news sites and social media as you see fit.
|
||||
|
||||
* If problems are discovered in the release, fix them in master and run
|
||||
`./release.sh candidate <commit>...` in the release branch to start a new micro release. The
|
||||
script automatically sees that the current branch's version no longer contains `-rc`, so it starts
|
||||
a new branch. Repeat the rest of the process above. If you decide to write a blog post (not
|
||||
always necessary), do it in the master branch and cherry-pick it.
|
|
@ -0,0 +1,78 @@
|
|||
# Cap'n Proto AppVeyor configuration
|
||||
#
|
||||
# See https://www.appveyor.com/docs/appveyor-yml/ for configuration options.
|
||||
#
|
||||
# This script configures AppVeyor to:
|
||||
# - Use CMake to ...
|
||||
# build Cap'n Proto with VS2017.
|
||||
# build Cap'n Proto samples with VS2017.
|
||||
# build Cap'n Proto with MinGW.
|
||||
# build Cap'n Proto with Cygwin.
|
||||
|
||||
version: "{build}"
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release-.*/
|
||||
# Don't build non-master branches (unless they open a pull request).
|
||||
|
||||
image: Visual Studio 2017
|
||||
# AppVeyor build worker image (VM template).
|
||||
|
||||
shallow_clone: true
|
||||
# Fetch repository as zip archive.
|
||||
|
||||
environment:
|
||||
MINGW_DIR: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64
|
||||
BUILD_TYPE: debug
|
||||
|
||||
matrix:
|
||||
# TODO(someday): Add MSVC x64 builds, MinGW x86 build?
|
||||
|
||||
- CMAKE_GENERATOR: Visual Studio 15 2017
|
||||
BUILD_NAME: vs2017
|
||||
EXTRA_BUILD_FLAGS: # /maxcpucount
|
||||
# TODO(someday): Right now /maxcpucount occasionally expresses a filesystem-related race:
|
||||
# capnp-capnpc++ complains that it can't create test.capnp.h.
|
||||
|
||||
- CMAKE_GENERATOR: MinGW Makefiles
|
||||
BUILD_NAME: mingw
|
||||
EXTRA_BUILD_FLAGS: -j2
|
||||
|
||||
- BUILD_NAME: cygwin
|
||||
|
||||
install:
|
||||
- ps: Get-Command sh.exe -All | Remove-Item
|
||||
# CMake refuses to generate MinGW Makefiles if sh.exe is in the PATH
|
||||
|
||||
before_build:
|
||||
- set PATH=%MINGW_DIR%\bin;%PATH%
|
||||
- set BUILD_DIR=build-%BUILD_NAME%
|
||||
- set INSTALL_PREFIX=%CD%\capnproto-c++-%BUILD_NAME%
|
||||
- cmake --version
|
||||
|
||||
build_script:
|
||||
- echo "Building Cap'n Proto with %CMAKE_GENERATOR%"
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" cmake -Hc++ -B%BUILD_DIR% -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX%
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" cmake --build %BUILD_DIR% --config %BUILD_TYPE% --target install -- %EXTRA_BUILD_FLAGS%
|
||||
# MinGW wants the build type at configure-time while MSVC wants the build type at build-time. We
|
||||
# can satisfy both by passing the build type to both cmake invocations. We have to suffer a
|
||||
# warning, but both generators will work.
|
||||
|
||||
- echo "Building Cap'n Proto samples with %CMAKE_GENERATOR%"
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" cmake -Hc++/samples -B%BUILD_DIR%-samples -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_PREFIX_PATH=%INSTALL_PREFIX%
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" cmake --build %BUILD_DIR%-samples --config %BUILD_TYPE%
|
||||
|
||||
# Cygwin build -- use super-test.sh like other Unix builds.
|
||||
# But, we need to install Cygwin's cmake package in order to pass the cmake part of super-test.
|
||||
# Somewhat ridiculously, this requires downloading Cygwin's setup program and running it.
|
||||
- if "%BUILD_NAME%"=="cygwin" appveyor DownloadFile "https://cygwin.com/setup-x86_64.exe" -FileName "C:\cygwin64\setup-x86_64.exe"
|
||||
- if "%BUILD_NAME%"=="cygwin" C:\cygwin64\setup-x86_64.exe --quiet-mode --no-shortcuts --upgrade-also --root "C:\cygwin64" --packages cmake
|
||||
- if "%BUILD_NAME%"=="cygwin" C:\cygwin64\bin\bash -lc 'cd /cygdrive/c/projects/capnproto; ./super-test.sh -j2 quick'
|
||||
|
||||
test_script:
|
||||
# Sleep a little to prevent interleaving test output with build output.
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" timeout /t 2
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" cd %BUILD_DIR%\src
|
||||
- if NOT "%BUILD_NAME%"=="cygwin" ctest -V -C %BUILD_TYPE%
|
|
@ -0,0 +1,155 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
project("Cap'n Proto" CXX)
|
||||
set(VERSION 0.7.0)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include(CheckIncludeFileCXX)
|
||||
include(GNUInstallDirs)
|
||||
if(MSVC)
|
||||
check_include_file_cxx(initializer_list HAS_CXX14)
|
||||
else()
|
||||
check_include_file_cxx(initializer_list HAS_CXX14 "-std=gnu++1y")
|
||||
endif()
|
||||
if(NOT HAS_CXX14)
|
||||
message(SEND_ERROR "Requires a C++14 compiler and standard library.")
|
||||
endif()
|
||||
|
||||
# these arguments are passed to all install(TARGETS) calls
|
||||
set(INSTALL_TARGETS_DEFAULT_ARGS
|
||||
EXPORT CapnProtoTargets
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
|
||||
# Options ======================================================================
|
||||
|
||||
option(BUILD_TESTING "Build unit tests and enable CTest 'check' target." ON)
|
||||
option(EXTERNAL_CAPNP "Use the system capnp binary, or the one specified in $CAPNP, instead of using the compiled one." OFF)
|
||||
option(CAPNP_LITE "Compile Cap'n Proto in 'lite mode', in which all reflection APIs (schema.h, dynamic.h, etc.) are not included. Produces a smaller library at the cost of features. All programs built against the library must be compiled with -DCAPNP_LITE. Requires EXTERNAL_CAPNP." OFF)
|
||||
|
||||
# Check for invalid combinations of build options
|
||||
if(CAPNP_LITE AND BUILD_TESTING AND NOT EXTERNAL_CAPNP)
|
||||
message(SEND_ERROR "You must set EXTERNAL_CAPNP when using CAPNP_LITE and BUILD_TESTING.")
|
||||
endif()
|
||||
|
||||
if(CAPNP_LITE)
|
||||
set(CAPNP_LITE_FLAG "-DCAPNP_LITE")
|
||||
# This flag is attached as PUBLIC target_compile_definition to kj target
|
||||
else()
|
||||
set(CAPNP_LITE_FLAG)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# TODO(cleanup): Enable higher warning level in MSVC, but make sure to test
|
||||
# build with that warning level and clean out false positives.
|
||||
|
||||
add_compile_options(/wo4503)
|
||||
# Only warn once on truncated decorated names. The maximum symbol length MSVC
|
||||
# supports is 4k characters, which the parser framework regularly blows. The
|
||||
# compiler likes to print out the entire type that went over the limit along
|
||||
# with this warning, which gets unbearably spammy. That said, we don't want to
|
||||
# just ignore it, so I'm letting it trigger once until we find some places to
|
||||
# inject ParserRefs.
|
||||
else()
|
||||
# Note that it's important to add new CXXFLAGS before ones specified by the
|
||||
# user, so that the user's flags override them. This is particularly
|
||||
# important if -Werror was enabled and then certain warnings need to be
|
||||
# disabled, as is done in super-test.sh.
|
||||
#
|
||||
# We enable a lot of warnings, but then disable some:
|
||||
# * strict-aliasing: We use type-punning in known-safe ways that GCC doesn't
|
||||
# recognize as safe.
|
||||
# * sign-compare: Low S/N ratio.
|
||||
# * unused-parameter: Low S/N ratio.
|
||||
add_compile_options(-Wall -Wextra -Wno-strict-aliasing -Wno-sign-compare -Wno-unused-parameter)
|
||||
|
||||
if(DEFINED CMAKE_CXX_EXTENSIONS AND NOT CMAKE_CXX_EXTENSIONS)
|
||||
message(SEND_ERROR "Cap'n Proto requires compiler-specific extensions (e.g., -std=gnu++14). Please leave CMAKE_CXX_EXTENSIONS undefined or ON.")
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID)
|
||||
add_compile_options(-pthread)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Source =======================================================================
|
||||
include(CapnProtoMacros)
|
||||
add_subdirectory(src)
|
||||
|
||||
# Install ======================================================================
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# We used to use write_basic_package_version_file(), but since the autotools build needs to install
|
||||
# a config version script as well, I copied the AnyNewerVersion template from my CMake Modules
|
||||
# directory to Cap'n Proto's cmake/ directory (alternatively, we could make the autotools build
|
||||
# depend on CMake).
|
||||
#
|
||||
# We might as well use the local copy of the template. In the future we can modify the project's
|
||||
# version compatibility policy just by changing that file.
|
||||
set(PACKAGE_VERSION ${VERSION})
|
||||
configure_file(cmake/CapnProtoConfigVersion.cmake.in cmake/CapnProtoConfigVersion.cmake @ONLY)
|
||||
|
||||
set(CONFIG_PACKAGE_LOCATION ${CMAKE_INSTALL_LIBDIR}/cmake/CapnProto)
|
||||
|
||||
configure_package_config_file(cmake/CapnProtoConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/CapnProtoConfig.cmake
|
||||
INSTALL_DESTINATION ${CONFIG_PACKAGE_LOCATION}
|
||||
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
|
||||
)
|
||||
export(EXPORT CapnProtoTargets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/CapnProtoTargets.cmake"
|
||||
NAMESPACE CapnProto::
|
||||
)
|
||||
install(EXPORT CapnProtoTargets
|
||||
FILE CapnProtoTargets.cmake
|
||||
NAMESPACE CapnProto::
|
||||
DESTINATION ${CONFIG_PACKAGE_LOCATION}
|
||||
)
|
||||
install(FILES
|
||||
cmake/CapnProtoMacros.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/CapnProtoConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/CapnProtoConfigVersion.cmake
|
||||
DESTINATION ${CONFIG_PACKAGE_LOCATION}
|
||||
)
|
||||
#install CapnProtoMacros for CapnProtoConfig.cmake build directory consumers
|
||||
configure_file(cmake/CapnProtoMacros.cmake cmake/CapnProtoMacros.cmake COPYONLY)
|
||||
|
||||
if(NOT MSVC) # Don't install pkg-config files when building with MSVC
|
||||
# Variables for pkg-config files
|
||||
set(prefix "${CMAKE_INSTALL_PREFIX}")
|
||||
set(exec_prefix "") # not needed since we use absolute paths in libdir and includedir
|
||||
set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
|
||||
set(PTHREAD_CFLAGS "-pthread")
|
||||
set(STDLIB_FLAG) # TODO: Unsupported
|
||||
|
||||
set(CAPNP_PKG_CONFIG_FILES
|
||||
pkgconfig/kj.pc
|
||||
pkgconfig/capnp.pc
|
||||
)
|
||||
|
||||
if(NOT CAPNP_LITE)
|
||||
list(APPEND CAPNP_PKG_CONFIG_FILES
|
||||
pkgconfig/kj-async.pc
|
||||
pkgconfig/kj-http.pc
|
||||
pkgconfig/kj-test.pc
|
||||
pkgconfig/capnp-rpc.pc
|
||||
pkgconfig/capnp-json.pc
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(pcfile ${CAPNP_PKG_CONFIG_FILES})
|
||||
configure_file(${pcfile}.in "${CMAKE_CURRENT_BINARY_DIR}/${pcfile}" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${pcfile}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
endforeach()
|
||||
|
||||
unset(STDLIB_FLAG)
|
||||
unset(PTHREAD_CFLAGS)
|
||||
unset(includedir)
|
||||
unset(libdir)
|
||||
unset(exec_prefix)
|
||||
unset(prefix)
|
||||
endif()
|
|
@ -0,0 +1 @@
|
|||
../LICENSE
|
|
@ -0,0 +1,556 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign subdir-objects
|
||||
|
||||
# When running distcheck, verify that we've included all the files needed by
|
||||
# the cmake build.
|
||||
distcheck-hook:
|
||||
rm -rf distcheck-cmake
|
||||
(mkdir distcheck-cmake && cd distcheck-cmake && cmake ../$(distdir) && make -j6 check)
|
||||
rm -rf distcheck-cmake
|
||||
|
||||
AM_CXXFLAGS = -I$(srcdir)/src -I$(builddir)/src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR='"$(includedir)"' $(PTHREAD_CFLAGS)
|
||||
|
||||
AM_LDFLAGS = $(PTHREAD_CFLAGS)
|
||||
|
||||
EXTRA_DIST = \
|
||||
README.txt \
|
||||
LICENSE.txt \
|
||||
$(test_capnpc_inputs) \
|
||||
src/capnp/compiler/capnp-test.sh \
|
||||
src/capnp/testdata/segmented-packed \
|
||||
src/capnp/testdata/errors.capnp.nobuild \
|
||||
src/capnp/testdata/short.txt \
|
||||
src/capnp/testdata/flat \
|
||||
src/capnp/testdata/binary \
|
||||
src/capnp/testdata/errors.txt \
|
||||
src/capnp/testdata/segmented \
|
||||
src/capnp/testdata/packed \
|
||||
src/capnp/testdata/pretty.txt \
|
||||
src/capnp/testdata/lists.binary \
|
||||
src/capnp/testdata/packedflat \
|
||||
src/capnp/testdata/pretty.json \
|
||||
src/capnp/testdata/short.json \
|
||||
src/capnp/testdata/annotated-json.binary \
|
||||
src/capnp/testdata/annotated.json \
|
||||
CMakeLists.txt \
|
||||
cmake/CapnProtoMacros.cmake \
|
||||
cmake/CapnProtoTargets.cmake \
|
||||
cmake/CapnProtoConfig.cmake.in \
|
||||
cmake/CapnProtoConfigVersion.cmake.in \
|
||||
src/CMakeLists.txt \
|
||||
src/kj/CMakeLists.txt \
|
||||
src/capnp/CMakeLists.txt
|
||||
|
||||
CLEANFILES = $(test_capnpc_outputs) test_capnpc_middleman distcheck-cmake
|
||||
|
||||
# Deletes all the files generated by autoreconf.
|
||||
MAINTAINERCLEANFILES = \
|
||||
aclocal.m4 \
|
||||
config.guess \
|
||||
config.sub \
|
||||
configure \
|
||||
depcomp \
|
||||
install-sh \
|
||||
ltmain.sh \
|
||||
Makefile.in \
|
||||
missing \
|
||||
mkinstalldirs \
|
||||
config.h.in \
|
||||
stamp.h.in \
|
||||
m4/ltsugar.m4 \
|
||||
m4/libtool.m4 \
|
||||
m4/ltversion.m4 \
|
||||
m4/lt~obsolete.m4 \
|
||||
m4/ltoptions.m4
|
||||
|
||||
maintainer-clean-local:
|
||||
-rm -rf build-aux
|
||||
|
||||
# gmake defines an implicit rule building n from n.o. Unfortunately, this triggers on our .capnp
|
||||
# files because they generate .capnp.c++ which is compiled to .capnp.o. In addition to being
|
||||
# nonsense, this leads to cyclic dependency issues and could even cause the .capnp files to be
|
||||
# unexpectedly overwritten! We need to cancel the implicit rule by declaring an explicit one.
|
||||
#
|
||||
# I want the hours of my life back that I spent figuring this out.
|
||||
%.capnp:
|
||||
@:
|
||||
|
||||
public_capnpc_inputs = \
|
||||
src/capnp/c++.capnp \
|
||||
src/capnp/schema.capnp \
|
||||
src/capnp/rpc.capnp \
|
||||
src/capnp/rpc-twoparty.capnp \
|
||||
src/capnp/persistent.capnp \
|
||||
src/capnp/compat/json.capnp
|
||||
|
||||
capnpc_inputs = \
|
||||
$(public_capnpc_inputs) \
|
||||
src/capnp/compiler/lexer.capnp \
|
||||
src/capnp/compiler/grammar.capnp
|
||||
|
||||
capnpc_outputs = \
|
||||
src/capnp/c++.capnp.c++ \
|
||||
src/capnp/c++.capnp.h \
|
||||
src/capnp/schema.capnp.c++ \
|
||||
src/capnp/schema.capnp.h \
|
||||
src/capnp/rpc.capnp.c++ \
|
||||
src/capnp/rpc.capnp.h \
|
||||
src/capnp/rpc-twoparty.capnp.c++ \
|
||||
src/capnp/rpc-twoparty.capnp.h \
|
||||
src/capnp/persistent.capnp.c++ \
|
||||
src/capnp/persistent.capnp.h \
|
||||
src/capnp/compat/json.capnp.h \
|
||||
src/capnp/compat/json.capnp.c++ \
|
||||
src/capnp/compiler/lexer.capnp.c++ \
|
||||
src/capnp/compiler/lexer.capnp.h \
|
||||
src/capnp/compiler/grammar.capnp.c++ \
|
||||
src/capnp/compiler/grammar.capnp.h
|
||||
|
||||
includecapnpdir = $(includedir)/capnp
|
||||
includecapnpcompatdir = $(includecapnpdir)/compat
|
||||
includekjdir = $(includedir)/kj
|
||||
includekjparsedir = $(includekjdir)/parse
|
||||
includekjstddir = $(includekjdir)/std
|
||||
includekjcompatdir = $(includekjdir)/compat
|
||||
|
||||
dist_includecapnp_DATA = $(public_capnpc_inputs)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = $(CAPNP_PKG_CONFIG_FILES)
|
||||
|
||||
cmakeconfigdir = $(libdir)/cmake/CapnProto
|
||||
cmakeconfig_DATA = $(CAPNP_CMAKE_CONFIG_FILES) \
|
||||
cmake/CapnProtoMacros.cmake \
|
||||
cmake/CapnProtoTargets.cmake
|
||||
|
||||
noinst_HEADERS = \
|
||||
src/kj/miniposix.h \
|
||||
src/kj/async-io-internal.h
|
||||
|
||||
includekj_HEADERS = \
|
||||
src/kj/common.h \
|
||||
src/kj/units.h \
|
||||
src/kj/memory.h \
|
||||
src/kj/refcount.h \
|
||||
src/kj/array.h \
|
||||
src/kj/vector.h \
|
||||
src/kj/string.h \
|
||||
src/kj/string-tree.h \
|
||||
src/kj/hash.h \
|
||||
src/kj/table.h \
|
||||
src/kj/map.h \
|
||||
src/kj/encoding.h \
|
||||
src/kj/exception.h \
|
||||
src/kj/debug.h \
|
||||
src/kj/arena.h \
|
||||
src/kj/io.h \
|
||||
src/kj/tuple.h \
|
||||
src/kj/one-of.h \
|
||||
src/kj/function.h \
|
||||
src/kj/mutex.h \
|
||||
src/kj/thread.h \
|
||||
src/kj/threadlocal.h \
|
||||
src/kj/filesystem.h \
|
||||
src/kj/async-prelude.h \
|
||||
src/kj/async.h \
|
||||
src/kj/async-inl.h \
|
||||
src/kj/time.h \
|
||||
src/kj/timer.h \
|
||||
src/kj/async-unix.h \
|
||||
src/kj/async-win32.h \
|
||||
src/kj/async-io.h \
|
||||
src/kj/main.h \
|
||||
src/kj/test.h \
|
||||
src/kj/windows-sanity.h
|
||||
|
||||
includekjparse_HEADERS = \
|
||||
src/kj/parse/common.h \
|
||||
src/kj/parse/char.h
|
||||
|
||||
includekjstd_HEADERS = \
|
||||
src/kj/std/iostream.h
|
||||
|
||||
includekjcompat_HEADERS = \
|
||||
src/kj/compat/gtest.h \
|
||||
src/kj/compat/url.h \
|
||||
src/kj/compat/http.h \
|
||||
src/kj/compat/gzip.h \
|
||||
src/kj/compat/readiness-io.h \
|
||||
src/kj/compat/tls.h
|
||||
|
||||
includecapnp_HEADERS = \
|
||||
src/capnp/c++.capnp.h \
|
||||
src/capnp/common.h \
|
||||
src/capnp/blob.h \
|
||||
src/capnp/endian.h \
|
||||
src/capnp/layout.h \
|
||||
src/capnp/orphan.h \
|
||||
src/capnp/list.h \
|
||||
src/capnp/any.h \
|
||||
src/capnp/message.h \
|
||||
src/capnp/capability.h \
|
||||
src/capnp/membrane.h \
|
||||
src/capnp/schema.capnp.h \
|
||||
src/capnp/schema-lite.h \
|
||||
src/capnp/schema.h \
|
||||
src/capnp/schema-loader.h \
|
||||
src/capnp/schema-parser.h \
|
||||
src/capnp/dynamic.h \
|
||||
src/capnp/pretty-print.h \
|
||||
src/capnp/serialize.h \
|
||||
src/capnp/serialize-async.h \
|
||||
src/capnp/serialize-packed.h \
|
||||
src/capnp/serialize-text.h \
|
||||
src/capnp/pointer-helpers.h \
|
||||
src/capnp/generated-header-support.h \
|
||||
src/capnp/raw-schema.h \
|
||||
src/capnp/rpc-prelude.h \
|
||||
src/capnp/rpc.h \
|
||||
src/capnp/rpc-twoparty.h \
|
||||
src/capnp/rpc.capnp.h \
|
||||
src/capnp/rpc-twoparty.capnp.h \
|
||||
src/capnp/persistent.capnp.h \
|
||||
src/capnp/ez-rpc.h
|
||||
|
||||
includecapnpcompat_HEADERS = \
|
||||
src/capnp/compat/json.h \
|
||||
src/capnp/compat/json.capnp.h
|
||||
|
||||
if BUILD_KJ_TLS
|
||||
MAYBE_KJ_TLS_LA=libkj-tls.la
|
||||
MAYBE_KJ_TLS_TESTS= \
|
||||
src/kj/compat/readiness-io-test.c++ \
|
||||
src/kj/compat/tls-test.c++
|
||||
else
|
||||
MAYBE_KJ_TLS_LA=
|
||||
MAYBE_KJ_TLS_TESTS=
|
||||
endif
|
||||
|
||||
if LITE_MODE
|
||||
lib_LTLIBRARIES = libkj.la libkj-test.la libcapnp.la
|
||||
else
|
||||
lib_LTLIBRARIES = libkj.la libkj-test.la libkj-async.la libkj-http.la $(MAYBE_KJ_TLS_LA) libcapnp.la libcapnp-rpc.la libcapnp-json.la libcapnpc.la
|
||||
endif
|
||||
|
||||
# Don't include security release in soname -- we want to replace old binaries
|
||||
# in this case.
|
||||
SO_VERSION = $(shell echo $(VERSION) | sed -e 's/^\([0-9]*[.][0-9]*[.][0-9]*\)\([.][0-9]*\)*\(-.*\)*$$/\1\3/g')
|
||||
|
||||
libkj_la_LIBADD = $(PTHREAD_LIBS)
|
||||
libkj_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libkj_la_SOURCES= \
|
||||
src/kj/common.c++ \
|
||||
src/kj/units.c++ \
|
||||
src/kj/memory.c++ \
|
||||
src/kj/refcount.c++ \
|
||||
src/kj/array.c++ \
|
||||
src/kj/string.c++ \
|
||||
src/kj/string-tree.c++ \
|
||||
src/kj/hash.c++ \
|
||||
src/kj/table.c++ \
|
||||
src/kj/encoding.c++ \
|
||||
src/kj/exception.c++ \
|
||||
src/kj/debug.c++ \
|
||||
src/kj/arena.c++ \
|
||||
src/kj/io.c++ \
|
||||
src/kj/mutex.c++ \
|
||||
src/kj/thread.c++ \
|
||||
src/kj/time.c++ \
|
||||
src/kj/filesystem.c++ \
|
||||
src/kj/filesystem-disk-unix.c++ \
|
||||
src/kj/filesystem-disk-win32.c++ \
|
||||
src/kj/test-helpers.c++ \
|
||||
src/kj/main.c++ \
|
||||
src/kj/parse/char.c++
|
||||
|
||||
libkj_test_la_LIBADD = libkj.la $(PTHREAD_LIBS)
|
||||
libkj_test_la_LDFLAGS = -release $(VERSION) -no-undefined
|
||||
libkj_test_la_SOURCES = src/kj/test.c++
|
||||
|
||||
if !LITE_MODE
|
||||
libkj_async_la_LIBADD = libkj.la $(ASYNC_LIBS) $(PTHREAD_LIBS)
|
||||
libkj_async_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libkj_async_la_SOURCES= \
|
||||
src/kj/async.c++ \
|
||||
src/kj/async-unix.c++ \
|
||||
src/kj/async-win32.c++ \
|
||||
src/kj/async-io.c++ \
|
||||
src/kj/async-io-unix.c++ \
|
||||
src/kj/async-io-win32.c++ \
|
||||
src/kj/timer.c++
|
||||
|
||||
libkj_http_la_LIBADD = libkj-async.la libkj.la $(ASYNC_LIBS) $(PTHREAD_LIBS)
|
||||
libkj_http_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libkj_http_la_SOURCES= \
|
||||
src/kj/compat/url.c++ \
|
||||
src/kj/compat/http.c++ \
|
||||
src/kj/compat/gzip.c++
|
||||
|
||||
libkj_tls_la_LIBADD = libkj-async.la libkj.la -lssl -lcrypto $(ASYNC_LIBS) $(PTHREAD_LIBS)
|
||||
libkj_tls_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libkj_tls_la_SOURCES= \
|
||||
src/kj/compat/readiness-io.c++ \
|
||||
src/kj/compat/tls.c++
|
||||
|
||||
endif !LITE_MODE
|
||||
|
||||
if !LITE_MODE
|
||||
heavy_sources = \
|
||||
src/capnp/schema.c++ \
|
||||
src/capnp/schema-loader.c++ \
|
||||
src/capnp/dynamic.c++ \
|
||||
src/capnp/stringify.c++
|
||||
endif !LITE_MODE
|
||||
|
||||
libcapnp_la_LIBADD = libkj.la $(PTHREAD_LIBS)
|
||||
libcapnp_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libcapnp_la_SOURCES= \
|
||||
src/capnp/c++.capnp.c++ \
|
||||
src/capnp/blob.c++ \
|
||||
src/capnp/arena.h \
|
||||
src/capnp/arena.c++ \
|
||||
src/capnp/layout.c++ \
|
||||
src/capnp/list.c++ \
|
||||
src/capnp/any.c++ \
|
||||
src/capnp/message.c++ \
|
||||
src/capnp/schema.capnp.c++ \
|
||||
src/capnp/serialize.c++ \
|
||||
src/capnp/serialize-packed.c++ \
|
||||
$(heavy_sources)
|
||||
|
||||
if !LITE_MODE
|
||||
|
||||
libcapnp_rpc_la_LIBADD = libcapnp.la libkj-async.la libkj.la $(ASYNC_LIBS) $(PTHREAD_LIBS)
|
||||
libcapnp_rpc_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libcapnp_rpc_la_SOURCES= \
|
||||
src/capnp/serialize-async.c++ \
|
||||
src/capnp/capability.c++ \
|
||||
src/capnp/membrane.c++ \
|
||||
src/capnp/dynamic-capability.c++ \
|
||||
src/capnp/rpc.c++ \
|
||||
src/capnp/rpc.capnp.c++ \
|
||||
src/capnp/rpc-twoparty.c++ \
|
||||
src/capnp/rpc-twoparty.capnp.c++ \
|
||||
src/capnp/persistent.capnp.c++ \
|
||||
src/capnp/ez-rpc.c++
|
||||
|
||||
libcapnp_json_la_LIBADD = libcapnp.la libkj.la $(PTHREAD_LIBS)
|
||||
libcapnp_json_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libcapnp_json_la_SOURCES= \
|
||||
src/capnp/compat/json.c++ \
|
||||
src/capnp/compat/json.capnp.c++
|
||||
|
||||
libcapnpc_la_LIBADD = libcapnp.la libkj.la $(PTHREAD_LIBS)
|
||||
libcapnpc_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
|
||||
libcapnpc_la_SOURCES= \
|
||||
src/capnp/compiler/type-id.h \
|
||||
src/capnp/compiler/type-id.c++ \
|
||||
src/capnp/compiler/error-reporter.h \
|
||||
src/capnp/compiler/error-reporter.c++ \
|
||||
src/capnp/compiler/lexer.capnp.h \
|
||||
src/capnp/compiler/lexer.capnp.c++ \
|
||||
src/capnp/compiler/lexer.h \
|
||||
src/capnp/compiler/lexer.c++ \
|
||||
src/capnp/compiler/grammar.capnp.h \
|
||||
src/capnp/compiler/grammar.capnp.c++ \
|
||||
src/capnp/compiler/parser.h \
|
||||
src/capnp/compiler/parser.c++ \
|
||||
src/capnp/compiler/node-translator.h \
|
||||
src/capnp/compiler/node-translator.c++ \
|
||||
src/capnp/compiler/compiler.h \
|
||||
src/capnp/compiler/compiler.c++ \
|
||||
src/capnp/schema-parser.c++ \
|
||||
src/capnp/serialize-text.c++
|
||||
|
||||
bin_PROGRAMS = capnp capnpc-capnp capnpc-c++
|
||||
|
||||
capnp_LDADD = libcapnpc.la libcapnp-json.la libcapnp.la libkj.la $(PTHREAD_LIBS)
|
||||
capnp_SOURCES = \
|
||||
src/capnp/compiler/module-loader.h \
|
||||
src/capnp/compiler/module-loader.c++ \
|
||||
src/capnp/compiler/capnp.c++
|
||||
|
||||
capnpc_capnp_LDADD = libcapnp.la libkj.la $(PTHREAD_LIBS)
|
||||
capnpc_capnp_SOURCES = src/capnp/compiler/capnpc-capnp.c++
|
||||
|
||||
capnpc_c___LDADD = libcapnp.la libkj.la $(PTHREAD_LIBS)
|
||||
capnpc_c___SOURCES = src/capnp/compiler/capnpc-c++.c++
|
||||
|
||||
# Symlink capnpc -> capnp. The capnp binary will behave like the old capnpc
|
||||
# binary (i.e. like "capnp compile") when invoked via this symlink.
|
||||
#
|
||||
# Also attempt to run ldconfig, because otherwise users get confused. If
|
||||
# it fails (e.g. because the platform doesn't have it, or because the
|
||||
# user doesn't have root privileges), don't worry about it.
|
||||
install-exec-hook:
|
||||
ln -sf capnp $(DESTDIR)$(bindir)/capnpc
|
||||
ldconfig < /dev/null > /dev/null 2>&1 || true
|
||||
|
||||
uninstall-hook:
|
||||
rm -f $(DESTDIR)$(bindir)/capnpc
|
||||
|
||||
else LITE_MODE
|
||||
|
||||
install-exec-hook:
|
||||
ldconfig < /dev/null > /dev/null 2>&1 || true
|
||||
|
||||
endif LITE_MODE
|
||||
|
||||
# Source files intentionally not included in the dist at this time:
|
||||
# src/capnp/serialize-snappy*
|
||||
# src/capnp/benchmark/...
|
||||
# src/capnp/compiler/...
|
||||
|
||||
# Tests ==============================================================
|
||||
|
||||
test_capnpc_inputs = \
|
||||
src/capnp/test.capnp \
|
||||
src/capnp/test-import.capnp \
|
||||
src/capnp/test-import2.capnp \
|
||||
src/capnp/compat/json-test.capnp
|
||||
|
||||
test_capnpc_outputs = \
|
||||
src/capnp/test.capnp.c++ \
|
||||
src/capnp/test.capnp.h \
|
||||
src/capnp/test-import.capnp.c++ \
|
||||
src/capnp/test-import.capnp.h \
|
||||
src/capnp/test-import2.capnp.c++ \
|
||||
src/capnp/test-import2.capnp.h \
|
||||
src/capnp/compat/json-test.capnp.c++ \
|
||||
src/capnp/compat/json-test.capnp.h
|
||||
|
||||
if USE_EXTERNAL_CAPNP
|
||||
|
||||
test_capnpc_middleman: $(test_capnpc_inputs)
|
||||
@$(MKDIR_P) src
|
||||
$(CAPNP) compile --src-prefix=$(srcdir)/src -o$(CAPNPC_CXX):src -I$(srcdir)/src $^
|
||||
touch test_capnpc_middleman
|
||||
|
||||
else
|
||||
|
||||
test_capnpc_middleman: capnp$(EXEEXT) capnpc-c++$(EXEEXT) $(test_capnpc_inputs)
|
||||
@$(MKDIR_P) src
|
||||
echo $^ | (read CAPNP CAPNPC_CXX SOURCES && ./$$CAPNP compile --src-prefix=$(srcdir)/src -o./$$CAPNPC_CXX:src -I$(srcdir)/src $$SOURCES)
|
||||
touch test_capnpc_middleman
|
||||
|
||||
endif
|
||||
|
||||
$(test_capnpc_outputs): test_capnpc_middleman
|
||||
|
||||
BUILT_SOURCES = $(test_capnpc_outputs)
|
||||
|
||||
check_LIBRARIES = libcapnp-test.a
|
||||
libcapnp_test_a_SOURCES = \
|
||||
src/capnp/test-util.c++ \
|
||||
src/capnp/test-util.h
|
||||
nodist_libcapnp_test_a_SOURCES = $(test_capnpc_outputs)
|
||||
|
||||
if LITE_MODE
|
||||
|
||||
check_PROGRAMS = capnp-test
|
||||
compiler_tests =
|
||||
capnp_test_LDADD = libcapnp-test.a libcapnp.la libkj-test.la libkj.la
|
||||
|
||||
else !LITE_MODE
|
||||
|
||||
check_PROGRAMS = capnp-test capnp-evolution-test capnp-afl-testcase
|
||||
heavy_tests = \
|
||||
src/kj/async-test.c++ \
|
||||
src/kj/async-unix-test.c++ \
|
||||
src/kj/async-win32-test.c++ \
|
||||
src/kj/async-io-test.c++ \
|
||||
src/kj/parse/common-test.c++ \
|
||||
src/kj/parse/char-test.c++ \
|
||||
src/kj/std/iostream-test.c++ \
|
||||
src/kj/compat/url-test.c++ \
|
||||
src/kj/compat/http-test.c++ \
|
||||
src/kj/compat/gzip-test.c++ \
|
||||
$(MAYBE_KJ_TLS_TESTS) \
|
||||
src/capnp/canonicalize-test.c++ \
|
||||
src/capnp/capability-test.c++ \
|
||||
src/capnp/membrane-test.c++ \
|
||||
src/capnp/schema-test.c++ \
|
||||
src/capnp/schema-loader-test.c++ \
|
||||
src/capnp/schema-parser-test.c++ \
|
||||
src/capnp/dynamic-test.c++ \
|
||||
src/capnp/stringify-test.c++ \
|
||||
src/capnp/serialize-async-test.c++ \
|
||||
src/capnp/serialize-text-test.c++ \
|
||||
src/capnp/rpc-test.c++ \
|
||||
src/capnp/rpc-twoparty-test.c++ \
|
||||
src/capnp/ez-rpc-test.c++ \
|
||||
src/capnp/compat/json-test.c++ \
|
||||
src/capnp/compiler/lexer-test.c++ \
|
||||
src/capnp/compiler/type-id-test.c++
|
||||
capnp_test_LDADD = \
|
||||
libcapnp-test.a \
|
||||
libcapnpc.la \
|
||||
libcapnp-rpc.la \
|
||||
libcapnp-json.la \
|
||||
libcapnp.la \
|
||||
libkj-http.la \
|
||||
$(MAYBE_KJ_TLS_LA) \
|
||||
libkj-async.la \
|
||||
libkj-test.la \
|
||||
libkj.la \
|
||||
$(ASYNC_LIBS) \
|
||||
$(PTHREAD_LIBS)
|
||||
|
||||
endif !LITE_MODE
|
||||
|
||||
capnp_test_CPPFLAGS = -Wno-deprecated-declarations
|
||||
capnp_test_SOURCES = \
|
||||
src/kj/common-test.c++ \
|
||||
src/kj/memory-test.c++ \
|
||||
src/kj/refcount-test.c++ \
|
||||
src/kj/array-test.c++ \
|
||||
src/kj/string-test.c++ \
|
||||
src/kj/string-tree-test.c++ \
|
||||
src/kj/table-test.c++ \
|
||||
src/kj/map-test.c++ \
|
||||
src/kj/encoding-test.c++ \
|
||||
src/kj/exception-test.c++ \
|
||||
src/kj/debug-test.c++ \
|
||||
src/kj/arena-test.c++ \
|
||||
src/kj/units-test.c++ \
|
||||
src/kj/tuple-test.c++ \
|
||||
src/kj/one-of-test.c++ \
|
||||
src/kj/function-test.c++ \
|
||||
src/kj/io-test.c++ \
|
||||
src/kj/mutex-test.c++ \
|
||||
src/kj/threadlocal-test.c++ \
|
||||
src/kj/threadlocal-pthread-test.c++ \
|
||||
src/kj/filesystem-test.c++ \
|
||||
src/kj/filesystem-disk-test.c++ \
|
||||
src/kj/test-test.c++ \
|
||||
src/capnp/common-test.c++ \
|
||||
src/capnp/blob-test.c++ \
|
||||
src/capnp/endian-test.c++ \
|
||||
src/capnp/endian-fallback-test.c++ \
|
||||
src/capnp/endian-reverse-test.c++ \
|
||||
src/capnp/layout-test.c++ \
|
||||
src/capnp/any-test.c++ \
|
||||
src/capnp/message-test.c++ \
|
||||
src/capnp/encoding-test.c++ \
|
||||
src/capnp/orphan-test.c++ \
|
||||
src/capnp/serialize-test.c++ \
|
||||
src/capnp/serialize-packed-test.c++ \
|
||||
src/capnp/fuzz-test.c++ \
|
||||
$(heavy_tests)
|
||||
|
||||
if !LITE_MODE
|
||||
capnp_evolution_test_LDADD = libcapnpc.la libcapnp.la libkj.la
|
||||
capnp_evolution_test_SOURCES = src/capnp/compiler/evolution-test.c++
|
||||
|
||||
capnp_afl_testcase_LDADD = libcapnp-test.a libcapnp-rpc.la libcapnp.la libkj.la libkj-async.la
|
||||
capnp_afl_testcase_SOURCES = src/capnp/afl-testcase.c++
|
||||
endif !LITE_MODE
|
||||
|
||||
if LITE_MODE
|
||||
TESTS = capnp-test
|
||||
else !LITE_MODE
|
||||
TESTS = capnp-test capnp-evolution-test src/capnp/compiler/capnp-test.sh
|
||||
endif !LITE_MODE
|
|
@ -0,0 +1,34 @@
|
|||
.PHONY: all once continuous continuous-opt clean
|
||||
|
||||
EKAM=`which ekam || echo .ekam/bin/ekam`
|
||||
|
||||
ifeq ($(CXX),clang++)
|
||||
# Clang's verbose diagnostics don't play nice with the Ekam Eclipse plugin's error parsing,
|
||||
# so disable them. Also enable some useful Clang warnings (dunno if GCC supports them, and don't
|
||||
# care).
|
||||
EXTRA_FLAG=-fno-caret-diagnostics -Wglobal-constructors -Wextra-semi -Werror=return-type
|
||||
# EXTRA_FLAG=-fno-caret-diagnostics -Weverything -Wno-c++98-compat -Wno-shadow -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-gnu -Wno-unused-parameter -Wno-sign-conversion -Wno-undef -Wno-shorten-64-to-32 -Wno-conversion -Wno-unreachable-code -Wno-non-virtual-dtor
|
||||
else
|
||||
EXTRA_FLAG=
|
||||
endif
|
||||
|
||||
all:
|
||||
echo "You probably accidentally told Eclipse to build. Stopping."
|
||||
|
||||
once:
|
||||
CXXFLAGS="$(EXTRA_FLAG) -std=c++14 -O2 -DNDEBUG -Wall" LIBS='-lz -pthread' $(EKAM) -j6
|
||||
|
||||
continuous:
|
||||
CXXFLAGS="$(EXTRA_FLAG) -std=c++14 -g -DCAPNP_DEBUG_TYPES=1 -Wall" LIBS='-lz -pthread' $(EKAM) -j6 -c -n :51315
|
||||
|
||||
continuous-opt:
|
||||
CXXFLAGS="$(EXTRA_FLAG) -std=c++14 -O2 -DNDEBUG -Wall" LIBS='-lz -pthread' $(EKAM) -j6 -c -n :51315
|
||||
|
||||
continuous-opt3:
|
||||
CXXFLAGS="$(EXTRA_FLAG) -std=c++14 -O3 -DNDEBUG -Wall" LIBS='-lz -pthread' $(EKAM) -j6 -c -n :51315
|
||||
|
||||
continuous-opts:
|
||||
CXXFLAGS="$(EXTRA_FLAG) -std=c++14 -Os -DNDEBUG -Wall" LIBS='-lz -pthread' $(EKAM) -j6 -c -n :51315
|
||||
|
||||
clean:
|
||||
rm -rf bin lib tmp
|
|
@ -0,0 +1,27 @@
|
|||
Cap'n Proto - Insanely Fast Data Serialization Format
|
||||
Copyright 2013-2015 Sandstorm Development Group, Inc.
|
||||
https://capnproto.org
|
||||
|
||||
Cap'n Proto is an insanely fast data interchange format and capability-based
|
||||
RPC system. Think JSON, except binary. Or think of Google's Protocol Buffers
|
||||
(http://protobuf.googlecode.com), except faster. In fact, in benchmarks,
|
||||
Cap'n Proto is INFINITY TIMES faster than Protocol Buffers.
|
||||
|
||||
Full installation and usage instructions and other documentation are maintained
|
||||
on the Cap'n Proto web site:
|
||||
http://kentonv.github.io/capnproto/install.html
|
||||
|
||||
WARNING: Cap'n Proto requires a modern compiler. See the above link for
|
||||
detailed requirements.
|
||||
|
||||
To build and install (from a release package), simply do:
|
||||
./configure
|
||||
make -j4 check
|
||||
sudo make install
|
||||
|
||||
The -j4 allows the build to use up to four processor cores instead of one.
|
||||
You can increase this number if you have more cores. Specifying "check"
|
||||
says to run tests in addition to building. This can be omitted to make the
|
||||
build slightly faster, but running tests and reporting failures back to the
|
||||
developers helps us out!
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#! /bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "Choose test case:"
|
||||
echo "1) TestAllTypes parsing"
|
||||
echo "2) TestLists parsing"
|
||||
echo "3) Canonicalization"
|
||||
|
||||
read -p "choice: " -n 1 TESTCASE
|
||||
echo
|
||||
|
||||
case "$TESTCASE" in
|
||||
1 )
|
||||
TESTDATA=binary
|
||||
FLAGS=
|
||||
TESTNAME=default
|
||||
;;
|
||||
2 )
|
||||
TESTDATA=lists.binary
|
||||
FLAGS=--lists
|
||||
TESTNAME=lists
|
||||
;;
|
||||
3 )
|
||||
TESTDATA=binary
|
||||
FLAGS=--canonicalize
|
||||
TESTNAME=canonicalize
|
||||
;;
|
||||
* )
|
||||
echo "Invalid choice: $TESTCASE" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
echo "Choose compiler:"
|
||||
echo "1) GCC"
|
||||
echo "2) Clang"
|
||||
|
||||
read -p "choice: " -n 1 TESTCASE
|
||||
echo
|
||||
|
||||
case "$TESTCASE" in
|
||||
1 )
|
||||
export CXX=afl-g++
|
||||
;;
|
||||
2 )
|
||||
export CXX=afl-clang++
|
||||
;;
|
||||
* )
|
||||
echo "Invalid choice: $TESTCASE" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
if [ -e Makefile ]; then
|
||||
if ! grep -q '^CXX *= *'"$CXX" Makefile; then
|
||||
# Wrong compiler used.
|
||||
make distclean
|
||||
$(dirname $0)/configure --disable-shared
|
||||
fi
|
||||
else
|
||||
$(dirname $0)/configure --disable-shared
|
||||
fi
|
||||
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) capnp-afl-testcase
|
||||
|
||||
NOW=$(date +%Y-%m-%d.%H-%M-%S).$TESTNAME.$CXX
|
||||
|
||||
mkdir afl.$NOW.inputs afl.$NOW.findings
|
||||
|
||||
cp $(dirname $0)/src/capnp/testdata/$TESTDATA afl.$NOW.inputs
|
||||
|
||||
afl-fuzz -i afl.$NOW.inputs -o afl.$NOW.findings -- ./capnp-afl-testcase $FLAGS
|
|
@ -0,0 +1,75 @@
|
|||
# Cap'n Proto CMake Package Configuration
|
||||
#
|
||||
# When configured and installed, this file enables client projects to find Cap'n Proto using
|
||||
# CMake's find_package() command. It adds imported targets in the CapnProto:: namespace, such as
|
||||
# CapnProto::kj, CapnProto::capnp, etc. (one target for each file in pkgconfig/*.pc.in), defines
|
||||
# the capnp_generate_cpp() function, and exposes some variables for compatibility with the original
|
||||
# FindCapnProto.cmake module.
|
||||
#
|
||||
# Example usage:
|
||||
# find_package(CapnProto)
|
||||
# capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS schema.capnp)
|
||||
# add_executable(foo main.cpp ${CAPNP_SRCS})
|
||||
# target_link_libraries(foo PRIVATE CapnProto::capnp)
|
||||
# target_include_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
#
|
||||
# If you are using RPC features, use 'CapnProto::capnp-rpc' in the target_link_libraries() call.
|
||||
#
|
||||
# Paths to `capnp` and `capnpc-c++` are exposed in the following variables:
|
||||
# CAPNP_EXECUTABLE
|
||||
# Path to the `capnp` tool (can be set to override).
|
||||
# CAPNPC_CXX_EXECUTABLE
|
||||
# Path to the `capnpc-c++` tool (can be set to override).
|
||||
#
|
||||
# For FindCapnProto.cmake compatibility, the following variables are also provided. Please prefer
|
||||
# using the imported targets in new CMake code.
|
||||
# CAPNP_INCLUDE_DIRS
|
||||
# Include directories for the library's headers.
|
||||
# CANP_LIBRARIES
|
||||
# The Cap'n Proto library paths.
|
||||
# CAPNP_LIBRARIES_LITE
|
||||
# Paths to only the 'lite' libraries.
|
||||
# CAPNP_DEFINITIONS
|
||||
# Compiler definitions required for building with the library.
|
||||
# CAPNP_FOUND
|
||||
# Set if the libraries have been located (prefer using CapnProto_FOUND in new code).
|
||||
#
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(CapnProto_VERSION @VERSION@)
|
||||
|
||||
set(CAPNP_EXECUTABLE $<TARGET_FILE:CapnProto::capnp_tool>
|
||||
CACHE FILEPATH "Location of capnp executable")
|
||||
set(CAPNPC_CXX_EXECUTABLE $<TARGET_FILE:CapnProto::capnpc_cpp>
|
||||
CACHE FILEPATH "Location of capnpc-c++ executable")
|
||||
set(CAPNP_INCLUDE_DIRECTORY "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||
|
||||
# work around http://public.kitware.com/Bug/view.php?id=15258
|
||||
if(NOT _IMPORT_PREFIX)
|
||||
set(_IMPORT_PREFIX ${PACKAGE_PREFIX_DIR})
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/CapnProtoTargets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/CapnProtoMacros.cmake")
|
||||
|
||||
|
||||
# FindCapnProto.cmake provides dependency information via several CAPNP_-prefixed variables. New
|
||||
# code should not rely on these variables, but prefer linking directly to the imported targets we
|
||||
# now provide. However, we should still set these variables to ease the transition for projects
|
||||
# which currently depend on the find-module.
|
||||
|
||||
set(CAPNP_INCLUDE_DIRS ${CAPNP_INCLUDE_DIRECTORY})
|
||||
|
||||
# No need to list all libraries, just the leaves of the dependency tree.
|
||||
set(CAPNP_LIBRARIES_LITE CapnProto::capnp)
|
||||
set(CAPNP_LIBRARIES CapnProto::capnp-rpc CapnProto::capnp-json
|
||||
CapnProto::kj-http CapnProto::kj-test)
|
||||
|
||||
set(CAPNP_DEFINITIONS)
|
||||
if(TARGET CapnProto::capnp AND NOT TARGET CapnProto::capnp-rpc)
|
||||
set(CAPNP_DEFINITIONS -DCAPNP_LITE)
|
||||
endif()
|
||||
|
||||
set(CAPNP_FOUND ${CapnProto_FOUND})
|
|
@ -0,0 +1,36 @@
|
|||
# This is a copy of /usr/share/cmake-3.5/Modules/BasicConfigVersion-AnyNewerVersion.cmake.in, with
|
||||
# the following change:
|
||||
# - @CVF_VERSION renamed to @PACKAGE_VERSION@. Autoconf defines a PACKAGE_VERSION
|
||||
# output variable for us, so might as well take advantage of that.
|
||||
|
||||
# This is a basic version file for the Config-mode of find_package().
|
||||
# It is used by write_basic_package_version_file() as input file for configure_file()
|
||||
# to create a version-file which can be installed along a config.cmake file.
|
||||
#
|
||||
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
|
||||
# the requested version string are exactly the same and it sets
|
||||
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version.
|
||||
# The variable PACKAGE_VERSION must be set before calling configure_file().
|
||||
|
||||
set(PACKAGE_VERSION "@PACKAGE_VERSION@")
|
||||
|
||||
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "@CMAKE_SIZEOF_VOID_P@" STREQUAL "")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
|
||||
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "@CMAKE_SIZEOF_VOID_P@")
|
||||
math(EXPR installedBits "@CMAKE_SIZEOF_VOID_P@ * 8")
|
||||
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
|
||||
set(PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
endif()
|
|
@ -0,0 +1,121 @@
|
|||
# CAPNP_GENERATE_CPP ===========================================================
|
||||
#
|
||||
# Example usage:
|
||||
# find_package(CapnProto)
|
||||
# capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS schema.capnp)
|
||||
# add_executable(foo main.cpp ${CAPNP_SRCS})
|
||||
# target_link_libraries(foo PRIVATE CapnProto::capnp-rpc)
|
||||
# target_include_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
#
|
||||
# If you are not using the RPC features you can use 'CapnProto::capnp' in the
|
||||
# target_link_libraries call
|
||||
#
|
||||
# Configuration variables (optional):
|
||||
# CAPNPC_OUTPUT_DIR
|
||||
# Directory to place compiled schema sources (default: CMAKE_CURRENT_BINARY_DIR).
|
||||
# CAPNPC_IMPORT_DIRS
|
||||
# List of additional include directories for the schema compiler.
|
||||
# (CAPNPC_SRC_PREFIX and CAPNP_INCLUDE_DIRECTORY are always included.)
|
||||
# CAPNPC_SRC_PREFIX
|
||||
# Schema file source prefix (default: CMAKE_CURRENT_SOURCE_DIR).
|
||||
# CAPNPC_FLAGS
|
||||
# Additional flags to pass to the schema compiler.
|
||||
#
|
||||
# TODO: convert to cmake_parse_arguments
|
||||
|
||||
function(CAPNP_GENERATE_CPP SOURCES HEADERS)
|
||||
if(NOT ARGN)
|
||||
message(SEND_ERROR "CAPNP_GENERATE_CPP() called without any source files.")
|
||||
endif()
|
||||
set(tool_depends ${EMPTY_STRING})
|
||||
#Use cmake targets available
|
||||
if(TARGET capnp_tool)
|
||||
set(CAPNP_EXECUTABLE capnp_tool)
|
||||
GET_TARGET_PROPERTY(CAPNPC_CXX_EXECUTABLE capnpc_cpp CAPNPC_CXX_EXECUTABLE)
|
||||
GET_TARGET_PROPERTY(CAPNP_INCLUDE_DIRECTORY capnp_tool CAPNP_INCLUDE_DIRECTORY)
|
||||
list(APPEND tool_depends capnp_tool capnpc_cpp)
|
||||
endif()
|
||||
if(NOT CAPNP_EXECUTABLE)
|
||||
message(SEND_ERROR "Could not locate capnp executable (CAPNP_EXECUTABLE).")
|
||||
endif()
|
||||
if(NOT CAPNPC_CXX_EXECUTABLE)
|
||||
message(SEND_ERROR "Could not locate capnpc-c++ executable (CAPNPC_CXX_EXECUTABLE).")
|
||||
endif()
|
||||
if(NOT CAPNP_INCLUDE_DIRECTORY)
|
||||
message(SEND_ERROR "Could not locate capnp header files (CAPNP_INCLUDE_DIRECTORY).")
|
||||
endif()
|
||||
|
||||
if(DEFINED CAPNPC_OUTPUT_DIR)
|
||||
# Prepend a ':' to get the format for the '-o' flag right
|
||||
set(output_dir ":${CAPNPC_OUTPUT_DIR}")
|
||||
else()
|
||||
set(output_dir ":.")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CAPNPC_SRC_PREFIX)
|
||||
set(CAPNPC_SRC_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endif()
|
||||
get_filename_component(CAPNPC_SRC_PREFIX "${CAPNPC_SRC_PREFIX}" ABSOLUTE)
|
||||
|
||||
# Default compiler includes. Note that in capnp's own test usage of capnp_generate_cpp(), these
|
||||
# two variables will end up evaluating to the same directory. However, it's difficult to
|
||||
# deduplicate them because if CAPNP_INCLUDE_DIRECTORY came from the capnp_tool target property,
|
||||
# then it must be a generator expression in order to handle usages in both the build tree and the
|
||||
# install tree. This vastly overcomplicates duplication detection, so the duplication doesn't seem
|
||||
# worth fixing.
|
||||
set(include_path -I "${CAPNPC_SRC_PREFIX}" -I "${CAPNP_INCLUDE_DIRECTORY}")
|
||||
|
||||
if(DEFINED CAPNPC_IMPORT_DIRS)
|
||||
# Append each directory as a series of '-I' flags in ${include_path}
|
||||
foreach(directory ${CAPNPC_IMPORT_DIRS})
|
||||
get_filename_component(absolute_path "${directory}" ABSOLUTE)
|
||||
list(APPEND include_path -I "${absolute_path}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(${SOURCES})
|
||||
set(${HEADERS})
|
||||
foreach(schema_file ${ARGN})
|
||||
get_filename_component(file_path "${schema_file}" ABSOLUTE)
|
||||
get_filename_component(file_dir "${file_path}" PATH)
|
||||
if(NOT EXISTS "${file_path}")
|
||||
message(FATAL_ERROR "Cap'n Proto schema file '${file_path}' does not exist!")
|
||||
endif()
|
||||
|
||||
# Figure out where the output files will go
|
||||
if (NOT DEFINED CAPNPC_OUTPUT_DIR)
|
||||
set(CAPNPC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/")
|
||||
endif()
|
||||
# Output files are placed in CAPNPC_OUTPUT_DIR, at a location as if they were
|
||||
# relative to CAPNPC_SRC_PREFIX.
|
||||
string(LENGTH "${CAPNPC_SRC_PREFIX}" prefix_len)
|
||||
string(SUBSTRING "${file_path}" 0 ${prefix_len} output_prefix)
|
||||
if(NOT "${CAPNPC_SRC_PREFIX}" STREQUAL "${output_prefix}")
|
||||
message(SEND_ERROR "Could not determine output path for '${schema_file}' ('${file_path}') with source prefix '${CAPNPC_SRC_PREFIX}' into '${CAPNPC_OUTPUT_DIR}'.")
|
||||
endif()
|
||||
|
||||
string(SUBSTRING "${file_path}" ${prefix_len} -1 output_path)
|
||||
set(output_base "${CAPNPC_OUTPUT_DIR}${output_path}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${output_base}.c++" "${output_base}.h"
|
||||
COMMAND "${CAPNP_EXECUTABLE}"
|
||||
ARGS compile
|
||||
-o ${CAPNPC_CXX_EXECUTABLE}${output_dir}
|
||||
--src-prefix ${CAPNPC_SRC_PREFIX}
|
||||
${include_path}
|
||||
${CAPNPC_FLAGS}
|
||||
${file_path}
|
||||
DEPENDS "${schema_file}" ${tool_depends}
|
||||
COMMENT "Compiling Cap'n Proto schema ${schema_file}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
list(APPEND ${SOURCES} "${output_base}.c++")
|
||||
list(APPEND ${HEADERS} "${output_base}.h")
|
||||
endforeach()
|
||||
|
||||
set_source_files_properties(${${SOURCES}} ${${HEADERS}} PROPERTIES GENERATED TRUE)
|
||||
set(${SOURCES} ${${SOURCES}} PARENT_SCOPE)
|
||||
set(${HEADERS} ${${HEADERS}} PARENT_SCOPE)
|
||||
endfunction()
|
|
@ -0,0 +1,221 @@
|
|||
# This CMake script adds imported targets for each shared library and executable distributed by
|
||||
# Cap'n Proto's autotools build.
|
||||
#
|
||||
# This file IS NOT USED by the CMake build! The CMake build generates its own version of this script
|
||||
# from its set of exported targets. I used such a generated script as a reference when writing this
|
||||
# one.
|
||||
#
|
||||
# The set of library targets provided by this script is automatically generated from the list of .pc
|
||||
# files maintained in configure.ac. The set of executable targets is hard-coded in this file.
|
||||
#
|
||||
# You can request that this script print debugging information by invoking cmake with:
|
||||
#
|
||||
# -DCapnProto_DEBUG=ON
|
||||
#
|
||||
# TODO(someday): Distinguish between debug and release builds. I.e., set IMPORTED_LOCATION_RELEASE
|
||||
# rather than IMPORTED_LOCATION, etc., if this installation was configured as a release build. But
|
||||
# how do we tell? grep for -g in CXXFLAGS?
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.1)
|
||||
message(FATAL_ERROR "CMake >= 3.1 required")
|
||||
endif()
|
||||
|
||||
set(forwarded_config_flags)
|
||||
if(CapnProto_FIND_QUIETLY)
|
||||
list(APPEND forwarded_config_flags QUIET)
|
||||
endif()
|
||||
if(CapnProto_FIND_REQUIRED)
|
||||
list(APPEND forwarded_config_flags REQUIRED)
|
||||
endif()
|
||||
# If the consuming project called find_package(CapnProto) with the QUIET or REQUIRED flags, forward
|
||||
# them to calls to find_package(PkgConfig) and pkg_check_modules(). Note that find_dependency()
|
||||
# would do this for us in the former case, but there is no such forwarding wrapper for
|
||||
# pkg_check_modules().
|
||||
|
||||
find_package(PkgConfig ${forwarded_config_flags})
|
||||
if(NOT ${PkgConfig_FOUND})
|
||||
# If we're here, the REQUIRED flag must not have been passed, else we would have had a fatal
|
||||
# error. Nevertheless, a diagnostic for this case is probably nice.
|
||||
if(NOT CapnProto_FIND_QUIETLY)
|
||||
message(WARNING "pkg-config cannot be found")
|
||||
endif()
|
||||
set(CapnProto_FOUND OFF)
|
||||
return()
|
||||
endif()
|
||||
|
||||
function(_capnp_import_pkg_config_target target)
|
||||
# Add an imported library target named CapnProto::${target}, using the output of various
|
||||
# invocations of `pkg-config ${target}`. The generated imported library target tries to mimic the
|
||||
# behavior of a real CMake-generated imported target as closely as possible.
|
||||
#
|
||||
# Usage: _capnp_import_pkg_config_target(target <all Cap'n Proto targets>)
|
||||
|
||||
set(all_targets ${ARGN})
|
||||
|
||||
pkg_check_modules(${target} ${forwarded_config_flags} ${target})
|
||||
|
||||
if(NOT ${${target}_FOUND})
|
||||
if(NOT CapnProto_FIND_QUIETLY)
|
||||
message(WARNING "CapnProtoConfig.cmake was configured to search for ${target}.pc, but pkg-config cannot find it. Ignoring this target.")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CapnProto_DEBUG)
|
||||
# Dump the information pkg-config discovered.
|
||||
foreach(var VERSION LIBRARY_DIRS LIBRARIES LDFLAGS_OTHER INCLUDE_DIRS CFLAGS_OTHER)
|
||||
message(STATUS "${target}_${var} = ${${target}_${var}}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT ${${target}_VERSION} VERSION_EQUAL ${CapnProto_VERSION})
|
||||
if(NOT CapnProto_FIND_QUIETLY)
|
||||
message(WARNING "CapnProtoConfig.cmake was configured to search for version ${CapnProto_VERSION}, but ${target} version ${${target}_VERSION} was found. Ignoring this target.")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Make an educated guess as to what the target's .so and .a filenames must be.
|
||||
set(target_name_shared
|
||||
${CMAKE_SHARED_LIBRARY_PREFIX}${target}-${CapnProto_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(target_name_static
|
||||
${CMAKE_STATIC_LIBRARY_PREFIX}${target}${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
|
||||
# Find the actual target's file. find_library() sets a cache variable, so I made the variable name
|
||||
# unique-ish.
|
||||
find_library(CapnProto_${target}_IMPORTED_LOCATION
|
||||
NAMES ${target_name_shared} ${target_name_static} # prefer libfoo-version.so over libfoo.a
|
||||
PATHS ${${target}_LIBRARY_DIRS}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
# If the installed version of Cap'n Proto is in a system location, pkg-config will not have filled
|
||||
# in ${target}_LIBRARY_DIRS. To account for this, fall back to a regular search.
|
||||
find_library(CapnProto_${target}_IMPORTED_LOCATION
|
||||
NAMES ${target_name_shared} ${target_name_static} # prefer libfoo-version.so over libfoo.a
|
||||
)
|
||||
|
||||
if(NOT CapnProto_${target}_IMPORTED_LOCATION)
|
||||
# Not an error if the library doesn't exist -- we may have found a lite mode installation.
|
||||
if(CapnProto_DEBUG)
|
||||
message(STATUS "${target} library does not exist")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Record some information about this target -- shared versus static, location and soname -- which
|
||||
# we'll use to build our imported target later.
|
||||
|
||||
set(target_location ${CapnProto_${target}_IMPORTED_LOCATION})
|
||||
get_filename_component(target_name "${target_location}" NAME)
|
||||
|
||||
set(target_type STATIC)
|
||||
set(imported_soname_property)
|
||||
if(target_name STREQUAL ${target_name_shared})
|
||||
set(target_type SHARED)
|
||||
set(imported_soname_property IMPORTED_SONAME ${target_name})
|
||||
endif()
|
||||
|
||||
# Each library dependency of the target is either the target itself, a sibling Cap'n Proto
|
||||
# library, or a system library. We ignore the first case by removing this target from the
|
||||
# dependencies. The remaining dependencies are either passed through or, if they are a sibling
|
||||
# Cap'n Proto library, prefixed with `CapnProto::`.
|
||||
set(dependencies ${${target}_LIBRARIES})
|
||||
list(REMOVE_ITEM dependencies ${target})
|
||||
set(target_interface_libs)
|
||||
foreach(dependency ${dependencies})
|
||||
list(FIND all_targets ${dependency} target_index)
|
||||
# TODO(cleanup): CMake >= 3.3 lets us write: `if(NOT ${dependency} IN_LIST all_targets)`
|
||||
if(target_index EQUAL -1)
|
||||
list(APPEND target_interface_libs ${dependency})
|
||||
else()
|
||||
list(APPEND target_interface_libs CapnProto::${dependency})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_library(CapnProto::${target} ${target_type} IMPORTED)
|
||||
set_target_properties(CapnProto::${target} PROPERTIES
|
||||
${imported_soname_property}
|
||||
IMPORTED_LOCATION "${target_location}"
|
||||
# TODO(cleanup): Use cxx_std_14 once it's safe to require cmake 3.8.
|
||||
INTERFACE_COMPILE_FEATURES "cxx_generic_lambdas"
|
||||
INTERFACE_COMPILE_OPTIONS "${${target}_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${target}_INCLUDE_DIRS}"
|
||||
|
||||
# I'm dumping LDFLAGS_OTHER in with the libraries because there exists no
|
||||
# INTERFACE_LINK_OPTIONS. See https://gitlab.kitware.com/cmake/cmake/issues/16543.
|
||||
INTERFACE_LINK_LIBRARIES "${target_interface_libs};${${target}_LDFLAGS_OTHER}"
|
||||
)
|
||||
|
||||
if(CapnProto_DEBUG)
|
||||
# Dump all the properties we generated for the imported target.
|
||||
foreach(prop
|
||||
IMPORTED_LOCATION
|
||||
IMPORTED_SONAME
|
||||
INTERFACE_COMPILE_FEATURES
|
||||
INTERFACE_COMPILE_OPTIONS
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
INTERFACE_LINK_LIBRARIES)
|
||||
get_target_property(value CapnProto::${target} ${prop})
|
||||
message(STATUS "CapnProto::${target} ${prop} = ${value}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# ========================================================================================
|
||||
# Imported library targets
|
||||
|
||||
# Build a list of targets to search for from the list of .pc files.
|
||||
# I.e. [somewhere/foo.pc, somewhere/bar.pc] -> [foo, bar]
|
||||
set(library_targets)
|
||||
foreach(filename ${CAPNP_PKG_CONFIG_FILES})
|
||||
get_filename_component(target ${filename} NAME_WE)
|
||||
list(APPEND library_targets ${target})
|
||||
endforeach()
|
||||
|
||||
# Try to add an imported library target CapnProto::foo for each foo.pc distributed with Cap'n Proto.
|
||||
foreach(target ${library_targets})
|
||||
_capnp_import_pkg_config_target(${target} ${library_targets})
|
||||
endforeach()
|
||||
|
||||
# Handle lite-mode and no libraries found cases. It is tempting to set a CapnProto_LITE variable
|
||||
# here, but the real CMake-generated implementation does no such thing -- we'd need to set it in
|
||||
# CapnProtoConfig.cmake.in itself.
|
||||
if(TARGET CapnProto::capnp AND TARGET CapnProto::kj)
|
||||
if(NOT TARGET CapnProto::capnp-rpc)
|
||||
if(NOT CapnProto_FIND_QUIETLY)
|
||||
message(STATUS "Found an installation of Cap'n Proto lite. Executable and library targets beyond libkj and libcapnp will be unavailable.")
|
||||
endif()
|
||||
# Lite mode doesn't include the executables, so return here.
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
# If we didn't even find capnp or kj, then we didn't find anything usable.
|
||||
set(CapnProto_FOUND OFF)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# ========================================================================================
|
||||
# Imported executable targets
|
||||
|
||||
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
|
||||
# Add executable targets for the capnp compiler and plugins. This list must be kept manually in sync
|
||||
# with the rest of the project.
|
||||
|
||||
add_executable(CapnProto::capnp_tool IMPORTED)
|
||||
set_target_properties(CapnProto::capnp_tool PROPERTIES
|
||||
IMPORTED_LOCATION "${_IMPORT_PREFIX}/bin/capnp${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
)
|
||||
|
||||
add_executable(CapnProto::capnpc_cpp IMPORTED)
|
||||
set_target_properties(CapnProto::capnpc_cpp PROPERTIES
|
||||
IMPORTED_LOCATION "${_IMPORT_PREFIX}/bin/capnpc-c++${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
)
|
||||
|
||||
add_executable(CapnProto::capnpc_capnp IMPORTED)
|
||||
set_target_properties(CapnProto::capnpc_capnp PROPERTIES
|
||||
IMPORTED_LOCATION "${_IMPORT_PREFIX}/bin/capnpc-capnp${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
)
|
|
@ -0,0 +1,166 @@
|
|||
## Process this file with autoconf to produce configure.
|
||||
|
||||
AC_INIT([Capn Proto],[0.7.0],[capnproto@googlegroups.com],[capnproto-c++])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/capnp/layout.c++])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
# autoconf's default CXXFLAGS are usually "-g -O2". A far more reasonable
|
||||
# default is -O2 -NDEBUG.
|
||||
AS_IF([test "x${ac_cv_env_CFLAGS_set}" = "x"],
|
||||
[CFLAGS="-O2 -DNDEBUG"])
|
||||
AS_IF([test "x${ac_cv_env_CXXFLAGS_set}" = "x"],
|
||||
[CXXFLAGS="-O2 -DNDEBUG"])
|
||||
|
||||
AM_INIT_AUTOMAKE([tar-ustar])
|
||||
|
||||
AC_ARG_WITH([external-capnp],
|
||||
[AS_HELP_STRING([--with-external-capnp],
|
||||
[use the system capnp binary (or the one specified with $CAPNP) instead of compiling a new
|
||||
one (useful for cross-compiling)])],
|
||||
[external_capnp=yes],[external_capnp=no])
|
||||
|
||||
AC_ARG_WITH([openssl],
|
||||
[AS_HELP_STRING([--with-openssl],
|
||||
[build libkj-tls by linking against openssl @<:@default=check@:>@])],
|
||||
[],[with_openssl=check])
|
||||
|
||||
AC_ARG_ENABLE([reflection], [
|
||||
AS_HELP_STRING([--disable-reflection], [
|
||||
compile Cap'n Proto in "lite mode", in which all reflection APIs (schema.h, dynamic.h, etc.)
|
||||
are not included. Produces a smaller library at the cost of features. All programs built
|
||||
against the library MUST be compiled with -DCAPNP_LITE=1. Note that because the compiler
|
||||
itself uses reflection in its implementation, you must also use --with-external-capnp when
|
||||
using this option.])
|
||||
], [
|
||||
case "${enableval}" in
|
||||
yes)
|
||||
lite_mode=no
|
||||
;;
|
||||
no)
|
||||
lite_mode=yes
|
||||
AS_IF([test "$external_capnp" != "yes"], [
|
||||
AC_MSG_ERROR([you must specify --with-external-capnp when using --disable-reflection])
|
||||
])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([bad value ${enableval} for --enable-reflection])
|
||||
;;
|
||||
esac
|
||||
], [lite_mode=no])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_LANG([C++])
|
||||
AX_CXX_COMPILE_STDCXX_14
|
||||
|
||||
AS_CASE("${host_os}", *mingw*, [
|
||||
# We don't use pthreads on MinGW.
|
||||
PTHREAD_CFLAGS="-mthreads"
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CC=""
|
||||
ASYNC_LIBS="-lws2_32"
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
AC_SUBST(ASYNC_LIBS)
|
||||
], *, [
|
||||
ACX_PTHREAD
|
||||
ASYNC_LIBS=""
|
||||
AC_SUBST(ASYNC_LIBS)
|
||||
])
|
||||
|
||||
LT_INIT
|
||||
|
||||
AS_IF([test "$external_capnp" != "no"], [
|
||||
AS_IF([test "x$CAPNP" = "x"], [CAPNP="capnp"], [with_capnp=yes])
|
||||
AS_IF([test "x$CAPNPC_CXX" = "x"], [
|
||||
# CAPNPC_CXX was not specified. Choose a reasonable default.
|
||||
AS_CASE([$CAPNP], [*/*], [
|
||||
# $CAPNP contains a slash, so it's not on $PATH. Assume capnpc-c++ is not either, but is
|
||||
# in the same directory.
|
||||
CAPNPC_CXX=`dirname $CAPNP`/capnpc-c++
|
||||
], [
|
||||
# $CAPNP is on $PATH, so tell it to find the plugin on $PATH as well.
|
||||
CAPNPC_CXX="c++"
|
||||
])
|
||||
])
|
||||
AC_SUBST([CAPNP])
|
||||
AC_SUBST([CAPNPC_CXX])
|
||||
])
|
||||
AM_CONDITIONAL([USE_EXTERNAL_CAPNP], [test "$external_capnp" != "no"])
|
||||
|
||||
AM_CONDITIONAL([LITE_MODE], [test "$lite_mode" = "yes"])
|
||||
|
||||
AS_IF([test "$lite_mode" = "yes"], [
|
||||
CXXFLAGS="-DCAPNP_LITE $CXXFLAGS"
|
||||
CAPNP_LITE_FLAG=-DCAPNP_LITE
|
||||
])
|
||||
AC_SUBST([CAPNP_LITE_FLAG])
|
||||
|
||||
AC_SEARCH_LIBS(sched_yield, rt)
|
||||
|
||||
# Users will need to use the same -stdlib as us so we'd better let pkg-config know about it.
|
||||
STDLIB_FLAG=`echo "$CXX $CXXFLAGS" | grep -o ' [[-]]stdlib=[[^ ]]*'`
|
||||
AC_SUBST([STDLIB_FLAG])
|
||||
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
AC_DEFUN([CAPNP_PKG_CONFIG_FILES], [ \
|
||||
pkgconfig/capnp.pc \
|
||||
pkgconfig/capnp-rpc.pc \
|
||||
pkgconfig/capnp-json.pc \
|
||||
pkgconfig/kj.pc \
|
||||
pkgconfig/kj-async.pc \
|
||||
pkgconfig/kj-http.pc \
|
||||
pkgconfig/kj-test.pc \
|
||||
])
|
||||
AC_DEFUN([CAPNP_CMAKE_CONFIG_FILES], [ \
|
||||
cmake/CapnProtoConfig.cmake \
|
||||
cmake/CapnProtoConfigVersion.cmake \
|
||||
])
|
||||
|
||||
[CAPNP_PKG_CONFIG_FILES]="CAPNP_PKG_CONFIG_FILES"
|
||||
[CAPNP_CMAKE_CONFIG_FILES]="CAPNP_CMAKE_CONFIG_FILES"
|
||||
AC_SUBST([CAPNP_PKG_CONFIG_FILES])
|
||||
AC_SUBST([CAPNP_CMAKE_CONFIG_FILES])
|
||||
|
||||
# CapnProtoConfig.cmake.in needs these PACKAGE_* output variables.
|
||||
PACKAGE_INIT="set([CAPNP_PKG_CONFIG_FILES] CAPNP_PKG_CONFIG_FILES)"
|
||||
PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR="\${CMAKE_CURRENT_LIST_DIR}/../../../include"
|
||||
AC_SUBST([PACKAGE_INIT])
|
||||
AC_SUBST([PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR])
|
||||
|
||||
# CapnProtoConfigVersion.cmake.in needs PACKAGE_VERSION (already defined by AC_INIT) and
|
||||
# CMAKE_SIZEOF_VOID_P output variables.
|
||||
AC_CHECK_SIZEOF([void *])
|
||||
AC_SUBST(CMAKE_SIZEOF_VOID_P, $ac_cv_sizeof_void_p)
|
||||
|
||||
# Detect presence of OpenSSL, if it was not specified explicitly.
|
||||
AS_IF([test "$with_openssl" = check], [
|
||||
AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, [:], [
|
||||
with_openssl=no
|
||||
])
|
||||
AC_CHECK_LIB(ssl, OPENSSL_init_ssl, [:], [
|
||||
with_openssl=no
|
||||
], [-lcrypto])
|
||||
AC_CHECK_HEADER([openssl/ssl.h], [:], [
|
||||
with_openssl=no
|
||||
])
|
||||
AS_IF([test "$with_openssl" = no], [
|
||||
AC_MSG_WARN("could not find OpenSSL -- won't build libkj-tls")
|
||||
], [
|
||||
with_openssl=yes
|
||||
])
|
||||
])
|
||||
AS_IF([test "$with_openssl" != no], [
|
||||
CXXFLAGS="$CXXFLAGS -DKJ_HAS_OPENSSL"
|
||||
])
|
||||
AM_CONDITIONAL([BUILD_KJ_TLS], [test "$with_openssl" != no])
|
||||
|
||||
AC_CONFIG_FILES([Makefile] CAPNP_PKG_CONFIG_FILES CAPNP_CMAKE_CONFIG_FILES)
|
||||
AC_OUTPUT
|
|
@ -0,0 +1 @@
|
|||
../src
|
|
@ -0,0 +1 @@
|
|||
../src
|
|
@ -0,0 +1,4 @@
|
|||
The sole purpose of this directory is to allow GDB to find source files when
|
||||
debugging an executable compiled using Ekam, since the source locations found
|
||||
in the compiled binary will correspond to Ekam's virtual filesystem, not the
|
||||
real one. This is a hack, and doesn't always work.
|
|
@ -0,0 +1,405 @@
|
|||
# This file was copied to Cap'n Proto from the Protocol Buffers distribution,
|
||||
# version 2.3.0.
|
||||
|
||||
# This was retrieved from
|
||||
# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi
|
||||
# See also (perhaps for new versions?)
|
||||
# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi
|
||||
#
|
||||
# We've rewritten the inconsistency check code (from avahi), to work
|
||||
# more broadly. In particular, it no longer assumes ld accepts -zdefs.
|
||||
# This caused a restructing of the code, but the functionality has only
|
||||
# changed a little.
|
||||
|
||||
dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
dnl
|
||||
dnl @summary figure out how to build C programs using POSIX threads
|
||||
dnl
|
||||
dnl This macro figures out how to build C programs using POSIX threads.
|
||||
dnl It sets the PTHREAD_LIBS output variable to the threads library and
|
||||
dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
|
||||
dnl C compiler flags that are needed. (The user can also force certain
|
||||
dnl compiler flags/libs to be tested by setting these environment
|
||||
dnl variables.)
|
||||
dnl
|
||||
dnl Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
dnl multi-threaded programs (defaults to the value of CC otherwise).
|
||||
dnl (This is necessary on AIX to use the special cc_r compiler alias.)
|
||||
dnl
|
||||
dnl NOTE: You are assumed to not only compile your program with these
|
||||
dnl flags, but also link it with them as well. e.g. you should link
|
||||
dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
|
||||
dnl $LIBS
|
||||
dnl
|
||||
dnl If you are only building threads programs, you may wish to use
|
||||
dnl these variables in your default LIBS, CFLAGS, and CC:
|
||||
dnl
|
||||
dnl LIBS="$PTHREAD_LIBS $LIBS"
|
||||
dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
dnl CC="$PTHREAD_CC"
|
||||
dnl
|
||||
dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
|
||||
dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
|
||||
dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
dnl
|
||||
dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
|
||||
dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
|
||||
dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
|
||||
dnl default action will define HAVE_PTHREAD.
|
||||
dnl
|
||||
dnl Please let the authors know if this macro fails on any platform, or
|
||||
dnl if you have any other suggestions or comments. This macro was based
|
||||
dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
|
||||
dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
|
||||
dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
|
||||
dnl We are also grateful for the helpful feedback of numerous users.
|
||||
dnl
|
||||
dnl @category InstalledPackages
|
||||
dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
dnl @version 2006-05-29
|
||||
dnl @license GPLWithACException
|
||||
dnl
|
||||
dnl Checks for GCC shared/pthread inconsistency based on work by
|
||||
dnl Marcin Owsiany <marcin@owsiany.pl>
|
||||
|
||||
|
||||
AC_DEFUN([ACX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
acx_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
|
||||
AC_MSG_RESULT($acx_pthread_ok)
|
||||
if test x"$acx_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# ... -mt is also the pthreads flag for HP/aCC
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$acx_pthread_ok" = xno; then
|
||||
for flag in $acx_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
|
||||
if test x"$acx_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[acx_pthread_ok=yes])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT($acx_pthread_ok)
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
attr_name=unknown
|
||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
|
||||
[attr_name=$attr; break])
|
||||
done
|
||||
AC_MSG_RESULT($attr_name)
|
||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
|
||||
[Define to necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
|
||||
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
|
||||
esac
|
||||
AC_MSG_RESULT(${flag})
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
# More AIX lossage: must compile with xlc_r or cc_r
|
||||
if test x"$GCC" != xyes; then
|
||||
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
|
||||
else
|
||||
PTHREAD_CC=$CC
|
||||
fi
|
||||
|
||||
# The next part tries to detect GCC inconsistency with -shared on some
|
||||
# architectures and systems. The problem is that in certain
|
||||
# configurations, when -shared is specified, GCC "forgets" to
|
||||
# internally use various flags which are still necessary.
|
||||
|
||||
#
|
||||
# Prepare the flags
|
||||
#
|
||||
save_CFLAGS="$CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
save_CC="$CC"
|
||||
|
||||
# Try with the flags determined by the earlier checks.
|
||||
#
|
||||
# -Wl,-z,defs forces link-time symbol resolution, so that the
|
||||
# linking checks with -shared actually have any value
|
||||
#
|
||||
# FIXME: -fPIC is required for -shared on many architectures,
|
||||
# so we specify it here, but the right way would probably be to
|
||||
# properly detect whether it is actually required.
|
||||
CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CC="$PTHREAD_CC"
|
||||
|
||||
# In order not to create several levels of indentation, we test
|
||||
# the value of "$done" until we find the cure or run out of ideas.
|
||||
done="no"
|
||||
|
||||
# First, make sure the CFLAGS we added are actually accepted by our
|
||||
# compiler. If not (and OS X's ld, for instance, does not accept -z),
|
||||
# then we can't do this test.
|
||||
if test x"$done" = xno; then
|
||||
AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies])
|
||||
AC_TRY_LINK(,, , [done=yes])
|
||||
|
||||
if test "x$done" = xyes ; then
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$done" = xno; then
|
||||
AC_MSG_CHECKING([whether -pthread is sufficient with -shared])
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[done=yes])
|
||||
|
||||
if test "x$done" = xyes; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Linux gcc on some architectures such as mips/mipsel forgets
|
||||
# about -lpthread
|
||||
#
|
||||
if test x"$done" = xno; then
|
||||
AC_MSG_CHECKING([whether -lpthread fixes that])
|
||||
LIBS="-lpthread $PTHREAD_LIBS $save_LIBS"
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[done=yes])
|
||||
|
||||
if test "x$done" = xyes; then
|
||||
AC_MSG_RESULT([yes])
|
||||
PTHREAD_LIBS="-lpthread $PTHREAD_LIBS"
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
fi
|
||||
#
|
||||
# FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc
|
||||
#
|
||||
if test x"$done" = xno; then
|
||||
AC_MSG_CHECKING([whether -lc_r fixes that])
|
||||
LIBS="-lc_r $PTHREAD_LIBS $save_LIBS"
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[done=yes])
|
||||
|
||||
if test "x$done" = xyes; then
|
||||
AC_MSG_RESULT([yes])
|
||||
PTHREAD_LIBS="-lc_r $PTHREAD_LIBS"
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
fi
|
||||
if test x"$done" = xno; then
|
||||
# OK, we have run out of ideas
|
||||
AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries])
|
||||
|
||||
# so it's not safe to assume that we may use pthreads
|
||||
acx_pthread_ok=no
|
||||
fi
|
||||
|
||||
CFLAGS="$save_CFLAGS"
|
||||
LIBS="$save_LIBS"
|
||||
CC="$save_CC"
|
||||
else
|
||||
PTHREAD_CC="$CC"
|
||||
fi
|
||||
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
# One more check: If we chose to use a compiler flag like -pthread but it is combined with
|
||||
# -nostdlib then the compiler won't implicitly link against libpthread. This can happen
|
||||
# in particular when using some versions of libtool on some distros. See:
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=661333
|
||||
|
||||
save_CFLAGS="$CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
save_CC="$CC"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
LIBS="-nostdlib $PTHREAD_LIBS $LIBS -lc"
|
||||
CC="$PTHREAD_CC"
|
||||
|
||||
AC_MSG_CHECKING([whether pthread flag is sufficient with -nostdlib])
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[AC_MSG_RESULT([yes])], [
|
||||
AC_MSG_RESULT([no])
|
||||
|
||||
AC_MSG_CHECKING([whether adding -lpthread fixes that])
|
||||
|
||||
LIBS="-nostdlib $PTHREAD_LIBS -lpthread $save_LIBS -lc"
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[
|
||||
AC_MSG_RESULT([yes])
|
||||
PTHREAD_LIBS="$PTHREAD_LIBS -lpthread"
|
||||
], [AC_MSG_RESULT([no])])
|
||||
])
|
||||
|
||||
CFLAGS="$save_CFLAGS"
|
||||
LIBS="$save_LIBS"
|
||||
CC="$save_CC"
|
||||
fi
|
||||
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$acx_pthread_ok" = xyes; then
|
||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
||||
:
|
||||
else
|
||||
acx_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
])dnl ACX_PTHREAD
|
|
@ -0,0 +1,191 @@
|
|||
# ============================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
|
||||
# Additionally modified to detect -stdlib by Kenton Varda.
|
||||
# Further modified for C++14 by Kenton Varda.
|
||||
# ============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX_14([ext|noext])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the C++14
|
||||
# standard; if necessary, add switches to CXXFLAGS to enable support.
|
||||
# Errors out if no mode that supports C++14 baseline syntax can be found.
|
||||
# The argument, if specified, indicates whether you insist on an extended
|
||||
# mode (e.g. -std=gnu++14) or a strict conformance mode (e.g. -std=c++14).
|
||||
# If neither is specified, you get whatever works, with preference for an
|
||||
# extended mode.
|
||||
#
|
||||
# Additionally, check if the standard library supports C++11. If not,
|
||||
# try adding -stdlib=libc++ to see if that fixes it. This is needed e.g.
|
||||
# on Mac OSX 10.8, which ships with a very old libstdc++ but a relatively
|
||||
# new libc++.
|
||||
#
|
||||
# Both flags are actually added to CXX rather than CXXFLAGS to work around
|
||||
# a bug in libtool: -stdlib is stripped from CXXFLAGS when linking dynamic
|
||||
# libraries because it is not recognized. A patch was committed to mainline
|
||||
# libtool in February 2012 but as of June 2013 there has not yet been a
|
||||
# release containing this patch.
|
||||
# http://git.savannah.gnu.org/gitweb/?p=libtool.git;a=commit;h=c0c49f289f22ae670066657c60905986da3b555f
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Kenton Varda <temporal@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 1
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_14_testbody], [[
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
typedef check<check<bool>> right_angle_brackets;
|
||||
|
||||
int a;
|
||||
decltype(a) b;
|
||||
|
||||
typedef check<int> check_type;
|
||||
check_type c;
|
||||
check_type&& cr = static_cast<check_type&&>(c);
|
||||
|
||||
// GCC 4.7 introduced __float128 and makes reference to it in type_traits.
|
||||
// Clang doesn't implement it, so produces an error. Using -std=c++11
|
||||
// instead of -std=gnu++11 works around the problem. But on some
|
||||
// platforms we need -std=gnu++11. So we want to make sure the test of
|
||||
// -std=gnu++11 fails only where this problem is present, and we hope that
|
||||
// -std=c++11 is always an acceptable fallback in these cases. Complicating
|
||||
// matters, though, is that we don't want to fail here if the platform is
|
||||
// completely missing a C++11 standard library, because we want to probe that
|
||||
// in a later test. It happens, though, that Clang allows us to check
|
||||
// whether a header exists at all before we include it.
|
||||
//
|
||||
// So, if we detect that __has_include is available (which it is on Clang),
|
||||
// and we use it to detect that <type_traits> (a C++11 header) exists, then
|
||||
// we go ahead and #include it to see if it breaks. In all other cases, we
|
||||
// don't #include it at all.
|
||||
#ifdef __has_include
|
||||
#if __has_include(<type_traits>)
|
||||
#include <type_traits>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// C++14 stuff
|
||||
auto deduceReturnType(int i) { return i; }
|
||||
|
||||
auto genericLambda = [](auto x, auto y) { return x + y; };
|
||||
auto captureExpressions = [x = 123]() { return x; };
|
||||
|
||||
// Avoid unused variable warnings.
|
||||
int foo() {
|
||||
return genericLambda(1, 2) + captureExpressions();
|
||||
}
|
||||
]])
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody_lib], [
|
||||
#include <initializer_list>
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX_14], [dnl
|
||||
m4_if([$1], [], [],
|
||||
[$1], [ext], [],
|
||||
[$1], [noext], [],
|
||||
[m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_14])])dnl
|
||||
AC_LANG_ASSERT([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++14 features by default,
|
||||
ax_cv_cxx_compile_cxx14,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_14_testbody])],
|
||||
[ax_cv_cxx_compile_cxx14=yes],
|
||||
[ax_cv_cxx_compile_cxx14=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx14 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$1], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++14 -std=gnu++1y; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx14_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++14 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_14_testbody])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$1], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=c++14 -std=c++1y; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx14_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++14 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_14_testbody])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++14 language features is required.])
|
||||
else
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++11 library features by default,
|
||||
ax_cv_cxx_compile_cxx11_lib,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody_lib])],
|
||||
[ax_cv_cxx_compile_cxx11_lib=yes],
|
||||
[ax_cv_cxx_compile_cxx11_lib=no])
|
||||
])
|
||||
if test x$ax_cv_cxx_compile_cxx11_lib = xyes; then
|
||||
ac_success=yes
|
||||
else
|
||||
# Try with -stdlib=libc++
|
||||
AC_CACHE_CHECK(whether $CXX supports C++11 library features with -stdlib=libc++,
|
||||
ax_cv_cxx_compile_cxx11_lib_libcxx,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX -stdlib=libc++"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody_lib])],
|
||||
[eval ax_cv_cxx_compile_cxx11_lib_libcxx=yes],
|
||||
[eval ax_cv_cxx_compile_cxx11_lib_libcxx=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x$ax_cv_cxx_compile_cxx11_lib_libcxx = xyes; then
|
||||
CXX="$CXX -stdlib=libc++"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A C++ library with support for C++11 features is required.])
|
||||
fi
|
||||
fi
|
||||
])
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Cap'n Proto JSON
|
||||
Description: JSON encoder and decoder for Cap'n Proto objects
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lcapnp-json
|
||||
Requires: capnp = @VERSION@ kj = @VERSION@
|
||||
Cflags: -I${includedir}
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Cap'n Proto RPC
|
||||
Description: Fast object-oriented RPC system
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lcapnp-rpc
|
||||
Requires: capnp = @VERSION@ kj-async = @VERSION@
|
||||
Cflags: -I${includedir}
|
|
@ -0,0 +1,12 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Cap'n Proto
|
||||
Description: Insanely fast serialization system
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lcapnp @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ @STDLIB_FLAG@
|
||||
Libs.private: @LIBS@
|
||||
Requires: kj = @VERSION@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@ @STDLIB_FLAG@ @CAPNP_LITE_FLAG@
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: KJ Async Framework Library
|
||||
Description: Basic utility library called KJ (async part)
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lkj-async @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ @STDLIB_FLAG@
|
||||
Requires: kj = @VERSION@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@ @STDLIB_FLAG@ @CAPNP_LITE_FLAG@
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: KJ HTTP Library
|
||||
Description: Basic utility library called KJ (HTTP part)
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lkj-http @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ @STDLIB_FLAG@
|
||||
Requires: kj-async = @VERSION@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@ @STDLIB_FLAG@ @CAPNP_LITE_FLAG@
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: KJ Test Framework
|
||||
Description: Basic utility library called KJ (test part)
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lkj-test @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ @STDLIB_FLAG@
|
||||
Requires: kj = @VERSION@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@ @STDLIB_FLAG@ @CAPNP_LITE_FLAG@
|
|
@ -0,0 +1,10 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: KJ Framework Library
|
||||
Description: Basic utility library called KJ
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lkj @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ @STDLIB_FLAG@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@ @STDLIB_FLAG@ @CAPNP_LITE_FLAG@
|
|
@ -0,0 +1,11 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export PATH=$PWD/bin:$PWD:$PATH
|
||||
|
||||
capnp compile -Isrc --no-standard-import --src-prefix=src -oc++:src \
|
||||
src/capnp/c++.capnp src/capnp/schema.capnp \
|
||||
src/capnp/compiler/lexer.capnp src/capnp/compiler/grammar.capnp \
|
||||
src/capnp/rpc.capnp src/capnp/rpc-twoparty.capnp src/capnp/persistent.capnp \
|
||||
src/capnp/compat/json.capnp
|
|
@ -0,0 +1,38 @@
|
|||
# A Cap'n Proto sample project.
|
||||
#
|
||||
# To build (non-MSVC):
|
||||
# 1. Install Cap'n Proto somewhere ($PREFIX below):
|
||||
#
|
||||
# mkdir capnproto/build
|
||||
# cd capnproto/build
|
||||
# cmake ../c++ -DCMAKE_INSTALL_PREFIX=$PREFIX
|
||||
# cmake --build . --target install
|
||||
#
|
||||
# 2. Ensure Cap'n Proto's executables are on the PATH, then build the sample project:
|
||||
#
|
||||
# export PATH=$PREFIX/bin:$PATH
|
||||
# mkdir ../build-samples
|
||||
# cd ../build-samples
|
||||
# cmake ../c++/samples
|
||||
# cmake --build .
|
||||
|
||||
project("Cap'n Proto Samples" CXX)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
find_package(CapnProto CONFIG REQUIRED)
|
||||
|
||||
capnp_generate_cpp(addressbookSources addressbookHeaders addressbook.capnp)
|
||||
add_executable(addressbook addressbook.c++ ${addressbookSources})
|
||||
target_link_libraries(addressbook PRIVATE CapnProto::capnp)
|
||||
target_include_directories(addressbook PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Don't build the rpc sample if find_package() found an installation of Cap'n Proto lite.
|
||||
if(TARGET CapnProto::capnp-rpc)
|
||||
capnp_generate_cpp(calculatorSources calculatorHeaders calculator.capnp)
|
||||
add_executable(calculator-client calculator-client.c++ ${calculatorSources})
|
||||
add_executable(calculator-server calculator-server.c++ ${calculatorSources})
|
||||
target_link_libraries(calculator-client PRIVATE CapnProto::capnp-rpc)
|
||||
target_link_libraries(calculator-server PRIVATE CapnProto::capnp-rpc)
|
||||
target_include_directories(calculator-client PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(calculator-server PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
|
@ -0,0 +1,289 @@
|
|||
// 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 sample code appears in the documentation for the C++ implementation.
|
||||
//
|
||||
// If Cap'n Proto is installed, build the sample like:
|
||||
// capnp compile -oc++ addressbook.capnp
|
||||
// c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ `pkg-config --cflags --libs capnp` -o addressbook
|
||||
//
|
||||
// If Cap'n Proto is not installed, but the source is located at $SRC and has been
|
||||
// compiled in $BUILD (often both are simply ".." from here), you can do:
|
||||
// $BUILD/capnp compile -I$SRC/src -o$BUILD/capnpc-c++ addressbook.capnp
|
||||
// c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ -I$SRC/src -L$BUILD/.libs -lcapnp -lkj -o addressbook
|
||||
//
|
||||
// Run like:
|
||||
// ./addressbook write | ./addressbook read
|
||||
// Use "dwrite" and "dread" to use dynamic code instead.
|
||||
|
||||
// TODO(test): Needs cleanup.
|
||||
|
||||
#include "addressbook.capnp.h"
|
||||
#include <capnp/message.h>
|
||||
#include <capnp/serialize-packed.h>
|
||||
#include <iostream>
|
||||
|
||||
using addressbook::Person;
|
||||
using addressbook::AddressBook;
|
||||
|
||||
void writeAddressBook(int fd) {
|
||||
::capnp::MallocMessageBuilder message;
|
||||
|
||||
AddressBook::Builder addressBook = message.initRoot<AddressBook>();
|
||||
::capnp::List<Person>::Builder people = addressBook.initPeople(2);
|
||||
|
||||
Person::Builder alice = people[0];
|
||||
alice.setId(123);
|
||||
alice.setName("Alice");
|
||||
alice.setEmail("alice@example.com");
|
||||
// Type shown for explanation purposes; normally you'd use auto.
|
||||
::capnp::List<Person::PhoneNumber>::Builder alicePhones =
|
||||
alice.initPhones(1);
|
||||
alicePhones[0].setNumber("555-1212");
|
||||
alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE);
|
||||
alice.getEmployment().setSchool("MIT");
|
||||
|
||||
Person::Builder bob = people[1];
|
||||
bob.setId(456);
|
||||
bob.setName("Bob");
|
||||
bob.setEmail("bob@example.com");
|
||||
auto bobPhones = bob.initPhones(2);
|
||||
bobPhones[0].setNumber("555-4567");
|
||||
bobPhones[0].setType(Person::PhoneNumber::Type::HOME);
|
||||
bobPhones[1].setNumber("555-7654");
|
||||
bobPhones[1].setType(Person::PhoneNumber::Type::WORK);
|
||||
bob.getEmployment().setUnemployed();
|
||||
|
||||
writePackedMessageToFd(fd, message);
|
||||
}
|
||||
|
||||
void printAddressBook(int fd) {
|
||||
::capnp::PackedFdMessageReader message(fd);
|
||||
|
||||
AddressBook::Reader addressBook = message.getRoot<AddressBook>();
|
||||
|
||||
for (Person::Reader person : addressBook.getPeople()) {
|
||||
std::cout << person.getName().cStr() << ": "
|
||||
<< person.getEmail().cStr() << std::endl;
|
||||
for (Person::PhoneNumber::Reader phone: person.getPhones()) {
|
||||
const char* typeName = "UNKNOWN";
|
||||
switch (phone.getType()) {
|
||||
case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break;
|
||||
case Person::PhoneNumber::Type::HOME: typeName = "home"; break;
|
||||
case Person::PhoneNumber::Type::WORK: typeName = "work"; break;
|
||||
}
|
||||
std::cout << " " << typeName << " phone: "
|
||||
<< phone.getNumber().cStr() << std::endl;
|
||||
}
|
||||
Person::Employment::Reader employment = person.getEmployment();
|
||||
switch (employment.which()) {
|
||||
case Person::Employment::UNEMPLOYED:
|
||||
std::cout << " unemployed" << std::endl;
|
||||
break;
|
||||
case Person::Employment::EMPLOYER:
|
||||
std::cout << " employer: "
|
||||
<< employment.getEmployer().cStr() << std::endl;
|
||||
break;
|
||||
case Person::Employment::SCHOOL:
|
||||
std::cout << " student at: "
|
||||
<< employment.getSchool().cStr() << std::endl;
|
||||
break;
|
||||
case Person::Employment::SELF_EMPLOYED:
|
||||
std::cout << " self-employed" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !CAPNP_LITE
|
||||
|
||||
#include "addressbook.capnp.h"
|
||||
#include <capnp/message.h>
|
||||
#include <capnp/serialize-packed.h>
|
||||
#include <iostream>
|
||||
#include <capnp/schema.h>
|
||||
#include <capnp/dynamic.h>
|
||||
|
||||
using ::capnp::DynamicValue;
|
||||
using ::capnp::DynamicStruct;
|
||||
using ::capnp::DynamicEnum;
|
||||
using ::capnp::DynamicList;
|
||||
using ::capnp::List;
|
||||
using ::capnp::Schema;
|
||||
using ::capnp::StructSchema;
|
||||
using ::capnp::EnumSchema;
|
||||
|
||||
using ::capnp::Void;
|
||||
using ::capnp::Text;
|
||||
using ::capnp::MallocMessageBuilder;
|
||||
using ::capnp::PackedFdMessageReader;
|
||||
|
||||
void dynamicWriteAddressBook(int fd, StructSchema schema) {
|
||||
// Write a message using the dynamic API to set each
|
||||
// field by text name. This isn't something you'd
|
||||
// normally want to do; it's just for illustration.
|
||||
|
||||
MallocMessageBuilder message;
|
||||
|
||||
// Types shown for explanation purposes; normally you'd
|
||||
// use auto.
|
||||
DynamicStruct::Builder addressBook =
|
||||
message.initRoot<DynamicStruct>(schema);
|
||||
|
||||
DynamicList::Builder people =
|
||||
addressBook.init("people", 2).as<DynamicList>();
|
||||
|
||||
DynamicStruct::Builder alice =
|
||||
people[0].as<DynamicStruct>();
|
||||
alice.set("id", 123);
|
||||
alice.set("name", "Alice");
|
||||
alice.set("email", "alice@example.com");
|
||||
auto alicePhones = alice.init("phones", 1).as<DynamicList>();
|
||||
auto phone0 = alicePhones[0].as<DynamicStruct>();
|
||||
phone0.set("number", "555-1212");
|
||||
phone0.set("type", "mobile");
|
||||
alice.get("employment").as<DynamicStruct>()
|
||||
.set("school", "MIT");
|
||||
|
||||
auto bob = people[1].as<DynamicStruct>();
|
||||
bob.set("id", 456);
|
||||
bob.set("name", "Bob");
|
||||
bob.set("email", "bob@example.com");
|
||||
|
||||
// Some magic: We can convert a dynamic sub-value back to
|
||||
// the native type with as<T>()!
|
||||
List<Person::PhoneNumber>::Builder bobPhones =
|
||||
bob.init("phones", 2).as<List<Person::PhoneNumber>>();
|
||||
bobPhones[0].setNumber("555-4567");
|
||||
bobPhones[0].setType(Person::PhoneNumber::Type::HOME);
|
||||
bobPhones[1].setNumber("555-7654");
|
||||
bobPhones[1].setType(Person::PhoneNumber::Type::WORK);
|
||||
bob.get("employment").as<DynamicStruct>()
|
||||
.set("unemployed", ::capnp::VOID);
|
||||
|
||||
writePackedMessageToFd(fd, message);
|
||||
}
|
||||
|
||||
void dynamicPrintValue(DynamicValue::Reader value) {
|
||||
// Print an arbitrary message via the dynamic API by
|
||||
// iterating over the schema. Look at the handling
|
||||
// of STRUCT in particular.
|
||||
|
||||
switch (value.getType()) {
|
||||
case DynamicValue::VOID:
|
||||
std::cout << "";
|
||||
break;
|
||||
case DynamicValue::BOOL:
|
||||
std::cout << (value.as<bool>() ? "true" : "false");
|
||||
break;
|
||||
case DynamicValue::INT:
|
||||
std::cout << value.as<int64_t>();
|
||||
break;
|
||||
case DynamicValue::UINT:
|
||||
std::cout << value.as<uint64_t>();
|
||||
break;
|
||||
case DynamicValue::FLOAT:
|
||||
std::cout << value.as<double>();
|
||||
break;
|
||||
case DynamicValue::TEXT:
|
||||
std::cout << '\"' << value.as<Text>().cStr() << '\"';
|
||||
break;
|
||||
case DynamicValue::LIST: {
|
||||
std::cout << "[";
|
||||
bool first = true;
|
||||
for (auto element: value.as<DynamicList>()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
std::cout << ", ";
|
||||
}
|
||||
dynamicPrintValue(element);
|
||||
}
|
||||
std::cout << "]";
|
||||
break;
|
||||
}
|
||||
case DynamicValue::ENUM: {
|
||||
auto enumValue = value.as<DynamicEnum>();
|
||||
KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) {
|
||||
std::cout <<
|
||||
enumerant->getProto().getName().cStr();
|
||||
} else {
|
||||
// Unknown enum value; output raw number.
|
||||
std::cout << enumValue.getRaw();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DynamicValue::STRUCT: {
|
||||
std::cout << "(";
|
||||
auto structValue = value.as<DynamicStruct>();
|
||||
bool first = true;
|
||||
for (auto field: structValue.getSchema().getFields()) {
|
||||
if (!structValue.has(field)) continue;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
std::cout << ", ";
|
||||
}
|
||||
std::cout << field.getProto().getName().cStr()
|
||||
<< " = ";
|
||||
dynamicPrintValue(structValue.get(field));
|
||||
}
|
||||
std::cout << ")";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// There are other types, we aren't handling them.
|
||||
std::cout << "?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dynamicPrintMessage(int fd, StructSchema schema) {
|
||||
PackedFdMessageReader message(fd);
|
||||
dynamicPrintValue(message.getRoot<DynamicStruct>(schema));
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Missing arg." << std::endl;
|
||||
return 1;
|
||||
} else if (strcmp(argv[1], "write") == 0) {
|
||||
writeAddressBook(1);
|
||||
} else if (strcmp(argv[1], "read") == 0) {
|
||||
printAddressBook(0);
|
||||
#if !CAPNP_LITE
|
||||
} else if (strcmp(argv[1], "dwrite") == 0) {
|
||||
StructSchema schema = Schema::from<AddressBook>();
|
||||
dynamicWriteAddressBook(1, schema);
|
||||
} else if (strcmp(argv[1], "dread") == 0) {
|
||||
StructSchema schema = Schema::from<AddressBook>();
|
||||
dynamicPrintMessage(0, schema);
|
||||
#endif
|
||||
} else {
|
||||
std::cerr << "Invalid arg: " << argv[1] << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# 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.
|
||||
|
||||
@0x9eb32e19f86ee174;
|
||||
|
||||
using Cxx = import "/capnp/c++.capnp";
|
||||
$Cxx.namespace("addressbook");
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
|
||||
employment :union {
|
||||
unemployed @4 :Void;
|
||||
employer @5 :Text;
|
||||
school @6 :Text;
|
||||
selfEmployed @7 :Void;
|
||||
# We assume that a person is only one of these.
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
|
@ -0,0 +1,367 @@
|
|||
// 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 "calculator.capnp.h"
|
||||
#include <capnp/ez-rpc.h>
|
||||
#include <kj/debug.h>
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
|
||||
class PowerFunction final: public Calculator::Function::Server {
|
||||
// An implementation of the Function interface wrapping pow(). Note that
|
||||
// we're implementing this on the client side and will pass a reference to
|
||||
// the server. The server will then be able to make calls back to the client.
|
||||
|
||||
public:
|
||||
kj::Promise<void> call(CallContext context) {
|
||||
auto params = context.getParams().getParams();
|
||||
KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
|
||||
context.getResults().setValue(pow(params[0], params[1]));
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "usage: " << argv[0] << " HOST:PORT\n"
|
||||
"Connects to the Calculator server at the given address and "
|
||||
"does some RPCs." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
capnp::EzRpcClient client(argv[1]);
|
||||
Calculator::Client calculator = client.getMain<Calculator>();
|
||||
|
||||
// Keep an eye on `waitScope`. Whenever you see it used is a place where we
|
||||
// stop and wait for the server to respond. If a line of code does not use
|
||||
// `waitScope`, then it does not block!
|
||||
auto& waitScope = client.getWaitScope();
|
||||
|
||||
{
|
||||
// Make a request that just evaluates the literal value 123.
|
||||
//
|
||||
// What's interesting here is that evaluate() returns a "Value", which is
|
||||
// another interface and therefore points back to an object living on the
|
||||
// server. We then have to call read() on that object to read it.
|
||||
// However, even though we are making two RPC's, this block executes in
|
||||
// *one* network round trip because of promise pipelining: we do not wait
|
||||
// for the first call to complete before we send the second call to the
|
||||
// server.
|
||||
|
||||
std::cout << "Evaluating a literal... ";
|
||||
std::cout.flush();
|
||||
|
||||
// Set up the request.
|
||||
auto request = calculator.evaluateRequest();
|
||||
request.getExpression().setLiteral(123);
|
||||
|
||||
// Send it, which returns a promise for the result (without blocking).
|
||||
auto evalPromise = request.send();
|
||||
|
||||
// Using the promise, create a pipelined request to call read() on the
|
||||
// returned object, and then send that.
|
||||
auto readPromise = evalPromise.getValue().readRequest().send();
|
||||
|
||||
// Now that we've sent all the requests, wait for the response. Until this
|
||||
// point, we haven't waited at all!
|
||||
auto response = readPromise.wait(waitScope);
|
||||
KJ_ASSERT(response.getValue() == 123);
|
||||
|
||||
std::cout << "PASS" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
// Make a request to evaluate 123 + 45 - 67.
|
||||
//
|
||||
// The Calculator interface requires that we first call getOperator() to
|
||||
// get the addition and subtraction functions, then call evaluate() to use
|
||||
// them. But, once again, we can get both functions, call evaluate(), and
|
||||
// then read() the result -- four RPCs -- in the time of *one* network
|
||||
// round trip, because of promise pipelining.
|
||||
|
||||
std::cout << "Using add and subtract... ";
|
||||
std::cout.flush();
|
||||
|
||||
Calculator::Function::Client add = nullptr;
|
||||
Calculator::Function::Client subtract = nullptr;
|
||||
|
||||
{
|
||||
// Get the "add" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::ADD);
|
||||
add = request.send().getFunc();
|
||||
}
|
||||
|
||||
{
|
||||
// Get the "subtract" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::SUBTRACT);
|
||||
subtract = request.send().getFunc();
|
||||
}
|
||||
|
||||
// Build the request to evaluate 123 + 45 - 67.
|
||||
auto request = calculator.evaluateRequest();
|
||||
|
||||
auto subtractCall = request.getExpression().initCall();
|
||||
subtractCall.setFunction(subtract);
|
||||
auto subtractParams = subtractCall.initParams(2);
|
||||
subtractParams[1].setLiteral(67);
|
||||
|
||||
auto addCall = subtractParams[0].initCall();
|
||||
addCall.setFunction(add);
|
||||
auto addParams = addCall.initParams(2);
|
||||
addParams[0].setLiteral(123);
|
||||
addParams[1].setLiteral(45);
|
||||
|
||||
// Send the evaluate() request, read() the result, and wait for read() to
|
||||
// finish.
|
||||
auto evalPromise = request.send();
|
||||
auto readPromise = evalPromise.getValue().readRequest().send();
|
||||
|
||||
auto response = readPromise.wait(waitScope);
|
||||
KJ_ASSERT(response.getValue() == 101);
|
||||
|
||||
std::cout << "PASS" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
// Make a request to evaluate 4 * 6, then use the result in two more
|
||||
// requests that add 3 and 5.
|
||||
//
|
||||
// Since evaluate() returns its result wrapped in a `Value`, we can pass
|
||||
// that `Value` back to the server in subsequent requests before the first
|
||||
// `evaluate()` has actually returned. Thus, this example again does only
|
||||
// one network round trip.
|
||||
|
||||
std::cout << "Pipelining eval() calls... ";
|
||||
std::cout.flush();
|
||||
|
||||
Calculator::Function::Client add = nullptr;
|
||||
Calculator::Function::Client multiply = nullptr;
|
||||
|
||||
{
|
||||
// Get the "add" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::ADD);
|
||||
add = request.send().getFunc();
|
||||
}
|
||||
|
||||
{
|
||||
// Get the "multiply" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::MULTIPLY);
|
||||
multiply = request.send().getFunc();
|
||||
}
|
||||
|
||||
// Build the request to evaluate 4 * 6
|
||||
auto request = calculator.evaluateRequest();
|
||||
|
||||
auto multiplyCall = request.getExpression().initCall();
|
||||
multiplyCall.setFunction(multiply);
|
||||
auto multiplyParams = multiplyCall.initParams(2);
|
||||
multiplyParams[0].setLiteral(4);
|
||||
multiplyParams[1].setLiteral(6);
|
||||
|
||||
auto multiplyResult = request.send().getValue();
|
||||
|
||||
// Use the result in two calls that add 3 and add 5.
|
||||
|
||||
auto add3Request = calculator.evaluateRequest();
|
||||
auto add3Call = add3Request.getExpression().initCall();
|
||||
add3Call.setFunction(add);
|
||||
auto add3Params = add3Call.initParams(2);
|
||||
add3Params[0].setPreviousResult(multiplyResult);
|
||||
add3Params[1].setLiteral(3);
|
||||
auto add3Promise = add3Request.send().getValue().readRequest().send();
|
||||
|
||||
auto add5Request = calculator.evaluateRequest();
|
||||
auto add5Call = add5Request.getExpression().initCall();
|
||||
add5Call.setFunction(add);
|
||||
auto add5Params = add5Call.initParams(2);
|
||||
add5Params[0].setPreviousResult(multiplyResult);
|
||||
add5Params[1].setLiteral(5);
|
||||
auto add5Promise = add5Request.send().getValue().readRequest().send();
|
||||
|
||||
// Now wait for the results.
|
||||
KJ_ASSERT(add3Promise.wait(waitScope).getValue() == 27);
|
||||
KJ_ASSERT(add5Promise.wait(waitScope).getValue() == 29);
|
||||
|
||||
std::cout << "PASS" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
// Our calculator interface supports defining functions. Here we use it
|
||||
// to define two functions and then make calls to them as follows:
|
||||
//
|
||||
// f(x, y) = x * 100 + y
|
||||
// g(x) = f(x, x + 1) * 2;
|
||||
// f(12, 34)
|
||||
// g(21)
|
||||
//
|
||||
// Once again, the whole thing takes only one network round trip.
|
||||
|
||||
std::cout << "Defining functions... ";
|
||||
std::cout.flush();
|
||||
|
||||
Calculator::Function::Client add = nullptr;
|
||||
Calculator::Function::Client multiply = nullptr;
|
||||
Calculator::Function::Client f = nullptr;
|
||||
Calculator::Function::Client g = nullptr;
|
||||
|
||||
{
|
||||
// Get the "add" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::ADD);
|
||||
add = request.send().getFunc();
|
||||
}
|
||||
|
||||
{
|
||||
// Get the "multiply" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::MULTIPLY);
|
||||
multiply = request.send().getFunc();
|
||||
}
|
||||
|
||||
{
|
||||
// Define f.
|
||||
auto request = calculator.defFunctionRequest();
|
||||
request.setParamCount(2);
|
||||
|
||||
{
|
||||
// Build the function body.
|
||||
auto addCall = request.getBody().initCall();
|
||||
addCall.setFunction(add);
|
||||
auto addParams = addCall.initParams(2);
|
||||
addParams[1].setParameter(1); // y
|
||||
|
||||
auto multiplyCall = addParams[0].initCall();
|
||||
multiplyCall.setFunction(multiply);
|
||||
auto multiplyParams = multiplyCall.initParams(2);
|
||||
multiplyParams[0].setParameter(0); // x
|
||||
multiplyParams[1].setLiteral(100);
|
||||
}
|
||||
|
||||
f = request.send().getFunc();
|
||||
}
|
||||
|
||||
{
|
||||
// Define g.
|
||||
auto request = calculator.defFunctionRequest();
|
||||
request.setParamCount(1);
|
||||
|
||||
{
|
||||
// Build the function body.
|
||||
auto multiplyCall = request.getBody().initCall();
|
||||
multiplyCall.setFunction(multiply);
|
||||
auto multiplyParams = multiplyCall.initParams(2);
|
||||
multiplyParams[1].setLiteral(2);
|
||||
|
||||
auto fCall = multiplyParams[0].initCall();
|
||||
fCall.setFunction(f);
|
||||
auto fParams = fCall.initParams(2);
|
||||
fParams[0].setParameter(0);
|
||||
|
||||
auto addCall = fParams[1].initCall();
|
||||
addCall.setFunction(add);
|
||||
auto addParams = addCall.initParams(2);
|
||||
addParams[0].setParameter(0);
|
||||
addParams[1].setLiteral(1);
|
||||
}
|
||||
|
||||
g = request.send().getFunc();
|
||||
}
|
||||
|
||||
// OK, we've defined all our functions. Now create our eval requests.
|
||||
|
||||
// f(12, 34)
|
||||
auto fEvalRequest = calculator.evaluateRequest();
|
||||
auto fCall = fEvalRequest.initExpression().initCall();
|
||||
fCall.setFunction(f);
|
||||
auto fParams = fCall.initParams(2);
|
||||
fParams[0].setLiteral(12);
|
||||
fParams[1].setLiteral(34);
|
||||
auto fEvalPromise = fEvalRequest.send().getValue().readRequest().send();
|
||||
|
||||
// g(21)
|
||||
auto gEvalRequest = calculator.evaluateRequest();
|
||||
auto gCall = gEvalRequest.initExpression().initCall();
|
||||
gCall.setFunction(g);
|
||||
gCall.initParams(1)[0].setLiteral(21);
|
||||
auto gEvalPromise = gEvalRequest.send().getValue().readRequest().send();
|
||||
|
||||
// Wait for the results.
|
||||
KJ_ASSERT(fEvalPromise.wait(waitScope).getValue() == 1234);
|
||||
KJ_ASSERT(gEvalPromise.wait(waitScope).getValue() == 4244);
|
||||
|
||||
std::cout << "PASS" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
// Make a request that will call back to a function defined locally.
|
||||
//
|
||||
// Specifically, we will compute 2^(4 + 5). However, exponent is not
|
||||
// defined by the Calculator server. So, we'll implement the Function
|
||||
// interface locally and pass it to the server for it to use when
|
||||
// evaluating the expression.
|
||||
//
|
||||
// This example requires two network round trips to complete, because the
|
||||
// server calls back to the client once before finishing. In this
|
||||
// particular case, this could potentially be optimized by using a tail
|
||||
// call on the server side -- see CallContext::tailCall(). However, to
|
||||
// keep the example simpler, we haven't implemented this optimization in
|
||||
// the sample server.
|
||||
|
||||
std::cout << "Using a callback... ";
|
||||
std::cout.flush();
|
||||
|
||||
Calculator::Function::Client add = nullptr;
|
||||
|
||||
{
|
||||
// Get the "add" function from the server.
|
||||
auto request = calculator.getOperatorRequest();
|
||||
request.setOp(Calculator::Operator::ADD);
|
||||
add = request.send().getFunc();
|
||||
}
|
||||
|
||||
// Build the eval request for 2^(4+5).
|
||||
auto request = calculator.evaluateRequest();
|
||||
|
||||
auto powCall = request.getExpression().initCall();
|
||||
powCall.setFunction(kj::heap<PowerFunction>());
|
||||
auto powParams = powCall.initParams(2);
|
||||
powParams[0].setLiteral(2);
|
||||
|
||||
auto addCall = powParams[1].initCall();
|
||||
addCall.setFunction(add);
|
||||
auto addParams = addCall.initParams(2);
|
||||
addParams[0].setLiteral(4);
|
||||
addParams[1].setLiteral(5);
|
||||
|
||||
// Send the request and wait.
|
||||
auto response = request.send().getValue().readRequest()
|
||||
.send().wait(waitScope);
|
||||
KJ_ASSERT(response.getValue() == 512);
|
||||
|
||||
std::cout << "PASS" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
// 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 "calculator.capnp.h"
|
||||
#include <kj/debug.h>
|
||||
#include <capnp/ez-rpc.h>
|
||||
#include <capnp/message.h>
|
||||
#include <iostream>
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
kj::Promise<double> readValue(Calculator::Value::Client value) {
|
||||
// Helper function to asynchronously call read() on a Calculator::Value and
|
||||
// return a promise for the result. (In the future, the generated code might
|
||||
// include something like this automatically.)
|
||||
|
||||
return value.readRequest().send()
|
||||
.then([](capnp::Response<Calculator::Value::ReadResults> result) {
|
||||
return result.getValue();
|
||||
});
|
||||
}
|
||||
|
||||
kj::Promise<double> evaluateImpl(
|
||||
Calculator::Expression::Reader expression,
|
||||
capnp::List<double>::Reader params = capnp::List<double>::Reader()) {
|
||||
// Implementation of CalculatorImpl::evaluate(), also shared by
|
||||
// FunctionImpl::call(). In the latter case, `params` are the parameter
|
||||
// values passed to the function; in the former case, `params` is just an
|
||||
// empty list.
|
||||
|
||||
switch (expression.which()) {
|
||||
case Calculator::Expression::LITERAL:
|
||||
return expression.getLiteral();
|
||||
|
||||
case Calculator::Expression::PREVIOUS_RESULT:
|
||||
return readValue(expression.getPreviousResult());
|
||||
|
||||
case Calculator::Expression::PARAMETER: {
|
||||
KJ_REQUIRE(expression.getParameter() < params.size(),
|
||||
"Parameter index out-of-range.");
|
||||
return params[expression.getParameter()];
|
||||
}
|
||||
|
||||
case Calculator::Expression::CALL: {
|
||||
auto call = expression.getCall();
|
||||
auto func = call.getFunction();
|
||||
|
||||
// Evaluate each parameter.
|
||||
kj::Array<kj::Promise<double>> paramPromises =
|
||||
KJ_MAP(param, call.getParams()) {
|
||||
return evaluateImpl(param, params);
|
||||
};
|
||||
|
||||
// Join the array of promises into a promise for an array.
|
||||
kj::Promise<kj::Array<double>> joinedParams =
|
||||
kj::joinPromises(kj::mv(paramPromises));
|
||||
|
||||
// When the parameters are complete, call the function.
|
||||
return joinedParams.then([KJ_CPCAP(func)](kj::Array<double>&& paramValues) mutable {
|
||||
auto request = func.callRequest();
|
||||
request.setParams(paramValues);
|
||||
return request.send().then(
|
||||
[](capnp::Response<Calculator::Function::CallResults>&& result) {
|
||||
return result.getValue();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
// Throw an exception.
|
||||
KJ_FAIL_REQUIRE("Unknown expression type.");
|
||||
}
|
||||
}
|
||||
|
||||
class ValueImpl final: public Calculator::Value::Server {
|
||||
// Simple implementation of the Calculator.Value Cap'n Proto interface.
|
||||
|
||||
public:
|
||||
ValueImpl(double value): value(value) {}
|
||||
|
||||
kj::Promise<void> read(ReadContext context) {
|
||||
context.getResults().setValue(value);
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
private:
|
||||
double value;
|
||||
};
|
||||
|
||||
class FunctionImpl final: public Calculator::Function::Server {
|
||||
// Implementation of the Calculator.Function Cap'n Proto interface, where the
|
||||
// function is defined by a Calculator.Expression.
|
||||
|
||||
public:
|
||||
FunctionImpl(uint paramCount, Calculator::Expression::Reader body)
|
||||
: paramCount(paramCount) {
|
||||
this->body.setRoot(body);
|
||||
}
|
||||
|
||||
kj::Promise<void> call(CallContext context) {
|
||||
auto params = context.getParams().getParams();
|
||||
KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");
|
||||
|
||||
return evaluateImpl(body.getRoot<Calculator::Expression>(), params)
|
||||
.then([KJ_CPCAP(context)](double value) mutable {
|
||||
context.getResults().setValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
uint paramCount;
|
||||
// The function's arity.
|
||||
|
||||
capnp::MallocMessageBuilder body;
|
||||
// Stores a permanent copy of the function body.
|
||||
};
|
||||
|
||||
class OperatorImpl final: public Calculator::Function::Server {
|
||||
// Implementation of the Calculator.Function Cap'n Proto interface, wrapping
|
||||
// basic binary arithmetic operators.
|
||||
|
||||
public:
|
||||
OperatorImpl(Calculator::Operator op): op(op) {}
|
||||
|
||||
kj::Promise<void> call(CallContext context) {
|
||||
auto params = context.getParams().getParams();
|
||||
KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
|
||||
|
||||
double result;
|
||||
switch (op) {
|
||||
case Calculator::Operator::ADD: result = params[0] + params[1]; break;
|
||||
case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break;
|
||||
case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break;
|
||||
case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break;
|
||||
default:
|
||||
KJ_FAIL_REQUIRE("Unknown operator.");
|
||||
}
|
||||
|
||||
context.getResults().setValue(result);
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
private:
|
||||
Calculator::Operator op;
|
||||
};
|
||||
|
||||
class CalculatorImpl final: public Calculator::Server {
|
||||
// Implementation of the Calculator Cap'n Proto interface.
|
||||
|
||||
public:
|
||||
kj::Promise<void> evaluate(EvaluateContext context) override {
|
||||
return evaluateImpl(context.getParams().getExpression())
|
||||
.then([KJ_CPCAP(context)](double value) mutable {
|
||||
context.getResults().setValue(kj::heap<ValueImpl>(value));
|
||||
});
|
||||
}
|
||||
|
||||
kj::Promise<void> defFunction(DefFunctionContext context) override {
|
||||
auto params = context.getParams();
|
||||
context.getResults().setFunc(kj::heap<FunctionImpl>(
|
||||
params.getParamCount(), params.getBody()));
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
kj::Promise<void> getOperator(GetOperatorContext context) override {
|
||||
context.getResults().setFunc(kj::heap<OperatorImpl>(
|
||||
context.getParams().getOp()));
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n"
|
||||
"Runs the server bound to the given address/port.\n"
|
||||
"ADDRESS may be '*' to bind to all local addresses.\n"
|
||||
":PORT may be omitted to choose a port automatically." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set up a server.
|
||||
capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]);
|
||||
|
||||
// Write the port number to stdout, in case it was chosen automatically.
|
||||
auto& waitScope = server.getWaitScope();
|
||||
uint port = server.getPort().wait(waitScope);
|
||||
if (port == 0) {
|
||||
// The address format "unix:/path/to/socket" opens a unix domain socket,
|
||||
// in which case the port will be zero.
|
||||
std::cout << "Listening on Unix socket..." << std::endl;
|
||||
} else {
|
||||
std::cout << "Listening on port " << port << "..." << std::endl;
|
||||
}
|
||||
|
||||
// Run forever, accepting connections and handling requests.
|
||||
kj::NEVER_DONE.wait(waitScope);
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
# 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.
|
||||
|
||||
@0x85150b117366d14b;
|
||||
|
||||
interface Calculator {
|
||||
# A "simple" mathematical calculator, callable via RPC.
|
||||
#
|
||||
# But, to show off Cap'n Proto, we add some twists:
|
||||
#
|
||||
# - You can use the result from one call as the input to the next
|
||||
# without a network round trip. To accomplish this, evaluate()
|
||||
# returns a `Value` object wrapping the actual numeric value.
|
||||
# This object may be used in a subsequent expression. With
|
||||
# promise pipelining, the Value can actually be used before
|
||||
# the evaluate() call that creates it returns!
|
||||
#
|
||||
# - You can define new functions, and then call them. This again
|
||||
# shows off pipelining, but it also gives the client the
|
||||
# opportunity to define a function on the client side and have
|
||||
# the server call back to it.
|
||||
#
|
||||
# - The basic arithmetic operators are exposed as Functions, and
|
||||
# you have to call getOperator() to obtain them from the server.
|
||||
# This again demonstrates pipelining -- using getOperator() to
|
||||
# get each operator and then using them in evaluate() still
|
||||
# only takes one network round trip.
|
||||
|
||||
evaluate @0 (expression :Expression) -> (value :Value);
|
||||
# Evaluate the given expression and return the result. The
|
||||
# result is returned wrapped in a Value interface so that you
|
||||
# may pass it back to the server in a pipelined request. To
|
||||
# actually get the numeric value, you must call read() on the
|
||||
# Value -- but again, this can be pipelined so that it incurs
|
||||
# no additional latency.
|
||||
|
||||
struct Expression {
|
||||
# A numeric expression.
|
||||
|
||||
union {
|
||||
literal @0 :Float64;
|
||||
# A literal numeric value.
|
||||
|
||||
previousResult @1 :Value;
|
||||
# A value that was (or, will be) returned by a previous
|
||||
# evaluate().
|
||||
|
||||
parameter @2 :UInt32;
|
||||
# A parameter to the function (only valid in function bodies;
|
||||
# see defFunction).
|
||||
|
||||
call :group {
|
||||
# Call a function on a list of parameters.
|
||||
function @3 :Function;
|
||||
params @4 :List(Expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Value {
|
||||
# Wraps a numeric value in an RPC object. This allows the value
|
||||
# to be used in subsequent evaluate() requests without the client
|
||||
# waiting for the evaluate() that returns the Value to finish.
|
||||
|
||||
read @0 () -> (value :Float64);
|
||||
# Read back the raw numeric value.
|
||||
}
|
||||
|
||||
defFunction @1 (paramCount :Int32, body :Expression)
|
||||
-> (func :Function);
|
||||
# Define a function that takes `paramCount` parameters and returns the
|
||||
# evaluation of `body` after substituting these parameters.
|
||||
|
||||
interface Function {
|
||||
# An algebraic function. Can be called directly, or can be used inside
|
||||
# an Expression.
|
||||
#
|
||||
# A client can create a Function that runs on the server side using
|
||||
# `defFunction()` or `getOperator()`. Alternatively, a client can
|
||||
# implement a Function on the client side and the server will call back
|
||||
# to it. However, a function defined on the client side will require a
|
||||
# network round trip whenever the server needs to call it, whereas
|
||||
# functions defined on the server and then passed back to it are called
|
||||
# locally.
|
||||
|
||||
call @0 (params :List(Float64)) -> (value :Float64);
|
||||
# Call the function on the given parameters.
|
||||
}
|
||||
|
||||
getOperator @2 (op :Operator) -> (func :Function);
|
||||
# Get a Function representing an arithmetic operator, which can then be
|
||||
# used in Expressions.
|
||||
|
||||
enum Operator {
|
||||
add @0;
|
||||
subtract @1;
|
||||
multiply @2;
|
||||
divide @3;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#! /usr/bin/env bash
|
||||
#
|
||||
# Quick script that compiles and runs the samples, then cleans up.
|
||||
# Used for release testing.
|
||||
|
||||
set -exuo pipefail
|
||||
|
||||
capnpc -oc++ addressbook.capnp
|
||||
c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ \
|
||||
$(pkg-config --cflags --libs capnp) -o addressbook
|
||||
./addressbook write | ./addressbook read
|
||||
./addressbook dwrite | ./addressbook dread
|
||||
rm addressbook addressbook.capnp.c++ addressbook.capnp.h
|
||||
|
||||
capnpc -oc++ calculator.capnp
|
||||
c++ -std=c++14 -Wall calculator-client.c++ calculator.capnp.c++ \
|
||||
$(pkg-config --cflags --libs capnp-rpc) -o calculator-client
|
||||
c++ -std=c++14 -Wall calculator-server.c++ calculator.capnp.c++ \
|
||||
$(pkg-config --cflags --libs capnp-rpc) -o calculator-server
|
||||
rm -f /tmp/capnp-calculator-example-$$
|
||||
./calculator-server unix:/tmp/capnp-calculator-example-$$ &
|
||||
sleep 0.1
|
||||
./calculator-client unix:/tmp/capnp-calculator-example-$$
|
||||
kill %+
|
||||
wait %+ || true
|
||||
rm calculator-client calculator-server calculator.capnp.c++ calculator.capnp.h /tmp/capnp-calculator-example-$$
|
|
@ -0,0 +1,6 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "This script is no longer needed. Go ahead and run autoreconf. For example:"
|
||||
echo " autoreconf -i && ./configure && make -j6 check && sudo make install"
|
|
@ -0,0 +1,94 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if ! uname | grep -iq Linux; then
|
||||
echo "Sorry, Ekam only works on Linux right now." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Looking for compiler... "
|
||||
if [ "x${CXX:-}" == "x" ]; then
|
||||
if ! (g++ --version | grep -q ' 4[.][789][.]'); then
|
||||
if which g++-4.7 > /dev/null; then
|
||||
CXX=g++-4.7
|
||||
elif which g++-4.8 > /dev/null; then
|
||||
CXX=g++-4.8
|
||||
else
|
||||
echo "none"
|
||||
echo "Please install G++ 4.7 or better. Or, set the environment variable CXX " >&2
|
||||
echo "to a compiler that you think will work." >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
CXX=g++
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$CXX"
|
||||
export CXX
|
||||
|
||||
if [ ! -e .ekam ]; then
|
||||
echo "================================================================================"
|
||||
echo "Fetching Ekam and Protobuf code..."
|
||||
echo "================================================================================"
|
||||
hg clone https://code.google.com/p/kentons-code/ .ekam
|
||||
|
||||
# You don't want these.
|
||||
rm -rf .ekam/src/modc .ekam/src/evlan
|
||||
fi
|
||||
|
||||
if [ ! -e .ekam/src/protobuf ]; then
|
||||
echo "================================================================================"
|
||||
echo "Fetching Protobuf code..."
|
||||
echo "================================================================================"
|
||||
svn checkout http://protobuf.googlecode.com/svn/tags/2.5.0/ .ekam/src/protobuf
|
||||
fi
|
||||
|
||||
if [ ! -e .ekam/src/protobuf/src/config.h ]; then
|
||||
echo "================================================================================"
|
||||
echo "Configuring Protobuf..."
|
||||
echo "================================================================================"
|
||||
pushd .ekam/src/protobuf > /dev/null
|
||||
./autogen.sh
|
||||
./configure
|
||||
cp config.h src
|
||||
make maintainer-clean
|
||||
popd
|
||||
fi
|
||||
|
||||
if ! which ekam > /dev/null; then
|
||||
if [ ! -e .ekam/bin/ekam ]; then
|
||||
echo "================================================================================"
|
||||
echo "Bootstrapping Ekam..."
|
||||
echo "================================================================================"
|
||||
pushd .ekam > /dev/null
|
||||
./bootstrap.sh
|
||||
popd
|
||||
fi
|
||||
else
|
||||
echo "================================================================================"
|
||||
echo "Using already-installed ekam binary: $(which ekam)"
|
||||
echo "================================================================================"
|
||||
fi
|
||||
|
||||
if [ ! -e src/base ]; then
|
||||
ln -s ../.ekam/src/base src/base
|
||||
fi
|
||||
if [ ! -e src/os ]; then
|
||||
ln -s ../.ekam/src/os src/os
|
||||
fi
|
||||
if [ ! -e src/ekam ]; then
|
||||
ln -s ../.ekam/src/ekam src/ekam
|
||||
fi
|
||||
if [ ! -e src/protobuf ]; then
|
||||
ln -s ../.ekam/src/protobuf src/protobuf
|
||||
fi
|
||||
|
||||
echo "================================================================================"
|
||||
echo "All done..."
|
||||
echo "================================================================================"
|
||||
echo "Try:"
|
||||
echo " make -f Makefile.ekam once"
|
||||
echo " make -f Makefile.ekam continuous"
|
||||
echo " make -f Makefile.ekam continuous-opt"
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
# Tests ========================================================================
|
||||
|
||||
if(BUILD_TESTING)
|
||||
include(CTest)
|
||||
|
||||
if(EXTERNAL_CAPNP)
|
||||
# Setup CAPNP_GENERATE_CPP for compiling test schemas
|
||||
find_package(CapnProto CONFIG QUIET)
|
||||
if(NOT CapnProto_FOUND)
|
||||
# No working installation of Cap'n Proto found, so fall back to searching the environment.
|
||||
#
|
||||
# We search for the external capnp compiler binaries via $CAPNP, $CAPNPC_CXX, and
|
||||
# find_program(). find_program() will use various paths in its search, among them
|
||||
# ${CMAKE_PREFIX_PATH}/bin and $PATH.
|
||||
|
||||
if(NOT CAPNP_EXECUTABLE)
|
||||
if(DEFINED ENV{CAPNP})
|
||||
set(CAPNP_EXECUTABLE "$ENV{CAPNP}")
|
||||
else()
|
||||
find_program(CAPNP_EXECUTABLE capnp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT CAPNPC_CXX_EXECUTABLE)
|
||||
if(DEFINED ENV{CAPNPC_CXX})
|
||||
set(CAPNPC_CXX_EXECUTABLE "$ENV{CAPNPC_CXX}")
|
||||
else()
|
||||
# Also search in the same directory that `capnp` was found in
|
||||
get_filename_component(capnp_dir "${CAPNP_EXECUTABLE}" DIRECTORY)
|
||||
find_program(CAPNPC_CXX_EXECUTABLE capnpc-c++ HINTS "${capnp_dir}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CAPNP_INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
#TODO(someday) It would be nice to use targets instead of variables in CAPNP_GENERATE_CPP macro
|
||||
endif()
|
||||
|
||||
# Sadly, we can't use the 'test' target, as that's coopted by ctest
|
||||
add_custom_target(check "${CMAKE_CTEST_COMMAND}" -V)
|
||||
endif() # BUILD_TESTING
|
||||
|
||||
# kj ===========================================================================
|
||||
|
||||
add_subdirectory(kj)
|
||||
|
||||
# capnp ========================================================================
|
||||
|
||||
add_subdirectory(capnp)
|
|
@ -0,0 +1,139 @@
|
|||
// 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 "carsales.capnp.h"
|
||||
#include "capnproto-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace capnp {
|
||||
|
||||
template <typename ReaderOrBuilder>
|
||||
uint64_t carValue(ReaderOrBuilder car) {
|
||||
// Do not think too hard about realism.
|
||||
|
||||
uint64_t result = 0;
|
||||
|
||||
result += car.getSeats() * 200;
|
||||
result += car.getDoors() * 350;
|
||||
for (auto wheel: car.getWheels()) {
|
||||
result += wheel.getDiameter() * wheel.getDiameter();
|
||||
result += wheel.getSnowTires() ? 100 : 0;
|
||||
}
|
||||
|
||||
result += car.getLength() * car.getWidth() * car.getHeight() / 50;
|
||||
|
||||
auto engine = car.getEngine();
|
||||
result += engine.getHorsepower() * 40;
|
||||
if (engine.getUsesElectric()) {
|
||||
if (engine.getUsesGas()) {
|
||||
// hybrid
|
||||
result += 5000;
|
||||
} else {
|
||||
result += 3000;
|
||||
}
|
||||
}
|
||||
|
||||
result += car.getHasPowerWindows() ? 100 : 0;
|
||||
result += car.getHasPowerSteering() ? 200 : 0;
|
||||
result += car.getHasCruiseControl() ? 400 : 0;
|
||||
result += car.getHasNavSystem() ? 2000 : 0;
|
||||
|
||||
result += car.getCupHolders() * 25;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void randomCar(Car::Builder car) {
|
||||
// Do not think too hard about realism.
|
||||
|
||||
static const char* const MAKES[] = { "Toyota", "GM", "Ford", "Honda", "Tesla" };
|
||||
static const char* const MODELS[] = { "Camry", "Prius", "Volt", "Accord", "Leaf", "Model S" };
|
||||
|
||||
car.setMake(MAKES[fastRand(sizeof(MAKES) / sizeof(MAKES[0]))]);
|
||||
car.setModel(MODELS[fastRand(sizeof(MODELS) / sizeof(MODELS[0]))]);
|
||||
|
||||
car.setColor((Color)fastRand((uint)Color::SILVER + 1));
|
||||
car.setSeats(2 + fastRand(6));
|
||||
car.setDoors(2 + fastRand(3));
|
||||
|
||||
for (auto wheel: car.initWheels(4)) {
|
||||
wheel.setDiameter(25 + fastRand(15));
|
||||
wheel.setAirPressure(30 + fastRandDouble(20));
|
||||
wheel.setSnowTires(fastRand(16) == 0);
|
||||
}
|
||||
|
||||
car.setLength(170 + fastRand(150));
|
||||
car.setWidth(48 + fastRand(36));
|
||||
car.setHeight(54 + fastRand(48));
|
||||
car.setWeight(car.getLength() * car.getWidth() * car.getHeight() / 200);
|
||||
|
||||
auto engine = car.initEngine();
|
||||
engine.setHorsepower(100 * fastRand(400));
|
||||
engine.setCylinders(4 + 2 * fastRand(3));
|
||||
engine.setCc(800 + fastRand(10000));
|
||||
engine.setUsesGas(true);
|
||||
engine.setUsesElectric(fastRand(2));
|
||||
|
||||
car.setFuelCapacity(10.0 + fastRandDouble(30.0));
|
||||
car.setFuelLevel(fastRandDouble(car.getFuelCapacity()));
|
||||
car.setHasPowerWindows(fastRand(2));
|
||||
car.setHasPowerSteering(fastRand(2));
|
||||
car.setHasCruiseControl(fastRand(2));
|
||||
car.setCupHolders(fastRand(12));
|
||||
car.setHasNavSystem(fastRand(2));
|
||||
}
|
||||
|
||||
class CarSalesTestCase {
|
||||
public:
|
||||
typedef ParkingLot Request;
|
||||
typedef TotalValue Response;
|
||||
typedef uint64_t Expectation;
|
||||
|
||||
static uint64_t setupRequest(ParkingLot::Builder request) {
|
||||
uint64_t result = 0;
|
||||
for (auto car: request.initCars(fastRand(200))) {
|
||||
randomCar(car);
|
||||
result += carValue(car);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static void handleRequest(ParkingLot::Reader request, TotalValue::Builder response) {
|
||||
uint64_t result = 0;
|
||||
for (auto car: request.getCars()) {
|
||||
result += carValue(car);
|
||||
}
|
||||
response.setAmount(result);
|
||||
}
|
||||
static inline bool checkResponse(TotalValue::Reader response, uint64_t expected) {
|
||||
return response.getAmount() == expected;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace capnp
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::capnp::BenchmarkTypes,
|
||||
capnp::benchmark::capnp::CarSalesTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
// 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 "catrank.capnp.h"
|
||||
#include "capnproto-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace capnp {
|
||||
|
||||
struct ScoredResult {
|
||||
double score;
|
||||
SearchResult::Reader result;
|
||||
|
||||
ScoredResult() = default;
|
||||
ScoredResult(double score, SearchResult::Reader result): score(score), result(result) {}
|
||||
|
||||
inline bool operator<(const ScoredResult& other) const { return score > other.score; }
|
||||
};
|
||||
|
||||
class CatRankTestCase {
|
||||
public:
|
||||
typedef SearchResultList Request;
|
||||
typedef SearchResultList Response;
|
||||
typedef int Expectation;
|
||||
|
||||
static int setupRequest(SearchResultList::Builder request) {
|
||||
int count = fastRand(1000);
|
||||
int goodCount = 0;
|
||||
|
||||
auto list = request.initResults(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
SearchResult::Builder result = list[i];
|
||||
result.setScore(1000 - i);
|
||||
int urlSize = fastRand(100);
|
||||
|
||||
static const char URL_PREFIX[] = "http://example.com/";
|
||||
size_t urlPrefixLength = strlen(URL_PREFIX);
|
||||
auto url = result.initUrl(urlSize + urlPrefixLength);
|
||||
|
||||
strcpy(url.begin(), URL_PREFIX);
|
||||
char* pos = url.begin() + urlPrefixLength;
|
||||
for (int j = 0; j < urlSize; j++) {
|
||||
*pos++ = 'a' + fastRand(26);
|
||||
}
|
||||
|
||||
bool isCat = fastRand(8) == 0;
|
||||
bool isDog = fastRand(8) == 0;
|
||||
goodCount += isCat && !isDog;
|
||||
|
||||
static std::string snippet;
|
||||
snippet.clear();
|
||||
snippet.push_back(' ');
|
||||
|
||||
int prefix = fastRand(20);
|
||||
for (int j = 0; j < prefix; j++) {
|
||||
snippet.append(WORDS[fastRand(WORDS_COUNT)]);
|
||||
}
|
||||
|
||||
if (isCat) snippet.append("cat ");
|
||||
if (isDog) snippet.append("dog ");
|
||||
|
||||
int suffix = fastRand(20);
|
||||
for (int j = 0; j < suffix; j++) {
|
||||
snippet.append(WORDS[fastRand(WORDS_COUNT)]);
|
||||
}
|
||||
|
||||
result.setSnippet(Text::Reader(snippet.c_str(), snippet.size()));
|
||||
}
|
||||
|
||||
return goodCount;
|
||||
}
|
||||
|
||||
static void handleRequest(SearchResultList::Reader request, SearchResultList::Builder response) {
|
||||
std::vector<ScoredResult> scoredResults;
|
||||
|
||||
for (auto result: request.getResults()) {
|
||||
double score = result.getScore();
|
||||
if (strstr(result.getSnippet().cStr(), " cat ") != nullptr) {
|
||||
score *= 10000;
|
||||
}
|
||||
if (strstr(result.getSnippet().cStr(), " dog ") != nullptr) {
|
||||
score /= 10000;
|
||||
}
|
||||
scoredResults.emplace_back(score, result);
|
||||
}
|
||||
|
||||
std::sort(scoredResults.begin(), scoredResults.end());
|
||||
|
||||
auto list = response.initResults(scoredResults.size());
|
||||
auto iter = list.begin();
|
||||
for (auto result: scoredResults) {
|
||||
iter->setScore(result.score);
|
||||
iter->setUrl(result.result.getUrl());
|
||||
iter->setSnippet(result.result.getSnippet());
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkResponse(SearchResultList::Reader response, int expectedGoodCount) {
|
||||
int goodCount = 0;
|
||||
for (auto result: response.getResults()) {
|
||||
if (result.getScore() > 1001) {
|
||||
++goodCount;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return goodCount == expectedGoodCount;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace capnp
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::capnp::BenchmarkTypes,
|
||||
capnp::benchmark::capnp::CatRankTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
// 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
|
||||
|
||||
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include <capnp/serialize.h>
|
||||
#include <capnp/serialize-packed.h>
|
||||
#include <kj/debug.h>
|
||||
#if HAVE_SNAPPY
|
||||
#include <capnp/serialize-snappy.h>
|
||||
#endif // HAVE_SNAPPY
|
||||
#include <thread>
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace capnp {
|
||||
|
||||
class CountingOutputStream: public kj::FdOutputStream {
|
||||
public:
|
||||
CountingOutputStream(int fd): FdOutputStream(fd), throughput(0) {}
|
||||
|
||||
uint64_t throughput;
|
||||
|
||||
void write(const void* buffer, size_t size) override {
|
||||
FdOutputStream::write(buffer, size);
|
||||
throughput += size;
|
||||
}
|
||||
|
||||
void write(kj::ArrayPtr<const kj::ArrayPtr<const byte>> pieces) override {
|
||||
FdOutputStream::write(pieces);
|
||||
for (auto& piece: pieces) {
|
||||
throughput += piece.size();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
struct Uncompressed {
|
||||
typedef kj::FdInputStream& BufferedInput;
|
||||
typedef InputStreamMessageReader MessageReader;
|
||||
|
||||
class ArrayMessageReader: public FlatArrayMessageReader {
|
||||
public:
|
||||
ArrayMessageReader(kj::ArrayPtr<const byte> array,
|
||||
ReaderOptions options = ReaderOptions(),
|
||||
kj::ArrayPtr<word> scratchSpace = nullptr)
|
||||
: FlatArrayMessageReader(kj::arrayPtr(
|
||||
reinterpret_cast<const word*>(array.begin()),
|
||||
reinterpret_cast<const word*>(array.end())), options) {}
|
||||
};
|
||||
|
||||
static inline void write(kj::OutputStream& output, MessageBuilder& builder) {
|
||||
writeMessage(output, builder);
|
||||
}
|
||||
};
|
||||
|
||||
struct Packed {
|
||||
typedef kj::BufferedInputStreamWrapper BufferedInput;
|
||||
typedef PackedMessageReader MessageReader;
|
||||
|
||||
class ArrayMessageReader: private kj::ArrayInputStream, public PackedMessageReader {
|
||||
public:
|
||||
ArrayMessageReader(kj::ArrayPtr<const byte> array,
|
||||
ReaderOptions options = ReaderOptions(),
|
||||
kj::ArrayPtr<word> scratchSpace = nullptr)
|
||||
: ArrayInputStream(array),
|
||||
PackedMessageReader(*this, options, scratchSpace) {}
|
||||
};
|
||||
|
||||
static inline void write(kj::OutputStream& output, MessageBuilder& builder) {
|
||||
writePackedMessage(output, builder);
|
||||
}
|
||||
|
||||
static inline void write(kj::BufferedOutputStream& output, MessageBuilder& builder) {
|
||||
writePackedMessage(output, builder);
|
||||
}
|
||||
};
|
||||
|
||||
#if HAVE_SNAPPY
|
||||
static byte snappyReadBuffer[SNAPPY_BUFFER_SIZE];
|
||||
static byte snappyWriteBuffer[SNAPPY_BUFFER_SIZE];
|
||||
static byte snappyCompressedBuffer[SNAPPY_COMPRESSED_BUFFER_SIZE];
|
||||
|
||||
struct SnappyCompressed {
|
||||
typedef BufferedInputStreamWrapper BufferedInput;
|
||||
typedef SnappyPackedMessageReader MessageReader;
|
||||
|
||||
class ArrayMessageReader: private ArrayInputStream, public SnappyPackedMessageReader {
|
||||
public:
|
||||
ArrayMessageReader(kj::ArrayPtr<const byte> array,
|
||||
ReaderOptions options = ReaderOptions(),
|
||||
kj::ArrayPtr<word> scratchSpace = nullptr)
|
||||
: ArrayInputStream(array),
|
||||
SnappyPackedMessageReader(static_cast<ArrayInputStream&>(*this), options, scratchSpace,
|
||||
kj::arrayPtr(snappyReadBuffer, SNAPPY_BUFFER_SIZE)) {}
|
||||
};
|
||||
|
||||
static inline void write(OutputStream& output, MessageBuilder& builder) {
|
||||
writeSnappyPackedMessage(output, builder,
|
||||
kj::arrayPtr(snappyWriteBuffer, SNAPPY_BUFFER_SIZE),
|
||||
kj::arrayPtr(snappyCompressedBuffer, SNAPPY_COMPRESSED_BUFFER_SIZE));
|
||||
}
|
||||
};
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
struct NoScratch {
|
||||
struct ScratchSpace {};
|
||||
|
||||
template <typename Compression>
|
||||
class MessageReader: public Compression::MessageReader {
|
||||
public:
|
||||
inline MessageReader(typename Compression::BufferedInput& input, ScratchSpace& scratch)
|
||||
: Compression::MessageReader(input) {}
|
||||
};
|
||||
|
||||
template <typename Compression>
|
||||
class ArrayMessageReader: public Compression::ArrayMessageReader {
|
||||
public:
|
||||
inline ArrayMessageReader(kj::ArrayPtr<const byte> input, ScratchSpace& scratch)
|
||||
: Compression::ArrayMessageReader(input) {}
|
||||
};
|
||||
|
||||
class MessageBuilder: public MallocMessageBuilder {
|
||||
public:
|
||||
inline MessageBuilder(ScratchSpace& scratch): MallocMessageBuilder() {}
|
||||
};
|
||||
|
||||
class ObjectSizeCounter {
|
||||
public:
|
||||
ObjectSizeCounter(uint64_t iters): counter(0) {}
|
||||
|
||||
template <typename RequestBuilder, typename ResponseBuilder>
|
||||
void add(RequestBuilder& request, ResponseBuilder& response) {
|
||||
for (auto segment: request.getSegmentsForOutput()) {
|
||||
counter += segment.size() * sizeof(word);
|
||||
}
|
||||
for (auto segment: response.getSegmentsForOutput()) {
|
||||
counter += segment.size() * sizeof(word);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t get() { return counter; }
|
||||
|
||||
private:
|
||||
uint64_t counter;
|
||||
};
|
||||
};
|
||||
|
||||
constexpr size_t SCRATCH_SIZE = 128 * 1024;
|
||||
word scratchSpace[6 * SCRATCH_SIZE];
|
||||
int scratchCounter = 0;
|
||||
|
||||
struct UseScratch {
|
||||
struct ScratchSpace {
|
||||
word* words;
|
||||
|
||||
ScratchSpace() {
|
||||
KJ_REQUIRE(scratchCounter < 6, "Too many scratch spaces needed at once.");
|
||||
words = scratchSpace + scratchCounter++ * SCRATCH_SIZE;
|
||||
}
|
||||
~ScratchSpace() noexcept {
|
||||
--scratchCounter;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Compression>
|
||||
class MessageReader: public Compression::MessageReader {
|
||||
public:
|
||||
inline MessageReader(typename Compression::BufferedInput& input, ScratchSpace& scratch)
|
||||
: Compression::MessageReader(
|
||||
input, ReaderOptions(), kj::arrayPtr(scratch.words, SCRATCH_SIZE)) {}
|
||||
};
|
||||
|
||||
template <typename Compression>
|
||||
class ArrayMessageReader: public Compression::ArrayMessageReader {
|
||||
public:
|
||||
inline ArrayMessageReader(kj::ArrayPtr<const byte> input, ScratchSpace& scratch)
|
||||
: Compression::ArrayMessageReader(
|
||||
input, ReaderOptions(), kj::arrayPtr(scratch.words, SCRATCH_SIZE)) {}
|
||||
};
|
||||
|
||||
class MessageBuilder: public MallocMessageBuilder {
|
||||
public:
|
||||
inline MessageBuilder(ScratchSpace& scratch)
|
||||
: MallocMessageBuilder(kj::arrayPtr(scratch.words, SCRATCH_SIZE)) {}
|
||||
};
|
||||
|
||||
class ObjectSizeCounter {
|
||||
public:
|
||||
ObjectSizeCounter(uint64_t iters): iters(iters), maxSize(0) {}
|
||||
|
||||
template <typename RequestBuilder, typename ResponseBuilder>
|
||||
void add(RequestBuilder& request, ResponseBuilder& response) {
|
||||
size_t counter = 0;
|
||||
for (auto segment: request.getSegmentsForOutput()) {
|
||||
counter += segment.size() * sizeof(word);
|
||||
}
|
||||
for (auto segment: response.getSegmentsForOutput()) {
|
||||
counter += segment.size() * sizeof(word);
|
||||
}
|
||||
maxSize = std::max(counter, maxSize);
|
||||
}
|
||||
|
||||
uint64_t get() { return iters * maxSize; }
|
||||
|
||||
private:
|
||||
uint64_t iters;
|
||||
size_t maxSize;
|
||||
};
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
template <typename TestCase, typename ReuseStrategy, typename Compression>
|
||||
struct BenchmarkMethods {
|
||||
static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
|
||||
kj::FdInputStream inputStream(inputFd);
|
||||
typename Compression::BufferedInput bufferedInput(inputStream);
|
||||
|
||||
CountingOutputStream output(outputFd);
|
||||
typename ReuseStrategy::ScratchSpace builderScratch;
|
||||
typename ReuseStrategy::ScratchSpace readerScratch;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename TestCase::Expectation expected;
|
||||
{
|
||||
typename ReuseStrategy::MessageBuilder builder(builderScratch);
|
||||
expected = TestCase::setupRequest(
|
||||
builder.template initRoot<typename TestCase::Request>());
|
||||
Compression::write(output, builder);
|
||||
}
|
||||
|
||||
{
|
||||
typename ReuseStrategy::template MessageReader<Compression> reader(
|
||||
bufferedInput, readerScratch);
|
||||
if (!TestCase::checkResponse(
|
||||
reader.template getRoot<typename TestCase::Response>(), expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.throughput;
|
||||
}
|
||||
|
||||
static uint64_t asyncClientSender(
|
||||
int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
|
||||
uint64_t iters) {
|
||||
CountingOutputStream output(outputFd);
|
||||
typename ReuseStrategy::ScratchSpace scratch;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename ReuseStrategy::MessageBuilder builder(scratch);
|
||||
expectations->post(TestCase::setupRequest(
|
||||
builder.template initRoot<typename TestCase::Request>()));
|
||||
Compression::write(output, builder);
|
||||
}
|
||||
|
||||
return output.throughput;
|
||||
}
|
||||
|
||||
static void asyncClientReceiver(
|
||||
int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
|
||||
uint64_t iters) {
|
||||
kj::FdInputStream inputStream(inputFd);
|
||||
typename Compression::BufferedInput bufferedInput(inputStream);
|
||||
|
||||
typename ReuseStrategy::ScratchSpace scratch;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename TestCase::Expectation expected = expectations->next();
|
||||
typename ReuseStrategy::template MessageReader<Compression> reader(bufferedInput, scratch);
|
||||
if (!TestCase::checkResponse(
|
||||
reader.template getRoot<typename TestCase::Response>(), expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
|
||||
ProducerConsumerQueue<typename TestCase::Expectation> expectations;
|
||||
std::thread receiverThread(asyncClientReceiver, inputFd, &expectations, iters);
|
||||
uint64_t throughput = asyncClientSender(outputFd, &expectations, iters);
|
||||
receiverThread.join();
|
||||
return throughput;
|
||||
}
|
||||
|
||||
static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
|
||||
kj::FdInputStream inputStream(inputFd);
|
||||
typename Compression::BufferedInput bufferedInput(inputStream);
|
||||
|
||||
CountingOutputStream output(outputFd);
|
||||
typename ReuseStrategy::ScratchSpace builderScratch;
|
||||
typename ReuseStrategy::ScratchSpace readerScratch;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename ReuseStrategy::MessageBuilder builder(builderScratch);
|
||||
typename ReuseStrategy::template MessageReader<Compression> reader(
|
||||
bufferedInput, readerScratch);
|
||||
TestCase::handleRequest(reader.template getRoot<typename TestCase::Request>(),
|
||||
builder.template initRoot<typename TestCase::Response>());
|
||||
Compression::write(output, builder);
|
||||
}
|
||||
|
||||
return output.throughput;
|
||||
}
|
||||
|
||||
static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
|
||||
typename ReuseStrategy::ScratchSpace requestScratch;
|
||||
typename ReuseStrategy::ScratchSpace responseScratch;
|
||||
|
||||
typename ReuseStrategy::ObjectSizeCounter counter(iters);
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename ReuseStrategy::MessageBuilder requestMessage(requestScratch);
|
||||
auto request = requestMessage.template initRoot<typename TestCase::Request>();
|
||||
typename TestCase::Expectation expected = TestCase::setupRequest(request);
|
||||
|
||||
typename ReuseStrategy::MessageBuilder responseMessage(responseScratch);
|
||||
auto response = responseMessage.template initRoot<typename TestCase::Response>();
|
||||
TestCase::handleRequest(request.asReader(), response);
|
||||
|
||||
if (!TestCase::checkResponse(response.asReader(), expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
|
||||
if (countObjectSize) {
|
||||
counter.add(requestMessage, responseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return counter.get();
|
||||
}
|
||||
|
||||
static uint64_t passByBytes(uint64_t iters) {
|
||||
uint64_t throughput = 0;
|
||||
typename ReuseStrategy::ScratchSpace clientRequestScratch;
|
||||
UseScratch::ScratchSpace requestBytesScratch;
|
||||
typename ReuseStrategy::ScratchSpace serverRequestScratch;
|
||||
typename ReuseStrategy::ScratchSpace serverResponseScratch;
|
||||
UseScratch::ScratchSpace responseBytesScratch;
|
||||
typename ReuseStrategy::ScratchSpace clientResponseScratch;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename ReuseStrategy::MessageBuilder requestBuilder(clientRequestScratch);
|
||||
typename TestCase::Expectation expected = TestCase::setupRequest(
|
||||
requestBuilder.template initRoot<typename TestCase::Request>());
|
||||
|
||||
kj::ArrayOutputStream requestOutput(kj::arrayPtr(
|
||||
reinterpret_cast<byte*>(requestBytesScratch.words), SCRATCH_SIZE * sizeof(word)));
|
||||
Compression::write(requestOutput, requestBuilder);
|
||||
throughput += requestOutput.getArray().size();
|
||||
typename ReuseStrategy::template ArrayMessageReader<Compression> requestReader(
|
||||
requestOutput.getArray(), serverRequestScratch);
|
||||
|
||||
typename ReuseStrategy::MessageBuilder responseBuilder(serverResponseScratch);
|
||||
TestCase::handleRequest(requestReader.template getRoot<typename TestCase::Request>(),
|
||||
responseBuilder.template initRoot<typename TestCase::Response>());
|
||||
|
||||
kj::ArrayOutputStream responseOutput(
|
||||
kj::arrayPtr(reinterpret_cast<byte*>(responseBytesScratch.words),
|
||||
SCRATCH_SIZE * sizeof(word)));
|
||||
Compression::write(responseOutput, responseBuilder);
|
||||
throughput += responseOutput.getArray().size();
|
||||
typename ReuseStrategy::template ArrayMessageReader<Compression> responseReader(
|
||||
responseOutput.getArray(), clientResponseScratch);
|
||||
|
||||
if (!TestCase::checkResponse(
|
||||
responseReader.template getRoot<typename TestCase::Response>(), expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
};
|
||||
|
||||
struct BenchmarkTypes {
|
||||
typedef capnp::Uncompressed Uncompressed;
|
||||
typedef capnp::Packed Packed;
|
||||
#if HAVE_SNAPPY
|
||||
typedef capnp::SnappyCompressed SnappyCompressed;
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
typedef capnp::UseScratch ReusableResources;
|
||||
typedef capnp::NoScratch SingleUseResources;
|
||||
|
||||
template <typename TestCase, typename ReuseStrategy, typename Compression>
|
||||
struct BenchmarkMethods: public capnp::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
|
||||
};
|
||||
|
||||
} // namespace capnp
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
|
@ -0,0 +1,124 @@
|
|||
// 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 "eval.capnp.h"
|
||||
#include "capnproto-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace capnp {
|
||||
|
||||
int32_t makeExpression(Expression::Builder exp, uint depth) {
|
||||
exp.setOp((Operation)(fastRand((int)Operation::MODULUS + 1)));
|
||||
|
||||
uint32_t left, right;
|
||||
|
||||
if (fastRand(8) < depth) {
|
||||
left = fastRand(128) + 1;
|
||||
exp.getLeft().setValue(left);
|
||||
} else {
|
||||
left = makeExpression(exp.getLeft().initExpression(), depth + 1);
|
||||
}
|
||||
|
||||
if (fastRand(8) < depth) {
|
||||
right = fastRand(128) + 1;
|
||||
exp.getRight().setValue(right);
|
||||
} else {
|
||||
right = makeExpression(exp.getRight().initExpression(), depth + 1);
|
||||
}
|
||||
|
||||
switch (exp.getOp()) {
|
||||
case Operation::ADD:
|
||||
return left + right;
|
||||
case Operation::SUBTRACT:
|
||||
return left - right;
|
||||
case Operation::MULTIPLY:
|
||||
return left * right;
|
||||
case Operation::DIVIDE:
|
||||
return div(left, right);
|
||||
case Operation::MODULUS:
|
||||
return mod(left, right);
|
||||
}
|
||||
throw std::logic_error("Can't get here.");
|
||||
}
|
||||
|
||||
int32_t evaluateExpression(Expression::Reader exp) {
|
||||
int32_t left = 0, right = 0;
|
||||
|
||||
switch (exp.getLeft().which()) {
|
||||
case Expression::Left::VALUE:
|
||||
left = exp.getLeft().getValue();
|
||||
break;
|
||||
case Expression::Left::EXPRESSION:
|
||||
left = evaluateExpression(exp.getLeft().getExpression());
|
||||
break;
|
||||
}
|
||||
|
||||
switch (exp.getRight().which()) {
|
||||
case Expression::Right::VALUE:
|
||||
right = exp.getRight().getValue();
|
||||
break;
|
||||
case Expression::Right::EXPRESSION:
|
||||
right = evaluateExpression(exp.getRight().getExpression());
|
||||
break;
|
||||
}
|
||||
|
||||
switch (exp.getOp()) {
|
||||
case Operation::ADD:
|
||||
return left + right;
|
||||
case Operation::SUBTRACT:
|
||||
return left - right;
|
||||
case Operation::MULTIPLY:
|
||||
return left * right;
|
||||
case Operation::DIVIDE:
|
||||
return div(left, right);
|
||||
case Operation::MODULUS:
|
||||
return mod(left, right);
|
||||
}
|
||||
throw std::logic_error("Can't get here.");
|
||||
}
|
||||
|
||||
class ExpressionTestCase {
|
||||
public:
|
||||
typedef Expression Request;
|
||||
typedef EvaluationResult Response;
|
||||
typedef int32_t Expectation;
|
||||
|
||||
static inline int32_t setupRequest(Expression::Builder request) {
|
||||
return makeExpression(request, 0);
|
||||
}
|
||||
static inline void handleRequest(Expression::Reader request, EvaluationResult::Builder response) {
|
||||
response.setValue(evaluateExpression(request));
|
||||
}
|
||||
static inline bool checkResponse(EvaluationResult::Reader response, int32_t expected) {
|
||||
return response.getValue() == expected;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace capnp
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::capnp::BenchmarkTypes,
|
||||
capnp::benchmark::capnp::ExpressionTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
# 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";
|
||||
|
||||
@0xff75ddc6a36723c9;
|
||||
$Cxx.namespace("capnp::benchmark::capnp");
|
||||
|
||||
struct ParkingLot {
|
||||
cars@0: List(Car);
|
||||
}
|
||||
|
||||
struct TotalValue {
|
||||
amount@0: UInt64;
|
||||
}
|
||||
|
||||
struct Car {
|
||||
make@0: Text;
|
||||
model@1: Text;
|
||||
color@2: Color;
|
||||
seats@3: UInt8;
|
||||
doors@4: UInt8;
|
||||
wheels@5: List(Wheel);
|
||||
length@6: UInt16;
|
||||
width@7: UInt16;
|
||||
height@8: UInt16;
|
||||
weight@9: UInt32;
|
||||
engine@10: Engine;
|
||||
fuelCapacity@11: Float32;
|
||||
fuelLevel@12: Float32;
|
||||
hasPowerWindows@13: Bool;
|
||||
hasPowerSteering@14: Bool;
|
||||
hasCruiseControl@15: Bool;
|
||||
cupHolders@16: UInt8;
|
||||
hasNavSystem@17: Bool;
|
||||
}
|
||||
|
||||
enum Color {
|
||||
black @0;
|
||||
white @1;
|
||||
red @2;
|
||||
green @3;
|
||||
blue @4;
|
||||
cyan @5;
|
||||
magenta @6;
|
||||
yellow @7;
|
||||
silver @8;
|
||||
}
|
||||
|
||||
struct Wheel {
|
||||
diameter@0: UInt16;
|
||||
airPressure@1: Float32;
|
||||
snowTires@2: Bool;
|
||||
}
|
||||
|
||||
struct Engine {
|
||||
horsepower@0: UInt16;
|
||||
cylinders@1: UInt8;
|
||||
cc@2: UInt32;
|
||||
usesGas@3: Bool;
|
||||
usesElectric@4: Bool;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// 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.
|
||||
|
||||
package capnp.benchmark.protobuf;
|
||||
|
||||
message ParkingLot {
|
||||
repeated Car car = 1;
|
||||
}
|
||||
|
||||
message TotalValue {
|
||||
required uint64 amount = 1;
|
||||
}
|
||||
|
||||
message Car {
|
||||
optional string make = 1;
|
||||
optional string model = 2;
|
||||
optional Color color = 3;
|
||||
optional uint32 seats = 4;
|
||||
optional uint32 doors = 5;
|
||||
repeated Wheel wheel = 6;
|
||||
optional uint32 length = 7;
|
||||
optional uint32 width = 8;
|
||||
optional uint32 height = 9;
|
||||
optional uint32 weight = 10;
|
||||
optional Engine engine = 11;
|
||||
optional float fuel_capacity = 12;
|
||||
optional float fuel_level = 13;
|
||||
optional bool has_power_windows = 14;
|
||||
optional bool has_power_steering = 15;
|
||||
optional bool has_cruise_control = 16;
|
||||
optional uint32 cup_holders = 17;
|
||||
optional bool has_nav_system = 18;
|
||||
}
|
||||
|
||||
enum Color {
|
||||
BLACK = 0;
|
||||
WHITE = 1;
|
||||
RED = 2;
|
||||
GREEN = 3;
|
||||
BLUE = 4;
|
||||
CYAN = 5;
|
||||
MAGENTA = 6;
|
||||
YELLOW = 7;
|
||||
SILVER = 8;
|
||||
}
|
||||
|
||||
message Wheel {
|
||||
optional uint32 diameter = 1;
|
||||
optional float air_pressure = 2;
|
||||
optional bool snow_tires = 3;
|
||||
}
|
||||
|
||||
message Engine {
|
||||
optional uint32 horsepower = 1;
|
||||
optional uint32 cylinders = 2;
|
||||
optional uint32 cc = 3;
|
||||
optional bool uses_gas = 4;
|
||||
optional bool uses_electric = 5;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
# 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";
|
||||
|
||||
@0x82beb8e37ff79aba;
|
||||
$Cxx.namespace("capnp::benchmark::capnp");
|
||||
|
||||
struct SearchResultList {
|
||||
results@0: List(SearchResult);
|
||||
}
|
||||
|
||||
struct SearchResult {
|
||||
url@0: Text;
|
||||
score@1: Float64;
|
||||
snippet@2: Text;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
package capnp.benchmark.protobuf;
|
||||
|
||||
message SearchResultList {
|
||||
repeated SearchResult result = 1;
|
||||
}
|
||||
|
||||
message SearchResult {
|
||||
optional string url = 1;
|
||||
optional double score = 2;
|
||||
optional string snippet = 3;
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
// 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
|
||||
|
||||
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include <semaphore.h>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
|
||||
// Use a 128-bit Xorshift algorithm.
|
||||
static inline uint32_t nextFastRand() {
|
||||
// These values are arbitrary. Any seed other than all zeroes is OK.
|
||||
static uint32_t x = 0x1d2acd47;
|
||||
static uint32_t y = 0x58ca3e14;
|
||||
static uint32_t z = 0xf563f232;
|
||||
static uint32_t w = 0x0bc76199;
|
||||
|
||||
uint32_t tmp = x ^ (x << 11);
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
w = w ^ (w >> 19) ^ tmp ^ (tmp >> 8);
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline uint32_t fastRand(uint32_t range) {
|
||||
return nextFastRand() % range;
|
||||
}
|
||||
|
||||
static inline double fastRandDouble(double range) {
|
||||
return nextFastRand() * range / std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
|
||||
inline int32_t div(int32_t a, int32_t b) {
|
||||
if (b == 0) return std::numeric_limits<int32_t>::max();
|
||||
// INT_MIN / -1 => SIGFPE. Who knew?
|
||||
if (a == std::numeric_limits<int32_t>::min() && b == -1) {
|
||||
return std::numeric_limits<int32_t>::max();
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
|
||||
inline int32_t mod(int32_t a, int32_t b) {
|
||||
if (b == 0) return std::numeric_limits<int32_t>::max();
|
||||
// INT_MIN % -1 => SIGFPE. Who knew?
|
||||
if (a == std::numeric_limits<int32_t>::min() && b == -1) {
|
||||
return std::numeric_limits<int32_t>::max();
|
||||
}
|
||||
return a % b;
|
||||
}
|
||||
|
||||
static const char* const WORDS[] = {
|
||||
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
|
||||
"plugh ", "xyzzy ", "thud "
|
||||
};
|
||||
constexpr size_t WORDS_COUNT = sizeof(WORDS) / sizeof(WORDS[0]);
|
||||
|
||||
template <typename T>
|
||||
class ProducerConsumerQueue {
|
||||
public:
|
||||
ProducerConsumerQueue() {
|
||||
front = new Node;
|
||||
back = front;
|
||||
sem_init(&semaphore, 0, 0);
|
||||
}
|
||||
|
||||
~ProducerConsumerQueue() noexcept(false) {
|
||||
while (front != nullptr) {
|
||||
Node* oldFront = front;
|
||||
front = front->next;
|
||||
delete oldFront;
|
||||
}
|
||||
sem_destroy(&semaphore);
|
||||
}
|
||||
|
||||
void post(T t) {
|
||||
back->next = new Node(t);
|
||||
back = back->next;
|
||||
sem_post(&semaphore);
|
||||
}
|
||||
|
||||
T next() {
|
||||
sem_wait(&semaphore);
|
||||
Node* oldFront = front;
|
||||
front = front->next;
|
||||
delete oldFront;
|
||||
return front->value;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
T value;
|
||||
Node* next;
|
||||
|
||||
Node(): next(nullptr) {}
|
||||
Node(T value): value(value), next(nullptr) {}
|
||||
};
|
||||
|
||||
Node* front; // Last node that has been consumed.
|
||||
Node* back; // Last node in list.
|
||||
sem_t semaphore;
|
||||
};
|
||||
|
||||
// TODO(cleanup): Use SYSCALL(), get rid of this exception class.
|
||||
class OsException: public std::exception {
|
||||
public:
|
||||
OsException(int error): error(error) {}
|
||||
~OsException() noexcept {}
|
||||
|
||||
const char* what() const noexcept override {
|
||||
return strerror(error);
|
||||
}
|
||||
|
||||
private:
|
||||
int error;
|
||||
};
|
||||
|
||||
static void writeAll(int fd, const void* buffer, size_t size) {
|
||||
const char* pos = reinterpret_cast<const char*>(buffer);
|
||||
while (size > 0) {
|
||||
ssize_t n = write(fd, pos, size);
|
||||
if (n <= 0) {
|
||||
throw OsException(errno);
|
||||
}
|
||||
pos += n;
|
||||
size -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static void readAll(int fd, void* buffer, size_t size) {
|
||||
char* pos = reinterpret_cast<char*>(buffer);
|
||||
while (size > 0) {
|
||||
ssize_t n = read(fd, pos, size);
|
||||
if (n <= 0) {
|
||||
throw OsException(errno);
|
||||
}
|
||||
pos += n;
|
||||
size -= n;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BenchmarkMethods, typename Func>
|
||||
uint64_t passByPipe(Func&& clientFunc, uint64_t iters) {
|
||||
int clientToServer[2];
|
||||
int serverToClient[2];
|
||||
if (pipe(clientToServer) < 0) throw OsException(errno);
|
||||
if (pipe(serverToClient) < 0) throw OsException(errno);
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
// Client.
|
||||
close(clientToServer[0]);
|
||||
close(serverToClient[1]);
|
||||
|
||||
uint64_t throughput = clientFunc(serverToClient[0], clientToServer[1], iters);
|
||||
writeAll(clientToServer[1], &throughput, sizeof(throughput));
|
||||
|
||||
exit(0);
|
||||
} else {
|
||||
// Server.
|
||||
close(clientToServer[1]);
|
||||
close(serverToClient[0]);
|
||||
|
||||
uint64_t throughput = BenchmarkMethods::server(clientToServer[0], serverToClient[1], iters);
|
||||
|
||||
uint64_t clientThroughput = 0;
|
||||
readAll(clientToServer[0], &clientThroughput, sizeof(clientThroughput));
|
||||
throughput += clientThroughput;
|
||||
|
||||
int status;
|
||||
if (waitpid(child, &status, 0) != child) {
|
||||
throw OsException(errno);
|
||||
}
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
throw std::logic_error("Child exited abnormally.");
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BenchmarkTypes, typename TestCase, typename Reuse, typename Compression>
|
||||
uint64_t doBenchmark(const std::string& mode, uint64_t iters) {
|
||||
typedef typename BenchmarkTypes::template BenchmarkMethods<TestCase, Reuse, Compression>
|
||||
BenchmarkMethods;
|
||||
if (mode == "client") {
|
||||
return BenchmarkMethods::syncClient(STDIN_FILENO, STDOUT_FILENO, iters);
|
||||
} else if (mode == "server") {
|
||||
return BenchmarkMethods::server(STDIN_FILENO, STDOUT_FILENO, iters);
|
||||
} else if (mode == "object") {
|
||||
return BenchmarkMethods::passByObject(iters, false);
|
||||
} else if (mode == "object-size") {
|
||||
return BenchmarkMethods::passByObject(iters, true);
|
||||
} else if (mode == "bytes") {
|
||||
return BenchmarkMethods::passByBytes(iters);
|
||||
} else if (mode == "pipe") {
|
||||
return passByPipe<BenchmarkMethods>(BenchmarkMethods::syncClient, iters);
|
||||
} else if (mode == "pipe-async") {
|
||||
return passByPipe<BenchmarkMethods>(BenchmarkMethods::asyncClient, iters);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown mode: %s\n", mode.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BenchmarkTypes, typename TestCase, typename Compression>
|
||||
uint64_t doBenchmark2(const std::string& mode, const std::string& reuse, uint64_t iters) {
|
||||
if (reuse == "reuse") {
|
||||
return doBenchmark<
|
||||
BenchmarkTypes, TestCase, typename BenchmarkTypes::ReusableResources, Compression>(
|
||||
mode, iters);
|
||||
} else if (reuse == "no-reuse") {
|
||||
return doBenchmark<
|
||||
BenchmarkTypes, TestCase, typename BenchmarkTypes::SingleUseResources, Compression>(
|
||||
mode, iters);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown reuse mode: %s\n", reuse.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BenchmarkTypes, typename TestCase>
|
||||
uint64_t doBenchmark3(const std::string& mode, const std::string& reuse,
|
||||
const std::string& compression, uint64_t iters) {
|
||||
if (compression == "none") {
|
||||
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Uncompressed>(
|
||||
mode, reuse, iters);
|
||||
} else if (compression == "packed") {
|
||||
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Packed>(
|
||||
mode, reuse, iters);
|
||||
#if HAVE_SNAPPY
|
||||
} else if (compression == "snappy") {
|
||||
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::SnappyCompressed>(
|
||||
mode, reuse, iters);
|
||||
#endif // HAVE_SNAPPY
|
||||
} else {
|
||||
fprintf(stderr, "Unknown compression mode: %s\n", compression.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BenchmarkTypes, typename TestCase>
|
||||
int benchmarkMain(int argc, char* argv[]) {
|
||||
if (argc != 5) {
|
||||
fprintf(stderr, "USAGE: %s MODE REUSE COMPRESSION ITERATION_COUNT\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t iters = strtoull(argv[4], nullptr, 0);
|
||||
uint64_t throughput = doBenchmark3<BenchmarkTypes, TestCase>(argv[1], argv[2], argv[3], iters);
|
||||
fprintf(stdout, "%llu\n", (long long unsigned int)throughput);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace capnp
|
||||
} // namespace benchmark
|
|
@ -0,0 +1,51 @@
|
|||
# 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";
|
||||
|
||||
@0xe12dc4c3e70e9eda;
|
||||
$Cxx.namespace("capnp::benchmark::capnp");
|
||||
|
||||
enum Operation {
|
||||
add @0;
|
||||
subtract @1;
|
||||
multiply @2;
|
||||
divide @3;
|
||||
modulus @4;
|
||||
}
|
||||
|
||||
struct Expression {
|
||||
op@0: Operation;
|
||||
|
||||
left :union {
|
||||
value@1: Int32;
|
||||
expression@2: Expression;
|
||||
}
|
||||
|
||||
right :union {
|
||||
value@3: Int32;
|
||||
expression@4: Expression;
|
||||
}
|
||||
}
|
||||
|
||||
struct EvaluationResult {
|
||||
value@0: Int32;
|
||||
}
|
|
@ -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.
|
||||
|
||||
package capnp.benchmark.protobuf;
|
||||
|
||||
enum Operation {
|
||||
ADD = 0;
|
||||
SUBTRACT = 1;
|
||||
MULTIPLY = 2;
|
||||
DIVIDE = 3;
|
||||
MODULUS = 4;
|
||||
}
|
||||
|
||||
message Expression {
|
||||
required Operation op = 1;
|
||||
|
||||
optional int32 left_value = 2;
|
||||
optional Expression left_expression = 3;
|
||||
|
||||
optional int32 right_value = 4;
|
||||
optional Expression right_expression = 5;
|
||||
}
|
||||
|
||||
message EvaluationResult {
|
||||
required sint32 value = 1;
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
// 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 "null-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace null {
|
||||
|
||||
enum class Color: uint8_t {
|
||||
BLACK,
|
||||
WHITE,
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE,
|
||||
CYAN,
|
||||
MAGENTA,
|
||||
YELLOW,
|
||||
SILVER
|
||||
};
|
||||
constexpr uint COLOR_RANGE = static_cast<uint>(Color::SILVER) + 1;
|
||||
|
||||
struct Wheel {
|
||||
float airPressure;
|
||||
uint16_t diameter;
|
||||
bool snowTires;
|
||||
};
|
||||
|
||||
struct Engine {
|
||||
uint32_t cc;
|
||||
uint16_t horsepower;
|
||||
uint8_t cylinders;
|
||||
uint8_t bits;
|
||||
inline bool usesGas() const { return bits & 1; }
|
||||
inline bool usesElectric() const { return bits & 2; }
|
||||
|
||||
inline void setBits(bool usesGas, bool usesElectric) {
|
||||
bits = (uint8_t)usesGas | ((uint8_t)usesElectric << 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct Car {
|
||||
// SORT FIELDS BY SIZE since we need "theoretical best" memory usage
|
||||
Engine engine;
|
||||
List<Wheel> wheels;
|
||||
const char* make;
|
||||
const char* model;
|
||||
float fuelCapacity;
|
||||
float fuelLevel;
|
||||
uint32_t weight;
|
||||
uint16_t length;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
Color color;
|
||||
uint8_t seats;
|
||||
uint8_t doors;
|
||||
uint8_t cupHolders;
|
||||
|
||||
uint8_t bits;
|
||||
|
||||
inline bool hasPowerWindows() const { return bits & 1; }
|
||||
inline bool hasPowerSteering() const { return bits & 2; }
|
||||
inline bool hasCruiseControl() const { return bits & 4; }
|
||||
inline bool hasNavSystem() const { return bits & 8; }
|
||||
|
||||
inline void setBits(bool hasPowerWindows, bool hasPowerSteering,
|
||||
bool hasCruiseControl, bool hasNavSystem) {
|
||||
bits = (uint8_t)hasPowerWindows
|
||||
| ((uint8_t)hasPowerSteering << 1)
|
||||
| ((uint8_t)hasCruiseControl << 2)
|
||||
| ((uint8_t)hasNavSystem << 3);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
uint64_t carValue(const Car& car) {
|
||||
// Do not think too hard about realism.
|
||||
|
||||
uint64_t result = 0;
|
||||
|
||||
result += car.seats * 200;
|
||||
result += car.doors * 350;
|
||||
for (auto wheel: car.wheels) {
|
||||
result += wheel.diameter * wheel.diameter;
|
||||
result += wheel.snowTires ? 100 : 0;
|
||||
}
|
||||
|
||||
result += car.length * car.width * car.height / 50;
|
||||
|
||||
auto engine = car.engine;
|
||||
result += engine.horsepower * 40;
|
||||
if (engine.usesElectric()) {
|
||||
if (engine.usesGas()) {
|
||||
// hybrid
|
||||
result += 5000;
|
||||
} else {
|
||||
result += 3000;
|
||||
}
|
||||
}
|
||||
|
||||
result += car.hasPowerWindows() ? 100 : 0;
|
||||
result += car.hasPowerSteering() ? 200 : 0;
|
||||
result += car.hasCruiseControl() ? 400 : 0;
|
||||
result += car.hasNavSystem() ? 2000 : 0;
|
||||
|
||||
result += car.cupHolders * 25;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void randomCar(Car* car) {
|
||||
// Do not think too hard about realism.
|
||||
|
||||
static const char* const MAKES[] = { "Toyota", "GM", "Ford", "Honda", "Tesla" };
|
||||
static const char* const MODELS[] = { "Camry", "Prius", "Volt", "Accord", "Leaf", "Model S" };
|
||||
|
||||
car->make = copyString(MAKES[fastRand(sizeof(MAKES) / sizeof(MAKES[0]))]);
|
||||
car->model = copyString(MODELS[fastRand(sizeof(MODELS) / sizeof(MODELS[0]))]);
|
||||
|
||||
car->color = (Color)fastRand(COLOR_RANGE);
|
||||
car->seats = 2 + fastRand(6);
|
||||
car->doors = 2 + fastRand(3);
|
||||
|
||||
for (auto& wheel: car->wheels.init(4)) {
|
||||
wheel.diameter = 25 + fastRand(15);
|
||||
wheel.airPressure = 30 + fastRandDouble(20);
|
||||
wheel.snowTires = fastRand(16) == 0;
|
||||
}
|
||||
|
||||
car->length = 170 + fastRand(150);
|
||||
car->width = 48 + fastRand(36);
|
||||
car->height = 54 + fastRand(48);
|
||||
car->weight = car->length * car->width * car->height / 200;
|
||||
|
||||
car->engine.horsepower = 100 * fastRand(400);
|
||||
car->engine.cylinders = 4 + 2 * fastRand(3);
|
||||
car->engine.cc = 800 + fastRand(10000);
|
||||
car->engine.setBits(true, fastRand(2));
|
||||
|
||||
car->fuelCapacity = 10.0 + fastRandDouble(30.0);
|
||||
car->fuelLevel = fastRandDouble(car->fuelCapacity);
|
||||
bool hasPowerWindows = fastRand(2);
|
||||
bool hasPowerSteering = fastRand(2);
|
||||
bool hasCruiseControl = fastRand(2);
|
||||
car->cupHolders = fastRand(12);
|
||||
bool hasNavSystem = fastRand(2);
|
||||
car->setBits(hasPowerWindows, hasPowerSteering, hasCruiseControl, hasNavSystem);
|
||||
}
|
||||
|
||||
class CarSalesTestCase {
|
||||
public:
|
||||
typedef List<Car> Request;
|
||||
typedef uint64_t Response;
|
||||
typedef uint64_t Expectation;
|
||||
|
||||
static uint64_t setupRequest(List<Car>* request) {
|
||||
uint64_t result = 0;
|
||||
for (auto& car: request->init(fastRand(200))) {
|
||||
randomCar(&car);
|
||||
result += carValue(car);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static void handleRequest(const List<Car>& request, uint64_t* response) {
|
||||
*response = 0;
|
||||
for (auto& car: request) {
|
||||
*response += carValue(car);
|
||||
}
|
||||
}
|
||||
static inline bool checkResponse(uint64_t response, uint64_t expected) {
|
||||
return response == expected;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace null
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::null::BenchmarkTypes,
|
||||
capnp::benchmark::null::CarSalesTestCase>(argc, argv);
|
||||
}
|
|
@ -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 "null-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace null {
|
||||
|
||||
struct SearchResult {
|
||||
const char* url;
|
||||
double score;
|
||||
const char* snippet;
|
||||
};
|
||||
|
||||
struct ScoredResult {
|
||||
double score;
|
||||
const SearchResult* result;
|
||||
|
||||
ScoredResult() = default;
|
||||
ScoredResult(double score, const SearchResult* result): score(score), result(result) {}
|
||||
|
||||
inline bool operator<(const ScoredResult& other) const { return score > other.score; }
|
||||
};
|
||||
|
||||
class CatRankTestCase {
|
||||
public:
|
||||
typedef List<SearchResult> Request;
|
||||
typedef List<SearchResult> Response;
|
||||
typedef int Expectation;
|
||||
|
||||
static int setupRequest(List<SearchResult>* request) {
|
||||
int count = fastRand(1000);
|
||||
int goodCount = 0;
|
||||
|
||||
request->init(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
SearchResult& result = request->items[i];
|
||||
result.score = 1000 - i;
|
||||
char* pos = reinterpret_cast<char*>(arenaPos);
|
||||
result.url = pos;
|
||||
|
||||
strcpy(pos, "http://example.com/");
|
||||
pos += strlen("http://example.com/");
|
||||
int urlSize = fastRand(100);
|
||||
for (int j = 0; j < urlSize; j++) {
|
||||
*pos++ = 'a' + fastRand(26);
|
||||
}
|
||||
*pos++ = '\0';
|
||||
|
||||
// Retroactively allocate the space we used.
|
||||
if (allocate<char>(pos - result.url) != result.url) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
bool isCat = fastRand(8) == 0;
|
||||
bool isDog = fastRand(8) == 0;
|
||||
goodCount += isCat && !isDog;
|
||||
|
||||
pos = reinterpret_cast<char*>(arenaPos);
|
||||
result.snippet = pos;
|
||||
|
||||
*pos++ = ' ';
|
||||
|
||||
int prefix = fastRand(20);
|
||||
for (int j = 0; j < prefix; j++) {
|
||||
const char* word = WORDS[fastRand(WORDS_COUNT)];
|
||||
size_t len = strlen(word);
|
||||
memcpy(pos, word, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
if (isCat) {
|
||||
strcpy(pos, "cat ");
|
||||
pos += 4;
|
||||
}
|
||||
if (isDog) {
|
||||
strcpy(pos, "dog ");
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
int suffix = fastRand(20);
|
||||
for (int j = 0; j < suffix; j++) {
|
||||
const char* word = WORDS[fastRand(WORDS_COUNT)];
|
||||
size_t len = strlen(word);
|
||||
memcpy(pos, word, len);
|
||||
pos += len;
|
||||
}
|
||||
*pos++ = '\0';
|
||||
|
||||
// Retroactively allocate the space we used.
|
||||
if (allocate<char>(pos - result.snippet) != result.snippet) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
return goodCount;
|
||||
}
|
||||
|
||||
static void handleRequest(const List<SearchResult>& request, List<SearchResult>* response) {
|
||||
std::vector<ScoredResult> scoredResults;
|
||||
scoredResults.reserve(request.size);
|
||||
|
||||
for (auto& result: request) {
|
||||
double score = result.score;
|
||||
if (strstr(result.snippet, " cat ") != nullptr) {
|
||||
score *= 10000;
|
||||
}
|
||||
if (strstr(result.snippet, " dog ") != nullptr) {
|
||||
score /= 10000;
|
||||
}
|
||||
scoredResults.emplace_back(score, &result);
|
||||
}
|
||||
|
||||
std::sort(scoredResults.begin(), scoredResults.end());
|
||||
|
||||
response->init(scoredResults.size());
|
||||
SearchResult* dst = response->items;
|
||||
for (auto& result: scoredResults) {
|
||||
dst->url = copyString(result.result->url);
|
||||
dst->score = result.score;
|
||||
dst->snippet = copyString(result.result->snippet);
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkResponse(const List<SearchResult>& response, int expectedGoodCount) {
|
||||
int goodCount = 0;
|
||||
for (auto& result: response) {
|
||||
if (result.score > 1001) {
|
||||
++goodCount;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return goodCount == expectedGoodCount;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace null
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::null::BenchmarkTypes,
|
||||
capnp::benchmark::null::CatRankTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
// 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"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace null {
|
||||
|
||||
uint64_t arena[1024*1024];
|
||||
uint64_t* arenaPos = arena;
|
||||
|
||||
template <typename T>
|
||||
T* allocate(int count = 1) {
|
||||
T* result = reinterpret_cast<T*>(arenaPos);
|
||||
arenaPos += (sizeof(T) * count + 7) / 8;
|
||||
if (arenaPos > arena + sizeof(arena) / sizeof(arena[0])) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char* copyString(const char* str) {
|
||||
size_t len = strlen(str);
|
||||
char* result = allocate<char>(len);
|
||||
memcpy(result, str, len + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct List {
|
||||
size_t size;
|
||||
T* items;
|
||||
|
||||
inline T* begin() const { return items; }
|
||||
inline T* end() const { return items + size; }
|
||||
|
||||
inline List<T>& init(size_t size) {
|
||||
this->size = size;
|
||||
items = allocate<T>(size);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
struct SingleUseObjects {
|
||||
class ObjectSizeCounter {
|
||||
public:
|
||||
ObjectSizeCounter(uint64_t iters): counter(0) {}
|
||||
|
||||
void add(uint64_t wordCount) {
|
||||
counter += wordCount;
|
||||
}
|
||||
|
||||
uint64_t get() { return counter; }
|
||||
|
||||
private:
|
||||
uint64_t counter;
|
||||
};
|
||||
};
|
||||
|
||||
struct ReusableObjects {
|
||||
class ObjectSizeCounter {
|
||||
public:
|
||||
ObjectSizeCounter(uint64_t iters): iters(iters), maxSize(0) {}
|
||||
|
||||
void add(size_t wordCount) {
|
||||
maxSize = std::max(wordCount, maxSize);
|
||||
}
|
||||
|
||||
uint64_t get() { return iters * maxSize; }
|
||||
|
||||
private:
|
||||
uint64_t iters;
|
||||
size_t maxSize;
|
||||
};
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
template <typename TestCase, typename ReuseStrategy, typename Compression>
|
||||
struct BenchmarkMethods {
|
||||
static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
|
||||
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static uint64_t asyncClientSender(
|
||||
int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
|
||||
uint64_t iters) {
|
||||
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void asyncClientReceiver(
|
||||
int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
|
||||
uint64_t iters) {
|
||||
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
|
||||
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
|
||||
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
|
||||
typename ReuseStrategy::ObjectSizeCounter sizeCounter(iters);
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
arenaPos = arena;
|
||||
|
||||
typename TestCase::Request request;
|
||||
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
|
||||
|
||||
typename TestCase::Response response;
|
||||
TestCase::handleRequest(request, &response);
|
||||
if (!TestCase::checkResponse(response, expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
|
||||
sizeCounter.add((arenaPos - arena) * sizeof(arena[0]));
|
||||
}
|
||||
|
||||
return sizeCounter.get();
|
||||
}
|
||||
|
||||
static uint64_t passByBytes(uint64_t iters) {
|
||||
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
struct BenchmarkTypes {
|
||||
typedef void Uncompressed;
|
||||
typedef void Packed;
|
||||
#if HAVE_SNAPPY
|
||||
typedef void SnappyCompressed;
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
typedef ReusableObjects ReusableResources;
|
||||
typedef SingleUseObjects SingleUseResources;
|
||||
|
||||
template <typename TestCase, typename ReuseStrategy, typename Compression>
|
||||
struct BenchmarkMethods: public null::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
|
||||
};
|
||||
|
||||
} // namespace null
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
||||
// Licensed under the MIT License:
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "null-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace null {
|
||||
|
||||
enum class Operation {
|
||||
ADD,
|
||||
SUBTRACT,
|
||||
MULTIPLY,
|
||||
DIVIDE,
|
||||
MODULUS
|
||||
};
|
||||
uint OPERATION_RANGE = static_cast<uint>(Operation::MODULUS) + 1;
|
||||
|
||||
struct Expression {
|
||||
Operation op;
|
||||
|
||||
bool leftIsValue;
|
||||
bool rightIsValue;
|
||||
|
||||
union {
|
||||
int32_t leftValue;
|
||||
Expression* leftExpression;
|
||||
};
|
||||
|
||||
union {
|
||||
int32_t rightValue;
|
||||
Expression* rightExpression;
|
||||
};
|
||||
};
|
||||
|
||||
int32_t makeExpression(Expression* exp, uint depth) {
|
||||
exp->op = (Operation)(fastRand(OPERATION_RANGE));
|
||||
|
||||
int32_t left, right;
|
||||
|
||||
if (fastRand(8) < depth) {
|
||||
exp->leftIsValue = true;
|
||||
left = fastRand(128) + 1;
|
||||
exp->leftValue = left;
|
||||
} else {
|
||||
exp->leftIsValue = false;
|
||||
exp->leftExpression = allocate<Expression>();
|
||||
left = makeExpression(exp->leftExpression, depth + 1);
|
||||
}
|
||||
|
||||
if (fastRand(8) < depth) {
|
||||
exp->rightIsValue = true;
|
||||
right = fastRand(128) + 1;
|
||||
exp->rightValue = right;
|
||||
} else {
|
||||
exp->rightIsValue = false;
|
||||
exp->rightExpression = allocate<Expression>();
|
||||
right = makeExpression(exp->rightExpression, depth + 1);
|
||||
}
|
||||
|
||||
switch (exp->op) {
|
||||
case Operation::ADD:
|
||||
return left + right;
|
||||
case Operation::SUBTRACT:
|
||||
return left - right;
|
||||
case Operation::MULTIPLY:
|
||||
return left * right;
|
||||
case Operation::DIVIDE:
|
||||
return div(left, right);
|
||||
case Operation::MODULUS:
|
||||
return mod(left, right);
|
||||
}
|
||||
throw std::logic_error("Can't get here.");
|
||||
}
|
||||
|
||||
int32_t evaluateExpression(const Expression& exp) {
|
||||
uint32_t left, right;
|
||||
|
||||
if (exp.leftIsValue) {
|
||||
left = exp.leftValue;
|
||||
} else {
|
||||
left = evaluateExpression(*exp.leftExpression);
|
||||
}
|
||||
|
||||
if (exp.rightIsValue) {
|
||||
right = exp.rightValue;
|
||||
} else {
|
||||
right = evaluateExpression(*exp.rightExpression);
|
||||
}
|
||||
|
||||
switch (exp.op) {
|
||||
case Operation::ADD:
|
||||
return left + right;
|
||||
case Operation::SUBTRACT:
|
||||
return left - right;
|
||||
case Operation::MULTIPLY:
|
||||
return left * right;
|
||||
case Operation::DIVIDE:
|
||||
return div(left, right);
|
||||
case Operation::MODULUS:
|
||||
return mod(left, right);
|
||||
}
|
||||
throw std::logic_error("Can't get here.");
|
||||
}
|
||||
|
||||
class ExpressionTestCase {
|
||||
public:
|
||||
typedef Expression Request;
|
||||
typedef int32_t Response;
|
||||
typedef int32_t Expectation;
|
||||
|
||||
static inline int32_t setupRequest(Expression* request) {
|
||||
return makeExpression(request, 0);
|
||||
}
|
||||
static inline void handleRequest(const Expression& request, int32_t* response) {
|
||||
*response = evaluateExpression(request);
|
||||
}
|
||||
static inline bool checkResponse(int32_t response, int32_t expected) {
|
||||
return response == expected;
|
||||
}
|
||||
|
||||
static size_t spaceUsed(const Expression& expression) {
|
||||
return sizeof(Expression) +
|
||||
(expression.leftExpression == nullptr ? 0 : spaceUsed(*expression.leftExpression)) +
|
||||
(expression.rightExpression == nullptr ? 0 : spaceUsed(*expression.rightExpression));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace null
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::null::BenchmarkTypes,
|
||||
capnp::benchmark::null::ExpressionTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
// 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 "carsales.pb.h"
|
||||
#include "protobuf-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace protobuf {
|
||||
|
||||
uint64_t carValue(const Car& car) {
|
||||
// Do not think too hard about realism.
|
||||
|
||||
uint64_t result = 0;
|
||||
|
||||
result += car.seats() * 200;
|
||||
result += car.doors() * 350;
|
||||
for (auto& wheel: car.wheel()) {
|
||||
result += wheel.diameter() * wheel.diameter();
|
||||
result += wheel.snow_tires() ? 100 : 0;
|
||||
}
|
||||
|
||||
result += car.length() * car.width() * car.height() / 50;
|
||||
|
||||
const Engine& engine = car.engine();
|
||||
result += engine.horsepower() * 40;
|
||||
if (engine.uses_electric()) {
|
||||
if (engine.uses_gas()) {
|
||||
// hybrid
|
||||
result += 5000;
|
||||
} else {
|
||||
result += 3000;
|
||||
}
|
||||
}
|
||||
|
||||
result += car.has_power_windows() ? 100 : 0;
|
||||
result += car.has_power_steering() ? 200 : 0;
|
||||
result += car.has_cruise_control() ? 400 : 0;
|
||||
result += car.has_nav_system() ? 2000 : 0;
|
||||
|
||||
result += car.cup_holders() * 25;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void randomCar(Car* car) {
|
||||
// Do not think too hard about realism.
|
||||
|
||||
static const char* const MAKES[] = { "Toyota", "GM", "Ford", "Honda", "Tesla" };
|
||||
static const char* const MODELS[] = { "Camry", "Prius", "Volt", "Accord", "Leaf", "Model S" };
|
||||
|
||||
car->set_make(MAKES[fastRand(sizeof(MAKES) / sizeof(MAKES[0]))]);
|
||||
car->set_model(MODELS[fastRand(sizeof(MODELS) / sizeof(MODELS[0]))]);
|
||||
|
||||
car->set_color((Color)fastRand(Color_MAX));
|
||||
car->set_seats(2 + fastRand(6));
|
||||
car->set_doors(2 + fastRand(3));
|
||||
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
Wheel* wheel = car->add_wheel();
|
||||
wheel->set_diameter(25 + fastRand(15));
|
||||
wheel->set_air_pressure(30 + fastRandDouble(20));
|
||||
wheel->set_snow_tires(fastRand(16) == 0);
|
||||
}
|
||||
|
||||
car->set_length(170 + fastRand(150));
|
||||
car->set_width(48 + fastRand(36));
|
||||
car->set_height(54 + fastRand(48));
|
||||
car->set_weight(car->length() * car->width() * car->height() / 200);
|
||||
|
||||
Engine* engine = car->mutable_engine();
|
||||
engine->set_horsepower(100 * fastRand(400));
|
||||
engine->set_cylinders(4 + 2 * fastRand(3));
|
||||
engine->set_cc(800 + fastRand(10000));
|
||||
engine->set_uses_gas(true);
|
||||
engine->set_uses_electric(fastRand(2));
|
||||
|
||||
car->set_fuel_capacity(10.0 + fastRandDouble(30.0));
|
||||
car->set_fuel_level(fastRandDouble(car->fuel_capacity()));
|
||||
car->set_has_power_windows(fastRand(2));
|
||||
car->set_has_power_steering(fastRand(2));
|
||||
car->set_has_cruise_control(fastRand(2));
|
||||
car->set_cup_holders(fastRand(12));
|
||||
car->set_has_nav_system(fastRand(2));
|
||||
}
|
||||
|
||||
class CarSalesTestCase {
|
||||
public:
|
||||
typedef ParkingLot Request;
|
||||
typedef TotalValue Response;
|
||||
typedef uint64_t Expectation;
|
||||
|
||||
static uint64_t setupRequest(ParkingLot* request) {
|
||||
uint count = fastRand(200);
|
||||
uint64_t result = 0;
|
||||
for (uint i = 0; i < count; i++) {
|
||||
Car* car = request->add_car();
|
||||
randomCar(car);
|
||||
result += carValue(*car);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static void handleRequest(const ParkingLot& request, TotalValue* response) {
|
||||
uint64_t result = 0;
|
||||
for (auto& car: request.car()) {
|
||||
result += carValue(car);
|
||||
}
|
||||
response->set_amount(result);
|
||||
}
|
||||
static inline bool checkResponse(const TotalValue& response, uint64_t expected) {
|
||||
return response.amount() == expected;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace protobuf
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::protobuf::BenchmarkTypes,
|
||||
capnp::benchmark::protobuf::CarSalesTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
||||
// Licensed under the MIT License:
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "catrank.pb.h"
|
||||
#include "protobuf-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace protobuf {
|
||||
|
||||
struct ScoredResult {
|
||||
double score;
|
||||
const SearchResult* result;
|
||||
|
||||
ScoredResult() = default;
|
||||
ScoredResult(double score, const SearchResult* result): score(score), result(result) {}
|
||||
|
||||
inline bool operator<(const ScoredResult& other) const { return score > other.score; }
|
||||
};
|
||||
|
||||
class CatRankTestCase {
|
||||
public:
|
||||
typedef SearchResultList Request;
|
||||
typedef SearchResultList Response;
|
||||
typedef int Expectation;
|
||||
|
||||
static int setupRequest(SearchResultList* request) {
|
||||
int count = fastRand(1000);
|
||||
int goodCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
SearchResult* result = request->add_result();
|
||||
result->set_score(1000 - i);
|
||||
result->set_url("http://example.com/");
|
||||
std::string* url = result->mutable_url();
|
||||
int urlSize = fastRand(100);
|
||||
for (int j = 0; j < urlSize; j++) {
|
||||
url->push_back('a' + fastRand(26));
|
||||
}
|
||||
|
||||
bool isCat = fastRand(8) == 0;
|
||||
bool isDog = fastRand(8) == 0;
|
||||
goodCount += isCat && !isDog;
|
||||
|
||||
std::string* snippet = result->mutable_snippet();
|
||||
snippet->reserve(7 * 22);
|
||||
snippet->push_back(' ');
|
||||
|
||||
int prefix = fastRand(20);
|
||||
for (int j = 0; j < prefix; j++) {
|
||||
snippet->append(WORDS[fastRand(WORDS_COUNT)]);
|
||||
}
|
||||
|
||||
if (isCat) snippet->append("cat ");
|
||||
if (isDog) snippet->append("dog ");
|
||||
|
||||
int suffix = fastRand(20);
|
||||
for (int j = 0; j < suffix; j++) {
|
||||
snippet->append(WORDS[fastRand(WORDS_COUNT)]);
|
||||
}
|
||||
}
|
||||
|
||||
return goodCount;
|
||||
}
|
||||
|
||||
static void handleRequest(const SearchResultList& request, SearchResultList* response) {
|
||||
std::vector<ScoredResult> scoredResults;
|
||||
|
||||
for (auto& result: request.result()) {
|
||||
double score = result.score();
|
||||
if (result.snippet().find(" cat ") != std::string::npos) {
|
||||
score *= 10000;
|
||||
}
|
||||
if (result.snippet().find(" dog ") != std::string::npos) {
|
||||
score /= 10000;
|
||||
}
|
||||
scoredResults.emplace_back(score, &result);
|
||||
}
|
||||
|
||||
std::sort(scoredResults.begin(), scoredResults.end());
|
||||
|
||||
for (auto& result: scoredResults) {
|
||||
SearchResult* out = response->add_result();
|
||||
out->set_score(result.score);
|
||||
out->set_url(result.result->url());
|
||||
out->set_snippet(result.result->snippet());
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkResponse(const SearchResultList& response, int expectedGoodCount) {
|
||||
int goodCount = 0;
|
||||
for (auto& result: response.result()) {
|
||||
if (result.score() > 1001) {
|
||||
++goodCount;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return goodCount == expectedGoodCount;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace protobuf
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::protobuf::BenchmarkTypes,
|
||||
capnp::benchmark::protobuf::CatRankTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
// 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 <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include <google/protobuf/io/coded_stream.h>
|
||||
#include <thread>
|
||||
#if HAVE_SNAPPY
|
||||
#include <snappy/snappy.h>
|
||||
#include <snappy/snappy-sinksource.h>
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace protobuf {
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
struct SingleUseMessages {
|
||||
template <typename MessageType>
|
||||
struct Message {
|
||||
struct Reusable {};
|
||||
struct SingleUse: public MessageType {
|
||||
inline SingleUse(Reusable&) {}
|
||||
};
|
||||
};
|
||||
|
||||
struct ReusableString {};
|
||||
struct SingleUseString: std::string {
|
||||
inline SingleUseString(ReusableString&) {}
|
||||
};
|
||||
|
||||
template <typename MessageType>
|
||||
static inline void doneWith(MessageType& message) {
|
||||
// Don't clear -- single-use.
|
||||
}
|
||||
};
|
||||
|
||||
struct ReusableMessages {
|
||||
template <typename MessageType>
|
||||
struct Message {
|
||||
struct Reusable: public MessageType {};
|
||||
typedef MessageType& SingleUse;
|
||||
};
|
||||
|
||||
typedef std::string ReusableString;
|
||||
typedef std::string& SingleUseString;
|
||||
|
||||
template <typename MessageType>
|
||||
static inline void doneWith(MessageType& message) {
|
||||
message.Clear();
|
||||
}
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// The protobuf Java library defines a format for writing multiple protobufs to a stream, in which
|
||||
// each message is prefixed by a varint size. This was never added to the C++ library. It's easy
|
||||
// to do naively, but tricky to implement without accidentally losing various optimizations. These
|
||||
// two functions should be optimal.
|
||||
|
||||
struct Uncompressed {
|
||||
typedef google::protobuf::io::FileInputStream InputStream;
|
||||
typedef google::protobuf::io::FileOutputStream OutputStream;
|
||||
|
||||
static uint64_t write(const google::protobuf::MessageLite& message,
|
||||
google::protobuf::io::FileOutputStream* rawOutput) {
|
||||
google::protobuf::io::CodedOutputStream output(rawOutput);
|
||||
const int size = message.ByteSize();
|
||||
output.WriteVarint32(size);
|
||||
uint8_t* buffer = output.GetDirectBufferForNBytesAndAdvance(size);
|
||||
if (buffer != NULL) {
|
||||
message.SerializeWithCachedSizesToArray(buffer);
|
||||
} else {
|
||||
message.SerializeWithCachedSizes(&output);
|
||||
if (output.HadError()) {
|
||||
throw OsException(rawOutput->GetErrno());
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void read(google::protobuf::io::ZeroCopyInputStream* rawInput,
|
||||
google::protobuf::MessageLite* message) {
|
||||
google::protobuf::io::CodedInputStream input(rawInput);
|
||||
uint32_t size;
|
||||
GOOGLE_CHECK(input.ReadVarint32(&size));
|
||||
|
||||
auto limit = input.PushLimit(size);
|
||||
|
||||
GOOGLE_CHECK(message->MergePartialFromCodedStream(&input) &&
|
||||
input.ConsumedEntireMessage());
|
||||
|
||||
input.PopLimit(limit);
|
||||
}
|
||||
|
||||
static void flush(google::protobuf::io::FileOutputStream* output) {
|
||||
if (!output->Flush()) throw OsException(output->GetErrno());
|
||||
}
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// The Snappy interface is really obnoxious. I gave up here and am just reading/writing flat
|
||||
// arrays in some static scratch space. This probably gives protobufs an edge that it doesn't
|
||||
// deserve.
|
||||
|
||||
#if HAVE_SNAPPY
|
||||
|
||||
static char scratch[1 << 20];
|
||||
static char scratch2[1 << 20];
|
||||
|
||||
struct SnappyCompressed {
|
||||
typedef int InputStream;
|
||||
typedef int OutputStream;
|
||||
|
||||
static uint64_t write(const google::protobuf::MessageLite& message, int* output) {
|
||||
size_t size = message.ByteSize();
|
||||
GOOGLE_CHECK_LE(size, sizeof(scratch));
|
||||
|
||||
message.SerializeWithCachedSizesToArray(reinterpret_cast<uint8_t*>(scratch));
|
||||
|
||||
size_t compressedSize = 0;
|
||||
snappy::RawCompress(scratch, size, scratch2 + sizeof(uint32_t), &compressedSize);
|
||||
uint32_t tag = compressedSize;
|
||||
memcpy(scratch2, &tag, sizeof(tag));
|
||||
|
||||
writeAll(*output, scratch2, compressedSize + sizeof(tag));
|
||||
return compressedSize + sizeof(tag);
|
||||
}
|
||||
|
||||
static void read(int* input, google::protobuf::MessageLite* message) {
|
||||
uint32_t size;
|
||||
readAll(*input, &size, sizeof(size));
|
||||
readAll(*input, scratch, size);
|
||||
|
||||
size_t uncompressedSize;
|
||||
GOOGLE_CHECK(snappy::GetUncompressedLength(scratch, size, &uncompressedSize));
|
||||
GOOGLE_CHECK(snappy::RawUncompress(scratch, size, scratch2));
|
||||
|
||||
GOOGLE_CHECK(message->ParsePartialFromArray(scratch2, uncompressedSize));
|
||||
}
|
||||
|
||||
static void flush(OutputStream*) {}
|
||||
};
|
||||
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
#define REUSABLE(type) \
|
||||
typename ReuseStrategy::template Message<typename TestCase::type>::Reusable
|
||||
#define SINGLE_USE(type) \
|
||||
typename ReuseStrategy::template Message<typename TestCase::type>::SingleUse
|
||||
|
||||
template <typename TestCase, typename ReuseStrategy, typename Compression>
|
||||
struct BenchmarkMethods {
|
||||
static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
|
||||
uint64_t throughput = 0;
|
||||
|
||||
typename Compression::OutputStream output(outputFd);
|
||||
typename Compression::InputStream input(inputFd);
|
||||
|
||||
REUSABLE(Request) reusableRequest;
|
||||
REUSABLE(Response) reusableResponse;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
SINGLE_USE(Request) request(reusableRequest);
|
||||
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
|
||||
throughput += Compression::write(request, &output);
|
||||
Compression::flush(&output);
|
||||
ReuseStrategy::doneWith(request);
|
||||
|
||||
SINGLE_USE(Response) response(reusableResponse);
|
||||
Compression::read(&input, &response);
|
||||
if (!TestCase::checkResponse(response, expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
ReuseStrategy::doneWith(response);
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
static uint64_t asyncClientSender(
|
||||
int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
|
||||
uint64_t iters) {
|
||||
uint64_t throughput = 0;
|
||||
|
||||
typename Compression::OutputStream output(outputFd);
|
||||
REUSABLE(Request) reusableRequest;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
SINGLE_USE(Request) request(reusableRequest);
|
||||
expectations->post(TestCase::setupRequest(&request));
|
||||
throughput += Compression::write(request, &output);
|
||||
Compression::flush(&output);
|
||||
ReuseStrategy::doneWith(request);
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
static void asyncClientReceiver(
|
||||
int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
|
||||
uint64_t iters) {
|
||||
typename Compression::InputStream input(inputFd);
|
||||
REUSABLE(Response) reusableResponse;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
typename TestCase::Expectation expected = expectations->next();
|
||||
SINGLE_USE(Response) response(reusableResponse);
|
||||
Compression::read(&input, &response);
|
||||
if (!TestCase::checkResponse(response, expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
ReuseStrategy::doneWith(response);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
|
||||
ProducerConsumerQueue<typename TestCase::Expectation> expectations;
|
||||
std::thread receiverThread(asyncClientReceiver, inputFd, &expectations, iters);
|
||||
uint64_t throughput = asyncClientSender(outputFd, &expectations, iters);
|
||||
receiverThread.join();
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
|
||||
uint64_t throughput = 0;
|
||||
|
||||
typename Compression::OutputStream output(outputFd);
|
||||
typename Compression::InputStream input(inputFd);
|
||||
|
||||
REUSABLE(Request) reusableRequest;
|
||||
REUSABLE(Response) reusableResponse;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
SINGLE_USE(Request) request(reusableRequest);
|
||||
Compression::read(&input, &request);
|
||||
|
||||
SINGLE_USE(Response) response(reusableResponse);
|
||||
TestCase::handleRequest(request, &response);
|
||||
ReuseStrategy::doneWith(request);
|
||||
|
||||
throughput += Compression::write(response, &output);
|
||||
Compression::flush(&output);
|
||||
ReuseStrategy::doneWith(response);
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
|
||||
uint64_t throughput = 0;
|
||||
|
||||
REUSABLE(Request) reusableRequest;
|
||||
REUSABLE(Response) reusableResponse;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
SINGLE_USE(Request) request(reusableRequest);
|
||||
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
|
||||
|
||||
SINGLE_USE(Response) response(reusableResponse);
|
||||
TestCase::handleRequest(request, &response);
|
||||
ReuseStrategy::doneWith(request);
|
||||
if (!TestCase::checkResponse(response, expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
ReuseStrategy::doneWith(response);
|
||||
|
||||
if (countObjectSize) {
|
||||
throughput += request.SpaceUsed();
|
||||
throughput += response.SpaceUsed();
|
||||
}
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
static uint64_t passByBytes(uint64_t iters) {
|
||||
uint64_t throughput = 0;
|
||||
|
||||
REUSABLE(Request) reusableClientRequest;
|
||||
REUSABLE(Request) reusableServerRequest;
|
||||
REUSABLE(Response) reusableServerResponse;
|
||||
REUSABLE(Response) reusableClientResponse;
|
||||
typename ReuseStrategy::ReusableString reusableRequestString, reusableResponseString;
|
||||
|
||||
for (; iters > 0; --iters) {
|
||||
SINGLE_USE(Request) clientRequest(reusableClientRequest);
|
||||
typename TestCase::Expectation expected = TestCase::setupRequest(&clientRequest);
|
||||
|
||||
typename ReuseStrategy::SingleUseString requestString(reusableRequestString);
|
||||
clientRequest.SerializePartialToString(&requestString);
|
||||
throughput += requestString.size();
|
||||
ReuseStrategy::doneWith(clientRequest);
|
||||
|
||||
SINGLE_USE(Request) serverRequest(reusableServerRequest);
|
||||
serverRequest.ParsePartialFromString(requestString);
|
||||
|
||||
SINGLE_USE(Response) serverResponse(reusableServerResponse);
|
||||
TestCase::handleRequest(serverRequest, &serverResponse);
|
||||
ReuseStrategy::doneWith(serverRequest);
|
||||
|
||||
typename ReuseStrategy::SingleUseString responseString(reusableResponseString);
|
||||
serverResponse.SerializePartialToString(&responseString);
|
||||
throughput += responseString.size();
|
||||
ReuseStrategy::doneWith(serverResponse);
|
||||
|
||||
SINGLE_USE(Response) clientResponse(reusableClientResponse);
|
||||
clientResponse.ParsePartialFromString(responseString);
|
||||
|
||||
if (!TestCase::checkResponse(clientResponse, expected)) {
|
||||
throw std::logic_error("Incorrect response.");
|
||||
}
|
||||
ReuseStrategy::doneWith(clientResponse);
|
||||
}
|
||||
|
||||
return throughput;
|
||||
}
|
||||
};
|
||||
|
||||
struct BenchmarkTypes {
|
||||
typedef protobuf::Uncompressed Uncompressed;
|
||||
typedef protobuf::Uncompressed Packed;
|
||||
#if HAVE_SNAPPY
|
||||
typedef protobuf::SnappyCompressed SnappyCompressed;
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
typedef protobuf::ReusableMessages ReusableResources;
|
||||
typedef protobuf::SingleUseMessages SingleUseResources;
|
||||
|
||||
template <typename TestCase, typename ReuseStrategy, typename Compression>
|
||||
struct BenchmarkMethods
|
||||
: public protobuf::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
|
||||
};
|
||||
|
||||
} // namespace protobuf
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
|
@ -0,0 +1,118 @@
|
|||
// 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 "eval.pb.h"
|
||||
#include "protobuf-common.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace protobuf {
|
||||
|
||||
int32_t makeExpression(Expression* exp, uint depth) {
|
||||
exp->set_op((Operation)(fastRand(Operation_MAX + 1)));
|
||||
|
||||
int32_t left, right;
|
||||
|
||||
if (fastRand(8) < depth) {
|
||||
left = fastRand(128) + 1;
|
||||
exp->set_left_value(left);
|
||||
} else {
|
||||
left = makeExpression(exp->mutable_left_expression(), depth + 1);
|
||||
}
|
||||
|
||||
if (fastRand(8) < depth) {
|
||||
right = fastRand(128) + 1;
|
||||
exp->set_right_value(right);
|
||||
} else {
|
||||
right = makeExpression(exp->mutable_right_expression(), depth + 1);
|
||||
}
|
||||
|
||||
switch (exp->op()) {
|
||||
case Operation::ADD:
|
||||
return left + right;
|
||||
case Operation::SUBTRACT:
|
||||
return left - right;
|
||||
case Operation::MULTIPLY:
|
||||
return left * right;
|
||||
case Operation::DIVIDE:
|
||||
return div(left, right);
|
||||
case Operation::MODULUS:
|
||||
return mod(left, right);
|
||||
}
|
||||
throw std::logic_error("Can't get here.");
|
||||
}
|
||||
|
||||
int32_t evaluateExpression(const Expression& exp) {
|
||||
uint32_t left, right;
|
||||
|
||||
if (exp.has_left_value()) {
|
||||
left = exp.left_value();
|
||||
} else {
|
||||
left = evaluateExpression(exp.left_expression());
|
||||
}
|
||||
|
||||
if (exp.has_right_value()) {
|
||||
right = exp.right_value();
|
||||
} else {
|
||||
right = evaluateExpression(exp.right_expression());
|
||||
}
|
||||
|
||||
switch (exp.op()) {
|
||||
case Operation::ADD:
|
||||
return left + right;
|
||||
case Operation::SUBTRACT:
|
||||
return left - right;
|
||||
case Operation::MULTIPLY:
|
||||
return left * right;
|
||||
case Operation::DIVIDE:
|
||||
return div(left, right);
|
||||
case Operation::MODULUS:
|
||||
return mod(left, right);
|
||||
}
|
||||
throw std::logic_error("Can't get here.");
|
||||
}
|
||||
|
||||
class ExpressionTestCase {
|
||||
public:
|
||||
typedef Expression Request;
|
||||
typedef EvaluationResult Response;
|
||||
typedef int32_t Expectation;
|
||||
|
||||
static inline int32_t setupRequest(Expression* request) {
|
||||
return makeExpression(request, 0);
|
||||
}
|
||||
static inline void handleRequest(const Expression& request, EvaluationResult* response) {
|
||||
response->set_value(evaluateExpression(request));
|
||||
}
|
||||
static inline bool checkResponse(const EvaluationResult& response, int32_t expected) {
|
||||
return response.value() == expected;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace protobuf
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::benchmarkMain<
|
||||
capnp::benchmark::protobuf::BenchmarkTypes,
|
||||
capnp::benchmark::protobuf::ExpressionTestCase>(argc, argv);
|
||||
}
|
|
@ -0,0 +1,648 @@
|
|||
// 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 <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace capnp {
|
||||
namespace benchmark {
|
||||
namespace runner {
|
||||
|
||||
struct Times {
|
||||
uint64_t real;
|
||||
uint64_t user;
|
||||
uint64_t sys;
|
||||
|
||||
uint64_t cpu() { return user + sys; }
|
||||
|
||||
Times operator-(const Times& other) {
|
||||
Times result;
|
||||
result.real = real - other.real;
|
||||
result.user = user - other.user;
|
||||
result.sys = sys - other.sys;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t asNanosecs(const struct timeval& tv) {
|
||||
return (uint64_t)tv.tv_sec * 1000000000 + (uint64_t)tv.tv_usec * 1000;
|
||||
}
|
||||
|
||||
Times currentTimes() {
|
||||
Times result;
|
||||
|
||||
struct rusage self, children;
|
||||
getrusage(RUSAGE_SELF, &self);
|
||||
getrusage(RUSAGE_CHILDREN, &children);
|
||||
|
||||
struct timeval real;
|
||||
gettimeofday(&real, nullptr);
|
||||
|
||||
result.real = asNanosecs(real);
|
||||
result.user = asNanosecs(self.ru_utime) + asNanosecs(children.ru_utime);
|
||||
result.sys = asNanosecs(self.ru_stime) + asNanosecs(children.ru_stime);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct TestResult {
|
||||
uint64_t objectSize;
|
||||
uint64_t messageSize;
|
||||
Times time;
|
||||
};
|
||||
|
||||
enum class Product {
|
||||
CAPNPROTO,
|
||||
PROTOBUF,
|
||||
NULLCASE
|
||||
};
|
||||
|
||||
enum class TestCase {
|
||||
EVAL,
|
||||
CATRANK,
|
||||
CARSALES
|
||||
};
|
||||
|
||||
const char* testCaseName(TestCase testCase) {
|
||||
switch (testCase) {
|
||||
case TestCase::EVAL:
|
||||
return "eval";
|
||||
case TestCase::CATRANK:
|
||||
return "catrank";
|
||||
case TestCase::CARSALES:
|
||||
return "carsales";
|
||||
}
|
||||
// Can't get here.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
enum class Mode {
|
||||
OBJECTS,
|
||||
OBJECT_SIZE,
|
||||
BYTES,
|
||||
PIPE_SYNC,
|
||||
PIPE_ASYNC
|
||||
};
|
||||
|
||||
enum class Reuse {
|
||||
YES,
|
||||
NO
|
||||
};
|
||||
|
||||
enum class Compression {
|
||||
NONE,
|
||||
PACKED,
|
||||
SNAPPY
|
||||
};
|
||||
|
||||
TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse,
|
||||
Compression compression, uint64_t iters) {
|
||||
char* argv[6];
|
||||
|
||||
string progName;
|
||||
|
||||
switch (product) {
|
||||
case Product::CAPNPROTO:
|
||||
progName = "capnproto-";
|
||||
break;
|
||||
case Product::PROTOBUF:
|
||||
progName = "protobuf-";
|
||||
break;
|
||||
case Product::NULLCASE:
|
||||
progName = "null-";
|
||||
break;
|
||||
}
|
||||
|
||||
progName += testCaseName(testCase);
|
||||
argv[0] = strdup(progName.c_str());
|
||||
|
||||
switch (mode) {
|
||||
case Mode::OBJECTS:
|
||||
argv[1] = strdup("object");
|
||||
break;
|
||||
case Mode::OBJECT_SIZE:
|
||||
argv[1] = strdup("object-size");
|
||||
break;
|
||||
case Mode::BYTES:
|
||||
argv[1] = strdup("bytes");
|
||||
break;
|
||||
case Mode::PIPE_SYNC:
|
||||
argv[1] = strdup("pipe");
|
||||
break;
|
||||
case Mode::PIPE_ASYNC:
|
||||
argv[1] = strdup("pipe-async");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (reuse) {
|
||||
case Reuse::YES:
|
||||
argv[2] = strdup("reuse");
|
||||
break;
|
||||
case Reuse::NO:
|
||||
argv[2] = strdup("no-reuse");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (compression) {
|
||||
case Compression::NONE:
|
||||
argv[3] = strdup("none");
|
||||
break;
|
||||
case Compression::PACKED:
|
||||
argv[3] = strdup("packed");
|
||||
break;
|
||||
case Compression::SNAPPY:
|
||||
argv[3] = strdup("snappy");
|
||||
break;
|
||||
}
|
||||
|
||||
char itersStr[64];
|
||||
sprintf(itersStr, "%llu", (long long unsigned int)iters);
|
||||
argv[4] = itersStr;
|
||||
|
||||
argv[5] = nullptr;
|
||||
|
||||
// Make pipe for child to write throughput.
|
||||
int childPipe[2];
|
||||
if (pipe(childPipe) < 0) {
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Spawn the child process.
|
||||
struct timeval start, end;
|
||||
gettimeofday(&start, nullptr);
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(childPipe[0]);
|
||||
dup2(childPipe[1], STDOUT_FILENO);
|
||||
close(childPipe[1]);
|
||||
execv(argv[0], argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(childPipe[1]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
free(argv[i]);
|
||||
}
|
||||
|
||||
// Read throughput number written to child's stdout.
|
||||
FILE* input = fdopen(childPipe[0], "r");
|
||||
long long unsigned int throughput;
|
||||
if (fscanf(input, "%lld", &throughput) != 1) {
|
||||
fprintf(stderr, "Child didn't write throughput to stdout.");
|
||||
}
|
||||
char buffer[1024];
|
||||
while (fgets(buffer, sizeof(buffer), input) != nullptr) {
|
||||
// Loop until EOF.
|
||||
}
|
||||
fclose(input);
|
||||
|
||||
// Wait for child exit.
|
||||
int status;
|
||||
struct rusage usage;
|
||||
wait4(child, &status, 0, &usage);
|
||||
gettimeofday(&end, nullptr);
|
||||
|
||||
// Calculate results.
|
||||
|
||||
TestResult result;
|
||||
result.objectSize = mode == Mode::OBJECT_SIZE ? throughput : 0;
|
||||
result.messageSize = mode == Mode::OBJECT_SIZE ? 0 : throughput;
|
||||
result.time.real = asNanosecs(end) - asNanosecs(start);
|
||||
result.time.user = asNanosecs(usage.ru_utime);
|
||||
result.time.sys = asNanosecs(usage.ru_stime);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void reportTableHeader() {
|
||||
cout << setw(40) << left << "Test"
|
||||
<< setw(10) << right << "obj size"
|
||||
<< setw(10) << right << "I/O bytes"
|
||||
<< setw(10) << right << "wall ns"
|
||||
<< setw(10) << right << "user ns"
|
||||
<< setw(10) << right << "sys ns"
|
||||
<< endl;
|
||||
cout << setfill('=') << setw(90) << "" << setfill(' ') << endl;
|
||||
}
|
||||
|
||||
void reportResults(const char* name, uint64_t iters, TestResult results) {
|
||||
cout << setw(40) << left << name
|
||||
<< setw(10) << right << (results.objectSize / iters)
|
||||
<< setw(10) << right << (results.messageSize / iters)
|
||||
<< setw(10) << right << (results.time.real / iters)
|
||||
<< setw(10) << right << (results.time.user / iters)
|
||||
<< setw(10) << right << (results.time.sys / iters)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
void reportComparisonHeader() {
|
||||
cout << setw(40) << left << "Measure"
|
||||
<< setw(15) << right << "Protobuf"
|
||||
<< setw(15) << right << "Cap'n Proto"
|
||||
<< setw(15) << right << "Improvement"
|
||||
<< endl;
|
||||
cout << setfill('=') << setw(85) << "" << setfill(' ') << endl;
|
||||
}
|
||||
|
||||
void reportOldNewComparisonHeader() {
|
||||
cout << setw(40) << left << "Measure"
|
||||
<< setw(15) << right << "Old"
|
||||
<< setw(15) << right << "New"
|
||||
<< setw(15) << right << "Improvement"
|
||||
<< endl;
|
||||
cout << setfill('=') << setw(85) << "" << setfill(' ') << endl;
|
||||
}
|
||||
|
||||
class Gain {
|
||||
public:
|
||||
Gain(double oldValue, double newValue)
|
||||
: amount(newValue / oldValue) {}
|
||||
|
||||
void writeTo(std::ostream& os) {
|
||||
if (amount < 2) {
|
||||
double percent = (amount - 1) * 100;
|
||||
os << (int)(percent + 0.5) << "%";
|
||||
} else {
|
||||
os << fixed << setprecision(2) << amount << "x";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double amount;
|
||||
};
|
||||
|
||||
ostream& operator<<(ostream& os, Gain gain) {
|
||||
gain.writeTo(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
void reportComparison(const char* name, double base, double protobuf, double capnproto,
|
||||
uint64_t iters) {
|
||||
cout << setw(40) << left << name
|
||||
<< setw(14) << right << Gain(base, protobuf)
|
||||
<< setw(14) << right << Gain(base, capnproto);
|
||||
|
||||
// Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf.
|
||||
cout << setw(14) << right << Gain(capnproto - base, protobuf - base) << endl;
|
||||
}
|
||||
|
||||
void reportComparison(const char* name, const char* unit, double protobuf, double capnproto,
|
||||
uint64_t iters) {
|
||||
cout << setw(40) << left << name
|
||||
<< setw(15-strlen(unit)) << fixed << right << setprecision(2) << (protobuf / iters) << unit
|
||||
<< setw(15-strlen(unit)) << fixed << right << setprecision(2) << (capnproto / iters) << unit;
|
||||
|
||||
// Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf.
|
||||
cout << setw(14) << right << Gain(capnproto, protobuf) << endl;
|
||||
}
|
||||
|
||||
void reportIntComparison(const char* name, const char* unit, uint64_t protobuf, uint64_t capnproto,
|
||||
uint64_t iters) {
|
||||
cout << setw(40) << left << name
|
||||
<< setw(15-strlen(unit)) << right << (protobuf / iters) << unit
|
||||
<< setw(15-strlen(unit)) << right << (capnproto / iters) << unit;
|
||||
|
||||
// Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf.
|
||||
cout << setw(14) << right << Gain(capnproto, protobuf) << endl;
|
||||
}
|
||||
|
||||
size_t fileSize(const std::string& name) {
|
||||
struct stat stats;
|
||||
if (stat(name.c_str(), &stats) < 0) {
|
||||
perror(name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return stats.st_size;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char* path = argv[0];
|
||||
char* slashpos = strrchr(path, '/');
|
||||
char origDir[1024];
|
||||
if (getcwd(origDir, sizeof(origDir)) == nullptr) {
|
||||
perror("getcwd");
|
||||
return 1;
|
||||
}
|
||||
if (slashpos != nullptr) {
|
||||
*slashpos = '\0';
|
||||
if (chdir(path) < 0) {
|
||||
perror("chdir");
|
||||
return 1;
|
||||
}
|
||||
*slashpos = '/';
|
||||
}
|
||||
|
||||
TestCase testCase = TestCase::CATRANK;
|
||||
Mode mode = Mode::PIPE_SYNC;
|
||||
Compression compression = Compression::NONE;
|
||||
uint64_t iters = 1;
|
||||
const char* oldDir = nullptr;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string arg = argv[i];
|
||||
if (isdigit(argv[i][0])) {
|
||||
iters = strtoul(argv[i], nullptr, 0);
|
||||
} else if (arg == "async") {
|
||||
mode = Mode::PIPE_ASYNC;
|
||||
} else if (arg == "inmem") {
|
||||
mode = Mode::BYTES;
|
||||
} else if (arg == "eval") {
|
||||
testCase = TestCase::EVAL;
|
||||
} else if (arg == "carsales") {
|
||||
testCase = TestCase::CARSALES;
|
||||
} else if (arg == "snappy") {
|
||||
compression = Compression::SNAPPY;
|
||||
} else if (arg == "-c") {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
fprintf(stderr, "-c requires argument.\n");
|
||||
return 1;
|
||||
}
|
||||
oldDir = argv[i];
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale iterations to something reasonable for each case.
|
||||
switch (testCase) {
|
||||
case TestCase::EVAL:
|
||||
iters *= 100000;
|
||||
break;
|
||||
case TestCase::CATRANK:
|
||||
iters *= 1000;
|
||||
break;
|
||||
case TestCase::CARSALES:
|
||||
iters *= 20000;
|
||||
break;
|
||||
}
|
||||
|
||||
cout << "Running " << iters << " iterations of ";
|
||||
switch (testCase) {
|
||||
case TestCase::EVAL:
|
||||
cout << "calculator";
|
||||
break;
|
||||
case TestCase::CATRANK:
|
||||
cout << "CatRank";
|
||||
break;
|
||||
case TestCase::CARSALES:
|
||||
cout << "car sales";
|
||||
break;
|
||||
}
|
||||
|
||||
cout << " example case with:" << endl;
|
||||
|
||||
switch (mode) {
|
||||
case Mode::OBJECTS:
|
||||
case Mode::OBJECT_SIZE:
|
||||
// Can't happen.
|
||||
break;
|
||||
case Mode::BYTES:
|
||||
cout << "* in-memory I/O" << endl;
|
||||
cout << " * with client and server in the same thread" << endl;
|
||||
break;
|
||||
case Mode::PIPE_SYNC:
|
||||
cout << "* pipe I/O" << endl;
|
||||
cout << " * with client and server in separate processes" << endl;
|
||||
cout << " * client waits for each response before sending next request" << endl;
|
||||
break;
|
||||
case Mode::PIPE_ASYNC:
|
||||
cout << "* pipe I/O" << endl;
|
||||
cout << " * with client and server in separate processes" << endl;
|
||||
cout << " * client sends as many simultaneous requests as it can" << endl;
|
||||
break;
|
||||
}
|
||||
switch (compression) {
|
||||
case Compression::NONE:
|
||||
cout << "* no compression" << endl;
|
||||
break;
|
||||
case Compression::PACKED:
|
||||
cout << "* de-zero packing for Cap'n Proto" << endl;
|
||||
cout << "* standard packing for Protobuf" << endl;
|
||||
break;
|
||||
case Compression::SNAPPY:
|
||||
cout << "* Snappy compression" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
|
||||
reportTableHeader();
|
||||
|
||||
TestResult nullCase = runTest(
|
||||
Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters);
|
||||
reportResults("Theoretical best pass-by-object", iters, nullCase);
|
||||
|
||||
TestResult protobufBase = runTest(
|
||||
Product::PROTOBUF, testCase, Mode::OBJECTS, Reuse::YES, compression, iters);
|
||||
protobufBase.objectSize = runTest(
|
||||
Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters).objectSize;
|
||||
reportResults("Protobuf pass-by-object", iters, protobufBase);
|
||||
|
||||
TestResult capnpBase = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::YES, compression, iters);
|
||||
capnpBase.objectSize = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters).objectSize;
|
||||
reportResults("Cap'n Proto pass-by-object", iters, capnpBase);
|
||||
|
||||
TestResult nullCaseNoReuse = runTest(
|
||||
Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters);
|
||||
reportResults("Theoretical best w/o object reuse", iters, nullCaseNoReuse);
|
||||
|
||||
TestResult protobufNoReuse = runTest(
|
||||
Product::PROTOBUF, testCase, Mode::OBJECTS, Reuse::NO, compression, iters);
|
||||
protobufNoReuse.objectSize = runTest(
|
||||
Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize;
|
||||
reportResults("Protobuf w/o object reuse", iters, protobufNoReuse);
|
||||
|
||||
TestResult capnpNoReuse = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::NO, compression, iters);
|
||||
capnpNoReuse.objectSize = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize;
|
||||
reportResults("Cap'n Proto w/o object reuse", iters, capnpNoReuse);
|
||||
|
||||
TestResult protobuf = runTest(
|
||||
Product::PROTOBUF, testCase, mode, Reuse::YES, compression, iters);
|
||||
protobuf.objectSize = protobufBase.objectSize;
|
||||
reportResults("Protobuf I/O", iters, protobuf);
|
||||
|
||||
TestResult capnp = runTest(
|
||||
Product::CAPNPROTO, testCase, mode, Reuse::YES, compression, iters);
|
||||
capnp.objectSize = capnpBase.objectSize;
|
||||
reportResults("Cap'n Proto I/O", iters, capnp);
|
||||
TestResult capnpPacked = runTest(
|
||||
Product::CAPNPROTO, testCase, mode, Reuse::YES, Compression::PACKED, iters);
|
||||
capnpPacked.objectSize = capnpBase.objectSize;
|
||||
reportResults("Cap'n Proto packed I/O", iters, capnpPacked);
|
||||
|
||||
size_t protobufBinarySize = fileSize("protobuf-" + std::string(testCaseName(testCase)));
|
||||
size_t capnpBinarySize = fileSize("capnproto-" + std::string(testCaseName(testCase)));
|
||||
size_t protobufCodeSize = fileSize(std::string(testCaseName(testCase)) + ".pb.cc")
|
||||
+ fileSize(std::string(testCaseName(testCase)) + ".pb.h");
|
||||
size_t capnpCodeSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.c++")
|
||||
+ fileSize(std::string(testCaseName(testCase)) + ".capnp.h");
|
||||
size_t protobufObjSize = fileSize(std::string(testCaseName(testCase)) + ".pb.o");
|
||||
size_t capnpObjSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.o");
|
||||
|
||||
TestResult oldNullCase;
|
||||
TestResult oldNullCaseNoReuse;
|
||||
TestResult oldCapnpBase;
|
||||
TestResult oldCapnpNoReuse;
|
||||
TestResult oldCapnp;
|
||||
TestResult oldCapnpPacked;
|
||||
size_t oldCapnpBinarySize = 0;
|
||||
size_t oldCapnpCodeSize = 0;
|
||||
size_t oldCapnpObjSize = 0;
|
||||
if (oldDir != nullptr) {
|
||||
if (chdir(origDir) < 0) {
|
||||
perror("chdir");
|
||||
return 1;
|
||||
}
|
||||
if (chdir(oldDir) < 0) {
|
||||
perror(oldDir);
|
||||
return 1;
|
||||
}
|
||||
|
||||
oldNullCase = runTest(
|
||||
Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters);
|
||||
reportResults("Old theoretical best pass-by-object", iters, nullCase);
|
||||
|
||||
oldCapnpBase = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::YES, compression, iters);
|
||||
oldCapnpBase.objectSize = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters)
|
||||
.objectSize;
|
||||
reportResults("Old Cap'n Proto pass-by-object", iters, oldCapnpBase);
|
||||
|
||||
oldNullCaseNoReuse = runTest(
|
||||
Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters);
|
||||
reportResults("Old theoretical best w/o object reuse", iters, oldNullCaseNoReuse);
|
||||
|
||||
oldCapnpNoReuse = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::NO, compression, iters);
|
||||
oldCapnpNoReuse.objectSize = runTest(
|
||||
Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize;
|
||||
reportResults("Old Cap'n Proto w/o object reuse", iters, oldCapnpNoReuse);
|
||||
|
||||
oldCapnp = runTest(
|
||||
Product::CAPNPROTO, testCase, mode, Reuse::YES, compression, iters);
|
||||
oldCapnp.objectSize = oldCapnpBase.objectSize;
|
||||
reportResults("Old Cap'n Proto I/O", iters, oldCapnp);
|
||||
oldCapnpPacked = runTest(
|
||||
Product::CAPNPROTO, testCase, mode, Reuse::YES, Compression::PACKED, iters);
|
||||
oldCapnpPacked.objectSize = oldCapnpBase.objectSize;
|
||||
reportResults("Old Cap'n Proto packed I/O", iters, oldCapnpPacked);
|
||||
|
||||
oldCapnpBinarySize = fileSize("capnproto-" + std::string(testCaseName(testCase)));
|
||||
oldCapnpCodeSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.c++")
|
||||
+ fileSize(std::string(testCaseName(testCase)) + ".capnp.h");
|
||||
oldCapnpObjSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.o");
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
|
||||
reportComparisonHeader();
|
||||
reportComparison("memory overhead (vs ideal)",
|
||||
nullCase.objectSize, protobufBase.objectSize, capnpBase.objectSize, iters);
|
||||
reportComparison("memory overhead w/o object reuse",
|
||||
nullCaseNoReuse.objectSize, protobufNoReuse.objectSize, capnpNoReuse.objectSize, iters);
|
||||
reportComparison("object manipulation time (us)", "",
|
||||
((int64_t)protobufBase.time.user - (int64_t)nullCase.time.user) / 1000.0,
|
||||
((int64_t)capnpBase.time.user - (int64_t)nullCase.time.user) / 1000.0, iters);
|
||||
reportComparison("object manipulation time w/o reuse (us)", "",
|
||||
((int64_t)protobufNoReuse.time.user - (int64_t)nullCaseNoReuse.time.user) / 1000.0,
|
||||
((int64_t)capnpNoReuse.time.user - (int64_t)nullCaseNoReuse.time.user) / 1000.0, iters);
|
||||
reportComparison("I/O time (us)", "",
|
||||
((int64_t)protobuf.time.user - (int64_t)protobufBase.time.user) / 1000.0,
|
||||
((int64_t)capnp.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters);
|
||||
reportComparison("packed I/O time (us)", "",
|
||||
((int64_t)protobuf.time.user - (int64_t)protobufBase.time.user) / 1000.0,
|
||||
((int64_t)capnpPacked.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters);
|
||||
|
||||
reportIntComparison("message size (bytes)", "", protobuf.messageSize, capnp.messageSize, iters);
|
||||
reportIntComparison("packed message size (bytes)", "",
|
||||
protobuf.messageSize, capnpPacked.messageSize, iters);
|
||||
|
||||
reportComparison("binary size (KiB)", "",
|
||||
protobufBinarySize / 1024.0, capnpBinarySize / 1024.0, 1);
|
||||
reportComparison("generated code size (KiB)", "",
|
||||
protobufCodeSize / 1024.0, capnpCodeSize / 1024.0, 1);
|
||||
reportComparison("generated obj size (KiB)", "",
|
||||
protobufObjSize / 1024.0, capnpObjSize / 1024.0, 1);
|
||||
|
||||
if (oldDir != nullptr) {
|
||||
cout << endl;
|
||||
reportOldNewComparisonHeader();
|
||||
|
||||
reportComparison("memory overhead",
|
||||
oldNullCase.objectSize, oldCapnpBase.objectSize, capnpBase.objectSize, iters);
|
||||
reportComparison("memory overhead w/o object reuse",
|
||||
oldNullCaseNoReuse.objectSize, oldCapnpNoReuse.objectSize, capnpNoReuse.objectSize, iters);
|
||||
reportComparison("object manipulation time (us)", "",
|
||||
((int64_t)oldCapnpBase.time.user - (int64_t)oldNullCase.time.user) / 1000.0,
|
||||
((int64_t)capnpBase.time.user - (int64_t)oldNullCase.time.user) / 1000.0, iters);
|
||||
reportComparison("object manipulation time w/o reuse (us)", "",
|
||||
((int64_t)oldCapnpNoReuse.time.user - (int64_t)oldNullCaseNoReuse.time.user) / 1000.0,
|
||||
((int64_t)capnpNoReuse.time.user - (int64_t)oldNullCaseNoReuse.time.user) / 1000.0, iters);
|
||||
reportComparison("I/O time (us)", "",
|
||||
((int64_t)oldCapnp.time.user - (int64_t)oldCapnpBase.time.user) / 1000.0,
|
||||
((int64_t)capnp.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters);
|
||||
reportComparison("packed I/O time (us)", "",
|
||||
((int64_t)oldCapnpPacked.time.user - (int64_t)oldCapnpBase.time.user) / 1000.0,
|
||||
((int64_t)capnpPacked.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters);
|
||||
|
||||
reportIntComparison("message size (bytes)", "", oldCapnp.messageSize, capnp.messageSize, iters);
|
||||
reportIntComparison("packed message size (bytes)", "",
|
||||
oldCapnpPacked.messageSize, capnpPacked.messageSize, iters);
|
||||
|
||||
reportComparison("binary size (KiB)", "",
|
||||
oldCapnpBinarySize / 1024.0, capnpBinarySize / 1024.0, 1);
|
||||
reportComparison("generated code size (KiB)", "",
|
||||
oldCapnpCodeSize / 1024.0, capnpCodeSize / 1024.0, 1);
|
||||
reportComparison("generated obj size (KiB)", "",
|
||||
oldCapnpObjSize / 1024.0, capnpObjSize / 1024.0, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace runner
|
||||
} // namespace benchmark
|
||||
} // namespace capnp
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return capnp::benchmark::runner::main(argc, argv);
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
|
||||
# capnp ========================================================================
|
||||
|
||||
set(capnp_sources_lite
|
||||
c++.capnp.c++
|
||||
blob.c++
|
||||
arena.c++
|
||||
layout.c++
|
||||
list.c++
|
||||
any.c++
|
||||
message.c++
|
||||
schema.capnp.c++
|
||||
serialize.c++
|
||||
serialize-packed.c++
|
||||
)
|
||||
set(capnp_sources_heavy
|
||||
schema.c++
|
||||
schema-loader.c++
|
||||
dynamic.c++
|
||||
stringify.c++
|
||||
)
|
||||
if(NOT CAPNP_LITE)
|
||||
set(capnp_sources ${capnp_sources_lite} ${capnp_sources_heavy})
|
||||
else()
|
||||
set(capnp_sources ${capnp_sources_lite})
|
||||
endif()
|
||||
|
||||
set(capnp_headers
|
||||
c++.capnp.h
|
||||
common.h
|
||||
blob.h
|
||||
endian.h
|
||||
layout.h
|
||||
orphan.h
|
||||
list.h
|
||||
any.h
|
||||
message.h
|
||||
capability.h
|
||||
membrane.h
|
||||
dynamic.h
|
||||
schema.h
|
||||
schema.capnp.h
|
||||
schema-lite.h
|
||||
schema-loader.h
|
||||
schema-parser.h
|
||||
pretty-print.h
|
||||
serialize.h
|
||||
serialize-async.h
|
||||
serialize-packed.h
|
||||
serialize-text.h
|
||||
pointer-helpers.h
|
||||
generated-header-support.h
|
||||
raw-schema.h
|
||||
)
|
||||
set(capnp_schemas
|
||||
c++.capnp
|
||||
schema.capnp
|
||||
)
|
||||
add_library(capnp ${capnp_sources})
|
||||
add_library(CapnProto::capnp ALIAS capnp)
|
||||
target_link_libraries(capnp PUBLIC kj)
|
||||
#make sure external consumers don't need to manually set the include dirs
|
||||
target_include_directories(capnp INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
# Ensure the library has a version set to match autotools build
|
||||
set_target_properties(capnp PROPERTIES VERSION ${VERSION})
|
||||
install(TARGETS capnp ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
install(FILES ${capnp_headers} ${capnp_schemas} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp")
|
||||
|
||||
set(capnp-rpc_sources
|
||||
serialize-async.c++
|
||||
capability.c++
|
||||
membrane.c++
|
||||
dynamic-capability.c++
|
||||
rpc.c++
|
||||
rpc.capnp.c++
|
||||
rpc-twoparty.c++
|
||||
rpc-twoparty.capnp.c++
|
||||
persistent.capnp.c++
|
||||
ez-rpc.c++
|
||||
)
|
||||
set(capnp-rpc_headers
|
||||
rpc-prelude.h
|
||||
rpc.h
|
||||
rpc-twoparty.h
|
||||
rpc.capnp.h
|
||||
rpc-twoparty.capnp.h
|
||||
persistent.capnp.h
|
||||
ez-rpc.h
|
||||
)
|
||||
set(capnp-rpc_schemas
|
||||
rpc.capnp
|
||||
rpc-twoparty.capnp
|
||||
persistent.capnp
|
||||
)
|
||||
if(NOT CAPNP_LITE)
|
||||
add_library(capnp-rpc ${capnp-rpc_sources})
|
||||
add_library(CapnProto::capnp-rpc ALIAS capnp-rpc)
|
||||
target_link_libraries(capnp-rpc PUBLIC capnp kj-async kj)
|
||||
# Ensure the library has a version set to match autotools build
|
||||
set_target_properties(capnp-rpc PROPERTIES VERSION ${VERSION})
|
||||
install(TARGETS capnp-rpc ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
install(FILES ${capnp-rpc_headers} ${capnp-rpc_schemas} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp")
|
||||
endif()
|
||||
|
||||
# capnp-json ========================================================================
|
||||
|
||||
set(capnp-json_sources
|
||||
compat/json.c++
|
||||
compat/json.capnp.c++
|
||||
)
|
||||
set(capnp-json_headers
|
||||
compat/json.h
|
||||
compat/json.capnp.h
|
||||
)
|
||||
set(capnp-json_schemas
|
||||
compat/json.capnp
|
||||
)
|
||||
if(NOT CAPNP_LITE)
|
||||
add_library(capnp-json ${capnp-json_sources})
|
||||
add_library(CapnProto::capnp-json ALIAS capnp-json)
|
||||
target_link_libraries(capnp-json PUBLIC capnp kj-async kj)
|
||||
# Ensure the library has a version set to match autotools build
|
||||
set_target_properties(capnp-json PROPERTIES VERSION ${VERSION})
|
||||
install(TARGETS capnp-json ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
install(FILES ${capnp-json_headers} ${capnp-json_schemas} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp/compat")
|
||||
endif()
|
||||
|
||||
# Tools/Compilers ==============================================================
|
||||
|
||||
set(capnpc_sources
|
||||
compiler/type-id.c++
|
||||
compiler/error-reporter.c++
|
||||
compiler/lexer.capnp.c++
|
||||
compiler/lexer.c++
|
||||
compiler/grammar.capnp.c++
|
||||
compiler/parser.c++
|
||||
compiler/node-translator.c++
|
||||
compiler/compiler.c++
|
||||
schema-parser.c++
|
||||
serialize-text.c++
|
||||
)
|
||||
if(NOT CAPNP_LITE)
|
||||
add_library(capnpc ${capnpc_sources})
|
||||
target_link_libraries(capnpc PUBLIC capnp kj)
|
||||
# Ensure the library has a version set to match autotools build
|
||||
set_target_properties(capnpc PROPERTIES VERSION ${VERSION})
|
||||
install(TARGETS capnpc ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
install(FILES ${capnpc_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp")
|
||||
endif()
|
||||
|
||||
if(NOT CAPNP_LITE)
|
||||
add_executable(capnp_tool
|
||||
compiler/module-loader.c++
|
||||
compiler/capnp.c++
|
||||
)
|
||||
target_link_libraries(capnp_tool capnpc capnp-json capnp kj)
|
||||
set_target_properties(capnp_tool PROPERTIES OUTPUT_NAME capnp)
|
||||
set_target_properties(capnp_tool PROPERTIES CAPNP_INCLUDE_DIRECTORY
|
||||
$<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/..>>
|
||||
)
|
||||
|
||||
add_executable(capnpc_cpp
|
||||
compiler/capnpc-c++.c++
|
||||
)
|
||||
target_link_libraries(capnpc_cpp capnp kj)
|
||||
set_target_properties(capnpc_cpp PROPERTIES OUTPUT_NAME capnpc-c++)
|
||||
#Capnp tool needs capnpc_cpp location. But cmake deprecated LOCATION property.
|
||||
#So we use custom property to pass location
|
||||
set_target_properties(capnpc_cpp PROPERTIES CAPNPC_CXX_EXECUTABLE
|
||||
$<TARGET_FILE:capnpc_cpp>
|
||||
)
|
||||
|
||||
add_executable(capnpc_capnp
|
||||
compiler/capnpc-capnp.c++
|
||||
)
|
||||
target_link_libraries(capnpc_capnp capnp kj)
|
||||
set_target_properties(capnpc_capnp PROPERTIES OUTPUT_NAME capnpc-capnp)
|
||||
|
||||
install(TARGETS capnp_tool capnpc_cpp capnpc_capnp ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
# Symlink capnpc -> capnp
|
||||
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink capnp \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/capnpc\")")
|
||||
endif() # NOT CAPNP_LITE
|
||||
|
||||
# Tests ========================================================================
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(test_capnp_files
|
||||
test.capnp
|
||||
test-import.capnp
|
||||
test-import2.capnp
|
||||
compat/json-test.capnp
|
||||
)
|
||||
|
||||
set(CAPNPC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/test_capnp")
|
||||
include_directories("${CAPNPC_OUTPUT_DIR}")
|
||||
file(MAKE_DIRECTORY "${CAPNPC_OUTPUT_DIR}")
|
||||
# Tell capnp_generate_cpp to set --src-prefix to our parent directory. This allows us to pass our
|
||||
# .capnp files relative to this directory, but have their canonical name end up as
|
||||
# capnp/test.capnp, capnp/test-import.capnp, etc.
|
||||
get_filename_component(CAPNPC_SRC_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}" DIRECTORY)
|
||||
|
||||
capnp_generate_cpp(test_capnp_cpp_files test_capnp_h_files ${test_capnp_files})
|
||||
|
||||
# TODO(cleanup): capnp-tests and capnp-heavy-tests both depend on the test.capnp output files. In
|
||||
# a parallel Makefile-based build (maybe others?), they can race and cause the custom capnp
|
||||
# command in capnp_generate_cpp() to run twice. To get around this I'm using a custom target to
|
||||
# force CMake to generate race-free Makefiles. Remove this garbage when we move to a
|
||||
# target-based capnp_generate() command, as that will make CMake do the right thing by default.
|
||||
add_custom_target(test_capnp DEPENDS ${test_capnp_cpp_files} ${test_capnp_h_files})
|
||||
|
||||
if(CAPNP_LITE)
|
||||
set(test_libraries capnp kj-test kj)
|
||||
else()
|
||||
set(test_libraries capnp-json capnp-rpc capnp capnpc kj-async kj-test kj)
|
||||
endif()
|
||||
|
||||
add_executable(capnp-tests
|
||||
common-test.c++
|
||||
blob-test.c++
|
||||
endian-test.c++
|
||||
endian-fallback-test.c++
|
||||
layout-test.c++
|
||||
any-test.c++
|
||||
message-test.c++
|
||||
encoding-test.c++
|
||||
orphan-test.c++
|
||||
serialize-test.c++
|
||||
serialize-packed-test.c++
|
||||
canonicalize-test.c++
|
||||
fuzz-test.c++
|
||||
test-util.c++
|
||||
${test_capnp_cpp_files}
|
||||
${test_capnp_h_files}
|
||||
)
|
||||
target_link_libraries(capnp-tests ${test_libraries})
|
||||
add_dependencies(capnp-tests test_capnp)
|
||||
add_dependencies(check capnp-tests)
|
||||
add_test(NAME capnp-tests-run COMMAND capnp-tests)
|
||||
|
||||
if(NOT CAPNP_LITE)
|
||||
add_executable(capnp-heavy-tests
|
||||
endian-reverse-test.c++
|
||||
capability-test.c++
|
||||
membrane-test.c++
|
||||
schema-test.c++
|
||||
schema-loader-test.c++
|
||||
schema-parser-test.c++
|
||||
dynamic-test.c++
|
||||
stringify-test.c++
|
||||
serialize-async-test.c++
|
||||
serialize-text-test.c++
|
||||
rpc-test.c++
|
||||
rpc-twoparty-test.c++
|
||||
ez-rpc-test.c++
|
||||
compiler/lexer-test.c++
|
||||
compiler/type-id-test.c++
|
||||
test-util.c++
|
||||
compat/json-test.c++
|
||||
${test_capnp_cpp_files}
|
||||
${test_capnp_h_files}
|
||||
)
|
||||
target_link_libraries(capnp-heavy-tests ${test_libraries})
|
||||
if(NOT MSVC)
|
||||
set_target_properties(capnp-heavy-tests
|
||||
PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_dependencies(capnp-heavy-tests test_capnp)
|
||||
add_dependencies(check capnp-heavy-tests)
|
||||
add_test(NAME capnp-heavy-tests-run COMMAND capnp-heavy-tests)
|
||||
|
||||
add_executable(capnp-evolution-tests compiler/evolution-test.c++)
|
||||
target_link_libraries(capnp-evolution-tests capnpc capnp kj)
|
||||
add_dependencies(check capnp-evolution-tests)
|
||||
add_test(NAME capnp-evolution-tests-run COMMAND capnp-evolution-tests)
|
||||
endif() # NOT CAPNP_LITE
|
||||
endif() # BUILD_TESTING
|
|
@ -0,0 +1,143 @@
|
|||
// 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.
|
||||
|
||||
#include "test-util.h"
|
||||
#include <kj/main.h>
|
||||
#include "serialize.h"
|
||||
#include <capnp/test.capnp.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace capnp {
|
||||
namespace _ {
|
||||
namespace {
|
||||
|
||||
class AflTestMain {
|
||||
public:
|
||||
explicit AflTestMain(kj::ProcessContext& context)
|
||||
: context(context) {}
|
||||
|
||||
kj::MainFunc getMain() {
|
||||
return kj::MainBuilder(context, "(unknown version)",
|
||||
"American Fuzzy Lop test case. Pass input on stdin. Expects a binary "
|
||||
"message of type TestAllTypes.")
|
||||
.addOption({"lists"}, KJ_BIND_METHOD(*this, runLists),
|
||||
"Expect a message of type TestLists instead of TestAllTypes.")
|
||||
.addOption({"canonicalize"}, KJ_BIND_METHOD(*this, canonicalize),
|
||||
"Test canonicalization code.")
|
||||
.callAfterParsing(KJ_BIND_METHOD(*this, run))
|
||||
.build();
|
||||
}
|
||||
|
||||
kj::MainBuilder::Validity run() {
|
||||
capnp::StreamFdMessageReader reader(STDIN_FILENO);
|
||||
KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
|
||||
checkTestMessage(reader.getRoot<TestAllTypes>());
|
||||
})) {
|
||||
KJ_LOG(ERROR, "threw");
|
||||
}
|
||||
KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
|
||||
checkDynamicTestMessage(reader.getRoot<DynamicStruct>(Schema::from<TestAllTypes>()));
|
||||
})) {
|
||||
KJ_LOG(ERROR, "dynamic threw");
|
||||
}
|
||||
KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
|
||||
kj::str(reader.getRoot<TestAllTypes>());
|
||||
})) {
|
||||
KJ_LOG(ERROR, "str threw");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
kj::MainBuilder::Validity runLists() {
|
||||
capnp::StreamFdMessageReader reader(STDIN_FILENO);
|
||||
KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
|
||||
kj::str(reader.getRoot<test::TestLists>());
|
||||
})) {
|
||||
KJ_LOG(ERROR, "threw");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
kj::MainBuilder::Validity canonicalize() {
|
||||
// (Test case contributed by David Renshaw.)
|
||||
|
||||
kj::Array<capnp::word> canonical;
|
||||
bool equal = false;
|
||||
KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
|
||||
capnp::ReaderOptions options;
|
||||
|
||||
// The default traversal limit of 8 * 1024 * 1024 causes
|
||||
// AFL to think that it has found "hang" bugs.
|
||||
options.traversalLimitInWords = 8 * 1024;
|
||||
|
||||
capnp::StreamFdMessageReader message(0, options); // read from stdin
|
||||
TestAllTypes::Reader myStruct = message.getRoot<TestAllTypes>();
|
||||
canonical = capnp::canonicalize(myStruct);
|
||||
|
||||
kj::ArrayPtr<const capnp::word> segments[1] = {canonical.asPtr()};
|
||||
capnp::SegmentArrayMessageReader reader(kj::arrayPtr(segments, 1));
|
||||
|
||||
auto originalAny = message.getRoot<capnp::AnyPointer>();
|
||||
|
||||
// Discard cases where the original message is null.
|
||||
KJ_ASSERT(!originalAny.isNull());
|
||||
|
||||
equal = originalAny == reader.getRoot<capnp::AnyPointer>();
|
||||
})) {
|
||||
// Probably some kind of decoding exception.
|
||||
KJ_LOG(ERROR, "threw");
|
||||
context.exit();
|
||||
}
|
||||
|
||||
KJ_ASSERT(equal);
|
||||
|
||||
kj::ArrayPtr<const capnp::word> segments[1] = {canonical.asPtr()};
|
||||
capnp::SegmentArrayMessageReader reader(kj::arrayPtr(segments, 1));
|
||||
KJ_ASSERT(reader.isCanonical());
|
||||
|
||||
kj::Array<capnp::word> canonical2;
|
||||
{
|
||||
capnp::ReaderOptions options;
|
||||
options.traversalLimitInWords = 8 * 1024;
|
||||
|
||||
TestAllTypes::Reader myStruct = reader.getRoot<TestAllTypes>();
|
||||
canonical2 = capnp::canonicalize(myStruct);
|
||||
}
|
||||
|
||||
KJ_ASSERT(canonical.size() == canonical2.size());
|
||||
auto b1 = canonical.asBytes();
|
||||
auto b2 = canonical2.asBytes();
|
||||
for (int idx = 0; idx < b1.size(); ++idx) {
|
||||
KJ_ASSERT(b1[idx] == b2[idx], idx, b1.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
kj::ProcessContext& context;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace _
|
||||
} // namespace capnp
|
||||
|
||||
KJ_MAIN(capnp::_::AflTestMain);
|
|
@ -0,0 +1,439 @@
|
|||
// 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 "any.h"
|
||||
#include "message.h"
|
||||
#include <kj/compat/gtest.h>
|
||||
#include "test-util.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace _ { // private
|
||||
namespace {
|
||||
|
||||
TEST(Any, AnyPointer) {
|
||||
MallocMessageBuilder builder;
|
||||
auto root = builder.getRoot<test::TestAnyPointer>();
|
||||
|
||||
initTestMessage(root.getAnyPointerField().initAs<TestAllTypes>());
|
||||
checkTestMessage(root.getAnyPointerField().getAs<TestAllTypes>());
|
||||
checkTestMessage(root.asReader().getAnyPointerField().getAs<TestAllTypes>());
|
||||
|
||||
root.getAnyPointerField().setAs<Text>("foo");
|
||||
EXPECT_EQ("foo", root.getAnyPointerField().getAs<Text>());
|
||||
EXPECT_EQ("foo", root.asReader().getAnyPointerField().getAs<Text>());
|
||||
|
||||
root.getAnyPointerField().setAs<Data>(data("foo"));
|
||||
EXPECT_EQ(data("foo"), root.getAnyPointerField().getAs<Data>());
|
||||
EXPECT_EQ(data("foo"), root.asReader().getAnyPointerField().getAs<Data>());
|
||||
|
||||
{
|
||||
root.getAnyPointerField().setAs<List<uint32_t>>({123, 456, 789});
|
||||
|
||||
{
|
||||
List<uint32_t>::Builder list = root.getAnyPointerField().getAs<List<uint32_t>>();
|
||||
ASSERT_EQ(3u, list.size());
|
||||
EXPECT_EQ(123u, list[0]);
|
||||
EXPECT_EQ(456u, list[1]);
|
||||
EXPECT_EQ(789u, list[2]);
|
||||
}
|
||||
|
||||
{
|
||||
List<uint32_t>::Reader list = root.asReader().getAnyPointerField().getAs<List<uint32_t>>();
|
||||
ASSERT_EQ(3u, list.size());
|
||||
EXPECT_EQ(123u, list[0]);
|
||||
EXPECT_EQ(456u, list[1]);
|
||||
EXPECT_EQ(789u, list[2]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
root.getAnyPointerField().setAs<List<Text>>({"foo", "bar"});
|
||||
|
||||
{
|
||||
List<Text>::Builder list = root.getAnyPointerField().getAs<List<Text>>();
|
||||
ASSERT_EQ(2u, list.size());
|
||||
EXPECT_EQ("foo", list[0]);
|
||||
EXPECT_EQ("bar", list[1]);
|
||||
}
|
||||
|
||||
{
|
||||
List<Text>::Reader list = root.asReader().getAnyPointerField().getAs<List<Text>>();
|
||||
ASSERT_EQ(2u, list.size());
|
||||
EXPECT_EQ("foo", list[0]);
|
||||
EXPECT_EQ("bar", list[1]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
List<TestAllTypes>::Builder list = root.getAnyPointerField().initAs<List<TestAllTypes>>(2);
|
||||
ASSERT_EQ(2u, list.size());
|
||||
initTestMessage(list[0]);
|
||||
}
|
||||
|
||||
{
|
||||
List<TestAllTypes>::Builder list = root.getAnyPointerField().getAs<List<TestAllTypes>>();
|
||||
ASSERT_EQ(2u, list.size());
|
||||
checkTestMessage(list[0]);
|
||||
checkTestMessageAllZero(list[1]);
|
||||
}
|
||||
|
||||
{
|
||||
List<TestAllTypes>::Reader list =
|
||||
root.asReader().getAnyPointerField().getAs<List<TestAllTypes>>();
|
||||
ASSERT_EQ(2u, list.size());
|
||||
checkTestMessage(list[0]);
|
||||
checkTestMessageAllZero(list[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Any, AnyStruct) {
|
||||
MallocMessageBuilder builder;
|
||||
auto root = builder.getRoot<test::TestAnyPointer>();
|
||||
|
||||
initTestMessage(root.getAnyPointerField().initAs<TestAllTypes>());
|
||||
checkTestMessage(root.getAnyPointerField().getAs<TestAllTypes>());
|
||||
checkTestMessage(root.asReader().getAnyPointerField().getAs<TestAllTypes>());
|
||||
|
||||
auto allTypes = root.getAnyPointerField().getAs<AnyStruct>().as<TestAllTypes>();
|
||||
auto allTypesReader = root.getAnyPointerField().getAs<AnyStruct>().asReader().as<TestAllTypes>();
|
||||
allTypes.setInt32Field(100);
|
||||
EXPECT_EQ(100, allTypes.getInt32Field());
|
||||
EXPECT_EQ(100, allTypesReader.getInt32Field());
|
||||
|
||||
EXPECT_EQ(48, root.getAnyPointerField().getAs<AnyStruct>().getDataSection().size());
|
||||
EXPECT_EQ(20, root.getAnyPointerField().getAs<AnyStruct>().getPointerSection().size());
|
||||
|
||||
EXPECT_EQ(48, root.getAnyPointerField().asReader().getAs<AnyStruct>().getDataSection().size());
|
||||
EXPECT_EQ(20, root.getAnyPointerField().asReader().getAs<AnyStruct>().getPointerSection().size());
|
||||
|
||||
auto b = toAny(root.getAnyPointerField().getAs<TestAllTypes>());
|
||||
EXPECT_EQ(48, b.getDataSection().size());
|
||||
EXPECT_EQ(20, b.getPointerSection().size());
|
||||
|
||||
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
|
||||
b = root.getAnyPointerField().getAs<TestAllTypes>();
|
||||
EXPECT_EQ(48, b.getDataSection().size());
|
||||
EXPECT_EQ(20, b.getPointerSection().size());
|
||||
#endif
|
||||
|
||||
auto r = toAny(root.getAnyPointerField().getAs<TestAllTypes>().asReader());
|
||||
EXPECT_EQ(48, r.getDataSection().size());
|
||||
EXPECT_EQ(20, r.getPointerSection().size());
|
||||
|
||||
r = toAny(root.getAnyPointerField().getAs<TestAllTypes>()).asReader();
|
||||
EXPECT_EQ(48, r.getDataSection().size());
|
||||
EXPECT_EQ(20, r.getPointerSection().size());
|
||||
|
||||
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
|
||||
r = root.getAnyPointerField().getAs<TestAllTypes>().asReader();
|
||||
EXPECT_EQ(48, r.getDataSection().size());
|
||||
EXPECT_EQ(20, r.getPointerSection().size());
|
||||
#endif
|
||||
|
||||
{
|
||||
MallocMessageBuilder b2;
|
||||
auto root2 = b2.getRoot<test::TestAnyPointer>();
|
||||
auto sb = root2.getAnyPointerField().initAsAnyStruct(
|
||||
r.getDataSection().size() / 8, r.getPointerSection().size());
|
||||
|
||||
EXPECT_EQ(48, sb.getDataSection().size());
|
||||
EXPECT_EQ(20, sb.getPointerSection().size());
|
||||
|
||||
// TODO: is there a higher-level API for this?
|
||||
memcpy(sb.getDataSection().begin(), r.getDataSection().begin(), r.getDataSection().size());
|
||||
}
|
||||
|
||||
{
|
||||
auto ptrs = r.getPointerSection();
|
||||
EXPECT_EQ("foo", ptrs[0].getAs<Text>());
|
||||
EXPECT_EQ("bar", kj::heapString(ptrs[1].getAs<Data>().asChars()));
|
||||
EXPECT_EQ("xyzzy", ptrs[15].getAs<List<Text>>()[1]);
|
||||
}
|
||||
|
||||
{
|
||||
auto ptrs = b.getPointerSection();
|
||||
EXPECT_EQ("foo", ptrs[0].getAs<Text>());
|
||||
EXPECT_EQ("bar", kj::heapString(ptrs[1].getAs<Data>().asChars()));
|
||||
EXPECT_EQ("xyzzy", ptrs[15].getAs<List<Text>>()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Any, AnyList) {
|
||||
MallocMessageBuilder builder;
|
||||
auto root = builder.getRoot<test::TestAnyPointer>();
|
||||
List<TestAllTypes>::Builder b = root.getAnyPointerField().initAs<List<TestAllTypes>>(2);
|
||||
initTestMessage(b[0]);
|
||||
|
||||
auto ptr = root.getAnyPointerField().getAs<AnyList>();
|
||||
|
||||
EXPECT_EQ(2, ptr.size());
|
||||
EXPECT_EQ(48, ptr.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, ptr.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
|
||||
auto readPtr = root.getAnyPointerField().asReader().getAs<AnyList>();
|
||||
|
||||
EXPECT_EQ(2, readPtr.size());
|
||||
EXPECT_EQ(48, readPtr.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, readPtr.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
|
||||
auto alb = toAny(root.getAnyPointerField().getAs<List<TestAllTypes>>());
|
||||
EXPECT_EQ(2, alb.size());
|
||||
EXPECT_EQ(48, alb.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, alb.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
|
||||
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
|
||||
alb = root.getAnyPointerField().getAs<List<TestAllTypes>>();
|
||||
EXPECT_EQ(2, alb.size());
|
||||
EXPECT_EQ(48, alb.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, alb.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
#endif
|
||||
|
||||
auto alr = toAny(root.getAnyPointerField().getAs<List<TestAllTypes>>().asReader());
|
||||
EXPECT_EQ(2, alr.size());
|
||||
EXPECT_EQ(48, alr.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, alr.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
|
||||
alr = toAny(root.getAnyPointerField().getAs<List<TestAllTypes>>()).asReader();
|
||||
EXPECT_EQ(2, alr.size());
|
||||
EXPECT_EQ(48, alr.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, alr.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
|
||||
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
|
||||
alr = root.getAnyPointerField().getAs<List<TestAllTypes>>().asReader();
|
||||
EXPECT_EQ(2, alr.size());
|
||||
EXPECT_EQ(48, alr.as<List<AnyStruct>>()[0].getDataSection().size());
|
||||
EXPECT_EQ(20, alr.as<List<AnyStruct>>()[0].getPointerSection().size());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Any, AnyStructListCapInSchema) {
|
||||
MallocMessageBuilder builder;
|
||||
auto root = builder.getRoot<test::TestAnyOthers>();
|
||||
|
||||
{
|
||||
initTestMessage(root.initAnyStructFieldAs<TestAllTypes>());
|
||||
AnyStruct::Builder anyStruct = root.getAnyStructField();
|
||||
checkTestMessage(anyStruct.as<TestAllTypes>());
|
||||
checkTestMessage(anyStruct.asReader().as<TestAllTypes>());
|
||||
|
||||
EXPECT_TRUE(root.hasAnyStructField());
|
||||
auto orphan = root.disownAnyStructField();
|
||||
checkTestMessage(orphan.getReader().as<TestAllTypes>());
|
||||
EXPECT_FALSE(root.hasAnyStructField());
|
||||
|
||||
root.adoptAnyStructField(kj::mv(orphan));
|
||||
EXPECT_TRUE(root.hasAnyStructField());
|
||||
checkTestMessage(root.getAnyStructField().as<TestAllTypes>());
|
||||
}
|
||||
|
||||
{
|
||||
List<int>::Builder list = root.initAnyListFieldAs<List<int>>(3);
|
||||
list.set(0, 123);
|
||||
list.set(1, 456);
|
||||
list.set(2, 789);
|
||||
|
||||
AnyList::Builder anyList = root.getAnyListField();
|
||||
checkList(anyList.as<List<int>>(), {123, 456, 789});
|
||||
|
||||
EXPECT_TRUE(root.hasAnyListField());
|
||||
auto orphan = root.disownAnyListField();
|
||||
checkList(orphan.getReader().as<List<int>>(), {123, 456, 789});
|
||||
EXPECT_FALSE(root.hasAnyListField());
|
||||
|
||||
root.adoptAnyListField(kj::mv(orphan));
|
||||
EXPECT_TRUE(root.hasAnyListField());
|
||||
checkList(root.getAnyListField().as<List<int>>(), {123, 456, 789});
|
||||
}
|
||||
|
||||
#if !CAPNP_LITE
|
||||
// This portion of the test relies on a Client, not present in lite-mode.
|
||||
{
|
||||
kj::EventLoop loop;
|
||||
kj::WaitScope waitScope(loop);
|
||||
int callCount = 0;
|
||||
root.setCapabilityField(kj::heap<TestInterfaceImpl>(callCount));
|
||||
Capability::Client client = root.getCapabilityField();
|
||||
auto req = client.castAs<test::TestInterface>().fooRequest();
|
||||
req.setI(123);
|
||||
req.setJ(true);
|
||||
req.send().wait(waitScope);
|
||||
EXPECT_EQ(1, callCount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KJ_TEST("Builder::isStruct() does not corrupt segment pointer") {
|
||||
MallocMessageBuilder builder(1); // small first segment
|
||||
auto root = builder.getRoot<AnyPointer>();
|
||||
|
||||
// Do a lot of allocations so that there is likely a segment with a decent
|
||||
// amount of free space.
|
||||
initTestMessage(root.initAs<test::TestAllTypes>());
|
||||
|
||||
// This will probably get allocated in a segment that still has room for the
|
||||
// Data allocation below.
|
||||
root.initAs<test::TestAllTypes>();
|
||||
|
||||
// At one point, this caused root.builder.segment to point to the segment
|
||||
// where the struct is allocated, rather than segment where the root pointer
|
||||
// lives, i.e. segment zero.
|
||||
EXPECT_TRUE(root.isStruct());
|
||||
|
||||
// If root.builder.segment points to the wrong segment and that segment has free
|
||||
// space, then this triggers a DREQUIRE failure in WirePointer::setKindAndTarget().
|
||||
root.initAs<Data>(1);
|
||||
}
|
||||
|
||||
TEST(Any, Equals) {
|
||||
MallocMessageBuilder builderA;
|
||||
auto rootA = builderA.getRoot<test::TestAllTypes>();
|
||||
auto anyA = builderA.getRoot<AnyPointer>();
|
||||
initTestMessage(rootA);
|
||||
|
||||
MallocMessageBuilder builderB;
|
||||
auto rootB = builderB.getRoot<test::TestAllTypes>();
|
||||
auto anyB = builderB.getRoot<AnyPointer>();
|
||||
initTestMessage(rootB);
|
||||
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootA.setBoolField(false);
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootB.setBoolField(false);
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootB.setEnumField(test::TestEnum::GARPLY);
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootA.setEnumField(test::TestEnum::GARPLY);
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootA.getStructField().setTextField("buzz");
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootB.getStructField().setTextField("buzz");
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootA.initVoidList(3);
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootB.initVoidList(3);
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootA.getBoolList().set(2, true);
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootB.getBoolList().set(2, true);
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootB.getStructList()[1].setTextField("my NEW structlist 2");
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, anyA.equals(anyB));
|
||||
|
||||
rootA.getStructList()[1].setTextField("my NEW structlist 2");
|
||||
EXPECT_EQ(Equality::EQUAL, anyA.equals(anyB));
|
||||
}
|
||||
|
||||
KJ_TEST("Bit list with nonzero pad bits") {
|
||||
AlignedData<2> segment1 = {{
|
||||
0x01, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, // eleven bit-sized elements
|
||||
0xee, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // twelfth bit is set!
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments1[1] = {
|
||||
kj::arrayPtr(segment1.words, 2)
|
||||
};
|
||||
SegmentArrayMessageReader message1(kj::arrayPtr(segments1, 1));
|
||||
|
||||
AlignedData<2> segment2 = {{
|
||||
0x01, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, // eleven bit-sized elements
|
||||
0xee, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // twelfth bit is not set
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments2[1] = {
|
||||
kj::arrayPtr(segment2.words, 2)
|
||||
};
|
||||
SegmentArrayMessageReader message2(kj::arrayPtr(segments2, 1));
|
||||
|
||||
// Should be equal, despite nonzero padding.
|
||||
KJ_ASSERT(message1.getRoot<AnyList>() == message2.getRoot<AnyList>());
|
||||
}
|
||||
|
||||
KJ_TEST("Pointer list unequal to struct list") {
|
||||
AlignedData<1> segment1 = {{
|
||||
// list with zero pointer-sized elements
|
||||
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments1[1] = {
|
||||
kj::arrayPtr(segment1.words, 1)
|
||||
};
|
||||
SegmentArrayMessageReader message1(kj::arrayPtr(segments1, 1));
|
||||
|
||||
AlignedData<2> segment2 = {{
|
||||
// struct list of length zero
|
||||
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||
|
||||
// struct list tag, zero elements
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments2[1] = {
|
||||
kj::arrayPtr(segment2.words, 2)
|
||||
};
|
||||
SegmentArrayMessageReader message2(kj::arrayPtr(segments2, 1));
|
||||
|
||||
EXPECT_EQ(Equality::NOT_EQUAL, message1.getRoot<AnyList>().equals(message2.getRoot<AnyList>()));
|
||||
}
|
||||
|
||||
KJ_TEST("Truncating non-null pointer fields does not preserve equality") {
|
||||
AlignedData<3> segment1 = {{
|
||||
// list with one data word and one pointer field
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||
|
||||
// data word
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
|
||||
// non-null pointer to zero-sized struct
|
||||
0xfc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments1[1] = {
|
||||
kj::arrayPtr(segment1.words, 3)
|
||||
};
|
||||
SegmentArrayMessageReader message1(kj::arrayPtr(segments1, 1));
|
||||
|
||||
AlignedData<2> segment2 = {{
|
||||
// list with one data word and zero pointers
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// data word
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments2[1] = {
|
||||
kj::arrayPtr(segment2.words, 2)
|
||||
};
|
||||
SegmentArrayMessageReader message2(kj::arrayPtr(segments2, 1));
|
||||
|
||||
EXPECT_EQ(Equality::NOT_EQUAL,
|
||||
message1.getRoot<AnyPointer>().equals(message2.getRoot<AnyPointer>()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
|
@ -0,0 +1,269 @@
|
|||
// 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 "any.h"
|
||||
|
||||
#include <kj/debug.h>
|
||||
|
||||
#if !CAPNP_LITE
|
||||
#include "capability.h"
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
namespace capnp {
|
||||
|
||||
#if !CAPNP_LITE
|
||||
|
||||
kj::Own<ClientHook> PipelineHook::getPipelinedCap(kj::Array<PipelineOp>&& ops) {
|
||||
return getPipelinedCap(ops.asPtr());
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> AnyPointer::Reader::getPipelinedCap(
|
||||
kj::ArrayPtr<const PipelineOp> ops) const {
|
||||
_::PointerReader pointer = reader;
|
||||
|
||||
for (auto& op: ops) {
|
||||
switch (op.type) {
|
||||
case PipelineOp::Type::NOOP:
|
||||
break;
|
||||
|
||||
case PipelineOp::Type::GET_POINTER_FIELD:
|
||||
pointer = pointer.getStruct(nullptr).getPointerField(bounded(op.pointerIndex) * POINTERS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pointer.getCapability();
|
||||
}
|
||||
|
||||
AnyPointer::Pipeline AnyPointer::Pipeline::noop() {
|
||||
auto newOps = kj::heapArray<PipelineOp>(ops.size());
|
||||
for (auto i: kj::indices(ops)) {
|
||||
newOps[i] = ops[i];
|
||||
}
|
||||
return Pipeline(hook->addRef(), kj::mv(newOps));
|
||||
}
|
||||
|
||||
AnyPointer::Pipeline AnyPointer::Pipeline::getPointerField(uint16_t pointerIndex) {
|
||||
auto newOps = kj::heapArray<PipelineOp>(ops.size() + 1);
|
||||
for (auto i: kj::indices(ops)) {
|
||||
newOps[i] = ops[i];
|
||||
}
|
||||
auto& newOp = newOps[ops.size()];
|
||||
newOp.type = PipelineOp::GET_POINTER_FIELD;
|
||||
newOp.pointerIndex = pointerIndex;
|
||||
|
||||
return Pipeline(hook->addRef(), kj::mv(newOps));
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> AnyPointer::Pipeline::asCap() {
|
||||
return hook->getPipelinedCap(ops);
|
||||
}
|
||||
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
Equality AnyStruct::Reader::equals(AnyStruct::Reader right) const {
|
||||
auto dataL = getDataSection();
|
||||
size_t dataSizeL = dataL.size();
|
||||
while(dataSizeL > 0 && dataL[dataSizeL - 1] == 0) {
|
||||
-- dataSizeL;
|
||||
}
|
||||
|
||||
auto dataR = right.getDataSection();
|
||||
size_t dataSizeR = dataR.size();
|
||||
while(dataSizeR > 0 && dataR[dataSizeR - 1] == 0) {
|
||||
-- dataSizeR;
|
||||
}
|
||||
|
||||
if(dataSizeL != dataSizeR) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
|
||||
if(0 != memcmp(dataL.begin(), dataR.begin(), dataSizeL)) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
|
||||
auto ptrsL = getPointerSection();
|
||||
size_t ptrsSizeL = ptrsL.size();
|
||||
while (ptrsSizeL > 0 && ptrsL[ptrsSizeL - 1].isNull()) {
|
||||
-- ptrsSizeL;
|
||||
}
|
||||
|
||||
auto ptrsR = right.getPointerSection();
|
||||
size_t ptrsSizeR = ptrsR.size();
|
||||
while (ptrsSizeR > 0 && ptrsR[ptrsSizeR - 1].isNull()) {
|
||||
-- ptrsSizeR;
|
||||
}
|
||||
|
||||
if(ptrsSizeL != ptrsSizeR) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
auto eqResult = Equality::EQUAL;
|
||||
for (; i < ptrsSizeL; i++) {
|
||||
auto l = ptrsL[i];
|
||||
auto r = ptrsR[i];
|
||||
switch(l.equals(r)) {
|
||||
case Equality::EQUAL:
|
||||
break;
|
||||
case Equality::NOT_EQUAL:
|
||||
return Equality::NOT_EQUAL;
|
||||
case Equality::UNKNOWN_CONTAINS_CAPS:
|
||||
eqResult = Equality::UNKNOWN_CONTAINS_CAPS;
|
||||
break;
|
||||
default:
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return eqResult;
|
||||
}
|
||||
|
||||
kj::StringPtr KJ_STRINGIFY(Equality res) {
|
||||
switch(res) {
|
||||
case Equality::NOT_EQUAL:
|
||||
return "NOT_EQUAL";
|
||||
case Equality::EQUAL:
|
||||
return "EQUAL";
|
||||
case Equality::UNKNOWN_CONTAINS_CAPS:
|
||||
return "UNKNOWN_CONTAINS_CAPS";
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
Equality AnyList::Reader::equals(AnyList::Reader right) const {
|
||||
if(size() != right.size()) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
|
||||
if (getElementSize() != right.getElementSize()) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
|
||||
auto eqResult = Equality::EQUAL;
|
||||
switch(getElementSize()) {
|
||||
case ElementSize::VOID:
|
||||
case ElementSize::BIT:
|
||||
case ElementSize::BYTE:
|
||||
case ElementSize::TWO_BYTES:
|
||||
case ElementSize::FOUR_BYTES:
|
||||
case ElementSize::EIGHT_BYTES: {
|
||||
size_t cmpSize = getRawBytes().size();
|
||||
|
||||
if (getElementSize() == ElementSize::BIT && size() % 8 != 0) {
|
||||
// The list does not end on a byte boundary. We need special handling for the final
|
||||
// byte because we only care about the bits that are actually elements of the list.
|
||||
|
||||
uint8_t mask = (1 << (size() % 8)) - 1; // lowest size() bits set
|
||||
if ((getRawBytes()[cmpSize - 1] & mask) != (right.getRawBytes()[cmpSize - 1] & mask)) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
cmpSize -= 1;
|
||||
}
|
||||
|
||||
if (memcmp(getRawBytes().begin(), right.getRawBytes().begin(), cmpSize) == 0) {
|
||||
return Equality::EQUAL;
|
||||
} else {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
}
|
||||
case ElementSize::POINTER:
|
||||
case ElementSize::INLINE_COMPOSITE: {
|
||||
auto llist = as<List<AnyStruct>>();
|
||||
auto rlist = right.as<List<AnyStruct>>();
|
||||
for(size_t i = 0; i < size(); i++) {
|
||||
switch(llist[i].equals(rlist[i])) {
|
||||
case Equality::EQUAL:
|
||||
break;
|
||||
case Equality::NOT_EQUAL:
|
||||
return Equality::NOT_EQUAL;
|
||||
case Equality::UNKNOWN_CONTAINS_CAPS:
|
||||
eqResult = Equality::UNKNOWN_CONTAINS_CAPS;
|
||||
break;
|
||||
default:
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
return eqResult;
|
||||
}
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
Equality AnyPointer::Reader::equals(AnyPointer::Reader right) const {
|
||||
if(getPointerType() != right.getPointerType()) {
|
||||
return Equality::NOT_EQUAL;
|
||||
}
|
||||
switch(getPointerType()) {
|
||||
case PointerType::NULL_:
|
||||
return Equality::EQUAL;
|
||||
case PointerType::STRUCT:
|
||||
return getAs<AnyStruct>().equals(right.getAs<AnyStruct>());
|
||||
case PointerType::LIST:
|
||||
return getAs<AnyList>().equals(right.getAs<AnyList>());
|
||||
case PointerType::CAPABILITY:
|
||||
return Equality::UNKNOWN_CONTAINS_CAPS;
|
||||
}
|
||||
// There aren't currently any other types of pointers
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
bool AnyPointer::Reader::operator==(AnyPointer::Reader right) const {
|
||||
switch(equals(right)) {
|
||||
case Equality::EQUAL:
|
||||
return true;
|
||||
case Equality::NOT_EQUAL:
|
||||
return false;
|
||||
case Equality::UNKNOWN_CONTAINS_CAPS:
|
||||
KJ_FAIL_REQUIRE(
|
||||
"operator== cannot determine equality of capabilities; use equals() instead if you need to handle this case");
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
bool AnyStruct::Reader::operator==(AnyStruct::Reader right) const {
|
||||
switch(equals(right)) {
|
||||
case Equality::EQUAL:
|
||||
return true;
|
||||
case Equality::NOT_EQUAL:
|
||||
return false;
|
||||
case Equality::UNKNOWN_CONTAINS_CAPS:
|
||||
KJ_FAIL_REQUIRE(
|
||||
"operator== cannot determine equality of capabilities; use equals() instead if you need to handle this case");
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
bool AnyList::Reader::operator==(AnyList::Reader right) const {
|
||||
switch(equals(right)) {
|
||||
case Equality::EQUAL:
|
||||
return true;
|
||||
case Equality::NOT_EQUAL:
|
||||
return false;
|
||||
case Equality::UNKNOWN_CONTAINS_CAPS:
|
||||
KJ_FAIL_REQUIRE(
|
||||
"operator== cannot determine equality of capabilities; use equals() instead if you need to handle this case");
|
||||
}
|
||||
KJ_UNREACHABLE;
|
||||
}
|
||||
|
||||
} // namespace capnp
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,338 @@
|
|||
// 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>
|
||||
|
||||
#if !CAPNP_LITE
|
||||
#include "capability.h"
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
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 = limit;
|
||||
uint64_t newValue = oldValue + unbound(amount / WORDS);
|
||||
if (newValue > oldValue) {
|
||||
limit = 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();
|
||||
}
|
||||
|
||||
void SegmentBuilder::throwNotWritable() {
|
||||
KJ_FAIL_REQUIRE(
|
||||
"Tried to form a Builder to an external data segment referenced by the MessageBuilder. "
|
||||
"When you use Orphanage::reference*(), you are not allowed to obtain Builders to the "
|
||||
"referenced data, only Readers, because that data is const.");
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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(), verifySegmentSize(firstSegment.size())) {}
|
||||
|
||||
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 = verifySegmentSize(newSegment.size());
|
||||
|
||||
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(MessageBuilder* message,
|
||||
kj::ArrayPtr<MessageBuilder::SegmentInit> segments)
|
||||
: message(message),
|
||||
segment0(this, SegmentId(0), segments[0].space.begin(),
|
||||
verifySegmentSize(segments[0].space.size()),
|
||||
&this->dummyLimiter, verifySegmentSize(segments[0].wordsUsed)) {
|
||||
if (segments.size() > 1) {
|
||||
kj::Vector<kj::Own<SegmentBuilder>> builders(segments.size() - 1);
|
||||
|
||||
uint i = 1;
|
||||
for (auto& segment: segments.slice(1, segments.size())) {
|
||||
builders.add(kj::heap<SegmentBuilder>(
|
||||
this, SegmentId(i++), segment.space.begin(), verifySegmentSize(segment.space.size()),
|
||||
&this->dummyLimiter, verifySegmentSize(segment.wordsUsed)));
|
||||
}
|
||||
|
||||
kj::Vector<kj::ArrayPtr<const word>> forOutput;
|
||||
forOutput.resize(segments.size());
|
||||
|
||||
segmentWithSpace = builders.back();
|
||||
|
||||
this->moreSegments = kj::heap<MultiSegmentState>(
|
||||
MultiSegmentState { kj::mv(builders), kj::mv(forOutput) });
|
||||
|
||||
} else {
|
||||
segmentWithSpace = &segment0;
|
||||
}
|
||||
}
|
||||
|
||||
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 = verifySegmentSize(ptr.size());
|
||||
|
||||
// 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::addExternalSegment(kj::ArrayPtr<const word> content) {
|
||||
return addSegmentInternal(content);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SegmentBuilder* BuilderArena::addSegmentInternal(kj::ArrayPtr<T> 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 external 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;
|
||||
}
|
||||
}
|
||||
|
||||
#if !CAPNP_LITE
|
||||
kj::Maybe<kj::Own<ClientHook>> BuilderArena::LocalCapTable::extractCap(uint index) {
|
||||
if (index < capTable.size()) {
|
||||
return capTable[index].map([](kj::Own<ClientHook>& cap) { return cap->addRef(); });
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint BuilderArena::LocalCapTable::injectCap(kj::Own<ClientHook>&& cap) {
|
||||
uint result = capTable.size();
|
||||
capTable.add(kj::mv(cap));
|
||||
return result;
|
||||
}
|
||||
|
||||
void BuilderArena::LocalCapTable::dropCap(uint index) {
|
||||
KJ_ASSERT(index < capTable.size(), "Invalid capability descriptor in message.") {
|
||||
return;
|
||||
}
|
||||
capTable[index] = nullptr;
|
||||
}
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
|
@ -0,0 +1,493 @@
|
|||
// 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
|
||||
|
||||
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#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>
|
||||
|
||||
#if !CAPNP_LITE
|
||||
#include "capability.h"
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
namespace capnp {
|
||||
|
||||
#if !CAPNP_LITE
|
||||
class ClientHook;
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
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.
|
||||
|
||||
inline void reset(WordCount64 limit);
|
||||
|
||||
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:
|
||||
volatile uint64_t limit;
|
||||
// Current limit, decremented each time catRead() is called. Volatile because multiple threads
|
||||
// could be trying to modify it at once. (This is not real thread-safety, but good enough for
|
||||
// the purpose of this class. See class comment.)
|
||||
|
||||
KJ_DISALLOW_COPY(ReadLimiter);
|
||||
};
|
||||
|
||||
#if !CAPNP_LITE
|
||||
class BrokenCapFactory {
|
||||
// Callback for constructing broken caps. We use this so that we can avoid arena.c++ having a
|
||||
// link-time dependency on capability code that lives in libcapnp-rpc.
|
||||
|
||||
public:
|
||||
virtual kj::Own<ClientHook> newBrokenCap(kj::StringPtr description) = 0;
|
||||
virtual kj::Own<ClientHook> newNullCap() = 0;
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
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(SegmentReader);
|
||||
|
||||
friend class SegmentBuilder;
|
||||
|
||||
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, SegmentWordCount wordsUsed = ZERO * WORDS);
|
||||
inline SegmentBuilder(BuilderArena* arena, SegmentId id, const 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(void checkWritable());
|
||||
// Throw an exception if the segment is read-only (meaning it is a reference to external data).
|
||||
|
||||
KJ_ALWAYS_INLINE(word* getPtrUnchecked(SegmentWordCount offset));
|
||||
// Get a writable pointer into the segment. Throws an exception if the segment is read-only (i.e.
|
||||
// a reference to external immutable data).
|
||||
|
||||
inline BuilderArena* getArena();
|
||||
|
||||
inline kj::ArrayPtr<const word> currentlyAllocated();
|
||||
|
||||
inline void reset();
|
||||
|
||||
inline bool isWritable() { return !readOnly; }
|
||||
|
||||
inline void tryTruncate(word* from, word* to);
|
||||
// If `from` points just past the current end of the segment, then move the end back to `to`.
|
||||
// Otherwise, do nothing.
|
||||
|
||||
inline bool tryExtend(word* from, word* to);
|
||||
// If `from` points just past the current end of the segment, and `to` is within the segment
|
||||
// boundaries, then move the end up to `to` and return true. Otherwise, do nothing and return
|
||||
// false.
|
||||
|
||||
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.
|
||||
|
||||
bool readOnly;
|
||||
|
||||
void throwNotWritable();
|
||||
|
||||
KJ_DISALLOW_COPY(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(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(MessageBuilder* message, kj::ArrayPtr<MessageBuilder::SegmentInit> segments);
|
||||
~BuilderArena() noexcept(false);
|
||||
KJ_DISALLOW_COPY(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.
|
||||
|
||||
inline CapTableBuilder* getLocalCapTable() {
|
||||
// Return a CapTableBuilder that merely implements local loopback. That is, you can set
|
||||
// capabilities, then read the same capabilities back, but there is no intent ever to transmit
|
||||
// these capabilities. A MessageBuilder that isn't imbued with some other CapTable uses this
|
||||
// by default.
|
||||
//
|
||||
// TODO(cleanup): It's sort of a hack that this exists. In theory, perhaps, unimbued
|
||||
// MessageBuilders should throw exceptions on any attempt to access capability fields, like
|
||||
// unimbued MessageReaders do. However, lots of code exists which uses MallocMessageBuilder
|
||||
// as a temporary holder for data to be copied in and out (without being serialized), and it
|
||||
// is expected that such data can include capabilities, which is admittedly reasonable.
|
||||
// Therefore, all MessageBuilders must have a cap table by default. Arguably we should
|
||||
// deprecate this usage and instead define a new helper type for this exact purpose.
|
||||
|
||||
return &localCapTable;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
SegmentBuilder* addExternalSegment(kj::ArrayPtr<const word> content);
|
||||
// Add a new segment to the arena which points to some existing memory region. The segment is
|
||||
// assumed to be completley full; the arena will never allocate from it. In fact, the segment
|
||||
// is considered read-only. Any attempt to get a Builder pointing into this segment will throw
|
||||
// an exception. Readers are allowed, however.
|
||||
//
|
||||
// This can be used to inject some external data into a message without a copy, e.g. embedding a
|
||||
// large mmap'd file into a message as `Data` without forcing that data to actually be read in
|
||||
// from disk (until the message itself is written out). `Orphanage` provides the public API for
|
||||
// this feature.
|
||||
|
||||
// implements Arena ------------------------------------------------
|
||||
SegmentReader* tryGetSegment(SegmentId id) override;
|
||||
void reportReadLimitReached() override;
|
||||
|
||||
private:
|
||||
MessageBuilder* message;
|
||||
ReadLimiter dummyLimiter;
|
||||
|
||||
class LocalCapTable: public CapTableBuilder {
|
||||
#if !CAPNP_LITE
|
||||
public:
|
||||
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override;
|
||||
uint injectCap(kj::Own<ClientHook>&& cap) override;
|
||||
void dropCap(uint index) override;
|
||||
|
||||
private:
|
||||
kj::Vector<kj::Maybe<kj::Own<ClientHook>>> capTable;
|
||||
#endif // ! CAPNP_LITE
|
||||
};
|
||||
|
||||
LocalCapTable localCapTable;
|
||||
|
||||
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. This is not necessarily the last segment because addExternalSegment() may add a
|
||||
// segment that is already-full, in which case we don't update this pointer.
|
||||
|
||||
template <typename T> // Can be `word` or `const word`.
|
||||
SegmentBuilder* addSegmentInternal(kj::ArrayPtr<T> content);
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
inline ReadLimiter::ReadLimiter()
|
||||
: limit(kj::maxValue) {}
|
||||
|
||||
inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(unbound(limit / WORDS)) {}
|
||||
|
||||
inline void ReadLimiter::reset(WordCount64 limit) { this->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 = limit;
|
||||
if (KJ_UNLIKELY(unbound(amount / WORDS) > current)) {
|
||||
arena->reportReadLimitReached();
|
||||
return false;
|
||||
} else {
|
||||
limit = 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, SegmentWordCount wordsUsed)
|
||||
: SegmentReader(arena, id, ptr, size, readLimiter),
|
||||
pos(ptr + wordsUsed), readOnly(false) {}
|
||||
inline SegmentBuilder::SegmentBuilder(
|
||||
BuilderArena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
|
||||
ReadLimiter* readLimiter)
|
||||
: SegmentReader(arena, id, ptr, size, readLimiter),
|
||||
// const_cast is safe here because the member won't ever be dereferenced because it appears
|
||||
// to point to the end of the segment anyway.
|
||||
pos(const_cast<word*>(ptr + size)), readOnly(true) {}
|
||||
inline SegmentBuilder::SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
|
||||
ReadLimiter* readLimiter)
|
||||
: SegmentReader(arena, id, nullptr, ZERO * WORDS, readLimiter),
|
||||
pos(nullptr), readOnly(false) {}
|
||||
|
||||
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 void SegmentBuilder::checkWritable() {
|
||||
if (KJ_UNLIKELY(readOnly)) throwNotWritable();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
inline void SegmentBuilder::reset() {
|
||||
word* start = getPtrUnchecked(ZERO * WORDS);
|
||||
memset(start, 0, (pos - start) * sizeof(word));
|
||||
pos = start;
|
||||
}
|
||||
|
||||
inline void SegmentBuilder::tryTruncate(word* from, word* to) {
|
||||
if (pos == from) pos = to;
|
||||
}
|
||||
|
||||
inline bool SegmentBuilder::tryExtend(word* from, word* to) {
|
||||
// Careful about overflow.
|
||||
if (pos == from && to <= ptr.end() && to >= from) {
|
||||
pos = to;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
|
@ -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.
|
||||
|
||||
#include "blob.h"
|
||||
#include <kj/compat/gtest.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "test-util.h"
|
||||
|
||||
// TODO(test): This test is outdated -- it predates the retrofit of Text and Data on top of
|
||||
// kj::ArrayPtr/kj::StringPtr. Clean it up.
|
||||
|
||||
namespace capnp {
|
||||
namespace {
|
||||
|
||||
TEST(Blob, Text) {
|
||||
std::string str = "foo";
|
||||
Text::Reader text = str.c_str();
|
||||
|
||||
EXPECT_EQ("foo", text);
|
||||
EXPECT_STREQ("foo", text.cStr());
|
||||
EXPECT_STREQ("foo", text.begin());
|
||||
EXPECT_EQ(3u, text.size());
|
||||
|
||||
Text::Reader text2 = "bar";
|
||||
EXPECT_EQ("bar", text2);
|
||||
|
||||
char c[4] = "baz";
|
||||
Text::Reader text3(c);
|
||||
EXPECT_EQ("baz", text3);
|
||||
|
||||
Text::Builder builder(c, 3);
|
||||
EXPECT_EQ("baz", builder);
|
||||
|
||||
EXPECT_EQ(kj::arrayPtr("az", 2), builder.slice(1, 3));
|
||||
}
|
||||
|
||||
Data::Reader dataLit(const char* str) {
|
||||
return Data::Reader(reinterpret_cast<const byte*>(str), strlen(str));
|
||||
}
|
||||
|
||||
TEST(Blob, Data) {
|
||||
Data::Reader data = dataLit("foo");
|
||||
|
||||
EXPECT_EQ(dataLit("foo"), data);
|
||||
EXPECT_EQ(3u, data.size());
|
||||
|
||||
Data::Reader data2 = dataLit("bar");
|
||||
EXPECT_EQ(dataLit("bar"), data2);
|
||||
|
||||
byte c[4] = "baz";
|
||||
Data::Reader data3(c, 3);
|
||||
EXPECT_EQ(dataLit("baz"), data3);
|
||||
|
||||
Data::Builder builder(c, 3);
|
||||
EXPECT_EQ(dataLit("baz"), builder);
|
||||
|
||||
EXPECT_EQ(dataLit("az"), builder.slice(1, 3));
|
||||
}
|
||||
|
||||
TEST(Blob, Compare) {
|
||||
EXPECT_TRUE (Text::Reader("foo") == Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("foo") != Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("foo") <= Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("foo") >= Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("foo") < Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("foo") > Text::Reader("foo"));
|
||||
|
||||
EXPECT_FALSE(Text::Reader("foo") == Text::Reader("bar"));
|
||||
EXPECT_TRUE (Text::Reader("foo") != Text::Reader("bar"));
|
||||
EXPECT_FALSE(Text::Reader("foo") <= Text::Reader("bar"));
|
||||
EXPECT_TRUE (Text::Reader("foo") >= Text::Reader("bar"));
|
||||
EXPECT_FALSE(Text::Reader("foo") < Text::Reader("bar"));
|
||||
EXPECT_TRUE (Text::Reader("foo") > Text::Reader("bar"));
|
||||
|
||||
EXPECT_FALSE(Text::Reader("bar") == Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("bar") != Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("bar") <= Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("bar") >= Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("bar") < Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("bar") > Text::Reader("foo"));
|
||||
|
||||
EXPECT_FALSE(Text::Reader("foobar") == Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("foobar") != Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("foobar") <= Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("foobar") >= Text::Reader("foo"));
|
||||
EXPECT_FALSE(Text::Reader("foobar") < Text::Reader("foo"));
|
||||
EXPECT_TRUE (Text::Reader("foobar") > Text::Reader("foo"));
|
||||
|
||||
EXPECT_FALSE(Text::Reader("foo") == Text::Reader("foobar"));
|
||||
EXPECT_TRUE (Text::Reader("foo") != Text::Reader("foobar"));
|
||||
EXPECT_TRUE (Text::Reader("foo") <= Text::Reader("foobar"));
|
||||
EXPECT_FALSE(Text::Reader("foo") >= Text::Reader("foobar"));
|
||||
EXPECT_TRUE (Text::Reader("foo") < Text::Reader("foobar"));
|
||||
EXPECT_FALSE(Text::Reader("foo") > Text::Reader("foobar"));
|
||||
}
|
||||
|
||||
#if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP
|
||||
TEST(Blob, StlInterop) {
|
||||
std::string foo = "foo";
|
||||
Text::Reader reader = foo;
|
||||
|
||||
EXPECT_EQ("foo", reader);
|
||||
|
||||
std::string bar = reader;
|
||||
EXPECT_EQ("foo", bar);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
} // namespace capnp
|
|
@ -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
|
|
@ -0,0 +1,219 @@
|
|||
// 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
|
||||
|
||||
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#include <kj/common.h>
|
||||
#include <kj/string.h>
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
struct Data {
|
||||
Data() = delete;
|
||||
class Reader;
|
||||
class Builder;
|
||||
class Pipeline {};
|
||||
};
|
||||
|
||||
struct Text {
|
||||
Text() = delete;
|
||||
class Reader;
|
||||
class Builder;
|
||||
class Pipeline {};
|
||||
};
|
||||
|
||||
class Data::Reader: public kj::ArrayPtr<const byte> {
|
||||
// Points to a blob of bytes. The usual Reader rules apply -- Data::Reader behaves like a simple
|
||||
// pointer which does not own its target, can be passed by value, etc.
|
||||
|
||||
public:
|
||||
typedef Data Reads;
|
||||
|
||||
Reader() = default;
|
||||
inline Reader(decltype(nullptr)): ArrayPtr<const byte>(nullptr) {}
|
||||
inline Reader(const byte* value, size_t size): ArrayPtr<const byte>(value, size) {}
|
||||
inline Reader(const kj::Array<const byte>& value): ArrayPtr<const byte>(value) {}
|
||||
inline Reader(const ArrayPtr<const byte>& value): ArrayPtr<const byte>(value) {}
|
||||
inline Reader(const kj::Array<byte>& value): ArrayPtr<const byte>(value) {}
|
||||
inline Reader(const ArrayPtr<byte>& value): ArrayPtr<const byte>(value) {}
|
||||
};
|
||||
|
||||
class Text::Reader: public kj::StringPtr {
|
||||
// Like Data::Reader, but points at NUL-terminated UTF-8 text. The NUL terminator is not counted
|
||||
// in the size but must be present immediately after the last byte.
|
||||
//
|
||||
// Text::Reader's interface contract is that its data MUST be NUL-terminated. The producer of
|
||||
// the Text::Reader must guarantee this, so that the consumer need not check. The data SHOULD
|
||||
// also be valid UTF-8, but this is NOT guaranteed -- the consumer must verify if it cares.
|
||||
|
||||
public:
|
||||
typedef Text Reads;
|
||||
|
||||
Reader() = default;
|
||||
inline Reader(decltype(nullptr)): StringPtr(nullptr) {}
|
||||
inline Reader(const char* value): StringPtr(value) {}
|
||||
inline Reader(const char* value, size_t size): StringPtr(value, size) {}
|
||||
inline Reader(const kj::String& value): StringPtr(value) {}
|
||||
inline Reader(const StringPtr& value): StringPtr(value) {}
|
||||
|
||||
#if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP
|
||||
template <typename T, typename = decltype(kj::instance<T>().c_str())>
|
||||
inline Reader(const T& t): StringPtr(t) {}
|
||||
// Allow implicit conversion from any class that has a c_str() method (namely, std::string).
|
||||
// We use a template trick to detect std::string in order to avoid including the header for
|
||||
// those who don't want it.
|
||||
#endif
|
||||
};
|
||||
|
||||
class Data::Builder: public kj::ArrayPtr<byte> {
|
||||
// Like Data::Reader except the pointers aren't const.
|
||||
|
||||
public:
|
||||
typedef Data Builds;
|
||||
|
||||
Builder() = default;
|
||||
inline Builder(decltype(nullptr)): ArrayPtr<byte>(nullptr) {}
|
||||
inline Builder(byte* value, size_t size): ArrayPtr<byte>(value, size) {}
|
||||
inline Builder(kj::Array<byte>& value): ArrayPtr<byte>(value) {}
|
||||
inline Builder(ArrayPtr<byte> value): ArrayPtr<byte>(value) {}
|
||||
|
||||
inline Data::Reader asReader() const {
|
||||
return Data::Reader(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 a == b.asString(); }
|
||||
inline bool operator!=(const char* a, const Text::Builder& b) { return a != b.asString(); }
|
||||
|
||||
inline Text::Builder::operator kj::StringPtr() const {
|
||||
return kj::StringPtr(content.begin(), content.size() - 1);
|
||||
}
|
||||
|
||||
inline kj::StringPtr Text::Builder::asString() const {
|
||||
return kj::StringPtr(content.begin(), content.size() - 1);
|
||||
}
|
||||
|
||||
inline Text::Builder::operator kj::ArrayPtr<char>() {
|
||||
return content.slice(0, content.size() - 1);
|
||||
}
|
||||
|
||||
inline kj::ArrayPtr<char> Text::Builder::asArray() {
|
||||
return content.slice(0, content.size() - 1);
|
||||
}
|
||||
|
||||
inline Text::Builder::operator kj::ArrayPtr<const char>() const {
|
||||
return content.slice(0, content.size() - 1);
|
||||
}
|
||||
|
||||
inline kj::ArrayPtr<const char> Text::Builder::asArray() const {
|
||||
return content.slice(0, content.size() - 1);
|
||||
}
|
||||
|
||||
inline kj::StringPtr Text::Builder::slice(size_t start) const {
|
||||
return asReader().slice(start);
|
||||
}
|
||||
inline kj::ArrayPtr<const char> Text::Builder::slice(size_t start, size_t end) const {
|
||||
return content.slice(start, end);
|
||||
}
|
||||
|
||||
inline Text::Builder Text::Builder::slice(size_t start) {
|
||||
return Text::Builder(content.slice(start, content.size()));
|
||||
}
|
||||
inline kj::ArrayPtr<char> Text::Builder::slice(size_t start, size_t end) {
|
||||
return content.slice(start, end);
|
||||
}
|
||||
|
||||
} // namespace capnp
|
|
@ -0,0 +1,76 @@
|
|||
#! /bin/sh
|
||||
|
||||
# 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 is a one-off test rule.
|
||||
|
||||
set -eu
|
||||
|
||||
echo findProvider special:ekam-interceptor
|
||||
read INTERCEPTOR
|
||||
|
||||
if test "$INTERCEPTOR" = ""; then
|
||||
echo "error: couldn't find intercept.so." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo findProvider file:capnp
|
||||
read CAPNP
|
||||
|
||||
if test "$CAPNP" = ""; then
|
||||
echo "error: couldn't find capnp." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo findProvider file:capnpc-c++
|
||||
read CAPNPC_CXX
|
||||
|
||||
if test "$CAPNPC_CXX" = ""; then
|
||||
echo "error: couldn't find capnpc-c++." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p tmp/capnp/bootstrap-test-tmp
|
||||
|
||||
INPUTS="capnp/c++.capnp capnp/schema.capnp capnp/compiler/lexer.capnp capnp/compiler/grammar.capnp \
|
||||
capnp/rpc.capnp capnp/rpc-twoparty.capnp capnp/persistent.capnp"
|
||||
|
||||
SRC_INPUTS=""
|
||||
for file in $INPUTS; do
|
||||
echo findProvider file:$file
|
||||
read srcfile
|
||||
SRC_INPUTS="$SRC_INPUTS $srcfile"
|
||||
done
|
||||
|
||||
$CAPNP compile --src-prefix=src -Isrc --no-standard-import \
|
||||
-o$CAPNPC_CXX:tmp/capnp/bootstrap-test-tmp $SRC_INPUTS
|
||||
|
||||
for file in $INPUTS; do
|
||||
for ext in h c++; do
|
||||
echo findProvider file:$file.$ext
|
||||
read srcfile
|
||||
test "x$srcfile" != x || (echo "missing: $file.$ext" >&2 && exit 1)
|
||||
diff -u $srcfile tmp/capnp/bootstrap-test-tmp/$file.$ext >&2
|
||||
done
|
||||
done
|
||||
|
||||
echo passed
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0xbdf87d7bb8304e81;
|
||||
$namespace("capnp::annotations");
|
||||
|
||||
annotation namespace(file): Text;
|
||||
annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text;
|
|
@ -0,0 +1,68 @@
|
|||
// Generated by Cap'n Proto compiler, DO NOT EDIT
|
||||
// source: c++.capnp
|
||||
|
||||
#include "c++.capnp.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace schemas {
|
||||
static const ::capnp::_::AlignedData<21> b_b9c6f99ebf805f2c = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
44, 95, 128, 191, 158, 249, 198, 185,
|
||||
16, 0, 0, 0, 5, 0, 1, 0,
|
||||
129, 78, 48, 184, 123, 125, 248, 189,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 210, 0, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 43,
|
||||
43, 46, 99, 97, 112, 110, 112, 58,
|
||||
110, 97, 109, 101, 115, 112, 97, 99,
|
||||
101, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_b9c6f99ebf805f2c = b_b9c6f99ebf805f2c.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_b9c6f99ebf805f2c = {
|
||||
0xb9c6f99ebf805f2c, b_b9c6f99ebf805f2c.words, 21, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_b9c6f99ebf805f2c, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<20> b_f264a779fef191ce = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
206, 145, 241, 254, 121, 167, 100, 242,
|
||||
16, 0, 0, 0, 5, 0, 252, 7,
|
||||
129, 78, 48, 184, 123, 125, 248, 189,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 170, 0, 0, 0,
|
||||
29, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
24, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 43,
|
||||
43, 46, 99, 97, 112, 110, 112, 58,
|
||||
110, 97, 109, 101, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_f264a779fef191ce = b_f264a779fef191ce.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_f264a779fef191ce = {
|
||||
0xf264a779fef191ce, b_f264a779fef191ce.words, 20, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_f264a779fef191ce, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
} // namespace schemas
|
||||
} // namespace capnp
|
|
@ -0,0 +1,32 @@
|
|||
// Generated by Cap'n Proto compiler, DO NOT EDIT
|
||||
// source: c++.capnp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <capnp/generated-header-support.h>
|
||||
#include <kj/windows-sanity.h>
|
||||
|
||||
#if CAPNP_VERSION != 7000
|
||||
#error "Version mismatch between generated code and library headers. You must use the same version of the Cap'n Proto compiler and library."
|
||||
#endif
|
||||
|
||||
|
||||
namespace capnp {
|
||||
namespace schemas {
|
||||
|
||||
CAPNP_DECLARE_SCHEMA(b9c6f99ebf805f2c);
|
||||
CAPNP_DECLARE_SCHEMA(f264a779fef191ce);
|
||||
|
||||
} // namespace schemas
|
||||
} // namespace capnp
|
||||
|
||||
namespace capnp {
|
||||
namespace annotations {
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
// Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors
|
||||
// Licensed under the MIT License:
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "message.h"
|
||||
#include "any.h"
|
||||
#include <kj/debug.h>
|
||||
#include <kj/test.h>
|
||||
#include "test-util.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace _ { // private
|
||||
using test::TestLists;
|
||||
namespace {
|
||||
|
||||
|
||||
KJ_TEST("canonicalize yields canonical message") {
|
||||
MallocMessageBuilder builder;
|
||||
auto root = builder.initRoot<TestAllTypes>();
|
||||
|
||||
initTestMessage(root);
|
||||
|
||||
auto canonicalWords = canonicalize(root.asReader());
|
||||
// Throws an exception on canonicalization failure.
|
||||
|
||||
kj::ArrayPtr<const capnp::word> canonicalSegments[1] = {canonicalWords.asPtr()};
|
||||
capnp::SegmentArrayMessageReader canonicalReader(kj::arrayPtr(canonicalSegments, 1));
|
||||
|
||||
KJ_ASSERT(AnyStruct::Reader(root.asReader()) ==
|
||||
AnyStruct::Reader(canonicalReader.getRoot<TestAllTypes>()));
|
||||
}
|
||||
|
||||
KJ_TEST("canonicalize succeeds on empty struct") {
|
||||
MallocMessageBuilder builder;
|
||||
auto root = builder.initRoot<TestAllTypes>();
|
||||
|
||||
canonicalize(root.asReader()); // Throws an exception on canoncalization failure.
|
||||
}
|
||||
|
||||
KJ_TEST("data word with only its most significant bit set does not get truncated") {
|
||||
AlignedData<3> segment = {{
|
||||
// Struct pointer, body immediately follows, two data words
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// First data word
|
||||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||||
|
||||
// Second data word, all zero except most significant bit
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 3)};
|
||||
SegmentArrayMessageReader messageReader(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(messageReader.isCanonical());
|
||||
auto canonicalWords = canonicalize(messageReader.getRoot<TestAllTypes>());
|
||||
|
||||
// At one point this failed because an off-by-one bug in canonicalization
|
||||
// caused the second word of the data section to be truncated.
|
||||
ASSERT_EQ(canonicalWords.asBytes(), kj::arrayPtr(segment.bytes, 3 * 8));
|
||||
}
|
||||
|
||||
KJ_TEST("INLINE_COMPOSITE data word with only its most significant bit set does not get truncated") {
|
||||
AlignedData<5> segment = {{
|
||||
// Struct pointer, body immediately follows, one pointer
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
|
||||
// List pointer, no offset, inline composite, two words long
|
||||
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
|
||||
|
||||
// Tag word, list has one element with two data words and no pointers
|
||||
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// First data word
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
||||
|
||||
// Second data word, all zero except most significant bit
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 5)};
|
||||
SegmentArrayMessageReader messageReader(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(messageReader.isCanonical());
|
||||
auto canonicalWords = canonicalize(messageReader.getRoot<TestLists>());
|
||||
|
||||
// At one point this failed because an off-by-one bug in canonicalization
|
||||
// caused the second word of the data section to be truncated.
|
||||
ASSERT_EQ(canonicalWords.asBytes(), kj::arrayPtr(segment.bytes, 5 * 8));
|
||||
}
|
||||
|
||||
KJ_TEST("canonical non-null empty struct field") {
|
||||
AlignedData<4> nonNullEmptyStruct = {{
|
||||
// Struct pointer, body immediately follows, two pointer fields, no data.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
|
||||
// First pointer field, struct, offset of 1, data size 1, no pointers.
|
||||
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// Non-null pointer to empty struct.
|
||||
0xfc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Body of struct filled with non-zero data.
|
||||
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(nonNullEmptyStruct.words, 4)};
|
||||
SegmentArrayMessageReader messageReader(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(messageReader.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("for pointers to empty structs, preorder is not canonical") {
|
||||
AlignedData<4> nonNullEmptyStruct = {{
|
||||
// Struct pointer, body immediately follows, two pointer fields, no data.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
|
||||
// First pointer field, struct, offset of 1, data size 1, no pointers.
|
||||
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// Non-null pointer to empty struct. Offset puts it in "preorder". Would need to have
|
||||
// an offset of -1 to be canonical.
|
||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Body of struct filled with non-zero data.
|
||||
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(nonNullEmptyStruct.words, 4)};
|
||||
SegmentArrayMessageReader messageReader(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!messageReader.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical requires pointer preorder") {
|
||||
AlignedData<5> misorderedSegment = {{
|
||||
//Struct pointer, data immediately follows, two pointer fields, no data
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
//Pointer field 1, pointing to the last entry, data size 1, no pointer
|
||||
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
//Pointer field 2, pointing to the next entry, data size 2, no pointer
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
//Data for field 2
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
//Data for field 1
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(misorderedSegment.words,
|
||||
5)};
|
||||
SegmentArrayMessageReader outOfOrder(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!outOfOrder.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical requires dense packing") {
|
||||
AlignedData<3> gapSegment = {{
|
||||
//Struct pointer, data after a gap
|
||||
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
//The gap
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
//Data for field 1
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(gapSegment.words,
|
||||
3)};
|
||||
SegmentArrayMessageReader gap(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!gap.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical rejects multi-segment messages") {
|
||||
AlignedData<1> farPtr = {{
|
||||
//Far pointer to next segment
|
||||
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
|
||||
AlignedData<2> farTarget = {{
|
||||
//Struct pointer (needed to make the far pointer legal)
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
//Dummy data
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
}};
|
||||
|
||||
kj::ArrayPtr<const word> segments[2] = {
|
||||
kj::arrayPtr(farPtr.words, 1),
|
||||
kj::arrayPtr(farTarget.words, 2)
|
||||
};
|
||||
|
||||
SegmentArrayMessageReader multiSegmentMessage(kj::arrayPtr(segments, 2));
|
||||
KJ_ASSERT(!multiSegmentMessage.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical rejects zero segment messages") {
|
||||
SegmentArrayMessageReader zero(kj::arrayPtr((kj::ArrayPtr<const word>*)NULL,
|
||||
0));
|
||||
KJ_ASSERT(!zero.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical requires truncation of 0-valued struct fields") {
|
||||
AlignedData<2> nonTruncatedSegment = {{
|
||||
//Struct pointer, data immediately follows
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
//Default data value, should have been truncated
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {
|
||||
kj::arrayPtr(nonTruncatedSegment.words, 3)
|
||||
};
|
||||
SegmentArrayMessageReader nonTruncated(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!nonTruncated.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical rejects unused trailing words") {
|
||||
AlignedData<3> segment = {{
|
||||
// Struct pointer, data in next word
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// Data section of struct
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
|
||||
// Trailing zero word
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 3)};
|
||||
SegmentArrayMessageReader message(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!message.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical accepts empty inline composite list of zero-sized structs") {
|
||||
AlignedData<3> segment = {{
|
||||
// Struct pointer, pointer in next word
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
|
||||
// List pointer, inline composite, zero words long
|
||||
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||
|
||||
// Tag word
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 3)};
|
||||
SegmentArrayMessageReader message(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(message.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical rejects inline composite list with inaccurate word-length") {
|
||||
AlignedData<6> segment = {{
|
||||
// Struct pointer, no offset, pointer section has two entries
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
|
||||
// List pointer, offset of one, inline composite, two words long
|
||||
// (The list only needs to be one word long to hold its actual elements;
|
||||
// therefore this message is not canonical.)
|
||||
0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
|
||||
|
||||
// Struct pointer, offset two, data section has one word
|
||||
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// Tag word, struct, one element, one word data section
|
||||
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// Data section of struct element of list
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
|
||||
// Data section of struct field in top-level struct
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 6)};
|
||||
SegmentArrayMessageReader message(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!message.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("upgraded lists can be canonicalized") {
|
||||
AlignedData<7> upgradedList = {{
|
||||
//Struct pointer, data immediately follows, 4 pointer fields, no data
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
|
||||
//Three words of default pointers to get to the int16 list
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
//List pointer, 3 int16s.
|
||||
0x01, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
|
||||
//First two elements
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06,
|
||||
//Last element
|
||||
0x07, 0x08, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {
|
||||
kj::arrayPtr(upgradedList.words, 7)
|
||||
};
|
||||
SegmentArrayMessageReader upgraded(kj::arrayPtr(segments, 1));
|
||||
|
||||
auto root = upgraded.getRoot<TestLists>();
|
||||
canonicalize(root);
|
||||
}
|
||||
|
||||
KJ_TEST("isCanonical requires truncation of 0-valued struct fields in all list members") {
|
||||
AlignedData<6> nonTruncatedList = {{
|
||||
//List pointer, composite,
|
||||
0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
|
||||
//Struct tag word, 2 structs, 2 data words per struct
|
||||
0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
//Data word non-null
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
//Null trailing word
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
//Data word non-null
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
//Null trailing word
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {
|
||||
kj::arrayPtr(nonTruncatedList.words, 6)
|
||||
};
|
||||
SegmentArrayMessageReader nonTruncated(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!nonTruncated.isCanonical());
|
||||
}
|
||||
|
||||
KJ_TEST("primitive list with nonzero padding") {
|
||||
AlignedData<3> segment = {{
|
||||
// Struct, one pointer field.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
|
||||
// List of three byte-sized elements.
|
||||
0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
|
||||
|
||||
// Fourth byte is non-zero!
|
||||
0x01, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 3)};
|
||||
SegmentArrayMessageReader message(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!message.isCanonical());
|
||||
|
||||
auto canonicalWords = canonicalize(message.getRoot<test::TestAnyPointer>());
|
||||
|
||||
AlignedData<3> canonicalSegment = {{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
|
||||
0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
|
||||
ASSERT_EQ(canonicalWords.asBytes(), kj::arrayPtr(canonicalSegment.bytes, 3 * 8));
|
||||
}
|
||||
|
||||
KJ_TEST("bit list with nonzero padding") {
|
||||
AlignedData<3> segment = {{
|
||||
// Struct, one pointer field.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
|
||||
// List of eleven bit-sized elements.
|
||||
0x01, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00,
|
||||
|
||||
// Twelfth bit is non-zero!
|
||||
0xee, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(segment.words, 3)};
|
||||
SegmentArrayMessageReader message(kj::arrayPtr(segments, 1));
|
||||
|
||||
KJ_ASSERT(!message.isCanonical());
|
||||
|
||||
auto canonicalWords = canonicalize(message.getRoot<test::TestAnyPointer>());
|
||||
|
||||
AlignedData<3> canonicalSegment = {{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00,
|
||||
0xee, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}};
|
||||
|
||||
ASSERT_EQ(canonicalWords.asBytes(), kj::arrayPtr(canonicalSegment.bytes, 3 * 8));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,729 @@
|
|||
// 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 "capability.h"
|
||||
#include "message.h"
|
||||
#include "arena.h"
|
||||
#include <kj/refcount.h>
|
||||
#include <kj/debug.h>
|
||||
#include <kj/vector.h>
|
||||
#include <map>
|
||||
#include "generated-header-support.h"
|
||||
|
||||
namespace capnp {
|
||||
|
||||
namespace _ {
|
||||
|
||||
void setGlobalBrokenCapFactoryForLayoutCpp(BrokenCapFactory& factory);
|
||||
// Defined in layout.c++.
|
||||
|
||||
} // namespace _
|
||||
|
||||
namespace {
|
||||
|
||||
static kj::Own<ClientHook> newNullCap();
|
||||
|
||||
class BrokenCapFactoryImpl: public _::BrokenCapFactory {
|
||||
public:
|
||||
kj::Own<ClientHook> newBrokenCap(kj::StringPtr description) override {
|
||||
return capnp::newBrokenCap(description);
|
||||
}
|
||||
kj::Own<ClientHook> newNullCap() override {
|
||||
return capnp::newNullCap();
|
||||
}
|
||||
};
|
||||
|
||||
static BrokenCapFactoryImpl brokenCapFactory;
|
||||
|
||||
} // namespace
|
||||
|
||||
ClientHook::ClientHook() {
|
||||
setGlobalBrokenCapFactoryForLayoutCpp(brokenCapFactory);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
Capability::Client::Client(decltype(nullptr))
|
||||
: hook(newNullCap()) {}
|
||||
|
||||
Capability::Client::Client(kj::Exception&& exception)
|
||||
: hook(newBrokenCap(kj::mv(exception))) {}
|
||||
|
||||
kj::Promise<void> Capability::Server::internalUnimplemented(
|
||||
const char* actualInterfaceName, uint64_t requestedTypeId) {
|
||||
return KJ_EXCEPTION(UNIMPLEMENTED, "Requested interface not implemented.",
|
||||
actualInterfaceName, requestedTypeId);
|
||||
}
|
||||
|
||||
kj::Promise<void> Capability::Server::internalUnimplemented(
|
||||
const char* interfaceName, uint64_t typeId, uint16_t methodId) {
|
||||
return KJ_EXCEPTION(UNIMPLEMENTED, "Method not implemented.", interfaceName, typeId, methodId);
|
||||
}
|
||||
|
||||
kj::Promise<void> Capability::Server::internalUnimplemented(
|
||||
const char* interfaceName, const char* methodName, uint64_t typeId, uint16_t methodId) {
|
||||
return KJ_EXCEPTION(UNIMPLEMENTED, "Method not implemented.", interfaceName,
|
||||
typeId, methodName, methodId);
|
||||
}
|
||||
|
||||
ResponseHook::~ResponseHook() noexcept(false) {}
|
||||
|
||||
kj::Promise<void> ClientHook::whenResolved() {
|
||||
KJ_IF_MAYBE(promise, whenMoreResolved()) {
|
||||
return promise->then([](kj::Own<ClientHook>&& resolution) {
|
||||
return resolution->whenResolved();
|
||||
});
|
||||
} else {
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
static inline uint firstSegmentSize(kj::Maybe<MessageSize> sizeHint) {
|
||||
KJ_IF_MAYBE(s, sizeHint) {
|
||||
return s->wordCount;
|
||||
} else {
|
||||
return SUGGESTED_FIRST_SEGMENT_WORDS;
|
||||
}
|
||||
}
|
||||
|
||||
class LocalResponse final: public ResponseHook, public kj::Refcounted {
|
||||
public:
|
||||
LocalResponse(kj::Maybe<MessageSize> sizeHint)
|
||||
: message(firstSegmentSize(sizeHint)) {}
|
||||
|
||||
MallocMessageBuilder message;
|
||||
};
|
||||
|
||||
class LocalCallContext final: public CallContextHook, public kj::Refcounted {
|
||||
public:
|
||||
LocalCallContext(kj::Own<MallocMessageBuilder>&& request, kj::Own<ClientHook> clientRef,
|
||||
kj::Own<kj::PromiseFulfiller<void>> cancelAllowedFulfiller)
|
||||
: request(kj::mv(request)), clientRef(kj::mv(clientRef)),
|
||||
cancelAllowedFulfiller(kj::mv(cancelAllowedFulfiller)) {}
|
||||
|
||||
AnyPointer::Reader getParams() override {
|
||||
KJ_IF_MAYBE(r, request) {
|
||||
return r->get()->getRoot<AnyPointer>();
|
||||
} else {
|
||||
KJ_FAIL_REQUIRE("Can't call getParams() after releaseParams().");
|
||||
}
|
||||
}
|
||||
void releaseParams() override {
|
||||
request = nullptr;
|
||||
}
|
||||
AnyPointer::Builder getResults(kj::Maybe<MessageSize> sizeHint) override {
|
||||
if (response == nullptr) {
|
||||
auto localResponse = kj::refcounted<LocalResponse>(sizeHint);
|
||||
responseBuilder = localResponse->message.getRoot<AnyPointer>();
|
||||
response = Response<AnyPointer>(responseBuilder.asReader(), kj::mv(localResponse));
|
||||
}
|
||||
return responseBuilder;
|
||||
}
|
||||
kj::Promise<void> tailCall(kj::Own<RequestHook>&& request) override {
|
||||
auto result = directTailCall(kj::mv(request));
|
||||
KJ_IF_MAYBE(f, tailCallPipelineFulfiller) {
|
||||
f->get()->fulfill(AnyPointer::Pipeline(kj::mv(result.pipeline)));
|
||||
}
|
||||
return kj::mv(result.promise);
|
||||
}
|
||||
ClientHook::VoidPromiseAndPipeline directTailCall(kj::Own<RequestHook>&& request) override {
|
||||
KJ_REQUIRE(response == nullptr, "Can't call tailCall() after initializing the results struct.");
|
||||
|
||||
auto promise = request->send();
|
||||
|
||||
auto voidPromise = promise.then([this](Response<AnyPointer>&& tailResponse) {
|
||||
response = kj::mv(tailResponse);
|
||||
});
|
||||
|
||||
return { kj::mv(voidPromise), PipelineHook::from(kj::mv(promise)) };
|
||||
}
|
||||
kj::Promise<AnyPointer::Pipeline> onTailCall() override {
|
||||
auto paf = kj::newPromiseAndFulfiller<AnyPointer::Pipeline>();
|
||||
tailCallPipelineFulfiller = kj::mv(paf.fulfiller);
|
||||
return kj::mv(paf.promise);
|
||||
}
|
||||
void allowCancellation() override {
|
||||
cancelAllowedFulfiller->fulfill();
|
||||
}
|
||||
kj::Own<CallContextHook> addRef() override {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Own<MallocMessageBuilder>> request;
|
||||
kj::Maybe<Response<AnyPointer>> response;
|
||||
AnyPointer::Builder responseBuilder = nullptr; // only valid if `response` is non-null
|
||||
kj::Own<ClientHook> clientRef;
|
||||
kj::Maybe<kj::Own<kj::PromiseFulfiller<AnyPointer::Pipeline>>> tailCallPipelineFulfiller;
|
||||
kj::Own<kj::PromiseFulfiller<void>> cancelAllowedFulfiller;
|
||||
};
|
||||
|
||||
class LocalRequest final: public RequestHook {
|
||||
public:
|
||||
inline LocalRequest(uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Maybe<MessageSize> sizeHint, kj::Own<ClientHook> client)
|
||||
: message(kj::heap<MallocMessageBuilder>(firstSegmentSize(sizeHint))),
|
||||
interfaceId(interfaceId), methodId(methodId), client(kj::mv(client)) {}
|
||||
|
||||
RemotePromise<AnyPointer> send() override {
|
||||
KJ_REQUIRE(message.get() != nullptr, "Already called send() on this request.");
|
||||
|
||||
// For the lambda capture.
|
||||
uint64_t interfaceId = this->interfaceId;
|
||||
uint16_t methodId = this->methodId;
|
||||
|
||||
auto cancelPaf = kj::newPromiseAndFulfiller<void>();
|
||||
|
||||
auto context = kj::refcounted<LocalCallContext>(
|
||||
kj::mv(message), client->addRef(), kj::mv(cancelPaf.fulfiller));
|
||||
auto promiseAndPipeline = client->call(interfaceId, methodId, kj::addRef(*context));
|
||||
|
||||
// We have to make sure the call is not canceled unless permitted. We need to fork the promise
|
||||
// so that if the client drops their copy, the promise isn't necessarily canceled.
|
||||
auto forked = promiseAndPipeline.promise.fork();
|
||||
|
||||
// We daemonize one branch, but only after joining it with the promise that fires if
|
||||
// cancellation is allowed.
|
||||
forked.addBranch()
|
||||
.attach(kj::addRef(*context))
|
||||
.exclusiveJoin(kj::mv(cancelPaf.promise))
|
||||
.detach([](kj::Exception&&) {}); // ignore exceptions
|
||||
|
||||
// Now the other branch returns the response from the context.
|
||||
auto promise = forked.addBranch().then(kj::mvCapture(context,
|
||||
[](kj::Own<LocalCallContext>&& context) {
|
||||
context->getResults(MessageSize { 0, 0 }); // force response allocation
|
||||
return kj::mv(KJ_ASSERT_NONNULL(context->response));
|
||||
}));
|
||||
|
||||
// We return the other branch.
|
||||
return RemotePromise<AnyPointer>(
|
||||
kj::mv(promise), AnyPointer::Pipeline(kj::mv(promiseAndPipeline.pipeline)));
|
||||
}
|
||||
|
||||
const void* getBrand() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
kj::Own<MallocMessageBuilder> message;
|
||||
|
||||
private:
|
||||
uint64_t interfaceId;
|
||||
uint16_t methodId;
|
||||
kj::Own<ClientHook> client;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Call queues
|
||||
//
|
||||
// These classes handle pipelining in the case where calls need to be queued in-memory until some
|
||||
// local operation completes.
|
||||
|
||||
class QueuedPipeline final: public PipelineHook, public kj::Refcounted {
|
||||
// A PipelineHook which simply queues calls while waiting for a PipelineHook to which to forward
|
||||
// them.
|
||||
|
||||
public:
|
||||
QueuedPipeline(kj::Promise<kj::Own<PipelineHook>>&& promiseParam)
|
||||
: promise(promiseParam.fork()),
|
||||
selfResolutionOp(promise.addBranch().then([this](kj::Own<PipelineHook>&& inner) {
|
||||
redirect = kj::mv(inner);
|
||||
}, [this](kj::Exception&& exception) {
|
||||
redirect = newBrokenPipeline(kj::mv(exception));
|
||||
}).eagerlyEvaluate(nullptr)) {}
|
||||
|
||||
kj::Own<PipelineHook> addRef() override {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) override {
|
||||
auto copy = kj::heapArrayBuilder<PipelineOp>(ops.size());
|
||||
for (auto& op: ops) {
|
||||
copy.add(op);
|
||||
}
|
||||
return getPipelinedCap(copy.finish());
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> getPipelinedCap(kj::Array<PipelineOp>&& ops) override;
|
||||
|
||||
private:
|
||||
kj::ForkedPromise<kj::Own<PipelineHook>> promise;
|
||||
|
||||
kj::Maybe<kj::Own<PipelineHook>> redirect;
|
||||
// Once the promise resolves, this will become non-null and point to the underlying object.
|
||||
|
||||
kj::Promise<void> selfResolutionOp;
|
||||
// Represents the operation which will set `redirect` when possible.
|
||||
};
|
||||
|
||||
class QueuedClient final: public ClientHook, public kj::Refcounted {
|
||||
// A ClientHook which simply queues calls while waiting for a ClientHook to which to forward
|
||||
// them.
|
||||
|
||||
public:
|
||||
QueuedClient(kj::Promise<kj::Own<ClientHook>>&& promiseParam)
|
||||
: promise(promiseParam.fork()),
|
||||
selfResolutionOp(promise.addBranch().then([this](kj::Own<ClientHook>&& inner) {
|
||||
redirect = kj::mv(inner);
|
||||
}, [this](kj::Exception&& exception) {
|
||||
redirect = newBrokenCap(kj::mv(exception));
|
||||
}).eagerlyEvaluate(nullptr)),
|
||||
promiseForCallForwarding(promise.addBranch().fork()),
|
||||
promiseForClientResolution(promise.addBranch().fork()) {}
|
||||
|
||||
Request<AnyPointer, AnyPointer> newCall(
|
||||
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) override {
|
||||
auto hook = kj::heap<LocalRequest>(
|
||||
interfaceId, methodId, sizeHint, kj::addRef(*this));
|
||||
auto root = hook->message->getRoot<AnyPointer>();
|
||||
return Request<AnyPointer, AnyPointer>(root, kj::mv(hook));
|
||||
}
|
||||
|
||||
VoidPromiseAndPipeline call(uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Own<CallContextHook>&& context) override {
|
||||
// This is a bit complicated. We need to initiate this call later on. When we initiate the
|
||||
// call, we'll get a void promise for its completion and a pipeline object. Right now, we have
|
||||
// to produce a similar void promise and pipeline that will eventually be chained to those.
|
||||
// The problem is, these are two independent objects, but they both depend on the result of
|
||||
// one future call.
|
||||
//
|
||||
// So, we need to set up a continuation that will initiate the call later, then we need to
|
||||
// fork the promise for that continuation in order to send the completion promise and the
|
||||
// pipeline to their respective places.
|
||||
//
|
||||
// TODO(perf): Too much reference counting? Can we do better? Maybe a way to fork
|
||||
// Promise<Tuple<T, U>> into Tuple<Promise<T>, Promise<U>>?
|
||||
|
||||
struct CallResultHolder: public kj::Refcounted {
|
||||
// Essentially acts as a refcounted \VoidPromiseAndPipeline, so that we can create a promise
|
||||
// for it and fork that promise.
|
||||
|
||||
VoidPromiseAndPipeline content;
|
||||
// One branch of the fork will use content.promise, the other branch will use
|
||||
// content.pipeline. Neither branch will touch the other's piece.
|
||||
|
||||
inline CallResultHolder(VoidPromiseAndPipeline&& content): content(kj::mv(content)) {}
|
||||
|
||||
kj::Own<CallResultHolder> addRef() { return kj::addRef(*this); }
|
||||
};
|
||||
|
||||
// Create a promise for the call initiation.
|
||||
kj::ForkedPromise<kj::Own<CallResultHolder>> callResultPromise =
|
||||
promiseForCallForwarding.addBranch().then(kj::mvCapture(context,
|
||||
[=](kj::Own<CallContextHook>&& context, kj::Own<ClientHook>&& client){
|
||||
return kj::refcounted<CallResultHolder>(
|
||||
client->call(interfaceId, methodId, kj::mv(context)));
|
||||
})).fork();
|
||||
|
||||
// Create a promise that extracts the pipeline from the call initiation, and construct our
|
||||
// QueuedPipeline to chain to it.
|
||||
auto pipelinePromise = callResultPromise.addBranch().then(
|
||||
[](kj::Own<CallResultHolder>&& callResult){
|
||||
return kj::mv(callResult->content.pipeline);
|
||||
});
|
||||
auto pipeline = kj::refcounted<QueuedPipeline>(kj::mv(pipelinePromise));
|
||||
|
||||
// Create a promise that simply chains to the void promise produced by the call initiation.
|
||||
auto completionPromise = callResultPromise.addBranch().then(
|
||||
[](kj::Own<CallResultHolder>&& callResult){
|
||||
return kj::mv(callResult->content.promise);
|
||||
});
|
||||
|
||||
// OK, now we can actually return our thing.
|
||||
return VoidPromiseAndPipeline { kj::mv(completionPromise), kj::mv(pipeline) };
|
||||
}
|
||||
|
||||
kj::Maybe<ClientHook&> getResolved() override {
|
||||
KJ_IF_MAYBE(inner, redirect) {
|
||||
return **inner;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Promise<kj::Own<ClientHook>>> whenMoreResolved() override {
|
||||
return promiseForClientResolution.addBranch();
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> addRef() override {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
const void* getBrand() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef kj::ForkedPromise<kj::Own<ClientHook>> ClientHookPromiseFork;
|
||||
|
||||
kj::Maybe<kj::Own<ClientHook>> redirect;
|
||||
// Once the promise resolves, this will become non-null and point to the underlying object.
|
||||
|
||||
ClientHookPromiseFork promise;
|
||||
// Promise that resolves when we have a new ClientHook to forward to.
|
||||
//
|
||||
// This fork shall only have three branches: `selfResolutionOp`, `promiseForCallForwarding`, and
|
||||
// `promiseForClientResolution`, in that order.
|
||||
|
||||
kj::Promise<void> selfResolutionOp;
|
||||
// Represents the operation which will set `redirect` when possible.
|
||||
|
||||
ClientHookPromiseFork promiseForCallForwarding;
|
||||
// When this promise resolves, each queued call will be forwarded to the real client. This needs
|
||||
// to occur *before* any 'whenMoreResolved()' promises resolve, because we want to make sure
|
||||
// previously-queued calls are delivered before any new calls made in response to the resolution.
|
||||
|
||||
ClientHookPromiseFork promiseForClientResolution;
|
||||
// whenMoreResolved() returns forks of this promise. These must resolve *after* queued calls
|
||||
// have been initiated (so that any calls made in the whenMoreResolved() handler are correctly
|
||||
// delivered after calls made earlier), but *before* any queued calls return (because it might
|
||||
// confuse the application if a queued call returns before the capability on which it was made
|
||||
// resolves). Luckily, we know that queued calls will involve, at the very least, an
|
||||
// eventLoop.evalLater.
|
||||
};
|
||||
|
||||
kj::Own<ClientHook> QueuedPipeline::getPipelinedCap(kj::Array<PipelineOp>&& ops) {
|
||||
KJ_IF_MAYBE(r, redirect) {
|
||||
return r->get()->getPipelinedCap(kj::mv(ops));
|
||||
} else {
|
||||
auto clientPromise = promise.addBranch().then(kj::mvCapture(ops,
|
||||
[](kj::Array<PipelineOp>&& ops, kj::Own<PipelineHook> pipeline) {
|
||||
return pipeline->getPipelinedCap(kj::mv(ops));
|
||||
}));
|
||||
|
||||
return kj::refcounted<QueuedClient>(kj::mv(clientPromise));
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
class LocalPipeline final: public PipelineHook, public kj::Refcounted {
|
||||
public:
|
||||
inline LocalPipeline(kj::Own<CallContextHook>&& contextParam)
|
||||
: context(kj::mv(contextParam)),
|
||||
results(context->getResults(MessageSize { 0, 0 })) {}
|
||||
|
||||
kj::Own<PipelineHook> addRef() {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) {
|
||||
return results.getPipelinedCap(ops);
|
||||
}
|
||||
|
||||
private:
|
||||
kj::Own<CallContextHook> context;
|
||||
AnyPointer::Reader results;
|
||||
};
|
||||
|
||||
class LocalClient final: public ClientHook, public kj::Refcounted {
|
||||
public:
|
||||
LocalClient(kj::Own<Capability::Server>&& serverParam)
|
||||
: server(kj::mv(serverParam)) {
|
||||
server->thisHook = this;
|
||||
}
|
||||
LocalClient(kj::Own<Capability::Server>&& serverParam,
|
||||
_::CapabilityServerSetBase& capServerSet, void* ptr)
|
||||
: server(kj::mv(serverParam)), capServerSet(&capServerSet), ptr(ptr) {
|
||||
server->thisHook = this;
|
||||
}
|
||||
|
||||
~LocalClient() noexcept(false) {
|
||||
server->thisHook = nullptr;
|
||||
}
|
||||
|
||||
Request<AnyPointer, AnyPointer> newCall(
|
||||
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) override {
|
||||
auto hook = kj::heap<LocalRequest>(
|
||||
interfaceId, methodId, sizeHint, kj::addRef(*this));
|
||||
auto root = hook->message->getRoot<AnyPointer>();
|
||||
return Request<AnyPointer, AnyPointer>(root, kj::mv(hook));
|
||||
}
|
||||
|
||||
VoidPromiseAndPipeline call(uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Own<CallContextHook>&& context) override {
|
||||
auto contextPtr = context.get();
|
||||
|
||||
// We don't want to actually dispatch the call synchronously, because we don't want the callee
|
||||
// to have any side effects before the promise is returned to the caller. This helps avoid
|
||||
// race conditions.
|
||||
//
|
||||
// So, we do an evalLater() here.
|
||||
//
|
||||
// Note also that QueuedClient depends on this evalLater() to ensure that pipelined calls don't
|
||||
// complete before 'whenMoreResolved()' promises resolve.
|
||||
auto promise = kj::evalLater([this,interfaceId,methodId,contextPtr]() {
|
||||
return server->dispatchCall(interfaceId, methodId,
|
||||
CallContext<AnyPointer, AnyPointer>(*contextPtr));
|
||||
}).attach(kj::addRef(*this));
|
||||
|
||||
// We have to fork this promise for the pipeline to receive a copy of the answer.
|
||||
auto forked = promise.fork();
|
||||
|
||||
auto pipelinePromise = forked.addBranch().then(kj::mvCapture(context->addRef(),
|
||||
[=](kj::Own<CallContextHook>&& context) -> kj::Own<PipelineHook> {
|
||||
context->releaseParams();
|
||||
return kj::refcounted<LocalPipeline>(kj::mv(context));
|
||||
}));
|
||||
|
||||
auto tailPipelinePromise = context->onTailCall().then([](AnyPointer::Pipeline&& pipeline) {
|
||||
return kj::mv(pipeline.hook);
|
||||
});
|
||||
|
||||
pipelinePromise = pipelinePromise.exclusiveJoin(kj::mv(tailPipelinePromise));
|
||||
|
||||
auto completionPromise = forked.addBranch().attach(kj::mv(context));
|
||||
|
||||
return VoidPromiseAndPipeline { kj::mv(completionPromise),
|
||||
kj::refcounted<QueuedPipeline>(kj::mv(pipelinePromise)) };
|
||||
}
|
||||
|
||||
kj::Maybe<ClientHook&> getResolved() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Promise<kj::Own<ClientHook>>> whenMoreResolved() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> addRef() override {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
const void* getBrand() override {
|
||||
// We have no need to detect local objects.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* getLocalServer(_::CapabilityServerSetBase& capServerSet) override {
|
||||
if (this->capServerSet == &capServerSet) {
|
||||
return ptr;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
kj::Own<Capability::Server> server;
|
||||
_::CapabilityServerSetBase* capServerSet = nullptr;
|
||||
void* ptr = nullptr;
|
||||
};
|
||||
|
||||
kj::Own<ClientHook> Capability::Client::makeLocalClient(kj::Own<Capability::Server>&& server) {
|
||||
return kj::refcounted<LocalClient>(kj::mv(server));
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> newLocalPromiseClient(kj::Promise<kj::Own<ClientHook>>&& promise) {
|
||||
return kj::refcounted<QueuedClient>(kj::mv(promise));
|
||||
}
|
||||
|
||||
kj::Own<PipelineHook> newLocalPromisePipeline(kj::Promise<kj::Own<PipelineHook>>&& promise) {
|
||||
return kj::refcounted<QueuedPipeline>(kj::mv(promise));
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
class BrokenPipeline final: public PipelineHook, public kj::Refcounted {
|
||||
public:
|
||||
BrokenPipeline(const kj::Exception& exception): exception(exception) {}
|
||||
|
||||
kj::Own<PipelineHook> addRef() override {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) override;
|
||||
|
||||
private:
|
||||
kj::Exception exception;
|
||||
};
|
||||
|
||||
class BrokenRequest final: public RequestHook {
|
||||
public:
|
||||
BrokenRequest(const kj::Exception& exception, kj::Maybe<MessageSize> sizeHint)
|
||||
: exception(exception), message(firstSegmentSize(sizeHint)) {}
|
||||
|
||||
RemotePromise<AnyPointer> send() override {
|
||||
return RemotePromise<AnyPointer>(kj::cp(exception),
|
||||
AnyPointer::Pipeline(kj::refcounted<BrokenPipeline>(exception)));
|
||||
}
|
||||
|
||||
const void* getBrand() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
kj::Exception exception;
|
||||
MallocMessageBuilder message;
|
||||
};
|
||||
|
||||
class BrokenClient final: public ClientHook, public kj::Refcounted {
|
||||
public:
|
||||
BrokenClient(const kj::Exception& exception, bool resolved, const void* brand = nullptr)
|
||||
: exception(exception), resolved(resolved), brand(brand) {}
|
||||
BrokenClient(const kj::StringPtr description, bool resolved, const void* brand = nullptr)
|
||||
: exception(kj::Exception::Type::FAILED, "", 0, kj::str(description)),
|
||||
resolved(resolved), brand(brand) {}
|
||||
|
||||
Request<AnyPointer, AnyPointer> newCall(
|
||||
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) override {
|
||||
return newBrokenRequest(kj::cp(exception), sizeHint);
|
||||
}
|
||||
|
||||
VoidPromiseAndPipeline call(uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Own<CallContextHook>&& context) override {
|
||||
return VoidPromiseAndPipeline { kj::cp(exception), kj::refcounted<BrokenPipeline>(exception) };
|
||||
}
|
||||
|
||||
kj::Maybe<ClientHook&> getResolved() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Promise<kj::Own<ClientHook>>> whenMoreResolved() override {
|
||||
if (resolved) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return kj::Promise<kj::Own<ClientHook>>(kj::cp(exception));
|
||||
}
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> addRef() override {
|
||||
return kj::addRef(*this);
|
||||
}
|
||||
|
||||
const void* getBrand() override {
|
||||
return brand;
|
||||
}
|
||||
|
||||
private:
|
||||
kj::Exception exception;
|
||||
bool resolved;
|
||||
const void* brand;
|
||||
};
|
||||
|
||||
kj::Own<ClientHook> BrokenPipeline::getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) {
|
||||
return kj::refcounted<BrokenClient>(exception, false);
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> newNullCap() {
|
||||
// A null capability, unlike other broken capabilities, is considered resolved.
|
||||
return kj::refcounted<BrokenClient>("Called null capability.", true,
|
||||
&ClientHook::NULL_CAPABILITY_BRAND);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
kj::Own<ClientHook> newBrokenCap(kj::StringPtr reason) {
|
||||
return kj::refcounted<BrokenClient>(reason, false);
|
||||
}
|
||||
|
||||
kj::Own<ClientHook> newBrokenCap(kj::Exception&& reason) {
|
||||
return kj::refcounted<BrokenClient>(kj::mv(reason), false);
|
||||
}
|
||||
|
||||
kj::Own<PipelineHook> newBrokenPipeline(kj::Exception&& reason) {
|
||||
return kj::refcounted<BrokenPipeline>(kj::mv(reason));
|
||||
}
|
||||
|
||||
Request<AnyPointer, AnyPointer> newBrokenRequest(
|
||||
kj::Exception&& reason, kj::Maybe<MessageSize> sizeHint) {
|
||||
auto hook = kj::heap<BrokenRequest>(kj::mv(reason), sizeHint);
|
||||
auto root = hook->message.getRoot<AnyPointer>();
|
||||
return Request<AnyPointer, AnyPointer>(root, kj::mv(hook));
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
ReaderCapabilityTable::ReaderCapabilityTable(
|
||||
kj::Array<kj::Maybe<kj::Own<ClientHook>>> table)
|
||||
: table(kj::mv(table)) {
|
||||
setGlobalBrokenCapFactoryForLayoutCpp(brokenCapFactory);
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Own<ClientHook>> ReaderCapabilityTable::extractCap(uint index) {
|
||||
if (index < table.size()) {
|
||||
return table[index].map([](kj::Own<ClientHook>& cap) { return cap->addRef(); });
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BuilderCapabilityTable::BuilderCapabilityTable() {
|
||||
setGlobalBrokenCapFactoryForLayoutCpp(brokenCapFactory);
|
||||
}
|
||||
|
||||
kj::Maybe<kj::Own<ClientHook>> BuilderCapabilityTable::extractCap(uint index) {
|
||||
if (index < table.size()) {
|
||||
return table[index].map([](kj::Own<ClientHook>& cap) { return cap->addRef(); });
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint BuilderCapabilityTable::injectCap(kj::Own<ClientHook>&& cap) {
|
||||
uint result = table.size();
|
||||
table.add(kj::mv(cap));
|
||||
return result;
|
||||
}
|
||||
|
||||
void BuilderCapabilityTable::dropCap(uint index) {
|
||||
KJ_ASSERT(index < table.size(), "Invalid capability descriptor in message.") {
|
||||
return;
|
||||
}
|
||||
table[index] = nullptr;
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// CapabilityServerSet
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
Capability::Client CapabilityServerSetBase::addInternal(
|
||||
kj::Own<Capability::Server>&& server, void* ptr) {
|
||||
return Capability::Client(kj::refcounted<LocalClient>(kj::mv(server), *this, ptr));
|
||||
}
|
||||
|
||||
kj::Promise<void*> CapabilityServerSetBase::getLocalServerInternal(Capability::Client& client) {
|
||||
ClientHook* hook = client.hook.get();
|
||||
|
||||
// Get the most-resolved-so-far version of the hook.
|
||||
KJ_IF_MAYBE(h, hook->getResolved()) {
|
||||
hook = h;
|
||||
};
|
||||
|
||||
KJ_IF_MAYBE(p, hook->whenMoreResolved()) {
|
||||
// This hook is an unresolved promise. We need to wait for it.
|
||||
return p->attach(hook->addRef())
|
||||
.then([this](kj::Own<ClientHook>&& resolved) {
|
||||
Capability::Client client(kj::mv(resolved));
|
||||
return getLocalServerInternal(client);
|
||||
});
|
||||
} else {
|
||||
return hook->getLocalServer(*this);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
} // namespace capnp
|
|
@ -0,0 +1,907 @@
|
|||
// 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
|
||||
|
||||
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#if CAPNP_LITE
|
||||
#error "RPC APIs, including this header, are not available in lite mode."
|
||||
#endif
|
||||
|
||||
#include <kj/async.h>
|
||||
#include <kj/vector.h>
|
||||
#include "raw-schema.h"
|
||||
#include "any.h"
|
||||
#include "pointer-helpers.h"
|
||||
|
||||
namespace capnp {
|
||||
|
||||
template <typename Results>
|
||||
class Response;
|
||||
|
||||
template <typename T>
|
||||
class RemotePromise: public kj::Promise<Response<T>>, public T::Pipeline {
|
||||
// A Promise which supports pipelined calls. T is typically a struct type. T must declare
|
||||
// an inner "mix-in" type "Pipeline" which implements pipelining; RemotePromise simply
|
||||
// multiply-inherits that type along with Promise<Response<T>>. T::Pipeline must be movable,
|
||||
// but does not need to be copyable (i.e. just like Promise<T>).
|
||||
//
|
||||
// The promise is for an owned pointer so that the RPC system can allocate the MessageReader
|
||||
// itself.
|
||||
|
||||
public:
|
||||
inline RemotePromise(kj::Promise<Response<T>>&& promise, typename T::Pipeline&& pipeline)
|
||||
: kj::Promise<Response<T>>(kj::mv(promise)),
|
||||
T::Pipeline(kj::mv(pipeline)) {}
|
||||
inline RemotePromise(decltype(nullptr))
|
||||
: kj::Promise<Response<T>>(nullptr),
|
||||
T::Pipeline(nullptr) {}
|
||||
KJ_DISALLOW_COPY(RemotePromise);
|
||||
RemotePromise(RemotePromise&& other) = default;
|
||||
RemotePromise& operator=(RemotePromise&& other) = default;
|
||||
|
||||
static RemotePromise<T> reducePromise(kj::Promise<RemotePromise>&& promise);
|
||||
// Hook for KJ so that Promise<RemotePromise<T>> automatically reduces to RemotePromise<T>.
|
||||
};
|
||||
|
||||
class LocalClient;
|
||||
namespace _ { // private
|
||||
extern const RawSchema NULL_INTERFACE_SCHEMA; // defined in schema.c++
|
||||
class CapabilityServerSetBase;
|
||||
} // namespace _ (private)
|
||||
|
||||
struct Capability {
|
||||
// A capability without type-safe methods. Typed capability clients wrap `Client` and typed
|
||||
// capability servers subclass `Server` to dispatch to the regular, typed methods.
|
||||
|
||||
class Client;
|
||||
class Server;
|
||||
|
||||
struct _capnpPrivate {
|
||||
struct IsInterface;
|
||||
static constexpr uint64_t typeId = 0x3;
|
||||
static constexpr Kind kind = Kind::INTERFACE;
|
||||
static constexpr _::RawSchema const* schema = &_::NULL_INTERFACE_SCHEMA;
|
||||
|
||||
static const _::RawBrandedSchema* brand() {
|
||||
return &_::NULL_INTERFACE_SCHEMA.defaultBrand;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Capability clients
|
||||
|
||||
class RequestHook;
|
||||
class ResponseHook;
|
||||
class PipelineHook;
|
||||
class ClientHook;
|
||||
|
||||
template <typename Params, typename Results>
|
||||
class Request: public Params::Builder {
|
||||
// A call that hasn't been sent yet. This class extends a Builder for the call's "Params"
|
||||
// structure with a method send() that actually sends it.
|
||||
//
|
||||
// Given a Cap'n Proto method `foo(a :A, b :B): C`, the generated client interface will have
|
||||
// a method `Request<FooParams, C> fooRequest()` (as well as a convenience method
|
||||
// `RemotePromise<C> foo(A::Reader a, B::Reader b)`).
|
||||
|
||||
public:
|
||||
inline Request(typename Params::Builder builder, kj::Own<RequestHook>&& hook)
|
||||
: Params::Builder(builder), hook(kj::mv(hook)) {}
|
||||
inline Request(decltype(nullptr)): Params::Builder(nullptr) {}
|
||||
|
||||
RemotePromise<Results> send() KJ_WARN_UNUSED_RESULT;
|
||||
// Send the call and return a promise for the results.
|
||||
|
||||
private:
|
||||
kj::Own<RequestHook> hook;
|
||||
|
||||
friend class Capability::Client;
|
||||
friend struct DynamicCapability;
|
||||
template <typename, typename>
|
||||
friend class CallContext;
|
||||
friend class RequestHook;
|
||||
};
|
||||
|
||||
template <typename Results>
|
||||
class Response: public Results::Reader {
|
||||
// A completed call. This class extends a Reader for the call's answer structure. The Response
|
||||
// is move-only -- once it goes out-of-scope, the underlying message will be freed.
|
||||
|
||||
public:
|
||||
inline Response(typename Results::Reader reader, kj::Own<ResponseHook>&& hook)
|
||||
: Results::Reader(reader), hook(kj::mv(hook)) {}
|
||||
|
||||
private:
|
||||
kj::Own<ResponseHook> hook;
|
||||
|
||||
template <typename, typename>
|
||||
friend class Request;
|
||||
friend class ResponseHook;
|
||||
};
|
||||
|
||||
class Capability::Client {
|
||||
// Base type for capability clients.
|
||||
|
||||
public:
|
||||
typedef Capability Reads;
|
||||
typedef Capability Calls;
|
||||
|
||||
Client(decltype(nullptr));
|
||||
// If you need to declare a Client before you have anything to assign to it (perhaps because
|
||||
// the assignment is going to occur in an if/else scope), you can start by initializing it to
|
||||
// `nullptr`. The resulting client is not meant to be called and throws exceptions from all
|
||||
// methods.
|
||||
|
||||
template <typename T, typename = kj::EnableIf<kj::canConvert<T*, Capability::Server*>()>>
|
||||
Client(kj::Own<T>&& server);
|
||||
// Make a client capability that wraps the given server capability. The server's methods will
|
||||
// only be executed in the given EventLoop, regardless of what thread calls the client's methods.
|
||||
|
||||
template <typename T, typename = kj::EnableIf<kj::canConvert<T*, Client*>()>>
|
||||
Client(kj::Promise<T>&& promise);
|
||||
// Make a client from a promise for a future client. The resulting client queues calls until the
|
||||
// promise resolves.
|
||||
|
||||
Client(kj::Exception&& exception);
|
||||
// Make a broken client that throws the given exception from all calls.
|
||||
|
||||
Client(Client& other);
|
||||
Client& operator=(Client& other);
|
||||
// Copies by reference counting. Warning: This refcounting is not thread-safe. All copies of
|
||||
// the client must remain in one thread.
|
||||
|
||||
Client(Client&&) = default;
|
||||
Client& operator=(Client&&) = default;
|
||||
// Move constructor avoids reference counting.
|
||||
|
||||
explicit Client(kj::Own<ClientHook>&& hook);
|
||||
// For use by the RPC implementation: Wrap a ClientHook.
|
||||
|
||||
template <typename T>
|
||||
typename T::Client castAs();
|
||||
// Reinterpret the capability as implementing the given interface. Note that no error will occur
|
||||
// here if the capability does not actually implement this interface, but later method calls will
|
||||
// fail. It's up to the application to decide how indicate that additional interfaces are
|
||||
// supported.
|
||||
//
|
||||
// TODO(perf): GCC 4.8 / Clang 3.3: rvalue-qualified version for better performance.
|
||||
|
||||
template <typename T>
|
||||
typename T::Client castAs(InterfaceSchema schema);
|
||||
// Dynamic version. `T` must be `DynamicCapability`, and you must `#include <capnp/dynamic.h>`.
|
||||
|
||||
kj::Promise<void> whenResolved();
|
||||
// If the capability is actually only a promise, the returned promise resolves once the
|
||||
// capability itself has resolved to its final destination (or propagates the exception if
|
||||
// the capability promise is rejected). This is mainly useful for error-checking in the case
|
||||
// where no calls are being made. There is no reason to wait for this before making calls; if
|
||||
// the capability does not resolve, the call results will propagate the error.
|
||||
|
||||
Request<AnyPointer, AnyPointer> typelessRequest(
|
||||
uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Maybe<MessageSize> sizeHint);
|
||||
// Make a request without knowing the types of the params or results. You specify the type ID
|
||||
// and method number manually.
|
||||
|
||||
// TODO(someday): method(s) for Join
|
||||
|
||||
protected:
|
||||
Client() = default;
|
||||
|
||||
template <typename Params, typename Results>
|
||||
Request<Params, Results> newCall(uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Maybe<MessageSize> sizeHint);
|
||||
|
||||
private:
|
||||
kj::Own<ClientHook> hook;
|
||||
|
||||
static kj::Own<ClientHook> makeLocalClient(kj::Own<Capability::Server>&& server);
|
||||
|
||||
template <typename, Kind>
|
||||
friend struct _::PointerHelpers;
|
||||
friend struct DynamicCapability;
|
||||
friend class Orphanage;
|
||||
friend struct DynamicStruct;
|
||||
friend struct DynamicList;
|
||||
template <typename, Kind>
|
||||
friend struct List;
|
||||
friend class _::CapabilityServerSetBase;
|
||||
friend class ClientHook;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Capability servers
|
||||
|
||||
class CallContextHook;
|
||||
|
||||
template <typename Params, typename Results>
|
||||
class CallContext: public kj::DisallowConstCopy {
|
||||
// Wrapper around CallContextHook with a specific return type.
|
||||
//
|
||||
// Methods of this class may only be called from within the server's event loop, not from other
|
||||
// threads.
|
||||
//
|
||||
// The CallContext becomes invalid as soon as the call reports completion.
|
||||
|
||||
public:
|
||||
explicit CallContext(CallContextHook& hook);
|
||||
|
||||
typename Params::Reader getParams();
|
||||
// Get the params payload.
|
||||
|
||||
void releaseParams();
|
||||
// Release the params payload. getParams() will throw an exception after this is called.
|
||||
// Releasing the params may allow the RPC system to free up buffer space to handle other
|
||||
// requests. Long-running asynchronous methods should try to call this as early as is
|
||||
// convenient.
|
||||
|
||||
typename Results::Builder getResults(kj::Maybe<MessageSize> sizeHint = nullptr);
|
||||
typename Results::Builder initResults(kj::Maybe<MessageSize> sizeHint = nullptr);
|
||||
void setResults(typename Results::Reader value);
|
||||
void adoptResults(Orphan<Results>&& value);
|
||||
Orphanage getResultsOrphanage(kj::Maybe<MessageSize> sizeHint = nullptr);
|
||||
// Manipulate the results payload. The "Return" message (part of the RPC protocol) will
|
||||
// typically be allocated the first time one of these is called. Some RPC systems may
|
||||
// allocate these messages in a limited space (such as a shared memory segment), therefore the
|
||||
// application should delay calling these as long as is convenient to do so (but don't delay
|
||||
// if doing so would require extra copies later).
|
||||
//
|
||||
// `sizeHint` indicates a guess at the message size. This will usually be used to decide how
|
||||
// much space to allocate for the first message segment (don't worry: only space that is actually
|
||||
// used will be sent on the wire). If omitted, the system decides. The message root pointer
|
||||
// should not be included in the size. So, if you are simply going to copy some existing message
|
||||
// directly into the results, just call `.totalSize()` and pass that in.
|
||||
|
||||
template <typename SubParams>
|
||||
kj::Promise<void> tailCall(Request<SubParams, Results>&& tailRequest);
|
||||
// Resolve the call by making a tail call. `tailRequest` is a request that has been filled in
|
||||
// but not yet sent. The context will send the call, then fill in the results with the result
|
||||
// of the call. If tailCall() is used, {get,init,set,adopt}Results (above) *must not* be called.
|
||||
//
|
||||
// The RPC implementation may be able to optimize a tail call to another machine such that the
|
||||
// results never actually pass through this machine. Even if no such optimization is possible,
|
||||
// `tailCall()` may allow pipelined calls to be forwarded optimistically to the new call site.
|
||||
//
|
||||
// In general, this should be the last thing a method implementation calls, and the promise
|
||||
// returned from `tailCall()` should then be returned by the method implementation.
|
||||
|
||||
void allowCancellation();
|
||||
// Indicate that it is OK for the RPC system to discard its Promise for this call's result if
|
||||
// the caller cancels the call, thereby transitively canceling any asynchronous operations the
|
||||
// call implementation was performing. This is not done by default because it could represent a
|
||||
// security risk: applications must be carefully written to ensure that they do not end up in
|
||||
// a bad state if an operation is canceled at an arbitrary point. However, for long-running
|
||||
// method calls that hold significant resources, prompt cancellation is often useful.
|
||||
//
|
||||
// Keep in mind that asynchronous cancellation cannot occur while the method is synchronously
|
||||
// executing on a local thread. The method must perform an asynchronous operation or call
|
||||
// `EventLoop::current().evalLater()` to yield control.
|
||||
//
|
||||
// Note: You might think that we should offer `onCancel()` and/or `isCanceled()` methods that
|
||||
// provide notification when the caller cancels the request without forcefully killing off the
|
||||
// promise chain. Unfortunately, this composes poorly with promise forking: the canceled
|
||||
// path may be just one branch of a fork of the result promise. The other branches still want
|
||||
// the call to continue. Promise forking is used within the Cap'n Proto implementation -- in
|
||||
// particular each pipelined call forks the result promise. So, if a caller made a pipelined
|
||||
// call and then dropped the original object, the call should not be canceled, but it would be
|
||||
// excessively complicated for the framework to avoid notififying of cancellation as long as
|
||||
// pipelined calls still exist.
|
||||
|
||||
private:
|
||||
CallContextHook* hook;
|
||||
|
||||
friend class Capability::Server;
|
||||
friend struct DynamicCapability;
|
||||
};
|
||||
|
||||
class Capability::Server {
|
||||
// Objects implementing a Cap'n Proto interface must subclass this. Typically, such objects
|
||||
// will instead subclass a typed Server interface which will take care of implementing
|
||||
// dispatchCall().
|
||||
|
||||
public:
|
||||
typedef Capability Serves;
|
||||
|
||||
virtual kj::Promise<void> dispatchCall(uint64_t interfaceId, uint16_t methodId,
|
||||
CallContext<AnyPointer, AnyPointer> context) = 0;
|
||||
// Call the given method. `params` is the input struct, and should be released as soon as it
|
||||
// is no longer needed. `context` may be used to allocate the output struct and deal with
|
||||
// cancellation.
|
||||
|
||||
// TODO(someday): Method which can optionally be overridden to implement Join when the object is
|
||||
// a proxy.
|
||||
|
||||
protected:
|
||||
inline Capability::Client thisCap();
|
||||
// Get a capability pointing to this object, much like the `this` keyword.
|
||||
//
|
||||
// The effect of this method is undefined if:
|
||||
// - No capability client has been created pointing to this object. (This is always the case in
|
||||
// the server's constructor.)
|
||||
// - The capability client pointing at this object has been destroyed. (This is always the case
|
||||
// in the server's destructor.)
|
||||
// - Multiple capability clients have been created around the same server (possible if the server
|
||||
// is refcounted, which is not recommended since the client itself provides refcounting).
|
||||
|
||||
template <typename Params, typename Results>
|
||||
CallContext<Params, Results> internalGetTypedContext(
|
||||
CallContext<AnyPointer, AnyPointer> typeless);
|
||||
kj::Promise<void> internalUnimplemented(const char* actualInterfaceName,
|
||||
uint64_t requestedTypeId);
|
||||
kj::Promise<void> internalUnimplemented(const char* interfaceName,
|
||||
uint64_t typeId, uint16_t methodId);
|
||||
kj::Promise<void> internalUnimplemented(const char* interfaceName, const char* methodName,
|
||||
uint64_t typeId, uint16_t methodId);
|
||||
|
||||
private:
|
||||
ClientHook* thisHook = nullptr;
|
||||
friend class LocalClient;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
class ReaderCapabilityTable: private _::CapTableReader {
|
||||
// Class which imbues Readers with the ability to read capabilities.
|
||||
//
|
||||
// In Cap'n Proto format, the encoding of a capability pointer is simply an integer index into
|
||||
// an external table. Since these pointers fundamentally point outside the message, a
|
||||
// MessageReader by default has no idea what they point at, and therefore reading capabilities
|
||||
// from such a reader will throw exceptions.
|
||||
//
|
||||
// In order to be able to read capabilities, you must first attach a capability table, using
|
||||
// this class. By "imbuing" a Reader, you get a new Reader which will interpret capability
|
||||
// pointers by treating them as indexes into the ReaderCapabilityTable.
|
||||
//
|
||||
// Note that when using Cap'n Proto's RPC system, this is handled automatically.
|
||||
|
||||
public:
|
||||
explicit ReaderCapabilityTable(kj::Array<kj::Maybe<kj::Own<ClientHook>>> table);
|
||||
KJ_DISALLOW_COPY(ReaderCapabilityTable);
|
||||
|
||||
template <typename T>
|
||||
T imbue(T reader);
|
||||
// Return a reader equivalent to `reader` except that when reading capability-valued fields,
|
||||
// the capabilities are looked up in this table.
|
||||
|
||||
private:
|
||||
kj::Array<kj::Maybe<kj::Own<ClientHook>>> table;
|
||||
|
||||
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override;
|
||||
};
|
||||
|
||||
class BuilderCapabilityTable: private _::CapTableBuilder {
|
||||
// Class which imbues Builders with the ability to read and write capabilities.
|
||||
//
|
||||
// This is much like ReaderCapabilityTable, except for builders. The table starts out empty,
|
||||
// but capabilities can be added to it over time.
|
||||
|
||||
public:
|
||||
BuilderCapabilityTable();
|
||||
KJ_DISALLOW_COPY(BuilderCapabilityTable);
|
||||
|
||||
inline kj::ArrayPtr<kj::Maybe<kj::Own<ClientHook>>> getTable() { return table; }
|
||||
|
||||
template <typename T>
|
||||
T imbue(T builder);
|
||||
// Return a builder equivalent to `builder` except that when reading capability-valued fields,
|
||||
// the capabilities are looked up in this table.
|
||||
|
||||
private:
|
||||
kj::Vector<kj::Maybe<kj::Own<ClientHook>>> table;
|
||||
|
||||
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override;
|
||||
uint injectCap(kj::Own<ClientHook>&& cap) override;
|
||||
void dropCap(uint index) override;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
class CapabilityServerSetBase {
|
||||
public:
|
||||
Capability::Client addInternal(kj::Own<Capability::Server>&& server, void* ptr);
|
||||
kj::Promise<void*> getLocalServerInternal(Capability::Client& client);
|
||||
};
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T>
|
||||
class CapabilityServerSet: private _::CapabilityServerSetBase {
|
||||
// Allows a server to recognize its own capabilities when passed back to it, and obtain the
|
||||
// underlying Server objects associated with them.
|
||||
//
|
||||
// All objects in the set must have the same interface type T. The objects may implement various
|
||||
// interfaces derived from T (and in fact T can be `capnp::Capability` to accept all objects),
|
||||
// but note that if you compile with RTTI disabled then you will not be able to down-cast through
|
||||
// virtual inheritance, and all inheritance between server interfaces is virtual. So, with RTTI
|
||||
// disabled, you will likely need to set T to be the most-derived Cap'n Proto interface type,
|
||||
// and you server class will need to be directly derived from that, so that you can use
|
||||
// static_cast (or kj::downcast) to cast to it after calling getLocalServer(). (If you compile
|
||||
// with RTTI, then you can freely dynamic_cast and ignore this issue!)
|
||||
|
||||
public:
|
||||
CapabilityServerSet() = default;
|
||||
KJ_DISALLOW_COPY(CapabilityServerSet);
|
||||
|
||||
typename T::Client add(kj::Own<typename T::Server>&& server);
|
||||
// Create a new capability Client for the given Server and also add this server to the set.
|
||||
|
||||
kj::Promise<kj::Maybe<typename T::Server&>> getLocalServer(typename T::Client& client);
|
||||
// Given a Client pointing to a server previously passed to add(), return the corresponding
|
||||
// Server. This returns a promise because if the input client is itself a promise, this must
|
||||
// wait for it to resolve. Keep in mind that the server will be deleted when all clients are
|
||||
// gone, so the caller should make sure to keep the client alive (hence why this method only
|
||||
// accepts an lvalue input).
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Hook interfaces which must be implemented by the RPC system. Applications never call these
|
||||
// directly; the RPC system implements them and the types defined earlier in this file wrap them.
|
||||
|
||||
class RequestHook {
|
||||
// Hook interface implemented by RPC system representing a request being built.
|
||||
|
||||
public:
|
||||
virtual RemotePromise<AnyPointer> send() = 0;
|
||||
// Send the call and return a promise for the result.
|
||||
|
||||
virtual const void* getBrand() = 0;
|
||||
// Returns a void* that identifies who made this request. This can be used by an RPC adapter to
|
||||
// discover when tail call is going to be sent over its own connection and therefore can be
|
||||
// optimized into a remote tail call.
|
||||
|
||||
template <typename T, typename U>
|
||||
inline static kj::Own<RequestHook> from(Request<T, U>&& request) {
|
||||
return kj::mv(request.hook);
|
||||
}
|
||||
};
|
||||
|
||||
class ResponseHook {
|
||||
// Hook interface implemented by RPC system representing a response.
|
||||
//
|
||||
// At present this class has no methods. It exists only for garbage collection -- when the
|
||||
// ResponseHook is destroyed, the results can be freed.
|
||||
|
||||
public:
|
||||
virtual ~ResponseHook() noexcept(false);
|
||||
// Just here to make sure the type is dynamic.
|
||||
|
||||
template <typename T>
|
||||
inline static kj::Own<ResponseHook> from(Response<T>&& response) {
|
||||
return kj::mv(response.hook);
|
||||
}
|
||||
};
|
||||
|
||||
// class PipelineHook is declared in any.h because it is needed there.
|
||||
|
||||
class ClientHook {
|
||||
public:
|
||||
ClientHook();
|
||||
|
||||
virtual Request<AnyPointer, AnyPointer> newCall(
|
||||
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) = 0;
|
||||
// Start a new call, allowing the client to allocate request/response objects as it sees fit.
|
||||
// This version is used when calls are made from application code in the local process.
|
||||
|
||||
struct VoidPromiseAndPipeline {
|
||||
kj::Promise<void> promise;
|
||||
kj::Own<PipelineHook> pipeline;
|
||||
};
|
||||
|
||||
virtual VoidPromiseAndPipeline call(uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Own<CallContextHook>&& context) = 0;
|
||||
// Call the object, but the caller controls allocation of the request/response objects. If the
|
||||
// callee insists on allocating these objects itself, it must make a copy. This version is used
|
||||
// when calls come in over the network via an RPC system. Note that even if the returned
|
||||
// `Promise<void>` is discarded, the call may continue executing if any pipelined calls are
|
||||
// waiting for it.
|
||||
//
|
||||
// Since the caller of this method chooses the CallContext implementation, it is the caller's
|
||||
// responsibility to ensure that the returned promise is not canceled unless allowed via
|
||||
// the context's `allowCancellation()`.
|
||||
//
|
||||
// The call must not begin synchronously; the callee must arrange for the call to begin in a
|
||||
// later turn of the event loop. Otherwise, application code may call back and affect the
|
||||
// callee's state in an unexpected way.
|
||||
|
||||
virtual kj::Maybe<ClientHook&> getResolved() = 0;
|
||||
// If this ClientHook is a promise that has already resolved, returns the inner, resolved version
|
||||
// of the capability. The caller may permanently replace this client with the resolved one if
|
||||
// desired. Returns null if the client isn't a promise or hasn't resolved yet -- use
|
||||
// `whenMoreResolved()` to distinguish between them.
|
||||
|
||||
virtual kj::Maybe<kj::Promise<kj::Own<ClientHook>>> whenMoreResolved() = 0;
|
||||
// If this client is a settled reference (not a promise), return nullptr. Otherwise, return a
|
||||
// promise that eventually resolves to a new client that is closer to being the final, settled
|
||||
// client (i.e. the value eventually returned by `getResolved()`). Calling this repeatedly
|
||||
// should eventually produce a settled client.
|
||||
|
||||
kj::Promise<void> whenResolved();
|
||||
// Repeatedly calls whenMoreResolved() until it returns nullptr.
|
||||
|
||||
virtual kj::Own<ClientHook> addRef() = 0;
|
||||
// Return a new reference to the same capability.
|
||||
|
||||
virtual const void* getBrand() = 0;
|
||||
// Returns a void* that identifies who made this client. This can be used by an RPC adapter to
|
||||
// discover when a capability it needs to marshal is one that it created in the first place, and
|
||||
// therefore it can transfer the capability without proxying.
|
||||
|
||||
static const uint NULL_CAPABILITY_BRAND;
|
||||
// Value is irrelevant; used for pointer.
|
||||
|
||||
inline bool isNull() { return getBrand() == &NULL_CAPABILITY_BRAND; }
|
||||
// Returns true if the capability was created as a result of assigning a Client to null or by
|
||||
// reading a null pointer out of a Cap'n Proto message.
|
||||
|
||||
virtual void* getLocalServer(_::CapabilityServerSetBase& capServerSet);
|
||||
// If this is a local capability created through `capServerSet`, return the underlying Server.
|
||||
// Otherwise, return nullptr. Default implementation (which everyone except LocalClient should
|
||||
// use) always returns nullptr.
|
||||
|
||||
static kj::Own<ClientHook> from(Capability::Client client) { return kj::mv(client.hook); }
|
||||
};
|
||||
|
||||
class CallContextHook {
|
||||
// Hook interface implemented by RPC system to manage a call on the server side. See
|
||||
// CallContext<T>.
|
||||
|
||||
public:
|
||||
virtual AnyPointer::Reader getParams() = 0;
|
||||
virtual void releaseParams() = 0;
|
||||
virtual AnyPointer::Builder getResults(kj::Maybe<MessageSize> sizeHint) = 0;
|
||||
virtual kj::Promise<void> tailCall(kj::Own<RequestHook>&& request) = 0;
|
||||
virtual void allowCancellation() = 0;
|
||||
|
||||
virtual kj::Promise<AnyPointer::Pipeline> onTailCall() = 0;
|
||||
// If `tailCall()` is called, resolves to the PipelineHook from the tail call. An
|
||||
// implementation of `ClientHook::call()` is allowed to call this at most once.
|
||||
|
||||
virtual ClientHook::VoidPromiseAndPipeline directTailCall(kj::Own<RequestHook>&& request) = 0;
|
||||
// Call this when you would otherwise call onTailCall() immediately followed by tailCall().
|
||||
// Implementations of tailCall() should typically call directTailCall() and then fulfill the
|
||||
// promise fulfiller for onTailCall() with the returned pipeline.
|
||||
|
||||
virtual kj::Own<CallContextHook> addRef() = 0;
|
||||
};
|
||||
|
||||
kj::Own<ClientHook> newLocalPromiseClient(kj::Promise<kj::Own<ClientHook>>&& promise);
|
||||
// Returns a ClientHook that queues up calls until `promise` resolves, then forwards them to
|
||||
// the new client. This hook's `getResolved()` and `whenMoreResolved()` methods will reflect the
|
||||
// redirection to the eventual replacement client.
|
||||
|
||||
kj::Own<PipelineHook> newLocalPromisePipeline(kj::Promise<kj::Own<PipelineHook>>&& promise);
|
||||
// Returns a PipelineHook that queues up calls until `promise` resolves, then forwards them to
|
||||
// the new pipeline.
|
||||
|
||||
kj::Own<ClientHook> newBrokenCap(kj::StringPtr reason);
|
||||
kj::Own<ClientHook> newBrokenCap(kj::Exception&& reason);
|
||||
// Helper function that creates a capability which simply throws exceptions when called.
|
||||
|
||||
kj::Own<PipelineHook> newBrokenPipeline(kj::Exception&& reason);
|
||||
// Helper function that creates a pipeline which simply throws exceptions when called.
|
||||
|
||||
Request<AnyPointer, AnyPointer> newBrokenRequest(
|
||||
kj::Exception&& reason, kj::Maybe<MessageSize> sizeHint);
|
||||
// Helper function that creates a Request object that simply throws exceptions when sent.
|
||||
|
||||
// =======================================================================================
|
||||
// Extend PointerHelpers for interfaces
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T>
|
||||
struct PointerHelpers<T, Kind::INTERFACE> {
|
||||
static inline typename T::Client get(PointerReader reader) {
|
||||
return typename T::Client(reader.getCapability());
|
||||
}
|
||||
static inline typename T::Client get(PointerBuilder builder) {
|
||||
return typename T::Client(builder.getCapability());
|
||||
}
|
||||
static inline void set(PointerBuilder builder, typename T::Client&& value) {
|
||||
builder.setCapability(kj::mv(value.Capability::Client::hook));
|
||||
}
|
||||
static inline void set(PointerBuilder builder, typename T::Client& value) {
|
||||
builder.setCapability(value.Capability::Client::hook->addRef());
|
||||
}
|
||||
static inline void adopt(PointerBuilder builder, Orphan<T>&& value) {
|
||||
builder.adopt(kj::mv(value.builder));
|
||||
}
|
||||
static inline Orphan<T> disown(PointerBuilder builder) {
|
||||
return Orphan<T>(builder.disown());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
// =======================================================================================
|
||||
// Extend List for interfaces
|
||||
|
||||
template <typename T>
|
||||
struct List<T, Kind::INTERFACE> {
|
||||
List() = delete;
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
typedef List<T> Reads;
|
||||
|
||||
Reader() = default;
|
||||
inline explicit Reader(_::ListReader reader): reader(reader) {}
|
||||
|
||||
inline uint size() const { return unbound(reader.size() / ELEMENTS); }
|
||||
inline typename T::Client operator[](uint index) const {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return typename T::Client(reader.getPointerElement(
|
||||
bounded(index) * ELEMENTS).getCapability());
|
||||
}
|
||||
|
||||
typedef _::IndexingIterator<const Reader, typename T::Client> Iterator;
|
||||
inline Iterator begin() const { return Iterator(this, 0); }
|
||||
inline Iterator end() const { return Iterator(this, size()); }
|
||||
|
||||
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;
|
||||
|
||||
Builder() = delete;
|
||||
inline Builder(decltype(nullptr)) {}
|
||||
inline explicit Builder(_::ListBuilder builder): builder(builder) {}
|
||||
|
||||
inline operator Reader() const { return Reader(builder.asReader()); }
|
||||
inline Reader asReader() const { return Reader(builder.asReader()); }
|
||||
|
||||
inline uint size() const { return unbound(builder.size() / ELEMENTS); }
|
||||
inline typename T::Client operator[](uint index) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return typename T::Client(builder.getPointerElement(
|
||||
bounded(index) * ELEMENTS).getCapability());
|
||||
}
|
||||
inline void set(uint index, typename T::Client value) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
builder.getPointerElement(bounded(index) * ELEMENTS).setCapability(kj::mv(value.hook));
|
||||
}
|
||||
inline void adopt(uint index, Orphan<T>&& value) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(value));
|
||||
}
|
||||
inline Orphan<T> disown(uint index) {
|
||||
KJ_IREQUIRE(index < size());
|
||||
return Orphan<T>(builder.getPointerElement(bounded(index) * ELEMENTS).disown());
|
||||
}
|
||||
|
||||
typedef _::IndexingIterator<Builder, typename T::Client> Iterator;
|
||||
inline Iterator begin() { return Iterator(this, 0); }
|
||||
inline Iterator end() { return Iterator(this, size()); }
|
||||
|
||||
private:
|
||||
_::ListBuilder builder;
|
||||
friend class Orphanage;
|
||||
template <typename U, Kind K>
|
||||
friend struct ToDynamic_;
|
||||
};
|
||||
|
||||
private:
|
||||
inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) {
|
||||
return builder.initList(ElementSize::POINTER, bounded(size) * ELEMENTS);
|
||||
}
|
||||
inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) {
|
||||
return builder.getList(ElementSize::POINTER, defaultValue);
|
||||
}
|
||||
inline static _::ListReader getFromPointer(
|
||||
const _::PointerReader& reader, const word* defaultValue) {
|
||||
return reader.getList(ElementSize::POINTER, defaultValue);
|
||||
}
|
||||
|
||||
template <typename U, Kind k>
|
||||
friend struct List;
|
||||
template <typename U, Kind K>
|
||||
friend struct _::PointerHelpers;
|
||||
};
|
||||
|
||||
// =======================================================================================
|
||||
// Inline implementation details
|
||||
|
||||
template <typename T>
|
||||
RemotePromise<T> RemotePromise<T>::reducePromise(kj::Promise<RemotePromise>&& promise) {
|
||||
kj::Tuple<kj::Promise<Response<T>>, kj::Promise<kj::Own<PipelineHook>>> splitPromise =
|
||||
promise.then([](RemotePromise&& inner) {
|
||||
// `inner` is multiply-inherited, and we want to move away each superclass separately.
|
||||
// Let's create two references to make clear what we're doing (though this is not strictly
|
||||
// necessary).
|
||||
kj::Promise<Response<T>>& innerPromise = inner;
|
||||
typename T::Pipeline& innerPipeline = inner;
|
||||
return kj::tuple(kj::mv(innerPromise), PipelineHook::from(kj::mv(innerPipeline)));
|
||||
}).split();
|
||||
|
||||
return RemotePromise(kj::mv(kj::get<0>(splitPromise)),
|
||||
typename T::Pipeline(AnyPointer::Pipeline(
|
||||
newLocalPromisePipeline(kj::mv(kj::get<1>(splitPromise))))));
|
||||
}
|
||||
|
||||
template <typename Params, typename Results>
|
||||
RemotePromise<Results> Request<Params, Results>::send() {
|
||||
auto typelessPromise = hook->send();
|
||||
hook = nullptr; // prevent reuse
|
||||
|
||||
// Convert the Promise to return the correct response type.
|
||||
// Explicitly upcast to kj::Promise to make clear that calling .then() doesn't invalidate the
|
||||
// Pipeline part of the RemotePromise.
|
||||
auto typedPromise = kj::implicitCast<kj::Promise<Response<AnyPointer>>&>(typelessPromise)
|
||||
.then([](Response<AnyPointer>&& response) -> Response<Results> {
|
||||
return Response<Results>(response.getAs<Results>(), kj::mv(response.hook));
|
||||
});
|
||||
|
||||
// Wrap the typeless pipeline in a typed wrapper.
|
||||
typename Results::Pipeline typedPipeline(
|
||||
kj::mv(kj::implicitCast<AnyPointer::Pipeline&>(typelessPromise)));
|
||||
|
||||
return RemotePromise<Results>(kj::mv(typedPromise), kj::mv(typedPipeline));
|
||||
}
|
||||
|
||||
inline Capability::Client::Client(kj::Own<ClientHook>&& hook): hook(kj::mv(hook)) {}
|
||||
template <typename T, typename>
|
||||
inline Capability::Client::Client(kj::Own<T>&& server)
|
||||
: hook(makeLocalClient(kj::mv(server))) {}
|
||||
template <typename T, typename>
|
||||
inline Capability::Client::Client(kj::Promise<T>&& promise)
|
||||
: hook(newLocalPromiseClient(promise.then([](T&& t) { return kj::mv(t.hook); }))) {}
|
||||
inline Capability::Client::Client(Client& other): hook(other.hook->addRef()) {}
|
||||
inline Capability::Client& Capability::Client::operator=(Client& other) {
|
||||
hook = other.hook->addRef();
|
||||
return *this;
|
||||
}
|
||||
template <typename T>
|
||||
inline typename T::Client Capability::Client::castAs() {
|
||||
return typename T::Client(hook->addRef());
|
||||
}
|
||||
inline kj::Promise<void> Capability::Client::whenResolved() {
|
||||
return hook->whenResolved();
|
||||
}
|
||||
inline Request<AnyPointer, AnyPointer> Capability::Client::typelessRequest(
|
||||
uint64_t interfaceId, uint16_t methodId,
|
||||
kj::Maybe<MessageSize> sizeHint) {
|
||||
return newCall<AnyPointer, AnyPointer>(interfaceId, methodId, sizeHint);
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline Request<Params, Results> Capability::Client::newCall(
|
||||
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) {
|
||||
auto typeless = hook->newCall(interfaceId, methodId, sizeHint);
|
||||
return Request<Params, Results>(typeless.template getAs<Params>(), kj::mv(typeless.hook));
|
||||
}
|
||||
|
||||
template <typename Params, typename Results>
|
||||
inline CallContext<Params, Results>::CallContext(CallContextHook& hook): hook(&hook) {}
|
||||
template <typename Params, typename Results>
|
||||
inline typename Params::Reader CallContext<Params, Results>::getParams() {
|
||||
return hook->getParams().template getAs<Params>();
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline void CallContext<Params, Results>::releaseParams() {
|
||||
hook->releaseParams();
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline typename Results::Builder CallContext<Params, Results>::getResults(
|
||||
kj::Maybe<MessageSize> sizeHint) {
|
||||
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
|
||||
return hook->getResults(sizeHint).template getAs<Results>();
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline typename Results::Builder CallContext<Params, Results>::initResults(
|
||||
kj::Maybe<MessageSize> sizeHint) {
|
||||
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
|
||||
return hook->getResults(sizeHint).template initAs<Results>();
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline void CallContext<Params, Results>::setResults(typename Results::Reader value) {
|
||||
hook->getResults(value.totalSize()).template setAs<Results>(value);
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline void CallContext<Params, Results>::adoptResults(Orphan<Results>&& value) {
|
||||
hook->getResults(nullptr).adopt(kj::mv(value));
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline Orphanage CallContext<Params, Results>::getResultsOrphanage(
|
||||
kj::Maybe<MessageSize> sizeHint) {
|
||||
return Orphanage::getForMessageContaining(hook->getResults(sizeHint));
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
template <typename SubParams>
|
||||
inline kj::Promise<void> CallContext<Params, Results>::tailCall(
|
||||
Request<SubParams, Results>&& tailRequest) {
|
||||
return hook->tailCall(kj::mv(tailRequest.hook));
|
||||
}
|
||||
template <typename Params, typename Results>
|
||||
inline void CallContext<Params, Results>::allowCancellation() {
|
||||
hook->allowCancellation();
|
||||
}
|
||||
|
||||
template <typename Params, typename Results>
|
||||
CallContext<Params, Results> Capability::Server::internalGetTypedContext(
|
||||
CallContext<AnyPointer, AnyPointer> typeless) {
|
||||
return CallContext<Params, Results>(*typeless.hook);
|
||||
}
|
||||
|
||||
Capability::Client Capability::Server::thisCap() {
|
||||
return Client(thisHook->addRef());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReaderCapabilityTable::imbue(T reader) {
|
||||
return T(_::PointerHelpers<FromReader<T>>::getInternalReader(reader).imbue(this));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T BuilderCapabilityTable::imbue(T builder) {
|
||||
return T(_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::mv(builder)).imbue(this));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename T::Client CapabilityServerSet<T>::add(kj::Own<typename T::Server>&& server) {
|
||||
void* ptr = reinterpret_cast<void*>(server.get());
|
||||
// Clang insists that `castAs` is a template-dependent member and therefore we need the
|
||||
// `template` keyword here, but AFAICT this is wrong: addImpl() is not a template.
|
||||
return addInternal(kj::mv(server), ptr).template castAs<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
kj::Promise<kj::Maybe<typename T::Server&>> CapabilityServerSet<T>::getLocalServer(
|
||||
typename T::Client& client) {
|
||||
return getLocalServerInternal(client)
|
||||
.then([](void* server) -> kj::Maybe<typename T::Server&> {
|
||||
if (server == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return *reinterpret_cast<typename T::Server*>(server);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Orphanage::GetInnerReader<T, Kind::INTERFACE> {
|
||||
static inline kj::Own<ClientHook> apply(typename T::Client t) {
|
||||
return ClientHook::from(kj::mv(t));
|
||||
}
|
||||
};
|
||||
|
||||
#define CAPNP_CAPABILITY_H_INCLUDED // for testing includes in unit test
|
||||
|
||||
} // namespace capnp
|
|
@ -0,0 +1,71 @@
|
|||
#! /bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
set -eu
|
||||
|
||||
if test $# = 0; then
|
||||
echo trigger filetype:.capnp
|
||||
exit 0
|
||||
fi
|
||||
|
||||
INPUT=$1
|
||||
|
||||
case "$INPUT" in
|
||||
*capnp/c++.capnp | \
|
||||
*capnp/schema.capnp | \
|
||||
*capnp/rpc.capnp | \
|
||||
*capnp/rpc-twoparty.capnp | \
|
||||
*capnp/persistent.capnp | \
|
||||
*capnp/compiler/lexer.capnp | \
|
||||
*capnp/compiler/grammar.capnp | \
|
||||
*capnp/compat/json.capnp )
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
echo findProvider special:ekam-interceptor
|
||||
read INTERCEPTOR
|
||||
|
||||
if test "$INTERCEPTOR" = ""; then
|
||||
echo "error: couldn't find intercept.so." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo findProvider file:compiler/capnp
|
||||
read CAPNP
|
||||
|
||||
if test "$CAPNP" = ""; then
|
||||
echo "error: couldn't find capnp." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo findProvider file:capnpc-c++
|
||||
read CAPNPC_CXX
|
||||
|
||||
if test "$CAPNPC_CXX" = ""; then
|
||||
echo "error: couldn't find capnpc-c++." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LD_PRELOAD=$INTERCEPTOR DYLD_FORCE_FLAT_NAMESPACE= DYLD_INSERT_LIBRARIES=$INTERCEPTOR \
|
||||
$CAPNP compile -I. -o$CAPNPC_CXX "$INPUT" 3>&1 4<&0 >&2
|
|
@ -0,0 +1,87 @@
|
|||
// 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 <kj/compat/gtest.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/debug.h>
|
||||
#include <capnp/test.capnp.h>
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
namespace capnp {
|
||||
namespace {
|
||||
|
||||
TEST(Common, Version) {
|
||||
#ifdef VERSION
|
||||
auto expectedVersion =
|
||||
kj::str(CAPNP_VERSION_MAJOR, '.', CAPNP_VERSION_MINOR, '.', CAPNP_VERSION_MICRO);
|
||||
auto devVersion =
|
||||
kj::str(CAPNP_VERSION_MAJOR, '.', CAPNP_VERSION_MINOR, "-dev");
|
||||
kj::StringPtr actualVersion = VERSION;
|
||||
KJ_ASSERT(actualVersion == expectedVersion ||
|
||||
actualVersion.startsWith(kj::str(expectedVersion, '-')) ||
|
||||
actualVersion.startsWith(kj::str(expectedVersion, '.')) ||
|
||||
(actualVersion == devVersion && CAPNP_VERSION_MICRO == 0),
|
||||
expectedVersion, actualVersion);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct ExampleStruct {
|
||||
struct _capnpPrivate {
|
||||
struct IsStruct;
|
||||
};
|
||||
};
|
||||
struct ExampleInterface {
|
||||
struct _capnpPrivate {
|
||||
struct IsInterface;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(_::Kind_<ExampleStruct>::kind == Kind::STRUCT, "Kind SFINAE failed.");
|
||||
static_assert(_::Kind_<ExampleInterface>::kind == Kind::INTERFACE, "Kind SFINAE failed.");
|
||||
|
||||
// Test FromAnay<>
|
||||
template <typename T, typename U>
|
||||
struct EqualTypes_ { static constexpr bool value = false; };
|
||||
|
||||
template <typename T>
|
||||
struct EqualTypes_<T, T> { static constexpr bool value = true; };
|
||||
|
||||
template <typename T, typename U>
|
||||
inline constexpr bool equalTypes() { return EqualTypes_<T, U>::value; }
|
||||
|
||||
using capnproto_test::capnp::test::TestAllTypes;
|
||||
using capnproto_test::capnp::test::TestInterface;
|
||||
|
||||
static_assert(equalTypes<FromAny<int>, int>(), "");
|
||||
static_assert(equalTypes<FromAny<TestAllTypes::Reader>, TestAllTypes>(), "");
|
||||
static_assert(equalTypes<FromAny<TestAllTypes::Builder>, TestAllTypes>(), "");
|
||||
#if !CAPNP_LITE
|
||||
static_assert(equalTypes<FromAny<TestAllTypes::Pipeline>, TestAllTypes>(), "");
|
||||
static_assert(equalTypes<FromAny<TestInterface::Client>, TestInterface>(), "");
|
||||
static_assert(equalTypes<FromAny<kj::Own<TestInterface::Server>>, TestInterface>(), "");
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
} // namespace capnp
|
|
@ -0,0 +1,743 @@
|
|||
// 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
|
||||
|
||||
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/memory.h>
|
||||
#include <kj/windows-sanity.h> // work-around macro conflict with `VOID`
|
||||
|
||||
#if CAPNP_DEBUG_TYPES
|
||||
#include <kj/units.h>
|
||||
#endif
|
||||
|
||||
namespace capnp {
|
||||
|
||||
#define CAPNP_VERSION_MAJOR 0
|
||||
#define CAPNP_VERSION_MINOR 7
|
||||
#define CAPNP_VERSION_MICRO 0
|
||||
|
||||
#define CAPNP_VERSION \
|
||||
(CAPNP_VERSION_MAJOR * 1000000 + CAPNP_VERSION_MINOR * 1000 + CAPNP_VERSION_MICRO)
|
||||
|
||||
#ifndef CAPNP_LITE
|
||||
#define CAPNP_LITE 0
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#if _MSC_VER
|
||||
|
||||
#define CAPNP_KIND(T) ::capnp::_::Kind_<T>::kind
|
||||
// Avoid constexpr methods in MSVC (it remains buggy in many situations).
|
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
#define CAPNP_KIND(T) ::capnp::kind<T>()
|
||||
// Use this macro rather than kind<T>() in any code which must work in MSVC.
|
||||
|
||||
#endif // _MSC_VER, else
|
||||
|
||||
#if !CAPNP_LITE
|
||||
|
||||
template <typename T, Kind k = kind<T>()>
|
||||
inline constexpr Style style() {
|
||||
return k == Kind::PRIMITIVE || k == Kind::ENUM ? Style::PRIMITIVE
|
||||
: k == Kind::STRUCT ? Style::STRUCT
|
||||
: k == Kind::INTERFACE ? Style::CAPABILITY : Style::POINTER;
|
||||
}
|
||||
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)>
|
||||
struct List;
|
||||
|
||||
#if _MSC_VER
|
||||
|
||||
template <typename T, Kind k>
|
||||
struct List {};
|
||||
// For some reason, without this declaration, MSVC will error out on some uses of List
|
||||
// claiming that "T" -- as used in the default initializer for the second template param, "k" --
|
||||
// is not defined. I do not understand this error, but adding this empty default declaration fixes
|
||||
// it.
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T> struct ListElementType_;
|
||||
template <typename T> struct ListElementType_<List<T>> { typedef T Type; };
|
||||
template <typename T> using ListElementType = typename ListElementType_<T>::Type;
|
||||
|
||||
namespace _ { // private
|
||||
template <typename T, Kind k> struct Kind_<List<T, k>> {
|
||||
static constexpr Kind kind = Kind::LIST;
|
||||
};
|
||||
} // namespace _ (private)
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)> struct ReaderFor_ { typedef typename T::Reader Type; };
|
||||
template <typename T> struct ReaderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
|
||||
template <typename T> struct ReaderFor_<T, Kind::ENUM> { typedef T Type; };
|
||||
template <typename T> struct ReaderFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
|
||||
template <typename T> using ReaderFor = typename ReaderFor_<T>::Type;
|
||||
// The type returned by List<T>::Reader::operator[].
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)> struct BuilderFor_ { typedef typename T::Builder Type; };
|
||||
template <typename T> struct BuilderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
|
||||
template <typename T> struct BuilderFor_<T, Kind::ENUM> { typedef T Type; };
|
||||
template <typename T> struct BuilderFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
|
||||
template <typename T> using BuilderFor = typename BuilderFor_<T>::Type;
|
||||
// The type returned by List<T>::Builder::operator[].
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)> struct PipelineFor_ { typedef typename T::Pipeline Type;};
|
||||
template <typename T> struct PipelineFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
|
||||
template <typename T> using PipelineFor = typename PipelineFor_<T>::Type;
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)> struct TypeIfEnum_;
|
||||
template <typename T> struct TypeIfEnum_<T, Kind::ENUM> { typedef T Type; };
|
||||
|
||||
template <typename T>
|
||||
using TypeIfEnum = typename TypeIfEnum_<kj::Decay<T>>::Type;
|
||||
|
||||
template <typename T>
|
||||
using FromReader = typename kj::Decay<T>::Reads;
|
||||
// FromReader<MyType::Reader> = MyType (for any Cap'n Proto type).
|
||||
|
||||
template <typename T>
|
||||
using FromBuilder = typename kj::Decay<T>::Builds;
|
||||
// FromBuilder<MyType::Builder> = MyType (for any Cap'n Proto type).
|
||||
|
||||
template <typename T>
|
||||
using FromPipeline = typename kj::Decay<T>::Pipelines;
|
||||
// FromBuilder<MyType::Pipeline> = MyType (for any Cap'n Proto type).
|
||||
|
||||
template <typename T>
|
||||
using FromClient = typename kj::Decay<T>::Calls;
|
||||
// FromReader<MyType::Client> = MyType (for any Cap'n Proto interface type).
|
||||
|
||||
template <typename T>
|
||||
using FromServer = typename kj::Decay<T>::Serves;
|
||||
// FromBuilder<MyType::Server> = MyType (for any Cap'n Proto interface type).
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct FromAny_;
|
||||
|
||||
template <typename T>
|
||||
struct FromAny_<T, kj::VoidSfinae<FromReader<T>>> {
|
||||
using Type = FromReader<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FromAny_<T, kj::VoidSfinae<FromBuilder<T>>> {
|
||||
using Type = FromBuilder<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FromAny_<T, kj::VoidSfinae<FromPipeline<T>>> {
|
||||
using Type = FromPipeline<T>;
|
||||
};
|
||||
|
||||
// Note that T::Client is covered by FromReader
|
||||
|
||||
template <typename T>
|
||||
struct FromAny_<kj::Own<T>, kj::VoidSfinae<FromServer<T>>> {
|
||||
using Type = FromServer<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FromAny_<T,
|
||||
kj::EnableIf<_::Kind_<T>::kind == Kind::PRIMITIVE || _::Kind_<T>::kind == Kind::ENUM>> {
|
||||
// TODO(msvc): Ideally the EnableIf condition would be `style<T>() == Style::PRIMITIVE`, but MSVC
|
||||
// cannot yet use style<T>() in this constexpr context.
|
||||
|
||||
using Type = kj::Decay<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using FromAny = typename FromAny_<T>::Type;
|
||||
// Given any Cap'n Proto value type as an input, return the Cap'n Proto base type. That is:
|
||||
//
|
||||
// Foo::Reader -> Foo
|
||||
// Foo::Builder -> Foo
|
||||
// Foo::Pipeline -> Foo
|
||||
// Foo::Client -> Foo
|
||||
// Own<Foo::Server> -> Foo
|
||||
// uint32_t -> uint32_t
|
||||
|
||||
namespace _ { // private
|
||||
|
||||
template <typename T, Kind k = CAPNP_KIND(T)>
|
||||
struct PointerHelpers;
|
||||
|
||||
#if _MSC_VER
|
||||
|
||||
template <typename T, Kind k>
|
||||
struct PointerHelpers {};
|
||||
// For some reason, without this declaration, MSVC will error out on some uses of PointerHelpers
|
||||
// claiming that "T" -- as used in the default initializer for the second template param, "k" --
|
||||
// is not defined. I do not understand this error, but adding this empty default declaration fixes
|
||||
// it.
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace _ (private)
|
||||
|
||||
struct MessageSize {
|
||||
// Size of a message. Every struct 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 becaues 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(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
|
|
@ -0,0 +1,980 @@
|
|||
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
|
||||
// Licensed under the MIT License:
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "json.h"
|
||||
#include <capnp/test-util.h>
|
||||
#include <capnp/compat/json.capnp.h>
|
||||
#include <capnp/compat/json-test.capnp.h>
|
||||
#include <kj/debug.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/test.h>
|
||||
|
||||
namespace capnp {
|
||||
namespace _ { // private
|
||||
namespace {
|
||||
|
||||
KJ_TEST("basic json encoding") {
|
||||
JsonCodec json;
|
||||
|
||||
KJ_EXPECT(json.encode(VOID) == "null");
|
||||
KJ_EXPECT(json.encode(true) == "true");
|
||||
KJ_EXPECT(json.encode(false) == "false");
|
||||
KJ_EXPECT(json.encode(123) == "123");
|
||||
KJ_EXPECT(json.encode(-5.5) == "-5.5");
|
||||
KJ_EXPECT(json.encode(Text::Reader("foo")) == "\"foo\"");
|
||||
KJ_EXPECT(json.encode(Text::Reader("ab\"cd\\ef\x03")) == "\"ab\\\"cd\\\\ef\\u0003\"");
|
||||
KJ_EXPECT(json.encode(test::TestEnum::CORGE) == "\"corge\"");
|
||||
|
||||
byte bytes[] = {12, 34, 56};
|
||||
KJ_EXPECT(json.encode(Data::Reader(bytes, 3)) == "[12,34,56]");
|
||||
|
||||
json.setPrettyPrint(true);
|
||||
KJ_EXPECT(json.encode(Data::Reader(bytes, 3)) == "[12, 34, 56]");
|
||||
}
|
||||
|
||||
const char ALL_TYPES_JSON[] =
|
||||
"{ \"voidField\": null,\n"
|
||||
" \"boolField\": true,\n"
|
||||
" \"int8Field\": -123,\n"
|
||||
" \"int16Field\": -12345,\n"
|
||||
" \"int32Field\": -12345678,\n"
|
||||
" \"int64Field\": \"-123456789012345\",\n"
|
||||
" \"uInt8Field\": 234,\n"
|
||||
" \"uInt16Field\": 45678,\n"
|
||||
" \"uInt32Field\": 3456789012,\n"
|
||||
" \"uInt64Field\": \"12345678901234567890\",\n"
|
||||
" \"float32Field\": 1234.5,\n"
|
||||
" \"float64Field\": -1.23e47,\n"
|
||||
" \"textField\": \"foo\",\n"
|
||||
" \"dataField\": [98, 97, 114],\n"
|
||||
" \"structField\": {\n"
|
||||
" \"voidField\": null,\n"
|
||||
" \"boolField\": true,\n"
|
||||
" \"int8Field\": -12,\n"
|
||||
" \"int16Field\": 3456,\n"
|
||||
" \"int32Field\": -78901234,\n"
|
||||
" \"int64Field\": \"56789012345678\",\n"
|
||||
" \"uInt8Field\": 90,\n"
|
||||
" \"uInt16Field\": 1234,\n"
|
||||
" \"uInt32Field\": 56789012,\n"
|
||||
" \"uInt64Field\": \"345678901234567890\",\n"
|
||||
" \"float32Field\": -1.2499999646475857e-10,\n"
|
||||
" \"float64Field\": 345,\n"
|
||||
" \"textField\": \"baz\",\n"
|
||||
" \"dataField\": [113, 117, 120],\n"
|
||||
" \"structField\": {\n"
|
||||
" \"voidField\": null,\n"
|
||||
" \"boolField\": false,\n"
|
||||
" \"int8Field\": 0,\n"
|
||||
" \"int16Field\": 0,\n"
|
||||
" \"int32Field\": 0,\n"
|
||||
" \"int64Field\": \"0\",\n"
|
||||
" \"uInt8Field\": 0,\n"
|
||||
" \"uInt16Field\": 0,\n"
|
||||
" \"uInt32Field\": 0,\n"
|
||||
" \"uInt64Field\": \"0\",\n"
|
||||
" \"float32Field\": 0,\n"
|
||||
" \"float64Field\": 0,\n"
|
||||
" \"textField\": \"nested\",\n"
|
||||
" \"structField\": {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"really nested\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
|
||||
" \"enumField\": \"foo\",\n"
|
||||
" \"interfaceField\": null },\n"
|
||||
" \"enumField\": \"baz\",\n"
|
||||
" \"interfaceField\": null,\n"
|
||||
" \"voidList\": [null, null, null],\n"
|
||||
" \"boolList\": [false, true, false, true, true],\n"
|
||||
" \"int8List\": [12, -34, -128, 127],\n"
|
||||
" \"int16List\": [1234, -5678, -32768, 32767],\n"
|
||||
" \"int32List\": [12345678, -90123456, -2147483648, 2147483647],\n"
|
||||
" \"int64List\": [\"123456789012345\", \"-678901234567890\", \"-9223372036854775808\", \"9223372036854775807\"],\n"
|
||||
" \"uInt8List\": [12, 34, 0, 255],\n"
|
||||
" \"uInt16List\": [1234, 5678, 0, 65535],\n"
|
||||
" \"uInt32List\": [12345678, 90123456, 0, 4294967295],\n"
|
||||
" \"uInt64List\": [\"123456789012345\", \"678901234567890\", \"0\", \"18446744073709551615\"],\n"
|
||||
" \"float32List\": [0, 1234567, 9.9999999338158125e36, -9.9999999338158125e36, 9.99999991097579e-38, -9.99999991097579e-38],\n"
|
||||
" \"float64List\": [0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306],\n"
|
||||
" \"textList\": [\"quux\", \"corge\", \"grault\"],\n"
|
||||
" \"dataList\": [[103, 97, 114, 112, 108, 121], [119, 97, 108, 100, 111], [102, 114, 101, 100]],\n"
|
||||
" \"structList\": [\n"
|
||||
" {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"x structlist 1\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
|
||||
" {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"x structlist 2\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
|
||||
" {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"x structlist 3\", \"enumField\": \"foo\", \"interfaceField\": null} ],\n"
|
||||
" \"enumList\": [\"qux\", \"bar\", \"grault\"] },\n"
|
||||
" \"enumField\": \"corge\",\n"
|
||||
" \"interfaceField\": null,\n"
|
||||
" \"voidList\": [null, null, null, null, null, null],\n"
|
||||
" \"boolList\": [true, false, false, true],\n"
|
||||
" \"int8List\": [111, -111],\n"
|
||||
" \"int16List\": [11111, -11111],\n"
|
||||
" \"int32List\": [111111111, -111111111],\n"
|
||||
" \"int64List\": [\"1111111111111111111\", \"-1111111111111111111\"],\n"
|
||||
" \"uInt8List\": [111, 222],\n"
|
||||
" \"uInt16List\": [33333, 44444],\n"
|
||||
" \"uInt32List\": [3333333333],\n"
|
||||
" \"uInt64List\": [\"11111111111111111111\"],\n"
|
||||
" \"float32List\": [5555.5, \"Infinity\", \"-Infinity\", \"NaN\"],\n"
|
||||
" \"float64List\": [7777.75, \"Infinity\", \"-Infinity\", \"NaN\"],\n"
|
||||
" \"textList\": [\"plugh\", \"xyzzy\", \"thud\"],\n"
|
||||
" \"dataList\": [[111, 111, 112, 115], [101, 120, 104, 97, 117, 115, 116, 101, 100], [114, 102, 99, 51, 48, 57, 50]],\n"
|
||||
" \"structList\": [\n"
|
||||
" {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"structlist 1\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
|
||||
" {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"structlist 2\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
|
||||
" {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"structlist 3\", \"enumField\": \"foo\", \"interfaceField\": null} ],\n"
|
||||
" \"enumList\": [\"foo\", \"garply\"] }";
|
||||
|
||||
KJ_TEST("encode all types") {
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<TestAllTypes>();
|
||||
initTestMessage(root);
|
||||
|
||||
JsonCodec json;
|
||||
json.setPrettyPrint(true);
|
||||
KJ_EXPECT(json.encode(root) == ALL_TYPES_JSON);
|
||||
|
||||
// Verify that if we strip out the non-string spaces, we get the non-pretty-print version.
|
||||
kj::Vector<char> chars;
|
||||
bool inQuotes = false;
|
||||
for (char c: ALL_TYPES_JSON) {
|
||||
if (c == '\"') inQuotes = !inQuotes;
|
||||
|
||||
if ((c == '\n' || c == ' ') && !inQuotes) {
|
||||
// skip space
|
||||
} else {
|
||||
chars.add(c);
|
||||
}
|
||||
}
|
||||
kj::String nospaces(chars.releaseAsArray());
|
||||
|
||||
json.setPrettyPrint(false);
|
||||
KJ_EXPECT(json.encode(root) == nospaces);
|
||||
}
|
||||
|
||||
KJ_TEST("encode union") {
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<test::TestUnnamedUnion>();
|
||||
|
||||
root.setBefore("a");
|
||||
root.setMiddle(44);
|
||||
root.setAfter("c");
|
||||
|
||||
JsonCodec json;
|
||||
|
||||
root.setFoo(123);
|
||||
KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"foo\":123,\"middle\":44,\"after\":\"c\"}");
|
||||
|
||||
root.setBar(321);
|
||||
KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"middle\":44,\"bar\":321,\"after\":\"c\"}");
|
||||
}
|
||||
|
||||
KJ_TEST("decode all types") {
|
||||
JsonCodec json;
|
||||
json.setHasMode(HasMode::NON_DEFAULT);
|
||||
|
||||
#define CASE_MAYBE_ROUNDTRIP(s, f, roundtrip) \
|
||||
{ \
|
||||
MallocMessageBuilder message; \
|
||||
auto root = message.initRoot<TestAllTypes>(); \
|
||||
kj::StringPtr input = s; \
|
||||
json.decode(input, root); \
|
||||
KJ_EXPECT((f), input, root); \
|
||||
auto reencoded = json.encode(root); \
|
||||
KJ_EXPECT(roundtrip == (input == reencoded), roundtrip, input, reencoded); \
|
||||
}
|
||||
#define CASE_NO_ROUNDTRIP(s, f) CASE_MAYBE_ROUNDTRIP(s, f, false)
|
||||
#define CASE(s, f) CASE_MAYBE_ROUNDTRIP(s, f, true)
|
||||
#define CASE_THROW(s, errorMessage) \
|
||||
{ \
|
||||
MallocMessageBuilder message; \
|
||||
auto root = message.initRoot<TestAllTypes>(); \
|
||||
KJ_EXPECT_THROW_MESSAGE(errorMessage, json.decode(s, root)); \
|
||||
}
|
||||
#define CASE_THROW_RECOVERABLE(s, errorMessage) \
|
||||
{ \
|
||||
MallocMessageBuilder message; \
|
||||
auto root = message.initRoot<TestAllTypes>(); \
|
||||
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE(errorMessage, json.decode(s, root)); \
|
||||
}
|
||||
|
||||
CASE(R"({})", root.getBoolField() == false);
|
||||
CASE_NO_ROUNDTRIP(R"({"unknownField":7})", root.getBoolField() == false);
|
||||
CASE(R"({"boolField":true})", root.getBoolField() == true);
|
||||
CASE(R"({"int8Field":-128})", root.getInt8Field() == -128);
|
||||
CASE_NO_ROUNDTRIP(R"({"int8Field":"127"})", root.getInt8Field() == 127);
|
||||
CASE_THROW_RECOVERABLE(R"({"int8Field":"-129"})", "Value out-of-range");
|
||||
CASE_THROW_RECOVERABLE(R"({"int8Field":128})", "Value out-of-range");
|
||||
CASE(R"({"int16Field":-32768})", root.getInt16Field() == -32768);
|
||||
CASE_NO_ROUNDTRIP(R"({"int16Field":"32767"})", root.getInt16Field() == 32767);
|
||||
CASE_THROW_RECOVERABLE(R"({"int16Field":"-32769"})", "Value out-of-range");
|
||||
CASE_THROW_RECOVERABLE(R"({"int16Field":32768})", "Value out-of-range");
|
||||
CASE(R"({"int32Field":-2147483648})", root.getInt32Field() == -2147483648);
|
||||
CASE_NO_ROUNDTRIP(R"({"int32Field":"2147483647"})", root.getInt32Field() == 2147483647);
|
||||
CASE_NO_ROUNDTRIP(R"({"int64Field":-9007199254740992})", root.getInt64Field() == -9007199254740992LL);
|
||||
CASE_NO_ROUNDTRIP(R"({"int64Field":9007199254740991})", root.getInt64Field() == 9007199254740991LL);
|
||||
CASE(R"({"int64Field":"-9223372036854775808"})", root.getInt64Field() == -9223372036854775808ULL);
|
||||
CASE(R"({"int64Field":"9223372036854775807"})", root.getInt64Field() == 9223372036854775807LL);
|
||||
CASE_THROW_RECOVERABLE(R"({"int64Field":"-9223372036854775809"})", "Value out-of-range");
|
||||
CASE_THROW_RECOVERABLE(R"({"int64Field":"9223372036854775808"})", "Value out-of-range");
|
||||
CASE(R"({"uInt8Field":255})", root.getUInt8Field() == 255);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt8Field":"0"})", root.getUInt8Field() == 0);
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt8Field":"256"})", "Value out-of-range");
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt8Field":-1})", "Value out-of-range");
|
||||
CASE(R"({"uInt16Field":65535})", root.getUInt16Field() == 65535);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt16Field":"0"})", root.getUInt16Field() == 0);
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt16Field":"655356"})", "Value out-of-range");
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt16Field":-1})", "Value out-of-range");
|
||||
CASE(R"({"uInt32Field":4294967295})", root.getUInt32Field() == 4294967295);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt32Field":"0"})", root.getUInt32Field() == 0);
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt32Field":"42949672956"})", "Value out-of-range");
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt32Field":-1})", "Value out-of-range");
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt64Field":9007199254740991})", root.getUInt64Field() == 9007199254740991ULL);
|
||||
CASE(R"({"uInt64Field":"18446744073709551615"})", root.getUInt64Field() == 18446744073709551615ULL);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt64Field":"0"})", root.getUInt64Field() == 0);
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt64Field":"18446744073709551616"})", "Value out-of-range");
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":0})", root.getFloat32Field() == 0);
|
||||
CASE(R"({"float32Field":4.5})", root.getFloat32Field() == 4.5);
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":null})", kj::isNaN(root.getFloat32Field()));
|
||||
CASE(R"({"float32Field":"NaN"})", kj::isNaN(root.getFloat32Field()));
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":"nan"})", kj::isNaN(root.getFloat32Field()));
|
||||
CASE(R"({"float32Field":"Infinity"})", root.getFloat32Field() == kj::inf());
|
||||
CASE(R"({"float32Field":"-Infinity"})", root.getFloat32Field() == -kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":"infinity"})", root.getFloat32Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":"-infinity"})", root.getFloat32Field() == -kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":"INF"})", root.getFloat32Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":"-INF"})", root.getFloat32Field() == -kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":1e39})", root.getFloat32Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float32Field":-1e39})", root.getFloat32Field() == -kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":0})", root.getFloat64Field() == 0);
|
||||
CASE(R"({"float64Field":4.5})", root.getFloat64Field() == 4.5);
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":null})", kj::isNaN(root.getFloat64Field()));
|
||||
CASE(R"({"float64Field":"NaN"})", kj::isNaN(root.getFloat64Field()));
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":"nan"})", kj::isNaN(root.getFloat64Field()));
|
||||
CASE(R"({"float64Field":"Infinity"})", root.getFloat64Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":"infinity"})", root.getFloat64Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":"-infinity"})", root.getFloat64Field() == -kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":"INF"})", root.getFloat64Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":"-INF"})", root.getFloat64Field() == -kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":1e309})", root.getFloat64Field() == kj::inf());
|
||||
CASE_NO_ROUNDTRIP(R"({"float64Field":-1e309})", root.getFloat64Field() == -kj::inf());
|
||||
CASE(R"({"textField":"hello"})", kj::str("hello") == root.getTextField());
|
||||
CASE(R"({"dataField":[7,0,122]})",
|
||||
kj::heapArray<byte>({7,0,122}).asPtr() == root.getDataField());
|
||||
CASE(R"({"structField":{}})", root.hasStructField() == true);
|
||||
CASE(R"({"structField":{}})", root.getStructField().getBoolField() == false);
|
||||
CASE_NO_ROUNDTRIP(R"({"structField":{"boolField":false}})", root.getStructField().getBoolField() == false);
|
||||
CASE(R"({"structField":{"boolField":true}})", root.getStructField().getBoolField() == true);
|
||||
CASE(R"({"enumField":"bar"})", root.getEnumField() == TestEnum::BAR);
|
||||
|
||||
CASE_NO_ROUNDTRIP(R"({"textField":"foo\u1234bar"})",
|
||||
kj::str(u8"foo\u1234bar") == root.getTextField());
|
||||
|
||||
CASE_THROW_RECOVERABLE(R"({"structField":null})", "Expected object value");
|
||||
CASE_THROW_RECOVERABLE(R"({"structList":null})", "Expected list value");
|
||||
CASE_THROW_RECOVERABLE(R"({"boolList":null})", "Expected list value");
|
||||
CASE_THROW_RECOVERABLE(R"({"structList":[null]})", "Expected object value");
|
||||
CASE_THROW_RECOVERABLE(R"({"int64Field":"177a"})", "String does not contain valid");
|
||||
CASE_THROW_RECOVERABLE(R"({"uInt64Field":"177a"})", "String does not contain valid");
|
||||
CASE_THROW_RECOVERABLE(R"({"float64Field":"177a"})", "String does not contain valid");
|
||||
|
||||
CASE(R"({})", root.hasBoolList() == false);
|
||||
CASE(R"({"boolList":[]})", root.hasBoolList() == true);
|
||||
CASE(R"({"boolList":[]})", root.getBoolList().size() == 0);
|
||||
CASE(R"({"boolList":[false]})", root.getBoolList().size() == 1);
|
||||
CASE(R"({"boolList":[false]})", root.getBoolList()[0] == false);
|
||||
CASE(R"({"boolList":[true]})", root.getBoolList()[0] == true);
|
||||
CASE(R"({"int8List":[7]})", root.getInt8List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"int8List":["7"]})", root.getInt8List()[0] == 7);
|
||||
CASE(R"({"int16List":[7]})", root.getInt16List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"int16List":["7"]})", root.getInt16List()[0] == 7);
|
||||
CASE(R"({"int32List":[7]})", root.getInt32List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"int32List":["7"]})", root.getInt32List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"int64List":[7]})", root.getInt64List()[0] == 7);
|
||||
CASE(R"({"int64List":["7"]})", root.getInt64List()[0] == 7);
|
||||
CASE(R"({"uInt8List":[7]})", root.getUInt8List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt8List":["7"]})", root.getUInt8List()[0] == 7);
|
||||
CASE(R"({"uInt16List":[7]})", root.getUInt16List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt16List":["7"]})", root.getUInt16List()[0] == 7);
|
||||
CASE(R"({"uInt32List":[7]})", root.getUInt32List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt32List":["7"]})", root.getUInt32List()[0] == 7);
|
||||
CASE_NO_ROUNDTRIP(R"({"uInt64List":[7]})", root.getUInt64List()[0] == 7);
|
||||
CASE(R"({"uInt64List":["7"]})", root.getUInt64List()[0] == 7);
|
||||
CASE(R"({"float32List":[4.5]})", root.getFloat32List()[0] == 4.5);
|
||||
CASE_NO_ROUNDTRIP(R"({"float32List":["4.5"]})", root.getFloat32List()[0] == 4.5);
|
||||
CASE_NO_ROUNDTRIP(R"({"float32List":[null]})", kj::isNaN(root.getFloat32List()[0]));
|
||||
CASE(R"({"float32List":["NaN"]})", kj::isNaN(root.getFloat32List()[0]));
|
||||
CASE(R"({"float32List":["Infinity"]})", root.getFloat32List()[0] == kj::inf());
|
||||
CASE(R"({"float32List":["-Infinity"]})", root.getFloat32List()[0] == -kj::inf());
|
||||
CASE(R"({"float64List":[4.5]})", root.getFloat64List()[0] == 4.5);
|
||||
CASE_NO_ROUNDTRIP(R"({"float64List":["4.5"]})", root.getFloat64List()[0] == 4.5);
|
||||
CASE_NO_ROUNDTRIP(R"({"float64List":[null]})", kj::isNaN(root.getFloat64List()[0]));
|
||||
CASE(R"({"float64List":["NaN"]})", kj::isNaN(root.getFloat64List()[0]));
|
||||
CASE(R"({"float64List":["Infinity"]})", root.getFloat64List()[0] == kj::inf());
|
||||
CASE(R"({"float64List":["-Infinity"]})", root.getFloat64List()[0] == -kj::inf());
|
||||
CASE(R"({"textList":["hello"]})", kj::str("hello") == root.getTextList()[0]);
|
||||
CASE(R"({"dataList":[[7,0,122]]})",
|
||||
kj::heapArray<byte>({7,0,122}).asPtr() == root.getDataList()[0]);
|
||||
CASE(R"({"structList":[{}]})", root.hasStructList() == true);
|
||||
CASE(R"({"structList":[{}]})", root.getStructList()[0].getBoolField() == false);
|
||||
CASE_NO_ROUNDTRIP(R"({"structList":[{"boolField":false}]})", root.getStructList()[0].getBoolField() == false);
|
||||
CASE(R"({"structList":[{"boolField":true}]})", root.getStructList()[0].getBoolField() == true);
|
||||
CASE(R"({"enumList":["bar"]})", root.getEnumList()[0] == TestEnum::BAR);
|
||||
#undef CASE_MAYBE_ROUNDTRIP
|
||||
#undef CASE_NO_ROUNDTRIP
|
||||
#undef CASE
|
||||
#undef CASE_THROW
|
||||
#undef CASE_THROW_RECOVERABLE
|
||||
}
|
||||
|
||||
KJ_TEST("decode test message") {
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<TestAllTypes>();
|
||||
initTestMessage(root);
|
||||
|
||||
JsonCodec json;
|
||||
auto encoded = json.encode(root);
|
||||
|
||||
MallocMessageBuilder decodedMessage;
|
||||
auto decodedRoot = decodedMessage.initRoot<TestAllTypes>();
|
||||
json.decode(encoded, decodedRoot);
|
||||
|
||||
KJ_EXPECT(root.toString().flatten() == decodedRoot.toString().flatten());
|
||||
}
|
||||
|
||||
KJ_TEST("basic json decoding") {
|
||||
// TODO(cleanup): this test is a mess!
|
||||
JsonCodec json;
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
json.decodeRaw("null", root);
|
||||
|
||||
KJ_EXPECT(root.which() == JsonValue::NULL_);
|
||||
KJ_EXPECT(root.getNull() == VOID);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("false", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::BOOLEAN);
|
||||
KJ_EXPECT(root.getBoolean() == false);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("true", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::BOOLEAN);
|
||||
KJ_EXPECT(root.getBoolean() == true);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("\"foo\"", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::STRING);
|
||||
KJ_EXPECT(kj::str("foo") == root.getString());
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(R"("\"")", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::STRING);
|
||||
KJ_EXPECT(kj::str("\"") == root.getString());
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(R"("\\abc\"d\\e")", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::STRING);
|
||||
KJ_EXPECT(kj::str("\\abc\"d\\e") == root.getString());
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(R"("\"\\\/\b\f\n\r\t\u0003abc\u0064\u0065f")", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::STRING);
|
||||
KJ_EXPECT(kj::str("\"\\/\b\f\n\r\t\x03""abcdef") == root.getString(), root.getString());
|
||||
}
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("[]", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::ARRAY, (uint)root.which());
|
||||
KJ_EXPECT(root.getArray().size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("[true]", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::ARRAY);
|
||||
auto array = root.getArray();
|
||||
KJ_EXPECT(array.size() == 1, array.size());
|
||||
KJ_EXPECT(root.getArray()[0].which() == JsonValue::BOOLEAN);
|
||||
KJ_EXPECT(root.getArray()[0].getBoolean() == true);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(" [ true , false\t\n , null]", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::ARRAY);
|
||||
auto array = root.getArray();
|
||||
KJ_EXPECT(array.size() == 3);
|
||||
KJ_EXPECT(array[0].which() == JsonValue::BOOLEAN);
|
||||
KJ_EXPECT(array[0].getBoolean() == true);
|
||||
KJ_EXPECT(array[1].which() == JsonValue::BOOLEAN);
|
||||
KJ_EXPECT(array[1].getBoolean() == false);
|
||||
KJ_EXPECT(array[2].which() == JsonValue::NULL_);
|
||||
KJ_EXPECT(array[2].getNull() == VOID);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("{}", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
|
||||
KJ_EXPECT(root.getObject().size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("\r\n\t {\r\n\t }\r\n\t ", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
|
||||
KJ_EXPECT(root.getObject().size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(R"({"some": null})", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
|
||||
auto object = root.getObject();
|
||||
KJ_EXPECT(object.size() == 1);
|
||||
KJ_EXPECT(kj::str("some") == object[0].getName());
|
||||
KJ_EXPECT(object[0].getValue().which() == JsonValue::NULL_);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(R"({"foo\n\tbaz": "a val", "bar": ["a", -5.5e11, { "z": {}}]})", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
|
||||
auto object = root.getObject();
|
||||
KJ_EXPECT(object.size() == 2);
|
||||
KJ_EXPECT(kj::str("foo\n\tbaz") == object[0].getName());
|
||||
KJ_EXPECT(object[0].getValue().which() == JsonValue::STRING);
|
||||
KJ_EXPECT(kj::str("a val") == object[0].getValue().getString());
|
||||
|
||||
KJ_EXPECT(kj::str("bar") == object[1].getName());
|
||||
KJ_EXPECT(object[1].getValue().which() == JsonValue::ARRAY);
|
||||
auto array = object[1].getValue().getArray();
|
||||
KJ_EXPECT(array.size() == 3, array.size());
|
||||
KJ_EXPECT(array[0].which() == JsonValue::STRING);
|
||||
KJ_EXPECT(kj::str("a") == array[0].getString());
|
||||
KJ_EXPECT(array[1].which() == JsonValue::NUMBER);
|
||||
KJ_EXPECT(array[1].getNumber() == -5.5e11);
|
||||
KJ_EXPECT(array[2].which() == JsonValue::OBJECT);
|
||||
KJ_EXPECT(array[2].getObject().size() == 1);
|
||||
KJ_EXPECT(array[2].getObject()[0].getValue().which() == JsonValue::OBJECT);
|
||||
KJ_EXPECT(array[2].getObject()[0].getValue().getObject().size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("123", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::NUMBER);
|
||||
KJ_EXPECT(root.getNumber() == 123);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
KJ_EXPECT_THROW_MESSAGE("input", json.decodeRaw("z", root));
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
// Leading + not allowed in numbers.
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("+123", root));
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("[00]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("[01]", root));
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("-", root));
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("-5", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::NUMBER);
|
||||
KJ_EXPECT(root.getNumber() == -5);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw("-5.5", root);
|
||||
KJ_EXPECT(root.which() == JsonValue::NUMBER);
|
||||
KJ_EXPECT(root.getNumber() == -5.5);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("a", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("[", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("{", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("\"\\u\"", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[}", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[}]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1, , ]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[,]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[true,]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[, 1]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1\"\"]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1,, \"\"]", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{\"a\"1: 0}", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw(R"({"some": null,})", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Input remains", json.decodeRaw("11a", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Invalid escape", json.decodeRaw(R"("\z")", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Invalid escape", json.decodeRaw(R"("\z")", root));
|
||||
{
|
||||
// TODO(msvc): This raw string literal currently confuses MSVC's preprocessor, so I hoisted
|
||||
// it outside the macro.
|
||||
auto f = [&] { json.decodeRaw(R"(["\n\", 3])", root); };
|
||||
KJ_EXPECT_THROW_MESSAGE("ends prematurely", f());
|
||||
}
|
||||
KJ_EXPECT_THROW_MESSAGE("Invalid hex", json.decodeRaw(R"("\u12zz")", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("-", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("--", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("\f{}", root));
|
||||
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{\v}", root));
|
||||
}
|
||||
}
|
||||
|
||||
KJ_TEST("maximum nesting depth") {
|
||||
JsonCodec json;
|
||||
auto input = kj::str(R"({"foo": "a", "bar": ["b", { "baz": [-5.5e11] }, [ [ 1 ], { "z": 2 }]]})");
|
||||
// `input` has a maximum nesting depth of 4, reached 3 times.
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(input, root);
|
||||
}
|
||||
|
||||
{
|
||||
json.setMaxNestingDepth(0);
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
KJ_EXPECT_THROW_MESSAGE("nest",
|
||||
json.decodeRaw(input, root));
|
||||
}
|
||||
|
||||
{
|
||||
json.setMaxNestingDepth(3);
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
KJ_EXPECT_THROW_MESSAGE("nest",
|
||||
json.decodeRaw(input, root));
|
||||
}
|
||||
|
||||
{
|
||||
json.setMaxNestingDepth(4);
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.initRoot<JsonValue>();
|
||||
|
||||
json.decodeRaw(input, root);
|
||||
}
|
||||
}
|
||||
|
||||
class TestCallHandler: public JsonCodec::Handler<Text> {
|
||||
public:
|
||||
void encode(const JsonCodec& codec, Text::Reader input,
|
||||
JsonValue::Builder output) const override {
|
||||
auto call = output.initCall();
|
||||
call.setFunction("Frob");
|
||||
auto params = call.initParams(2);
|
||||
params[0].setNumber(123);
|
||||
params[1].setString(input);
|
||||
}
|
||||
|
||||
Orphan<Text> decode(const JsonCodec& codec, JsonValue::Reader input,
|
||||
Orphanage orphanage) const override {
|
||||
KJ_UNIMPLEMENTED("TestHandler::decode");
|
||||
}
|
||||
};
|
||||
|
||||
class TestDynamicStructHandler: public JsonCodec::Handler<DynamicStruct> {
|
||||
public:
|
||||
void encode(const JsonCodec& codec, DynamicStruct::Reader input,
|
||||
JsonValue::Builder output) const override {
|
||||
auto fields = input.getSchema().getFields();
|
||||
auto items = output.initArray(fields.size());
|
||||
for (auto field: fields) {
|
||||
KJ_REQUIRE(field.getIndex() < items.size());
|
||||
auto item = items[field.getIndex()];
|
||||
if (input.has(field)) {
|
||||
codec.encode(input.get(field), field.getType(), item);
|
||||
} else {
|
||||
item.setNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode(const JsonCodec& codec, JsonValue::Reader input,
|
||||
DynamicStruct::Builder output) const override {
|
||||
auto orphanage = Orphanage::getForMessageContaining(output);
|
||||
auto fields = output.getSchema().getFields();
|
||||
auto items = input.getArray();
|
||||
for (auto field: fields) {
|
||||
KJ_REQUIRE(field.getIndex() < items.size());
|
||||
auto item = items[field.getIndex()];
|
||||
if (!item.isNull()) {
|
||||
output.adopt(field, codec.decode(item, field.getType(), orphanage));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TestStructHandler: public JsonCodec::Handler<test::TestOldVersion> {
|
||||
public:
|
||||
void encode(const JsonCodec& codec, test::TestOldVersion::Reader input, JsonValue::Builder output) const override {
|
||||
dynamicHandler.encode(codec, input, output);
|
||||
}
|
||||
|
||||
void decode(const JsonCodec& codec, JsonValue::Reader input, test::TestOldVersion::Builder output) const override {
|
||||
dynamicHandler.decode(codec, input, output);
|
||||
}
|
||||
|
||||
private:
|
||||
TestDynamicStructHandler dynamicHandler;
|
||||
};
|
||||
|
||||
KJ_TEST("register custom encoding handlers") {
|
||||
JsonCodec json;
|
||||
|
||||
TestStructHandler structHandler;
|
||||
json.addTypeHandler(structHandler);
|
||||
|
||||
// JSON decoder can't parse calls back, so test only encoder here
|
||||
TestCallHandler callHandler;
|
||||
json.addTypeHandler(callHandler);
|
||||
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<test::TestOldVersion>();
|
||||
root.setOld1(123);
|
||||
root.setOld2("foo");
|
||||
|
||||
KJ_EXPECT(json.encode(root) == "[\"123\",Frob(123,\"foo\"),null]");
|
||||
}
|
||||
|
||||
KJ_TEST("register custom roundtrip handler") {
|
||||
for (auto i = 1; i <= 2; i++) {
|
||||
JsonCodec json;
|
||||
TestStructHandler staticHandler;
|
||||
TestDynamicStructHandler dynamicHandler;
|
||||
kj::String encoded;
|
||||
|
||||
if (i == 1) {
|
||||
// first iteration: test with explicit struct handler
|
||||
json.addTypeHandler(staticHandler);
|
||||
} else {
|
||||
// second iteration: same checks, but with DynamicStruct handler
|
||||
json.addTypeHandler(StructSchema::from<test::TestOldVersion>(), dynamicHandler);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<test::TestOldVersion>();
|
||||
root.setOld1(123);
|
||||
root.initOld3().setOld2("foo");
|
||||
|
||||
encoded = json.encode(root);
|
||||
|
||||
KJ_EXPECT(encoded == "[\"123\",null,[\"0\",\"foo\",null]]");
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<test::TestOldVersion>();
|
||||
json.decode(encoded, root);
|
||||
|
||||
KJ_EXPECT(root.getOld1() == 123);
|
||||
KJ_EXPECT(!root.hasOld2());
|
||||
auto nested = root.getOld3();
|
||||
KJ_EXPECT(nested.getOld1() == 0);
|
||||
KJ_EXPECT("foo" == nested.getOld2());
|
||||
KJ_EXPECT(!nested.hasOld3());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KJ_TEST("register field handler") {
|
||||
TestStructHandler handler;
|
||||
JsonCodec json;
|
||||
json.addFieldHandler(StructSchema::from<test::TestOldVersion>().getFieldByName("old3"),
|
||||
handler);
|
||||
|
||||
kj::String encoded;
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<test::TestOldVersion>();
|
||||
root.setOld1(123);
|
||||
root.setOld2("foo");
|
||||
auto nested = root.initOld3();
|
||||
nested.setOld2("bar");
|
||||
|
||||
encoded = json.encode(root);
|
||||
|
||||
KJ_EXPECT(encoded == "{\"old1\":\"123\",\"old2\":\"foo\",\"old3\":[\"0\",\"bar\",null]}")
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<test::TestOldVersion>();
|
||||
json.decode(encoded, root);
|
||||
|
||||
KJ_EXPECT(root.getOld1() == 123);
|
||||
KJ_EXPECT("foo" == root.getOld2());
|
||||
auto nested = root.getOld3();
|
||||
KJ_EXPECT(nested.getOld1() == 0);
|
||||
KJ_EXPECT("bar" == nested.getOld2());
|
||||
KJ_EXPECT(!nested.hasOld3());
|
||||
}
|
||||
}
|
||||
|
||||
class TestCapabilityHandler: public JsonCodec::Handler<test::TestInterface> {
|
||||
public:
|
||||
void encode(const JsonCodec& codec, test::TestInterface::Client input,
|
||||
JsonValue::Builder output) const override {
|
||||
KJ_UNIMPLEMENTED("TestCapabilityHandler::encode");
|
||||
}
|
||||
|
||||
test::TestInterface::Client decode(
|
||||
const JsonCodec& codec, JsonValue::Reader input) const override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
KJ_TEST("register capability handler") {
|
||||
// This test currently only checks that this compiles, which at one point wasn't the caes.
|
||||
// TODO(test): Actually run some code here.
|
||||
|
||||
TestCapabilityHandler handler;
|
||||
JsonCodec json;
|
||||
json.addTypeHandler(handler);
|
||||
}
|
||||
|
||||
static constexpr kj::StringPtr GOLDEN_ANNOTATED =
|
||||
R"({ "names-can_contain!anything Really": "foo",
|
||||
"flatFoo": 123,
|
||||
"flatBar": "abc",
|
||||
"renamed-flatBaz": {"hello": true},
|
||||
"flatQux": "cba",
|
||||
"pfx.foo": "this is a long string in order to force multi-line pretty printing",
|
||||
"pfx.renamed-bar": 321,
|
||||
"pfx.baz": {"hello": true},
|
||||
"pfx.xfp.qux": "fed",
|
||||
"union-type": "renamed-bar",
|
||||
"barMember": 789,
|
||||
"multiMember": "ghi",
|
||||
"dependency": {"renamed-foo": "corge"},
|
||||
"simpleGroup": {"renamed-grault": "garply"},
|
||||
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
|
||||
"innerJson": [123, "hello", {"object": true}],
|
||||
"customFieldHandler": "add-prefix-waldo",
|
||||
"testBase64": "ZnJlZA==",
|
||||
"testHex": "706c756768",
|
||||
"bUnion": "renamed-bar",
|
||||
"bValue": 678,
|
||||
"externalUnion": {"type": "bar", "value": "cba"},
|
||||
"unionWithVoid": {"type": "voidValue"} })"_kj;
|
||||
|
||||
static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE =
|
||||
R"({
|
||||
"unionWithVoid": {"type": "voidValue"},
|
||||
"externalUnion": {"type": "bar", "value": "cba"},
|
||||
"bValue": 678,
|
||||
"bUnion": "renamed-bar",
|
||||
"testHex": "706c756768",
|
||||
"testBase64": "ZnJlZA==",
|
||||
"customFieldHandler": "add-prefix-waldo",
|
||||
"innerJson": [123, "hello", {"object": true}],
|
||||
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
|
||||
"simpleGroup": { "renamed-grault": "garply" },
|
||||
"dependency": { "renamed-foo": "corge" },
|
||||
"multiMember": "ghi",
|
||||
"barMember": 789,
|
||||
"union-type": "renamed-bar",
|
||||
"pfx.xfp.qux": "fed",
|
||||
"pfx.baz": {"hello": true},
|
||||
"pfx.renamed-bar": 321,
|
||||
"pfx.foo": "this is a long string in order to force multi-line pretty printing",
|
||||
"flatQux": "cba",
|
||||
"renamed-flatBaz": {"hello": true},
|
||||
"flatBar": "abc",
|
||||
"flatFoo": 123,
|
||||
"names-can_contain!anything Really": "foo"
|
||||
})"_kj;
|
||||
|
||||
class PrefixAdder: public JsonCodec::Handler<capnp::Text> {
|
||||
public:
|
||||
void encode(const JsonCodec& codec, capnp::Text::Reader input, JsonValue::Builder output) const {
|
||||
output.setString(kj::str("add-prefix-", input));
|
||||
}
|
||||
|
||||
Orphan<capnp::Text> decode(const JsonCodec& codec, JsonValue::Reader input,
|
||||
Orphanage orphanage) const {
|
||||
return orphanage.newOrphanCopy(capnp::Text::Reader(input.getString().slice(11)));
|
||||
}
|
||||
};
|
||||
|
||||
KJ_TEST("rename fields") {
|
||||
JsonCodec json;
|
||||
json.handleByAnnotation<TestJsonAnnotations>();
|
||||
json.setPrettyPrint(true);
|
||||
|
||||
PrefixAdder customHandler;
|
||||
json.addFieldHandler(Schema::from<TestJsonAnnotations>().getFieldByName("customFieldHandler"),
|
||||
customHandler);
|
||||
|
||||
kj::String goldenText;
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<TestJsonAnnotations>();
|
||||
root.setSomeField("foo");
|
||||
|
||||
auto aGroup = root.getAGroup();
|
||||
aGroup.setFlatFoo(123);
|
||||
aGroup.setFlatBar("abc");
|
||||
aGroup.getFlatBaz().setHello(true);
|
||||
aGroup.getDoubleFlat().setFlatQux("cba");
|
||||
|
||||
auto prefixedGroup = root.getPrefixedGroup();
|
||||
prefixedGroup.setFoo("this is a long string in order to force multi-line pretty printing");
|
||||
prefixedGroup.setBar(321);
|
||||
prefixedGroup.getBaz().setHello(true);
|
||||
prefixedGroup.getMorePrefix().setQux("fed");
|
||||
|
||||
auto unionBar = root.getAUnion().initBar();
|
||||
unionBar.setBarMember(789);
|
||||
unionBar.setMultiMember("ghi");
|
||||
|
||||
root.initDependency().setFoo("corge");
|
||||
root.initSimpleGroup().setGrault("garply");
|
||||
|
||||
root.setEnums({
|
||||
TestJsonAnnotatedEnum::QUX,
|
||||
TestJsonAnnotatedEnum::BAR,
|
||||
TestJsonAnnotatedEnum::FOO,
|
||||
TestJsonAnnotatedEnum::BAZ
|
||||
});
|
||||
|
||||
auto val = root.initInnerJson();
|
||||
auto arr = val.initArray(3);
|
||||
arr[0].setNumber(123);
|
||||
arr[1].setString("hello");
|
||||
auto field = arr[2].initObject(1)[0];
|
||||
field.setName("object");
|
||||
field.initValue().setBoolean(true);
|
||||
|
||||
root.setCustomFieldHandler("waldo");
|
||||
|
||||
root.setTestBase64("fred"_kj.asBytes());
|
||||
root.setTestHex("plugh"_kj.asBytes());
|
||||
|
||||
root.getBUnion().setBar(678);
|
||||
|
||||
root.initExternalUnion().initBar().setValue("cba");
|
||||
|
||||
root.initUnionWithVoid().setVoidValue();
|
||||
|
||||
auto encoded = json.encode(root.asReader());
|
||||
KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded);
|
||||
|
||||
goldenText = kj::str(root);
|
||||
}
|
||||
|
||||
{
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<TestJsonAnnotations>();
|
||||
json.decode(GOLDEN_ANNOTATED, root);
|
||||
|
||||
KJ_EXPECT(kj::str(root) == goldenText, root, goldenText);
|
||||
}
|
||||
|
||||
{
|
||||
// Try parsing in reverse, mostly to test that union tags can come after content.
|
||||
MallocMessageBuilder message;
|
||||
auto root = message.getRoot<TestJsonAnnotations>();
|
||||
json.decode(GOLDEN_ANNOTATED_REVERSE, root);
|
||||
|
||||
KJ_EXPECT(kj::str(root) == goldenText, root, goldenText);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace _ (private)
|
||||
} // namespace capnp
|
|
@ -0,0 +1,116 @@
|
|||
# Copyright (c) 2018 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.
|
||||
|
||||
@0xc9d405cf4333e4c9;
|
||||
|
||||
using Json = import "/capnp/compat/json.capnp";
|
||||
|
||||
$import "/capnp/c++.capnp".namespace("capnp");
|
||||
|
||||
struct TestJsonAnnotations {
|
||||
someField @0 :Text $Json.name("names-can_contain!anything Really");
|
||||
|
||||
aGroup :group $Json.flatten() {
|
||||
flatFoo @1 :UInt32;
|
||||
flatBar @2 :Text;
|
||||
flatBaz :group $Json.name("renamed-flatBaz") {
|
||||
hello @3 :Bool;
|
||||
}
|
||||
doubleFlat :group $Json.flatten() {
|
||||
flatQux @4 :Text;
|
||||
}
|
||||
}
|
||||
|
||||
prefixedGroup :group $Json.flatten(prefix = "pfx.") {
|
||||
foo @5 :Text;
|
||||
bar @6 :UInt32 $Json.name("renamed-bar");
|
||||
baz :group {
|
||||
hello @7 :Bool;
|
||||
}
|
||||
morePrefix :group $Json.flatten(prefix = "xfp.") {
|
||||
qux @8 :Text;
|
||||
}
|
||||
}
|
||||
|
||||
aUnion :union $Json.flatten() $Json.discriminator(name = "union-type") {
|
||||
foo :group $Json.flatten() {
|
||||
fooMember @9 :Text;
|
||||
multiMember @10 :UInt32;
|
||||
}
|
||||
bar :group $Json.flatten() $Json.name("renamed-bar") {
|
||||
barMember @11 :UInt32;
|
||||
multiMember @12 :Text;
|
||||
}
|
||||
}
|
||||
|
||||
dependency @13 :TestJsonAnnotations2;
|
||||
# To test that dependencies are loaded even if not flattened.
|
||||
|
||||
simpleGroup :group {
|
||||
# To test that group types are loaded even if not flattened.
|
||||
grault @14 :Text $Json.name("renamed-grault");
|
||||
}
|
||||
|
||||
enums @15 :List(TestJsonAnnotatedEnum);
|
||||
|
||||
innerJson @16 :Json.Value;
|
||||
|
||||
customFieldHandler @17 :Text;
|
||||
|
||||
testBase64 @18 :Data $Json.base64;
|
||||
testHex @19 :Data $Json.hex;
|
||||
|
||||
bUnion :union $Json.flatten() $Json.discriminator(valueName = "bValue") {
|
||||
foo @20 :Text;
|
||||
bar @21 :UInt32 $Json.name("renamed-bar");
|
||||
}
|
||||
|
||||
externalUnion @22 :TestJsonAnnotations3;
|
||||
|
||||
unionWithVoid :union $Json.discriminator(name = "type") {
|
||||
intValue @23 :UInt32;
|
||||
voidValue @24 :Void;
|
||||
textValue @25 :Text;
|
||||
}
|
||||
}
|
||||
|
||||
struct TestJsonAnnotations2 {
|
||||
foo @0 :Text $Json.name("renamed-foo");
|
||||
cycle @1 :TestJsonAnnotations;
|
||||
}
|
||||
|
||||
struct TestJsonAnnotations3 $Json.discriminator(name = "type") {
|
||||
union {
|
||||
foo @0 :UInt32;
|
||||
bar @1 :TestFlattenedStruct $Json.flatten();
|
||||
}
|
||||
}
|
||||
|
||||
struct TestFlattenedStruct {
|
||||
value @0 :Text;
|
||||
}
|
||||
|
||||
enum TestJsonAnnotatedEnum {
|
||||
foo @0;
|
||||
bar @1 $Json.name("renamed-bar");
|
||||
baz @2 $Json.name("renamed-baz");
|
||||
qux @3;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,112 @@
|
|||
# Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0x8ef99297a43a5e34;
|
||||
|
||||
$import "/capnp/c++.capnp".namespace("capnp::json");
|
||||
|
||||
struct Value {
|
||||
union {
|
||||
null @0 :Void;
|
||||
boolean @1 :Bool;
|
||||
number @2 :Float64;
|
||||
string @3 :Text;
|
||||
array @4 :List(Value);
|
||||
object @5 :List(Field);
|
||||
# Standard JSON values.
|
||||
|
||||
call @6 :Call;
|
||||
# Non-standard: A "function call", applying a named function (named by a single identifier)
|
||||
# to a parameter list. Examples:
|
||||
#
|
||||
# BinData(0, "Zm9vCg==")
|
||||
# ISODate("2015-04-15T08:44:50.218Z")
|
||||
#
|
||||
# Mongo DB users will recognize the above as exactly the syntax Mongo uses to represent BSON
|
||||
# "binary" and "date" types in text, since JSON has no analog of these. This is basically the
|
||||
# reason this extension exists. We do NOT recommend using `call` unless you specifically need
|
||||
# to be compatible with some silly format that uses this syntax.
|
||||
}
|
||||
|
||||
struct Field {
|
||||
name @0 :Text;
|
||||
value @1 :Value;
|
||||
}
|
||||
|
||||
struct Call {
|
||||
function @0 :Text;
|
||||
params @1 :List(Value);
|
||||
}
|
||||
}
|
||||
|
||||
# ========================================================================================
|
||||
# Annotations to control parsing. Typical usage:
|
||||
#
|
||||
# using Json = import "/capnp/compat/json.capnp";
|
||||
#
|
||||
# And then later on:
|
||||
#
|
||||
# myField @0 :Text $Json.name("my_field");
|
||||
|
||||
annotation name @0xfa5b1fd61c2e7c3d (field, enumerant, method, group, union): Text;
|
||||
# Define an alternative name to use when encoding the given item in JSON. This can be used, for
|
||||
# example, to use snake_case names where needed, even though Cap'n Proto uses strictly camelCase.
|
||||
#
|
||||
# (However, because JSON is derived from JavaScript, you *should* use camelCase names when
|
||||
# defining JSON-based APIs. But, when supporting a pre-existing API you may not have a choice.)
|
||||
|
||||
annotation flatten @0x82d3e852af0336bf (field, group, union): FlattenOptions;
|
||||
# Specifies that an aggregate field should be flattened into its parent.
|
||||
#
|
||||
# In order to flatten a member of a union, the union (or, for an anonymous union, the parent
|
||||
# struct type) must have the $jsonDiscriminator annotation.
|
||||
#
|
||||
# TODO(someday): Maybe support "flattening" a List(Value.Field) as a way to support unknown JSON
|
||||
# fields?
|
||||
|
||||
struct FlattenOptions {
|
||||
prefix @0 :Text = "";
|
||||
# Optional: Adds the given prefix to flattened field names.
|
||||
}
|
||||
|
||||
annotation discriminator @0xcfa794e8d19a0162 (struct, union): DiscriminatorOptions;
|
||||
# Specifies that a union's variant will be decided not by which fields are present, but instead
|
||||
# by a special discriminator field. The value of the discriminator field is a string naming which
|
||||
# variant is active. This allows the members of the union to have the $jsonFlatten annotation, or
|
||||
# to all have the same name.
|
||||
|
||||
struct DiscriminatorOptions {
|
||||
name @0 :Text;
|
||||
# The name of the discriminator field. Defaults to matching the name of the union.
|
||||
|
||||
valueName @1 :Text;
|
||||
# If non-null, specifies that the union's value shall have the given field name, rather than the
|
||||
# value's name. In this case the union's variant can only be determined by looking at the
|
||||
# discriminant field, not by inspecting which value field is present.
|
||||
#
|
||||
# It is an error to use `valueName` while also declaring some variants as $flatten.
|
||||
}
|
||||
|
||||
annotation base64 @0xd7d879450a253e4b (field): Void;
|
||||
# Place on a field of type `Data` to indicate that its JSON representation is a Base64 string.
|
||||
|
||||
annotation hex @0xf061e22f0ae5c7b5 (field): Void;
|
||||
# Place on a field of type `Data` to indicate that its JSON representation is a hex string.
|
|
@ -0,0 +1,602 @@
|
|||
// Generated by Cap'n Proto compiler, DO NOT EDIT
|
||||
// source: json.capnp
|
||||
|
||||
#include "json.capnp.h"
|
||||
|
||||
namespace capnp {
|
||||
namespace schemas {
|
||||
static const ::capnp::_::AlignedData<137> b_a3fa7845f919dd83 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
131, 221, 25, 249, 69, 120, 250, 163,
|
||||
24, 0, 0, 0, 1, 0, 2, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
1, 0, 7, 0, 0, 0, 7, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 242, 0, 0, 0,
|
||||
33, 0, 0, 0, 39, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
53, 0, 0, 0, 143, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
86, 97, 108, 117, 101, 0, 0, 0,
|
||||
8, 0, 0, 0, 1, 0, 1, 0,
|
||||
223, 157, 214, 53, 231, 38, 16, 227,
|
||||
9, 0, 0, 0, 50, 0, 0, 0,
|
||||
72, 61, 201, 161, 236, 246, 217, 160,
|
||||
5, 0, 0, 0, 42, 0, 0, 0,
|
||||
70, 105, 101, 108, 100, 0, 0, 0,
|
||||
67, 97, 108, 108, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
181, 0, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
176, 0, 0, 0, 3, 0, 1, 0,
|
||||
188, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 254, 255, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
185, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
180, 0, 0, 0, 3, 0, 1, 0,
|
||||
192, 0, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 253, 255, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
189, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
184, 0, 0, 0, 3, 0, 1, 0,
|
||||
196, 0, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 252, 255, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
193, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 0, 0, 0, 3, 0, 1, 0,
|
||||
200, 0, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 251, 255, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
197, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
192, 0, 0, 0, 3, 0, 1, 0,
|
||||
220, 0, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 250, 255, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
217, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 0, 0, 0, 3, 0, 1, 0,
|
||||
240, 0, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 249, 255, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
237, 0, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 0, 0, 0, 3, 0, 1, 0,
|
||||
244, 0, 0, 0, 2, 0, 1, 0,
|
||||
110, 117, 108, 108, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
98, 111, 111, 108, 101, 97, 110, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
110, 117, 109, 98, 101, 114, 0, 0,
|
||||
11, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
11, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
115, 116, 114, 105, 110, 103, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
97, 114, 114, 97, 121, 0, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 3, 0, 1, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
131, 221, 25, 249, 69, 120, 250, 163,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
111, 98, 106, 101, 99, 116, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 3, 0, 1, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
223, 157, 214, 53, 231, 38, 16, 227,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 108, 108, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
72, 61, 201, 161, 236, 246, 217, 160,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_a3fa7845f919dd83 = b_a3fa7845f919dd83.words;
|
||||
#if !CAPNP_LITE
|
||||
static const ::capnp::_::RawSchema* const d_a3fa7845f919dd83[] = {
|
||||
&s_a0d9f6eca1c93d48,
|
||||
&s_a3fa7845f919dd83,
|
||||
&s_e31026e735d69ddf,
|
||||
};
|
||||
static const uint16_t m_a3fa7845f919dd83[] = {4, 1, 6, 0, 2, 5, 3};
|
||||
static const uint16_t i_a3fa7845f919dd83[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
const ::capnp::_::RawSchema s_a3fa7845f919dd83 = {
|
||||
0xa3fa7845f919dd83, b_a3fa7845f919dd83.words, 137, d_a3fa7845f919dd83, m_a3fa7845f919dd83,
|
||||
3, 7, i_a3fa7845f919dd83, nullptr, nullptr, { &s_a3fa7845f919dd83, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<49> b_e31026e735d69ddf = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
223, 157, 214, 53, 231, 38, 16, 227,
|
||||
30, 0, 0, 0, 1, 0, 0, 0,
|
||||
131, 221, 25, 249, 69, 120, 250, 163,
|
||||
2, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 34, 1, 0, 0,
|
||||
37, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 119, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
86, 97, 108, 117, 101, 46, 70, 105,
|
||||
101, 108, 100, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
8, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 0, 0, 0, 3, 0, 1, 0,
|
||||
48, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 0, 0, 0, 3, 0, 1, 0,
|
||||
52, 0, 0, 0, 2, 0, 1, 0,
|
||||
110, 97, 109, 101, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
118, 97, 108, 117, 101, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
131, 221, 25, 249, 69, 120, 250, 163,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_e31026e735d69ddf = b_e31026e735d69ddf.words;
|
||||
#if !CAPNP_LITE
|
||||
static const ::capnp::_::RawSchema* const d_e31026e735d69ddf[] = {
|
||||
&s_a3fa7845f919dd83,
|
||||
};
|
||||
static const uint16_t m_e31026e735d69ddf[] = {0, 1};
|
||||
static const uint16_t i_e31026e735d69ddf[] = {0, 1};
|
||||
const ::capnp::_::RawSchema s_e31026e735d69ddf = {
|
||||
0xe31026e735d69ddf, b_e31026e735d69ddf.words, 49, d_e31026e735d69ddf, m_e31026e735d69ddf,
|
||||
1, 2, i_e31026e735d69ddf, nullptr, nullptr, { &s_e31026e735d69ddf, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<54> b_a0d9f6eca1c93d48 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
72, 61, 201, 161, 236, 246, 217, 160,
|
||||
30, 0, 0, 0, 1, 0, 0, 0,
|
||||
131, 221, 25, 249, 69, 120, 250, 163,
|
||||
2, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 26, 1, 0, 0,
|
||||
37, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 119, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
86, 97, 108, 117, 101, 46, 67, 97,
|
||||
108, 108, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
8, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 0, 0, 0, 3, 0, 1, 0,
|
||||
52, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 0, 0, 0, 3, 0, 1, 0,
|
||||
72, 0, 0, 0, 2, 0, 1, 0,
|
||||
102, 117, 110, 99, 116, 105, 111, 110,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
112, 97, 114, 97, 109, 115, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 3, 0, 1, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
131, 221, 25, 249, 69, 120, 250, 163,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_a0d9f6eca1c93d48 = b_a0d9f6eca1c93d48.words;
|
||||
#if !CAPNP_LITE
|
||||
static const ::capnp::_::RawSchema* const d_a0d9f6eca1c93d48[] = {
|
||||
&s_a3fa7845f919dd83,
|
||||
};
|
||||
static const uint16_t m_a0d9f6eca1c93d48[] = {0, 1};
|
||||
static const uint16_t i_a0d9f6eca1c93d48[] = {0, 1};
|
||||
const ::capnp::_::RawSchema s_a0d9f6eca1c93d48 = {
|
||||
0xa0d9f6eca1c93d48, b_a0d9f6eca1c93d48.words, 54, d_a0d9f6eca1c93d48, m_a0d9f6eca1c93d48,
|
||||
1, 2, i_a0d9f6eca1c93d48, nullptr, nullptr, { &s_a0d9f6eca1c93d48, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<21> b_fa5b1fd61c2e7c3d = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
61, 124, 46, 28, 214, 31, 91, 250,
|
||||
24, 0, 0, 0, 5, 0, 232, 2,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 234, 0, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
110, 97, 109, 101, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_fa5b1fd61c2e7c3d = b_fa5b1fd61c2e7c3d.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_fa5b1fd61c2e7c3d = {
|
||||
0xfa5b1fd61c2e7c3d, b_fa5b1fd61c2e7c3d.words, 21, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_fa5b1fd61c2e7c3d, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<21> b_82d3e852af0336bf = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
191, 54, 3, 175, 82, 232, 211, 130,
|
||||
24, 0, 0, 0, 5, 0, 224, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 2, 1, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
102, 108, 97, 116, 116, 101, 110, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
97, 234, 194, 123, 37, 19, 223, 196,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_82d3e852af0336bf = b_82d3e852af0336bf.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_82d3e852af0336bf = {
|
||||
0x82d3e852af0336bf, b_82d3e852af0336bf.words, 21, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_82d3e852af0336bf, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<35> b_c4df13257bc2ea61 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
97, 234, 194, 123, 37, 19, 223, 196,
|
||||
24, 0, 0, 0, 1, 0, 0, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
1, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 58, 1, 0, 0,
|
||||
37, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 63, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
70, 108, 97, 116, 116, 101, 110, 79,
|
||||
112, 116, 105, 111, 110, 115, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
4, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8, 0, 0, 0, 3, 0, 1, 0,
|
||||
20, 0, 0, 0, 2, 0, 1, 0,
|
||||
112, 114, 101, 102, 105, 120, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_c4df13257bc2ea61 = b_c4df13257bc2ea61.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_c4df13257bc2ea61[] = {0};
|
||||
static const uint16_t i_c4df13257bc2ea61[] = {0};
|
||||
const ::capnp::_::RawSchema s_c4df13257bc2ea61 = {
|
||||
0xc4df13257bc2ea61, b_c4df13257bc2ea61.words, 35, nullptr, m_c4df13257bc2ea61,
|
||||
0, 1, i_c4df13257bc2ea61, nullptr, nullptr, { &s_c4df13257bc2ea61, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<22> b_cfa794e8d19a0162 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
98, 1, 154, 209, 232, 148, 167, 207,
|
||||
24, 0, 0, 0, 5, 0, 80, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 50, 1, 0, 0,
|
||||
37, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
100, 105, 115, 99, 114, 105, 109, 105,
|
||||
110, 97, 116, 111, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 83, 62, 41, 12, 194, 248, 194,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_cfa794e8d19a0162 = b_cfa794e8d19a0162.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_cfa794e8d19a0162 = {
|
||||
0xcfa794e8d19a0162, b_cfa794e8d19a0162.words, 22, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_cfa794e8d19a0162, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<51> b_c2f8c20c293e5319 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
25, 83, 62, 41, 12, 194, 248, 194,
|
||||
24, 0, 0, 0, 1, 0, 0, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
2, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 106, 1, 0, 0,
|
||||
41, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 0, 0, 0, 119, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
68, 105, 115, 99, 114, 105, 109, 105,
|
||||
110, 97, 116, 111, 114, 79, 112, 116,
|
||||
105, 111, 110, 115, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
8, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 0, 0, 0, 3, 0, 1, 0,
|
||||
48, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 0, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 0, 0, 0, 3, 0, 1, 0,
|
||||
56, 0, 0, 0, 2, 0, 1, 0,
|
||||
110, 97, 109, 101, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
118, 97, 108, 117, 101, 78, 97, 109,
|
||||
101, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_c2f8c20c293e5319 = b_c2f8c20c293e5319.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_c2f8c20c293e5319[] = {0, 1};
|
||||
static const uint16_t i_c2f8c20c293e5319[] = {0, 1};
|
||||
const ::capnp::_::RawSchema s_c2f8c20c293e5319 = {
|
||||
0xc2f8c20c293e5319, b_c2f8c20c293e5319.words, 51, nullptr, m_c2f8c20c293e5319,
|
||||
0, 2, i_c2f8c20c293e5319, nullptr, nullptr, { &s_c2f8c20c293e5319, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<21> b_d7d879450a253e4b = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
75, 62, 37, 10, 69, 121, 216, 215,
|
||||
24, 0, 0, 0, 5, 0, 32, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 250, 0, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
98, 97, 115, 101, 54, 52, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_d7d879450a253e4b = b_d7d879450a253e4b.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_d7d879450a253e4b = {
|
||||
0xd7d879450a253e4b, b_d7d879450a253e4b.words, 21, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_d7d879450a253e4b, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<21> b_f061e22f0ae5c7b5 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
181, 199, 229, 10, 47, 226, 97, 240,
|
||||
24, 0, 0, 0, 5, 0, 32, 0,
|
||||
52, 94, 58, 164, 151, 146, 249, 142,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 226, 0, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 112, 110, 112, 47, 99, 111,
|
||||
109, 112, 97, 116, 47, 106, 115, 111,
|
||||
110, 46, 99, 97, 112, 110, 112, 58,
|
||||
104, 101, 120, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_f061e22f0ae5c7b5 = b_f061e22f0ae5c7b5.words;
|
||||
#if !CAPNP_LITE
|
||||
const ::capnp::_::RawSchema s_f061e22f0ae5c7b5 = {
|
||||
0xf061e22f0ae5c7b5, b_f061e22f0ae5c7b5.words, 21, nullptr, nullptr,
|
||||
0, 0, nullptr, nullptr, nullptr, { &s_f061e22f0ae5c7b5, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
} // namespace schemas
|
||||
} // namespace capnp
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
namespace capnp {
|
||||
namespace json {
|
||||
|
||||
// Value
|
||||
constexpr uint16_t Value::_capnpPrivate::dataWordSize;
|
||||
constexpr uint16_t Value::_capnpPrivate::pointerCount;
|
||||
#if !CAPNP_LITE
|
||||
constexpr ::capnp::Kind Value::_capnpPrivate::kind;
|
||||
constexpr ::capnp::_::RawSchema const* Value::_capnpPrivate::schema;
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
// Value::Field
|
||||
constexpr uint16_t Value::Field::_capnpPrivate::dataWordSize;
|
||||
constexpr uint16_t Value::Field::_capnpPrivate::pointerCount;
|
||||
#if !CAPNP_LITE
|
||||
constexpr ::capnp::Kind Value::Field::_capnpPrivate::kind;
|
||||
constexpr ::capnp::_::RawSchema const* Value::Field::_capnpPrivate::schema;
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
// Value::Call
|
||||
constexpr uint16_t Value::Call::_capnpPrivate::dataWordSize;
|
||||
constexpr uint16_t Value::Call::_capnpPrivate::pointerCount;
|
||||
#if !CAPNP_LITE
|
||||
constexpr ::capnp::Kind Value::Call::_capnpPrivate::kind;
|
||||
constexpr ::capnp::_::RawSchema const* Value::Call::_capnpPrivate::schema;
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
// FlattenOptions
|
||||
constexpr uint16_t FlattenOptions::_capnpPrivate::dataWordSize;
|
||||
constexpr uint16_t FlattenOptions::_capnpPrivate::pointerCount;
|
||||
#if !CAPNP_LITE
|
||||
constexpr ::capnp::Kind FlattenOptions::_capnpPrivate::kind;
|
||||
constexpr ::capnp::_::RawSchema const* FlattenOptions::_capnpPrivate::schema;
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
// DiscriminatorOptions
|
||||
constexpr uint16_t DiscriminatorOptions::_capnpPrivate::dataWordSize;
|
||||
constexpr uint16_t DiscriminatorOptions::_capnpPrivate::pointerCount;
|
||||
#if !CAPNP_LITE
|
||||
constexpr ::capnp::Kind DiscriminatorOptions::_capnpPrivate::kind;
|
||||
constexpr ::capnp::_::RawSchema const* DiscriminatorOptions::_capnpPrivate::schema;
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue