mirror of
https://github.com/openappsec/attachment.git
synced 2025-11-15 17:02:15 +03:00
Uploading attachment code
This commit is contained in:
60
.gitignore
vendored
60
.gitignore
vendored
@@ -1,52 +1,44 @@
|
|||||||
# Prerequisites
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
|
|
||||||
# Object files
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
*.o
|
*.o
|
||||||
*.ko
|
|
||||||
*.obj
|
*.obj
|
||||||
*.elf
|
|
||||||
|
|
||||||
# Linker output
|
|
||||||
*.ilk
|
|
||||||
*.map
|
|
||||||
*.exp
|
|
||||||
|
|
||||||
# Precompiled Headers
|
# Precompiled Headers
|
||||||
*.gch
|
*.gch
|
||||||
*.pch
|
*.pch
|
||||||
|
|
||||||
# Libraries
|
# Compiled Dynamic libraries
|
||||||
*.lib
|
|
||||||
*.a
|
|
||||||
*.la
|
|
||||||
*.lo
|
|
||||||
|
|
||||||
# Shared objects (inc. Windows DLLs)
|
|
||||||
*.dll
|
|
||||||
*.so
|
*.so
|
||||||
*.so.*
|
|
||||||
*.dylib
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
# Executables
|
# Executables
|
||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
*.i*86
|
|
||||||
*.x86_64
|
|
||||||
*.hex
|
|
||||||
|
|
||||||
# Debug files
|
# CMake build files
|
||||||
*.dSYM/
|
**/cmake-build-debug
|
||||||
*.su
|
**/CMakeCache.txt
|
||||||
*.idb
|
**/cmake_install.cmake
|
||||||
*.pdb
|
**/install_manifest.txt
|
||||||
|
**/CMakeFiles/
|
||||||
# Kernel Module Compile Results
|
**/CTestTestfile.cmake
|
||||||
*.mod*
|
**/Makefile
|
||||||
*.cmd
|
**/*.cbp
|
||||||
.tmp_versions/
|
**/CMakeScripts
|
||||||
modules.order
|
**/compile_commands.json
|
||||||
Module.symvers
|
|
||||||
Mkfile.old
|
|
||||||
dkms.conf
|
|
||||||
|
|||||||
12
CMakeLists.txt
Normal file
12
CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
cmake_minimum_required (VERSION 2.8.4)
|
||||||
|
project (ngen)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||||
|
|
||||||
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
include_directories(external)
|
||||||
|
include_directories(core/include/attachments)
|
||||||
|
|
||||||
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(attachments)
|
||||||
27
LICENSE
27
LICENSE
@@ -172,30 +172,3 @@
|
|||||||
defend, and hold each Contributor harmless for any liability
|
defend, and hold each Contributor harmless for any liability
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
of your accepting any such warranty or additional liability.
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|||||||
5
NOTICE
Normal file
5
NOTICE
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Agent Repo Demo2
|
||||||
|
Copyright 2022 Check Point Software Technologies
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
Check Point Software Technologies (https://www.checkpoint.com/).
|
||||||
36
README.md
36
README.md
@@ -1 +1,37 @@
|
|||||||
# attachment
|
# attachment
|
||||||
|
|
||||||
|
## NGINX attachment compilation instructions
|
||||||
|
|
||||||
|
### Compiling this repository
|
||||||
|
1. Clone this repository
|
||||||
|
2. Run CMake command (we recommand something like `cmake -DCMAKE_INSTALL_PREFIX=<output path> <repository path>`).
|
||||||
|
3. From the repositoy directory run `make install`
|
||||||
|
|
||||||
|
For example:
|
||||||
|
```bash
|
||||||
|
$ git clone https://github.com/openappsec/attachment.git
|
||||||
|
$ cd attachment/
|
||||||
|
$ cmake -DCMAKE_INSTALL_PREFIX=build_out .
|
||||||
|
$ make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### For the NGINX plugin
|
||||||
|
Grab the nginx source code from [nginx.org](http://nginx.org/), for example,
|
||||||
|
the version 1.23.0 (see [nginx compatibility](http://nginx.org/en/docs/njs/compatibility.html)), and then build the source with this module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ module_path=/absolute/path/to/attachment
|
||||||
|
|
||||||
|
$ wget 'http://nginx.org/download/nginx-1.23.0.tar.gz'
|
||||||
|
$ tar -xzvf nginx-1.23.0.tar.gz
|
||||||
|
$ cd nginx-1.23.0/
|
||||||
|
|
||||||
|
$ ./configure --add-dynamic-module=$module_path --with-cc-opt="-I $module_path/core/include/attachments"
|
||||||
|
|
||||||
|
$ make modules
|
||||||
|
```
|
||||||
|
|
||||||
|
#### NGINX plugin associated libraries
|
||||||
|
The NGINX plugin uses these libraries: shmem_ipc, compression_utils, and nginx_attachment_util.
|
||||||
|
|
||||||
|
They can be found under the `lib` directory in the `<output path>` given to the CMake.
|
||||||
|
|||||||
1
attachments/CMakeLists.txt
Normal file
1
attachments/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(nginx)
|
||||||
1
attachments/nginx/CMakeLists.txt
Normal file
1
attachments/nginx/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(nginx_attachment_util)
|
||||||
5
attachments/nginx/nginx_attachment_util/CMakeLists.txt
Normal file
5
attachments/nginx/nginx_attachment_util/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
add_definitions(-DUSERSPACE)
|
||||||
|
|
||||||
|
add_library(osrc_nginx_attachment_util SHARED nginx_attachment_util.cc)
|
||||||
|
|
||||||
|
install(TARGETS osrc_nginx_attachment_util DESTINATION lib)
|
||||||
233
attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc
Normal file
233
attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "nginx_attachment_util.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "http_configuration.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static HttpAttachmentConfiguration conf_data;
|
||||||
|
|
||||||
|
int
|
||||||
|
initAttachmentConfig(c_str conf_file)
|
||||||
|
{
|
||||||
|
return conf_data.init(conf_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_http_inspection_mode_e
|
||||||
|
getInspectionMode()
|
||||||
|
{
|
||||||
|
return static_cast<ngx_http_inspection_mode_e>(conf_data.getNumericalValue("nginx_inspection_mode"));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getNumOfNginxIpcElements()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("num_of_nginx_ipc_elements");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getKeepAliveIntervalMsec()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("keep_alive_interval_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getDbgLevel()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("dbg_level");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isDebugContext(c_str client, c_str server, unsigned int port, c_str method, c_str host , c_str uri)
|
||||||
|
{
|
||||||
|
auto &ctx = conf_data.getDebugContext();
|
||||||
|
return
|
||||||
|
(ctx.client == "" || ctx.client == client) &&
|
||||||
|
(ctx.server == "" || ctx.server == server) &&
|
||||||
|
(ctx.port == 0 || ctx.port == port) &&
|
||||||
|
(ctx.method == "" || ctx.method == method) &&
|
||||||
|
(ctx.host == "" || ctx.host == host) &&
|
||||||
|
(ctx.uri == "" || ctx.uri == uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
c_str
|
||||||
|
getStaticResourcesPath()
|
||||||
|
{
|
||||||
|
return conf_data.getStringValue("static_resources_path").c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isFailOpenMode()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("is_fail_open_mode_enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getFailOpenTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("fail_open_timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getMaxSessionsPerMinute()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("max_sessions_per_minute");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isFailOpenOnSessionLimit()
|
||||||
|
{
|
||||||
|
return conf_data.getStringValue("sessions_per_minute_limit_verdict") == "Accept";
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getRegistrationThreadTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("registration_thread_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getReqProccessingTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("req_proccessing_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getReqHeaderThreadTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("req_header_thread_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getReqBodyThreadTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("req_body_thread_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getResProccessingTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("res_proccessing_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getResHeaderThreadTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("res_header_thread_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
getResBodyThreadTimeout()
|
||||||
|
{
|
||||||
|
return conf_data.getNumericalValue("res_body_thread_timeout_msec");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isIPAddress(c_str ip_str)
|
||||||
|
{
|
||||||
|
int address_family = AF_INET;
|
||||||
|
for (int i = 0; ip_str[i]; ++i) {
|
||||||
|
if (ip_str[i] == ':') address_family = AF_INET6;
|
||||||
|
}
|
||||||
|
|
||||||
|
char placeholder[16];
|
||||||
|
return inet_pton(address_family, ip_str, placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IpAddress
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct in_addr ipv4;
|
||||||
|
struct in6_addr ipv6;
|
||||||
|
} ip;
|
||||||
|
bool is_ipv4;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator<(const IpAddress &other) const
|
||||||
|
{
|
||||||
|
if (is_ipv4 != other.is_ipv4) return is_ipv4 < other.is_ipv4;
|
||||||
|
if (is_ipv4) return memcmp(&ip.ipv4, &other.ip.ipv4, sizeof(struct in_addr)) < 0;
|
||||||
|
return memcmp(&ip.ipv6, &other.ip.ipv6, sizeof(struct in6_addr)) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator<=(const IpAddress &other) const
|
||||||
|
{
|
||||||
|
return !(other < *this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static IpAddress
|
||||||
|
createIPAddress(c_str ip_str)
|
||||||
|
{
|
||||||
|
IpAddress res;
|
||||||
|
|
||||||
|
for (int i = 0; ip_str[i]; ++i) {
|
||||||
|
if (ip_str[i] == ':') {
|
||||||
|
res.is_ipv4 = false;
|
||||||
|
inet_pton(AF_INET6, ip_str, &res.ip.ipv6);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.is_ipv4 = true;
|
||||||
|
inet_pton(AF_INET, ip_str, &res.ip.ipv4);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
isIPInRange(const IpAddress &ip, const IpAddress &start, const IpAddress &end)
|
||||||
|
{
|
||||||
|
if (ip.is_ipv4 != start.is_ipv4 || ip.is_ipv4 != end.is_ipv4) return false;
|
||||||
|
return start <= ip && ip <= end;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
isIPInRange(const IpAddress &ip, const string &range)
|
||||||
|
{
|
||||||
|
auto delimiter = range.find('-');
|
||||||
|
|
||||||
|
if (delimiter == string::npos) {
|
||||||
|
if (!isIPAddress(range.c_str())) return false;
|
||||||
|
auto address = createIPAddress(range.c_str());
|
||||||
|
return isIPInRange(ip, address, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start_str = range.substr(0, delimiter);
|
||||||
|
if (!isIPAddress(start_str.c_str())) return false;
|
||||||
|
auto start_addr = createIPAddress(start_str.c_str());
|
||||||
|
|
||||||
|
auto end_str = range.substr(delimiter + 1);
|
||||||
|
if (!isIPAddress(end_str.c_str())) return false;
|
||||||
|
auto end_addr = createIPAddress(end_str.c_str());
|
||||||
|
|
||||||
|
return isIPInRange(ip, start_addr, end_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isSkipSource(c_str ip_str)
|
||||||
|
{
|
||||||
|
if (!isIPAddress(ip_str)) return 0;
|
||||||
|
auto ip = createIPAddress(ip_str);
|
||||||
|
|
||||||
|
for (auto &range : conf_data.getExcludeSources()) {
|
||||||
|
if (isIPInRange(ip, range)) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
548
attachments/nginx/ngx_module/ngx_cp_compression.c
Normal file
548
attachments/nginx/ngx_module/ngx_cp_compression.c
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_compression.c
|
||||||
|
#include "ngx_cp_compression.h"
|
||||||
|
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_metric.h"
|
||||||
|
|
||||||
|
static ngx_int_t is_debug_printing_initialized = 0;
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
is_compression_debug_printing_initialized()
|
||||||
|
{
|
||||||
|
return is_debug_printing_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writes a debug message at a debug level of Trace.
|
||||||
|
/// @param[in] debug_message message to be written.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
compression_trace_level_debug_printer(const char *debug_message)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, debug_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writes a debug message at a debug level of Debug.
|
||||||
|
/// @param[in] debug_message message to be written.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
compression_debug_level_debug_printer(const char *debug_message)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, debug_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writes a debug message at a debug level of Info.
|
||||||
|
/// @param[in] debug_message message to be written.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
compression_info_level_debug_printer(const char *debug_message)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_INFO, debug_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writes a debug message at a debug level of Warning.
|
||||||
|
/// @param[in] debug_message message to be written.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
compression_warning_level_debug_printer(const char *debug_message)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, debug_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writes a debug message at a debug level of Error.
|
||||||
|
/// @param[in] debug_message message to be written.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
compression_error_level_debug_printer(const char *debug_message)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_ERROR, debug_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writes a debug message at a debug level of Assert.
|
||||||
|
/// @param[in] debug_message message to be written.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
compression_assert_level_debug_printer(const char *debug_message)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_ASSERT, debug_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
initialize_compression_debug_printing()
|
||||||
|
{
|
||||||
|
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_TRACE, compression_trace_level_debug_printer);
|
||||||
|
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_DEBUG, compression_debug_level_debug_printer);
|
||||||
|
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_INFO, compression_info_level_debug_printer);
|
||||||
|
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_WARNING, compression_warning_level_debug_printer);
|
||||||
|
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_ERROR, compression_error_level_debug_printer);
|
||||||
|
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_ASSERTION, compression_assert_level_debug_printer);
|
||||||
|
|
||||||
|
is_debug_printing_initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Checks if the compression buffer is valid.
|
||||||
|
/// @param[in] should_compress Checks if buffer can be used for compression.
|
||||||
|
/// - #0 - Buffer is used for decompression.
|
||||||
|
/// - #1 - Buffer is used for compression.
|
||||||
|
/// @param[in] buffer message to be written.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
is_valid_compression_buffer(const ngx_int_t should_compress, const ngx_buf_t *buffer)
|
||||||
|
{
|
||||||
|
uint64_t buffer_size = buffer->last - buffer->pos;
|
||||||
|
|
||||||
|
if (buffer_size == 0 && !should_compress) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Invalid decompression buffer: has size 0");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Gets the NGINX string data from NGINX buffer.
|
||||||
|
/// @param[in, out] buffer_data NGINX string, used as a destination.
|
||||||
|
/// @param[in] buffer NGINX buffer.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
get_buffer_data(ngx_str_t *buffer_data, const ngx_buf_t *buffer)
|
||||||
|
{
|
||||||
|
if (buffer_data == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Passed a null pointer as destination buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_data->len = buffer->last - buffer->pos;
|
||||||
|
buffer_data->data = buffer->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets the buffer from NGINX string to NGINX buffer.
|
||||||
|
/// @param[in, out] buffer NGINX buffer, used as a destination.
|
||||||
|
/// @param[in] buffer_data NGINX string.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
set_buffer_data(ngx_buf_t *buffer, const ngx_str_t *data)
|
||||||
|
{
|
||||||
|
buffer->start = data->data;
|
||||||
|
buffer->pos = buffer->start;
|
||||||
|
buffer->last = buffer->start + data->len;
|
||||||
|
buffer->end = buffer->last;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Decompresses or compresses the provided data.
|
||||||
|
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||||
|
/// - #0 - Function will decompression.
|
||||||
|
/// - #1 - Function will compress.
|
||||||
|
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||||
|
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||||
|
/// @param[in, out] output NGINX string to be used as output.
|
||||||
|
/// @param[in] input NGINX string input to be used as input.
|
||||||
|
/// @param[in] pool NGINX pool.
|
||||||
|
/// @param[in, out] params Holds NGINX compression parameters.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
compression_data_filter(
|
||||||
|
const ngx_int_t should_compress,
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
int *is_last_decompressed_part,
|
||||||
|
ngx_str_t *output,
|
||||||
|
ngx_str_t *input,
|
||||||
|
ngx_pool_t *pool,
|
||||||
|
ngx_cp_http_compression_params *params
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CompressionResult compression_result;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Performing %s on buffer data", should_compress ? "compression" : "decompression");
|
||||||
|
|
||||||
|
if (should_compress && params == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_ASSERT, "Passed a pointer to null as compression parameters");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_compress) {
|
||||||
|
// Compressing data.
|
||||||
|
compression_result = compressData(
|
||||||
|
compression_stream,
|
||||||
|
params->compression_type,
|
||||||
|
input->len,
|
||||||
|
input->data,
|
||||||
|
params->is_last_part
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Decompressing data.
|
||||||
|
DecompressionResult decompression_result = decompressData(compression_stream, input->len, input->data);
|
||||||
|
compression_result.ok = decompression_result.ok;
|
||||||
|
compression_result.num_output_bytes = decompression_result.num_output_bytes;
|
||||||
|
compression_result.output = decompression_result.output;
|
||||||
|
*is_last_decompressed_part = decompression_result.is_last_chunk;
|
||||||
|
}
|
||||||
|
if (!compression_result.ok) return NGX_ERROR;
|
||||||
|
|
||||||
|
if (compression_result.output == NULL) {
|
||||||
|
output->len = 0;
|
||||||
|
output->data = (u_char *)"";
|
||||||
|
} else {
|
||||||
|
output->len = compression_result.num_output_bytes;
|
||||||
|
output->data = ngx_palloc(pool, output->len);
|
||||||
|
if (output->data == NULL) {
|
||||||
|
// Failed to allocate a new buffer.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate a new buffer");
|
||||||
|
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memcpy(output->data, compression_result.output, output->len);
|
||||||
|
free(compression_result.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully %s buffer data", should_compress ? "compressed" : "decompressed");
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Decompresses or compresses the provided buffer.
|
||||||
|
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||||
|
/// - #0 - Function will decompression.
|
||||||
|
/// - #1 - Function will compress.
|
||||||
|
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||||
|
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||||
|
/// @param[in, out] dest NGINX buffer used as destination.
|
||||||
|
/// @param[in] src NGINX buffer used as source.
|
||||||
|
/// @param[in] pool NGINX pool.
|
||||||
|
/// @param[in] params Holds NGINX compression parameters.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
compression_buffer_filter(
|
||||||
|
const ngx_int_t should_compress,
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
int *is_last_decompressed_part,
|
||||||
|
ngx_buf_t *dest,
|
||||||
|
ngx_buf_t *src,
|
||||||
|
ngx_pool_t *pool,
|
||||||
|
ngx_cp_http_compression_params *params
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_str_t src_data;
|
||||||
|
ngx_str_t dest_data;
|
||||||
|
ngx_int_t compression_result;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Performing %s on buffer", should_compress ? "compression" : "decompression");
|
||||||
|
|
||||||
|
if (is_valid_compression_buffer(should_compress, src) != NGX_OK) {
|
||||||
|
// Invalid buffer provided.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to %s: invalid buffer", should_compress ? "compress" : "decompress");
|
||||||
|
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_compress) {
|
||||||
|
// Preparing data for compression.
|
||||||
|
params->is_last_part = src->last_buf;
|
||||||
|
|
||||||
|
if (params->is_last_part && src->pos == NULL) {
|
||||||
|
src->start = (u_char *)"";
|
||||||
|
src->pos = src->start;
|
||||||
|
src->last = src->start;
|
||||||
|
src->end = src->start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_buffer_data(&src_data, src);
|
||||||
|
// Compresses the data
|
||||||
|
compression_result = compression_data_filter(
|
||||||
|
should_compress,
|
||||||
|
compression_stream,
|
||||||
|
is_last_decompressed_part,
|
||||||
|
&dest_data,
|
||||||
|
&src_data,
|
||||||
|
pool,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (compression_result != NGX_OK) {
|
||||||
|
// Failed to compress or decompress.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to %s data", should_compress ? "compress" : "decompress");
|
||||||
|
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memcpy(dest, src, sizeof(ngx_buf_t));
|
||||||
|
set_buffer_data(dest, &dest_data);
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully %s buffer", should_compress ? "compressed" : "decompressed");
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Compresses the provided chain.
|
||||||
|
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||||
|
/// - #0 - Function will decompression.
|
||||||
|
/// - #1 - Function will compress.
|
||||||
|
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||||
|
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||||
|
/// @param[in, out] body NGINX chain used as destination.
|
||||||
|
/// @param[in] original_body_contents NGINX chain used as source.
|
||||||
|
/// @param[in] pool NGINX pool.
|
||||||
|
/// @param[in] params Holds NGINX cp compression parameters.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
compression_chain_filter(
|
||||||
|
const ngx_int_t should_compress,
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
int *is_last_decompressed_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body_contents,
|
||||||
|
ngx_pool_t *pool,
|
||||||
|
ngx_cp_http_compression_params *params
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_int_t compression_result;
|
||||||
|
ngx_buf_t *output_buffer = ngx_calloc_buf(pool);
|
||||||
|
ngx_chain_t *curr_input_link = NULL;
|
||||||
|
ngx_chain_t *curr_original_contents_link = original_body_contents == NULL ? NULL : *original_body_contents;
|
||||||
|
|
||||||
|
if (body == NULL) {
|
||||||
|
// Null body parameter has been passed.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to %s chain: passed null pointer as output chain",
|
||||||
|
should_compress ? "compress" : "decompress"
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Performing %s on chain", should_compress ? "compression" : "decompression");
|
||||||
|
|
||||||
|
for (curr_input_link = *body; curr_input_link != NULL; curr_input_link = curr_input_link->next) {
|
||||||
|
// Decompress or compresses buffer
|
||||||
|
compression_result = compression_buffer_filter(
|
||||||
|
should_compress,
|
||||||
|
compression_stream,
|
||||||
|
is_last_decompressed_part,
|
||||||
|
output_buffer,
|
||||||
|
curr_input_link->buf,
|
||||||
|
pool,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (compression_result != NGX_OK) {
|
||||||
|
// Failed to decompress or compress.
|
||||||
|
free_chain(pool, *body);
|
||||||
|
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr_original_contents_link != NULL) {
|
||||||
|
curr_original_contents_link->buf = ngx_calloc_buf(pool);
|
||||||
|
ngx_memcpy(curr_original_contents_link->buf, curr_input_link->buf, sizeof(ngx_buf_t));
|
||||||
|
|
||||||
|
if (curr_input_link->next != NULL) {
|
||||||
|
// Allocates next chain.
|
||||||
|
curr_original_contents_link->next = ngx_alloc_chain_link(pool);
|
||||||
|
ngx_memset(curr_original_contents_link->next, 0, sizeof(ngx_chain_t));
|
||||||
|
curr_original_contents_link = curr_original_contents_link->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memcpy(curr_input_link->buf, output_buffer, sizeof(ngx_buf_t));
|
||||||
|
curr_input_link->buf->memory = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully %s chain", should_compress ? "compressed" : "decompressed");
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets the ngx_cp_http_compression_params and calls compression_chain_filter with compression flag.
|
||||||
|
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||||
|
/// @param[in] compression_type Compression type to be used by compressions.
|
||||||
|
/// @param[in] is_last_part Flags if the buffer's last part was compressed.
|
||||||
|
/// @param[in, out] body NGINX chain used as destination.
|
||||||
|
/// @param[in] original_body_contents NGINX chain used as source.
|
||||||
|
/// @param[in] pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
compress_chain(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const CompressionType compression_type,
|
||||||
|
const int is_last_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body_contents,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_cp_http_compression_params params;
|
||||||
|
params.compression_type = compression_type;
|
||||||
|
params.is_last_part = is_last_part;
|
||||||
|
|
||||||
|
return compression_chain_filter(1, compression_stream, NULL, body, original_body_contents, pool, ¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets the ngx_cp_http_compression_params and calls compression_chain_filter with decompression flag.
|
||||||
|
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||||
|
/// @param[in] compression_type Compression type to be used by compressions.
|
||||||
|
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||||
|
/// @param[in, out] body NGINX chain used as destination.
|
||||||
|
/// @param[in] original_body_contents NGINX chain used as source.
|
||||||
|
/// @param[in] pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
decompress_chain(
|
||||||
|
CompressionStream *decompress_stream,
|
||||||
|
int *is_last_decompressed_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
compression_chain_filter(
|
||||||
|
0,
|
||||||
|
decompress_stream,
|
||||||
|
is_last_decompressed_part,
|
||||||
|
body,
|
||||||
|
original_body,
|
||||||
|
pool,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
decompress_body(
|
||||||
|
CompressionStream *decompression_stream,
|
||||||
|
const ngx_http_chunk_type_e chunk_type,
|
||||||
|
int *is_last_decompressed_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body_contents,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char *body_type = chunk_type == REQUEST_BODY ? "request" : "response";
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Decompressing %s body", body_type);
|
||||||
|
|
||||||
|
ngx_int_t decompress_data_res = decompress_chain(
|
||||||
|
decompression_stream,
|
||||||
|
is_last_decompressed_part,
|
||||||
|
body,
|
||||||
|
original_body_contents,
|
||||||
|
pool
|
||||||
|
);
|
||||||
|
if (decompress_data_res != NGX_OK) {
|
||||||
|
// Failed to decompress the provided data.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to decompress %s body", body_type);
|
||||||
|
updateMetricField(
|
||||||
|
chunk_type == REQUEST_BODY ? REQ_FAILED_DECOMPRESSION_COUNT : RES_FAILED_DECOMPRESSION_COUNT,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully decompressed %s body", body_type);
|
||||||
|
updateMetricField(
|
||||||
|
chunk_type == REQUEST_BODY ? REQ_SUCCESSFUL_DECOMPRESSION_COUNT : RES_SUCCESSFUL_DECOMPRESSION_COUNT,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
compress_body(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const CompressionType compression_type,
|
||||||
|
const ngx_http_chunk_type_e chunk_type,
|
||||||
|
const int is_last_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body_contents,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_int_t compress_res;
|
||||||
|
char *body_type;
|
||||||
|
|
||||||
|
if (compression_type == NO_COMPRESSION) {
|
||||||
|
// This function should not be called with a NO_COMPRESSION type.
|
||||||
|
// This if statement serves a case that somewhere throughout the code the data
|
||||||
|
// is set to be compressed but the compression type is wrongly set.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Invalid compression type: NO_COMPRESSION");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
body_type = chunk_type == REQUEST_BODY ? "request" : "response";
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Compressing plain-text %s body in the format \"%s\"",
|
||||||
|
body_type,
|
||||||
|
compression_type == GZIP ? "gzip" : "zlib"
|
||||||
|
);
|
||||||
|
// Checks if the compression was successful.
|
||||||
|
compress_res = compress_chain(
|
||||||
|
compression_stream,
|
||||||
|
compression_type,
|
||||||
|
is_last_part,
|
||||||
|
body,
|
||||||
|
original_body_contents,
|
||||||
|
pool
|
||||||
|
);
|
||||||
|
if (compress_res != NGX_OK) {
|
||||||
|
// Failed to compress the body.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to compress %s body", body_type);
|
||||||
|
updateMetricField(
|
||||||
|
chunk_type == REQUEST_BODY ? REQ_FAILED_COMPRESSION_COUNT : RES_FAILED_COMPRESSION_COUNT,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully compressed %s body", body_type);
|
||||||
|
updateMetricField(
|
||||||
|
chunk_type == REQUEST_BODY ? REQ_SUCCESSFUL_COMPRESSION_COUNT : RES_SUCCESSFUL_COMPRESSION_COUNT,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
100
attachments/nginx/ngx_module/ngx_cp_compression.h
Normal file
100
attachments/nginx/ngx_module/ngx_cp_compression.h
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_compression.h
|
||||||
|
#ifndef __NGX_CP_COMPRESSION_H__
|
||||||
|
#define __NGX_CP_COMPRESSION_H__
|
||||||
|
|
||||||
|
#include <ngx_core.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
#include "compression_utils.h"
|
||||||
|
|
||||||
|
/// @struct ngx_cp_http_compression_params
|
||||||
|
/// @brief Holds all the information regarding NGINX compression.
|
||||||
|
typedef struct {
|
||||||
|
ngx_int_t is_last_part;
|
||||||
|
CompressionType compression_type;
|
||||||
|
} ngx_cp_http_compression_params;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Returns compression debug printing initialization status.
|
||||||
|
/// @returns ngx_int_t;
|
||||||
|
/// - #0 Debug printing is not initialized.
|
||||||
|
/// - #1 Debug printing is initialized.
|
||||||
|
///
|
||||||
|
ngx_int_t is_compression_debug_printing_initialized();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initialize compression debug printing.
|
||||||
|
///
|
||||||
|
void initialize_compression_debug_printing();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Decompress the provided body stream.
|
||||||
|
/// @param[in, out] decompression_stream CompressionStream to decompress.
|
||||||
|
/// @param[in] chunk_type Body chunk type:
|
||||||
|
/// - #REQUEST_BODY
|
||||||
|
/// - #RESPONSE_BODY
|
||||||
|
/// @param[in, out] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||||
|
/// @param[in] body NGINX chain, serves as an output.
|
||||||
|
/// @param[in] original_body_contents NGINX chain, serves as an input to be decompressed.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
decompress_body(
|
||||||
|
CompressionStream *decompression_stream,
|
||||||
|
const ngx_http_chunk_type_e chunk_type,
|
||||||
|
int *is_last_decompressed_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body_contents,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Compresses the provided body stream.
|
||||||
|
/// @details Provided by the body, body type (Response/Request) and stream.
|
||||||
|
/// Compresses the provided body by using the provided compression stream.
|
||||||
|
/// @param[in, out] decompression_stream CompressionStream to compress.
|
||||||
|
/// @param[in] compression_type Compression type.
|
||||||
|
/// - #GZIP
|
||||||
|
/// - #ZLIB
|
||||||
|
/// - #NO_COMPRESSION - Serves as a sanity check in case this function is called
|
||||||
|
/// on a compression type of data that isn't defined and will return NGX_ERROR.
|
||||||
|
/// @param[in] chunk_type Body chunk type:
|
||||||
|
/// - #REQUEST_BODY
|
||||||
|
/// - #RESPONSE_BODY
|
||||||
|
/// @param[in, out] is_last_part Saves the value if last part was compressed.
|
||||||
|
/// @param[in] body NGINX chain.
|
||||||
|
/// @param[in] original_body_contents NGINX chain, serves as an input to be compressed.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
compress_body(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const CompressionType compression_type,
|
||||||
|
const ngx_http_chunk_type_e chunk_type,
|
||||||
|
const int is_last_part,
|
||||||
|
ngx_chain_t **body,
|
||||||
|
ngx_chain_t **original_body_contents,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_COMPRESSION_H__
|
||||||
852
attachments/nginx/ngx_module/ngx_cp_custom_response.c
Normal file
852
attachments/nginx/ngx_module/ngx_cp_custom_response.c
Normal file
@@ -0,0 +1,852 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_custom_response.c
|
||||||
|
#include "ngx_cp_custom_response.h"
|
||||||
|
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Pushes a key and value into a header list.
|
||||||
|
/// @param[in, out] headers_list Headers list to push the new head into.
|
||||||
|
/// @param[in] key_data_size Key data size to be pushed.
|
||||||
|
/// @param[in, out] key_data Key data to be pushed.
|
||||||
|
/// @param[in] value_data_size Value data size to be pushed.
|
||||||
|
/// @param[in, out] value_data Value data to be pushed.
|
||||||
|
/// @return ngx_table_elt_t
|
||||||
|
/// - #A pointer to the pushed NGINX header element.
|
||||||
|
/// - #NULL if failed.
|
||||||
|
///
|
||||||
|
static ngx_table_elt_t *
|
||||||
|
push_header_to_list(
|
||||||
|
ngx_list_t *headers_list,
|
||||||
|
int16_t key_data_size,
|
||||||
|
u_char *key_data,
|
||||||
|
int16_t value_data_size,
|
||||||
|
u_char *value_data
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Allocates a file buffer to the provided file.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool.
|
||||||
|
/// @param[in, out] open_file_info NGINX file info - file information.
|
||||||
|
/// @param[in, out] is_last_buffer Symbolize if the newly allocated buffer is the last buffer.
|
||||||
|
/// @param[in, out] file_path NGINX string.
|
||||||
|
/// @param[in, out] log NGINX log.
|
||||||
|
/// @returns ngx_buf_t
|
||||||
|
/// - #A valid pointer to NGINX buffer.
|
||||||
|
/// - #NULL
|
||||||
|
///
|
||||||
|
static ngx_buf_t *
|
||||||
|
allocate_file_buffer(
|
||||||
|
ngx_pool_t *memory_pool,
|
||||||
|
ngx_open_file_info_t *open_file_info,
|
||||||
|
ngx_int_t is_last_buffer,
|
||||||
|
ngx_str_t *file_path,
|
||||||
|
ngx_log_t *log
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_buf_t *file_buffer = ngx_calloc_buf(memory_pool);
|
||||||
|
if (file_buffer == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate file buffer: could not allocate memory for the buffer");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_buffer->file = ngx_pcalloc(memory_pool, sizeof(ngx_file_t));
|
||||||
|
if (file_buffer->file == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate file buffer's file descriptor");
|
||||||
|
|
||||||
|
ngx_pfree(memory_pool, file_buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_buffer->file_pos = 0;
|
||||||
|
file_buffer->file_last = open_file_info->size;
|
||||||
|
file_buffer->in_file = file_buffer->file_last ? 1: 0;
|
||||||
|
file_buffer->last_buf = is_last_buffer;
|
||||||
|
file_buffer->last_in_chain = 1;
|
||||||
|
file_buffer->file->fd = open_file_info->fd;
|
||||||
|
file_buffer->file->name = *file_path;
|
||||||
|
file_buffer->file->log = log;
|
||||||
|
file_buffer->file->directio = open_file_info->is_directio;
|
||||||
|
|
||||||
|
return file_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Deletes the provided header list.
|
||||||
|
/// @details Iterates over the header list and frees all the nodes' memory back to the NGX pool.
|
||||||
|
/// @param[in, out] headers_list
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
delete_headers_list(ngx_list_t *headers_list)
|
||||||
|
{
|
||||||
|
ngx_list_part_t *headers_iter = headers_list->part.next;
|
||||||
|
ngx_list_part_t *header_to_del;
|
||||||
|
|
||||||
|
while (headers_iter) {
|
||||||
|
header_to_del = headers_iter;
|
||||||
|
headers_iter = headers_iter->next;
|
||||||
|
ngx_pfree(headers_list->pool, header_to_del->elts);
|
||||||
|
header_to_del->elts = NULL;
|
||||||
|
header_to_del->nelts = 0;
|
||||||
|
ngx_pfree(headers_list->pool, header_to_del);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers_list->part.nelts = 0;
|
||||||
|
headers_list->last = &headers_list->part;
|
||||||
|
headers_list->part.next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_response_headers_sender(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
const ngx_uint_t response_code,
|
||||||
|
const off_t content_length,
|
||||||
|
const time_t last_modified_time,
|
||||||
|
const unsigned int allow_ranges,
|
||||||
|
const unsigned int keepalive
|
||||||
|
)
|
||||||
|
{
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Generating response headers: response code: %ui, content length: %O, last modified time: %T",
|
||||||
|
response_code,
|
||||||
|
content_length,
|
||||||
|
last_modified_time
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writes the custom response data onto the response headers.
|
||||||
|
request->headers_out.status = response_code;
|
||||||
|
request->headers_out.content_length_n = content_length;
|
||||||
|
request->headers_out.last_modified_time = last_modified_time;
|
||||||
|
request->allow_ranges = allow_ranges;
|
||||||
|
request->keepalive = keepalive;
|
||||||
|
|
||||||
|
if (ngx_http_set_content_type(request) != NGX_OK) {
|
||||||
|
// Failed to get the header's type.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to set content type header");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG,"Successfully generated response headers, sending response headers");
|
||||||
|
return ngx_http_send_header(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_file_response_sender(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_str_t *file_path,
|
||||||
|
ngx_open_file_info_t *open_file_info,
|
||||||
|
ngx_int_t is_main_request,
|
||||||
|
ngx_log_t *log,
|
||||||
|
ngx_pool_t *memory_pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_int_t is_last_buffer;
|
||||||
|
ngx_buf_t *file_buffer;
|
||||||
|
ngx_int_t send_output_chain_result;
|
||||||
|
ngx_chain_t *output_chain;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Trying to send file: %.*s", file_path->len, file_path->data);
|
||||||
|
|
||||||
|
is_last_buffer = is_main_request ? 1: 0;
|
||||||
|
|
||||||
|
// Allocates file's buffer and NGINX chain.
|
||||||
|
file_buffer = allocate_file_buffer(memory_pool, open_file_info, is_last_buffer, file_path, log);
|
||||||
|
|
||||||
|
output_chain = ngx_alloc_chain_link(memory_pool);
|
||||||
|
output_chain->buf = file_buffer;
|
||||||
|
output_chain->next = NULL;
|
||||||
|
|
||||||
|
send_output_chain_result = ngx_http_output_filter(request, output_chain);
|
||||||
|
|
||||||
|
ngx_pfree(memory_pool, file_buffer->file);
|
||||||
|
ngx_pfree(memory_pool, file_buffer);
|
||||||
|
|
||||||
|
return send_output_chain_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Adds event ID to the provided NGINX request.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
///
|
||||||
|
void
|
||||||
|
ngx_add_event_id_to_header(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
u_char *uuid = (u_char *)get_web_response_uuid();
|
||||||
|
ngx_uint_t uuid_size = get_web_response_uuid_size();
|
||||||
|
static u_char uuid_key[] = { 'x', '_', 'e', 'v', 'e', 'n', 't', '_', 'i', 'd' };
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Adding instance ID to header. Incident ID: %s, Incident ID size: %d",
|
||||||
|
uuid,
|
||||||
|
uuid_size
|
||||||
|
);
|
||||||
|
push_header_to_list(
|
||||||
|
&(request->headers_out.headers),
|
||||||
|
sizeof(uuid_key),
|
||||||
|
uuid_key,
|
||||||
|
uuid_size,
|
||||||
|
uuid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
static u_char text_html[] = {'t', 'e', 'x', 't', '/', 'h', 't', 'm', 'l'};
|
||||||
|
static size_t size_of_text_html = sizeof(text_html);
|
||||||
|
ngx_int_t res_code, res;
|
||||||
|
ngx_table_elt_t *location_header;
|
||||||
|
ngx_chain_t out_chain[7]; // http://lxr.nginx.org/source/src/http/ngx_http_special_response.c#0772
|
||||||
|
int send_response_custom_body = 1;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Finalizing rejecting request");
|
||||||
|
|
||||||
|
request->keepalive = 0;
|
||||||
|
|
||||||
|
res_code = get_response_code();
|
||||||
|
request->headers_out.status = res_code;
|
||||||
|
request->headers_out.status_line.len = 0;
|
||||||
|
|
||||||
|
if (res_code == 0) {
|
||||||
|
// Response code was not provided, setting it to NGX_HTTP_CLOSE.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Response code was not provided. Returning default response: %d (NGX_HTTP_CLOSE)",
|
||||||
|
NGX_HTTP_CLOSE
|
||||||
|
);
|
||||||
|
res_code = NGX_HTTP_CLOSE;
|
||||||
|
request->headers_out.status = res_code;
|
||||||
|
|
||||||
|
goto CUSTOM_RES_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_response_code() == NGX_HTTP_TEMPORARY_REDIRECT) {
|
||||||
|
// Handling redirect web response.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Sending Redirect web response"
|
||||||
|
);
|
||||||
|
|
||||||
|
static u_char location_key[] = {'L', 'o', 'c', 'a', 't', 'i', 'o', 'n'};
|
||||||
|
location_header = push_header_to_list(
|
||||||
|
&(request->headers_out.headers),
|
||||||
|
sizeof(location_key),
|
||||||
|
location_key,
|
||||||
|
get_redirect_location_size(),
|
||||||
|
get_redirect_location()
|
||||||
|
);
|
||||||
|
if (location_header == NULL) {
|
||||||
|
// Failed to allocate header.
|
||||||
|
write_dbg(DBG_LEVEL_ERROR, "Failed to allocate header");
|
||||||
|
res_code = NGX_HTTP_CLOSE;
|
||||||
|
goto CUSTOM_RES_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_add_event_id()) {
|
||||||
|
// Add event ID into the header.
|
||||||
|
ngx_add_event_id_to_header(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
request->keepalive = 1;
|
||||||
|
goto CUSTOM_RES_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_add_event_id_to_header(request);
|
||||||
|
|
||||||
|
if (get_response_page_length() == 0) {
|
||||||
|
// Page details were not provided.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Web response page details were not provided. Returning %d response code",
|
||||||
|
get_response_code()
|
||||||
|
);
|
||||||
|
send_response_custom_body = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the finalized rejected data into the headers.
|
||||||
|
request->headers_out.content_type.len = size_of_text_html;
|
||||||
|
request->headers_out.content_type_len = request->headers_out.content_type.len;
|
||||||
|
request->headers_out.content_type.data = text_html;
|
||||||
|
request->headers_out.content_length_n = get_response_page_length();
|
||||||
|
|
||||||
|
delete_headers_list(&request->headers_out.headers);
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Sending response headers for rejected request");
|
||||||
|
res = ngx_http_send_header(request);
|
||||||
|
if (res == NGX_ERROR || res > NGX_OK) {
|
||||||
|
// Failed to send response headers.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Failed to send response headers (result: %d). Returning response code: %d",
|
||||||
|
res,
|
||||||
|
res_code
|
||||||
|
);
|
||||||
|
goto CUSTOM_RES_OUT;
|
||||||
|
}
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Successfully sent response headers for rejected request."
|
||||||
|
" Generating web response page for rejected request."
|
||||||
|
);
|
||||||
|
|
||||||
|
if (send_response_custom_body) {
|
||||||
|
// Sending response custom body.
|
||||||
|
if (get_response_page(request, &out_chain) != NGX_OK) {
|
||||||
|
// Failed to generate custom response page.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Failed to generate web response page. Returning response code: %d",
|
||||||
|
get_response_code()
|
||||||
|
);
|
||||||
|
goto CUSTOM_RES_OUT;
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully generated web response page for rejected request");
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Sending web response body");
|
||||||
|
ngx_int_t output_filter_result = ngx_http_output_filter(request, out_chain);
|
||||||
|
if (output_filter_result != NGX_OK) {
|
||||||
|
// Failed to send response body.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to send web response body");
|
||||||
|
} else {
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully sent web response body");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out_chain[0].buf = ngx_calloc_buf(request->pool);
|
||||||
|
if (out_chain[0].buf == NULL) {
|
||||||
|
// Failed to send web response.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to send web response");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
out_chain[0].buf->last_buf = 1;
|
||||||
|
out_chain[0].next = NULL;
|
||||||
|
return ngx_http_output_filter(request, &out_chain[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_RES_OUT:
|
||||||
|
ngx_http_finalize_request(request, res_code);
|
||||||
|
return res_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Frees modification list.
|
||||||
|
/// @param[in, out] modification_list NGINX modifications.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
free_modifications_list(ngx_http_cp_modification_list *modification_list, ngx_pool_t *pool)
|
||||||
|
{
|
||||||
|
ngx_http_cp_modification_list *next_modification;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Freeing modification list");
|
||||||
|
|
||||||
|
while (modification_list) {
|
||||||
|
next_modification = modification_list->next;
|
||||||
|
ngx_pfree(pool, modification_list->modification_buffer);
|
||||||
|
ngx_pfree(pool, modification_list);
|
||||||
|
modification_list = next_modification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Injects the provided buffer at the provided position into the original_buffer.
|
||||||
|
/// @param[in, out] original_buffer NGINX string that new data will be injected to.
|
||||||
|
/// @param[in] injection_pos Injection position on the original buffer.
|
||||||
|
/// @param[in] injected_buffer_size Injected buffer size.
|
||||||
|
/// @param[in, out] injection_buffer Injected buffer.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
inject_buffer(
|
||||||
|
ngx_str_t *original_buffer,
|
||||||
|
const size_t injection_pos,
|
||||||
|
const size_t injected_buffer_size,
|
||||||
|
u_char *injection_buffer,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t new_buffer_len;
|
||||||
|
|
||||||
|
if (injection_pos > original_buffer->len) {
|
||||||
|
// Injection position is after original buffer's end position.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Injection position is after original buffer's end. Injection position: %u, buffer's size: %u",
|
||||||
|
injection_pos,
|
||||||
|
original_buffer->len
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates memory for a new buffer.
|
||||||
|
new_buffer_len = original_buffer->len + injected_buffer_size;
|
||||||
|
u_char *new_buffer_value = ngx_palloc(pool, new_buffer_len);
|
||||||
|
if (new_buffer_value == NULL) {
|
||||||
|
// Failed to allocate memory for a new buffer.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate memory for a new buffer, size: %u", new_buffer_len);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies the injected data onto the original buffer.
|
||||||
|
ngx_memcpy(new_buffer_value, original_buffer->data, injection_pos);
|
||||||
|
ngx_memcpy(new_buffer_value + injection_pos, injection_buffer, injected_buffer_size);
|
||||||
|
if (injection_pos < original_buffer->len) {
|
||||||
|
ngx_memcpy(
|
||||||
|
new_buffer_value + injection_pos + injected_buffer_size,
|
||||||
|
original_buffer->data + injection_pos,
|
||||||
|
original_buffer->len - injection_pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
original_buffer->len = new_buffer_len;
|
||||||
|
|
||||||
|
ngx_pfree(pool, original_buffer->data);
|
||||||
|
original_buffer->data = new_buffer_value;
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Inject modification into a header's value.
|
||||||
|
/// @param[in, out] header NGINX string that the modifications will be injected into.
|
||||||
|
/// @param[in] modification Modifications to inject.
|
||||||
|
/// @param[in, out] header_pool NGINX pool.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
inject_header_value(
|
||||||
|
ngx_table_elt_t *header,
|
||||||
|
ngx_http_cp_modification_list *modification,
|
||||||
|
ngx_pool_t *header_pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (modification->modification.injection_pos < 0) {
|
||||||
|
// Injection position is after original buffer's end position.
|
||||||
|
write_dbg(DBG_LEVEL_ASSERT, "Negative injection position: %d", modification->modification.injection_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Injecting buffer into header's value. "
|
||||||
|
"Header's index: %ui, original header's data: '%.*s' (size: %u): '%.*s' (size: %u), "
|
||||||
|
"injection position: %ui, injected buffer: %s (size: %u)",
|
||||||
|
modification->modification.orig_buff_index,
|
||||||
|
header->key.len,
|
||||||
|
header->key.data,
|
||||||
|
header->key.len,
|
||||||
|
header->value.len,
|
||||||
|
header->value.data,
|
||||||
|
header->value.len,
|
||||||
|
modification->modification.injection_pos,
|
||||||
|
modification->modification_buffer,
|
||||||
|
modification->modification.injection_size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Inject the modification's buffer into the header.
|
||||||
|
ngx_int_t inject_buffer_result = inject_buffer(
|
||||||
|
&header->value,
|
||||||
|
modification->modification.injection_pos,
|
||||||
|
modification->modification.injection_size,
|
||||||
|
(u_char *)modification->modification_buffer,
|
||||||
|
header_pool
|
||||||
|
);
|
||||||
|
if (inject_buffer_result != NGX_OK) return NGX_ERROR;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Successfully injected header value. Header's value after injection: %.*s (size: %u)",
|
||||||
|
header->value.len,
|
||||||
|
header->value.data,
|
||||||
|
header->value.len
|
||||||
|
);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ngx_table_elt_t *
|
||||||
|
push_header_to_list(
|
||||||
|
ngx_list_t *headers_list,
|
||||||
|
int16_t key_data_size,
|
||||||
|
u_char *key_data,
|
||||||
|
int16_t value_data_size,
|
||||||
|
u_char *value_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_table_elt_t *header = ngx_list_push(headers_list);
|
||||||
|
if (header == NULL) {
|
||||||
|
// Failed to allocate header.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate header");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
header->hash = 1;
|
||||||
|
header->key.data = key_data;
|
||||||
|
header->key.len = key_data_size;
|
||||||
|
header->value.data = value_data;
|
||||||
|
header->value.len = value_data_size;
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Validate header modification.
|
||||||
|
/// @details Modifications come in two nodes: The first one handles the key, the other handles the value.
|
||||||
|
/// This function validates that the second node (which is the next) that holds data exists and if their
|
||||||
|
/// data is valid.
|
||||||
|
/// @param[in, out] modification
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
validate_append_header_modification(ngx_http_cp_modification_list *modification)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Validating append header modification data");
|
||||||
|
|
||||||
|
if (modification->next == NULL) {
|
||||||
|
// Modification data value for the provided modification is missing.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Error: Append header modification is missing modification data for value");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
if (!modification->modification.is_header || !modification->next->modification.is_header) {
|
||||||
|
// Modification key or value are not of type header and therefor aren't a proper header modification.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Error: Append header modification is missing modification data for %s",
|
||||||
|
!modification->modification.is_header ? "key" : "value"
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Append header modification data is valid");
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Append modification into the headers list.
|
||||||
|
/// @param[in, out] headers_list NGINX list.
|
||||||
|
/// @param[in, out] modification modification to append into the header list.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
append_header(ngx_list_t *headers_list, ngx_http_cp_modification_list *modification)
|
||||||
|
{
|
||||||
|
ngx_http_cp_modification_list *key_modification = modification;
|
||||||
|
ngx_http_cp_modification_list *value_modification = modification->next;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Appending header: '%s' (size: %ui) : '%s' (size: %ui)",
|
||||||
|
key_modification->modification_buffer,
|
||||||
|
key_modification->modification.injection_size,
|
||||||
|
value_modification->modification_buffer,
|
||||||
|
value_modification->modification.injection_size
|
||||||
|
);
|
||||||
|
// Appending the header.
|
||||||
|
ngx_table_elt_t *new_header = push_header_to_list(
|
||||||
|
headers_list,
|
||||||
|
key_modification->modification.injection_size,
|
||||||
|
(u_char *)key_modification->modification_buffer,
|
||||||
|
value_modification->modification.injection_size,
|
||||||
|
(u_char *)value_modification->modification_buffer
|
||||||
|
);
|
||||||
|
if (new_header == NULL) return NGX_ERROR;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Successfully appended header. Key: '%.*s' (size: %u), value: '%.*s' (size: %u)",
|
||||||
|
new_header->key.len,
|
||||||
|
new_header->key.data,
|
||||||
|
new_header->key.len,
|
||||||
|
new_header->value.len,
|
||||||
|
new_header->value.data,
|
||||||
|
new_header->value.len
|
||||||
|
);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Modifies a header using the modification list.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in, out] headers headers NGINX list.
|
||||||
|
/// @param[in, out] headers_iterator NGINX CP header iterator.
|
||||||
|
/// @param[in, out] modification Modifications list.
|
||||||
|
/// @param[in] type Modification type.
|
||||||
|
/// @param[in] is_content_length A flag if the header is content length.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
perform_header_modification(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_list_t *headers,
|
||||||
|
ngx_http_cp_list_iterator *headers_iterator,
|
||||||
|
ngx_http_cp_modification_list *modification,
|
||||||
|
ngx_http_modification_type_e type,
|
||||||
|
ngx_flag_t is_content_length
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_table_elt_t *injected_header;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case APPEND: {
|
||||||
|
// Appends a modification into the header.
|
||||||
|
if (append_header(headers, modification) != NGX_OK) {
|
||||||
|
// Failed to append the modification.
|
||||||
|
write_dbg(DBG_LEVEL_ERROR, "Failed to append header");
|
||||||
|
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INJECT: {
|
||||||
|
// Injects a modification into the header.
|
||||||
|
injected_header = get_list_element(headers_iterator, modification->modification.orig_buff_index);
|
||||||
|
if (injected_header == NULL) {
|
||||||
|
// No header found with the index.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_ASSERT,
|
||||||
|
"No header found with index %ui",
|
||||||
|
modification->modification.orig_buff_index
|
||||||
|
)
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inject_header_value(injected_header, modification, headers->pool) != NGX_OK) {
|
||||||
|
// Failed to inject a header value.
|
||||||
|
write_dbg(DBG_LEVEL_ERROR, "Failed to inject header value");
|
||||||
|
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REPLACE: {
|
||||||
|
if (is_content_length == 1) {
|
||||||
|
// Replacing Content-Length.
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Content-Length will be replaced")
|
||||||
|
ngx_http_clear_content_length(request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Failed to get a known modificatino type.
|
||||||
|
write_dbg(DBG_LEVEL_ASSERT, "Unknown modification type: %d", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the next modification from modification list.
|
||||||
|
/// @param[in, out] modification Modification list to get the next header from.
|
||||||
|
/// @param[in, out] type Modification type.
|
||||||
|
/// @return ngx_http_cp_modification_list
|
||||||
|
/// - #ngx_http_cp_modification_list pointer to the next element.
|
||||||
|
/// - #NULL if failed to get the next element.
|
||||||
|
///
|
||||||
|
static ngx_http_cp_modification_list *
|
||||||
|
get_next_header_modification(ngx_http_cp_modification_list *modification, ngx_http_modification_type_e type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case APPEND:
|
||||||
|
return modification->next->next;
|
||||||
|
case INJECT:
|
||||||
|
case REPLACE:
|
||||||
|
return modification->next;
|
||||||
|
default:
|
||||||
|
write_dbg(DBG_LEVEL_ASSERT, "Unknown modification type: %d", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Free modifications.
|
||||||
|
/// @param[in, out] modification Modification to free.
|
||||||
|
/// @param[in, out] type Modification type.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
free_header_modification(
|
||||||
|
ngx_http_cp_modification_list *modification,
|
||||||
|
ngx_http_modification_type_e type,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Freeing header modification");
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case APPEND: {
|
||||||
|
ngx_pfree(pool, modification->next->modification_buffer);
|
||||||
|
ngx_pfree(pool, modification->next);
|
||||||
|
ngx_pfree(pool, modification->modification_buffer);
|
||||||
|
ngx_pfree(pool, modification);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INJECT:
|
||||||
|
case REPLACE: {
|
||||||
|
ngx_pfree(pool, modification->modification_buffer);
|
||||||
|
ngx_pfree(pool, modification);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
write_dbg(DBG_LEVEL_ASSERT, "Unknown modification type: %d", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_header_modifier(
|
||||||
|
ngx_list_t *headers,
|
||||||
|
ngx_http_cp_modification_list *modifications,
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_flag_t is_content_length
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_http_modification_type_e type;
|
||||||
|
ngx_http_cp_modification_list *next_modification;
|
||||||
|
ngx_http_cp_list_iterator headers_iterator;
|
||||||
|
init_list_iterator(headers, &headers_iterator);
|
||||||
|
|
||||||
|
while (modifications != NULL) {
|
||||||
|
// Check if modification is a header.
|
||||||
|
if (!modifications->modification.is_header) return NGX_OK;
|
||||||
|
|
||||||
|
type = modifications->modification.mod_type;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
type == APPEND ?
|
||||||
|
"Appending header" :
|
||||||
|
type == REPLACE ?
|
||||||
|
"Changing header's value" :
|
||||||
|
"Injecting into header's value");
|
||||||
|
|
||||||
|
if (type == APPEND && validate_append_header_modification(modifications) != NGX_OK) {
|
||||||
|
// Modification is not of a valid append type.
|
||||||
|
free_modifications_list(modifications, request->pool);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perform_header_modification(
|
||||||
|
request,
|
||||||
|
headers,
|
||||||
|
&headers_iterator,
|
||||||
|
modifications,
|
||||||
|
type,
|
||||||
|
is_content_length
|
||||||
|
) != NGX_OK) {
|
||||||
|
// Failed to perform modification on header
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to perform modification on header");
|
||||||
|
|
||||||
|
free_modifications_list(modifications, request->pool);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
next_modification = get_next_header_modification(modifications, type);
|
||||||
|
free_header_modification(modifications, type, request->pool);
|
||||||
|
modifications = next_modification;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_body_modifier(
|
||||||
|
ngx_chain_t *body_chain,
|
||||||
|
ngx_http_cp_modification_list *curr_modification,
|
||||||
|
ngx_pool_t *pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_http_cp_modification_list *next_modification;
|
||||||
|
ngx_uint_t cur_body_chunk = 0;
|
||||||
|
ngx_chain_t *chain_iter;
|
||||||
|
ngx_chain_t *injected_chain_elem;
|
||||||
|
ngx_uint_t num_appended_elements;
|
||||||
|
size_t cur_chunk_size = 0;
|
||||||
|
|
||||||
|
for (chain_iter = body_chain; chain_iter; chain_iter = chain_iter->next, cur_body_chunk++) {
|
||||||
|
// Iterates of the body chains
|
||||||
|
if (curr_modification == NULL) return NGX_OK;
|
||||||
|
if (curr_modification->modification.orig_buff_index != cur_body_chunk) continue;
|
||||||
|
|
||||||
|
cur_chunk_size = body_chain->buf->last - body_chain->buf->pos;
|
||||||
|
if (cur_chunk_size == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "No need to modify body chunk of size 0. Chunk index: %d", cur_body_chunk);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Handling current modification. "
|
||||||
|
"Injection position: %d, injection size: %d, original buffer index: %d, modification buffer: %s",
|
||||||
|
curr_modification->modification.injection_pos,
|
||||||
|
curr_modification->modification.injection_size,
|
||||||
|
curr_modification->modification.orig_buff_index,
|
||||||
|
curr_modification->modification_buffer
|
||||||
|
);
|
||||||
|
// Create a chain element.
|
||||||
|
injected_chain_elem = create_chain_elem(
|
||||||
|
curr_modification->modification.injection_size,
|
||||||
|
curr_modification->modification_buffer,
|
||||||
|
pool
|
||||||
|
);
|
||||||
|
|
||||||
|
if (injected_chain_elem == NULL) {
|
||||||
|
free_modifications_list(curr_modification, pool);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Handling modification of chain element number %d", cur_body_chunk);
|
||||||
|
// Handling modification of a chain element.
|
||||||
|
if (curr_modification->modification.injection_pos == 0) {
|
||||||
|
// Pre appends chain element.
|
||||||
|
prepend_chain_elem(chain_iter, injected_chain_elem);
|
||||||
|
chain_iter = chain_iter->next;
|
||||||
|
num_appended_elements = 0;
|
||||||
|
} else if (curr_modification->modification.injection_pos == chain_iter->buf->last - chain_iter->buf->pos + 1) {
|
||||||
|
// Prepend a chain element.
|
||||||
|
append_chain_elem(chain_iter, injected_chain_elem);
|
||||||
|
chain_iter = chain_iter->next;
|
||||||
|
num_appended_elements = 1;
|
||||||
|
} else {
|
||||||
|
if (split_chain_elem(chain_iter, curr_modification->modification.injection_pos, pool) != NGX_OK) {
|
||||||
|
// Failed to iterate over the modification.
|
||||||
|
free_modifications_list(curr_modification, pool);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
append_chain_elem(chain_iter, injected_chain_elem);
|
||||||
|
chain_iter = chain_iter->next->next;
|
||||||
|
num_appended_elements = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves to the next modification element and frees the modifier.
|
||||||
|
next_modification = curr_modification->next;
|
||||||
|
ngx_pfree(pool, curr_modification);
|
||||||
|
curr_modification = next_modification;
|
||||||
|
|
||||||
|
cur_body_chunk += num_appended_elements;
|
||||||
|
}
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
120
attachments/nginx/ngx_module/ngx_cp_custom_response.h
Normal file
120
attachments/nginx/ngx_module/ngx_cp_custom_response.h
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_custom_response.h
|
||||||
|
#ifndef __NGX_CP_CUSTOM_RESPONSE_H__
|
||||||
|
#define __NGX_CP_CUSTOM_RESPONSE_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
|
||||||
|
/// @struct ngx_http_cp_modification_list
|
||||||
|
/// @brief A node that holds all the information regarding modifications.
|
||||||
|
typedef struct ngx_http_cp_modification_list {
|
||||||
|
struct ngx_http_cp_modification_list *next; ///< Next node.
|
||||||
|
ngx_http_cp_inject_data_t modification; ///< Modification data.
|
||||||
|
char *modification_buffer; ///< Modification buffer used to store extra needed data.
|
||||||
|
} ngx_http_cp_modification_list;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Generates and sends a response headers.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in] response_code Response code.
|
||||||
|
/// @param[in] content_length Content length.
|
||||||
|
/// @param[in] last_modified_time Last modification time.
|
||||||
|
/// @param[in] allow_ranges Allowed ranges.
|
||||||
|
/// @param[in] keepalive Keep alive metadata.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_response_headers_sender(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
const ngx_uint_t response_code,
|
||||||
|
const off_t content_length,
|
||||||
|
const time_t last_modified_time,
|
||||||
|
const unsigned int allow_ranges,
|
||||||
|
const unsigned int keepalive
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Generates and sends a response file.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in, out] file_path NGINX string.
|
||||||
|
/// @param[in, out] open_file_info NGINX file info - file information.
|
||||||
|
/// @param[in] is_main_request Flags if the file is the main request.
|
||||||
|
/// @param[in] log NGINX log.
|
||||||
|
/// @param[in] memory_pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_file_response_sender(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_str_t *file_path,
|
||||||
|
ngx_open_file_info_t *open_file_info,
|
||||||
|
ngx_int_t is_main_request,
|
||||||
|
ngx_log_t *log,
|
||||||
|
ngx_pool_t *memory_pool
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Finalizing a rejected request.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Modifies headers with the provided modifiers.
|
||||||
|
/// @param[in, out] headers NGINX headers list.
|
||||||
|
/// @param[in] modifications CP modification list.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in] is_content_length Flag that signals if the header is of content length.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_header_modifier(
|
||||||
|
ngx_list_t *headers,
|
||||||
|
ngx_http_cp_modification_list *modifications,
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_flag_t is_content_length
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Modifies body chain with the provided modifiers.
|
||||||
|
/// @param[in, out] body_chain NGINX body chain.
|
||||||
|
/// @param[in] modifications CP modification list.
|
||||||
|
/// @param[in, out] modification_pool NGINX pool for modifications.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_body_modifier(
|
||||||
|
ngx_chain_t *body_chain,
|
||||||
|
ngx_http_cp_modification_list *modifications,
|
||||||
|
ngx_pool_t *modification_pool
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_CUSTOM_RESPONSE_H__
|
||||||
238
attachments/nginx/ngx_module/ngx_cp_failing_state.c
Normal file
238
attachments/nginx/ngx_module/ngx_cp_failing_state.c
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_failing_state.c
|
||||||
|
#include "ngx_cp_failing_state.h"
|
||||||
|
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_initializer.h"
|
||||||
|
#include "ngx_cp_metric.h"
|
||||||
|
|
||||||
|
#define FIVE_ERRORS_PER_FIFTEEN_SECONDS 0
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @struct failure_state
|
||||||
|
/// @brief Holds failure state data.
|
||||||
|
///
|
||||||
|
typedef struct failure_state {
|
||||||
|
ngx_uint_t max_allowed_failed_requests; ///< Maximum allowed failed requests.
|
||||||
|
ngx_uint_t failing_interval_boundry; ///< Intervals between each failure.
|
||||||
|
ngx_uint_t transparent_period_sec; ///< Transperancy mode period.
|
||||||
|
} failure_state;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @struct ngx_http_cp_periodic_failure
|
||||||
|
/// @brief Holds NGINX periodic failure data.
|
||||||
|
///
|
||||||
|
typedef struct ngx_http_cp_periodic_failure {
|
||||||
|
ngx_uint_t max_allowed_failed_requests; ///< Maximum allowed failed requests.
|
||||||
|
ngx_uint_t current_failed_requests; ///< Current failed requests.
|
||||||
|
ngx_flag_t is_transparent_mode_active; ///< Transparent mode flag.
|
||||||
|
ngx_uint_t transparent_interval_boundry; ///< Transparent internval boundary.
|
||||||
|
struct timeval transparent_interval; ///< Transparent time interval.
|
||||||
|
struct timeval failing_interval; ///< Falling data time interval.
|
||||||
|
} ngx_http_cp_periodic_failure;
|
||||||
|
|
||||||
|
/// Failure state session monitors.
|
||||||
|
static const failure_state failed_sessions_monitor[] = {{5, 20, 60}, {5, 20, 300}, {5, 20, 600}};
|
||||||
|
static const ngx_uint_t failed_sessions_monitor_length = 3;
|
||||||
|
|
||||||
|
static ngx_http_cp_periodic_failure current_periodic_failure = {
|
||||||
|
.max_allowed_failed_requests = 0,
|
||||||
|
.current_failed_requests = 0,
|
||||||
|
.is_transparent_mode_active = 0,
|
||||||
|
.transparent_interval_boundry = 0,
|
||||||
|
.transparent_interval = {0, 0},
|
||||||
|
.failing_interval = {0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ngx_uint_t current_fail_state = FIVE_ERRORS_PER_FIFTEEN_SECONDS;
|
||||||
|
static ngx_flag_t should_update_timeout = 0;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Resetting current failure state.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
reset_failure_state()
|
||||||
|
{
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Resetting failing interval to default. Interval: %u seconds",
|
||||||
|
failed_sessions_monitor[current_fail_state].failing_interval_boundry
|
||||||
|
);
|
||||||
|
|
||||||
|
current_periodic_failure.max_allowed_failed_requests =
|
||||||
|
failed_sessions_monitor[current_fail_state].max_allowed_failed_requests;
|
||||||
|
|
||||||
|
current_periodic_failure.failing_interval =
|
||||||
|
get_timeout_val_sec(failed_sessions_monitor[current_fail_state].failing_interval_boundry);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
is_in_transparent_mode()
|
||||||
|
{
|
||||||
|
static int transparent_mode_is_active = 0;
|
||||||
|
if (current_periodic_failure.is_transparent_mode_active && !is_timeout_reached(¤t_periodic_failure.transparent_interval)) {
|
||||||
|
if (!transparent_mode_is_active) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_INFO,
|
||||||
|
"NGINX is in transparent mode. Transparent timeout: %u seconds",
|
||||||
|
current_periodic_failure.transparent_interval_boundry
|
||||||
|
);
|
||||||
|
|
||||||
|
updateMetricField(TOTAL_TRANSPARENTS_TIME, (uint64_t)current_periodic_failure.transparent_interval_boundry);
|
||||||
|
}
|
||||||
|
|
||||||
|
transparent_mode_is_active = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
transparent_mode_is_active = 0;
|
||||||
|
|
||||||
|
current_periodic_failure.is_transparent_mode_active = 0;
|
||||||
|
current_periodic_failure.current_failed_requests = 0;
|
||||||
|
reset_failure_state();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_transparent_mode()
|
||||||
|
{
|
||||||
|
current_fail_state = 0;
|
||||||
|
should_update_timeout = 0;
|
||||||
|
memset(¤t_periodic_failure, 0, sizeof(current_periodic_failure));
|
||||||
|
|
||||||
|
current_periodic_failure.max_allowed_failed_requests = failed_sessions_monitor[current_fail_state].max_allowed_failed_requests;
|
||||||
|
current_periodic_failure.current_failed_requests = 0;
|
||||||
|
current_periodic_failure.is_transparent_mode_active = 0;
|
||||||
|
current_periodic_failure.transparent_interval_boundry = failed_sessions_monitor[current_fail_state].transparent_period_sec;
|
||||||
|
current_periodic_failure.failing_interval = get_timeout_val_sec(failed_sessions_monitor[current_fail_state].failing_interval_boundry);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Activate transparent mode.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
activate_transparent_mode()
|
||||||
|
{
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Activating transparency mode. Transparency period: %u seconds",
|
||||||
|
failed_sessions_monitor[current_fail_state].transparent_period_sec
|
||||||
|
);
|
||||||
|
current_periodic_failure.is_transparent_mode_active = 1;
|
||||||
|
current_periodic_failure.transparent_interval = get_timeout_val_sec(failed_sessions_monitor[current_fail_state].transparent_period_sec);
|
||||||
|
current_periodic_failure.transparent_interval_boundry = failed_sessions_monitor[current_fail_state].transparent_period_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Reports the provided session's metric.
|
||||||
|
/// @param[in, out] state Session's data to report the time metric of.
|
||||||
|
///
|
||||||
|
void
|
||||||
|
ngx_http_cp_report_time_metrics(ngx_http_cp_session_data *state)
|
||||||
|
{
|
||||||
|
struct timespec session_end_time;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &session_end_time);
|
||||||
|
|
||||||
|
double begin_usec = (state->session_start_time.tv_sec * 1000000) + (state->session_start_time.tv_nsec / 1000);
|
||||||
|
double end_usec = (session_end_time.tv_sec * 1000000) + (session_end_time.tv_nsec / 1000);
|
||||||
|
double overall_process_time = end_usec - begin_usec;
|
||||||
|
|
||||||
|
updateMetricField(AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT, overall_process_time);
|
||||||
|
updateMetricField(MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT, overall_process_time);
|
||||||
|
updateMetricField(MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT, overall_process_time);
|
||||||
|
|
||||||
|
updateMetricField(AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT, state->req_proccesing_time);
|
||||||
|
updateMetricField(MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT, state->req_proccesing_time);
|
||||||
|
updateMetricField(MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT, state->req_proccesing_time);
|
||||||
|
|
||||||
|
updateMetricField(AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT, state->res_proccesing_time);
|
||||||
|
updateMetricField(MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT, state->res_proccesing_time);
|
||||||
|
updateMetricField(MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT, state->res_proccesing_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_inspection_failure(int weight, ngx_int_t fail_mode_verdict, ngx_http_cp_session_data *state)
|
||||||
|
{
|
||||||
|
if (state->verdict != TRAFFIC_VERDICT_INSPECT) {
|
||||||
|
// Skipping already inspected data.
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Skipping already inspected data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_http_cp_report_time_metrics(state);
|
||||||
|
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
// Increase open fail counter.
|
||||||
|
updateMetricField(INSPECTION_OPEN_FAILURES_COUNT, 1);
|
||||||
|
state->verdict = TRAFFIC_VERDICT_ACCEPT;
|
||||||
|
} else {
|
||||||
|
// Increase close fail counter.
|
||||||
|
updateMetricField(INSPECTION_CLOSE_FAILURES_COUNT, 1);
|
||||||
|
state->verdict = TRAFFIC_VERDICT_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_periodic_failure.current_failed_requests += weight;
|
||||||
|
|
||||||
|
if (is_timeout_reached(¤t_periodic_failure.failing_interval)) {
|
||||||
|
// Enough time had passed without errors. Limits can be reset
|
||||||
|
reset_failure_state();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_periodic_failure.current_failed_requests <= current_periodic_failure.max_allowed_failed_requests) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Failure count did not reach maximum allowed limit. Current failure count: %u, Max failure limit: %u",
|
||||||
|
current_periodic_failure.current_failed_requests,
|
||||||
|
current_periodic_failure.max_allowed_failed_requests
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activate_transparent_mode();
|
||||||
|
|
||||||
|
if (current_fail_state < failed_sessions_monitor_length) {
|
||||||
|
// Setting new transparent interval.
|
||||||
|
current_fail_state++;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Setting new transparent interval. New interval: %u seconds",
|
||||||
|
failed_sessions_monitor[current_fail_state].transparent_period_sec
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Reached impossible fail state, setting highest level of state.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Reached impossible fail state - Setting highest level instead. Current fail state: %u, New fail state: %u",
|
||||||
|
current_fail_state,
|
||||||
|
failed_sessions_monitor_length - 1
|
||||||
|
);
|
||||||
|
current_fail_state = failed_sessions_monitor_length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_inspection_success(ngx_http_cp_session_data *state)
|
||||||
|
{
|
||||||
|
updateMetricField(INSPECTION_SUCCESSES_COUNT, 1);
|
||||||
|
ngx_http_cp_report_time_metrics(state);
|
||||||
|
if (!is_timeout_reached(¤t_periodic_failure.failing_interval)) return;
|
||||||
|
if (current_periodic_failure.current_failed_requests != 0 || should_update_timeout == 1) return;
|
||||||
|
|
||||||
|
current_fail_state = 0;
|
||||||
|
}
|
||||||
59
attachments/nginx/ngx_module/ngx_cp_failing_state.h
Normal file
59
attachments/nginx/ngx_module/ngx_cp_failing_state.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_failing_state.h
|
||||||
|
#ifndef __NGX_CP_FAILING_STATE_H__
|
||||||
|
#define __NGX_CP_FAILING_STATE_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
#include "ngx_cp_hooks.h"
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Returns if transparent mode is activated.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #0 if transparent mode is off.
|
||||||
|
/// - #1 if transparent mode is on.
|
||||||
|
///
|
||||||
|
ngx_int_t is_in_transparent_mode(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Handles inspection failure.
|
||||||
|
/// @details Updates metric fields with the provided data.
|
||||||
|
/// Metric fields included such as:
|
||||||
|
/// - #INSPECTION_OPEN_FAILURES_COUNT
|
||||||
|
/// - #INSPECTION_CLOSE_FAILURES_COUNT
|
||||||
|
/// @param[in, out] weight Failure's weight.
|
||||||
|
/// @param[in, out] fail_mode_verdict Fail mode verdict.
|
||||||
|
/// @param[in, out] state NGINX session data.
|
||||||
|
///
|
||||||
|
void handle_inspection_failure(int weight, ngx_int_t fail_mode_verdict, ngx_http_cp_session_data *state);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Handles inspection success.
|
||||||
|
/// @details The function updates "INSPECTION_SUCCESSES_COUNT" metric.
|
||||||
|
/// Furthermore the function updates time verdicts metrics.
|
||||||
|
/// @param[in, out] state NGINX session data.
|
||||||
|
///
|
||||||
|
void handle_inspection_success(ngx_http_cp_session_data *state);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Resets transparent mode.
|
||||||
|
/// @details Reset transparent mode to 0, and all the related parameters.
|
||||||
|
///
|
||||||
|
void reset_transparent_mode(void);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_FAILING_STATE_H__
|
||||||
453
attachments/nginx/ngx_module/ngx_cp_hook_threads.c
Normal file
453
attachments/nginx/ngx_module/ngx_cp_hook_threads.c
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_hook_threads.c
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
#include <ngx_files.h>
|
||||||
|
#include <ngx_string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_util.h"
|
||||||
|
#include "shmem_ipc.h"
|
||||||
|
#include "compression_utils.h"
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
#include "ngx_cp_io.h"
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_initializer.h"
|
||||||
|
#include "ngx_http_cp_attachment_module.h"
|
||||||
|
#include "ngx_cp_static_content.h"
|
||||||
|
#include "ngx_cp_compression.h"
|
||||||
|
#include "ngx_cp_http_parser.h"
|
||||||
|
#include "ngx_cp_hook_threads.h"
|
||||||
|
#include "ngx_cp_failing_state.h"
|
||||||
|
#include "ngx_cp_metric.h"
|
||||||
|
#include "ngx_cp_thread.h"
|
||||||
|
#include "ngx_cp_hooks.h"
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief THREAD_CTX_RETURN, sets this session's context to _X value and set the context
|
||||||
|
/// to return without going to the next filter.
|
||||||
|
///
|
||||||
|
#define THREAD_CTX_RETURN(_X) do { ctx->res = (_X); ctx->should_return = 1; return NULL; } while (0);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief THREAD_CTX_RETURN_NEXT_FILTER, sets this session's context to _X value and set the context
|
||||||
|
/// to be scanned by the next filter.
|
||||||
|
///
|
||||||
|
#define THREAD_CTX_RETURN_NEXT_FILTER() do { ctx->should_return_next_filter = 1; return NULL; } while (0);
|
||||||
|
|
||||||
|
static ngx_int_t already_registered = 0; ///< Registration status with the nano service.
|
||||||
|
static const ngx_int_t inspection_irrelevant = INSPECTION_IRRELEVANT;
|
||||||
|
extern struct timeval metric_timeout; ///< Holds per-session metric timeout.
|
||||||
|
|
||||||
|
void
|
||||||
|
set_already_registered(ngx_int_t value)
|
||||||
|
{
|
||||||
|
already_registered = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
get_already_registered()
|
||||||
|
{
|
||||||
|
return already_registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init_thread_ctx(
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx,
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_http_cp_session_data *session_data_p,
|
||||||
|
ngx_chain_t *chain)
|
||||||
|
{
|
||||||
|
ctx->request = request;
|
||||||
|
ctx->session_data_p = session_data_p;
|
||||||
|
ctx->res = NGX_OK;
|
||||||
|
ctx->should_return = 0;
|
||||||
|
ctx->should_return_next_filter = 0;
|
||||||
|
ctx->chain = chain;
|
||||||
|
ctx->modifications = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ngx_http_cp_registration_thread(void *_ctx)
|
||||||
|
{
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||||
|
ngx_int_t res = ngx_cp_attachment_init_process();
|
||||||
|
if (res == NGX_ABORT && already_registered) {
|
||||||
|
already_registered = 0;
|
||||||
|
disconnect_communication();
|
||||||
|
reset_transparent_mode();
|
||||||
|
}
|
||||||
|
if (res != NGX_OK) {
|
||||||
|
// failed to register to the attachment service.
|
||||||
|
if (already_registered) handle_inspection_failure(registration_failure_weight, fail_mode_verdict, ctx->session_data_p);
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Communication with nano service is not ready yet");
|
||||||
|
THREAD_CTX_RETURN(NGX_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends end request header to the attachment's service.
|
||||||
|
/// @details Communicates with the attachment service by sending end request header
|
||||||
|
/// to the attachment's service and returns verdict.
|
||||||
|
/// @param[in, out] session_data_p If the function returns NGX_OK, session data will be modified.
|
||||||
|
/// @param[in, out] request NGINX Request.
|
||||||
|
/// @param[in] modifications A list of this session's data modifications.
|
||||||
|
/// @return ngx_int_t of the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
end_req_header_handler(
|
||||||
|
ngx_http_cp_session_data *session_data_p,
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_http_cp_modification_list **modifications)
|
||||||
|
{
|
||||||
|
ngx_uint_t num_messages_sent = 0;
|
||||||
|
|
||||||
|
if (!does_contain_body(&(request->headers_in))) {
|
||||||
|
if (ngx_http_cp_end_transaction_sender(REQUEST_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send request end data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
return fail_mode_verdict;
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
}
|
||||||
|
// Fetch nano services' results.
|
||||||
|
return ngx_http_cp_reply_receiver(
|
||||||
|
&session_data_p->remaining_messages_to_reply,
|
||||||
|
&session_data_p->verdict,
|
||||||
|
session_data_p->session_id,
|
||||||
|
request,
|
||||||
|
modifications
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
does_contain_body(ngx_http_headers_in_t *headers)
|
||||||
|
{
|
||||||
|
return headers->chunked || (headers->content_length_n != (off_t)(-1) && headers->content_length_n > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ngx_http_cp_req_header_handler_thread(void *_ctx)
|
||||||
|
{
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||||
|
ngx_http_request_t *request = ctx->request;
|
||||||
|
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||||
|
ngx_int_t send_meta_data_result;
|
||||||
|
ngx_uint_t num_messages_sent = 0;
|
||||||
|
ngx_int_t send_header_result;
|
||||||
|
|
||||||
|
send_meta_data_result = ngx_http_cp_meta_data_sender(request, session_data_p->session_id, &num_messages_sent);
|
||||||
|
if (send_meta_data_result == inspection_irrelevant) {
|
||||||
|
// Ignoring irrelevant requests.
|
||||||
|
session_data_p->verdict = TRAFFIC_VERDICT_IRRELEVANT;
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Ignoring non-interesting request. Session ID: %d", session_data_p->session_id);
|
||||||
|
THREAD_CTX_RETURN(NGX_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send_meta_data_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send request meta data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
THREAD_CTX_RETURN(fail_mode_verdict);
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
if (is_timeout_reached(&metric_timeout)) {
|
||||||
|
// Thread task was timed out.
|
||||||
|
set_metric_cpu_usage();
|
||||||
|
set_metric_memory_usage();
|
||||||
|
if (ngx_http_cp_metric_data_sender() != NGX_OK) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Failed to send metric data from the plugin to the service");
|
||||||
|
}
|
||||||
|
metric_timeout = get_timeout_val_sec(METRIC_TIMEOUT_VAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
num_messages_sent = 0;
|
||||||
|
send_header_result = ngx_http_cp_header_sender(
|
||||||
|
&(request->headers_in.headers.part),
|
||||||
|
REQUEST_HEADER,
|
||||||
|
session_data_p->session_id,
|
||||||
|
&num_messages_sent
|
||||||
|
);
|
||||||
|
if (send_header_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send request headers to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
THREAD_CTX_RETURN(fail_mode_verdict);
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
// Notify the nano service that we've reached the end of the request headers.
|
||||||
|
ctx->res = end_req_header_handler(session_data_p, request, &ctx->modifications);
|
||||||
|
|
||||||
|
// The caller function will continue and apply the modified ctx->modifications
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ngx_http_cp_req_body_filter_thread(void *_ctx)
|
||||||
|
{
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||||
|
ngx_http_request_t *request = ctx->request;
|
||||||
|
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||||
|
ngx_int_t is_last_part;
|
||||||
|
ngx_int_t send_body_result;
|
||||||
|
ngx_uint_t num_messages_sent = 0;
|
||||||
|
|
||||||
|
send_body_result = ngx_http_cp_body_sender(
|
||||||
|
ctx->chain,
|
||||||
|
REQUEST_BODY,
|
||||||
|
session_data_p,
|
||||||
|
&is_last_part,
|
||||||
|
&num_messages_sent,
|
||||||
|
&ctx->chain
|
||||||
|
);
|
||||||
|
|
||||||
|
if (send_body_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send request body data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
num_messages_sent = 0;
|
||||||
|
if (is_last_part) {
|
||||||
|
// Signals the nano service that the transaction reached the end.
|
||||||
|
if (ngx_http_cp_end_transaction_sender(REQUEST_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send request end data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch nano services' results.
|
||||||
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
|
&session_data_p->remaining_messages_to_reply,
|
||||||
|
&session_data_p->verdict,
|
||||||
|
session_data_p->session_id,
|
||||||
|
request,
|
||||||
|
&ctx->modifications
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is_last_part) session_data_p->was_request_fully_inspected = 1;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ngx_http_cp_res_header_filter_thread(void *_ctx)
|
||||||
|
{
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||||
|
ngx_http_request_t *request = ctx->request;
|
||||||
|
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||||
|
ngx_int_t set_response_content_encoding_res;
|
||||||
|
ngx_int_t send_res_code_result;
|
||||||
|
ngx_int_t send_content_length_result;
|
||||||
|
ngx_int_t send_header_result;
|
||||||
|
ngx_uint_t num_messages_sent = 0;
|
||||||
|
|
||||||
|
// Sends response code to the nano service.
|
||||||
|
send_res_code_result = ngx_http_cp_res_code_sender(
|
||||||
|
request->headers_out.status,
|
||||||
|
session_data_p->session_id,
|
||||||
|
&num_messages_sent
|
||||||
|
);
|
||||||
|
if (send_res_code_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send response meta data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
num_messages_sent = 0;
|
||||||
|
|
||||||
|
// Sends response content length to the nano service.
|
||||||
|
send_content_length_result = ngx_http_cp_content_length_sender(
|
||||||
|
request->headers_out.content_length_n,
|
||||||
|
session_data_p->session_id,
|
||||||
|
&num_messages_sent
|
||||||
|
);
|
||||||
|
if (send_content_length_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send headers content length to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
// Sets response body's content encoding.
|
||||||
|
set_response_content_encoding_res = set_response_content_encoding(
|
||||||
|
&session_data_p->response_data.original_compression_type,
|
||||||
|
request->headers_out.content_encoding
|
||||||
|
);
|
||||||
|
if (set_response_content_encoding_res != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to set response body's content encoding. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
session_data_p->response_data.new_compression_type = session_data_p->response_data.original_compression_type;
|
||||||
|
|
||||||
|
// Sends response headers to the nano service.
|
||||||
|
num_messages_sent = 0;
|
||||||
|
send_header_result = ngx_http_cp_header_sender(
|
||||||
|
&request->headers_out.headers.part,
|
||||||
|
RESPONSE_HEADER,
|
||||||
|
session_data_p->session_id,
|
||||||
|
&num_messages_sent
|
||||||
|
);
|
||||||
|
if (send_header_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send response headers to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
// Fetch nano services' results.
|
||||||
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
|
&session_data_p->remaining_messages_to_reply,
|
||||||
|
&session_data_p->verdict,
|
||||||
|
session_data_p->session_id,
|
||||||
|
request,
|
||||||
|
&ctx->modifications
|
||||||
|
);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ngx_http_cp_res_body_filter_thread(void *_ctx)
|
||||||
|
{
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||||
|
ngx_http_request_t *request = ctx->request;
|
||||||
|
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||||
|
ngx_int_t send_body_result;
|
||||||
|
ngx_uint_t num_messages_sent = 0;
|
||||||
|
ngx_int_t is_last_response_part = 0;
|
||||||
|
|
||||||
|
// Send response body data to the nano service.
|
||||||
|
send_body_result = ngx_http_cp_body_sender(
|
||||||
|
ctx->chain,
|
||||||
|
RESPONSE_BODY,
|
||||||
|
session_data_p,
|
||||||
|
&is_last_response_part,
|
||||||
|
&num_messages_sent,
|
||||||
|
&ctx->chain
|
||||||
|
);
|
||||||
|
if (send_body_result != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send response body data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
num_messages_sent = 0;
|
||||||
|
if (is_last_response_part) {
|
||||||
|
// Signals the nano service that the transaction reached the end.
|
||||||
|
if (ngx_http_cp_end_transaction_sender(RESPONSE_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send response end data to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_ERROR);
|
||||||
|
}
|
||||||
|
session_data_p->remaining_messages_to_reply++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch nano services' results.
|
||||||
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
|
&session_data_p->remaining_messages_to_reply,
|
||||||
|
&session_data_p->verdict,
|
||||||
|
session_data_p->session_id,
|
||||||
|
request,
|
||||||
|
&ctx->modifications
|
||||||
|
);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
157
attachments/nginx/ngx_module/ngx_cp_hook_threads.h
Normal file
157
attachments/nginx/ngx_module/ngx_cp_hook_threads.h
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_hook_threads.h
|
||||||
|
#ifndef __NGX_CP_HOOK_THREADS_H__
|
||||||
|
#define __NGX_CP_HOOK_THREADS_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
typedef struct ngx_http_cp_session_data ngx_http_cp_session_data; ///< Holds per-session data.
|
||||||
|
typedef struct ngx_http_cp_modification_list ngx_http_cp_modification_list; ///< Holds per-session data modifications.
|
||||||
|
|
||||||
|
/// @struct ngx_http_cp_event_thread_ctx_t
|
||||||
|
/// @brief Holds all the information needed to communicate with the attachment service.
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t
|
||||||
|
{
|
||||||
|
ngx_http_request_t *request; ///< NGINX request.
|
||||||
|
ngx_http_cp_session_data *session_data_p; ///< Provided session data.
|
||||||
|
ngx_chain_t *chain; ///< only relevant to body filters
|
||||||
|
|
||||||
|
/// Connection results with the attachment service
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
ngx_int_t res;
|
||||||
|
|
||||||
|
/// Sets if the context should return and not continue to the next filter.
|
||||||
|
int should_return;
|
||||||
|
|
||||||
|
/// Should context continue to the next filter.
|
||||||
|
int should_return_next_filter;
|
||||||
|
|
||||||
|
ngx_http_cp_modification_list *modifications; ///< Context's modification.
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Modifies already_registered value.
|
||||||
|
/// already_registered is a value that symbolize a successful registeration of the thread context with the nano service.
|
||||||
|
/// @param[in] value set registration with the nano service:
|
||||||
|
/// - #0 Thread is not registered with the nano service.
|
||||||
|
/// - #1 Thread is registered with the nano service.
|
||||||
|
/// @returns current registration status.
|
||||||
|
///
|
||||||
|
void set_already_registered(ngx_int_t value);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Returns already_registered value.
|
||||||
|
/// already_registered is a value that symbolize a successful registeration of the thread context with the nano service.
|
||||||
|
/// @returns ngx_in_t get registration value with the nano service:
|
||||||
|
/// - #0 Thread is not registered with the nano service.
|
||||||
|
/// - #1 Thread is registered with the nano service.
|
||||||
|
///
|
||||||
|
ngx_int_t get_already_registered(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initates ngx_http_cp_event_thread_ctx_t struct.
|
||||||
|
/// @param[in, out] ctx struct to initiate.
|
||||||
|
/// @param[in] request
|
||||||
|
/// @param[in] session_data_p
|
||||||
|
/// @param[in] chain
|
||||||
|
/// Modifies _ctx res to the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// @return NULL.
|
||||||
|
///
|
||||||
|
void
|
||||||
|
init_thread_ctx(
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx,
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_http_cp_session_data *session_data_p,
|
||||||
|
ngx_chain_t *chain
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Registers the context against the nano agent.
|
||||||
|
/// @note _ctx needs to be properly initialized by init_thread_ctx().
|
||||||
|
/// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t.
|
||||||
|
/// Modifies _ctx res to the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// @return NULL.
|
||||||
|
///
|
||||||
|
void * ngx_http_cp_registration_thread(void *_ctx);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends request headers to the attachment's service.
|
||||||
|
/// @details Communicates with the attachment service by sending request headers to the attachment's service
|
||||||
|
/// and modifies _ctx by the received response.
|
||||||
|
/// @note _ctx needs to be properly initialized by init_thread_ctx().
|
||||||
|
/// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t.
|
||||||
|
/// Modifies _ctx res to the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// @return NULL.
|
||||||
|
///
|
||||||
|
void * ngx_http_cp_req_header_handler_thread(void *_ctx);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends request body to the attachment's service.
|
||||||
|
/// @details Communicates with the attachment service by sending request body to the attachment's service
|
||||||
|
/// and modifies _ctx by the received response.
|
||||||
|
/// @note _ctx needs to be properly initialized by init_thread_ctx() and ngx_chain_t needs of not NULL.
|
||||||
|
/// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t.
|
||||||
|
/// Modifies _ctx res to the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// @return NULL.
|
||||||
|
///
|
||||||
|
void * ngx_http_cp_req_body_filter_thread(void *_ctx);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends response headers to the attachment's service.
|
||||||
|
/// @details Communicates with the attachment service by sending response headers to the attachment's service
|
||||||
|
/// and modifies _ctx by the received response.
|
||||||
|
/// @note _ctx needs to be properly initialized by init_thread_ctx().
|
||||||
|
/// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t.
|
||||||
|
/// Modifies _ctx res to the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// @return NULL.
|
||||||
|
///
|
||||||
|
void * ngx_http_cp_res_header_filter_thread(void *_ctx);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends response body to the attachment's service.
|
||||||
|
/// @details Communicates with the attachment service by sending response bodies to the attachment's service
|
||||||
|
/// and modifies _ctx by the received response.
|
||||||
|
/// @note _ctx needs to be properly initialized by init_thread_ctx() and ngx_chain_t needs to be defined.
|
||||||
|
/// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t.
|
||||||
|
/// Modifies _ctx res to the following values:
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// @return NULL.
|
||||||
|
///
|
||||||
|
void * ngx_http_cp_res_body_filter_thread(void *_ctx);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Check if transaction contains headers.
|
||||||
|
/// @param[in] headers ngx_http_headers_in_t struct.
|
||||||
|
/// @returns 1 if the transaction contains headers, otherwise 0.
|
||||||
|
///
|
||||||
|
ngx_int_t does_contain_body(ngx_http_headers_in_t *headers);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_HOOK_THREADS_H__
|
||||||
1030
attachments/nginx/ngx_module/ngx_cp_hooks.c
Normal file
1030
attachments/nginx/ngx_module/ngx_cp_hooks.c
Normal file
File diff suppressed because it is too large
Load Diff
119
attachments/nginx/ngx_module/ngx_cp_hooks.h
Normal file
119
attachments/nginx/ngx_module/ngx_cp_hooks.h
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_hooks.h
|
||||||
|
#ifndef __NGX_CP_HOOKS_H__
|
||||||
|
#define __NGX_CP_HOOKS_H__
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ngx_cp_http_parser.h"
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
|
||||||
|
static const int registration_failure_weight = 2; ///< Registration failure weight.
|
||||||
|
static const int inspection_failure_weight = 1; ///< Inspection failure weight.
|
||||||
|
static const ngx_int_t METRIC_TIMEOUT_VAL = METRIC_PERIODIC_TIMEOUT;
|
||||||
|
|
||||||
|
/// @struct ngx_http_cp_session_data
|
||||||
|
/// @brief Holds all the session's information needed to communicate with the nano service.
|
||||||
|
/// @details Such as to save verdict and session ID between the request and the response
|
||||||
|
typedef struct ngx_http_cp_session_data {
|
||||||
|
ngx_int_t was_request_fully_inspected; ///< Holds if the request fully inspected.
|
||||||
|
ngx_http_cp_verdict_e verdict; ///< Holds the session's verdict from the Nano Service.
|
||||||
|
uint32_t session_id; ///< Current session's Id.
|
||||||
|
ngx_int_t remaining_messages_to_reply; ///< Remaining messages left for the agent to respond to.
|
||||||
|
ngx_http_response_data response_data; ///< Holds session's response data.
|
||||||
|
struct timespec session_start_time; ///< Holds session's start time.
|
||||||
|
double req_proccesing_time; ///< Holds session's request processing time.
|
||||||
|
double res_proccesing_time; ///< Holds session's response processing time.
|
||||||
|
uint64_t processed_req_body_size; ///< Holds session's request body's size.
|
||||||
|
uint64_t processed_res_body_size; ///< Holds session's response body's size'.
|
||||||
|
} ngx_http_cp_session_data;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends response body to the nano service.
|
||||||
|
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||||
|
/// to communicate the response bodies to the nano service and fetch the response back to the NGINX.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in, out] input NGINX body chain.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_HTTP_FORBIDDEN
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *input);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends request body to the nano service.
|
||||||
|
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||||
|
/// to communicate the request bodies to the nano service and fetch the response back to the NGINX.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in, out] input_body_chain NGINX body chain.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_HTTP_FORBIDDEN
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *input_body_chain);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends response headers to the nano service.
|
||||||
|
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||||
|
/// to communicate the response headers to the nano service and fetch the response back to the NGINX.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_HTTP_FORBIDDEN
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_res_header_filter(ngx_http_request_t *request);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends request headers to the nano service.
|
||||||
|
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||||
|
/// to communicate the request headers to the nano service and fetch the response back to the NGINX.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_HTTP_FORBIDDEN
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_req_header_handler(ngx_http_request_t *request);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Checks if transaction was timed out.
|
||||||
|
/// @param[in, out] ctx
|
||||||
|
/// @note ctx needs to be properly intialized.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #0 - Timed out did not occure.
|
||||||
|
/// - #1 - Timed out occured.
|
||||||
|
///
|
||||||
|
ngx_int_t was_transaction_timedout(ngx_http_cp_session_data *ctx);
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Enforces the sessions rate.
|
||||||
|
/// @returns ngx_http_cp_verdict_e
|
||||||
|
/// - #TRAFFIC_VERDICT_INSPECT
|
||||||
|
/// - #TRAFFIC_VERDICT_ACCEPT
|
||||||
|
/// - #TRAFFIC_VERDICT_DROP
|
||||||
|
///
|
||||||
|
ngx_http_cp_verdict_e enforce_sessions_rate();
|
||||||
|
|
||||||
|
#endif // __NGX_CP_HOOKS_H__
|
||||||
88
attachments/nginx/ngx_module/ngx_cp_http_parser.c
Normal file
88
attachments/nginx/ngx_module/ngx_cp_http_parser.c
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_http_parser.c
|
||||||
|
#include "ngx_cp_http_parser.h"
|
||||||
|
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
|
||||||
|
static const char *gzip_encoding_string = "gzip";
|
||||||
|
static const char *zlib_encoding_string = "deflate";
|
||||||
|
static const char *identity_encoding_string = "identity";
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
parse_content_encoding(CompressionType *response_encoding, const ngx_str_t *content_encoding_header_value)
|
||||||
|
{
|
||||||
|
if (ngx_strcmp(content_encoding_header_value->data, gzip_encoding_string) == 0) {
|
||||||
|
/// Sets GZIP encoding.
|
||||||
|
*response_encoding = GZIP;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_strcmp(content_encoding_header_value->data, zlib_encoding_string) == 0) {
|
||||||
|
/// Sets GZIP encoding.
|
||||||
|
*response_encoding = ZLIB;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_strcmp(content_encoding_header_value->data, identity_encoding_string) == 0) {
|
||||||
|
/// Sets NO_COMPRESSION encoding.
|
||||||
|
*response_encoding = NO_COMPRESSION;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Unsupported response content encoding: %.*s",
|
||||||
|
content_encoding_header_value->len,
|
||||||
|
content_encoding_header_value->data
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
set_response_content_encoding(CompressionType *content_encoding, const ngx_table_elt_t *content_encoding_header)
|
||||||
|
{
|
||||||
|
ngx_int_t parse_content_encoding_result;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Determining response body's content encoding");
|
||||||
|
|
||||||
|
if (content_encoding_header == NULL) {
|
||||||
|
*content_encoding = NO_COMPRESSION;
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Response body is not encoded");
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Detected Content-Encoding header: key: %.*s, value: %.*s",
|
||||||
|
content_encoding_header->key.len,
|
||||||
|
content_encoding_header->key.data,
|
||||||
|
content_encoding_header->value.len,
|
||||||
|
content_encoding_header->value.data
|
||||||
|
);
|
||||||
|
/// Parses content header's value into content_encoding variable.
|
||||||
|
parse_content_encoding_result = parse_content_encoding(content_encoding, &content_encoding_header->value);
|
||||||
|
if (parse_content_encoding_result != NGX_OK) return NGX_ERROR;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Parsed content encoding: %.*s",
|
||||||
|
content_encoding_header->value.len,
|
||||||
|
content_encoding_header->value.data
|
||||||
|
);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
84
attachments/nginx/ngx_module/ngx_cp_http_parser.h
Normal file
84
attachments/nginx/ngx_module/ngx_cp_http_parser.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_http_parser.h
|
||||||
|
#ifndef __NGX_CP_HTTP_PARSER_H__
|
||||||
|
#define __NGX_CP_HTTP_PARSER_H__
|
||||||
|
|
||||||
|
#include <ngx_core.h>
|
||||||
|
|
||||||
|
#include "compression_utils.h"
|
||||||
|
|
||||||
|
/// @struct ngx_http_response_data
|
||||||
|
/// Holds all the data for NGINX CP http response data.
|
||||||
|
typedef struct {
|
||||||
|
uint32_t num_body_chunk; ///< Number of body chunks.
|
||||||
|
|
||||||
|
/// NGINX Response data status
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
ngx_int_t response_data_status;
|
||||||
|
|
||||||
|
/// Original compression type, can hold the following values:
|
||||||
|
/// - #GZIP
|
||||||
|
/// - #ZLIB
|
||||||
|
CompressionType original_compression_type;
|
||||||
|
|
||||||
|
/// A new compression type, can hold the following values:
|
||||||
|
/// - #GZIP
|
||||||
|
/// - #ZLIB
|
||||||
|
CompressionType new_compression_type;
|
||||||
|
|
||||||
|
/// Compression stream
|
||||||
|
CompressionStream *compression_stream;
|
||||||
|
|
||||||
|
/// Decompression stream
|
||||||
|
CompressionStream *decompression_stream;
|
||||||
|
} ngx_http_response_data;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Parses content encoding and returns it in response_encoding.
|
||||||
|
/// @param[in, out] response_encoding Returns value of one of the supported encoding:
|
||||||
|
/// - #GZIP
|
||||||
|
/// - #ZLIB
|
||||||
|
/// - #NO_COMPRESSION
|
||||||
|
/// @param[in, out] content_encoding_header_value Encoded value.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR - Unsupported encoding.
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
parse_content_encoding(
|
||||||
|
CompressionType *response_encoding,
|
||||||
|
const ngx_str_t *content_encoding_header_value
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets the content encoding type of the provided encoding header.
|
||||||
|
/// @param[in, out] content_encoding Returns variable of one of the supported encoding:
|
||||||
|
/// - #GZIP
|
||||||
|
/// - #ZLIB
|
||||||
|
/// - #NO_COMPRESSION
|
||||||
|
/// @param[in, out] content_encoding_header NGINX table Encoding header.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR - Unsupported encoding.
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
set_response_content_encoding(
|
||||||
|
CompressionType *content_encoding,
|
||||||
|
const ngx_table_elt_t *content_encoding_header
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_HTTP_PARSER_H__
|
||||||
496
attachments/nginx/ngx_module/ngx_cp_http_usercheck.h
Normal file
496
attachments/nginx/ngx_module/ngx_cp_http_usercheck.h
Normal file
@@ -0,0 +1,496 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_http_usercheck.h
|
||||||
|
/// \brief This file contains the string for the block-page returned instead of the normal server response
|
||||||
|
|
||||||
|
"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
|
||||||
|
"\t\t<meta name=\"robots\" content=\"noindex,nofollow\">\n"
|
||||||
|
"\t\t<meta content=\"yes\" name=\"apple-mobile-web-app-capable\">\n"
|
||||||
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=0.75\">\t\n"
|
||||||
|
" <div id=\"portal_top_pane_div\" class=\"usercheck_header_class\" >\n"
|
||||||
|
"<img class=\"usercheck_company_logo_class\" id=\"usercheck_company_logo\"><table height=\"108px\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"usercheck_header_table_class usercheck_content_table_class\">\n"
|
||||||
|
"\t\t<img style='display:block; width:75px;height:50px;' class='usercheck_company_logo_class' id='usercheck_company_logo' \n"
|
||||||
|
" src='data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAAJMAAABuCAYAAAAj6SHSAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAADIWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkFCRkI4ODI2MzM2RDExRTg5MDg5RjkxMTgwQTA1QTZDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkFCRkI4ODI3MzM2RDExRTg5MDg5RjkxMTgwQTA1QTZDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QUJGQjg4MjQzMzZEMTFFODkwODlGOTExODBBMDVBNkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QUJGQjg4MjUzMzZEMTFFODkwODlGOTExODBBMDVBNkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7AUQj7AAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMTk6MDc6MDgg"
|
||||||
|
"MDg6NDY6MTfhISrqAABKg0lEQVR4Xu29B5QlV3UufCrXzbdv556erBwAgUgCnuEZg0k2wRJBCCEM6DlgDBg/r/XW+mWt9Xh+zxgwYAQChIyCZSSyASMQCLAkclSE0ShN6AndffO9lev/vl11RzMjCQtNTzKzb5+uupVunXO+s/e39zl1SlPH5IiQ9KKL9Gbh1IoVa2vx7ZRUN5+qDP1kpWvTmq4XNR0HJZqXJOkOFUV3aiq9JVXqVl/Tt07+z5d2s6scXjkGpiNAdl50bblkGBtTTZ2W6OpUZagTlKadrOvGqlTTKpqm6UgqVWmq4oTA2Yaau10l6g5s+1msaT+rV3ds1S68MMyueHjkGJgOoyy844pSqWysTjXzybqhP01ZxpMAnrUAUg3wKUj15DWkJfinA1D5d2ilIVabaZLepSXxTdhzc6qlP6u88xW7siMOvRwD02EQaBOtd9GVk2liPiM1jd/TDOMpmqGvSXVtDBrIBJiyA2XBf6nSgB5uxzHcmElKUQEO2a2S9PY0jr6CDV+qbUzu1845J86POmRyDEyHWJK3vN8ZFOunJJb9QgDp+bppPC419No+GCGYCB7yJFaR7MsPEFTlQjXFY8UEJpEWx5sBqH9XUfqZStT/kXbxBV5+5CGR/A6PyaEQciNn6D3dsM2XKdP6vdTQ1gEH5r6VkIODoMmBIrVEBi7gIZhyQHHB/RSiEWoKZm+XiuMb9DC8Ytjr3TL57jceMnKe38kxOdjSfccVU/DOnqts41XK0M5C7Y+j5gGAhAAALgQZAgqSbQEHa0c3crwATAaSqDAcC+DIAbKEyHYkLpK0q8LoBuVFn4p63vVj77+gJcccZOFPH5ODLP2/vnI+NrQXgGS/HvB5CqrfTKNIwcVXSQIwyYdVgT1ADpw3KCIsaeYMQ+m6CbBgG8CkmWL7IATOXtVHUCX4zt3YnsaJn4bxLbofftiLh189FBrqGJgOsrTe9rGGbhXP1i3j3ERLzozDqBBFIRhOxAonh84OHGEEAhhlmgbbjBxMOsFkQksxQVvpQBrJ+Mi700S7QfhvBLI0GUBDfSMJwkuq/eaN+gff6mc7Do4cVjChIPf5faj3vGT/a0jn7ZdOKFV8qXL0Nydp8sQwDIwoCFQUQyslcLaQ/ZFGEi1EGYGBmwUUIFUAkoAJwDJNOHuGCZBl38X0CZ/KTaWUYF6scum0nwbhv6d++LFax79R++jBi0Xlv3pw5d7Lb3TtpW5ZS4NS2S6WHMtytDQ0owRusJ43owTQsowwSVLPMvUgVuGgMFQdbbbXPxxu7oHK4luurDpW/CIg4U/CNDorDEIj9"
|
||||||
|
"D0AiWYtRsGPKpxgyCA1qo7Mm+M2gi1TWgImaCSDwAKgdJg7k6DCOr5AS/FEns0lg1IUbKSGS6KuCpLPaaH37sp73nhbvnPFJc/Rykp67bWG6k9a7WG/EATphG3o6/w4no+jaK5kOXOOadUSFbtxHFs4PL8HKmrNB1fogqAuG4m2A37OFmy7LzbD+8qWMdCG44F24ZmHNcr7aCR986VWp2I/TzPNPwvD6Hc8v18MoZFS8KNM24xqHTLKfa6FRPbaTZEzCCau4zgxc/ABLcOCprIAKAvbCMr86D1gwjqvS3OYxFu0MPyoEajLS+97/bb8gBUV+fmVlM5l35msKO1EL/JO8MLwOEPT5vQ0nQu8YSPyonEtTerItp3EkZHEMdtkdg+ZyU+gv0PTNIYgq8uaZS2Ba+xGYd1vWva9qaXflSTRrcU//r2FI9UkphddZHbb656aWuZbwzh4iecNXN8b5kAi/2F281vPS5+0CeWUfaHku/dEArBMRD+xtAAmOVSDo2coC5rJtmzRUDpAhn/cmcmIjxm8EAAVJT9Ph+G7/aD/helL/ryX7Vw52SsHj13YSZmufdGqZtxb4+r6k+xEe1LkD08PfG8uDaNi5IVu7A0NtE49CkA+4xjkEwRU7PxIsI7SozqnGjdttLqiEwNEke5YfcN1l0zbvh2ldRMa5U8Sy9lUfNOztx1poGq95Z83GpZ+oadFF/jDwQQSPDaYNUEAzQ4WOUC4KTNmWJdc5Fl5uFoRZPEcmj4chz8BFkydDc1kWRbKjZqKAXT8CI+DrpcDdfwKgJZG6QD86TNaGFxS++Cbv5ddeOXk4W77Ucu1Z19rrJ3aWZ+en3r8munVz07S8PGh550Qed5sMOhXgt5ADwa+isJACGcM8MDUCZBYeCxIZlfUMoEVI4kl0JQBMFlFW1lYGigoaClscwZWsbDFKhbvMxznByjKG8Iwub36589dPhJA1XnLZZOozbNBsS8c+v3H+YMhsg2yzQaSIUe8d6gZARU3sQr2r4RRRrIyytdHyz0Hc4+G66GsABTLyMrKtm3QtCyUwPKV43AhDWCEN5lqsdqWBvHH9GFwSfWjFy7KpVZI9s/Ho5Z7L7rc7faGp5qO9fSpiZnnNwqFM2PPm+i1Wmav21PBYKDoucQgnMqCvS8gk5aDvIFLR2ipxA/AI2XCwB20VQytFYF6c52bDZJMaiiocRJOncByHabYLhaXrHLxFmirbxiJ/k1Hr286XHwKTUBTZ1+n9+d7z4+M9O39weAZg37PTSLcTm56BEwSU3pQMmWTVcHIImWSf8EBo82jtQfBlAu+k7DryhBz5zgAlY"
|
||||||
|
"my3ltD8VT+47lMcfqNJAz/rt42btY+uXJdLvvf2qMSdguEevxUEOtXlAulZxi6tgHgKQ9bLdVrtZU38KTxmQCQUXSUWSlg6QooUgAJbmqWAB6WkegUJHo7oWiySNxnmgd6LxZbmwMQWWxxKBwUEkFm1cq9QrVyn10oXG/p1r+Zhv0T/Y3PPCxje4Z/edkGX1N/Mgy9N/QH/UboZyEdCT6yUikjxOxT6kQD//J9kNFuWilu3fvw/cHEr6MzTR1lgrKybUfBYxaNRfNGeTCexd9L7k/9+GOJFl3Z+MCFD2Q7Dlz2u7X/XIYfuH59MBg+G/b4D4u2/Uwo0/FBp6Oai4tq2O2oGEDQARq7VlL2eE3Z5aLSASRGbtkKkyGMQB/qv4c09IU/ScQXZDKGJvOxjxURA0wxSSu0lsnAHfkAwKTT5OUtXcd3o+Aqt1bdXhyrfN+yC1+IUu0bxW3f2q5dfDHxfEgk/dN/Kvft4qsGSfQX3UHndDgbEtlmYFGKmKWcoyCHlYhULytZ/kaQeLBSuIU8cp99+9XY3sfyG8MFNjRTAYCyYO7E02MsCiLXYWNM00BLkhvTMHlPbaL2be3ic8BDDlwy2D5KWX73N9ZCG/2RqdLzjVR7Nkh2qb28rJq7dyuv24X2TJRbLqvSVEOVZydVYXJcOZWSAr8RsLBgEvAngiim9uJ6ECm4z+BSoQo8H0DCNvIqqitR0ewPhwnEMRmRhVYys3gLt0e4DkBYSdLkhNTWZ2wHjmJ59fL//rcrl/LbPqhCEzd4yq2PC7T0Nf1g8KzBoG8wsi34wUcqmf+wYT8c5Pvk/2ghwkoffc2cFH7L0t7r8odEDcayj1EeepQqE82ICV40ubfwJfl9QaJQflQGfLxU2xx4waa/+8EXB9hxwLJ3Q3lEQQa0u95xxXpTj15jaeoCndHcQc9o715UrZ27VB+aicXn1sdUZX5GVVdNq8J4VVkwc+Q5cO+FUJuw5+A4MFfsFmCWgGXY9gSFT4007PeVJ4G9rM+KWkm0Ge8B61EY4riBGnbAyWhGcL5hmyoeeqq7fZfR3bbzScNO540otPOST3zreHqZWQ4OnvTeceVkbOq/5wXemd5wYPMe5d5ZexTWNghiioZALRzvldg4xCnJjhTB0fJdElBCAi+NJkL+UU4RzovYuPA9STI6EKBRhmiI2jBSFsBU0ExwJ1csQmIbCk0VPBRlyVuR+5JUx5aTcd1ZfFkRyXP86+VXb/nIRt21Xz03Pn1uQddO6g1g1paWVLfZRCY8ZYEUu40xVRxvCIjMUkHBnacRB2AyngPNAU2EgoF5C9vgVcttFXVgDsI"
|
||||||
|
"YGgnmDYQ98AYqRAEzXkLNw4bEusj6sAguXgrFgYZluTClxYKyYeY0HETTyCp06zVVnZncVq7Xr1NeeIU92/vFwYqgE6y95dW/46XJ33YG7WcMhgNopTwMQNOdly61iawyL7IlE8nfqAryHdkCZyBP4tpziSRePi+LJGWAD7U3wZVCu9swVFWnqMpTE8pdPaXMmQaAZKpgOFT+zqbSWj3lUGsxWk5KkSqo9OS7aRJd0nW6X15d7fgHSg3+05Z771svrxu28YKSaZ5jBP4JPsxZd7mpuq2muPwmbLNbqwFIVVRkCa0BHhu0DZig0guO0ktoISUsXcZB8o5KaCkeI6oZLTmNQ+QrViFaGQuH6jqEqQtA0EO2dBYYPaG8gHGWYkQZ3E3AzHI2AS6uDJZaqrV9x6re8vLZ4Fznh1tLZ2Q5WXnpNecnoXSeBK10YhCGxoPxJKKEAMgTao7Kav+akvbBfKGxjLQVwcj8UltTw5E3jj6jBoVLStkForUApERX1WpN1U5cr8pPPkk5TzpOGaetUeZJ88raMKv0mbryXUP1IuAH5WmgDNG8zVRPV6HVrqlH9XJ+SwckvxZMBJKrJc8bd4vnVG3nFHAkvdVcVORJASqRZM+tlpVTr4Jwl5VVLSkTySCoqgWl4MUpAEmhonUcK0QQf2wZGs0bCj5CYfjgUENopQEBgsyGABcT90li4RJIUogsz6yS6PV5MI+M5xBkNrShjtY62L2slu7ftqrbbr06SJI3+v/vi09ML7oWaFs54fW0xHqiF4fP9vzBeBj6UtkjwMtS0ggA2Lf3djYKAiUHDUG1Bzx7HSdgxHcJowGjPCYCaAOUCzWSG2rBeGO8OXHGScuV550R2GedpPT10woMXCwDQwXFakWlaNStxFfdCHwV5xvsP1ZqGpechwke274w5+ZZe8zyiGD61Vve7xRS/0kFy3qlq5tPNtPEGPS7qtlpoeKHombpSZkAkwuvzYGZMwBwSY2y0utFpVFLQdUyzkK1z59j62RKCSxkB21LDQNP9aGOPWg6trZQWikKGL8hpB3Hs5DZGsVcSmFn36m5CKiAhB7fTXh7LHRqz6WtC1OddutlsZGeHzrG41cSUMPdndkw9p85iIZP8QPfJCgoom3TTMuIpuG98iPgeHC7pBxAIxDtsw+J25Epua5kCsLt1ODsySwmdrdRqn+/fsL6rxeedvLt+omrPFUDT8L+mA2sO1C6D82Ve3V9gKgdovFFQRaOURpQlm6I4/SksqXNyw8cgDwimMZCa51pWL8Pt/ypWpq4Q3Cabq+rfBA9FhDNFc2YBY1kk2zXyqleLrZVwdmR"
|
||||||
|
"Ft1O6pqpcgAk4p/uKBZ0KlgoYgoIJrj2AQq5DxARSCE8ugikkqDgMdIzjmMYCmDkmJXEVkkCKuYQiUsWrgcSHgwDKoGM5OP8QYsmb/tUu9V6MYr/1YFlncI7OFAhV0Llb/Qj/3HDwG9Qe6JSkPgHTQIzzHsVrSPaJWsAezSORPqZck02Sry2pPyDbVLr+OO5EfglGx85U8V0+4362Lfq61Z/zD1l7TfVfKOL3zKSdl+lHThnaFwpuChIqNKBSRZ9iLLthx7AFEqLlhBYmq7T9fQM3PBJzNuByMOCafnNl9ZQi882DOvF+L05mqFur6MGAJS0FmSQJssuFVURJs4ql/ua49ymWea/QRN9SrO0LypL/yEAsxMg8njX0tnIxCAatZQASlNwqRVMhQoAkpCtkwVHwOEcdorqOIaRcA4S43Xo/RBANHG0/9IKca4PE8lgaYB7ZR3Y9CKx0l1qqsXtCxtandYrojQ6J33XF09KL7oRKH9sgirWejs3ToDpPWUQBid7vg8KklW8gEVAkykU6taRGctSdoxslzQCD9ceKtzHPRHODWDSmE/2wdVMd9hwSt+p1cc+Wjpu+otqfqyLa8wlzU4hAdFO0agUyDZO4Mm4CEEN04hyxj0rH2VGAdkgxmaQpdNQWMfLxgOQh4ApPftaI9Hjk01Ne46h6cehMLQB+QzMEDVCVnBo/YalCq4L7Dh91PF3QT4/EafaZQDI5SjBj+Ogj+Lgz2ix+gGQs1sisQBEalJLPZhEcwEwWaFSsI1ZRCKwCB5qqWzEIUGIYpOWjtaPxKV8RwpZUNBQEVskAGvh/hg47DdbavfCjjXdXu8PQzv9Q+W2HrNKT9/yAWQ5PgUa9Gl+6M1HMbSh/H4GqEzj4MZZSPjbA7Q8ZRpo9OFhWOYJ/7LE7ZLYcBgOoD7KgeSWwjG7/OOqW7yyeMLs19N1k1GoaSfFrf4G1R3qKbuj5Hq4AJFIILHxQRv5CRptCg6K++XV6QWjbJ1E18awUsfGA5KHgGmpsTRjaPYzDd04Q0fVR1CLw+FAzAi5TAiEs2LLVlGVdKerR+m3Iy+6FEV6teOUvm8ny3eYPfMHdpB+Jo2Mf4RK/0dk6tPAxs+hzQaMO9G80Ztj/MkGOWelK6zT1SXnYGEwxkKTF6AAqJ7JKRiwZByd+yOkkADCdgY0aVZoJgmogESe5hgFSg2FWlSdpWW1Y2Hb8cvN5tlhqv1B8q6vTOZZ/g2l4SRhxCE2J+K3nAzMBArrbS/A7J2w/SHbkIiYvFq5KkmEu3BdmvQYeacUQKTHCuWw4VZ/WA6sfwaX/Yr+1hf6MFjrVd8/Me17Z"
|
||||||
|
"WqkrKsK58AkinZiTAplMgTxHoArEVDQ0PmPyAIKPLVxOyioA5N9wEStZCbaqZqe/nc06fWMCwW+ByCBtMGkMHM0PwXLVWW74FmJ8a0kDD9eiHr/Xn3hE3frZ60ZameeGcryjPUt94mrNzkl78uplnwQ9/0+6KYvm7qxbDmOBDA51MTBulsoSFCTqp/aRgioFCRJKV1gAEW2oeipzaCpWAkElBQ495FPYJ0qPEChBfCuwsCXyqUWZQttLu62Frbc/4Rmc/n1STj4/eSdn69kOX90AghocK9X9cPhqf1hf5rcg5YkAxO1ZZZw3D4fAu3hBHskMS9Ss/l1hMBjnYDjkN2KW1BjxXrSKI59v6w5n0i86AvaRy9sJ9f/rBQo/QkA0wZtEOipB5AMmQAxAMpAmWhIQyiC1rCn+gBUQC6H67I85DeFyT7CDf6Gsg+Y2tV2Ddrj8aBDJ4LyWhEqxIPnFggxZiYTcGoHYCoq+Ae3x0F07VBT39Bf9/x+fomHiHbaaYH79BPvtNuD65IovhSZu9ZKtU0OSLKFQrIcF4lRcYKJJp6gwG/lFcPWTkAJT+J3tOKEhJzVwMLHNqptIeRcR0vOiDzdZ6h1JBYXh2ZQQywvLxkLC9se3xn0Xxu7/n9L3vIVuJyPTna89soiQHpqz/NO8QK/JCMi4BkQLKJtqIGYeF9yb3ni3eIeJUkuR4J1qc/8eOxnfrIIq6Zsw1Rlt6jqpUpaL1RudTX9X6JU+1L1S38lQ0f8ljel++EpaRTNpj5AAjBRKyVeqDQkgimBpur0e6qJxHACdbto97xsCStULNvlAQd294CJWkkZ8Qb8zhOxeZLBswgte0AwQU1KQA7HlZyCKtjWtiSNvhgH/tcm3vrCTnaFXy/aH5w5cKfS7wBM/2AE0QdszboFZrFbKJZUoVyEdnIVCDwaVISGBc2CZcaLCJAcMFyyOnAjrBLRXCiQbD/BxphUppkY9GRlh1TtETwbaDTHptucqJ1LO/Xtu7c/qz8YnB+Vl57yaAm57nRmh4H3lEEwPAn3Z2daCAlLAf7os993qTHec37fo/3IkpQp057/5IdIrmmrqlNWjWI1rjuVW53UuCINos9VLnvTTh6X3nijCXDOaV5wOrZPJzRtABOBBDuvNDQiTeJwfbXYWlJL8GzZ21DULQGpNErcA1Z8/B52qrbcwAHIHjBRK6FVnYHbPA25LNNseED10M/sLIvN1AxVNJ3ANowfJlr6rcp7XvcbTZJAE1h49TM3+5Z5tRmk7y1qxpdLbnFXpVZTVSQHgAJ9hMcxhLaGZ4aKzwCTAYlLqQS2YhyHdpebuhxocg"
|
||||||
|
"xAhYYQMHzAbbwGABYhDyTwjklAxWrrzoXC1t0Lz/P98Lx0uON4tNGsNn+NoDJWDyL/9CDyx/g7RAm9JAFTfk97tBIS16WTNd++J8kWJPwbkXfZAsCzE7tgAUhuSdUKZVWyC/eDGvyrkUSfKX/yz3fgQJHm7qgUD8N1aRCuR4sxU4CIZm7El/QQdwNz1+121e7mkmp1mtgeqSJMvsMuFfwm8pCC0y7ixjdrWnzAQ1H2MnN+Q9fTx5mpPq8lGkANMAlPyiLQbDk0cbZl3aPZ5k1+ktyVnfebS/3cZzWdULveiLWPOZp1Ta1Q2zwxPqHGxxrKLjjCe3qw8+QkzDSbz4hLUFuxAriN1SKmYU9iARFcSNSs9ILy7eyA5XdUjHA+csHtSztq21q7n+8lwcvTd1y7Tm7uYQS/oy2e+/4q7ugkL/LXhWHs4Cdw3QwQ8k/WR0DiWRywxv/ZfXIjA408Vg7nEh8BEkMt0EYcMuKijCtOEWCCtrbsHYDi52It/mzxE39+P686EnNHPAuieFoSR+Mk3eRIHIHBZIB4G/DqPDhOBNJia1noioG7IVDZc8E+UDTOFHx2K37/Z2n02OtzJAImFlZiqDksTkg1rcrKoqnwQdj4o/SY6EkVbddzTeuH+HLLzNLEAQ3x0P78OT13pv1tVO6HS7p92USh+oP5ienebGNCFcClhjFalT+E5s4CkSx40TS4N1QBUuYBUTsxsZ7I6bhf7pnmTjRSBrAggj6H+YzSEI6jiUJ1VM/rq/t2bV+90Fx81TAc/kHyl1c+fA/62dfpAxWcOAz9M4PAn8P1DTFVci+EtcA9X+4r2b3vLzgv38ieBPi1yoZGKqKiyzDFBJNNIGn6F9M4urr6kT/5VXZ0JunlN7qalp6uRclT0fLGFKwHTZsCx9WwbgUoLYCr1W6pBxYX1FK7CYBpqgSt7BrgjvhxxpzQOH1dS++Bjb8t1a1788s/ZhEw7XjtPxSNVF+LfE1qWqJTdTMuAXUuZoM/zl78ku0MTKXdAV/yHu26A++JZ2++85e//ystTa5xU/PjE8X69+bHZ/2ZxqS49H0UTjeAuQP/IbFlDbACadqkvWOZdV+MEr9nSVoe42Jc5scxxCCAAsg4eIy8pD/oaTB3xy/3my8L9fSZD0fIN80smMPYO84Lg1O8OK4IkAQQ2T1Qu4zWRUTT4DtWs7Tfen6cAAnaiN0dLhPAVACY4H2G0Eg3wSpc04qrd+K47IRchkutSWTscaicU7QgclBA8Fzg10H7GsizjobD4O1iq6l2tBbVAI2yAK5UA5gIXJYLHBTchQa+q+/SU71Zd7wDHr6rs2u"
|
||||||
|
"gYJWmUTQbcdMN5pNjZegFSYVgAwN/lmmltmkvoFLvLi/UYIBXRqDeU/dvXnq/YyWfLxvWh+dqE185bnp1c35sSlprF/ypHcAJgNkaAWhPwvm83xBAy7RRpkXFrLFBMEAHAAnXwtHcHmCdEXeawaJZUA44xO5ey7lnaduTm93WHymz+bT9+/CSxbCCdnwCyPxqOCY5TPLYkNxHdj+8GTIkgkQ0JzblbUCOkXvgOTiInQEWGihHkfLpkgIqumjhfkw7xvnfB8/79LAdfH/9fmO0pSsn1Y/HlZ+QhuE4WodKB+C0EmOKlIWzuVyGVtq2vEMtd5vSU1ACWOtWUVm4Fw5ExI0BgdqSnqhFrAy0xvKBe3PpcsOCiVuFfB6Peq0D8dJy6V5TI9AjIvsvmmbP1vS7VKw9AK0EnbpyQkDp/+uVu03D+0rVLP7TmsbMtSfOrFtY25gBWbRgjoaqEwzgCNCXo5HYK6FqRwR9T8IehgZo6mRYC7YxH/hD3gAm5I39gax2mG4ZbrqjtVTctGvbcxcHzVelveTxqHziQipPxf31AOpJKJdxajiCYiQCGC6zrw8KNoy28UI55OQ7vTUSbT7z5kpyoJFg2kwHl9I2oTFfZwzNr6359DuGcsJeMjSfsArAfVYaxmdofmQypkTynQyhkYJEWUTGoKceWNquti7tlBihi6xUANgaNJ+G/LN+UegBtNTWVNO3Vny7py7+2wMH04LvGlocT8DezaFgCmxhI4+Iia2ZqhgtuAmC/ivdSnfn5664cHIqrdn6D8ewPzY3NvnZ46ZWL6ypTwlp7kND9aGhqIGkpaOARCsw7fcRkyZmjeqcmor5wBLfM65DzhAoeGaidRkUpEe2pbmzsXnXwnO7g85L0ndeI/zp55vLhWESn+YH4QZcy5FfwPl0BrjMJFtm6BsluUlJPI6GihqLJNiAVzwyb3wq1wF/45MlqOBFaNQbAhXfUP+XP32I9qcJDqPo8WgZZyEDc6mH/MDEAVjZcF12IIMaLHWW1BZoJWhapQMiFd1RJfyOBRNHILGhoQGFAPU2Q0+38gmV/U3pYxG9FPdcFMKk0vRJXdMsMRH4wSA3D1TMqMzU0e1lU9M3OQN9RZ+12l9kYgXn9p+6euGS+frMZSfPrr9jXWM6pimgNunB/pNEg9vhYJouagp8kIksjkMTl2koeJxScNToPAZrSPzPZSpdDENwDXpRRRukH+Zg09LWDXfteuAV/V7/v/Mx743lUgmN6/QwDtfDxGd4EZDwL/twnDV78nFTRAyP2Dflx+s4nabNRuOwkAimArSSCzDpug7aHH0X9OLaWcvdxKvs"
|
||||||
|
"L51SdzV+4jlpmJym+TC39N5AuGHuYL5QT9A6y51ldc+urfBUd0Mr+dAOphq3S6oKD5b3SrrAewG5HwDV9ypTW8gvf8Ciq9Acw3ISJLiCuoEWjAVIBBQrinilXbcMvYMWv11bveUhqnelhcNH9fe88g6Q0svnxic/ccrcujvWj82mLsxtD9qpx0eicI9ZPRHuAAdBtSdBc2AbISR5oTbiBwDkh2CTY1D4Q3hB7ElnpZZBytteX//FjntPvGdxyzmeljw7COKNcDo2gCtV+FsSP0aNMhE5rCARAUy+LjooPyZPBJtwJACIz7TZfCwJ2shBMg0dmjHaHATh1/Uo/snDzVSy7aJLi7AMz4YG/V20khkJA8Bcy7N54IAmQMJx8ffDtN27uF11+h1lonVVkK9xp6yqMKMSc2OdcsSHYe9WqX7PoK8fcLByJNDy5jQwNI3slriB2oiA4o+KepZCMGJdM9tw9JZQ0ajFQyPaP557j63Z187VJ64+aXrNT6GhQrZoEvI2NBTjUVKHOFa0zl5g4kbRskjZ0BZqsTyBFUvCQRLTQiUQnAULmgLmoDXsGj/fcc9Zt+7a8saW1321lmrHaSgAARHvS/5TsvLhh/eQpdFnD6SkP5PeMJ9jk2fbchOHCpWGCuu0BE52Y5po35y96p0P2zVV7hVOxx2/QIuSE7Uw1BN5YNWXYbsWlDSfR9zZWlL37F5QOzstGTNVwvXrTkECoHCgkMesawm/7+m6cTdu64EJM16xhzChedO1KAqSOhAHTXG8MlssP3Q5GDVGIQzROneDzB6S6ez2Fv29r94CL/JfVzWmLjtlcs0P14xlgOoCUJ3QE++MHpMEK1GFTGKcsQ24QmsEmEg6xeRhP0Ek69RaSGjRjGm1QfAJxgpDBqiE+ztL4z/acfeL7m3tfk2QxsfB1JvsY96jiSAECvu59hceM4IXH9I2Ua4caUGNRM1kwUGn2bYBXHC/EB7nD6A1vjT7QOsh5g3X0tpv/+QJqIQ/wA0+PY1CNw48FaExJZ6vDA5+g+Va7nbUnTsfUFvbi9IlRU07ViiqyVJVFR0H+aWnGwmoUZ7bTUP7sanrD6zkfE0E0xrkeApFY6HRohLQxvGjo2e/qJ5BGgdQYc1IWzkU/yailTdtMVX6hZn61DUnTa25dU19ImGl9Nl3KME3HETwyP1nGonbRCsBIBkRxzYmbM+0FAEFbw/8iy02G6IBs4H8FvIHPduBV9o1bDeCJHBInMmNeG3+DhuewIjr/McvSLku2mszGyRJN00cNdNoaco2tN7FOAq+p6LoDu3bD9X6u/70QyXc0nNxrRehZUwn1"
|
||||||
|
"KJIsR+CdEPLJLoaep7a2tqt7m/CewMBdwHSiuOKRqoXytCeBvhgNoJCHiG3zW1QEb9wnfaKzhnOoOUc2lEDRWTkbTpzrVliSAImXe+hQFpuaBwyE7e3CId63+u3ma7x+dna5JWnTq3/6braRGLD9g9ikGgOUMuOzMwe/jEf8iFokDITiIaCxPUs/sQhJAAUEs0dh2hwzA/zXySHAmm1DBN0EuUCIMpvEDB7gAPJfy/zhWTLPv+p2cWkUSPxES2N2omPbqN5UHEq9SOA/Juzx/e2yAl7CeNdBct9LnJ1DsB2ahwEOke9hh7yizNhLYSSLLSX1eblBdXy+SwlzBsAM14sqyloJa4nyCfDPBxqDbIfuKZ5t6knm7RqZ0WnJQSYtAZ4aRmNBxobBY6Ckwf8pFZ4ADWTgpnTB4leOOBYxIGI/vfnbS3YxnWrxqY/edrM+l+srU2BlBvKAwh8aihWOo4TKCEvXMon17YxvLqQCescJixjyLHkuo/KGcArounsgZCz/2qyUFMT8ITQyiRexczn+kj+j35rJNmeB4XajfEkjqeSR7UBJvImxpc4HBl6cjuA+O1AN27b/5k1zhfeWl5+BjjZ+UkUPy32A5PPwAXkSbhvkHEox0TtHrTU5uaCWmAYAOcVTFvVnCKAVIOZq8i1GPUnV3JtJ3Zt+y5dt35kF/UFdfHfPnjzKyB8MoixE0sKApeWFsiWjdZLyZS2HuPGo7Q42CfDh0O0952/3VHm50DKrzlpYv6X85VxeLeaRLUJqEy78sPKyj4j0i1mDonxM4ml4Xo0fxJBR3594U+BcCieUwSgGOxjCQgXYwPDutyH/H8omEbbWbGGmLfMxFHD8+FSTtBlMPzNR1K19C7cxc/X3dfaZ+ItBkq7YeEMaJ5zcePPgklz+GgZJ/ag90YgUuN1wJvub+1SC71l0bSM5pftghovVQRIbBDsrKfWtQHgolX0HcP+CVraj7Xilj7ytbJgwq27QL8t44F5aW7BQiqDjJNw0zRAWw/SxDj8YMJd6h88b6vrFD61bmzm6lMn19y9GoCiBmUgkhqEdc7qYj4ILXIkgiUj4eRQAA6+I0OyzkRTyKE2jDVRW5Hb2Kh4emsyx2QuXJNvUkj4R9Tg2NEhXJBbkXDTUeCwHQJKuqTkmnRo4Oho2r1RlHwriNM79+ZKAJLZ3jV/Bor9Dbillwd+OO7D2fChMalRZdIOgKmH7w+AbN+/vFv1ATKaUYY2JssVNV2pqxI4E0d+DOH1UTFwRGu5UNgJp/SWQim940Cf3n04AQHnU/8SmBVhGUnJjEoHagqrAVpWkA4KsvtIEP09r7"
|
||||||
|
"3fLTrXrq1Pf/rEiTULs+UauZ2MMiBwJCPIAk0TA5TkTxLMRAoBAtFI+Xfp2MZ+HybQQ4VxLHrJBlGFiQLOsvP3KhKR0XqOp+w/NxI4GdEWMKFojZTRKZo7fMM2TgkEDbkJ2uR73i5zTxBYgLRj/om6Si6AFnxZGIRjHoAkZhz3xsZNjcTuoIXOstrS3KV63kAqj0NL6m5ZTZfqYuZowgeMx0HLMixQsN3AsZyfgKv9XD9Ic4KL5wrAUP2gUImdbMcePHGbJrNMH1a+9HCivfd1mwpW8V/X1af/7eSJ+SUWJIfKyIOcAAXNErPDJkhQ8LuACfCQjmCaOwFSZvakL4+tHx+S7wIqQcZj82Rsk8QLspCQ6IzRfHHbCGiZogJ0RCNlHEl4EraJlsIShdkJo+S2ONbuOv6rfyH9nOn5l7u9XaufZejpm9B+Xw6TPUGNM4TnJuQZ19eh1eiZ7u521PbWkmoNunIPRXifVaegpqtjqg7izfvkg60EoXAlt6BKhdL9pmF8NYyth42ur4Rknn9WBvsIyyYrHxZtgsaGjxk95LjDKTR52vjmW+tu5Z+PH1/1pZMaq3bPFKtoHSp/zJzeG7lOFocaBWQlJIBEc0atw+1CxIVLpfC4bFUxXGVrOZiQHuRGoyV+HzUlcaasoLIKxz8JBeQgYiJPyoi4mE0fmvCnSRzdvPbqt8nIyf6FH141LMYvgefzFniYr/YCf7YPz2wgo01ptjOvmmDf1WuprdBIi722AJpjoOrFipqpjKmJclXM4AAmkGPBOLqUY9/LxWq74BZuMUz9xvqHz12xER/7yx5wkC+xMMgRpHDY+qDboZLhGmsG6LepuV5ebEeO0PZraeHWklu8YsPY7Hc2jM0M625RvDmaAxm6AoDQpPHp4cysZdkjQMinGAZhtwsrjuaoZDniFREsJOc8P+OUPCMrKGofKavsLgRjpJjsqqCJI+nOoEZg8Xi0WSyh3psA781Rqn7ExtB8w4fWwLt5LSD+FgD8d/uBV2KHtoy7AsBTEDD+Fs1Ws99WO8CTmoMO7j9RLjQnB9JNlsc4Vlzug09Gc5SFF5DmcsqBQoKyucsx7e84qb5n2O/BEInDoVSkaDOhwZPiyUWKDy5N+pifgj3Yol1yTs+wSt8dK1Y+cfzY3Nc3jk33G25FNETm+kNLwZiJOQMgSMpl6AoyRiAxMWBJ4HCA2rhbUuwHZDwqCzeweDLA4NBc8pWsBeIbYTPy3qjumbLWSmBxdCdAEcVJtKUfDn7pJcPa/a9+90vw/e1RHLyhHw7P6kSDKjuy++wqojvPwDF+l8OXd0MTbW0uqqV+R0A"
|
||||||
|
"PcKh6oSRkuwHTZkHrDQNfdYc4H2Ai2IoA2liluqPgutfDVn5Tf8/rHvEpopUQcKYUhhXlvJdkQOJ/aY0oqYSDxRwttB/E2BEm+vteOTT72jcb5drlx4/N37y+NhU2UJgGQOOl2dOs5Bti6gAnMW/8sKsFaQheQkBU4Vo3ACYCgkOXaWJGkq09tAgywFAPwazhQ7rNbcKd6L3RzNHEgc4NoyBsD3sbBl733DDx/3oYBxd0w+EJ7UHf6Ax6olUIHppocjoChCZtKzjSYq8r2zlKtFGowqwBSCWGAMCRQl/O5dPX1GIc+lyvjnnVavUmNKp/L7z/gvvkZg+ioO1qHuhluHfLotAUCIEFwrGwsLQ1Pd5jFo9E4bgcW7f+Y7xQvWpddfq2VaVGWkYLJgyooUZjeWSgnCTyqRxQ2E5zVIFmYgcptQI5Cql3BpfMxLF4aN5GJoylRS8Gp8LEZeZMOnUlBMAEIDFgCc4EE6vDw1rjheEfwgS/phsMntQadksAF4DQh2nKyLbcK8qd8a5FaKIdnaZqYT9NHl3+Bh+BKpdVBeacd8AulAG9PiTOcMzBjJVKLR2r1DbblvW1QkHdIdk4yMISaSL1Rq1OT7Nn+mkiWHjSgtOUoe+SFtPwH9mif+j8pYJZ/tKq6sSlx4/N/HSu2Ig5ZJVmSjp2AR62bmn5qLQs5gQjiMormBzaCi8uN3HUZKKds0uLUsrAlKV8U5bwb4+Jyz25rHHCk4O3RVANwqG1q788szjsnLbk9Tfs9juFxUFHX+r3VFtGk3o4xoeWycZtLYMbYb/qYTuvS48NplzVS2WYMFd+l94eh+TQeyPhphaslqpqcmzy3nKl9tlUd2/QLz7vUT3beKDCoPwibqoNMMVscVTQJJAEE91sidNoSRHaqeKH8RHLm/YWDR5LWZmfn602rlpbnbhrAi2ZMR/mRaLdBBI1E9YZuGRsiWBgT3sJSfq8sJ0NKYNK9n+ki/jhFtFQPDFfSsRbtBHKDyBioJIgshivAl9oe56xtbNs7uq3zabfVx2YJHpdHKPFPkE6DHTnOb6KAOoFA4Ac/MgyVQ1AqjslVbE5WYglmpMd3TzO43AUjh5AgykBaGPj491ysfRNTmddeNcrVmxq5v9MkH19C3jBIlpuJIXDAkFBMCbCsuQECuAZBaS6aSaF/LwjXvTL3rSz4VqfXlubvGptbfoe8iB2jMhwZIBI4kzw+BgiIKElQa6iokjAad4z4p0B6SGSqXEBYAYmNkFoopFWx3f6NBL1hrYzcG0/irVd4D7b4dpDK8moUQ8mLZtOaBSq4L0AULEv3SB0EAicMrQQx6o7"
|
||||||
|
"uBZuWaYTokaiNhKvD9+RMTQGmMCx8ahWr9/sOoXPAky3o0bzuz34QjJwN4pjq6YST+IZsPti41mQOWeK49BF4TZMTa9lpx0don/4z7aMWaWrT6jPfnJDdfLOMbsYc85iaFoJXHJGFw+mgZXJx57GQNg5Vpomj1pJcLIHUtn/hxPuoU4n9UblZRshHL/kAgTcuzToqq2dRbXodYUL0atk2EJMLjmSJIIbZhcaSgb9Azh8NInXpfA4Em0+uiRmjfyKjgO9O5jSemM8mJic+lG5UrnGriff0i7+gxWZkvnRip5oage0465E04YpVHGmplE0KElmgTEWZNCIk7iEvJTlaY2jSIr//GdbalbpsyDkn11badxf5is3CCQAKIt6Q/MCPHxkuoKW7UCLUHuhrqGhqWGYqLEBCS4JlgxloqB4HI8RWsBEQKEMOYaIYCJfojklmHaB/xA0PFn0BU4WJ4daEMewMTNhVbp3JMAKjcN5Ezh1ID07SdBa1KZ8D42Ge7XxW/Da1MTk9APlSv2zyna/pb/jlQd9ePX+oiehvgQTtws4gruAbKajsP+Ia1Plx1qQhGWAboyPRuU7jhpp+HN3ThTrV58wNvcvG2pTm8GLEnG7UUHsUaeCLsO8cdA9qLKAi+OgBBj4EDBYPJj2SPZFsIXiggMnDVAgJeEAmCWAlJ2yW7uLavegDdDxQYKRrgF45POgELCwAOB47MfjUQAXwCYz0CHJzTARcfjKgXBjYw01uWr+3vrE5BWu63628D9f8pCxUYdCYNW8PhT5Em6tnYIl0s5zJKB4I3kLlO6HNCpDS013IquYn3vUCJ8+nrvybXfOlGtXnT6x+uoTqjN3VeyicJZBzKdTNFWzCophBFafaAt8CBWmrOL3E7QsCosIpwstIPio0flhENFBRQMKatewIz38Ta8nACFQsrN57ez4EaBY5nQWsufpLFlSW4L/yJhxDiXh/fIKrpi2Rjq9etXm8ampa1zHukL/6xduxjVGlzukont8vBOOBhTtMthdyMbAzDJDJJNsBEJSw7gEMzhr+zbj9kelrCrV71ldHv/UGRPrrzquOnk3zFjKUQaMy9QBpiK9JBwn/XlYZoDCFtY8N8hGfsFWLLL95Jgk4PknN3lS6fDCSLAXBk21o98U88R5NkFLBTQ0j5kHSJNKUAKOTASLgEqeIpGhtuR0vE/xFPHjBFm1Xk0nVs1uaUxM/atplv5FWzvYips7bKJPj9nsJl9C8WwHmPqZ/Yc7SzWNJQuLU9vAHFSTRFuLvDXyc4864eD5uavfeceGavXKp04f95FTxlb9fNwtR3W7ANfbFfNOn"
|
||||||
|
"sKGzXznZ+VLgicD0AhqqHNpcKMwADkTUYFiA5hsmD5TdeHe7+gsSQhAroR/Wedxpjz4O7iC/Jdf4HYkHitbc4AB99jOmXPZ5SNkO5lZtfqX45MzHy2WCp9w/uYFtx/udxnrWmM5RAvdnabatiTVwP5x8yggeVZOCorjjBkNDspJGq1N4mhSJgY7imXi6r/Zunps6l+fPLvh0ifPHP8fx1dneyXLSbOHT2PkmaVAyMDHlWrlt1wbAWj0cNnpy3KipiAQRNPgRBm8xsYIrUS4NIc9tdTrSIiFvGxPyAXXyLpqMtiIMBaAsmbCbtkjhBzfOV8A3GrRUgBSOLVq/idjkzNXGUX3Gu3tL74nu8DhFZ297qk23I0b34IS6qJMpIDkURyTpg6FSK8ijuwgDueRuRk1ueuoiTc9kqy+4u3bTyrPXfP8tY+/7IzJdXeVDCciISeYpHJZEKIlsuP3CL6z6lku1EiiyXEsxxplJo7OC2g8+BLDD/TgdiPxRAYc3fy5OT5cwGenCBi5qPwWv+PD3+ZWAoqkG9yO49CLhYIam5jsTc/P39KYmP5oqVS42m3/8KD3uT1akSZRm++00ODux80vohUkbGm2bsNddpDprNXQ1PWDoBEl4ZqB5x61vGkkqLJ043V/0z5lYvpnc6X6NuiW0AeQ4DNJvdKqsEqzas2wlX1nZefAyZO8zZsaiZPgY51zB3AbI9vbe021OOTr01KJsAuh5jECKD6pQkuQEXg2YnqAOoDIvj0peWgl8q9afUzNzq/eOjM//4VqY+IDTtn+lPa2F9x3MIbfPlaR++UNJXq8gAayA5kORTsho5xUga2PJclhGl7sO14SrzI1f+Joizc9nMic51Ey48d+Dd6qTA4vSkI+ueyFKJZLxmEIoszbJbDEtMknC6kwis7wwmK/C77UElDxEkLUeS4AlHl1DB+QTrDLJXd6cKQOTkWNRHJeLJRUY3wimF41f9vU3Pw15bH6Rwpl9wb9Uc4leihlDyDgYyzj/31JmshNWmgZ9CRYOCwATv4wiHw7CKN1kaZvVLsnj7oQwf6S1lqzHX/4xI7fXxOGoZMFEuUvAxNLR9jhPmjKlhBWPL0uxoVEQyGxo5WvNR3CSd7aaWZPjoCLZQ4NAUe9z2N5HVwDpi1L+c9hyW4fTvpVqdSC6blVD8yuWfeFybm5fyrXa5cVNkTfPRKBRNkDJtO0+sjDPSCWnNArodqW1kJ3FBknYfTCwPTjcH2cpCcPBxonvDhqhVrJS5LVPd8/bRiEM1RKGURyEcyIURPs7FnnLn7Hh8CQUQLkPtQ2WKeZQ2HK83dbu7uhndoCHp"
|
||||||
|
"q10RX4YQhcIt40UvxtrPM6rm2rarkeTk1NbZ1dteY74zMzV1brE+8vTlhX6m97wS8Pt8f262QPmGTCJ01tRr62IIce1TnVLgNn5E2c28iPOc9lOO8n4fGJaY3npx6dcsrtmqdHU340XJ8koYznENde0MKUr6CSszWBwB5gZfEhAAqNLvPisngTGx/fWLUc9NXW9pKMQ2JZsmtGHmBAOXL8lLw5HW4+tSE1m23baalYCccbE7tm4anNrlt/1eTs5HtgDj/mlq0faRce2n62xyJ7wMSBZXDrfoWC2oT8dVl09Ob4hAY1FIUdkYM4KHlRdFysRWuO5hBBunmjE4bBGpjueTR1XSL+AIOUCABDXHF1TwFBWCaZNqI5yxLZcyqAAiDAlTgXJ4cJbwGQ+IBkzx+IKeQ57IMjODmYjg2VpqxeqarJicnW/KrVt61dt+7TMGvvLdXr/7dYLX68FE3d0HjXa+/nay3yWziiZe+ygiPrLqOl3IvVNikoYyKcIo9DH8gJKFl/lj8Lp+d4Vexnzx8fheJVuo0AWtYL44oQb1S48BhChus5cPifOecu2S7Ee7SOI2TJ79BKABPfaM5HkBYAJgKpAPPGcUh1t6TG3Eo0XioPJqrVxel64765yelbV83M3Dg7Nfepyam5yxtjUyDXhcsm/u68LxT++uWbtYufs89w6iNd9gGTzNWjaZtjLX0ABRyy95JursOQPgqFhcvuh37gz4RJdEboxMdxdrX89KNGqFH9INmIhrEuTMKihAOQMu7CQmFO86LhQswYtjIBNBnZpolj4tHsHEdCo4uxbWdvud8etLfVDPvujbWJTRz+sro8/rN1Y5M3rxufvX7jzJpPb5xfe+m6+TV/Pzk5dXGhVPwHw9audBvF71cvPncRv4a7OPqETXEf6fzxh05OkvQNKKXXmLo2Rxu/u9dSu3pNmSWEIxX5VMTqysTtY6XSeyJD+3z9ww+df/FIlp1/+k9l1QvP295ZfNPufvtxAJQhT49o2TguAgfYkMJh7DuFSRJyjY0ychKNzLThnNBBAQ3gxBRscPVKTUWWPvzR/Xfe/ON77/zaoNvbMlOp2lOlMYZZ/ILp9MZq1d6YW26NjTd2FuNiW3vfOSsyn+SRIPtoJkocBQuJ0m5FIW5nDskl+AwZ+4MYR2G/EgdvDSJvKo6SE5xIP+r66oq+Uxom0XHwTFchP8AJTRW0TG6+SJj5ETiJJsq+c4vEiODmS5wJnEk0FpbyMmssozhphWH0bS8afsatlL9QKadfKEfuFz238BWrmHzTbda/N99bfVv5Hy7YwSdqcNX/EkCisHz2EbRDrf3"
|
||||||
|
"6S85EWb0d2XwFWqLlh77a1W+pHbl2YiueKFS9ucr4jWXHvaRYCL+uf/CtRwVJZP56/+OyU7e2tv/vHe3FFw/DAA5T5oXRraeBy2CTC00cwESNRdIsA96ojbBktJpL6dUHmS44bjSMou/dvvOB/3fWv/x/X8ZV/ssA5dHIQzQTW0qoJ9vSJL0DBQ/zBSKOwitafFUCB3ZlfXW9yHO7sXcyPKGn+Z5zwC8LPlSSvu29bpxE80PwviCJhE2TVI/62eQ78iyJ69JfJptz80e+tFci/LANSFSRli6lafqLuWpt228bkCgPARNloru4mOjxz+DG3g4iPpT+JnACvm+EY2oYbOMbDIZBMANC/kSU6CO+xOZIk2G3Mu4H3gbcfyOJEmgckudcI6H6RScJcpiydY4+FaKdhwP2JGyTOQSgncTEqWinodKfjlvlwzqu6HDJw4JJu+7iQE+s2+HffDfRUpnal6q8DBeXjyUzZsKnRvuB5/b9wel+GD6t+8cfm6YJkQscocL+RFT4/CDwzvDCYJw+HPUOTdi+gmKhtoFQQ2XBSZDwvB+NQ3JlWK4FkwcuyRcvQrVxgrp7kyT8ecU/9BPJHgnysGCiDMP+TjTKW6GGFjgemr3bJacIMDnCFVgJXuSn7WAw48fR0y09eaI6+7ojPExwqpmk+lw/DE4M4khGPiBbooSEZI8wtWfJbQCSAcARTEgagUQthcR1zhOpWYzd6ovQbHeZerpVzW4/Yrs8DqY8IphmrvqrQaqSX8Ki/QwcqU0vhhNKFWHq2MXCqG4Yh1ov8M1B7D0uTONnDCo7J/LTj0hJl303SL2ZYThcBfNt0lOlxhl5cKOUjbQkpvJtOXj2TwImaCccFCkdlCBOf1BqOs0jaVjIoZRHBBMKMtUc6z4U6o0o+LuzMIEu70PjRFhspdRY9PTa3gCt3X8m/OMnc2LP7ApHnvhRMOH5/vph5NdJj/lwJLmPAIMlkSdyJJYAx2WTF2VPmlAzmTiW/AggpDeHxCX4UkfF2vfTMPkxu6X4W7+N8ohgotSmdna1NP4xmNDtcZwNmuNrrPh0KYdUsOWSO3UDz2gFw1NQP88/Uj078iVo0nmY5rVhGPLJSPAfhgMAFtFGGZKYR/yTgiHIDHhpzCs1WJZy4k2AkS9hm0q0bXBxbx+brK7Ye0iORvm1YIK6jlrd6F4o/ZvSNLmNT27RxPFx5ZINzw4FCdKtQGhVZ9ib6PrD56pUf9buN3z8iOqzo2PQXJireIF/AjzQ9VGaWAQEx2xJnyPAMzJr2RoKRswbNRJHPVKDZQRckoAMWolcydCbWhzflATJreris1ds"
|
||||||
|
"tv+jUX4tmCh855mRxjejjL8BDtWiv8aIeMXmhFjw7LCDc2x74VBrep01wyh4sasnTz6i+uzOvk53zbDuRcFGeHFrzFSDwgHXAWCYJJ6EDwtD4EQgAWjZ1IEcz0UQAXgWtnNoLnsDkFKwbTgom1I//rpnGptwjd+62NLe8p+CiVJJqveAiP9HmqT3cDgqC7jsZOZOosYoQs5l1PYHTi8aPhOge4EXxqvz0w+/jDV1P9EbfhKug1Yah3YBJjKtJEASUD0YZaTnStOWPVX7oHnLEtahnTk6ANBqJVHykzhJfzp7kGdlOxrkUYGJpDK14h/DRflaFCXgBYZ0H1ScEl+VLoAid+L0LkvD3nQ/9F6UmMYL2+d98IgYQLfLDJ00jld7UTgfqxhWiryHuijXRHJUtqR548Rc1D5ZlwnXMy0lJg4ereHIAwOBipOfaUH09VrS+a3mSiN5VGCijDWbO9IkuR7t99YIbhyKFZ5dURIfPGCr5vNdHa+vWoPOcfCcXmrY7lOTt73nsD8WVQrNShRF66IoGOc9ZhoJWd9LG4mZwzYBEDSPcCICiMCSbQQWAAaQaUi4wIKK4hsNL/ze0dIvebDlUYOJUXFlO79AmX89TmOQ8iQtgTtxInNqJxmpCFvoRx7MXd9qB8MzIxWePey5px3OyDh/OzSSmWEUHx/FyZiMg+NnZN5y4cz5wpOoiciToG33BCklYRtBJnGlNEiT6Icw+98oTmw5qDPYHk3yqMFEaXz0wjYa9vVwg69PVLrMDs4KuFMDgOJUNRJNRm0NQ5i7QafWC4cvjFP1Su9Nl27IL3HIJX3btS406Vov8k4M0nBM4EPg5GaOBcA1Gb8NjWMjUdNyogjhS2wkWJdn2bAvNQzOzXhn6gWfrxbin/y2BigfTn4jMFEa/cVf6ir9EkjnTzhlFd3mWiF7rxlfxseK4YC6vt9Xba875cXBS6HJ/qj/hg/O5Zc4pKIlAydJ0/lhGK6JksTJSLTs2ZNGQUk+0m1B29Kk0bzR1MnEZ9RSNvLmcCx8ukOF0VfTQN3MF1TzSsckk98YTDR3SRJ/FxX0OSzvktgTtBIn7uTL8uR9s/D4OFkn+VNz2N84iKPzIqW/ZvD6Dx1yD28YWjU/8DeAw01zQi1G8eXBUgitHIHCkRCmzVlG+JpTgAd5INnmugAMQDKwH7hb1qLo65offam+vXRY5kA6kuU3BhNl7JNva5lpdIMWq69GSbyTJLZguapRqCjXcHFR1BL5UxjK4PqW1zsp1OLXxKZ6aeeC909mVzlEonlzYRSvjaLYYWbluTZu1vKJJ3JyzTmQMiDR9c+0kiTHV"
|
||||||
|
"jqAhKOTNEp+kgbRZ4JQ/wXnfJILHZM98pjARKmubd6baOnn4OHdCDPWYVS4Bu7UgLmjpuJTPZw+bxD4qj3sG0inB3F0vqHbr+y/8f2HpMsledvHGkMvOHUYeSekaWQzs0K66Q+I52bIkzcyrAbmjN4cAWYyGEtgYZ+MCICmgkW/VfOSz6RReNPEB887Ip+oPdzymMHErpZeL/wRjNrVcZLcFKdpwNerjxdrqoHkwPPhEx+cdY7dLc1h14TJO2OYBm+OU/P89hsuOfFgz1fQ96x5L/HODEJ/Hbw6IxsZmZFvTqfMKZFJuEcD3MiTGOEm0ORYAIvgS5P4ThWGVxnK+2L9fW9azi9/TPaTA6rM1Z9+uxdbzi0o/c8nSXJrlPMnvoqBbxpiJaEShT9xrup20Nfb/uBkaKjX4fTz+9smH5eefS1fIbDiQqAmabDGi8ITwzgqkCdR6wAjOZD4cCnJdh6cBLDo9jPCDZ0mDYH3Dvq3VQXxtSoNvlB89x8fC07+GjkgMKGVpwwXxJHxFVCQK0DGbwN/Skq2qyagnep2RR4L4mRhfA/JwPNUa9gzm17/hGEcvC5K9L8Y1Fov6lxw2STjQfllV0S8pY1r/Tj8b57vnxylCXi2lpFqgMixYdoskm0Qa+FIMGdAGWNQnF2YnwTtINH1+1Ssrkzj+Mrau9/wW9/39p9JxkYPUP7+51/u/tWZz9+m8VVJaboBHlMjG97BmdcIJAYR0NJRFXxJMd9XoqVaNdW0jVAW86xj/4wfdN717N/tX/y9rx3wU6zJOz9eCZPoD7te/5Vtr78xikKNXiYJNjWSPMZtOtLHxrHbVFeMjDNRqKksw/4lNNkVyNNV9X943d2y45j8WlkxbUDN0n7dBzakpnGBobTXQBWsj1N4c8Oe4sz8XX/A+cTBS3SZv6BgFrLHg2wnKurW7SC8N1qm+R8gvz8sLJZ3wluSN0T+ptJ86+V12M1nD4LBny51m7/T7HbtJIyV7YAfwSsrOG6mlWDiCCIO3GZcjDkwQLwt2wqx/1cA2lVxlH5q7D2v4+Pyx+RRyIqaFkrz/A+v0/ToXGiB80Bdj4+SRIcnpxb7LdUK+jLNH93zAmemg9vNZ9Bc00mLptNzHedOU9NugEX6oZZYmzQtWR7GZk8eW2+OJeq6s5O9TY2Yxov+Vks7VaejqgU9NKcsQ3tOkMbnLHaXnr7cbjm+70vg0cVvuS6AZIMj4TvIESchgTbie1MIJJq/Qtdx3B+Ac39WM63PVP7uNTvznzomj0JWHEyU1gWXbFBa8iojVa/TdIMEWMHkqMVhWy37PZnInVPw8aXKJOz0/O"
|
||||||
|
"iag89EjmHtcA1zW1G3N4Mc3x4n6V2BHt6LCm6bWjKIfCeIioPI0Ku62YucWI/LuqnN64Zxgm1YTwm0+GntQfu4Xa2m2x/0yaVVESDi+9cc28HvAOISVM0INvvcBGROoYcGcIOu9Kv0SLul/I/nHSPbv6EcFDBRCChNi18O/+llMCdPCuPQoalbAqDavieA4mvLOXLTAahMzu8I08OJ2Km5DM0Y2Ka5BST5PpDkB2xd78FN76e68nDb4FWpju8uiHMF1mo2VdoG8LLjul6v0ey0VLvfEw5EflQs8AU7ruLwEs4zRc5GzUTvzXbc2HWKv3Jt5yZoqM/CMbh58iC9Xfu/uhw0MFG6r71kKnGi5+ma/irU3lOCKBrvB0O9A1C1/b68W403MJryMEsmXw0s3hVJM8xgXDAd37XsGIQ4xuYIflnCABA8NOBRN3G8HcS+PRj2tXavK68V5TtHGEMqiNZxoCgVuBFfnpwARyDiBTd13VIXpvU2aKTPKCP9WtnefFDe9//bIivizT2S/J+XnTn0vOoDKowX+B4NXTOmAKoKwz0c7ouFeHiciZYTYvAlNXziRQao4SAijTMiwjOzBlFoe2HgBmFcCOK4GCZxMYiCwjD07J4/MPvDnsbXkjJAyj444WLQeJyCmheSWdvwYTjAtp2kXK4sFp3C9eBKV+qp/uVSs/+A9t6/OtZFcgByUDXTSBZe++5SwS2dpKfa82BiXgAN9bhh4NX4cr5e5IvJI3/hY+cEmXS+MvbDYSIAFm9TPgCYBB6hnhggY0xI3hsHjUOtkwAwPI4cDGZLukNiXJt9hLw+vblqqdIqFIq3WlbhBmjErxVS/3b9mFlbETkkYBpJ700fmU2T5JngNs/3I/8sgGm+63sVvgWSgU2+5w11Ds2UTdNHUp4l3ibhAM5DZw6go/aRCUZ5AnYTdBwRIHyLx9MeEkh8NQw+BdvuVCv1rbXS2M2WZd+gO/HNpf/z+m3ZnR2TlZBDCiYKJ9oqB/qJMFNneYn/zI43eHJ70JvtRzBhoqFo/ti3Cr6kwySBQ1kmyDK2cTZIEhrp6gD4OEMJkAdtlWkuIAqGjC+FjOTlySGAZJqGV69Ut9bLtR+X3epNbsG9pVR0fqVdfE4Phx6TFZRDDiYKNIW2dO4HKoWCdUrf989a9rpPa3m94/qht2qQhGMwgya0F9i1IeS8wBgQQJVpHY5+xG1Di/GlgJzsnm+0JN9iDIv8CxoONi9ZLFrO9rFybfNsffyWSqV8SxS5d41/8LWc/BXq7JistBwWMI0kvegis7dzuuEPonW9aHD6otc9c2ev9QQAaxZcagxgceC"
|
||||||
|
"x2QXd0oqMTpvZLCTkVcCM4mtGPWihQRikfhKGOD4A0JqWYW0ZL5Z+vLox88OJSvkO2yxumZk+eflom3D0aJPDCqa9ZfnNl9Z2Nncet72/dMIwDufDRG3ohd6afoQU+qtBoktBGhs0cWIIYQ8NUChoqoGlWQu2ZdxXdtwtRqJvDuLw7rli7Vfz0+s3PeGqd/7WP892aESp/x/WUvryj4PUIQAAAABJRU5ErkJggg==' /> \t\t\n"
|
||||||
|
" <p style=\"font-size: 22px;font-family: 'Segoe UI'\">Check Point Infinity Next</p>\n"
|
||||||
|
"</table>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <style>\n"
|
||||||
|
"p\n"
|
||||||
|
"{\n"
|
||||||
|
"\tmargin: 0px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"/* SDB_PREVIEW_EXCLUDE_BEGIN */\n"
|
||||||
|
".portalMessage_title\n"
|
||||||
|
"{\n"
|
||||||
|
"\tfont-size: 18px;\n"
|
||||||
|
"\tfont-weight: bold;\n"
|
||||||
|
"\tcolor: #FFFFFF;\n"
|
||||||
|
"\tbackground: url('/UserCheck/img/pop_up_banner.png') repeat-x center center;\n"
|
||||||
|
" height: 60px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".portalMessage_title_td\n"
|
||||||
|
"{\n"
|
||||||
|
" padding-top: 13px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".portalMessageTableContainer\n"
|
||||||
|
"{\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\twidth: 100%;\n"
|
||||||
|
"\tcursor: default;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"/* SDB_PREVIEW_EXCLUDE_END */\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_body_background_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tbackground: #f7f7f7 repeat scroll center center;\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_main_background_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tbackground: repeat-y scroll center center;\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_content_table_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\twidth:550px;\n"
|
||||||
|
"\ttable-layout: fixed;\n"
|
||||||
|
"\ttext-wrap: suppress;\n"
|
||||||
|
"\tmargin-left:auto;\n"
|
||||||
|
"\tmargin-right:auto;\n"
|
||||||
|
"\tmargin-top: 35px;\n"
|
||||||
|
"\ttext-align:center;\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_buttons_table_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-top: -15px;\n"
|
||||||
|
"\twidth:400px;\n"
|
||||||
|
"\ttable-layout: fixed;\n"
|
||||||
|
"\ttext-wrap: suppress;\n"
|
||||||
|
"\tmargin-left:auto;\n"
|
||||||
|
"\tmargin-right:auto;\n"
|
||||||
|
"\ttext-align:center;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_title_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-top: 20px;\n"
|
||||||
|
"\tmargin-left:auto;\n"
|
||||||
|
"\tmargin-right:auto;\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-size: 40px;\n"
|
||||||
|
"\tfont-weight: 100;\n"
|
||||||
|
"\tcolor: #333333;\n"
|
||||||
|
"\tword-wrap:break-word;\n"
|
||||||
|
"\twidth: expression( document.body.clientWidth > 599 ? \"600px\" : \"auto\" ); /* sets max-width for IE */\n"
|
||||||
|
"\tmax-width: 600px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||||
|
"\ttext-align: center;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_message_icon_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"\tmargin-right: auto;\n"
|
||||||
|
"\tmargin-left: auto;\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-top: 35px;\n"
|
||||||
|
"\twidth: auto;\n"
|
||||||
|
"\theight: auto;\n"
|
||||||
|
"\ttext-align: center;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_message_icon_column\n"
|
||||||
|
"{\n"
|
||||||
|
"\twidth:70px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_red_highlight\n"
|
||||||
|
"{\n"
|
||||||
|
"\tborder: 1px red solid;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_red_no_highlight\n"
|
||||||
|
"{\n"
|
||||||
|
"\tborder: auto;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_logo_img_div_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tbackground: transparent url('cpnewlogo_1.png') no-repeat scroll;\n"
|
||||||
|
"\tbackground-position: center;\n"
|
||||||
|
"\tdisplay: block;\n"
|
||||||
|
"\t/*width: auto; \n"
|
||||||
|
"\theight: auto; \n"
|
||||||
|
"\tmargin: 0;\n"
|
||||||
|
"\tpadding: 0;*/\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-top: 30px;\n"
|
||||||
|
"\tmargin-right: auto;\n"
|
||||||
|
"\tmargin-left: auto;\n"
|
||||||
|
"\theight: 30px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".smartField incident_id\n"
|
||||||
|
"{\n"
|
||||||
|
"\tcolor: #7c7c7c;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".smartField orig_url\n"
|
||||||
|
"{\n"
|
||||||
|
"\tcolor: 1b81ff; \n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_message_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tmargin-right: auto;\n"
|
||||||
|
"\tmargin-left: auto;\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-top: 0px;\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-size: 17px;\n"
|
||||||
|
"\tcolor: #333333;\n"
|
||||||
|
"\tline-height: 20px;\n"
|
||||||
|
"\ttext-align: center;\n"
|
||||||
|
"\twidth: expression( document.body.clientWidth > 499 ? \"550px\" : \"auto\" ); /* sets max-width for IE */\n"
|
||||||
|
"\tmax-width: 550px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||||
|
"\tmin-width: 550px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||||
|
"\ttext-wrap: suppress;\n"
|
||||||
|
"\tword-wrap: break-word;\n"
|
||||||
|
"\twhite-space: nowrap;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"p.instance_id_uuid {\n"
|
||||||
|
"\tfont-size: 12;\n"
|
||||||
|
"}\n"
|
||||||
|
".usercheck_url_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-size: 17px;\n"
|
||||||
|
"\t/* padding: 10px 15px 0px 15px; */\n"
|
||||||
|
"\tline-height: 20px;\n"
|
||||||
|
"\ttext-align: left;\n"
|
||||||
|
"\twidth: expression( document.body.clientWidth > 334 ? \"335px\" : \"auto\" ); /* sets max-width for IE */\n"
|
||||||
|
"\tmax-width: 335px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||||
|
"\ttext-decoration: underline;\n"
|
||||||
|
"\tcolor: #3778FF;\n"
|
||||||
|
"\ttext-wrap: suppress;\n"
|
||||||
|
" word-wrap: break-word;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_url_class:hover\n"
|
||||||
|
"{\n"
|
||||||
|
"\tcolor: #1B2F5B;\n"
|
||||||
|
"\ttext-decoration: underline;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_disabled_url_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tcolor: #1B2F5B;\n"
|
||||||
|
"\ttext-decoration: none;\n"
|
||||||
|
"\tcursor: text;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_disabled_url_class:hover\n"
|
||||||
|
"{\n"
|
||||||
|
"\tcolor: #1B2F5B;\n"
|
||||||
|
"\ttext-decoration: none;\n"
|
||||||
|
"\tcursor: text;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_textual_input_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\t\n"
|
||||||
|
"\tline-height: normal;\n"
|
||||||
|
" padding: 2px 2px 2px 2px;\n"
|
||||||
|
"\tmargin-left: -1px;\n"
|
||||||
|
"\tmargin-top: 30px;\n"
|
||||||
|
" width: 328px;\n"
|
||||||
|
"\tmax-width: 328px;\n"
|
||||||
|
"\tmin-width: 328px;\n"
|
||||||
|
"\toverflow-y: auto;\n"
|
||||||
|
"\toverflow-x: hidden;\n"
|
||||||
|
"\ttext-align: left;\n"
|
||||||
|
"\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_textual_input_class_required\n"
|
||||||
|
"{\n"
|
||||||
|
"\tcolor: #8F8F8F;\n"
|
||||||
|
"\t/*border: 1px solid #92979b;*/\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_header_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tvertical-align: middle;\n"
|
||||||
|
"\ttext-align: center;\n"
|
||||||
|
"\tbackground-color: #ffffff;\n"
|
||||||
|
" height: 108px;\n"
|
||||||
|
"\tborder-bottom: 1px solid #c7c7c7;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_header_table_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tmargin-left:auto;\n"
|
||||||
|
"\tmargin-right:auto;\n"
|
||||||
|
"\tmargin-top: 0px;\n"
|
||||||
|
"\twidth: 100%;\n"
|
||||||
|
"\theight: 60px;\n"
|
||||||
|
"\t/*direction:ltr;*/\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_company_logo_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tposition:relative;\n"
|
||||||
|
"\tmargin-left: auto;\n"
|
||||||
|
"\tmargin-right: auto;\n"
|
||||||
|
"\tmargin-bottom: 6px;\n"
|
||||||
|
"\tmargin-top: 3px;\n"
|
||||||
|
"\ttext-align: center;\n"
|
||||||
|
"\tmax-width: 300px;\n"
|
||||||
|
"\tmax-height: 300px;\n"
|
||||||
|
" font-size: 22px;\n"
|
||||||
|
"\t/* SDB_PREVIEW_EXCLUDE_BEGIN */\n"
|
||||||
|
"\twidth: expression( this.width > 175 ? ( this.width / this.height > 176/52 ? \"176px\" : \"auto\" ) : \"auto\" ); /* sets max-width for IE6 */\n"
|
||||||
|
"\theight: expression( this.height > 51 ? ( this.width / this.height > 176/52 ? \"auto\" : \"52px\" ) : \"auto\" ); /* sets max-height for IE6 */\n"
|
||||||
|
"\t/* SDB_PREVIEW_EXCLUDE_END */\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_header_left_class\n"
|
||||||
|
"{\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_header_center_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_header_right_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_header_right_empty_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_page_title_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-left:auto;\n"
|
||||||
|
"\tmargin-right:auto;\n"
|
||||||
|
"\t/*width: 1100px;*/\n"
|
||||||
|
"\ttext-align: left;\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-size: 23px;\n"
|
||||||
|
"\tfont-weight: 100;\n"
|
||||||
|
"\tcolor: #FFFFFF;\n"
|
||||||
|
"\ttop: -12px;\n"
|
||||||
|
"\t\n"
|
||||||
|
"\t-webkit-user-select: text;\n"
|
||||||
|
"\t-khtml-user-select: text;\n"
|
||||||
|
"\t-moz-user-select: text;\n"
|
||||||
|
"\t-o-user-select: text;\n"
|
||||||
|
"\tuser-select: text;\n"
|
||||||
|
"\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_footer_table_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tmargin-left:auto;\n"
|
||||||
|
"\tmargin-right:auto;\n"
|
||||||
|
"\twidth: 100%;\n"
|
||||||
|
"\twidth: 100%;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".footer_logo\n"
|
||||||
|
"{\t\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".footer_no_logo\n"
|
||||||
|
"{\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_footer_height_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\theight: 40px;\n"
|
||||||
|
"\tvertical-align: bottom;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_footer_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"\tposition: relative;\n"
|
||||||
|
"\tmargin-top: 80px;\n"
|
||||||
|
"\tmargin-bottom: 10px;\n"
|
||||||
|
"\tvertical-align: bottom;\n"
|
||||||
|
"\ttext-align: right;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_lang_select_div_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\tmargin-left: 60%;\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-size: 17px;\n"
|
||||||
|
"\tcolor: #333333;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_lang_select_class\n"
|
||||||
|
"{\t\n"
|
||||||
|
"\tfloat: right;\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-size: 17px;\n"
|
||||||
|
"\tcolor: #333333;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_ok_button_div_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\twidth: 110px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
".usercheck_middle_button_div_class\n"
|
||||||
|
"{\n"
|
||||||
|
"\twidth: 110px;\n"
|
||||||
|
"\t/*margin-left:auto;*/\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"/* Buttons section - split into several portions for IE support. */\n"
|
||||||
|
"\n"
|
||||||
|
"a.button {\n"
|
||||||
|
"\tbackground: #4a4a4a;\n"
|
||||||
|
" display: block;\n"
|
||||||
|
" height: 28px;\n"
|
||||||
|
"\tmargin-top: 15px;\n"
|
||||||
|
" padding-right: 14px; /* Sliding doors padding. */\n"
|
||||||
|
" text-decoration: none;\n"
|
||||||
|
"\tcursor: pointer;\n"
|
||||||
|
"\ttext-align: center;\n"
|
||||||
|
"\tborder: solid 1px #2c2c2c;\n"
|
||||||
|
"\tborder-radius: 2px;\n"
|
||||||
|
"\t/* Normal state right image position. */\n"
|
||||||
|
"\tbackground-position: right 0px;\n"
|
||||||
|
"}\n"
|
||||||
|
"a.button span {\n"
|
||||||
|
"\tbackground: #4a4a4a;\n"
|
||||||
|
"\tdisplay: block;\n"
|
||||||
|
" line-height: 28px;\n"
|
||||||
|
" padding: 0px 22px 0px 35px;\n"
|
||||||
|
"\t/* Font styling. */\n"
|
||||||
|
"\tfont-family: \"Segoe UI\";\n"
|
||||||
|
"\tfont-weight: 600;\n"
|
||||||
|
"\tfont-size: 17px;\n"
|
||||||
|
"\tcolor: #FFFFFF;\n"
|
||||||
|
"\t/* Cursor styling. */\n"
|
||||||
|
"\tcursor: pointer;\n"
|
||||||
|
"\t/* Normal state left image position. */\n"
|
||||||
|
"\tbackground-position: left 0px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.cancel_button span {\n"
|
||||||
|
" padding: 0px 20px 0px 30px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonHover {\n"
|
||||||
|
"\t/* Normal state right image position. */\n"
|
||||||
|
"\tbackground-position: right -41px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonHover span {\n"
|
||||||
|
"\t/* Normal state left image position. */\n"
|
||||||
|
"\tbackground-position: left -41px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonActive {\n"
|
||||||
|
"\t/* Normal state right image position. */\n"
|
||||||
|
"\tbackground-position: right -82px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonActive span {\n"
|
||||||
|
"\t/* Normal state left image position. */\n"
|
||||||
|
"\tbackground-position: left -82px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonDisabled {\n"
|
||||||
|
"\t/* Normal state right image position. */\n"
|
||||||
|
"\tbackground-position: right -123px;\n"
|
||||||
|
"\tcursor: default;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonDisabled span {\n"
|
||||||
|
"\t/* Text color. */\n"
|
||||||
|
"\tcolor: #BABABA;\n"
|
||||||
|
"\tdisplay: block;\n"
|
||||||
|
"\tline-height: 41px;\n"
|
||||||
|
"\tpadding: 0px 22px 0px 25px;\n"
|
||||||
|
"\t/* Cursor styling. */\n"
|
||||||
|
"\tcursor: default;\n"
|
||||||
|
"\t/* Normal state left image position. */\n"
|
||||||
|
"\tbackground-position: left -123px;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"a.buttonDisabled:hover {\n"
|
||||||
|
"\tcolor: #BABABA;\n"
|
||||||
|
"}\n"
|
||||||
|
" </style>\n"
|
||||||
|
"\t</head>\n"
|
||||||
|
"\t<body class=\"usercheck_body_background_class\" unselectable=\"on\">\n"
|
||||||
|
"\t\t<div id=\"portal_main_view\" class=\"\" unselectable=\"on\">\n"
|
||||||
|
"\t\t\t<table height=\"100%\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\">\n"
|
||||||
|
"\t\t\t\t<tbody><tr>\n"
|
||||||
|
"\t\t\t\t\t<td id=\"portal_center_div\" valign=\"top\" unselectable=\"on\" class=\"usercheck_main_background_class\">\n"
|
||||||
|
"\t\t\t\t\t\t\n"
|
||||||
|
"\t\t\t\t\t\t<div class=\"usercheck_message_icon_class\" id=\"usercheck_img_div\" unselectable=\"on\">\n"
|
||||||
|
" <img style='display:block; width:60px;height:60px;' class=\"usercheck_message_icon_class\" id='usercheck_img_div' \n"
|
||||||
|
" src='data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAADkAAAA5CAYAAACMGIOFAAAFhElEQVRogc3bWaxdYxQH8N/dbWgNJRLKg5mW4iII+iA0HghVRQliSpCIKYb2QWJ8IObgxRTzVBLcVngzxRAx02q1hvJiCkGrA1oe1j7u6bG\n"
|
||||||
|
" /ffYZ7/0nNznn+9a31vqf/e31DWvdgZ9nHacHmIhDsTcmYTK2xKbYCCuxHD/hcyzBx3gVP3TbmbFd1LUbTsVM7NFEdqP8byL2bOhbiGfxBBZ3w7GBDp/kAGZgNqZ2w6EGvIWbMYR/2lWSdeDAdHyK5/SGoFzvc/gER7erpB2S24tfdp7m07Jb2BP\n"
|
||||||
|
" z8Ty2bXVwqyRPEb/qMa0a6hJmYEHuR2VUDTxjcDsubEH3IryC90UE/QYr8Cs2xyZiVkzGfjgMu1fQOwGP4yBcgrXNBlQJPONEpJtZwYFv8CAew5cV5BuxM07DmeIHaIYXcCJWlQk1m64binehGcEvcBZ2xbXaIygfd02u56xcbxmOFrFhgzKhMpJj\n"
|
||||||
|
" 8DQOL5FZnTs1iIfwVxOnquKvXN9grn9NiezheEr4W4gykrcpDzCrRIi/VpPp0gFW5fqnYmmJ3EzcmupMkTwBFzVxYDzOExuCXuMDHCACWQoXozDAFJHcHvdXNH4O7tEfor/hSLFGp/AAtmtsLCJ5JzZrwXg/ia4R0fTlRP9mwv/10EhyuvR7uKLEeD\n"
|
||||||
|
" +J/immZSryzsBR9Q31JAdwfWLgGhyi5OXW/6l7knTUvaHej3qSM/3/2FPD9fgQlxs9RD8QZIqwl3iiWJ/knMSAL3BT3ffRRPRG6Y3H7NqHGslBHJgQvlos+vUYLURX46pE31RxK/EfydSufpnY9RRhtBB9Gt8m+k5nmOT0hNC9+LvEwGgg+nduowj\n"
|
||||||
|
" HEiS3xpSE0JMVjIwGok8k2qdgYibOcUVYJKZrFYw00WXC30YM4NBMBJ0ipHYVKYw00ZS/e2fiKrEI77dhaCSJpvydnMnDbAE+b9PYSBFdkmiflImb7SJ81YHBkSCa8nerTPrE8UuHRvtN9NdE+6aZ9P3In10w3E+iqduJ8Zk0mdLLoRbQL6LjE+2rM\n"
|
||||||
|
" vye6NyiQ6P16AfR1Gu3PMOPic6dOjBYhF4TTfn7Y6Yk9LZprAy9JDo50b40k14P92/DUBX0iuh+ifbFmcjwFiG1p+0GekF0WqL9k0yksIswRbV8RLvoJtEdpJNFr2b4TvEOHk6uYKATdItoys+F+L52aJ6fEDpbd+sKitAp0bE4N9E3xPDNQOpwvDN\n"
|
||||||
|
" mlfvYFXRCdJaYrkV4lGGSH+HdhOB1IoXXa7RDdJzwrwhvy6tH6q8kb0wI7yJ9XdlttEp0jvCvCLfUPtRnmjNRzVF037MaB4sn3g/cgstK+u/D3XhTPM1GLBCFUutY/0muwxUJpeMwV2uJoE5Q5Ym+oZggwWNd7UtjwmdI5OGLMElUSnXrdNIMzYimTh\n"
|
||||||
|
" 3zNawWRam7C6VPJtNE0cNoIdqI3xQkj4tILpNed4iQPSRKTfqBVoieq+AaNZVOn4u7SpQdgfewT0XjneIxUVVZhjslUhplhRGX4KWS/l3FWnSl3q2jG+b63xZlpCnMw6WpzjKSa0WBxOslMrXFeIEoMurWFnCsSNYsyPWnoihxwDhZSWVWs2KllWJqz\n"
|
||||||
|
" msitwseEWUoV2LHJvIp7JiPX4qHpRf6Gl4UqfOVZUJV613H4A6cX0U4x0LDtXWLRUnaHyJyT8DG4ii3mzjwTpNOPBXhLjFFy7JuqD691uICvCbKX6pE1j30plT0d3E6eqbqgFZLQZ/BvtIbhl5jvojolQnSXlHvVyJpeyw+a2N8O1goCh2OwdetDu6k\n"
|
||||||
|
" PHtIVFkcL31M6xTv5PoHNQ9+SXQa8teJ/eyzIoCcIX7xKsW5KSwSZdiPGCX/TZDCNiJaDoqN/STD1coTRPCoVTEvEdein4pE6nfdduZfFQ03500WPlQAAAAASUVORK5CYII=' />\n"
|
||||||
|
" </div>\n"
|
||||||
|
"\t\t\t\t\t\t<div class=\"locale_message usercheck_title_class\" data-locale-message-constnat=\"LOCALE_PHISHING_TITLE\" unselectable=\"on\" style=\"color: rgb(237, 85, 82);font-size: 31px;\"><!-- CHECK_POINT_USERCHECK_TITLE_PLACEHOLDER--></div>\t\t\t\t\t\t\n"
|
||||||
|
"\t\t\t\t\t\t<div style=\"text-align: center;\" unselectable=\"on\">\n"
|
||||||
|
"\t\t\t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\" class=\"usercheck_content_table_class\">\n"
|
||||||
|
"\t\t\t\t\t\t\t\t<tbody><tr>\n"
|
||||||
|
"\t\t\t\t\t\t\t\t\t<td>\n"
|
||||||
|
"\t\t\t\t\t\t\t\t\t\t<div class=\"usercheck_message_class\" unselectable=\"on\" style=\"FONT-FAMILY: 'Arial', 'sans-serif'\"><span class=\"locale_message\" data-locale-message-constnat=\"LOCALE_PHISHING_DOMAINS\" ><p><!-- CHECK_POINT_USERCHECK_BODY_PLACEHOLDER--></p> <p class=\"instance_id_uuid\"><br><!-- CHECK_POINT_USERCHECK_UUID_PLACEHOLDER--></p></span>\n"
|
||||||
|
"</body></html>";
|
||||||
664
attachments/nginx/ngx_module/ngx_cp_initializer.c
Normal file
664
attachments/nginx/ngx_module/ngx_cp_initializer.c
Normal file
@@ -0,0 +1,664 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_initializer.c
|
||||||
|
#include "ngx_cp_initializer.h"
|
||||||
|
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <ngx_log.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_string.h>
|
||||||
|
#include <ngx_files.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
#include "ngx_cp_io.h"
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_static_content.h"
|
||||||
|
#include "ngx_cp_compression.h"
|
||||||
|
#include "attachment_types.h"
|
||||||
|
#include "ngx_http_cp_attachment_module.h"
|
||||||
|
|
||||||
|
typedef enum ngx_cp_attachment_registration_state {
|
||||||
|
SET_UNIQUE_ID,
|
||||||
|
RESGISTER_TO_NODE,
|
||||||
|
LOAD_CONFIG,
|
||||||
|
LOAD_IPC,
|
||||||
|
DONE
|
||||||
|
} ngx_cp_attachment_registration_state_e; ///< Indicates the current initialization stage.
|
||||||
|
|
||||||
|
char unique_id[MAX_NGINX_UID_LEN] = ""; // Holds the unique identifier for this instance.
|
||||||
|
char shared_verdict_signal_path[128]; // Holds the path associating the attachment and service.
|
||||||
|
|
||||||
|
int registration_socket = -1; // Holds the file descriptor used for registering the instance.
|
||||||
|
|
||||||
|
struct sockaddr_un server;
|
||||||
|
|
||||||
|
uint32_t nginx_user_id, nginx_group_id; // Hold the process UID and GID respectively.
|
||||||
|
|
||||||
|
int
|
||||||
|
exchange_communication_data_with_service(
|
||||||
|
int socket,
|
||||||
|
void *data,
|
||||||
|
uint32_t size,
|
||||||
|
ngx_cp_comm_direction_e direction,
|
||||||
|
struct timeval *remaining_timeout)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
ngx_int_t retry;
|
||||||
|
// `remaining_size` and `cur_data_ptr` are used to keep track of where we are in the memory.
|
||||||
|
// This allows us to read to\write from the socket in parts (if we need to).
|
||||||
|
int remaining_size = size;
|
||||||
|
char *cur_data_ptr = data;
|
||||||
|
|
||||||
|
while (remaining_size > 0) {
|
||||||
|
if (direction == WRITE_TO_SOCKET){
|
||||||
|
res = write(socket, (void *)cur_data_ptr, remaining_size);
|
||||||
|
} else {
|
||||||
|
// The read operation must not block the attachment indefinitely.
|
||||||
|
// To avoid that we check whether the socket has data to be read prior to the read operation.
|
||||||
|
// If the socket doesn't have data to be read from within a reasonable time, we treat this as an error.
|
||||||
|
for (retry = 0; retry < 3; retry++) {
|
||||||
|
struct pollfd s_poll;
|
||||||
|
s_poll.fd = socket;
|
||||||
|
s_poll.events = POLLIN;
|
||||||
|
s_poll.revents = 0;
|
||||||
|
res = poll(&s_poll, 1, 1000);
|
||||||
|
if (res > 0 && (s_poll.revents & POLLIN) != 0) break; // Socket is ready to be read from
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != -1) {
|
||||||
|
res = read(socket, (void *)cur_data_ptr, remaining_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `res` is -1 in case of an error: either write or read functions failed or socket wasn't available.
|
||||||
|
if (res < 0) {
|
||||||
|
close(socket);
|
||||||
|
socket = -1;
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_TRACE, "Failed to communicate with the socket");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining_size -= res;
|
||||||
|
cur_data_ptr += res;
|
||||||
|
|
||||||
|
// If the operation exceeded the allowed time, treat it as a failure.
|
||||||
|
if (is_timeout_reached(remaining_timeout)) {
|
||||||
|
close(socket);
|
||||||
|
socket = -1;
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_TRACE, "Reached timeout while communicating with the socket");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initialize socket communication with the serive.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
init_signaling_socket()
|
||||||
|
{
|
||||||
|
uint8_t initialization_ack;
|
||||||
|
int res = 0;
|
||||||
|
uint8_t uid_size_to_send = strlen(unique_id);
|
||||||
|
struct timeval timeout = get_timeout_val_sec(1);
|
||||||
|
|
||||||
|
// Close the old socket if there was one.
|
||||||
|
if (comm_socket > 0) {
|
||||||
|
close(comm_socket);
|
||||||
|
comm_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a new socket
|
||||||
|
comm_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (comm_socket < 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Could not create socket");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.sun_family = AF_UNIX;
|
||||||
|
strncpy(server.sun_path, shared_verdict_signal_path, sizeof(server.sun_path) - 1);
|
||||||
|
|
||||||
|
if (connect(comm_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) == -1) {
|
||||||
|
close(comm_socket);
|
||||||
|
comm_socket = -1;
|
||||||
|
write_dbg_if_needed(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Could not connect to nano service. Path: %s, Error: %s",
|
||||||
|
server.sun_path,
|
||||||
|
strerror(errno)
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the following information to the service (in this order):
|
||||||
|
// 1. The length of the unique identifier for this instance.
|
||||||
|
// 2. The unique identifier for this instance.
|
||||||
|
// 3. The process UID.
|
||||||
|
// 4. The process GID.
|
||||||
|
// If any of them fail - return an error.
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
comm_socket,
|
||||||
|
&uid_size_to_send,
|
||||||
|
sizeof(uid_size_to_send),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send unique id size");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
comm_socket,
|
||||||
|
unique_id,
|
||||||
|
uid_size_to_send,
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send unique id %s", unique_id);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(comm_socket, &nginx_user_id, sizeof(uint32_t), WRITE_TO_SOCKET, &timeout);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send nginx user id");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
comm_socket,
|
||||||
|
&nginx_group_id,
|
||||||
|
sizeof(uint32_t),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send nginx group id");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an acknowledgement form the service that communication has been established.
|
||||||
|
timeout = get_timeout_val_sec(1);
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
comm_socket,
|
||||||
|
&initialization_ack,
|
||||||
|
sizeof(initialization_ack),
|
||||||
|
READ_FROM_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to read registration ack");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Successfully connected on client socket %d", comm_socket);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_CONTAINER_LEN 12 // Maximum size for container identifier
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
get_docker_id(char **_docker_id)
|
||||||
|
{
|
||||||
|
// We keep the container ID as a static variable so we won't have to read it multiple times.
|
||||||
|
// The `already_evaluated` variable indicate if we already have the identifier.
|
||||||
|
static char docker_id[MAX_CONTAINER_LEN + 1];
|
||||||
|
static int already_evaluated = 0;
|
||||||
|
const char *container_id_file_path = "/proc/self/cgroup";
|
||||||
|
if (already_evaluated) {
|
||||||
|
// Already found the identifier before, just return the answer.
|
||||||
|
*_docker_id = docker_id;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
docker_id[0] = '\0';
|
||||||
|
|
||||||
|
FILE *file = fopen(container_id_file_path, "r");
|
||||||
|
if (file == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to open %s", container_id_file_path);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "opened file %s", container_id_file_path);
|
||||||
|
|
||||||
|
// Reading the file line by line.
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
while (getline(&line, &len, file) != -1) {
|
||||||
|
char *docker_ptr = strstr(line, "docker/");
|
||||||
|
if (docker_ptr == NULL) continue;
|
||||||
|
|
||||||
|
// We've found a line with "docker/" so the identifier will be right after that.
|
||||||
|
docker_ptr += strlen("docker/");
|
||||||
|
snprintf(docker_id, MAX_CONTAINER_LEN + 1, "%s", docker_ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// Return the answer and set the indication so we won't have to
|
||||||
|
*_docker_id = docker_id;
|
||||||
|
already_evaluated = 1;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Register the attachment instance with the attachment manager to associate it with a service.
|
||||||
|
/// @param[in] request Points to an HTTP request, needed to get the number of workers.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
register_to_attachments_manager(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
uint8_t path_length;
|
||||||
|
int res = 0;
|
||||||
|
uint8_t family_name_size = strlen(unique_id);
|
||||||
|
uint8_t attachment_type = NGINX_ATT_ID;
|
||||||
|
uint8_t worker_id = ngx_worker + 1;
|
||||||
|
uint8_t workers_amount = get_num_of_workers(request);
|
||||||
|
char *family_name = NULL;
|
||||||
|
int cur_errno = 0; // temp fix for errno changing during print
|
||||||
|
struct timeval timeout = get_timeout_val_sec(1);
|
||||||
|
|
||||||
|
if (get_docker_id(&family_name) == NGX_ERROR) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Could not evaluate family name");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
family_name_size = strlen(family_name);
|
||||||
|
|
||||||
|
// If there was an old socket, close it.
|
||||||
|
if (registration_socket > 0) {
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect a new socket.
|
||||||
|
registration_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (registration_socket < 0) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Could not create socket");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.sun_family = AF_UNIX;
|
||||||
|
strncpy(server.sun_path, SHARED_REGISTRATION_SIGNAL_PATH, sizeof(server.sun_path) - 1);
|
||||||
|
|
||||||
|
if (connect(registration_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) == -1) {
|
||||||
|
cur_errno = errno;
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
write_dbg_if_needed(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Could not connect to nano service. Path: %s, Error: %s, Errno: %d",
|
||||||
|
server.sun_path,
|
||||||
|
strerror(errno),
|
||||||
|
cur_errno
|
||||||
|
);
|
||||||
|
if (cur_errno == ENOENT) {
|
||||||
|
strncpy(shared_verdict_signal_path, SHARED_VERDICT_SIGNAL_PATH, 128);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send to the attachment manager the following details:
|
||||||
|
// 1. The type of the attachment (fixed NGINX).
|
||||||
|
// 2. The number of this worker.
|
||||||
|
// 3. The total amount of workers.
|
||||||
|
// 4. The size of the docker ID.
|
||||||
|
// 5. If the docker ID isn't empty (size 0), the docker id itself.
|
||||||
|
// If any of these fail - return an error.
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
&attachment_type,
|
||||||
|
sizeof(attachment_type),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send attachment type");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
&worker_id,
|
||||||
|
sizeof(worker_id),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send worker ID");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
&workers_amount,
|
||||||
|
sizeof(workers_amount),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send workers amount");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
&family_name_size,
|
||||||
|
sizeof(family_name_size),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name size");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family_name_size > 0) {
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
family_name,
|
||||||
|
family_name_size,
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from the attachment manager:
|
||||||
|
// 1. The length of signal path.
|
||||||
|
// 2. The signal path itsel.
|
||||||
|
// If that fails - return an error.
|
||||||
|
timeout = get_timeout_val_sec(1);
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
&path_length,
|
||||||
|
sizeof(path_length),
|
||||||
|
READ_FROM_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to read path length");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
registration_socket,
|
||||||
|
shared_verdict_signal_path,
|
||||||
|
path_length,
|
||||||
|
READ_FROM_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to read socket path");
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Successfully go the shared communication path - add null termination and exit.
|
||||||
|
shared_verdict_signal_path[path_length] = '\0';
|
||||||
|
int32_t dbg_id = worker_id;
|
||||||
|
int32_t dbg_size = workers_amount_to_send;
|
||||||
|
write_dbg_if_needed(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Successfully registered on client. socket: %d, instance ID: %d, instances amount: %d, received path: %s",
|
||||||
|
registration_socket,
|
||||||
|
dbg_id,
|
||||||
|
dbg_size,
|
||||||
|
shared_verdict_signal_path
|
||||||
|
);
|
||||||
|
close(registration_socket);
|
||||||
|
registration_socket = -1;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
get_unique_id()
|
||||||
|
{
|
||||||
|
return unique_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
set_unique_id()
|
||||||
|
{
|
||||||
|
int is_container_env = 0;
|
||||||
|
static const int max_container_id_len = 12;
|
||||||
|
const char *container_id_file_path = "/proc/self/cgroup";
|
||||||
|
|
||||||
|
unsigned int unique_id_size = 0;
|
||||||
|
if (strlen(unique_id) > 0) return NGX_OK;
|
||||||
|
|
||||||
|
FILE *file = fopen(container_id_file_path, "r");
|
||||||
|
if (file == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to open %s", container_id_file_path);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
char docker_id[max_container_id_len + 1];
|
||||||
|
memset(docker_id, '\0', max_container_id_len + 1);
|
||||||
|
size_t len = 0;
|
||||||
|
while (getline(&line, &len, file) != -1) {
|
||||||
|
char *docker_ptr = strstr(line, "docker/");
|
||||||
|
if (docker_ptr == NULL) continue;
|
||||||
|
|
||||||
|
is_container_env = 1;
|
||||||
|
docker_ptr += strlen("docker/");
|
||||||
|
snprintf(docker_id, max_container_id_len + 1, "%s", docker_ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
fclose(file);
|
||||||
|
long unsigned int ngx_worker_id = ngx_worker + 1;
|
||||||
|
if (is_container_env) {
|
||||||
|
unique_id_size += strlen(docker_id) + 1 + get_number_of_digits(ngx_worker_id) + 1;
|
||||||
|
snprintf(unique_id, unique_id_size, "%s_%lu", docker_id, ngx_worker_id);
|
||||||
|
} else {
|
||||||
|
unique_id_size += get_number_of_digits(ngx_worker_id) + 1;
|
||||||
|
snprintf(unique_id, unique_id_size, "%lu", ngx_worker_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "Successfully set attachment's unique_id: '%s'", unique_id);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_cp_attachment_init_process(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
ngx_pool_t *memory_pool;
|
||||||
|
nginx_user_id = getuid();
|
||||||
|
nginx_group_id = getgid();
|
||||||
|
static int need_registration = 1;
|
||||||
|
num_of_connection_attempts++;
|
||||||
|
|
||||||
|
// Best-effort attempt to read the configuration before we start.
|
||||||
|
init_general_config(SHARED_ATTACHMENT_CONF_PATH);
|
||||||
|
|
||||||
|
// Initalizing the various elements of the system (if needed):
|
||||||
|
// 1. Get the unique identifier.
|
||||||
|
// 2. Register with the attachment manager.
|
||||||
|
// 3. Signal to the service to start communication.
|
||||||
|
// 4. Make sure that the configuration is up-to-date.
|
||||||
|
if (access(SHARED_REGISTRATION_SIGNAL_PATH, F_OK) != 0) {
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Attachment registration manager is turned off");
|
||||||
|
return NGX_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(unique_id, "", 1) == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Setting attachment's unique id");
|
||||||
|
if (set_unique_id() == NGX_ERROR) {
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "Failed to set attachment's unique_id");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_registration) {
|
||||||
|
if (register_to_attachments_manager(request) == NGX_ERROR) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_INFO, "Failed to register to Attachments Manager service");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
need_registration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comm_socket < 0) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Registering to nano service");
|
||||||
|
if (init_signaling_socket() == NGX_ERROR) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Failed to register to the Nano Service");
|
||||||
|
need_registration = 1;
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_general_config(SHARED_ATTACHMENT_CONF_PATH) == NGX_ERROR) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_INFO, "Failed to initialize attachment's configuration");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initalize the the communication channel with the service.
|
||||||
|
// If we encounter repeated failures - we will restart the whole communication.
|
||||||
|
static const int max_ipc_init_retry_count = 10;
|
||||||
|
static int max_retry_count = max_ipc_init_retry_count;
|
||||||
|
if (nano_service_ipc == NULL) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_INFO, "Initializing IPC channel");
|
||||||
|
nano_service_ipc = initIpc(
|
||||||
|
unique_id,
|
||||||
|
nginx_user_id,
|
||||||
|
nginx_group_id,
|
||||||
|
0,
|
||||||
|
num_of_nginx_ipc_elements,
|
||||||
|
write_dbg_impl
|
||||||
|
);
|
||||||
|
if (nano_service_ipc == NULL) {
|
||||||
|
if (max_retry_count-- == 0) {
|
||||||
|
restart_communication(request);
|
||||||
|
max_retry_count = max_ipc_init_retry_count;
|
||||||
|
}
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_INFO, "Failed to initialize IPC with nano service");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max_retry_count = max_ipc_init_retry_count;
|
||||||
|
|
||||||
|
// Initialize internal resources.
|
||||||
|
if (!is_static_resources_table_initialized()) {
|
||||||
|
memory_pool = get_memory_pool();
|
||||||
|
if (memory_pool == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Cannot initialize static resources. No memory pool has been allocated.");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Initializing static resources");
|
||||||
|
if (init_static_resources(memory_pool) != NGX_OK) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to initialize static resources");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_compression_debug_printing_initialized()) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Initializing compression debug message printing");
|
||||||
|
initialize_compression_debug_printing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to indicate about successful registration only once in default level
|
||||||
|
write_dbg(dbg_is_needed ? DBG_LEVEL_DEBUG : DBG_LEVEL_INFO, "NGINX attachment (UID='%s') successfully registered to nano service after %d attempts.", unique_id, num_of_connection_attempts);
|
||||||
|
|
||||||
|
dbg_is_needed = 1;
|
||||||
|
num_of_connection_attempts = 0;
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
restart_communication(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_TRACE, "Restarting communication channels with nano service");
|
||||||
|
if (nano_service_ipc != NULL) {
|
||||||
|
destroyIpc(nano_service_ipc, 0);
|
||||||
|
nano_service_ipc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_signaling_socket() == NGX_ERROR) {
|
||||||
|
if (register_to_attachments_manager(request) == NGX_ERROR) {
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Failed to register to Attachments Manager service");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_signaling_socket() == NGX_ERROR) return -1;
|
||||||
|
}
|
||||||
|
nano_service_ipc = initIpc(unique_id, nginx_user_id, nginx_group_id, 0, num_of_nginx_ipc_elements, write_dbg_impl);
|
||||||
|
if (nano_service_ipc == NULL) return -2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
disconnect_communication()
|
||||||
|
{
|
||||||
|
if (comm_socket > 0) {
|
||||||
|
close(comm_socket);
|
||||||
|
comm_socket = -1;
|
||||||
|
}
|
||||||
|
if (nano_service_ipc != NULL) {
|
||||||
|
destroyIpc(nano_service_ipc, 0);
|
||||||
|
nano_service_ipc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
handle_shmem_corruption()
|
||||||
|
{
|
||||||
|
if (nano_service_ipc == NULL) return NGX_OK;
|
||||||
|
|
||||||
|
if (isCorruptedShmem(nano_service_ipc, 0)) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Shared memory is corrupted! restarting communication");
|
||||||
|
disconnect_communication();
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
isIpcReady()
|
||||||
|
{
|
||||||
|
return nano_service_ipc != NULL && comm_socket > 0;
|
||||||
|
}
|
||||||
96
attachments/nginx/ngx_module/ngx_cp_initializer.h
Normal file
96
attachments/nginx/ngx_module/ngx_cp_initializer.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_initializer.h
|
||||||
|
#ifndef __NGX_CP_INITIALIZER_H__
|
||||||
|
#define __NGX_CP_INITIALIZER_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
typedef enum ngx_cp_comm_direction {
|
||||||
|
READ_FROM_SOCKET,
|
||||||
|
WRITE_TO_SOCKET
|
||||||
|
} ngx_cp_comm_direction_e; ///< Indicate whether communication exchange is to read or to write from a socket.
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initialize all the attachments resources and communication channels.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
/// - #NGX_ABORT
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_cp_attachment_init_process();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Preforms send\receive information to\from the service via a socket.
|
||||||
|
/// @param[in] socket The file descriptor of the socket to work with.
|
||||||
|
/// @param[in, out] data An allocated memory which data will be read from (if sending) or written to (if recieving).
|
||||||
|
/// @param[in] size The number of bytes in the allocated memoty.
|
||||||
|
/// @param[in] direction Sets whether we read from the socket or write to it:
|
||||||
|
/// - #READ_FROM_SOCKET
|
||||||
|
/// - #WRITE_TO_SOCKET
|
||||||
|
/// @param[in] remaining_timeout Points to the maximal time point the function is allowed to reach.
|
||||||
|
/// @return int - positive if successful, other values indicate an error.
|
||||||
|
///
|
||||||
|
int exchange_communication_data_with_service(
|
||||||
|
int socket,
|
||||||
|
void *data,
|
||||||
|
uint32_t size,
|
||||||
|
ngx_cp_comm_direction_e direction,
|
||||||
|
struct timeval *remaining_timeout);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get an identifier that distinguish between different instances running on the same machine.
|
||||||
|
/// @return A null-terminated string that is unique to this instance.
|
||||||
|
///
|
||||||
|
const char * get_unique_id();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get an identifier for the current docker instance.
|
||||||
|
/// @param[out] _docker_id Will point to the null terminated string (which shouldn't be freed) indentifier
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t get_docker_id(char **_docker_id);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Closes any existing communication to the service and tries to open a new one.
|
||||||
|
/// @param[in] request Points to an HTTP request, needed to get the number of workers.
|
||||||
|
/// @returns int - 0 on success, negative number on failure.
|
||||||
|
///
|
||||||
|
int restart_communication(ngx_http_request_t *request);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Checks that the shared memory with the service isn't corrupted, disconnect if it is.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t handle_shmem_corruption();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Closes all the communication channels with the service.
|
||||||
|
///
|
||||||
|
void disconnect_communication();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Checks if communication with the service is up and running.
|
||||||
|
/// @returns ngx_int_t - 1 if communication is active, otherwise 0.
|
||||||
|
///
|
||||||
|
ngx_int_t isIpcReady();
|
||||||
|
|
||||||
|
#endif // __NGX_CP_INITIALIZER_H__
|
||||||
1097
attachments/nginx/ngx_module/ngx_cp_io.c
Normal file
1097
attachments/nginx/ngx_module/ngx_cp_io.c
Normal file
File diff suppressed because it is too large
Load Diff
201
attachments/nginx/ngx_module/ngx_cp_io.h
Normal file
201
attachments/nginx/ngx_module/ngx_cp_io.h
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_io.h
|
||||||
|
#ifndef __NGX_CP_IO_H__
|
||||||
|
#define __NGX_CP_IO_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "shmem_ipc.h"
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
#include "ngx_cp_custom_response.h"
|
||||||
|
#include "ngx_cp_hooks.h"
|
||||||
|
|
||||||
|
#define INSPECTION_IRRELEVANT NGX_DECLINED
|
||||||
|
|
||||||
|
extern SharedMemoryIPC *nano_service_ipc; ///< Nano service's IPC.
|
||||||
|
extern int comm_socket; ///< Communication socket.
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Receives nano service's response.
|
||||||
|
/// @details The function awaits for the expected_replies of replies from the nano service.
|
||||||
|
/// The recieved verdict is saved onto the verdict argument and depends on the reply
|
||||||
|
/// one of the ngx_int_t returns.
|
||||||
|
/// @param[in, out] expected_replies Amount of expected replies.
|
||||||
|
/// @param[in, out] verdict Value to save the verdict onto:
|
||||||
|
/// - #TRAFFIC_VERDICT_INSPECT
|
||||||
|
/// - #TRAFFIC_VERDICT_INJECT
|
||||||
|
/// - #TRAFFIC_VERDICT_DROP
|
||||||
|
/// - #TRAFFIC_VERDICT_ACCEPT
|
||||||
|
/// - #TRAFFIC_VERDICT_IRRELEVANT
|
||||||
|
/// - #TRAFFIC_VERDICT_RECONF
|
||||||
|
/// @param[in] cur_session_id Session's Id.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in] modification_list
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_HTTP_FORBIDDEN
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_reply_receiver(
|
||||||
|
ngx_int_t *expected_replies,
|
||||||
|
ngx_http_cp_verdict_e *verdict,
|
||||||
|
uint32_t cur_session_id,
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_http_cp_modification_list **modification_list
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends meta data to the nano service.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in] cur_request_id Request session's Id.
|
||||||
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_meta_data_sender(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
uint32_t cur_request_id,
|
||||||
|
ngx_uint_t *num_messages_sent
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends end of a transaction to the nano service.
|
||||||
|
/// @param[in] end_transaction_type Sets the transaction type, can be of the values:
|
||||||
|
/// - #REQUEST_END
|
||||||
|
/// - #RESPONSE_END
|
||||||
|
/// @param[in] cur_request_id Request session's Id.
|
||||||
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_end_transaction_sender(
|
||||||
|
ngx_http_chunk_type_e end_transaction_type,
|
||||||
|
uint32_t cur_request_id,
|
||||||
|
ngx_uint_t *num_messages_sent
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends response code to the nano service.
|
||||||
|
/// @param[in] response_code response code to send.
|
||||||
|
/// @param[in] cur_request_id Request session's Id.
|
||||||
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_res_code_sender(
|
||||||
|
uint16_t response_code,
|
||||||
|
uint32_t cur_request_id,
|
||||||
|
ngx_uint_t *num_messages_sent
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends content length to the nano service.
|
||||||
|
/// @param[in] content_length_n content length to send.
|
||||||
|
/// @param[in] cur_req_id Request session's Id.
|
||||||
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_content_length_sender(
|
||||||
|
uint64_t content_length_n,
|
||||||
|
uint32_t cur_req_id,
|
||||||
|
ngx_uint_t *num_messages_sent
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends request/response headers to the nano service.
|
||||||
|
/// @param[in] headers Headers to be sent.
|
||||||
|
/// @param[in, out] header_type Sets the header type, can be of the values:
|
||||||
|
/// - #REQUEST_HEADER
|
||||||
|
/// - #RESPONSE_HEADER
|
||||||
|
/// @param[in] cur_request_id Request session's Id.
|
||||||
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_header_sender(
|
||||||
|
ngx_list_part_t *headers,
|
||||||
|
ngx_http_chunk_type_e header_type,
|
||||||
|
uint32_t cur_request_id,
|
||||||
|
ngx_uint_t *num_messages_sent
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends request/response bodies to the nano service.
|
||||||
|
/// @param[in] input NGX chain.
|
||||||
|
/// @param[in] body_type Sets the body type, can be of the values:
|
||||||
|
/// - #REQUEST_BODY
|
||||||
|
/// - #RESPONSE_BODY
|
||||||
|
/// @param[in, out] session_data Session's data.
|
||||||
|
/// @param[in, out] is_last_part If the last part will be saved onto this parameter.
|
||||||
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @param[in, out] next_elem_to_inspect Next NGX chain to inspect.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_cp_body_sender(
|
||||||
|
ngx_chain_t *input,
|
||||||
|
ngx_http_chunk_type_e body_type,
|
||||||
|
ngx_http_cp_session_data *session_data,
|
||||||
|
ngx_int_t *is_last_part,
|
||||||
|
ngx_uint_t *num_messages_sent,
|
||||||
|
ngx_chain_t **next_elem_to_inspect
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Checks if reconf is needed and reconfigs if necessary.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_is_reconf_needed();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends metric data to the server.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t ngx_http_cp_metric_data_sender();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Updates session related metric fields.
|
||||||
|
/// @param[in] session_start_time Session's start time.
|
||||||
|
/// @param[in] req_proccesing_time Session's request processing time.
|
||||||
|
/// @param[in] res_proccesing_time Session's response processing time.
|
||||||
|
///
|
||||||
|
void ngx_http_cp_report_time_metrics(
|
||||||
|
clock_t session_start_time,
|
||||||
|
double req_proccesing_time,
|
||||||
|
double res_proccesing_time
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_IO_H__
|
||||||
121
attachments/nginx/ngx_module/ngx_cp_metric.c
Normal file
121
attachments/nginx/ngx_module/ngx_cp_metric.c
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_metric.c
|
||||||
|
#include <ngx_config.h>
|
||||||
|
|
||||||
|
#include "ngx_cp_metric.h"
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
|
||||||
|
uint64_t metric_data[METRIC_TYPES_COUNT];
|
||||||
|
uint64_t metric_average_data_divisor[METRIC_TYPES_COUNT];
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_metric_data()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0 ; i < METRIC_TYPES_COUNT ; i++) {
|
||||||
|
metric_data[i] = 0;
|
||||||
|
metric_average_data_divisor[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Increment the provided metric type by the value.
|
||||||
|
/// @param[in] metric_type Metric type.
|
||||||
|
/// @param[in] value Value to increment the metric type.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
updateCounterMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||||
|
{
|
||||||
|
metric_data[metric_type] += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Update the average metric field.
|
||||||
|
/// @param[in] metric_type Metric type.
|
||||||
|
/// @param[in] value Value to add to the average metric.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
updateAverageMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||||
|
{
|
||||||
|
metric_data[metric_type] =
|
||||||
|
(((metric_data[metric_type] * metric_average_data_divisor[metric_type]) + value) / (metric_average_data_divisor[metric_type] + 1));
|
||||||
|
metric_average_data_divisor[metric_type] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets the value as metric if it is higher than the current metric's value.
|
||||||
|
/// @param[in] metric_type Metric type to set the value in.
|
||||||
|
/// @param[in] value Value to set.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
updateMaxMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||||
|
{
|
||||||
|
if (metric_data[metric_type] < value) metric_data[metric_type] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets the value as metric if it is lower than the current metric's value.
|
||||||
|
/// @param[in] metric_type Metric type to set the value in.
|
||||||
|
/// @param[in] value Value to set.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
updateMinMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||||
|
{
|
||||||
|
if (metric_data[metric_type] == 0) {
|
||||||
|
metric_data[metric_type] = value;
|
||||||
|
} else if (metric_data[metric_type] > value) {
|
||||||
|
metric_data[metric_type] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
updateMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||||
|
{
|
||||||
|
switch (metric_type) {
|
||||||
|
case CPU_USAGE:
|
||||||
|
case AVERAGE_VM_MEMORY_USAGE:
|
||||||
|
case AVERAGE_RSS_MEMORY_USAGE:
|
||||||
|
case AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT:
|
||||||
|
case AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT:
|
||||||
|
case AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||||
|
case AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||||
|
case AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
|
||||||
|
if (value != 0) updateAverageMetricField(metric_type, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAX_VM_MEMORY_USAGE:
|
||||||
|
case MAX_RSS_MEMORY_USAGE:
|
||||||
|
case MAX_REQ_BODY_SIZE_UPON_TIMEOUT:
|
||||||
|
case MAX_RES_BODY_SIZE_UPON_TIMEOUT:
|
||||||
|
case MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||||
|
case MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||||
|
case MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
|
||||||
|
if (value != 0) updateMaxMetricField(metric_type, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIN_REQ_BODY_SIZE_UPON_TIMEOUT:
|
||||||
|
case MIN_RES_BODY_SIZE_UPON_TIMEOUT:
|
||||||
|
case MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||||
|
case MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||||
|
case MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
|
||||||
|
if (value != 0) updateMinMetricField(metric_type, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
updateCounterMetricField(metric_type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
attachments/nginx/ngx_module/ngx_cp_metric.h
Normal file
35
attachments/nginx/ngx_module/ngx_cp_metric.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_metric.h
|
||||||
|
#ifndef __NGX_CP_METRIC_H__
|
||||||
|
#define __NGX_CP_METRIC_H__
|
||||||
|
|
||||||
|
#include <nginx_attachment_common.h>
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Depending on the metric type, set the provided value in the metric.
|
||||||
|
/// @param[in] metric_type Metric type to update.
|
||||||
|
/// @param[in] value Value to set.
|
||||||
|
///
|
||||||
|
void updateMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Goes over all the metrics and resets them to 0.
|
||||||
|
///
|
||||||
|
void reset_metric_data();
|
||||||
|
|
||||||
|
#endif // __NGX_CP_METRIC_H__
|
||||||
546
attachments/nginx/ngx_module/ngx_cp_static_content.c
Normal file
546
attachments/nginx/ngx_module/ngx_cp_static_content.c
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "ngx_cp_static_content.h"
|
||||||
|
|
||||||
|
#include <ngx_hash.h>
|
||||||
|
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_custom_response.h"
|
||||||
|
|
||||||
|
ngx_hash_t static_resources_hash_table; ///< Holds all the static resources data.
|
||||||
|
ngx_int_t is_static_resources_table_initialized_var = 0; ///< A flag if static resources hash table was initialized.
|
||||||
|
|
||||||
|
static const ngx_int_t not_a_static_resource = NOT_A_STATIC_RESOURCE; ///< Not a static resource variable.
|
||||||
|
static const unsigned int static_resource_res_headers_allow_ranges = 1; ///< Static resource result allow ranges.
|
||||||
|
static const unsigned int static_resource_res_headers_keepalive = 1; ///< Static resource result headers keep alive configuration.
|
||||||
|
static const ngx_int_t static_resource_res_headers_response_code = NGX_HTTP_OK; ///< Static resource good result response.
|
||||||
|
static const ngx_int_t default_max_hash_table_size = 512; ///< Maximum hash table size configuration.
|
||||||
|
static const ngx_int_t default_hash_table_bucket_size = 64;
|
||||||
|
static const ngx_int_t default_part_items_num = 100; ///< Default number of elements to be allocated for a static resources list during static resources initialization.
|
||||||
|
static const ngx_int_t initial_hash_table_data_value = 1; ///< Initial value of initiated table data.
|
||||||
|
|
||||||
|
static ngx_hash_init_t static_resources_hash_initializer; ///< NGINX Hash initialization settings.
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the static resources into the static_resources and return the number of resources.
|
||||||
|
/// @details Given a memory poll and static sources list. Attempts to open the static resource directory.
|
||||||
|
/// for each static data in the directory, allocate a new element in the static_resources list and load the
|
||||||
|
/// data into the new element.
|
||||||
|
/// @param[in, out] static_resources NGINX list of the static allocated resources.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool used to allocate data into.
|
||||||
|
/// @return ngx_int_t - The number of allocated static resources.
|
||||||
|
///
|
||||||
|
static ngx_uint_t
|
||||||
|
get_static_resources(ngx_list_t *static_resources, ngx_pool_t *memory_pool)
|
||||||
|
{
|
||||||
|
size_t num_static_resources = 0;
|
||||||
|
struct dirent *current_entry = NULL;
|
||||||
|
ngx_str_t *current_resource = NULL;
|
||||||
|
const char *static_resources_path = get_static_resources_path();
|
||||||
|
|
||||||
|
DIR *static_resources_directory = opendir(static_resources_path);
|
||||||
|
if (static_resources_directory == NULL) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to open the static resources directory. Path: %s, error: %s",
|
||||||
|
static_resources_path,
|
||||||
|
strerror(errno)
|
||||||
|
);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Successfully opened the static resources' directory: %s",
|
||||||
|
static_resources_path
|
||||||
|
);
|
||||||
|
while ((current_entry = readdir (static_resources_directory)) != NULL) {
|
||||||
|
// Iterates over the files in the static resources directory.
|
||||||
|
// Allocates a new element to the list and initialize it given the resources data.
|
||||||
|
if (strcmp(current_entry->d_name, ".") == 0 || strcmp(current_entry->d_name, "..") == 0) continue;
|
||||||
|
|
||||||
|
current_resource = ngx_list_push(static_resources);
|
||||||
|
if (current_resource == NULL) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_ERROR,
|
||||||
|
"Failed to allocate memory for a static resource path. Path: %s",
|
||||||
|
static_resources_path
|
||||||
|
);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Found static resource: %s", current_entry->d_name);
|
||||||
|
|
||||||
|
// Load the read data from the file onto the current resource element.
|
||||||
|
current_resource->len = strlen(current_entry->d_name);
|
||||||
|
current_resource->data = ngx_palloc(memory_pool, current_resource->len + 1);
|
||||||
|
ngx_memcpy(current_resource->data, current_entry->d_name, current_resource->len);
|
||||||
|
current_resource->data[current_resource->len] = '\0';
|
||||||
|
num_static_resources++;
|
||||||
|
}
|
||||||
|
closedir(static_resources_directory);
|
||||||
|
|
||||||
|
return num_static_resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initiates static resources hash table with the provided data in static_resources.
|
||||||
|
/// @details Takes the provided variables and initiates the static hash table that
|
||||||
|
/// is used throughout the attachment.
|
||||||
|
/// @param[in, out] static_resources NGINX list - data to be initiated
|
||||||
|
/// @param[in] num_static_resources The number of provided resources in static_resources.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool used to allocate data into.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
init_static_web_resources_hash_table(
|
||||||
|
ngx_list_t *static_resources,
|
||||||
|
const size_t num_static_resources,
|
||||||
|
ngx_pool_t *memory_pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_int_t init_hash_table_result;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Received %u static web resources. Initializing the static resources table.",
|
||||||
|
num_static_resources
|
||||||
|
);
|
||||||
|
|
||||||
|
// initiate the static hash table with the provided data in static_resources.
|
||||||
|
init_hash_table_result = init_hash_table(
|
||||||
|
memory_pool,
|
||||||
|
&static_resources_hash_initializer,
|
||||||
|
&static_resources_hash_table,
|
||||||
|
"static resources",
|
||||||
|
default_max_hash_table_size,
|
||||||
|
default_hash_table_bucket_size,
|
||||||
|
static_resources,
|
||||||
|
&initial_hash_table_data_value,
|
||||||
|
sizeof(initial_hash_table_data_value)
|
||||||
|
);
|
||||||
|
if (init_hash_table_result != NGX_OK) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to initialize static resources table");
|
||||||
|
free_list_from_pool(memory_pool, static_resources);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_static_resources_table_initialized_var = 1;
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully initialized the static resources table");
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
init_static_resources(ngx_pool_t *memory_pool)
|
||||||
|
{
|
||||||
|
size_t num_static_resources;
|
||||||
|
ngx_list_t *static_resources = ngx_list_create(memory_pool, default_part_items_num, sizeof(ngx_str_t));
|
||||||
|
if (static_resources == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate the list of static resource paths");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the static data saved into static_resources.
|
||||||
|
num_static_resources = get_static_resources(static_resources, memory_pool);
|
||||||
|
if (num_static_resources == (size_t)-1) {
|
||||||
|
free_list_from_pool(memory_pool, static_resources);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
if (num_static_resources == 0) {
|
||||||
|
free_list_from_pool(memory_pool, static_resources);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate the static_resources_hash_table with static_resources data.
|
||||||
|
if (init_static_web_resources_hash_table(static_resources, num_static_resources, memory_pool) != NGX_OK) {
|
||||||
|
free_list_from_pool(memory_pool, static_resources);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_list_from_pool(memory_pool, static_resources);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
is_static_resources_table_initialized(void)
|
||||||
|
{
|
||||||
|
return is_static_resources_table_initialized_var;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief
|
||||||
|
/// @param[in] null_terminated_uri Null terminated uri that holds the resource name.
|
||||||
|
/// @param[in, out] static_resource_name A variable to save data and len of the extracted resource name.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #1 Successed in getting a static resource name.
|
||||||
|
/// - #0 Failed to get static resource name.
|
||||||
|
///
|
||||||
|
ngx_int_t
|
||||||
|
get_static_resource_name(const ngx_str_t *null_terminated_uri, ngx_str_t *static_resource_name)
|
||||||
|
{
|
||||||
|
size_t uri_prefix_length;
|
||||||
|
u_char *last_uri_separator;
|
||||||
|
|
||||||
|
// Skip past last "/"
|
||||||
|
last_uri_separator = reverse_strnchr(null_terminated_uri->data, '/', null_terminated_uri->len);
|
||||||
|
if (last_uri_separator == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Invalid URI in HTTP request, URI: %s", null_terminated_uri->data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri_prefix_length = last_uri_separator - null_terminated_uri->data + 1;
|
||||||
|
static_resource_name->data = last_uri_separator + 1;
|
||||||
|
static_resource_name->len = null_terminated_uri->len - uri_prefix_length;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Validates that the provided static resource name is a valid.
|
||||||
|
/// @details The function checks if the static resources table has been properly intiliazed.
|
||||||
|
/// If it has, it initates a hash key and searchs for it in the static resources hash table.
|
||||||
|
/// If it finds it, returns 1, in any other case returns 0.
|
||||||
|
/// @param[in, out] static_resource_name NGINX string - resource name to be checked to be valid.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #1 Successed in getting a static resource.
|
||||||
|
/// - #0 Failed to get static resource.
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
is_static_resource_request(ngx_str_t *static_resource_name)
|
||||||
|
{
|
||||||
|
char *data;
|
||||||
|
ngx_uint_t key;
|
||||||
|
|
||||||
|
if (!is_static_resources_table_initialized()) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Cannot determine whether request is for a static resource: static resources' table is not initialized"
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Checking whether requested resource %s (name length: %u) is a static resource",
|
||||||
|
static_resource_name->data, static_resource_name->len
|
||||||
|
);
|
||||||
|
|
||||||
|
key = ngx_hash_key(static_resource_name->data, static_resource_name->len);
|
||||||
|
data = ngx_hash_find(&static_resources_hash_table, key, static_resource_name->data, static_resource_name->len);
|
||||||
|
if (data == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Requested resource %s is not a static resource", static_resource_name->data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_location_conf_root(
|
||||||
|
ngx_http_core_loc_conf_t *location_conf,
|
||||||
|
const ngx_str_t *root_name,
|
||||||
|
ngx_array_t *root_lengths,
|
||||||
|
ngx_array_t *root_values
|
||||||
|
)
|
||||||
|
{
|
||||||
|
location_conf->root.len = root_name->len;
|
||||||
|
location_conf->root.data = root_name->data;
|
||||||
|
location_conf->root_lengths = root_lengths;
|
||||||
|
location_conf->root_values = root_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends a static content response header.
|
||||||
|
/// @param[in, out] request
|
||||||
|
/// @param[in] static_resource_size
|
||||||
|
/// @param[in] static_resource_last_modified_time
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
send_static_content_response_headers(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
const off_t static_resource_size,
|
||||||
|
const time_t static_resource_last_modified_time
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_int_t send_headers_res = ngx_http_cp_response_headers_sender(
|
||||||
|
request,
|
||||||
|
static_resource_res_headers_response_code,
|
||||||
|
static_resource_size,
|
||||||
|
static_resource_last_modified_time,
|
||||||
|
static_resource_res_headers_allow_ranges,
|
||||||
|
static_resource_res_headers_keepalive
|
||||||
|
);
|
||||||
|
if (send_headers_res != NGX_OK) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to send headers for static content response");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Open a cached file at the location configuration.
|
||||||
|
/// @param[in] location_conf Location configuration regarding the file to be handled.
|
||||||
|
/// @param[in, out] open_files_cache A cache of opened files.
|
||||||
|
/// @param[in, out] file_path File path to the file to be read.
|
||||||
|
/// @param[in, out] open_file_info Information regarding the file to be read.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
open_cached_file(
|
||||||
|
ngx_http_core_loc_conf_t *location_conf,
|
||||||
|
ngx_open_file_cache_t *open_files_cache,
|
||||||
|
ngx_str_t *file_path,
|
||||||
|
ngx_open_file_info_t *open_file_info,
|
||||||
|
ngx_pool_t *memory_pool
|
||||||
|
)
|
||||||
|
{
|
||||||
|
open_file_info->read_ahead = location_conf->read_ahead;
|
||||||
|
open_file_info->directio = location_conf->directio;
|
||||||
|
open_file_info->valid = location_conf->open_file_cache_valid;
|
||||||
|
open_file_info->min_uses = location_conf->open_file_cache_min_uses;
|
||||||
|
open_file_info->errors = location_conf->open_file_cache_errors;
|
||||||
|
open_file_info->events = location_conf->open_file_cache_events;
|
||||||
|
|
||||||
|
return ngx_open_cached_file(open_files_cache, file_path, open_file_info, memory_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends the static resource.
|
||||||
|
/// @param[in, out] request NGINX request static resource request to be sent.
|
||||||
|
/// @param[in, out] static_resource_name A path to the static resource.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
send_static_resource(ngx_http_request_t *request, ngx_str_t *static_resource_name)
|
||||||
|
{
|
||||||
|
const char *static_resources_root_path = get_static_resources_path();
|
||||||
|
|
||||||
|
ngx_int_t open_file_res;
|
||||||
|
ngx_int_t send_headers_res;
|
||||||
|
ngx_open_file_info_t open_file_info;
|
||||||
|
ngx_str_t static_resource_path;
|
||||||
|
ngx_http_core_loc_conf_t *core_module_loc_conf = ngx_http_get_module_loc_conf(request, ngx_http_core_module);
|
||||||
|
|
||||||
|
ngx_str_t old_root = core_module_loc_conf->root;
|
||||||
|
ngx_array_t *old_root_lengths = core_module_loc_conf->root_lengths;
|
||||||
|
ngx_array_t *old_root_values = core_module_loc_conf->root_values;
|
||||||
|
ngx_str_t old_uri = request->uri;
|
||||||
|
|
||||||
|
ngx_str_t new_root = { strlen(static_resources_root_path), (u_char *)static_resources_root_path };
|
||||||
|
set_location_conf_root(core_module_loc_conf, &new_root, NULL, NULL);
|
||||||
|
request->uri = *static_resource_name;
|
||||||
|
|
||||||
|
// Map static_resource_path URI to file path.
|
||||||
|
if (ngx_http_map_uri_to_path(request, &static_resource_path, &new_root.len, 0) == NULL) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to map static resource's URI to file path. URI: %.*s",
|
||||||
|
request->uri.len,
|
||||||
|
request->uri.data
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
static_resource_path.len = new_root.len + static_resource_name->len;
|
||||||
|
|
||||||
|
ngx_memzero(&open_file_info, sizeof(ngx_open_file_info_t));
|
||||||
|
|
||||||
|
// Open static resource's file.
|
||||||
|
open_file_res = open_cached_file(
|
||||||
|
core_module_loc_conf,
|
||||||
|
core_module_loc_conf->open_file_cache,
|
||||||
|
&static_resource_path,
|
||||||
|
&open_file_info,
|
||||||
|
request->pool
|
||||||
|
);
|
||||||
|
if (open_file_res != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to open static resource's file. File path: %.*s",
|
||||||
|
static_resource_path.len,
|
||||||
|
static_resource_path.data
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send static content response headers.
|
||||||
|
send_headers_res = send_static_content_response_headers(request, open_file_info.size, open_file_info.mtime);
|
||||||
|
if (send_headers_res != NGX_OK) return send_headers_res;
|
||||||
|
if (request != request->main && open_file_info.size == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Tried to send empty file, sent only headers");
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_location_conf_root(core_module_loc_conf, &old_root, old_root_lengths, old_root_values);
|
||||||
|
request->uri = old_uri;
|
||||||
|
|
||||||
|
return ngx_http_cp_file_response_sender(
|
||||||
|
request,
|
||||||
|
&static_resource_path,
|
||||||
|
&open_file_info,
|
||||||
|
request == request->main,
|
||||||
|
request->connection->log,
|
||||||
|
request->pool
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @param[in, out] null_terminated_uri Null terminated Uri.
|
||||||
|
/// @param[in, out] static_resource_name Static resource name.
|
||||||
|
/// @param[in] handle_static_resource_result Results in handling the static resource request.
|
||||||
|
///
|
||||||
|
void
|
||||||
|
finalize_static_resource_response(
|
||||||
|
ngx_http_request_t *request,
|
||||||
|
ngx_str_t *null_terminated_uri,
|
||||||
|
ngx_str_t *static_resource_name,
|
||||||
|
const ngx_int_t handle_static_resource_result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ngx_int_t finalize_request_response_code;
|
||||||
|
|
||||||
|
// Frees null_terminated_uri data.
|
||||||
|
if (null_terminated_uri->data != NULL) {
|
||||||
|
ngx_pfree(request->pool, null_terminated_uri->data);
|
||||||
|
null_terminated_uri->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle_static_resource_result == not_a_static_resource) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Request is not for a static resource. Request's URI: %.*s",
|
||||||
|
request->uri.len,
|
||||||
|
request->uri.data
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug printing of request result response.
|
||||||
|
switch (handle_static_resource_result) {
|
||||||
|
case NGX_OK:
|
||||||
|
case NGX_DONE: {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Successfully sent requested static resource: %.*s",
|
||||||
|
static_resource_name->len,
|
||||||
|
static_resource_name->data
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NGX_AGAIN: {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Failed to finish sending requested static resource, retrying. Static resource: %.*s",
|
||||||
|
static_resource_name->len,
|
||||||
|
static_resource_name->data
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING, "Failed to send requested static resource: %.*s",
|
||||||
|
static_resource_name->len,
|
||||||
|
static_resource_name->data
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize response of the static response request.
|
||||||
|
if (
|
||||||
|
handle_static_resource_result == NGX_OK ||
|
||||||
|
handle_static_resource_result == NGX_DONE ||
|
||||||
|
handle_static_resource_result == NGX_AGAIN
|
||||||
|
) {
|
||||||
|
finalize_request_response_code = handle_static_resource_result;
|
||||||
|
} else {
|
||||||
|
finalize_request_response_code = NGX_HTTP_FORBIDDEN;
|
||||||
|
}
|
||||||
|
ngx_http_finalize_request(request, finalize_request_response_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
handle_static_resource_request(uint32_t session_id, ngx_http_cp_verdict_e *verdict, ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
ngx_str_t null_terminated_uri;
|
||||||
|
ngx_str_t static_resource_name;
|
||||||
|
ngx_int_t send_static_resource_res;
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Trying to serve requested resource as static content. URI: %.*s",
|
||||||
|
request->uri.len,
|
||||||
|
request->uri.data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (duplicate_ngx_string(&null_terminated_uri, &request->uri, request->pool) != NGX_OK) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to create a null terminated duplicate of URI. URI: %.*s",
|
||||||
|
request->uri.len,
|
||||||
|
request->uri.data
|
||||||
|
);
|
||||||
|
finalize_static_resource_response(request, &null_terminated_uri, &static_resource_name, NGX_ERROR);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get static resource name in static_resource_name.
|
||||||
|
if (!get_static_resource_name(&null_terminated_uri, &static_resource_name)) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to create a null terminated duplicate of URI. URI: %.*s",
|
||||||
|
request->uri.len,
|
||||||
|
request->uri.data
|
||||||
|
);
|
||||||
|
finalize_static_resource_response(request, &null_terminated_uri, &static_resource_name, NGX_ERROR);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates that static_resource_name is a valid request.
|
||||||
|
if (!is_static_resource_request(&static_resource_name)) {
|
||||||
|
finalize_static_resource_response(
|
||||||
|
request,
|
||||||
|
&null_terminated_uri,
|
||||||
|
&static_resource_name,
|
||||||
|
not_a_static_resource
|
||||||
|
);
|
||||||
|
return not_a_static_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
*verdict = TRAFFIC_VERDICT_IRRELEVANT;
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Request is for a static resource, inspection is not needed (session ID = %d)",
|
||||||
|
session_id
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sends the static request.
|
||||||
|
send_static_resource_res = send_static_resource(request, &static_resource_name);
|
||||||
|
finalize_static_resource_response(request, &null_terminated_uri, &static_resource_name, send_static_resource_res);
|
||||||
|
|
||||||
|
return NGX_DONE;
|
||||||
|
}
|
||||||
62
attachments/nginx/ngx_module/ngx_cp_static_content.h
Normal file
62
attachments/nginx/ngx_module/ngx_cp_static_content.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_static_content.h
|
||||||
|
#ifndef __NGX_CP_STATIC_CONTENT_H__
|
||||||
|
#define __NGX_CP_STATIC_CONTENT_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
|
||||||
|
#define NOT_A_STATIC_RESOURCE NGX_DECLINED
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initiates the static resources hash table.
|
||||||
|
/// @details Read the data from the static resources directory, load it into static_resources_hash_table.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool used to allocate data into.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
ngx_int_t init_static_resources(ngx_pool_t *memory_pool);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Returns if static resources hash has been initialized.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #1 Is intialized.
|
||||||
|
/// - #0 Is not intialized.
|
||||||
|
///
|
||||||
|
ngx_int_t is_static_resources_table_initialized(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Handles a resource request.
|
||||||
|
/// @details Recieves a static resource name, get the data out of the static resource hash table and sends it.
|
||||||
|
/// @param[in] session_id Session ID, used for debug message.
|
||||||
|
/// @param[in, out] verdict Verdict to be returned back to the callee.
|
||||||
|
/// - #TRAFFIC_VERDICT_IRRELEVANT If the function returns a static resource.
|
||||||
|
/// @param[in, out] request NGINX request.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NOT_A_STATIC_RESOURCE
|
||||||
|
/// - #NGX_DONE
|
||||||
|
///
|
||||||
|
ngx_int_t handle_static_resource_request(
|
||||||
|
uint32_t session_id,
|
||||||
|
ngx_http_cp_verdict_e *verdict,
|
||||||
|
ngx_http_request_t *request
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_STATIC_CONTENT_H__
|
||||||
133
attachments/nginx/ngx_module/ngx_cp_thread.c
Normal file
133
attachments/nginx/ngx_module/ngx_cp_thread.c
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_thread.c
|
||||||
|
#include "ngx_cp_thread.h"
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_hook_threads.h"
|
||||||
|
#include "ngx_cp_failing_state.h"
|
||||||
|
#include "ngx_cp_hooks.h"
|
||||||
|
#include "ngx_cp_metric.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int success_count = 0;
|
||||||
|
///
|
||||||
|
/// @brief runs the provided routine with the arguments in a non thread and without a timeout.
|
||||||
|
/// @param[in, out] thread_func A pointer to the provided routine to run in a thread.
|
||||||
|
/// @param[in, out] arg Routine's arguments.
|
||||||
|
/// @param[in, out] func_name Called thread timeout.
|
||||||
|
/// @returns 1
|
||||||
|
///
|
||||||
|
int
|
||||||
|
ngx_cp_run_without_thread_timeout(CpThreadRoutine thread_func, void *arg, char *func_name)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Executing cb in blocking mode, fn=%s", func_name);
|
||||||
|
|
||||||
|
thread_func(arg);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ngx_cp_run_in_thread_timeout(CpThreadRoutine thread_func, void *arg, int timeout_msecs, char *func_name)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
int ret = 0;
|
||||||
|
void *res = NULL;
|
||||||
|
pthread_t thread;
|
||||||
|
struct timespec ts;
|
||||||
|
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)arg;
|
||||||
|
|
||||||
|
if (inspection_mode == NO_THREAD) return ngx_cp_run_without_thread_timeout(thread_func, arg, func_name);
|
||||||
|
|
||||||
|
/// Runs the routine in a dedicated thread.
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Executing cb in dedicated thread, fn=%s", func_name);
|
||||||
|
if (pthread_create(&thread, NULL, thread_func, arg) != 0) {
|
||||||
|
updateMetricField(THREAD_FAILURE, 1);
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "pthread_create failed with errno=%d, fn=%s", errno, func_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inspection_mode == BLOCKING_THREAD) {
|
||||||
|
// Runs the function in a blocking thread.
|
||||||
|
status = pthread_join(thread, &res);
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "pthread_join returned from blocking call. status=%d, fn=%s", status, func_name);
|
||||||
|
return status == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
|
||||||
|
updateMetricField(THREAD_FAILURE, 1);
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_ERROR,
|
||||||
|
"clock_gettime(CLOCK_REALTIME) failed. Status: %s",
|
||||||
|
strerror(errno)
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert milliseconds to timespec
|
||||||
|
long long tv_nsec = ts.tv_nsec + (timeout_msecs % 1000) * 1000000;
|
||||||
|
|
||||||
|
ts.tv_sec += timeout_msecs / 1000 + tv_nsec / 1000000000;
|
||||||
|
ts.tv_nsec = tv_nsec % 1000000000;
|
||||||
|
|
||||||
|
status = pthread_timedjoin_np(thread, NULL, &ts);
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
/// Handling failed thread.
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, ctx->session_data_p);
|
||||||
|
write_dbg(
|
||||||
|
status == ETIMEDOUT ? DBG_LEVEL_DEBUG : DBG_LEVEL_WARNING,
|
||||||
|
"pthread_timejoin_np returns with %d (%s), successes so far %d, fn=%s",
|
||||||
|
status,
|
||||||
|
strerror(status),
|
||||||
|
success_count,
|
||||||
|
func_name
|
||||||
|
);
|
||||||
|
|
||||||
|
ret = pthread_cancel(thread);
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "pthread_cancel returns with ret=%d, fn=%s", ret, func_name);
|
||||||
|
|
||||||
|
ret = pthread_join(thread, &res);
|
||||||
|
if (ret != 0) {
|
||||||
|
updateMetricField(THREAD_FAILURE, 1);
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "pthread_join failed while fail open is enabled. RET=%d, fn=%s", ret, func_name);
|
||||||
|
return ret != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == PTHREAD_CANCELED) {
|
||||||
|
updateMetricField(THREAD_TIMEOUT, 1);
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "thread was canceled, fn=%s", func_name);
|
||||||
|
} else {
|
||||||
|
updateMetricField(THREAD_FAILURE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "pthread_join returns with ret=%d", ret);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Successfully executed thread. successes so far=%d, fn=%s",
|
||||||
|
success_count,
|
||||||
|
func_name
|
||||||
|
);
|
||||||
|
success_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status == 0;
|
||||||
|
}
|
||||||
38
attachments/nginx/ngx_module/ngx_cp_thread.h
Normal file
38
attachments/nginx/ngx_module/ngx_cp_thread.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_thread.h
|
||||||
|
#ifndef __NGX_CP_THREAD_H__
|
||||||
|
#define __NGX_CP_THREAD_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef void *(*CpThreadRoutine)(void *); ///< Func
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Runs the provided routine with the provided arguments as a thread
|
||||||
|
/// @details Runs the provided routine as thread_func(args) in a thread. Depending on the inspection
|
||||||
|
/// mode runs it with a timeout.
|
||||||
|
/// This provided routine updates metrics if needed such as THREAD_TIMEOUT and THREAD_FAILURE.
|
||||||
|
/// @param[in, out] thread_func A pointer to the provided routine to run in a thread.
|
||||||
|
/// @param[in, out] arg Routine's arguments.
|
||||||
|
/// @param[in, out] timeout_msecs Called thread timeout.
|
||||||
|
/// @param[in, out] func_name The name of the provided routine.
|
||||||
|
/// @return int
|
||||||
|
/// - #0 Thread success.
|
||||||
|
/// - #1 Thread fail.
|
||||||
|
///
|
||||||
|
int ngx_cp_run_in_thread_timeout(CpThreadRoutine thread_func, void *arg, int timeout_msecs, char *);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_THREAD_H__
|
||||||
1086
attachments/nginx/ngx_module/ngx_cp_utils.c
Normal file
1086
attachments/nginx/ngx_module/ngx_cp_utils.c
Normal file
File diff suppressed because it is too large
Load Diff
429
attachments/nginx/ngx_module/ngx_cp_utils.h
Normal file
429
attachments/nginx/ngx_module/ngx_cp_utils.h
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_cp_utils.h
|
||||||
|
#ifndef __NGX_CP_UTILS_H__
|
||||||
|
#define __NGX_CP_UTILS_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
|
||||||
|
#ifndef __FILENAME__
|
||||||
|
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define write_dbg(_dbg_level, fmt, ...) \
|
||||||
|
{ \
|
||||||
|
write_dbg_impl(_dbg_level, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||||
|
if ((_dbg_level) == DBG_LEVEL_ASSERT) assert(0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define write_dbg_if_needed(_dbg_level, fmt, ...) \
|
||||||
|
{ \
|
||||||
|
if ((dbg_is_needed) == 0) { \
|
||||||
|
write_dbg_impl(DBG_LEVEL_TRACE, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
write_dbg_impl(_dbg_level, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
if ((_dbg_level) == DBG_LEVEL_ASSERT) assert(0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
extern ngx_int_t fail_mode_verdict;
|
||||||
|
extern ngx_int_t dbg_is_needed;
|
||||||
|
extern ngx_int_t num_of_connection_attempts;
|
||||||
|
extern ngx_uint_t content_length_would_change;
|
||||||
|
extern ngx_uint_t fail_open_timeout;
|
||||||
|
extern ngx_uint_t req_max_proccessing_ms_time;
|
||||||
|
extern ngx_uint_t res_max_proccessing_ms_time;
|
||||||
|
extern ngx_uint_t registration_thread_timeout_msec;
|
||||||
|
extern ngx_uint_t req_header_thread_timeout_msec;
|
||||||
|
extern ngx_uint_t req_body_thread_timeout_msec;
|
||||||
|
extern ngx_uint_t res_header_thread_timeout_msec;
|
||||||
|
extern ngx_uint_t res_body_thread_timeout_msec;
|
||||||
|
extern ngx_http_inspection_mode_e inspection_mode;
|
||||||
|
extern ngx_uint_t num_of_nginx_ipc_elements;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @struct ngx_http_cp_list_iterator
|
||||||
|
/// @brief NGINX list iterator's data.
|
||||||
|
///
|
||||||
|
typedef struct {
|
||||||
|
ngx_list_part_t *current_part; ///< Iterator's current node.
|
||||||
|
size_t current_part_element_index; ///< Current part index.
|
||||||
|
size_t current_list_element_index; ///< Current list index.
|
||||||
|
size_t list_element_size; ///< The total size of the list that the iterator belongs to.
|
||||||
|
} ngx_http_cp_list_iterator;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @struct ngx_http_cp_sessions_per_minute_limit
|
||||||
|
/// @brief Holds sessions per minute related limitations.
|
||||||
|
///
|
||||||
|
typedef struct {
|
||||||
|
/// After more than a minute has passed, we reset all session monitoring data.
|
||||||
|
/// sessions_per_second array helps keeping track with sessions that need to be closed.
|
||||||
|
unsigned int sessions_per_second[60];
|
||||||
|
unsigned int last_minute_sessions_sum; ///< Sum of all the last minutes sessions' sum.
|
||||||
|
unsigned int last_session_time; ///< The length of the latest session.
|
||||||
|
} ngx_http_cp_sessions_per_minute_limit;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Set debug context.
|
||||||
|
/// @param[in, out] client_ip Client IP to set in the debug.
|
||||||
|
/// @param[in, out] listening_ip Listening IP to set in the debug.
|
||||||
|
/// @param[in, out] uri_prefix Uri prefix to set in the debug.
|
||||||
|
/// @param[in, out] hostname Hostname to set in the debug.
|
||||||
|
/// @param[in, out] method Method to set in the debug.
|
||||||
|
/// @param[in] listening_port Listening port to set in the debug.
|
||||||
|
///
|
||||||
|
void set_dbg_by_ctx(
|
||||||
|
char *client_ip,
|
||||||
|
char *listening_ip,
|
||||||
|
char *uri_prefix,
|
||||||
|
char *hostname,
|
||||||
|
char *method,
|
||||||
|
unsigned int listening_port);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Reset debug context.
|
||||||
|
///
|
||||||
|
void reset_dbg_ctx();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initiate list iterator of the provided list.
|
||||||
|
/// @param[in, out] list to get the iterator of.
|
||||||
|
/// @param[in, out] iterator the iterator to be initiated
|
||||||
|
///
|
||||||
|
void init_list_iterator(ngx_list_t *list, ngx_http_cp_list_iterator *iterator);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get list element
|
||||||
|
/// @param[in, out] iterator the iterator to be initiated.
|
||||||
|
/// @param[in] index.
|
||||||
|
/// @returns void*
|
||||||
|
/// - #A pointer to the element.
|
||||||
|
/// - #NULL if failed to get or reached the end of the list.
|
||||||
|
///
|
||||||
|
void *get_list_element(ngx_http_cp_list_iterator *iterator, const size_t index);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Free a list from NGINX pool.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool.
|
||||||
|
/// @param[in, out] list A pointer to a list to free.
|
||||||
|
/// @returns void*
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR.
|
||||||
|
///
|
||||||
|
ngx_int_t free_list_from_pool(ngx_pool_t *memory_pool, ngx_list_t *list);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initiate a provided hash table with the provided values.
|
||||||
|
/// @param[in, out] memory_pool NGINX pool.
|
||||||
|
/// @param[in, out] hash_table_initializer NGINX hash initializator.
|
||||||
|
/// @param[in, out] hash_table Hash table to init.
|
||||||
|
/// @param[in] hash_table_name Hash table name.
|
||||||
|
/// @param[in] max_size Maximum size to set the hash table.
|
||||||
|
/// @param[in] bucket_size Bucket size to set in the hash table.
|
||||||
|
/// @param[in, out] keys Keys initiate and put into the hash_table.
|
||||||
|
/// @param[in, out] initial_value Initial hash value.
|
||||||
|
/// @param[in, out] initial_value_size Initial hash value's size.
|
||||||
|
/// @return ngx_int_t
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR.
|
||||||
|
///
|
||||||
|
ngx_int_t init_hash_table(
|
||||||
|
ngx_pool_t *memory_pool,
|
||||||
|
ngx_hash_init_t *hash_table_initializer,
|
||||||
|
ngx_hash_t *hash_table,
|
||||||
|
char *hash_table_name,
|
||||||
|
ngx_uint_t max_size,
|
||||||
|
ngx_uint_t bucket_size,
|
||||||
|
ngx_list_t *keys,
|
||||||
|
const void *initial_value,
|
||||||
|
const size_t initial_value_size
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Copy the src buffer to the dest.
|
||||||
|
/// @param[in, out] dest NGINX chain to be copied into.
|
||||||
|
/// @param[in] src NGINX chain to come from.
|
||||||
|
///
|
||||||
|
void copy_chain_buffers(ngx_chain_t *dest, ngx_chain_t *src);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Adds a new chain element before current list element.
|
||||||
|
/// @param[in, out] current_elem NGINX chain to be copied into.
|
||||||
|
/// @param[in] new_elem NGINX chain to come from.
|
||||||
|
///
|
||||||
|
void prepend_chain_elem(ngx_chain_t *current_elem, ngx_chain_t *new_elem);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Adds a new chain element after current list element.
|
||||||
|
/// @param[in, out] current_elem NGINX chain to be copied into.
|
||||||
|
/// @param[in] new_elem NGINX chain to come from.
|
||||||
|
///
|
||||||
|
void append_chain_elem(ngx_chain_t *current_elem, ngx_chain_t *new_elem);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Split chain element.
|
||||||
|
/// @param[in, out] elem NGINX chain to be split.
|
||||||
|
/// @param[in, out] split_index Index to split from.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR.
|
||||||
|
///
|
||||||
|
ngx_int_t split_chain_elem(ngx_chain_t *elem, uint16_t split_index, ngx_pool_t *pool);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Create chain element
|
||||||
|
/// @param[in, out] elem NGINX chain to be split.
|
||||||
|
/// @param[in, out] split_index Index to split from.
|
||||||
|
/// @param[in, out] pool NGINX pool.
|
||||||
|
/// @returns ngx_chain_t
|
||||||
|
/// - #A valid pointer to a ngx_chain_t.
|
||||||
|
/// - #NULL if failed to create a chain element.
|
||||||
|
///
|
||||||
|
ngx_chain_t * create_chain_elem(uint32_t data_size, char *data, ngx_pool_t *pool);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Free a NGINX chain.
|
||||||
|
/// @param[in, out] pool NGINX pool that free the resources into.
|
||||||
|
/// @param[in, out] chain NGINX chain to free.
|
||||||
|
/// @returns ngx_chain_t
|
||||||
|
///
|
||||||
|
void free_chain(ngx_pool_t *pool, ngx_chain_t *chain);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get currently set response uuid.
|
||||||
|
/// @returns char * of set web_response_uuid variable.
|
||||||
|
///
|
||||||
|
const char *get_web_response_uuid(void);
|
||||||
|
|
||||||
|
///y
|
||||||
|
/// @brief Get currently set response code.
|
||||||
|
/// @returns Returns the size of web_response_uuid variable.
|
||||||
|
///
|
||||||
|
ngx_uint_t get_web_response_uuid_size(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets a custom response page by modifying web_response_title/body/uuid variables.
|
||||||
|
/// @param[in] title Sets the web response title.
|
||||||
|
/// @param[in] message Sets the response body.
|
||||||
|
/// @param[in] uuid Sets the uuid of the custom response.
|
||||||
|
/// @param[in, out] response_code Sets the response code of the custom response.
|
||||||
|
///
|
||||||
|
void set_custom_response(const ngx_str_t *title, const ngx_str_t *message, const ngx_str_t *uuid, ngx_uint_t response_code);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets a redirect response by modifying redirect triggers, redirect_location and web_response_uuid.
|
||||||
|
/// @param[in] location Redirect location to set to.
|
||||||
|
/// @param[in] uuid Redirection's response uuid to set.
|
||||||
|
/// @param[in, out] add_event_id_to_header Event ID to add to the response header.
|
||||||
|
///
|
||||||
|
void set_redirect_response(const ngx_str_t *location, const ngx_str_t *uuid, uint add_event_id_to_header);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the redirect location.
|
||||||
|
/// @returns redirect_location variable.
|
||||||
|
///
|
||||||
|
u_char *get_redirect_location();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the redirect location.
|
||||||
|
/// @returns redirect_location_size variable.
|
||||||
|
///
|
||||||
|
ngx_uint_t get_redirect_location_size();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the redirect location.
|
||||||
|
/// @returns add_event_id variable.
|
||||||
|
///
|
||||||
|
ngx_uint_t get_add_event_id();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Returns if timeout has been reached.
|
||||||
|
/// @param[in, out] timeout NGINX pool that free the resources into.
|
||||||
|
/// @returns Returns 1 it timeout reached, otherwise 0.
|
||||||
|
///
|
||||||
|
int is_timeout_reached(struct timeval *timeout);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get delta current time + delta_time_in_sec value in seconds.
|
||||||
|
/// @param[in] delta_time_in_sec Delta time to return
|
||||||
|
/// @returns timeval struct with tv_sec value of += delta_time_in_sec.
|
||||||
|
///
|
||||||
|
struct timeval get_timeout_val_sec(const int delta_time_in_sec);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get delta current time + delta_time_in_usec value in seconds.
|
||||||
|
/// @param[in] delta_time_in_usec Delta time to return
|
||||||
|
/// @returns timeval struct with tv_sec value of += delta_time_in_usec.
|
||||||
|
///
|
||||||
|
struct timeval get_timeout_val_usec(const int delta_time_in_usec);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the currently set response page.
|
||||||
|
/// @param[in, out] request NGINX request, used to get the NGINX pool to allocate buffer needed for out_chain.
|
||||||
|
/// @param[in, out] out_chain NGINX chain that the response page data will be written to.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR_ERR.
|
||||||
|
///
|
||||||
|
ngx_int_t get_response_page(ngx_http_request_t *request, ngx_chain_t (*out_chain)[7]);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get currently set response page length.
|
||||||
|
/// @returns ngx_uint_t length of the response page.
|
||||||
|
///
|
||||||
|
ngx_uint_t get_response_page_length(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get currently set response code.
|
||||||
|
/// @returns ngx_uint_t web_triggers_response_code variable.
|
||||||
|
///
|
||||||
|
ngx_uint_t get_response_code(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get currently set static resource path.
|
||||||
|
/// @returns char * get static_resources_path variable.
|
||||||
|
///
|
||||||
|
const char * get_static_resources_path(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get currently set memory_pool.
|
||||||
|
/// @returns ngx_pool_t * get memory_pool.
|
||||||
|
///
|
||||||
|
ngx_pool_t *get_memory_pool(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Set memory_pool.
|
||||||
|
/// @param[in, out] new_memory_pool A new NGINX pool to be set.
|
||||||
|
///
|
||||||
|
void set_memory_pool(ngx_pool_t *new_memory_pool);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get number of digits of the provided num variable.
|
||||||
|
/// @param[in] num The number variable to get the number of digits from.
|
||||||
|
/// @returns Returns the number of digits.
|
||||||
|
///
|
||||||
|
unsigned int get_number_of_digits(int num);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get sessions per minute limit verdict.
|
||||||
|
/// @returns ngx_http_cp_verdict_e sessions_per_minute_limit_verdict variable.
|
||||||
|
///
|
||||||
|
ngx_http_cp_verdict_e get_sessions_per_minute_limit_verdict(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get maximum sessions per minute.
|
||||||
|
/// @returns unsigned int max_sessions_per_minute variable.
|
||||||
|
///
|
||||||
|
unsigned int get_max_sessions_per_minute(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get periodic session limit info..
|
||||||
|
/// @returns ngx_http_cp_sessions_per_minute_limit * Session per minute limit info.
|
||||||
|
///
|
||||||
|
ngx_http_cp_sessions_per_minute_limit *get_periodic_sessions_limit_info(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Writing into debug implementation.
|
||||||
|
/// @param[in] _dbg_level Debug level to write into.
|
||||||
|
/// @param[in] func Function name from which the write debug was called from.
|
||||||
|
/// @param[in] file File from which the debug function was called from.
|
||||||
|
/// @param[in] line_num Line number of the write debug was called on.
|
||||||
|
/// @param[in] fmt Debug formatter.
|
||||||
|
/// @param[in] ... Extra values to write into the debug using the formatter.
|
||||||
|
///
|
||||||
|
void ngx_cdecl write_dbg_impl(int _dbg_level, const char *func, const char *file, int line_num, const char *fmt, ...);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets a new debug level.
|
||||||
|
/// @param[in] _dbg_level New debug level to be set.
|
||||||
|
///
|
||||||
|
void set_cp_ngx_attachment_debug_level(int _dbg_level);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets a new session ID.
|
||||||
|
/// @param[in] _dbg_level New session ID to be set.
|
||||||
|
///
|
||||||
|
void set_current_session_id(uint32_t cur_session_id);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Checks if inspection required for a provided source IP.
|
||||||
|
/// @param[in] src_ip Provided source IP to be checked.
|
||||||
|
/// @returns 1 if inspection required, otherwise 0.
|
||||||
|
///
|
||||||
|
int is_inspection_required_for_source(const char *src_ip);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Initiates general configuration with the provided file path.
|
||||||
|
/// @param[in] conf_path Configuration path to a file of general configuration to initiate.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR.
|
||||||
|
///
|
||||||
|
ngx_int_t init_general_config(const char *conf_path);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Resets attachment configuration and loads them again from the file path in SHARED_ATTACMENT_CONF_PATH.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR.
|
||||||
|
///
|
||||||
|
ngx_int_t reset_attachment_config(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Resets attachment configuration and loads them again from the file path in SHARED_ATTACMENT_CONF_PATH.
|
||||||
|
/// @param[in] null_terminated_string null terminated string that the original string will be copied into.
|
||||||
|
/// @param[in] original_string String to be copied into the null_terminated_string.
|
||||||
|
/// @param[in] memory_pool NGINX pool for allocation the needed buffer for null_terminated_string.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK.
|
||||||
|
/// - #NGX_ERROR.
|
||||||
|
///
|
||||||
|
ngx_int_t duplicate_ngx_string(ngx_str_t *null_terminated_string, ngx_str_t *original_string, ngx_pool_t *memory_pool);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Reverse implementation to strnchr - finding a character in a length limited string from the end.
|
||||||
|
/// @param[in] string
|
||||||
|
/// @param[in] char_to_find
|
||||||
|
/// @param[in] string_length
|
||||||
|
/// @returns u_char* pointer to the first u_char that was found.
|
||||||
|
///
|
||||||
|
u_char *reverse_strnchr(u_char *string, const u_char char_to_find, const size_t string_length);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get keep alive internal milliseconds.
|
||||||
|
/// @returns ngx_msec_t keep_alive_interval_msec variable.
|
||||||
|
///
|
||||||
|
ngx_msec_t get_keep_alive_interval_msec(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Update CPU's max, average metrics and time usage metric.
|
||||||
|
///
|
||||||
|
void set_metric_cpu_usage(void);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Update memory's max, average metrics and time usage metric.
|
||||||
|
///
|
||||||
|
void set_metric_memory_usage(void);
|
||||||
|
|
||||||
|
#endif // __NGX_CP_UTILS_H__
|
||||||
503
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c
Normal file
503
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_http_cp_attachment_module.c
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
#include <ngx_files.h>
|
||||||
|
|
||||||
|
#include "ngx_cp_hooks.h"
|
||||||
|
#include "ngx_cp_utils.h"
|
||||||
|
#include "ngx_cp_initializer.h"
|
||||||
|
#include "ngx_http_cp_attachment_module.h"
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
|
||||||
|
extern ngx_uint_t current_config_version; ///< NGINX configuration version.
|
||||||
|
typedef struct {
|
||||||
|
ngx_flag_t enable; ///< Flags if the configuration enabled.
|
||||||
|
ngx_int_t num_of_workers; ///< Number of workers.
|
||||||
|
ngx_uint_t current_loc_config_version; ///< NGINX configuration version.
|
||||||
|
} ngx_cp_attachment_conf_t;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Creates NGINX cp attachment configuration.
|
||||||
|
/// @param[in, out] conf NGINX configuration.
|
||||||
|
/// @return
|
||||||
|
/// - #ngx_cp_attachment_conf_t if successed to create conf.
|
||||||
|
/// - #NULL if failed to create conf.
|
||||||
|
///
|
||||||
|
static void * ngx_cp_attachment_create_conf(ngx_conf_t *conf);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets attachment's module configuration in NGINX configuration chain.
|
||||||
|
/// @param[in, out] configure NGINX configuration.
|
||||||
|
/// @param[in] curr ngx_cp_attachment_conf_t Pointer to the configuration.
|
||||||
|
/// @param[in] next ngx_cp_attachment_conf_t Pointer to the configuration.
|
||||||
|
/// @return NGX_CONF_OK.
|
||||||
|
///
|
||||||
|
static char * ngx_cp_attachment_merge_conf(ngx_conf_t *conf, void *curr, void *next);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sets numbers of workers configuration.
|
||||||
|
/// @param[in, out] cf NGINX configuration.
|
||||||
|
/// @param[in, out] cmd NGINX command.
|
||||||
|
/// @param[in, out] conf NGINX CP configuration.
|
||||||
|
/// @return NGX_CONF_OK
|
||||||
|
///
|
||||||
|
static char * ngx_cp_attachment_set_num_workers_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Inits NGINX CP attachment.
|
||||||
|
/// @param[in] conf NGINX configuration.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t ngx_cp_attachment_init(ngx_conf_t *conf);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Creates NGINX cp attachment configuration.
|
||||||
|
/// @param[in, out] cf NGINX configuration.
|
||||||
|
/// @return ngx_cp_attachment_conf_t
|
||||||
|
/// - #ngx_cp_attachment_conf_t pointer if successed.
|
||||||
|
/// - #NULL if failed.
|
||||||
|
///
|
||||||
|
static void * ngx_cp_attachment_create_main_conf(ngx_conf_t *cf);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Inits a NGINX CP worker.
|
||||||
|
/// @param[in] cycle NGINX cycle.
|
||||||
|
/// @returns NGX_OK.
|
||||||
|
///
|
||||||
|
static ngx_int_t ngx_cp_attachment_init_worker(ngx_cycle_t *cycle);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Finis a NGINX CP worker.
|
||||||
|
/// @param[in] cycle NGINX cycle.
|
||||||
|
///
|
||||||
|
static void ngx_cp_attachment_fini_worker(ngx_cycle_t *cycle);
|
||||||
|
|
||||||
|
ngx_http_output_header_filter_pt ngx_http_next_response_header_filter; ///< NGINX response header filter.
|
||||||
|
ngx_http_request_body_filter_pt ngx_http_next_request_body_filter; ///< NGINX request body filter.
|
||||||
|
ngx_http_output_body_filter_pt ngx_http_next_response_body_filter; ///< NGINX output body filter.
|
||||||
|
|
||||||
|
struct sockaddr_un attachment_server; ///< NGINX CP attachments server socket.
|
||||||
|
|
||||||
|
static ngx_event_t ngx_keep_alive_event;
|
||||||
|
static ngx_int_t is_timer_active = 0;
|
||||||
|
static ngx_connection_t dumb_connection;
|
||||||
|
static ngx_msec_t keep_alive_interval_msec = 0;
|
||||||
|
static ngx_msec_t timer_interval_msec = 10000;
|
||||||
|
|
||||||
|
/// NGINX CP attachment command array.
|
||||||
|
static ngx_command_t ngx_cp_attachment_commands[] = {
|
||||||
|
{
|
||||||
|
ngx_string("cp-nano-nginx-attachment"),
|
||||||
|
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||||
|
ngx_conf_set_flag_slot,
|
||||||
|
NGX_HTTP_LOC_CONF_OFFSET,
|
||||||
|
offsetof(ngx_cp_attachment_conf_t, enable),
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ngx_string("cp_worker_processes"),
|
||||||
|
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||||
|
ngx_cp_attachment_set_num_workers_conf,
|
||||||
|
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||||
|
offsetof(ngx_cp_attachment_conf_t, num_of_workers),
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
ngx_null_command
|
||||||
|
};
|
||||||
|
|
||||||
|
/// NGINX CP attachment module context.
|
||||||
|
static ngx_http_module_t ngx_cp_attachment_module_ctx = {
|
||||||
|
NULL,
|
||||||
|
ngx_cp_attachment_init,
|
||||||
|
ngx_cp_attachment_create_main_conf,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
ngx_cp_attachment_create_conf,
|
||||||
|
ngx_cp_attachment_merge_conf
|
||||||
|
};
|
||||||
|
|
||||||
|
/// NGINX attachment module.
|
||||||
|
ngx_module_t ngx_http_cp_attachment_module = {
|
||||||
|
NGX_MODULE_V1, ///< NGINX CP module version.
|
||||||
|
&ngx_cp_attachment_module_ctx, ///< module context.
|
||||||
|
ngx_cp_attachment_commands, ///< module directives.
|
||||||
|
NGX_HTTP_MODULE, ///< module type.
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
ngx_cp_attachment_init_worker, ///< init process.
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
ngx_cp_attachment_fini_worker, ///< exit process.
|
||||||
|
NULL,
|
||||||
|
NGX_MODULE_V1_PADDING
|
||||||
|
};
|
||||||
|
|
||||||
|
int workers_amount_to_send = 0;
|
||||||
|
|
||||||
|
static void *
|
||||||
|
ngx_cp_attachment_create_main_conf(ngx_conf_t *cf)
|
||||||
|
{
|
||||||
|
ngx_cp_attachment_conf_t *conf;
|
||||||
|
|
||||||
|
conf = ngx_pcalloc(cf->pool, sizeof(ngx_cp_attachment_conf_t));
|
||||||
|
if (conf == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_cp_attachment_set_num_workers_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
|
{
|
||||||
|
(void) cmd;
|
||||||
|
ngx_str_t *value;
|
||||||
|
ngx_cp_attachment_conf_t *ccf;
|
||||||
|
ccf = (ngx_cp_attachment_conf_t *)conf;
|
||||||
|
|
||||||
|
value = cf->args->elts;
|
||||||
|
|
||||||
|
if (ngx_strcmp(value[1].data, "auto") == 0) {
|
||||||
|
ccf->num_of_workers = ngx_ncpu;
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccf->num_of_workers = ngx_atoi(value[1].data, value[1].len);
|
||||||
|
|
||||||
|
if (ccf->num_of_workers == NGX_ERROR) {
|
||||||
|
ccf->num_of_workers = 0;
|
||||||
|
return "invalid value";
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
ngx_cp_attachment_create_conf(ngx_conf_t *conf)
|
||||||
|
{
|
||||||
|
ngx_cp_attachment_conf_t *module_conf;
|
||||||
|
|
||||||
|
if (conf == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to create attachment module configuration: input argument is NULL");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_conf = ngx_pcalloc(conf->pool, sizeof(ngx_cp_attachment_conf_t));
|
||||||
|
if (module_conf == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate attachment module configuration");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_conf->enable = NGX_CONF_UNSET;
|
||||||
|
module_conf->num_of_workers = 0;
|
||||||
|
module_conf->current_loc_config_version = current_config_version;
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully created attachment module configuration");
|
||||||
|
return module_conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_uint_t
|
||||||
|
get_num_of_workers(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
if (workers_amount_to_send > 0) return workers_amount_to_send;
|
||||||
|
|
||||||
|
ngx_cp_attachment_conf_t *conf = ngx_http_get_module_main_conf(request, ngx_http_cp_attachment_module);
|
||||||
|
if (conf == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to fetch the local NGINX attachment state");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "num_of_workers=%d", conf->num_of_workers);
|
||||||
|
|
||||||
|
workers_amount_to_send = conf->num_of_workers;
|
||||||
|
return conf->num_of_workers;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
is_ngx_cp_attachment_disabled(ngx_http_request_t *request)
|
||||||
|
{
|
||||||
|
ngx_cp_attachment_conf_t *conf = ngx_http_get_module_loc_conf(request, ngx_http_cp_attachment_module);
|
||||||
|
if (conf == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to fetch the local NGINX attachment state");
|
||||||
|
return NGX_CONF_UNSET;
|
||||||
|
}
|
||||||
|
if (conf->current_loc_config_version != current_config_version) {
|
||||||
|
conf->current_loc_config_version = current_config_version;
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "Reconfiguring the local NGINX attachment state");
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "NGINX attachment state: %s", conf->enable ? "enabled" : "disabled");
|
||||||
|
return !conf->enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ngx_cp_set_module_loc_conf(ngx_http_request_t *request, ngx_flag_t new_state)
|
||||||
|
{
|
||||||
|
ngx_cp_attachment_conf_t *conf;
|
||||||
|
conf = ngx_http_get_module_loc_conf(request, ngx_http_cp_attachment_module);
|
||||||
|
conf->enable = new_state;
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "Configuration set to be %s", conf->enable ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_cp_attachment_merge_conf(ngx_conf_t *configure, void *curr, void *next)
|
||||||
|
{
|
||||||
|
(void)configure;
|
||||||
|
ngx_cp_attachment_conf_t *prev = curr;
|
||||||
|
ngx_cp_attachment_conf_t *conf = next;
|
||||||
|
|
||||||
|
ngx_conf_merge_value(conf->enable, prev->enable, NGX_CONF_UNSET);
|
||||||
|
ngx_conf_merge_value(conf->num_of_workers, prev->num_of_workers, ngx_ncpu);
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully set attachment module configuration in nginx configuration chain");
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Sends keep alive to the nano agent.
|
||||||
|
/// @param[in] event NGINX event.
|
||||||
|
///
|
||||||
|
static void
|
||||||
|
ngx_send_keep_alive(ngx_event_t *event)
|
||||||
|
{
|
||||||
|
char *family_name = NULL;
|
||||||
|
uint8_t family_name_size = 0;
|
||||||
|
uint8_t worker_id = 0;
|
||||||
|
int keep_alive_socket = -1;
|
||||||
|
struct timeval timeout = get_timeout_val_sec(1);
|
||||||
|
int res = 0;
|
||||||
|
int connected = 0;
|
||||||
|
static ngx_msec_t keep_alive_wait_period = 0;
|
||||||
|
|
||||||
|
if (ngx_exiting) {
|
||||||
|
is_timer_active = 0;
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "exiting without re-sched of ngx_send_keep_alive . ngx_exiting=%d", ngx_exiting);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_alive_interval_msec = get_keep_alive_interval_msec();
|
||||||
|
if (keep_alive_interval_msec <= 0) {
|
||||||
|
// Received invalid interval size, using default.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Received invalid interval size, using default value instead. Received value: %d, Default value: %u",
|
||||||
|
keep_alive_interval_msec,
|
||||||
|
DEFAULT_KEEP_ALIVE_INTERVAL_MSEC
|
||||||
|
);
|
||||||
|
keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_alive_wait_period += timer_interval_msec;
|
||||||
|
if (keep_alive_wait_period < keep_alive_interval_msec) {
|
||||||
|
// Wait alive period is still within interval size.
|
||||||
|
goto keep_alive_handler_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_docker_id(&family_name);
|
||||||
|
family_name_size = strlen(family_name);
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Keep alive signal. Family id: %s, UID: %u", family_name, worker_id);
|
||||||
|
|
||||||
|
if (keep_alive_socket > 0) {
|
||||||
|
close(keep_alive_socket);
|
||||||
|
keep_alive_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempting to create a socket.
|
||||||
|
keep_alive_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (keep_alive_socket < 0) {
|
||||||
|
// Failed to create a socket.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Could not create socket due to error. Socket number: %d, error: %s, errno: %d",
|
||||||
|
keep_alive_socket,
|
||||||
|
strerror(errno),
|
||||||
|
errno
|
||||||
|
);
|
||||||
|
goto keep_alive_handler_out;
|
||||||
|
}
|
||||||
|
attachment_server.sun_family = AF_UNIX;
|
||||||
|
strncpy(attachment_server.sun_path, SHARED_KEEP_ALIVE_PATH, sizeof(attachment_server.sun_path) - 1);
|
||||||
|
|
||||||
|
// Attempting to connect to the nano service.
|
||||||
|
connected = connect(keep_alive_socket, (struct sockaddr *)&attachment_server, sizeof(struct sockaddr_un));
|
||||||
|
if (connected == -1) {
|
||||||
|
// Failed to connect to the nano service.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Could not connect to nano service. Path: %s, Error: %s, Errno: %d",
|
||||||
|
attachment_server.sun_path,
|
||||||
|
strerror(errno),
|
||||||
|
errno
|
||||||
|
);
|
||||||
|
goto keep_alive_handler_out;
|
||||||
|
}
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "connected to socket: %d. sending keep alive signals");
|
||||||
|
|
||||||
|
// Exchanging worker id with the nano service.
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
keep_alive_socket,
|
||||||
|
&worker_id,
|
||||||
|
sizeof(worker_id),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
// Failed to send worker id
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send worker id");
|
||||||
|
goto keep_alive_handler_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchanging family name size with the nano service.
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
keep_alive_socket,
|
||||||
|
&family_name_size,
|
||||||
|
sizeof(family_name_size),
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
// Failed to send family name size.
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name size");
|
||||||
|
goto keep_alive_handler_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family_name_size > 0) {
|
||||||
|
// Exchanging family name with the nano service.
|
||||||
|
res = exchange_communication_data_with_service(
|
||||||
|
keep_alive_socket,
|
||||||
|
family_name,
|
||||||
|
family_name_size,
|
||||||
|
WRITE_TO_SOCKET,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
if (res <= 0) {
|
||||||
|
// Failed to send family name.
|
||||||
|
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name");
|
||||||
|
goto keep_alive_handler_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keep_alive_wait_period = 0;
|
||||||
|
|
||||||
|
keep_alive_handler_out:
|
||||||
|
// Sends another signal.
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "send signal again in %u sec", (timer_interval_msec / 1000));
|
||||||
|
ngx_add_timer(event, timer_interval_msec);
|
||||||
|
if (keep_alive_socket > 0) {
|
||||||
|
close(keep_alive_socket);
|
||||||
|
keep_alive_socket = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_cp_attachment_init_worker(ngx_cycle_t *cycle)
|
||||||
|
{
|
||||||
|
ngx_core_conf_t *core_main_conf;
|
||||||
|
ngx_cp_attachment_conf_t *attachment_conf;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "entering init worker. ngx_exiting=%d", ngx_exiting);
|
||||||
|
|
||||||
|
attachment_conf = (ngx_cp_attachment_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_cp_attachment_module);
|
||||||
|
if (attachment_conf && attachment_conf->num_of_workers) workers_amount_to_send = attachment_conf->num_of_workers;
|
||||||
|
if (!workers_amount_to_send) {
|
||||||
|
core_main_conf = (ngx_core_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_core_module);
|
||||||
|
workers_amount_to_send = core_main_conf->worker_processes;
|
||||||
|
}
|
||||||
|
// Worker number 0 will always exist.
|
||||||
|
// Therefore the single instance of the timer will be created and destroyed by it.
|
||||||
|
if (ngx_worker == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "Configured workers amount: %d", workers_amount_to_send);
|
||||||
|
ngx_keep_alive_event.handler = ngx_send_keep_alive;
|
||||||
|
ngx_keep_alive_event.log = cycle->log;
|
||||||
|
ngx_keep_alive_event.data = &dumb_connection;
|
||||||
|
dumb_connection.fd = (ngx_socket_t) -1;
|
||||||
|
keep_alive_interval_msec = get_keep_alive_interval_msec();
|
||||||
|
if (keep_alive_interval_msec == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Invalid interval size: %u, set to default value: %d ", keep_alive_interval_msec, DEFAULT_KEEP_ALIVE_INTERVAL_MSEC);
|
||||||
|
keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_add_timer(&ngx_keep_alive_event, timer_interval_msec);
|
||||||
|
is_timer_active = 1;
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_INFO,
|
||||||
|
"Timer successfully added. Keep alive interval: %d, timer interval: %d",
|
||||||
|
keep_alive_interval_msec,
|
||||||
|
timer_interval_msec
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ngx_cp_attachment_fini_worker(ngx_cycle_t *cycle)
|
||||||
|
{
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "entering fini worker. is_timer_active=%d, ngx_exiting=%d", is_timer_active, ngx_exiting);
|
||||||
|
|
||||||
|
// only worker number 0 (always exists since it is worker number 1 is allowed to create
|
||||||
|
// the single instance of the timer and destroy it)
|
||||||
|
if (ngx_worker != 0) return;
|
||||||
|
|
||||||
|
(void)cycle;
|
||||||
|
if (is_timer_active) ngx_del_timer(&ngx_keep_alive_event);
|
||||||
|
write_dbg(DBG_LEVEL_INFO, "Timer successfully deleted");
|
||||||
|
is_timer_active = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_cp_attachment_init(ngx_conf_t *conf)
|
||||||
|
{
|
||||||
|
ngx_http_handler_pt *handler;
|
||||||
|
ngx_http_core_main_conf_t *http_core_main_conf;
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Setting the memory pool used in the current context");
|
||||||
|
if (conf->pool == NULL) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to set memory pool in the current context, "
|
||||||
|
"no memory pool has been allocated for the current configuration"
|
||||||
|
);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
set_memory_pool(conf->pool);
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
"Successfully set the memory pool in the current context. Setting attachment module's hooks."
|
||||||
|
);
|
||||||
|
|
||||||
|
http_core_main_conf = ngx_http_conf_get_module_main_conf(conf, ngx_http_core_module);
|
||||||
|
handler = ngx_array_push(&http_core_main_conf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
|
||||||
|
|
||||||
|
if (handler == NULL) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Failed to set HTTP request headers' handler");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
*handler = ngx_http_cp_req_header_handler;
|
||||||
|
|
||||||
|
ngx_http_next_response_header_filter = ngx_http_top_header_filter;
|
||||||
|
ngx_http_top_header_filter = ngx_http_cp_res_header_filter;
|
||||||
|
|
||||||
|
ngx_http_next_request_body_filter = ngx_http_top_request_body_filter;
|
||||||
|
ngx_http_top_request_body_filter = ngx_http_cp_req_body_filter;
|
||||||
|
|
||||||
|
ngx_http_next_response_body_filter = ngx_http_top_body_filter;
|
||||||
|
ngx_http_top_body_filter = ngx_http_cp_res_body_filter;
|
||||||
|
|
||||||
|
write_dbg(DBG_LEVEL_TRACE, "Successfully set attachment module's hooks");
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
53
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.h
Normal file
53
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// @file ngx_http_cp_attachment_module.h
|
||||||
|
#ifndef __NGX_HTTP_CP_ATTACHMENT_MODULE_H__
|
||||||
|
#define __NGX_HTTP_CP_ATTACHMENT_MODULE_H__
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include <ngx_http.h>
|
||||||
|
|
||||||
|
extern ngx_http_output_header_filter_pt ngx_http_next_response_header_filter; ///< NGINX response header filter.
|
||||||
|
|
||||||
|
extern ngx_http_request_body_filter_pt ngx_http_next_request_body_filter; ///< NGINX request body filter.
|
||||||
|
extern ngx_http_output_body_filter_pt ngx_http_next_response_body_filter; ///< NGINX output body filter.
|
||||||
|
|
||||||
|
extern ngx_module_t ngx_http_cp_attachment_module; ///< NGINX Module.
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Returns if NGINX CP attachment is disabled.
|
||||||
|
/// @param[in] request NGINX request.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #0 attachment is enabled.
|
||||||
|
/// - #1 attachment is disabled.
|
||||||
|
///
|
||||||
|
ngx_int_t is_ngx_cp_attachment_disabled(ngx_http_request_t *request);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Get the number of workers.
|
||||||
|
/// @param[in] request NGINX request.
|
||||||
|
/// @returns ngx_uint_t returns number of workers.
|
||||||
|
///
|
||||||
|
ngx_uint_t get_num_of_workers(ngx_http_request_t *request);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Set module config.
|
||||||
|
/// @param[in] request NGINX request.
|
||||||
|
/// @param[in] new_state NGINX flag to set.
|
||||||
|
///
|
||||||
|
void ngx_cp_set_module_loc_conf(ngx_http_request_t *request, ngx_flag_t new_state);
|
||||||
|
|
||||||
|
#endif // __NGX_HTTP_CP_ATTACHMENT_MODULE_H__
|
||||||
67
config
Normal file
67
config
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
ngx_addon_name="ngx_ckp_module"
|
||||||
|
|
||||||
|
CHK_CORE_MODULES=" \
|
||||||
|
"
|
||||||
|
|
||||||
|
CHK_HTTP_MODULES=" \
|
||||||
|
ngx_ckp_module \
|
||||||
|
"
|
||||||
|
|
||||||
|
CHK_DEPS=" \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_compression.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_initializer.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_custom_response.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_io.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_failing_state.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_metric.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_hook_threads.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_static_content.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_hooks.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_thread.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_http_parser.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_utils.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_http_usercheck.h \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_http_cp_attachment_module.h \
|
||||||
|
$ngx_addon_dir/core/include/attachments/nginx_attachment_common.h \
|
||||||
|
$ngx_addon_dir/core/include/attachments/nginx_attachment_util.h \
|
||||||
|
$ngx_addon_dir/core/include/attachments/shmem_ipc.h \
|
||||||
|
$ngx_addon_dir/core/include/attachments/compression_utils.h \
|
||||||
|
$ngx_addon_dir/core/include/attachments/attachment_types.h \
|
||||||
|
$ngx_addon_dir/core/include/attachments/c_common/ip_common.h \
|
||||||
|
"
|
||||||
|
|
||||||
|
CHK_CORE_SRCS=" \
|
||||||
|
"
|
||||||
|
|
||||||
|
CHK_HTTP_SRCS=" \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_compression.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_io.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_custom_response.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_metric.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_failing_state.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_static_content.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_hook_threads.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_thread.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_hooks.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_utils.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_http_parser.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c \
|
||||||
|
$ngx_addon_dir/attachments/nginx/ngx_module/ngx_cp_initializer.c \
|
||||||
|
"
|
||||||
|
|
||||||
|
if [ $ngx_module_link = DYNAMIC ] ; then
|
||||||
|
ngx_module_name="$CHK_CORE_MODULES $CHK_HTTP_MODULES"
|
||||||
|
ngx_module_srcs="$CHK_CORE_SRCS $CHK_HTTP_SRCS"
|
||||||
|
. auto/module
|
||||||
|
elif [ $ngx_module_link = ADDON ] ; then
|
||||||
|
ngx_module_type=CORE
|
||||||
|
ngx_module_name=$CHK_CORE_MODULES
|
||||||
|
ngx_module_srcs=$CHK_CORE_SRCS
|
||||||
|
. auto/module
|
||||||
|
ngx_module_type=HTTP
|
||||||
|
ngx_module_name=$CHK_HTTP_MODULES
|
||||||
|
ngx_module_srcs=$CHK_HTTP_SRCS
|
||||||
|
. auto/module
|
||||||
|
fi
|
||||||
|
|
||||||
|
USE_OPENSSL=YES
|
||||||
2
core/CMakeLists.txt
Normal file
2
core/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
add_subdirectory(shmem_ipc)
|
||||||
|
add_subdirectory(compression)
|
||||||
5
core/compression/CMakeLists.txt
Normal file
5
core/compression/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
add_definitions(-DZLIB_CONST)
|
||||||
|
|
||||||
|
add_library(osrc_compression_utils SHARED compression_utils.cc)
|
||||||
|
|
||||||
|
install(TARGETS osrc_compression_utils DESTINATION lib)
|
||||||
383
core/compression/compression_utils.cc
Normal file
383
core/compression/compression_utils.cc
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "compression_utils.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
using DebugFunction = void(*)(const char *);
|
||||||
|
|
||||||
|
static const int max_debug_level = static_cast<int>(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION);
|
||||||
|
|
||||||
|
static void
|
||||||
|
defaultPrint(const char *debug_message)
|
||||||
|
{
|
||||||
|
cerr << debug_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZlibDebugStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZlibDebugStream(const CompressionUtilsDebugLevel _debug_level) : debug_level(_debug_level) {}
|
||||||
|
|
||||||
|
~ZlibDebugStream()
|
||||||
|
{
|
||||||
|
ZlibDebugStream::debug_funcs[debug_level](debug_message.str().c_str());
|
||||||
|
|
||||||
|
if (debug_level == CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION) abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
resetDebugFunctions()
|
||||||
|
{
|
||||||
|
for (auto &func : debug_funcs) {
|
||||||
|
func = defaultPrint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
setDebugFunction(const CompressionUtilsDebugLevel debug_level, DebugFunction function)
|
||||||
|
{
|
||||||
|
if (static_cast<int>(debug_level) > max_debug_level) return;
|
||||||
|
debug_funcs[static_cast<int>(debug_level)] = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ZlibDebugStream & operator<<(const T &message) { debug_message << message; return *this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ostringstream debug_message;
|
||||||
|
CompressionUtilsDebugLevel debug_level;
|
||||||
|
|
||||||
|
static array<DebugFunction, max_debug_level + 1> debug_funcs;
|
||||||
|
};
|
||||||
|
|
||||||
|
array<DebugFunction, max_debug_level + 1> ZlibDebugStream::debug_funcs = {
|
||||||
|
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_TRACE
|
||||||
|
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_DEBUG
|
||||||
|
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_INFO
|
||||||
|
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_WARNING
|
||||||
|
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR
|
||||||
|
defaultPrint // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION
|
||||||
|
};
|
||||||
|
|
||||||
|
#define zlibDbgError ZlibDebugStream(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR)
|
||||||
|
#define zlibDbgAssertion ZlibDebugStream(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION)
|
||||||
|
|
||||||
|
static const int default_num_window_bits = 15; // Default used by zlib.
|
||||||
|
static const int default_compression_level = Z_DEFAULT_COMPRESSION;
|
||||||
|
static const int default_compression_method = Z_DEFLATED;
|
||||||
|
static const int default_mem_level = 8; // Default recommended in zlib documentation.
|
||||||
|
static const int default_strategy = Z_DEFAULT_STRATEGY;
|
||||||
|
|
||||||
|
static const int zlib_ok_return_value = Z_OK;
|
||||||
|
static const int zlib_stream_done_return_value = Z_STREAM_END;
|
||||||
|
static const int zlib_bad_stream_state_error = Z_STREAM_ERROR;
|
||||||
|
static const int zlib_invalid_data_error = Z_DATA_ERROR;
|
||||||
|
static const int zlib_out_of_memory_error = Z_MEM_ERROR;
|
||||||
|
static const int zlib_version_mismatch_error = Z_VERSION_ERROR;
|
||||||
|
static const int zlib_buf_error = Z_BUF_ERROR;
|
||||||
|
|
||||||
|
static const int zlib_finish_flush = Z_FINISH;
|
||||||
|
static const int zlib_sync_flush = Z_SYNC_FLUSH;
|
||||||
|
static const int zlib_no_flush = Z_NO_FLUSH;
|
||||||
|
|
||||||
|
struct CompressionStream
|
||||||
|
{
|
||||||
|
CompressionStream() { bzero(&stream, sizeof(z_stream)); }
|
||||||
|
|
||||||
|
tuple<basic_string<unsigned char>, bool>
|
||||||
|
decompress(const unsigned char *data, uint32_t size)
|
||||||
|
{
|
||||||
|
initInflate();
|
||||||
|
if (state != TYPE::DECOMPRESS) throw runtime_error("Could not start decompression");
|
||||||
|
|
||||||
|
stream.avail_in = size;
|
||||||
|
stream.next_in = data;
|
||||||
|
|
||||||
|
vector<unsigned char> work_space;
|
||||||
|
work_space.reserve(4096);
|
||||||
|
basic_string<unsigned char> res;
|
||||||
|
int retries = 0;
|
||||||
|
|
||||||
|
while (stream.avail_in != 0) {
|
||||||
|
stream.avail_out = work_space.capacity();
|
||||||
|
stream.next_out = work_space.data();
|
||||||
|
|
||||||
|
auto old_total_out = stream.total_out;
|
||||||
|
|
||||||
|
auto inflate_res = inflate(&stream, zlib_no_flush);
|
||||||
|
|
||||||
|
if (inflate_res != Z_OK && inflate_res != Z_STREAM_END) {
|
||||||
|
fini();
|
||||||
|
throw runtime_error("error in 'inflate': " + getZlibError(inflate_res));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.total_out != old_total_out) {
|
||||||
|
res.append(work_space.data(), stream.total_out - old_total_out);
|
||||||
|
} else {
|
||||||
|
++retries;
|
||||||
|
if (retries > 3) {
|
||||||
|
fini();
|
||||||
|
throw runtime_error("No results from inflate more than three times");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inflate_res == Z_STREAM_END) {
|
||||||
|
fini();
|
||||||
|
return make_tuple(res, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_tuple(res, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string<unsigned char>
|
||||||
|
compress(CompressionType type, const unsigned char *data, uint32_t size, int is_last_chunk)
|
||||||
|
{
|
||||||
|
initDeflate(type);
|
||||||
|
if (state != TYPE::COMPRESS) throw runtime_error("Could not start compression");
|
||||||
|
|
||||||
|
stream.avail_in = size;
|
||||||
|
stream.next_in = data;
|
||||||
|
|
||||||
|
vector<unsigned char> work_space;
|
||||||
|
work_space.reserve(deflateBound(&stream, stream.avail_in));
|
||||||
|
basic_string<unsigned char> res;
|
||||||
|
int retries = 0;
|
||||||
|
|
||||||
|
while (stream.avail_in != 0) {
|
||||||
|
stream.avail_out = work_space.capacity();
|
||||||
|
stream.next_out = work_space.data();
|
||||||
|
|
||||||
|
auto old_total_out = stream.total_out;
|
||||||
|
|
||||||
|
int deflate_res = deflate(&stream, is_last_chunk ? zlib_finish_flush : zlib_sync_flush);
|
||||||
|
|
||||||
|
if (deflate_res != Z_OK && deflate_res != Z_STREAM_END) {
|
||||||
|
fini();
|
||||||
|
throw runtime_error("error in 'deflate': " + getZlibError(deflate_res));
|
||||||
|
}
|
||||||
|
if (stream.total_out != old_total_out) {
|
||||||
|
res.append(work_space.data(), stream.total_out - old_total_out);
|
||||||
|
} else {
|
||||||
|
++retries;
|
||||||
|
if (retries > 3) {
|
||||||
|
fini();
|
||||||
|
throw runtime_error("No results from deflate more than three times");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deflate_res == Z_STREAM_END) {
|
||||||
|
fini();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
initInflate()
|
||||||
|
{
|
||||||
|
if (state != TYPE::UNINITIALIZAED) return;
|
||||||
|
|
||||||
|
auto init_status = inflateInit2(&stream, default_num_window_bits + 32);
|
||||||
|
if (init_status != zlib_ok_return_value) {
|
||||||
|
throw runtime_error(
|
||||||
|
"Failed to initialize decompression stream. Error: " + getZlibError(init_status)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = TYPE::DECOMPRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
initDeflate(CompressionType type)
|
||||||
|
{
|
||||||
|
if (state != TYPE::UNINITIALIZAED) return;
|
||||||
|
|
||||||
|
int num_history_window_bits;
|
||||||
|
switch (type) {
|
||||||
|
case CompressionType::GZIP: {
|
||||||
|
num_history_window_bits = default_num_window_bits + 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompressionType::ZLIB: {
|
||||||
|
num_history_window_bits = default_num_window_bits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
zlibDbgAssertion
|
||||||
|
<< "Invalid compression type value: "
|
||||||
|
<< static_cast<int>(type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_status = deflateInit2(
|
||||||
|
&stream,
|
||||||
|
default_compression_level,
|
||||||
|
default_compression_method,
|
||||||
|
num_history_window_bits,
|
||||||
|
default_mem_level,
|
||||||
|
default_strategy
|
||||||
|
);
|
||||||
|
if (init_status != zlib_ok_return_value) {
|
||||||
|
throw runtime_error(
|
||||||
|
"Failed to initialize compression stream. Error: " + getZlibError(init_status)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = TYPE::COMPRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fini()
|
||||||
|
{
|
||||||
|
int end_stream_res = zlib_ok_return_value;
|
||||||
|
|
||||||
|
if (state == TYPE::DECOMPRESS) end_stream_res = inflateEnd(&stream);
|
||||||
|
if (state == TYPE::COMPRESS) end_stream_res = deflateEnd(&stream);
|
||||||
|
|
||||||
|
if (end_stream_res != zlib_ok_return_value) {
|
||||||
|
zlibDbgError << "Failed to clean state: " << getZlibError(end_stream_res);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = TYPE::UNINITIALIZAED;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
getZlibError(int zlibErrorCode)
|
||||||
|
{
|
||||||
|
switch (zlibErrorCode) {
|
||||||
|
case zlib_buf_error:
|
||||||
|
return "No progress was possible (possibly no more input data or not enough output buffer space)";
|
||||||
|
case zlib_bad_stream_state_error:
|
||||||
|
return "Inconsistent compression stream state";
|
||||||
|
case zlib_invalid_data_error:
|
||||||
|
return "Invalid or corrupted stream data";
|
||||||
|
case zlib_out_of_memory_error:
|
||||||
|
return "Out of memory";
|
||||||
|
case zlib_version_mismatch_error:
|
||||||
|
return "zlib version mismatch";
|
||||||
|
default:
|
||||||
|
return "zlib error occurred. Error code: " + to_string(zlibErrorCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z_stream stream;
|
||||||
|
enum class TYPE { UNINITIALIZAED, COMPRESS, DECOMPRESS } state = TYPE::UNINITIALIZAED;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
resetCompressionDebugFunctionsToStandardError()
|
||||||
|
{
|
||||||
|
ZlibDebugStream::resetDebugFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setCompressionDebugFunction(const CompressionUtilsDebugLevel debug_level, void (*debug_function)(const char *))
|
||||||
|
{
|
||||||
|
ZlibDebugStream::setDebugFunction(debug_level, debug_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionStream *
|
||||||
|
initCompressionStream()
|
||||||
|
{
|
||||||
|
return new CompressionStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
finiCompressionStream(CompressionStream *compression_stream)
|
||||||
|
{
|
||||||
|
delete compression_stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *
|
||||||
|
duplicateMemory(const basic_string<unsigned char> &str)
|
||||||
|
{
|
||||||
|
auto res = static_cast<unsigned char *>(malloc(str.size()));
|
||||||
|
|
||||||
|
if (res == nullptr) throw bad_alloc();
|
||||||
|
|
||||||
|
memcpy(res, str.data(), str.size());
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionResult
|
||||||
|
compressData(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const CompressionType compression_type,
|
||||||
|
const uint32_t data_size,
|
||||||
|
const unsigned char *uncompressed_data,
|
||||||
|
const int is_last_chunk
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CompressionResult result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (compression_stream == nullptr) throw invalid_argument("Compression stream is NULL");
|
||||||
|
if (uncompressed_data == nullptr) throw invalid_argument("Data pointer is NULL");
|
||||||
|
|
||||||
|
auto compress = compression_stream->compress(compression_type, uncompressed_data, data_size, is_last_chunk);
|
||||||
|
result.output = duplicateMemory(compress);
|
||||||
|
result.num_output_bytes = compress.size();
|
||||||
|
result.ok = 1;
|
||||||
|
} catch (const exception &e) {
|
||||||
|
zlibDbgError << "Compression failed " << e.what();
|
||||||
|
|
||||||
|
result.ok = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompressionResult
|
||||||
|
decompressData(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const uint32_t compressed_data_size,
|
||||||
|
const unsigned char *compressed_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DecompressionResult result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (compression_stream == nullptr) throw invalid_argument("Compression stream is NULL");
|
||||||
|
if (compressed_data == nullptr) throw invalid_argument("Data pointer is NULL");
|
||||||
|
if (compressed_data_size == 0) throw invalid_argument("Data size is 0");
|
||||||
|
|
||||||
|
auto decompress = compression_stream->decompress(compressed_data, compressed_data_size);
|
||||||
|
result.output = duplicateMemory(get<0>(decompress));
|
||||||
|
result.num_output_bytes = get<0>(decompress).size();
|
||||||
|
result.is_last_chunk = get<1>(decompress);
|
||||||
|
result.ok = 1;
|
||||||
|
} catch (const exception &e) {
|
||||||
|
zlibDbgError << "Decompression failed " << e.what();
|
||||||
|
|
||||||
|
result.ok = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
31
core/include/attachments/attachment_types.h
Normal file
31
core/include/attachments/attachment_types.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __ATTACHMENT_TYPES_H__
|
||||||
|
#define __ATTACHMENT_TYPES_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
enum class AttachmentType
|
||||||
|
#else // __cplusplus
|
||||||
|
enum AttachmentType
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
NGINX_ATT_ID,
|
||||||
|
PRELOAD_ATT_ID,
|
||||||
|
#ifdef __cplusplus
|
||||||
|
COUNT
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __ATTACHMENT_TYPES_H__
|
||||||
86
core/include/attachments/compression_utils.h
Normal file
86
core/include/attachments/compression_utils.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __COMPRESSION_UTILS_H__
|
||||||
|
#define __COMPRESSION_UTILS_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
typedef enum CompressionUtilsDebugLevel
|
||||||
|
{
|
||||||
|
COMPRESSION_DBG_LEVEL_TRACE,
|
||||||
|
COMPRESSION_DBG_LEVEL_DEBUG,
|
||||||
|
COMPRESSION_DBG_LEVEL_INFO,
|
||||||
|
COMPRESSION_DBG_LEVEL_WARNING,
|
||||||
|
COMPRESSION_DBG_LEVEL_ERROR,
|
||||||
|
COMPRESSION_DBG_LEVEL_ASSERTION
|
||||||
|
} CompressionUtilsDebugLevel;
|
||||||
|
|
||||||
|
void resetCompressionDebugFunctionsToStandardError();
|
||||||
|
void setCompressionDebugFunction(const CompressionUtilsDebugLevel debug_level, void (*debug_function)(const char *));
|
||||||
|
|
||||||
|
typedef struct CompressionStream CompressionStream;
|
||||||
|
|
||||||
|
CompressionStream * initCompressionStream();
|
||||||
|
void finiCompressionStream(CompressionStream *compression_stream);
|
||||||
|
|
||||||
|
typedef enum CompressionType
|
||||||
|
{
|
||||||
|
NO_COMPRESSION,
|
||||||
|
GZIP,
|
||||||
|
ZLIB
|
||||||
|
} CompressionType;
|
||||||
|
|
||||||
|
typedef struct CompressionResult
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
uint32_t num_output_bytes;
|
||||||
|
unsigned char *output;
|
||||||
|
} CompressionResult;
|
||||||
|
|
||||||
|
CompressionResult
|
||||||
|
compressData(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const CompressionType compression_type,
|
||||||
|
const uint32_t uncompressed_data_size,
|
||||||
|
const unsigned char *uncompressed_data,
|
||||||
|
const int is_last_chunk
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct DecompressionResult
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
uint32_t num_output_bytes;
|
||||||
|
unsigned char *output;
|
||||||
|
int is_last_chunk;
|
||||||
|
} DecompressionResult;
|
||||||
|
|
||||||
|
DecompressionResult
|
||||||
|
decompressData(
|
||||||
|
CompressionStream *compression_stream,
|
||||||
|
const uint32_t compressed_data_size,
|
||||||
|
const unsigned char *compressed_data
|
||||||
|
);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // __COMPRESSION_UTILS_H__
|
||||||
68
core/include/attachments/http_configuration.h
Normal file
68
core/include/attachments/http_configuration.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __HTTP_CONFIGURATION_H__
|
||||||
|
#define __HTTP_CONFIGURATION_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "cereal/archives/json.hpp"
|
||||||
|
|
||||||
|
struct DebugConfig
|
||||||
|
{
|
||||||
|
void save(cereal::JSONOutputArchive &archive) const;
|
||||||
|
void load(cereal::JSONInputArchive &archive);
|
||||||
|
bool operator==(const DebugConfig &another) const;
|
||||||
|
|
||||||
|
std::string client;
|
||||||
|
std::string server;
|
||||||
|
uint port = 0;
|
||||||
|
std::string method;
|
||||||
|
std::string host;
|
||||||
|
std::string uri;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpAttachmentConfiguration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int init(const std::string &conf_file);
|
||||||
|
|
||||||
|
void save(cereal::JSONOutputArchive &archive) const;
|
||||||
|
void load(cereal::JSONInputArchive &archive);
|
||||||
|
|
||||||
|
bool operator==(const HttpAttachmentConfiguration &other) const;
|
||||||
|
|
||||||
|
uint getNumericalValue(const std::string &key) const;
|
||||||
|
const std::string & getStringValue(const std::string &key) const;
|
||||||
|
const std::vector<std::string> & getExcludeSources() const { return exclude_sources; }
|
||||||
|
const DebugConfig & getDebugContext() const { return dbg; }
|
||||||
|
|
||||||
|
void setNumericalValue(const std::string &key, uint value) { numerical_values[key] = value; }
|
||||||
|
void setStringValue(const std::string &key, const std::string &value) { string_values[key] = value; }
|
||||||
|
void setExcludeSources(const std::vector<std::string> &new_sources) { exclude_sources = new_sources; }
|
||||||
|
void setDebugContext(const DebugConfig &_dbg) { dbg = _dbg; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadNumericalValue(cereal::JSONInputArchive &archive, const std::string &name, uint default_value);
|
||||||
|
|
||||||
|
DebugConfig dbg;
|
||||||
|
std::map<std::string, uint> numerical_values;
|
||||||
|
std::map<std::string, std::string> string_values;
|
||||||
|
std::vector<std::string> exclude_sources;
|
||||||
|
std::string empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __HTTP_CONFIGURATION_H__
|
||||||
275
core/include/attachments/nginx_attachment_common.h
Normal file
275
core/include/attachments/nginx_attachment_common.h
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __NGINX_ATTACHMENT_COMMON_H__
|
||||||
|
#define __NGINX_ATTACHMENT_COMMON_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define MAX_NGINX_UID_LEN 32
|
||||||
|
#define NUM_OF_NGINX_IPC_ELEMENTS 200
|
||||||
|
#define DEFAULT_KEEP_ALIVE_INTERVAL_MSEC 300000
|
||||||
|
#define SHARED_MEM_PATH "/dev/shm/"
|
||||||
|
#define SHARED_REGISTRATION_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration"
|
||||||
|
#define SHARED_KEEP_ALIVE_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration-expiration-socket"
|
||||||
|
#define SHARED_VERDICT_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-http-transaction-handler"
|
||||||
|
#define SHARED_ATTACHMENT_CONF_PATH SHARED_MEM_PATH "cp_nano_http_attachment_conf"
|
||||||
|
#define DEFAULT_STATIC_RESOURCES_PATH SHARED_MEM_PATH "static_resources"
|
||||||
|
#define INJECT_POS_IRRELEVANT -1
|
||||||
|
#define CORRUPTED_SESSION_ID 0
|
||||||
|
#define METRIC_PERIODIC_TIMEOUT 600
|
||||||
|
|
||||||
|
extern char shared_verdict_signal_path[];
|
||||||
|
extern int workers_amount_to_send;
|
||||||
|
|
||||||
|
typedef int64_t ngx_http_cp_inject_pos_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_modification_type
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_modification_type
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
APPEND,
|
||||||
|
INJECT,
|
||||||
|
REPLACE
|
||||||
|
} ngx_http_modification_type_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_chunk_type
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_chunk_type
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
REQUEST_START,
|
||||||
|
REQUEST_HEADER,
|
||||||
|
REQUEST_BODY,
|
||||||
|
REQUEST_END,
|
||||||
|
RESPONSE_CODE,
|
||||||
|
RESPONSE_HEADER,
|
||||||
|
RESPONSE_BODY,
|
||||||
|
RESPONSE_END,
|
||||||
|
CONTENT_LENGTH,
|
||||||
|
METRIC_DATA_FROM_PLUGIN,
|
||||||
|
|
||||||
|
COUNT
|
||||||
|
} ngx_http_chunk_type_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_plugin_metric_type
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_plugin_metric_type
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
TRANSPARENTS_COUNT,
|
||||||
|
TOTAL_TRANSPARENTS_TIME,
|
||||||
|
INSPECTION_OPEN_FAILURES_COUNT,
|
||||||
|
INSPECTION_CLOSE_FAILURES_COUNT,
|
||||||
|
INSPECTION_SUCCESSES_COUNT,
|
||||||
|
INJECT_VERDICTS_COUNT,
|
||||||
|
DROP_VERDICTS_COUNT,
|
||||||
|
ACCEPT_VERDICTS_COUNT,
|
||||||
|
IRRELEVANT_VERDICTS_COUNT,
|
||||||
|
RECONF_VERDICTS_COUNT,
|
||||||
|
INSPECT_VERDICTS_COUNT,
|
||||||
|
AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT,
|
||||||
|
THREAD_TIMEOUT,
|
||||||
|
REG_THREAD_TIMEOUT,
|
||||||
|
REQ_HEADER_THREAD_TIMEOUT,
|
||||||
|
REQ_BODY_THREAD_TIMEOUT,
|
||||||
|
AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT,
|
||||||
|
MAX_REQ_BODY_SIZE_UPON_TIMEOUT,
|
||||||
|
MIN_REQ_BODY_SIZE_UPON_TIMEOUT,
|
||||||
|
RES_HEADER_THREAD_TIMEOUT,
|
||||||
|
RES_BODY_THREAD_TIMEOUT,
|
||||||
|
AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT,
|
||||||
|
MAX_RES_BODY_SIZE_UPON_TIMEOUT,
|
||||||
|
MIN_RES_BODY_SIZE_UPON_TIMEOUT,
|
||||||
|
THREAD_FAILURE,
|
||||||
|
REQ_PROCCESSING_TIMEOUT,
|
||||||
|
RES_PROCCESSING_TIMEOUT,
|
||||||
|
REQ_FAILED_TO_REACH_UPSTREAM,
|
||||||
|
REQ_FAILED_COMPRESSION_COUNT,
|
||||||
|
RES_FAILED_COMPRESSION_COUNT,
|
||||||
|
REQ_FAILED_DECOMPRESSION_COUNT,
|
||||||
|
RES_FAILED_DECOMPRESSION_COUNT,
|
||||||
|
REQ_SUCCESSFUL_COMPRESSION_COUNT,
|
||||||
|
RES_SUCCESSFUL_COMPRESSION_COUNT,
|
||||||
|
REQ_SUCCESSFUL_DECOMPRESSION_COUNT,
|
||||||
|
RES_SUCCESSFUL_DECOMPRESSION_COUNT,
|
||||||
|
CORRUPTED_ZIP_SKIPPED_SESSION_COUNT,
|
||||||
|
CPU_USAGE,
|
||||||
|
AVERAGE_VM_MEMORY_USAGE,
|
||||||
|
AVERAGE_RSS_MEMORY_USAGE,
|
||||||
|
MAX_VM_MEMORY_USAGE,
|
||||||
|
MAX_RSS_MEMORY_USAGE,
|
||||||
|
|
||||||
|
METRIC_TYPES_COUNT
|
||||||
|
} ngx_http_plugin_metric_type_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_cp_verdict
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_cp_verdict
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
TRAFFIC_VERDICT_INSPECT,
|
||||||
|
TRAFFIC_VERDICT_ACCEPT,
|
||||||
|
TRAFFIC_VERDICT_DROP,
|
||||||
|
TRAFFIC_VERDICT_INJECT,
|
||||||
|
TRAFFIC_VERDICT_IRRELEVANT,
|
||||||
|
TRAFFIC_VERDICT_RECONF
|
||||||
|
} ngx_http_cp_verdict_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_cp_debug_level
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_cp_debug_level
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
DBG_LEVEL_TRACE,
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
DBG_LEVEL_INFO,
|
||||||
|
DBG_LEVEL_WARNING,
|
||||||
|
DBG_LEVEL_ERROR,
|
||||||
|
#ifndef __cplusplus
|
||||||
|
DBG_LEVEL_ASSERT,
|
||||||
|
#endif
|
||||||
|
DBG_LEVEL_COUNT
|
||||||
|
} ngx_http_cp_debug_level_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_meta_data
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_meta_data
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
HTTP_PROTOCOL_SIZE,
|
||||||
|
HTTP_PROTOCOL_DATA,
|
||||||
|
HTTP_METHOD_SIZE,
|
||||||
|
HTTP_METHOD_DATA,
|
||||||
|
HOST_NAME_SIZE,
|
||||||
|
HOST_NAME_DATA,
|
||||||
|
LISTENING_ADDR_SIZE,
|
||||||
|
LISTENING_ADDR_DATA,
|
||||||
|
LISTENING_PORT,
|
||||||
|
URI_SIZE,
|
||||||
|
URI_DATA,
|
||||||
|
CLIENT_ADDR_SIZE,
|
||||||
|
CLIENT_ADDR_DATA,
|
||||||
|
CLIENT_PORT,
|
||||||
|
|
||||||
|
META_DATA_COUNT
|
||||||
|
} ngx_http_meta_data_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_http_header_data
|
||||||
|
#else
|
||||||
|
typedef enum ngx_http_header_data
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
HEADER_KEY_SIZE,
|
||||||
|
HEADER_KEY_DATA,
|
||||||
|
HEADER_VAL_SIZE,
|
||||||
|
HEADER_VAL_DATA,
|
||||||
|
|
||||||
|
HEADER_DATA_COUNT
|
||||||
|
} ngx_http_header_data_e;
|
||||||
|
|
||||||
|
typedef enum ngx_http_inspection_mode
|
||||||
|
{
|
||||||
|
NON_BLOCKING_THREAD,
|
||||||
|
BLOCKING_THREAD,
|
||||||
|
NO_THREAD,
|
||||||
|
|
||||||
|
INSPECTION_MODE_COUNT
|
||||||
|
} ngx_http_inspection_mode_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
typedef enum class ngx_web_response_type
|
||||||
|
#else
|
||||||
|
typedef enum ngx_web_response_type
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
CUSTOM_WEB_RESPONSE,
|
||||||
|
REDIRECT_WEB_RESPONSE
|
||||||
|
} ngx_web_response_type_e;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) ngx_http_cp_inject_data {
|
||||||
|
ngx_http_cp_inject_pos_t injection_pos;
|
||||||
|
ngx_http_modification_type_e mod_type;
|
||||||
|
uint16_t injection_size;
|
||||||
|
uint8_t is_header;
|
||||||
|
uint8_t orig_buff_index;
|
||||||
|
char data[0];
|
||||||
|
} ngx_http_cp_inject_data_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) ngx_http_cp_web_response_data {
|
||||||
|
uint8_t web_repsonse_type;
|
||||||
|
uint8_t uuid_size;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct __attribute__((__packed__)) ngx_http_cp_custom_web_response_data {
|
||||||
|
uint16_t response_code;
|
||||||
|
uint8_t title_size;
|
||||||
|
uint8_t body_size;
|
||||||
|
char data[0];
|
||||||
|
} custom_response_data;
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) ngx_http_cp_redirect_data {
|
||||||
|
uint8_t add_event_id;
|
||||||
|
uint16_t redirect_location_size;
|
||||||
|
char redirect_location[0];
|
||||||
|
} redirect_data;
|
||||||
|
} response_data;
|
||||||
|
} ngx_http_cp_web_response_data_t;
|
||||||
|
|
||||||
|
typedef union __attribute__((__packed__)) ngx_http_cp_modify_data {
|
||||||
|
ngx_http_cp_inject_data_t inject_data[0];
|
||||||
|
ngx_http_cp_web_response_data_t web_response_data[0];
|
||||||
|
} ngx_http_cp_modify_data_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) ngx_http_cp_reply_from_service {
|
||||||
|
uint16_t verdict;
|
||||||
|
uint32_t session_id;
|
||||||
|
uint8_t modification_count;
|
||||||
|
ngx_http_cp_modify_data_t modify_data[0];
|
||||||
|
} ngx_http_cp_reply_from_service_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) ngx_http_cp_request_data {
|
||||||
|
uint16_t data_type;
|
||||||
|
uint32_t session_id;
|
||||||
|
unsigned char data[0];
|
||||||
|
} ngx_http_cp_request_data_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) ngx_http_cp_metric_data {
|
||||||
|
uint16_t data_type;
|
||||||
|
#ifdef __cplusplus
|
||||||
|
uint64_t data[static_cast<int>(ngx_http_plugin_metric_type::METRIC_TYPES_COUNT)];
|
||||||
|
#else
|
||||||
|
uint64_t data[METRIC_TYPES_COUNT];
|
||||||
|
#endif
|
||||||
|
} ngx_http_cp_metric_data_t;
|
||||||
|
|
||||||
|
#endif // __NGINX_ATTACHMENT_COMMON_H__
|
||||||
62
core/include/attachments/nginx_attachment_util.h
Normal file
62
core/include/attachments/nginx_attachment_util.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __NGINX_ATTACHMENT_UTIL__
|
||||||
|
#define __NGINX_ATTACHMENT_UTIL__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "nginx_attachment_common.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#define IP_STR_MAX_LEN 40
|
||||||
|
|
||||||
|
typedef const char * c_str;
|
||||||
|
|
||||||
|
int initAttachmentConfig(c_str conf_file);
|
||||||
|
|
||||||
|
ngx_http_inspection_mode_e getInspectionMode();
|
||||||
|
unsigned int getNumOfNginxIpcElements();
|
||||||
|
unsigned int getKeepAliveIntervalMsec();
|
||||||
|
unsigned int getDbgLevel();
|
||||||
|
int isDebugContext(c_str client, c_str server, unsigned int port, c_str method, c_str host , c_str uri);
|
||||||
|
c_str getStaticResourcesPath();
|
||||||
|
|
||||||
|
int isFailOpenMode();
|
||||||
|
unsigned int getFailOpenTimeout();
|
||||||
|
|
||||||
|
unsigned int getMaxSessionsPerMinute();
|
||||||
|
int isFailOpenOnSessionLimit();
|
||||||
|
|
||||||
|
unsigned int getRegistrationThreadTimeout();
|
||||||
|
|
||||||
|
unsigned int getReqProccessingTimeout();
|
||||||
|
unsigned int getReqHeaderThreadTimeout();
|
||||||
|
unsigned int getReqBodyThreadTimeout();
|
||||||
|
|
||||||
|
unsigned int getResProccessingTimeout();
|
||||||
|
unsigned int getResHeaderThreadTimeout();
|
||||||
|
unsigned int getResBodyThreadTimeout();
|
||||||
|
|
||||||
|
int isIPAddress(c_str ip_str);
|
||||||
|
int isSkipSource(c_str ip_str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __NGINX_ATTACHMENT_UTIL__
|
||||||
66
core/include/attachments/shmem_ipc.h
Normal file
66
core/include/attachments/shmem_ipc.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __SHMEM_IPC_H__
|
||||||
|
#define __SHMEM_IPC_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
typedef struct SharedMemoryIPC SharedMemoryIPC;
|
||||||
|
extern const int corrupted_shmem_error;
|
||||||
|
|
||||||
|
SharedMemoryIPC * initIpc(
|
||||||
|
const char queue_name[32],
|
||||||
|
const uint32_t user_id,
|
||||||
|
const uint32_t group_id,
|
||||||
|
int is_owner,
|
||||||
|
uint16_t num_of_queue_elem,
|
||||||
|
void (*debug_func)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...)
|
||||||
|
);
|
||||||
|
|
||||||
|
void destroyIpc(SharedMemoryIPC *ipc, int is_owner);
|
||||||
|
|
||||||
|
int sendData(SharedMemoryIPC *ipc, const uint16_t data_to_send_size, const char *data_to_send);
|
||||||
|
|
||||||
|
int
|
||||||
|
sendChunkedData(
|
||||||
|
SharedMemoryIPC *ipc,
|
||||||
|
const uint16_t *data_to_send_sizes,
|
||||||
|
const char **data_elem_to_send,
|
||||||
|
const uint8_t num_of_data_elem
|
||||||
|
);
|
||||||
|
|
||||||
|
int receiveData(SharedMemoryIPC *ipc, uint16_t *received_data_size, const char **received_data);
|
||||||
|
|
||||||
|
int popData(SharedMemoryIPC *ipc);
|
||||||
|
|
||||||
|
int isDataAvailable(SharedMemoryIPC *ipc);
|
||||||
|
|
||||||
|
void resetIpc(SharedMemoryIPC *ipc, uint16_t num_of_data_segments);
|
||||||
|
|
||||||
|
void dumpIpcMemory(SharedMemoryIPC *ipc);
|
||||||
|
|
||||||
|
int isCorruptedShmem(SharedMemoryIPC *ipc, int is_owner);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // __SHMEM_IPC_H__
|
||||||
5
core/shmem_ipc/CMakeLists.txt
Normal file
5
core/shmem_ipc/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
add_library(osrc_shmem_ipc SHARED shmem_ipc.c shared_ring_queue.c)
|
||||||
|
|
||||||
|
target_link_libraries(osrc_shmem_ipc -lrt)
|
||||||
|
|
||||||
|
install(TARGETS osrc_shmem_ipc DESTINATION lib)
|
||||||
31
core/shmem_ipc/shared_ipc_debug.h
Normal file
31
core/shmem_ipc/shared_ipc_debug.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __SHARED_IPC_DEBUG_H__
|
||||||
|
#define __SHARED_IPC_DEBUG_H__
|
||||||
|
|
||||||
|
extern void (*debug_int)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...);
|
||||||
|
|
||||||
|
#ifndef __FILENAME__
|
||||||
|
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum debugLevel { TraceLevel = 0, WarningLevel = 3 };
|
||||||
|
|
||||||
|
#define writeDebug(debug_level, fmt, ...) \
|
||||||
|
{ \
|
||||||
|
debug_int(debug_level, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __SHARED_IPC_DEBUG_H__
|
||||||
513
core/shmem_ipc/shared_ring_queue.c
Normal file
513
core/shmem_ipc/shared_ring_queue.c
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "shared_ring_queue.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "shared_ipc_debug.h"
|
||||||
|
|
||||||
|
static const uint16_t empty_buff_mgmt_magic = 0xcafe;
|
||||||
|
static const uint16_t skip_buff_mgmt_magic = 0xbeef;
|
||||||
|
const uint16_t max_num_of_data_segments = sizeof(DataSegment)/sizeof(uint16_t);
|
||||||
|
|
||||||
|
char g_rx_location_name[MAX_ONE_WAY_QUEUE_NAME_LENGTH] = "";
|
||||||
|
char g_tx_location_name[MAX_ONE_WAY_QUEUE_NAME_LENGTH] = "";
|
||||||
|
int32_t g_rx_fd = -1;
|
||||||
|
int32_t g_tx_fd = -1;
|
||||||
|
int32_t g_memory_size = -1;
|
||||||
|
|
||||||
|
static uint16_t g_num_of_data_segments = 0;
|
||||||
|
|
||||||
|
static int
|
||||||
|
getNumOfDataSegmentsNeeded(uint16_t data_size)
|
||||||
|
{
|
||||||
|
int res = (data_size + SHARED_MEMORY_SEGMENT_ENTRY_SIZE - 1) / SHARED_MEMORY_SEGMENT_ENTRY_SIZE;
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel, "Checking amount of segments needed. Res: %d, data size: %u, shmem entry size: %u",
|
||||||
|
res,
|
||||||
|
data_size,
|
||||||
|
SHARED_MEMORY_SEGMENT_ENTRY_SIZE
|
||||||
|
);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
isThereEnoughMemoryInQueue(SharedRingQueue *queue, uint8_t num_of_elem_to_push)
|
||||||
|
{
|
||||||
|
uint16_t write_pos = queue->write_pos;
|
||||||
|
uint16_t read_pos = queue->read_pos;
|
||||||
|
uint16_t num_of_data_segments = queue->num_of_data_segments;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel, "Checking if memory has space for new elements. "
|
||||||
|
"Num of elements to push: %u, write index: %u, read index: %u, amount of queue segments: %u",
|
||||||
|
num_of_elem_to_push,
|
||||||
|
write_pos,
|
||||||
|
read_pos,
|
||||||
|
num_of_data_segments
|
||||||
|
);
|
||||||
|
if (num_of_elem_to_push >= num_of_data_segments) {
|
||||||
|
writeDebug(TraceLevel, "Amount of elements to push is larger then amount of available elements in the queue");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add skipped elements during write that does not fit from cur write position till end of queue
|
||||||
|
if (write_pos + num_of_elem_to_push > num_of_data_segments) {
|
||||||
|
num_of_elem_to_push += num_of_data_segments - write_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// removing the aspect of circularity in queue and simulating as if the queue continued at its end
|
||||||
|
if (write_pos + num_of_elem_to_push >= num_of_data_segments) {
|
||||||
|
read_pos += num_of_data_segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = write_pos + num_of_elem_to_push < read_pos || write_pos >= read_pos;
|
||||||
|
writeDebug(TraceLevel, "Finished checking if there is enough place in shared memory. Res: %d", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
resetRingQueue(SharedRingQueue *queue, uint16_t num_of_data_segments)
|
||||||
|
{
|
||||||
|
uint16_t *buffer_mgmt;
|
||||||
|
unsigned int idx;
|
||||||
|
|
||||||
|
queue->read_pos = 0;
|
||||||
|
queue->write_pos = 0;
|
||||||
|
queue->num_of_data_segments = num_of_data_segments;
|
||||||
|
buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
|
||||||
|
for (idx = 0; idx < queue->num_of_data_segments; idx++) {
|
||||||
|
buffer_mgmt[idx] = empty_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedRingQueue *
|
||||||
|
createSharedRingQueue(const char *shared_location_name, uint16_t num_of_data_segments, int is_owner, int is_tx)
|
||||||
|
{
|
||||||
|
SharedRingQueue *queue = NULL;
|
||||||
|
uint16_t *buffer_mgmt;
|
||||||
|
uint16_t shmem_fd_flags = is_owner ? O_RDWR | O_CREAT : O_RDWR;
|
||||||
|
int32_t fd = -1;
|
||||||
|
uint32_t size_of_memory;
|
||||||
|
unsigned int idx;
|
||||||
|
|
||||||
|
writeDebug(TraceLevel, "Creating a new shared ring queue");
|
||||||
|
|
||||||
|
if (num_of_data_segments > max_num_of_data_segments) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"createSharedRingQueue: Cannot create data segment with %d elements (max number of elements is %u)\n",
|
||||||
|
num_of_data_segments,
|
||||||
|
max_num_of_data_segments
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_num_of_data_segments = num_of_data_segments;
|
||||||
|
|
||||||
|
fd = shm_open(shared_location_name, shmem_fd_flags, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||||
|
if (fd == -1) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"createSharedRingQueue: Failed to open shared memory for '%s'. Errno: %d\n",
|
||||||
|
shared_location_name,
|
||||||
|
errno
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_of_memory = sizeof(SharedRingQueue) + (num_of_data_segments * sizeof(DataSegment));
|
||||||
|
if (is_owner && ftruncate(fd, size_of_memory + 1) != 0) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"createSharedRingQueue: Failed to ftruncate shared memory '%s' to size '%x'\n",
|
||||||
|
shared_location_name,
|
||||||
|
size_of_memory
|
||||||
|
);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue = (SharedRingQueue *)mmap(0, size_of_memory, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (queue == NULL) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"createSharedRingQueue: Error allocating queue for '%s' of size=%x\n",
|
||||||
|
shared_location_name,
|
||||||
|
size_of_memory
|
||||||
|
);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_owner) {
|
||||||
|
snprintf(queue->shared_location_name, MAX_ONE_WAY_QUEUE_NAME_LENGTH, "%s", shared_location_name);
|
||||||
|
queue->num_of_data_segments = num_of_data_segments;
|
||||||
|
queue->read_pos = 0;
|
||||||
|
queue->write_pos = 0;
|
||||||
|
queue->size_of_memory = size_of_memory;
|
||||||
|
buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
|
||||||
|
for (idx = 0; idx < queue->num_of_data_segments; idx++) {
|
||||||
|
buffer_mgmt[idx] = empty_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
queue->owner_fd = fd;
|
||||||
|
} else {
|
||||||
|
queue->user_fd = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_memory_size = size_of_memory;
|
||||||
|
if (is_tx) {
|
||||||
|
g_tx_fd = fd;
|
||||||
|
snprintf(g_tx_location_name, MAX_ONE_WAY_QUEUE_NAME_LENGTH, "%s", shared_location_name);
|
||||||
|
} else {
|
||||||
|
g_rx_fd = fd;
|
||||||
|
snprintf(g_rx_location_name, MAX_ONE_WAY_QUEUE_NAME_LENGTH, "%s", shared_location_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Successfully created a new shared ring queue. "
|
||||||
|
"Shared memory path: %s, number of segments: %u, is owner: %d, "
|
||||||
|
"fd flags: %u, fd: %d, memory size: %u, read index: %u, write index: %u",
|
||||||
|
shared_location_name,
|
||||||
|
queue->num_of_data_segments,
|
||||||
|
is_owner,
|
||||||
|
shmem_fd_flags,
|
||||||
|
fd,
|
||||||
|
queue->size_of_memory,
|
||||||
|
queue->read_pos,
|
||||||
|
queue->write_pos
|
||||||
|
);
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
destroySharedRingQueue(SharedRingQueue *queue, int is_owner, int is_tx)
|
||||||
|
{
|
||||||
|
uint32_t size_of_memory = g_memory_size;
|
||||||
|
int32_t fd = 0;
|
||||||
|
|
||||||
|
if(is_owner) {
|
||||||
|
queue->owner_fd = 0;
|
||||||
|
} else {
|
||||||
|
queue->user_fd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_tx) {
|
||||||
|
fd = g_tx_fd;
|
||||||
|
g_tx_fd = -1;
|
||||||
|
} else {
|
||||||
|
fd = g_rx_fd;
|
||||||
|
g_rx_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (munmap(queue, size_of_memory) != 0) {
|
||||||
|
writeDebug(WarningLevel, "destroySharedRingQueue: Failed to unmap shared ring queue\n");
|
||||||
|
}
|
||||||
|
if (fd > 0) close(fd);
|
||||||
|
fd = 0;
|
||||||
|
|
||||||
|
// shm_open cleanup
|
||||||
|
if(is_owner) {
|
||||||
|
shm_unlink(is_tx ? g_tx_location_name : g_rx_location_name);
|
||||||
|
}
|
||||||
|
writeDebug(TraceLevel, "Successfully destroyed shared ring queue. Is owner: %d", is_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dumpRingQueueShmem(SharedRingQueue *queue)
|
||||||
|
{
|
||||||
|
uint16_t segment_idx;
|
||||||
|
uint16_t data_idx;
|
||||||
|
uint16_t *buffer_mgmt = NULL;
|
||||||
|
char data_byte;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"owner_fd: %d, user_fd: %d, size_of_memory: %d, write_pos: %d, read_pos: %d, num_of_data_segments: %d\n",
|
||||||
|
queue->owner_fd,
|
||||||
|
queue->user_fd,
|
||||||
|
queue->size_of_memory,
|
||||||
|
queue->write_pos,
|
||||||
|
queue->read_pos,
|
||||||
|
queue->num_of_data_segments
|
||||||
|
);
|
||||||
|
|
||||||
|
writeDebug(WarningLevel, "mgmt_segment:");
|
||||||
|
buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
|
||||||
|
for (segment_idx = 0; segment_idx < max_num_of_data_segments; segment_idx++) {
|
||||||
|
writeDebug(WarningLevel, "%s%u", (segment_idx == 0 ? " " : ", "), buffer_mgmt[segment_idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDebug(WarningLevel, "\ndata_segment: ");
|
||||||
|
for (segment_idx = 0; segment_idx < queue->num_of_data_segments; segment_idx++) {
|
||||||
|
writeDebug(WarningLevel, "\nMgmt index: %u, value: %u,\nactual data: ", segment_idx, buffer_mgmt[segment_idx]);
|
||||||
|
for (data_idx = 0; data_idx < SHARED_MEMORY_SEGMENT_ENTRY_SIZE; data_idx++) {
|
||||||
|
data_byte = queue->data_segment[segment_idx].data[data_idx];
|
||||||
|
writeDebug(WarningLevel, isprint(data_byte) ? "%c" : "%02X", data_byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeDebug(WarningLevel, "\nEnd of memory\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
peekToQueue(SharedRingQueue *queue, const char **output_buffer, uint16_t *output_buffer_size)
|
||||||
|
{
|
||||||
|
uint16_t read_pos = queue->read_pos;
|
||||||
|
const uint16_t num_of_data_segments = queue->num_of_data_segments;
|
||||||
|
uint16_t *buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Reading data from queue. Read index: %u, number of queue elements: %u",
|
||||||
|
read_pos,
|
||||||
|
num_of_data_segments
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isQueueEmpty(queue)) {
|
||||||
|
writeDebug(WarningLevel, "peekToQueue: Failed to read from an empty queue\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_pos >= num_of_data_segments) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"peekToQueue: Failed to read from a corrupted queue! (read_pos= %d > num_of_data_segments=%d)\n",
|
||||||
|
read_pos,
|
||||||
|
num_of_data_segments
|
||||||
|
);
|
||||||
|
return CORRUPTED_SHMEM_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer_mgmt[read_pos] == skip_buff_mgmt_magic) {
|
||||||
|
for ( ; read_pos < num_of_data_segments && buffer_mgmt[read_pos] == skip_buff_mgmt_magic; ++read_pos) {
|
||||||
|
buffer_mgmt[read_pos] = empty_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_pos == num_of_data_segments) read_pos = 0;
|
||||||
|
|
||||||
|
*output_buffer_size = buffer_mgmt[read_pos];
|
||||||
|
*output_buffer = queue->data_segment[read_pos].data;
|
||||||
|
|
||||||
|
queue->read_pos = read_pos;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Successfully read data from queue. Data size: %u, new Read index: %u",
|
||||||
|
*output_buffer_size,
|
||||||
|
queue->read_pos
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pushBuffersToQueue(
|
||||||
|
SharedRingQueue *queue,
|
||||||
|
const char **input_buffers,
|
||||||
|
const uint16_t *input_buffers_sizes,
|
||||||
|
const uint8_t num_of_input_buffers
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
const uint16_t num_of_queue_elem = queue->num_of_data_segments;
|
||||||
|
uint16_t write_pos = queue->write_pos;
|
||||||
|
uint16_t total_elem_size = 0;
|
||||||
|
uint16_t *buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
|
||||||
|
uint16_t end_pos;
|
||||||
|
uint16_t num_of_segments_to_write;
|
||||||
|
char *current_copy_pos;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Writing new data to queue. write index: %u, number of queue elements: %u, number of elements to push: %u",
|
||||||
|
write_pos,
|
||||||
|
num_of_queue_elem,
|
||||||
|
num_of_input_buffers
|
||||||
|
);
|
||||||
|
|
||||||
|
for (idx = 0; idx < num_of_input_buffers; idx++) {
|
||||||
|
total_elem_size += input_buffers_sizes[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
num_of_segments_to_write = getNumOfDataSegmentsNeeded(total_elem_size);
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Checking if there is enough space to push new data. Total new data size: %u, number of segments needed: %u",
|
||||||
|
total_elem_size,
|
||||||
|
num_of_segments_to_write
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (!isThereEnoughMemoryInQueue(queue, num_of_segments_to_write)) {
|
||||||
|
writeDebug(WarningLevel, "Cannot write to a full queue\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_pos >= num_of_queue_elem) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"Cannot write to a location outside the queue. Write index: %u, number of queue elements: %u",
|
||||||
|
write_pos,
|
||||||
|
num_of_queue_elem
|
||||||
|
);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_pos + num_of_segments_to_write > num_of_queue_elem) {
|
||||||
|
for ( ; write_pos < num_of_queue_elem; ++write_pos) {
|
||||||
|
buffer_mgmt[write_pos] = skip_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
write_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Setting new management data. Write index: %u, total elements in index: %u",
|
||||||
|
write_pos,
|
||||||
|
total_elem_size
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer_mgmt[write_pos] = total_elem_size;
|
||||||
|
current_copy_pos = queue->data_segment[write_pos].data;
|
||||||
|
for (idx = 0; idx < num_of_input_buffers; idx++) {
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Writing data to queue. Data index: %u, data size: %u, copy destination: %p",
|
||||||
|
idx,
|
||||||
|
input_buffers_sizes[idx],
|
||||||
|
current_copy_pos
|
||||||
|
);
|
||||||
|
memcpy(current_copy_pos, input_buffers[idx], input_buffers_sizes[idx]);
|
||||||
|
current_copy_pos += input_buffers_sizes[idx];
|
||||||
|
}
|
||||||
|
write_pos++;
|
||||||
|
|
||||||
|
end_pos = write_pos + num_of_segments_to_write - 1;
|
||||||
|
for ( ; write_pos < end_pos; ++write_pos) {
|
||||||
|
buffer_mgmt[write_pos] = skip_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->write_pos = write_pos < num_of_queue_elem ? write_pos : 0;
|
||||||
|
writeDebug(TraceLevel, "Successfully pushed data to queue. New write index: %u", queue->write_pos);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pushToQueue(SharedRingQueue *queue, const char *input_buffer, const uint16_t input_buffer_size)
|
||||||
|
{
|
||||||
|
return pushBuffersToQueue(queue, &input_buffer, &input_buffer_size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
popFromQueue(SharedRingQueue *queue)
|
||||||
|
{
|
||||||
|
uint16_t num_of_read_segments;
|
||||||
|
uint16_t read_pos = queue->read_pos;
|
||||||
|
uint16_t end_pos;
|
||||||
|
uint16_t num_of_data_segments = queue->num_of_data_segments;
|
||||||
|
uint16_t *buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Removing data from queue. new data to queue. Read index: %u, number of queue elements: %u",
|
||||||
|
read_pos,
|
||||||
|
num_of_data_segments
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isQueueEmpty(queue)) {
|
||||||
|
writeDebug(TraceLevel, "Cannot pop data from empty queue");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num_of_read_segments = getNumOfDataSegmentsNeeded(buffer_mgmt[read_pos]);
|
||||||
|
|
||||||
|
end_pos = read_pos + num_of_read_segments;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Size of data to remove: %u, number of queue elements to free: %u, current read index: %u, end index: %u",
|
||||||
|
buffer_mgmt[read_pos],
|
||||||
|
num_of_read_segments,
|
||||||
|
read_pos,
|
||||||
|
end_pos
|
||||||
|
);
|
||||||
|
|
||||||
|
for ( ; read_pos < end_pos; ++read_pos ) {
|
||||||
|
buffer_mgmt[read_pos] = empty_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_pos < num_of_data_segments && buffer_mgmt[read_pos] == skip_buff_mgmt_magic) {
|
||||||
|
for ( ; read_pos < num_of_data_segments; ++read_pos ) {
|
||||||
|
buffer_mgmt[read_pos] = empty_buff_mgmt_magic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_pos == num_of_data_segments) read_pos = 0;
|
||||||
|
|
||||||
|
queue->read_pos = read_pos;
|
||||||
|
writeDebug(TraceLevel, "Successfully popped data from queue. New read index: %u", queue->read_pos);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isQueueEmpty(SharedRingQueue *queue)
|
||||||
|
{
|
||||||
|
return queue->read_pos == queue->write_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isCorruptedQueue(SharedRingQueue *queue, int is_tx)
|
||||||
|
{
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Checking if shared ring queue is corrupted. "
|
||||||
|
"g_num_of_data_segments = %u, queue->num_of_data_segments = %u, queue->read_pos = %u, queue->write_pos = %u, "
|
||||||
|
"g_memory_size = %d, queue->size_of_memory = %d, "
|
||||||
|
"queue->shared_location_name = %s, g_tx_location_name = %s, g_rx_location_name = %s, is_tx = %d",
|
||||||
|
g_num_of_data_segments,
|
||||||
|
queue->num_of_data_segments,
|
||||||
|
queue->read_pos,
|
||||||
|
queue->write_pos,
|
||||||
|
g_memory_size,
|
||||||
|
queue->size_of_memory,
|
||||||
|
queue->shared_location_name,
|
||||||
|
g_tx_location_name,
|
||||||
|
g_rx_location_name,
|
||||||
|
is_tx
|
||||||
|
);
|
||||||
|
|
||||||
|
if (g_num_of_data_segments == 0) return 0;
|
||||||
|
|
||||||
|
if (queue->num_of_data_segments != g_num_of_data_segments) return 1;
|
||||||
|
if (queue->size_of_memory != g_memory_size) return 1;
|
||||||
|
if (queue->read_pos > g_num_of_data_segments) return 1;
|
||||||
|
if (queue->write_pos > g_num_of_data_segments) return 1;
|
||||||
|
if (strcmp(queue->shared_location_name, is_tx ? g_tx_location_name : g_rx_location_name) != 0) return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
75
core/shmem_ipc/shared_ring_queue.h
Normal file
75
core/shmem_ipc/shared_ring_queue.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef __SHARED_RING_QUEUE_H__
|
||||||
|
#define __SHARED_RING_QUEUE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#define SHARED_MEMORY_SEGMENT_ENTRY_SIZE 1024
|
||||||
|
#define MAX_ONE_WAY_QUEUE_NAME_LENGTH 64
|
||||||
|
#define CORRUPTED_SHMEM_ERROR -2
|
||||||
|
|
||||||
|
typedef struct DataSegment {
|
||||||
|
char data[SHARED_MEMORY_SEGMENT_ENTRY_SIZE];
|
||||||
|
} DataSegment;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) SharedRingQueue {
|
||||||
|
char shared_location_name[MAX_ONE_WAY_QUEUE_NAME_LENGTH];
|
||||||
|
int32_t owner_fd;
|
||||||
|
int32_t user_fd;
|
||||||
|
int32_t size_of_memory;
|
||||||
|
uint16_t write_pos;
|
||||||
|
uint16_t read_pos;
|
||||||
|
uint16_t num_of_data_segments;
|
||||||
|
DataSegment mgmt_segment;
|
||||||
|
DataSegment data_segment[0];
|
||||||
|
} SharedRingQueue;
|
||||||
|
|
||||||
|
SharedRingQueue *
|
||||||
|
createSharedRingQueue(
|
||||||
|
const char *shared_location_name,
|
||||||
|
uint16_t num_of_data_segments,
|
||||||
|
int is_owner,
|
||||||
|
int is_tx
|
||||||
|
);
|
||||||
|
|
||||||
|
void destroySharedRingQueue(SharedRingQueue *queue, int is_owner, int is_tx);
|
||||||
|
int isQueueEmpty(SharedRingQueue *queue);
|
||||||
|
int isCorruptedQueue(SharedRingQueue *queue, int is_tx);
|
||||||
|
int peekToQueue(SharedRingQueue *queue, const char **output_buffer, uint16_t *output_buffer_size);
|
||||||
|
int popFromQueue(SharedRingQueue *queue);
|
||||||
|
int pushToQueue(SharedRingQueue *queue, const char *input_buffer, const uint16_t input_buffer_size);
|
||||||
|
void resetRingQueue(SharedRingQueue *queue, uint16_t num_of_data_segments);
|
||||||
|
void dumpRingQueueShmem(SharedRingQueue *queue);
|
||||||
|
|
||||||
|
int
|
||||||
|
pushBuffersToQueue(
|
||||||
|
SharedRingQueue *queue,
|
||||||
|
const char **input_buffers,
|
||||||
|
const uint16_t *input_buffers_sizes,
|
||||||
|
const uint8_t num_of_input_buffers
|
||||||
|
);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // __SHARED_RING_QUEUE_H__
|
||||||
288
core/shmem_ipc/shmem_ipc.c
Normal file
288
core/shmem_ipc/shmem_ipc.c
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "shmem_ipc.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "shared_ring_queue.h"
|
||||||
|
#include "shared_ipc_debug.h"
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
const int corrupted_shmem_error = CORRUPTED_SHMEM_ERROR;
|
||||||
|
static const size_t max_one_way_queue_name_length = MAX_ONE_WAY_QUEUE_NAME_LENGTH;
|
||||||
|
static const size_t max_shmem_path_length = 72;
|
||||||
|
|
||||||
|
struct SharedMemoryIPC {
|
||||||
|
char shm_name[32];
|
||||||
|
SharedRingQueue *rx_queue;
|
||||||
|
SharedRingQueue *tx_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
debugInitial(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
UNUSED(is_error);
|
||||||
|
UNUSED(func);
|
||||||
|
UNUSED(file);
|
||||||
|
UNUSED(line_num);
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vprintf(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void (*debug_int)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...) = debugInitial;
|
||||||
|
|
||||||
|
static int
|
||||||
|
isTowardsOwner(int is_owner, int is_tx)
|
||||||
|
{
|
||||||
|
if (is_owner) return !is_tx;
|
||||||
|
return is_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SharedRingQueue *
|
||||||
|
createOneWayIPCQueue(
|
||||||
|
const char *name,
|
||||||
|
const uint32_t user_id,
|
||||||
|
const uint32_t group_id,
|
||||||
|
int is_tx_queue,
|
||||||
|
int is_owner,
|
||||||
|
uint16_t num_of_queue_elem
|
||||||
|
)
|
||||||
|
{
|
||||||
|
SharedRingQueue *ring_queue = NULL;
|
||||||
|
char queue_name[max_one_way_queue_name_length];
|
||||||
|
char shmem_path[max_shmem_path_length];
|
||||||
|
const char *direction = isTowardsOwner(is_owner, is_tx_queue) ? "rx" : "tx";
|
||||||
|
snprintf(queue_name, sizeof(queue_name) - 1, "__cp_nano_%s_shared_memory_%s__", direction, name);
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Creating one way IPC queue. Name: %s, direction: %s, size: %d",
|
||||||
|
name,
|
||||||
|
direction,
|
||||||
|
num_of_queue_elem
|
||||||
|
);
|
||||||
|
ring_queue = createSharedRingQueue(queue_name, num_of_queue_elem, is_owner, isTowardsOwner(is_owner, is_tx_queue));
|
||||||
|
if (ring_queue == NULL) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"Failed to create %s shared ring queue of size=%d for '%s'\n",
|
||||||
|
direction,
|
||||||
|
num_of_queue_elem,
|
||||||
|
queue_name
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int ret = snprintf(shmem_path, sizeof(shmem_path) - 1, "/dev/shm/%s", queue_name);
|
||||||
|
if (ret < 0 || (size_t)ret < (strlen(direction) + strlen(name))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_owner && chmod(shmem_path, 0666) == -1) {
|
||||||
|
writeDebug(WarningLevel, "Failed to set the permissions");
|
||||||
|
destroySharedRingQueue(ring_queue, is_owner, isTowardsOwner(is_owner, is_tx_queue));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Successfully created one way IPC queue. "
|
||||||
|
"Name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u, direction: %s, path: %s",
|
||||||
|
queue_name,
|
||||||
|
user_id,
|
||||||
|
group_id,
|
||||||
|
is_owner,
|
||||||
|
num_of_queue_elem,
|
||||||
|
direction,
|
||||||
|
shmem_path
|
||||||
|
);
|
||||||
|
return ring_queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemoryIPC *
|
||||||
|
initIpc(
|
||||||
|
const char queue_name[32],
|
||||||
|
uint32_t user_id,
|
||||||
|
uint32_t group_id,
|
||||||
|
int is_owner,
|
||||||
|
uint16_t num_of_queue_elem,
|
||||||
|
void (*debug_func)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...))
|
||||||
|
{
|
||||||
|
SharedMemoryIPC *ipc = NULL;
|
||||||
|
debug_int = debug_func;
|
||||||
|
|
||||||
|
writeDebug(
|
||||||
|
TraceLevel,
|
||||||
|
"Initializing new IPC. "
|
||||||
|
"Queue name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u\n",
|
||||||
|
queue_name,
|
||||||
|
user_id,
|
||||||
|
group_id,
|
||||||
|
is_owner,
|
||||||
|
num_of_queue_elem
|
||||||
|
);
|
||||||
|
|
||||||
|
ipc = malloc(sizeof(SharedMemoryIPC));
|
||||||
|
if (ipc == NULL) {
|
||||||
|
writeDebug(WarningLevel, "Failed to allocate Shared Memory IPC for '%s'\n", queue_name);
|
||||||
|
debug_int = debugInitial;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc->rx_queue = NULL;
|
||||||
|
ipc->tx_queue = NULL;
|
||||||
|
|
||||||
|
ipc->rx_queue = createOneWayIPCQueue(queue_name, user_id, group_id, 0, is_owner, num_of_queue_elem);
|
||||||
|
if (ipc->rx_queue == NULL) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"Failed to allocate rx queue. "
|
||||||
|
"Queue name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u",
|
||||||
|
queue_name,
|
||||||
|
user_id,
|
||||||
|
group_id,
|
||||||
|
is_owner,
|
||||||
|
num_of_queue_elem
|
||||||
|
);
|
||||||
|
|
||||||
|
destroyIpc(ipc, is_owner);
|
||||||
|
debug_int = debugInitial;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc->tx_queue = createOneWayIPCQueue(queue_name, user_id, group_id, 1, is_owner, num_of_queue_elem);
|
||||||
|
if (ipc->tx_queue == NULL) {
|
||||||
|
writeDebug(
|
||||||
|
WarningLevel,
|
||||||
|
"Failed to allocate rx queue. "
|
||||||
|
"Queue name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u",
|
||||||
|
queue_name,
|
||||||
|
user_id,
|
||||||
|
group_id,
|
||||||
|
is_owner,
|
||||||
|
num_of_queue_elem
|
||||||
|
);
|
||||||
|
destroyIpc(ipc, is_owner);
|
||||||
|
debug_int = debugInitial;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDebug(TraceLevel, "Successfully allocated IPC");
|
||||||
|
|
||||||
|
strncpy(ipc->shm_name, queue_name, sizeof(ipc->shm_name));
|
||||||
|
return ipc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
resetIpc(SharedMemoryIPC *ipc, uint16_t num_of_data_segments)
|
||||||
|
{
|
||||||
|
writeDebug(TraceLevel, "Reseting IPC queues\n");
|
||||||
|
resetRingQueue(ipc->rx_queue, num_of_data_segments);
|
||||||
|
resetRingQueue(ipc->tx_queue, num_of_data_segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
destroyIpc(SharedMemoryIPC *shmem, int is_owner)
|
||||||
|
{
|
||||||
|
writeDebug(TraceLevel, "Destroying IPC queues\n");
|
||||||
|
|
||||||
|
if (shmem->rx_queue != NULL) {
|
||||||
|
destroySharedRingQueue(shmem->rx_queue, is_owner, isTowardsOwner(is_owner, 0));
|
||||||
|
shmem->rx_queue = NULL;
|
||||||
|
}
|
||||||
|
if (shmem->tx_queue != NULL) {
|
||||||
|
destroySharedRingQueue(shmem->tx_queue, is_owner, isTowardsOwner(is_owner, 1));
|
||||||
|
shmem->tx_queue = NULL;
|
||||||
|
}
|
||||||
|
debug_int = debugInitial;
|
||||||
|
free(shmem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dumpIpcMemory(SharedMemoryIPC *ipc)
|
||||||
|
{
|
||||||
|
writeDebug(WarningLevel, "Ipc memory dump:\n");
|
||||||
|
writeDebug(WarningLevel, "RX queue:\n");
|
||||||
|
dumpRingQueueShmem(ipc->rx_queue);
|
||||||
|
writeDebug(WarningLevel, "TX queue:\n");
|
||||||
|
dumpRingQueueShmem(ipc->tx_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sendData(SharedMemoryIPC *ipc, const uint16_t data_to_send_size, const char *data_to_send)
|
||||||
|
{
|
||||||
|
writeDebug(TraceLevel, "Sending data of size %u\n", data_to_send_size);
|
||||||
|
return pushToQueue(ipc->tx_queue, data_to_send, data_to_send_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sendChunkedData(
|
||||||
|
SharedMemoryIPC *ipc,
|
||||||
|
const uint16_t *data_to_send_sizes,
|
||||||
|
const char **data_elem_to_send,
|
||||||
|
const uint8_t num_of_data_elem
|
||||||
|
)
|
||||||
|
{
|
||||||
|
writeDebug(TraceLevel, "Sending %u chunks of data\n", num_of_data_elem);
|
||||||
|
|
||||||
|
return pushBuffersToQueue(ipc->tx_queue, data_elem_to_send, data_to_send_sizes, num_of_data_elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
receiveData(SharedMemoryIPC *ipc, uint16_t *received_data_size, const char **received_data)
|
||||||
|
{
|
||||||
|
int res = peekToQueue(ipc->rx_queue, received_data, received_data_size);
|
||||||
|
writeDebug(TraceLevel, "Received data from queue. Res: %d, data size: %u\n", res, *received_data_size);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
popData(SharedMemoryIPC *ipc)
|
||||||
|
{
|
||||||
|
int res = popFromQueue(ipc->rx_queue);
|
||||||
|
writeDebug(TraceLevel, "Popped data from queue. Res: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isDataAvailable(SharedMemoryIPC *ipc)
|
||||||
|
{
|
||||||
|
int res = !isQueueEmpty(ipc->rx_queue);
|
||||||
|
writeDebug(TraceLevel, "Checking if there is data pending to be read. Res: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isCorruptedShmem(SharedMemoryIPC *ipc, int is_owner)
|
||||||
|
{
|
||||||
|
if (isCorruptedQueue(ipc->rx_queue, isTowardsOwner(is_owner, 0)) ||
|
||||||
|
isCorruptedQueue(ipc->tx_queue, isTowardsOwner(is_owner, 1))
|
||||||
|
) {
|
||||||
|
writeDebug(WarningLevel, "Detected corrupted shared memory queue. Shared memory name: %s", ipc->shm_name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
24
external/cereal/LICENSE
vendored
Normal file
24
external/cereal/LICENSE
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2013-2022, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
448
external/cereal/access.hpp
vendored
Normal file
448
external/cereal/access.hpp
vendored
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
/*! \file access.hpp
|
||||||
|
\brief Access control, default construction, and serialization disambiguation */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_ACCESS_HPP_
|
||||||
|
#define CEREAL_ACCESS_HPP_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <cereal/macros.hpp>
|
||||||
|
#include <cereal/details/helpers.hpp>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
// ######################################################################
|
||||||
|
//! A class that allows cereal to load smart pointers to types that have no default constructor
|
||||||
|
/*! If your class does not have a default constructor, cereal will not be able
|
||||||
|
to load any smart pointers to it unless you overload LoadAndConstruct
|
||||||
|
for your class, and provide an appropriate load_and_construct method. You can also
|
||||||
|
choose to define a member static function instead of specializing this class.
|
||||||
|
|
||||||
|
The specialization of LoadAndConstruct must be placed within the cereal namespace:
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
struct MyType
|
||||||
|
{
|
||||||
|
MyType( int x ); // note: no default ctor
|
||||||
|
int myX;
|
||||||
|
|
||||||
|
// Define a serialize or load/save pair as you normally would
|
||||||
|
template <class Archive>
|
||||||
|
void serialize( Archive & ar )
|
||||||
|
{
|
||||||
|
ar( myX );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Provide a specialization for LoadAndConstruct for your type
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
template <> struct LoadAndConstruct<MyType>
|
||||||
|
{
|
||||||
|
// load_and_construct will be passed the archive that you will be loading
|
||||||
|
// from as well as a construct object which you can use as if it were the
|
||||||
|
// constructor for your type. cereal will handle all memory management for you.
|
||||||
|
template <class Archive>
|
||||||
|
static void load_and_construct( Archive & ar, cereal::construct<MyType> & construct )
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
ar( x );
|
||||||
|
construct( x );
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you require versioning, simply add a const std::uint32_t as the final parameter, e.g.:
|
||||||
|
// load_and_construct( Archive & ar, cereal::construct<MyType> & construct, std::uint32_t const version )
|
||||||
|
};
|
||||||
|
} // end namespace cereal
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Please note that just as in using external serialization functions, you cannot get
|
||||||
|
access to non-public members of your class by befriending cereal::access. If you
|
||||||
|
have the ability to modify the class you wish to serialize, it is recommended that you
|
||||||
|
use member serialize functions and a static member load_and_construct function.
|
||||||
|
|
||||||
|
load_and_construct functions, regardless of whether they are static members of your class or
|
||||||
|
whether you create one in the LoadAndConstruct specialization, have the following signature:
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
// generally Archive will be templated, but it can be specific if desired
|
||||||
|
template <class Archive>
|
||||||
|
static void load_and_construct( Archive & ar, cereal::construct<MyType> & construct );
|
||||||
|
// with an optional last parameter specifying the version: const std::uint32_t version
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Versioning behaves the same way as it does for standard serialization functions.
|
||||||
|
|
||||||
|
@tparam T The type to specialize for
|
||||||
|
@ingroup Access */
|
||||||
|
template <class T>
|
||||||
|
struct LoadAndConstruct
|
||||||
|
{ };
|
||||||
|
|
||||||
|
// forward decl for construct
|
||||||
|
//! @cond PRIVATE_NEVERDEFINED
|
||||||
|
namespace memory_detail{ template <class Ar, class T> struct LoadAndConstructLoadWrapper; }
|
||||||
|
//! @endcond
|
||||||
|
|
||||||
|
//! Used to construct types with no default constructor
|
||||||
|
/*! When serializing a type that has no default constructor, cereal
|
||||||
|
will attempt to call either the class static function load_and_construct
|
||||||
|
or the appropriate template specialization of LoadAndConstruct. cereal
|
||||||
|
will pass that function a reference to the archive as well as a reference
|
||||||
|
to a construct object which should be used to perform the allocation once
|
||||||
|
data has been appropriately loaded.
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
struct MyType
|
||||||
|
{
|
||||||
|
// note the lack of default constructor
|
||||||
|
MyType( int xx, int yy );
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
double notInConstructor;
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void serialize( Archive & ar )
|
||||||
|
{
|
||||||
|
ar( x, y );
|
||||||
|
ar( notInConstructor );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
static void load_and_construct( Archive & ar, cereal::construct<MyType> & construct )
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
ar( x, y );
|
||||||
|
|
||||||
|
// use construct object to initialize with loaded data
|
||||||
|
construct( x, y );
|
||||||
|
|
||||||
|
// access to member variables and functions via -> operator
|
||||||
|
ar( construct->notInConstructor );
|
||||||
|
|
||||||
|
// could also do the above section by:
|
||||||
|
double z;
|
||||||
|
ar( z );
|
||||||
|
construct->notInConstructor = z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@tparam T The class type being serialized
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
class construct
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Construct and initialize the type T with the given arguments
|
||||||
|
/*! This will forward all arguments to the underlying type T,
|
||||||
|
calling an appropriate constructor.
|
||||||
|
|
||||||
|
Calling this function more than once will result in an exception
|
||||||
|
being thrown.
|
||||||
|
|
||||||
|
@param args The arguments to the constructor for T
|
||||||
|
@throw Exception If called more than once */
|
||||||
|
template <class ... Args>
|
||||||
|
void operator()( Args && ... args );
|
||||||
|
// implementation deferred due to reliance on cereal::access
|
||||||
|
|
||||||
|
//! Get a reference to the initialized underlying object
|
||||||
|
/*! This must be called after the object has been initialized.
|
||||||
|
|
||||||
|
@return A reference to the initialized object
|
||||||
|
@throw Exception If called before initialization */
|
||||||
|
T * operator->()
|
||||||
|
{
|
||||||
|
if( !itsValid )
|
||||||
|
throw Exception("Object must be initialized prior to accessing members");
|
||||||
|
|
||||||
|
return itsPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns a raw pointer to the initialized underlying object
|
||||||
|
/*! This is mainly intended for use with passing an instance of
|
||||||
|
a constructed object to cereal::base_class.
|
||||||
|
|
||||||
|
It is strongly recommended to avoid using this function in
|
||||||
|
any other circumstance.
|
||||||
|
|
||||||
|
@return A raw pointer to the initialized type */
|
||||||
|
T * ptr()
|
||||||
|
{
|
||||||
|
return operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class A, class B> friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper;
|
||||||
|
|
||||||
|
construct( T * p ) : itsPtr( p ), itsEnableSharedRestoreFunction( [](){} ), itsValid( false ) {}
|
||||||
|
construct( T * p, std::function<void()> enableSharedFunc ) : // g++4.7 ice with default lambda to std func
|
||||||
|
itsPtr( p ), itsEnableSharedRestoreFunction( enableSharedFunc ), itsValid( false ) {}
|
||||||
|
construct( construct const & ) = delete;
|
||||||
|
construct & operator=( construct const & ) = delete;
|
||||||
|
|
||||||
|
T * itsPtr;
|
||||||
|
std::function<void()> itsEnableSharedRestoreFunction;
|
||||||
|
bool itsValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! A class that can be made a friend to give cereal access to non public functions
|
||||||
|
/*! If you desire non-public serialization functions within a class, cereal can only
|
||||||
|
access these if you declare cereal::access a friend.
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
class MyClass
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
friend class cereal::access; // gives access to the private serialize
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void serialize( Archive & ar )
|
||||||
|
{
|
||||||
|
// some code
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@endcode
|
||||||
|
@ingroup Access */
|
||||||
|
class access
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// ####### Standard Serialization ########################################
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_serialize(Archive & ar, T & t) -> decltype(t.CEREAL_SERIALIZE_FUNCTION_NAME(ar))
|
||||||
|
{ return t.CEREAL_SERIALIZE_FUNCTION_NAME(ar); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save(Archive & ar, T const & t) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar))
|
||||||
|
{ return t.CEREAL_SAVE_FUNCTION_NAME(ar); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save_non_const(Archive & ar, T & t) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar))
|
||||||
|
{ return t.CEREAL_SAVE_FUNCTION_NAME(ar); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_load(Archive & ar, T & t) -> decltype(t.CEREAL_LOAD_FUNCTION_NAME(ar))
|
||||||
|
{ return t.CEREAL_LOAD_FUNCTION_NAME(ar); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save_minimal(Archive const & ar, T const & t) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar))
|
||||||
|
{ return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save_minimal_non_const(Archive const & ar, T & t) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar))
|
||||||
|
{ return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar); }
|
||||||
|
|
||||||
|
template<class Archive, class T, class U> inline
|
||||||
|
static auto member_load_minimal(Archive const & ar, T & t, U && u) -> decltype(t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward<U>(u)))
|
||||||
|
{ return t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward<U>(u)); }
|
||||||
|
|
||||||
|
// ####### Versioned Serialization #######################################
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_serialize(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_SERIALIZE_FUNCTION_NAME(ar, version))
|
||||||
|
{ return t.CEREAL_SERIALIZE_FUNCTION_NAME(ar, version); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save(Archive & ar, T const & t, const std::uint32_t version ) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar, version))
|
||||||
|
{ return t.CEREAL_SAVE_FUNCTION_NAME(ar, version); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save_non_const(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar, version))
|
||||||
|
{ return t.CEREAL_SAVE_FUNCTION_NAME(ar, version); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_load(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_LOAD_FUNCTION_NAME(ar, version))
|
||||||
|
{ return t.CEREAL_LOAD_FUNCTION_NAME(ar, version); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save_minimal(Archive const & ar, T const & t, const std::uint32_t version) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version))
|
||||||
|
{ return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version); }
|
||||||
|
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
static auto member_save_minimal_non_const(Archive const & ar, T & t, const std::uint32_t version) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version))
|
||||||
|
{ return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version); }
|
||||||
|
|
||||||
|
template<class Archive, class T, class U> inline
|
||||||
|
static auto member_load_minimal(Archive const & ar, T & t, U && u, const std::uint32_t version) -> decltype(t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward<U>(u), version))
|
||||||
|
{ return t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward<U>(u), version); }
|
||||||
|
|
||||||
|
// ####### Other Functionality ##########################################
|
||||||
|
// for detecting inheritance from enable_shared_from_this
|
||||||
|
template <class T> inline
|
||||||
|
static auto shared_from_this(T & t) -> decltype(t.shared_from_this());
|
||||||
|
|
||||||
|
// for placement new
|
||||||
|
template <class T, class ... Args> inline
|
||||||
|
static void construct( T *& ptr, Args && ... args )
|
||||||
|
{
|
||||||
|
new (ptr) T( std::forward<Args>( args )... );
|
||||||
|
}
|
||||||
|
|
||||||
|
// for non-placement new with a default constructor
|
||||||
|
template <class T> inline
|
||||||
|
static T * construct()
|
||||||
|
{
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> inline
|
||||||
|
static std::false_type load_and_construct(...)
|
||||||
|
{ return std::false_type(); }
|
||||||
|
|
||||||
|
template<class T, class Archive> inline
|
||||||
|
static auto load_and_construct(Archive & ar, ::cereal::construct<T> & construct) -> decltype(T::load_and_construct(ar, construct))
|
||||||
|
{
|
||||||
|
T::load_and_construct( ar, construct );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Archive> inline
|
||||||
|
static auto load_and_construct(Archive & ar, ::cereal::construct<T> & construct, const std::uint32_t version) -> decltype(T::load_and_construct(ar, construct, version))
|
||||||
|
{
|
||||||
|
T::load_and_construct( ar, construct, version );
|
||||||
|
}
|
||||||
|
}; // end class access
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! A specifier used in conjunction with cereal::specialize to disambiguate
|
||||||
|
//! serialization in special cases
|
||||||
|
/*! @relates specialize
|
||||||
|
@ingroup Access */
|
||||||
|
enum class specialization
|
||||||
|
{
|
||||||
|
member_serialize, //!< Force the use of a member serialize function
|
||||||
|
member_load_save, //!< Force the use of a member load/save pair
|
||||||
|
member_load_save_minimal, //!< Force the use of a member minimal load/save pair
|
||||||
|
non_member_serialize, //!< Force the use of a non-member serialize function
|
||||||
|
non_member_load_save, //!< Force the use of a non-member load/save pair
|
||||||
|
non_member_load_save_minimal //!< Force the use of a non-member minimal load/save pair
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A class used to disambiguate cases where cereal cannot detect a unique way of serializing a class
|
||||||
|
/*! cereal attempts to figure out which method of serialization (member vs. non-member serialize
|
||||||
|
or load/save pair) at compile time. If for some reason cereal cannot find a non-ambiguous way
|
||||||
|
of serializing a type, it will produce a static assertion complaining about this.
|
||||||
|
|
||||||
|
This can happen because you have both a serialize and load/save pair, or even because a base
|
||||||
|
class has a serialize (public or private with friend access) and a derived class does not
|
||||||
|
overwrite this due to choosing some other serialization type.
|
||||||
|
|
||||||
|
Specializing this class will tell cereal to explicitly use the serialization type you specify
|
||||||
|
and it will not complain about ambiguity in its compile time selection. However, if cereal detects
|
||||||
|
an ambiguity in specializations, it will continue to issue a static assertion.
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
class MyParent
|
||||||
|
{
|
||||||
|
friend class cereal::access;
|
||||||
|
template <class Archive>
|
||||||
|
void serialize( Archive & ar ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Although serialize is private in MyParent, to cereal::access it will look public,
|
||||||
|
// even through MyDerived
|
||||||
|
class MyDerived : public MyParent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <class Archive>
|
||||||
|
void load( Archive & ar ) {}
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void save( Archive & ar ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The load/save pair in MyDerived is ambiguous because serialize in MyParent can
|
||||||
|
// be accessed from cereal::access. This looks the same as making serialize public
|
||||||
|
// in MyParent, making it seem as though MyDerived has both a serialize and a load/save pair.
|
||||||
|
// cereal will complain about this at compile time unless we disambiguate:
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
// This struct specialization will tell cereal which is the right way to serialize the ambiguity
|
||||||
|
template <class Archive> struct specialize<Archive, MyDerived, cereal::specialization::member_load_save> {};
|
||||||
|
|
||||||
|
// If we only had a disambiguation for a specific archive type, it would look something like this
|
||||||
|
template <> struct specialize<cereal::BinaryOutputArchive, MyDerived, cereal::specialization::member_load_save> {};
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
You can also choose to use the macros CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES or
|
||||||
|
CEREAL_SPECIALIZE_FOR_ARCHIVE if you want to type a little bit less.
|
||||||
|
|
||||||
|
@tparam T The type to specialize the serialization for
|
||||||
|
@tparam S The specialization type to use for T
|
||||||
|
@ingroup Access */
|
||||||
|
template <class Archive, class T, specialization S>
|
||||||
|
struct specialize : public std::false_type {};
|
||||||
|
|
||||||
|
//! Convenient macro for performing specialization for all archive types
|
||||||
|
/*! This performs specialization for the specific type for all types of archives.
|
||||||
|
This macro should be placed at the global namespace.
|
||||||
|
|
||||||
|
@code{cpp}
|
||||||
|
struct MyType {};
|
||||||
|
CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES( MyType, cereal::specialization::member_load_save );
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@relates specialize
|
||||||
|
@ingroup Access */
|
||||||
|
#define CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES( Type, Specialization ) \
|
||||||
|
namespace cereal { template <class Archive> struct specialize<Archive, Type, Specialization> {}; }
|
||||||
|
|
||||||
|
//! Convenient macro for performing specialization for a single archive type
|
||||||
|
/*! This performs specialization for the specific type for a single type of archive.
|
||||||
|
This macro should be placed at the global namespace.
|
||||||
|
|
||||||
|
@code{cpp}
|
||||||
|
struct MyType {};
|
||||||
|
CEREAL_SPECIALIZE_FOR_ARCHIVE( cereal::XMLInputArchive, MyType, cereal::specialization::member_load_save );
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@relates specialize
|
||||||
|
@ingroup Access */
|
||||||
|
#define CEREAL_SPECIALIZE_FOR_ARCHIVE( Archive, Type, Specialization ) \
|
||||||
|
namespace cereal { template <> struct specialize<Archive, Type, Specialization> {}; }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// Deferred Implementation, see construct for more information
|
||||||
|
template <class T> template <class ... Args> inline
|
||||||
|
void construct<T>::operator()( Args && ... args )
|
||||||
|
{
|
||||||
|
if( itsValid )
|
||||||
|
throw Exception("Attempting to construct an already initialized object");
|
||||||
|
|
||||||
|
::cereal::access::construct( itsPtr, std::forward<Args>( args )... );
|
||||||
|
itsEnableSharedRestoreFunction();
|
||||||
|
itsValid = true;
|
||||||
|
}
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_ACCESS_HPP_
|
||||||
163
external/cereal/archives/adapters.hpp
vendored
Normal file
163
external/cereal/archives/adapters.hpp
vendored
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*! \file adapters.hpp
|
||||||
|
\brief Archive adapters that provide additional functionality
|
||||||
|
on top of an existing archive */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_ARCHIVES_ADAPTERS_HPP_
|
||||||
|
#define CEREAL_ARCHIVES_ADAPTERS_HPP_
|
||||||
|
|
||||||
|
#include <cereal/details/helpers.hpp>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
#ifdef CEREAL_FUTURE_EXPERIMENTAL
|
||||||
|
|
||||||
|
// Forward declaration for friend access
|
||||||
|
template <class U, class A> U & get_user_data( A & );
|
||||||
|
|
||||||
|
//! Wraps an archive and gives access to user data
|
||||||
|
/*! This adapter is useful if you require access to
|
||||||
|
either raw pointers or references within your
|
||||||
|
serialization functions.
|
||||||
|
|
||||||
|
While cereal does not directly support serialization
|
||||||
|
raw pointers or references, it is sometimes the case
|
||||||
|
that you may want to supply something such as a raw
|
||||||
|
pointer or global reference to some constructor.
|
||||||
|
In this situation this adapter would likely be used
|
||||||
|
with the construct class to allow for non-default
|
||||||
|
constructors.
|
||||||
|
|
||||||
|
@note This feature is experimental and may be altered or removed in a future release. See issue #46.
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
struct MyUserData
|
||||||
|
{
|
||||||
|
int * myRawPointer;
|
||||||
|
std::reference_wrapper<MyOtherType> myReference;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MyClass
|
||||||
|
{
|
||||||
|
// Note the raw pointer parameter
|
||||||
|
MyClass( int xx, int * rawP );
|
||||||
|
|
||||||
|
int x;
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void serialize( Archive & ar )
|
||||||
|
{ ar( x ); }
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
static void load_and_construct( Archive & ar, cereal::construct<MyClass> & construct )
|
||||||
|
{
|
||||||
|
int xx;
|
||||||
|
ar( xx );
|
||||||
|
// note the need to use get_user_data to retrieve user data from the archive
|
||||||
|
construct( xx, cereal::get_user_data<MyUserData>( ar ).myRawPointer );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
MyUserData md;
|
||||||
|
md.myRawPointer = &something;
|
||||||
|
md.myReference = someInstanceOfType;
|
||||||
|
|
||||||
|
std::ifstream is( "data.xml" );
|
||||||
|
cereal::UserDataAdapter<MyUserData, cereal::XMLInputArchive> ar( md, is );
|
||||||
|
|
||||||
|
std::unique_ptr<MyClass> sc;
|
||||||
|
ar( sc ); // use as normal
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@relates get_user_data
|
||||||
|
|
||||||
|
@tparam UserData The type to give the archive access to
|
||||||
|
@tparam Archive The archive to wrap */
|
||||||
|
template <class UserData, class Archive>
|
||||||
|
class UserDataAdapter : public Archive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Construct the archive with some user data struct
|
||||||
|
/*! This will forward all arguments (other than the user
|
||||||
|
data) to the wrapped archive type. The UserDataAdapter
|
||||||
|
can then be used identically to the wrapped archive type
|
||||||
|
|
||||||
|
@tparam Args The arguments to pass to the constructor of
|
||||||
|
the archive. */
|
||||||
|
template <class ... Args>
|
||||||
|
UserDataAdapter( UserData & ud, Args && ... args ) :
|
||||||
|
Archive( std::forward<Args>( args )... ),
|
||||||
|
userdata( ud )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Overload the rtti function to enable dynamic_cast
|
||||||
|
void rtti() {}
|
||||||
|
friend UserData & get_user_data<UserData>( Archive & ar );
|
||||||
|
UserData & userdata; //!< The actual user data
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Retrieves user data from an archive wrapped by UserDataAdapter
|
||||||
|
/*! This will attempt to retrieve the user data associated with
|
||||||
|
some archive wrapped by UserDataAdapter. If this is used on
|
||||||
|
an archive that is not wrapped, a run-time exception will occur.
|
||||||
|
|
||||||
|
@note This feature is experimental and may be altered or removed in a future release. See issue #46.
|
||||||
|
|
||||||
|
@note The correct use of this function cannot be enforced at compile
|
||||||
|
time.
|
||||||
|
|
||||||
|
@relates UserDataAdapter
|
||||||
|
@tparam UserData The data struct contained in the archive
|
||||||
|
@tparam Archive The archive, which should be wrapped by UserDataAdapter
|
||||||
|
@param ar The archive
|
||||||
|
@throws Exception if the archive this is used upon is not wrapped with
|
||||||
|
UserDataAdapter. */
|
||||||
|
template <class UserData, class Archive>
|
||||||
|
UserData & get_user_data( Archive & ar )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return dynamic_cast<UserDataAdapter<UserData, Archive> &>( ar ).userdata;
|
||||||
|
}
|
||||||
|
catch( std::bad_cast const & )
|
||||||
|
{
|
||||||
|
throw ::cereal::Exception("Attempting to get user data from archive not wrapped in UserDataAdapter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // CEREAL_FUTURE_EXPERIMENTAL
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_ARCHIVES_ADAPTERS_HPP_
|
||||||
169
external/cereal/archives/binary.hpp
vendored
Normal file
169
external/cereal/archives/binary.hpp
vendored
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*! \file binary.hpp
|
||||||
|
\brief Binary input and output archives */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_ARCHIVES_BINARY_HPP_
|
||||||
|
#define CEREAL_ARCHIVES_BINARY_HPP_
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
// ######################################################################
|
||||||
|
//! An output archive designed to save data in a compact binary representation
|
||||||
|
/*! This archive outputs data to a stream in an extremely compact binary
|
||||||
|
representation with as little extra metadata as possible.
|
||||||
|
|
||||||
|
This archive does nothing to ensure that the endianness of the saved
|
||||||
|
and loaded data is the same. If you need to have portability over
|
||||||
|
architectures with different endianness, use PortableBinaryOutputArchive.
|
||||||
|
|
||||||
|
When using a binary archive and a file stream, you must use the
|
||||||
|
std::ios::binary format flag to avoid having your data altered
|
||||||
|
inadvertently.
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class BinaryOutputArchive : public OutputArchive<BinaryOutputArchive, AllowEmptyClassElision>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Construct, outputting to the provided stream
|
||||||
|
/*! @param stream The stream to output to. Can be a stringstream, a file stream, or
|
||||||
|
even cout! */
|
||||||
|
BinaryOutputArchive(std::ostream & stream) :
|
||||||
|
OutputArchive<BinaryOutputArchive, AllowEmptyClassElision>(this),
|
||||||
|
itsStream(stream)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~BinaryOutputArchive() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Writes size bytes of data to the output stream
|
||||||
|
void saveBinary( const void * data, std::size_t size )
|
||||||
|
{
|
||||||
|
auto const writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ) );
|
||||||
|
|
||||||
|
if(writtenSize != size)
|
||||||
|
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream & itsStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! An input archive designed to load data saved using BinaryOutputArchive
|
||||||
|
/* This archive does nothing to ensure that the endianness of the saved
|
||||||
|
and loaded data is the same. If you need to have portability over
|
||||||
|
architectures with different endianness, use PortableBinaryOutputArchive.
|
||||||
|
|
||||||
|
When using a binary archive and a file stream, you must use the
|
||||||
|
std::ios::binary format flag to avoid having your data altered
|
||||||
|
inadvertently.
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class BinaryInputArchive : public InputArchive<BinaryInputArchive, AllowEmptyClassElision>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Construct, loading from the provided stream
|
||||||
|
BinaryInputArchive(std::istream & stream) :
|
||||||
|
InputArchive<BinaryInputArchive, AllowEmptyClassElision>(this),
|
||||||
|
itsStream(stream)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~BinaryInputArchive() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Reads size bytes of data from the input stream
|
||||||
|
void loadBinary( void * const data, std::size_t size )
|
||||||
|
{
|
||||||
|
auto const readSize = static_cast<std::size_t>( itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size ) );
|
||||||
|
|
||||||
|
if(readSize != size)
|
||||||
|
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::istream & itsStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// Common BinaryArchive serialization functions
|
||||||
|
|
||||||
|
//! Saving for POD types to binary
|
||||||
|
template<class T> inline
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||||
|
CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, T const & t)
|
||||||
|
{
|
||||||
|
ar.saveBinary(std::addressof(t), sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading for POD types from binary
|
||||||
|
template<class T> inline
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||||
|
CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, T & t)
|
||||||
|
{
|
||||||
|
ar.loadBinary(std::addressof(t), sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Serializing NVP types to binary
|
||||||
|
template <class Archive, class T> inline
|
||||||
|
CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
|
||||||
|
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
|
||||||
|
{
|
||||||
|
ar( t.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Serializing SizeTags to binary
|
||||||
|
template <class Archive, class T> inline
|
||||||
|
CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
|
||||||
|
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
|
||||||
|
{
|
||||||
|
ar( t.size );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saving binary data
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, BinaryData<T> const & bd)
|
||||||
|
{
|
||||||
|
ar.saveBinary( bd.data, static_cast<std::size_t>( bd.size ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading binary data
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, BinaryData<T> & bd)
|
||||||
|
{
|
||||||
|
ar.loadBinary(bd.data, static_cast<std::size_t>(bd.size));
|
||||||
|
}
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
// register archives for polymorphic support
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::BinaryOutputArchive)
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::BinaryInputArchive)
|
||||||
|
|
||||||
|
// tie input and output archives together
|
||||||
|
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::BinaryInputArchive, cereal::BinaryOutputArchive)
|
||||||
|
|
||||||
|
#endif // CEREAL_ARCHIVES_BINARY_HPP_
|
||||||
975
external/cereal/archives/json.hpp
vendored
Normal file
975
external/cereal/archives/json.hpp
vendored
Normal file
@@ -0,0 +1,975 @@
|
|||||||
|
/*! \file json.hpp
|
||||||
|
\brief JSON input and output archives */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_ARCHIVES_JSON_HPP_
|
||||||
|
#define CEREAL_ARCHIVES_JSON_HPP_
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <cereal/details/util.hpp>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
//! An exception thrown when rapidjson fails an internal assertion
|
||||||
|
/*! @ingroup Utility */
|
||||||
|
struct RapidJSONException : Exception
|
||||||
|
{ RapidJSONException( const char * what_ ) : Exception( what_ ) {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override rapidjson assertions to throw exceptions by default
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ASSERT
|
||||||
|
#define CEREAL_RAPIDJSON_ASSERT(x) if(!(x)){ \
|
||||||
|
throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); }
|
||||||
|
#endif // RAPIDJSON_ASSERT
|
||||||
|
|
||||||
|
// Enable support for parsing of nan, inf, -inf
|
||||||
|
#define CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNanAndInfFlag
|
||||||
|
#define CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag | kParseNanAndInfFlag
|
||||||
|
|
||||||
|
#include <cereal/external/rapidjson/prettywriter.h>
|
||||||
|
#include <cereal/external/rapidjson/ostreamwrapper.h>
|
||||||
|
#include <cereal/external/rapidjson/istreamwrapper.h>
|
||||||
|
#include <cereal/external/rapidjson/document.h>
|
||||||
|
#include <cereal/external/base64.hpp>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
// ######################################################################
|
||||||
|
//! An output archive designed to save data to JSON
|
||||||
|
/*! This archive uses RapidJSON to build serialize data to JSON.
|
||||||
|
|
||||||
|
JSON archives provides a human readable output but at decreased
|
||||||
|
performance (both in time and space) compared to binary archives.
|
||||||
|
|
||||||
|
JSON archives are only guaranteed to finish flushing their contents
|
||||||
|
upon destruction and should thus be used in an RAII fashion.
|
||||||
|
|
||||||
|
JSON benefits greatly from name-value pairs, which if present, will
|
||||||
|
name the nodes in the output. If these are not present, each level
|
||||||
|
of the output will be given an automatically generated delimited name.
|
||||||
|
|
||||||
|
The precision of the output archive controls the number of decimals output
|
||||||
|
for floating point numbers and should be sufficiently large (i.e. at least 20)
|
||||||
|
if there is a desire to have binary equality between the numbers output and
|
||||||
|
those read in. In general you should expect a loss of precision when going
|
||||||
|
from floating point to text and back.
|
||||||
|
|
||||||
|
JSON archives do not output the size information for any dynamically sized structure
|
||||||
|
and instead infer it from the number of children for a node. This means that data
|
||||||
|
can be hand edited for dynamic sized structures and will still be readable. This
|
||||||
|
is accomplished through the cereal::SizeTag object, which will cause the archive
|
||||||
|
to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates
|
||||||
|
that the container is variable sized and may be edited.
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class JSONOutputArchive : public OutputArchive<JSONOutputArchive>, public traits::TextArchive
|
||||||
|
{
|
||||||
|
enum class NodeType { StartObject, InObject, StartArray, InArray };
|
||||||
|
|
||||||
|
using WriteStream = rapidjson::OStreamWrapper;
|
||||||
|
using JSONWriter = rapidjson::PrettyWriter<WriteStream>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @name Common Functionality
|
||||||
|
Common use cases for directly interacting with an JSONOutputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! A class containing various advanced options for the JSON archive
|
||||||
|
class Options
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Default options
|
||||||
|
static Options Default(){ return Options(); }
|
||||||
|
|
||||||
|
//! Default options with no indentation
|
||||||
|
static Options NoIndent(){ return Options( JSONWriter::kDefaultMaxDecimalPlaces, IndentChar::space, 0 ); }
|
||||||
|
|
||||||
|
//! The character to use for indenting
|
||||||
|
enum class IndentChar : char
|
||||||
|
{
|
||||||
|
space = ' ',
|
||||||
|
tab = '\t',
|
||||||
|
newline = '\n',
|
||||||
|
carriage_return = '\r'
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Specify specific options for the JSONOutputArchive
|
||||||
|
/*! @param precision The precision used for floating point numbers
|
||||||
|
@param indentChar The type of character to indent with
|
||||||
|
@param indentLength The number of indentChar to use for indentation
|
||||||
|
(0 corresponds to no indentation) */
|
||||||
|
explicit Options( int precision = JSONWriter::kDefaultMaxDecimalPlaces,
|
||||||
|
IndentChar indentChar = IndentChar::space,
|
||||||
|
unsigned int indentLength = 4 ) :
|
||||||
|
itsPrecision( precision ),
|
||||||
|
itsIndentChar( static_cast<char>(indentChar) ),
|
||||||
|
itsIndentLength( indentLength ) { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class JSONOutputArchive;
|
||||||
|
int itsPrecision;
|
||||||
|
char itsIndentChar;
|
||||||
|
unsigned int itsIndentLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Construct, outputting to the provided stream
|
||||||
|
/*! @param stream The stream to output to.
|
||||||
|
@param options The JSON specific options to use. See the Options struct
|
||||||
|
for the values of default parameters */
|
||||||
|
JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) :
|
||||||
|
OutputArchive<JSONOutputArchive>(this),
|
||||||
|
itsWriteStream(stream),
|
||||||
|
itsWriter(itsWriteStream),
|
||||||
|
itsNextName(nullptr)
|
||||||
|
{
|
||||||
|
itsWriter.SetMaxDecimalPlaces( options.itsPrecision );
|
||||||
|
itsWriter.SetIndent( options.itsIndentChar, options.itsIndentLength );
|
||||||
|
itsNameCounter.push(0);
|
||||||
|
itsNodeStack.push(NodeType::StartObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructor, flushes the JSON
|
||||||
|
~JSONOutputArchive() CEREAL_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (itsNodeStack.top() == NodeType::InObject)
|
||||||
|
itsWriter.EndObject();
|
||||||
|
else if (itsNodeStack.top() == NodeType::InArray)
|
||||||
|
itsWriter.EndArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saves some binary data, encoded as a base64 string, with an optional name
|
||||||
|
/*! This will create a new node, optionally named, and insert a value that consists of
|
||||||
|
the data encoded as a base64 string */
|
||||||
|
void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
|
||||||
|
{
|
||||||
|
setNextName( name );
|
||||||
|
writeName();
|
||||||
|
|
||||||
|
auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
|
||||||
|
saveValue( base64string );
|
||||||
|
};
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
/*! @name Internal Functionality
|
||||||
|
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||||
|
the JSONOutputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Starts a new node in the JSON output
|
||||||
|
/*! The node can optionally be given a name by calling setNextName prior
|
||||||
|
to creating the node
|
||||||
|
|
||||||
|
Nodes only need to be started for types that are themselves objects or arrays */
|
||||||
|
void startNode()
|
||||||
|
{
|
||||||
|
writeName();
|
||||||
|
itsNodeStack.push(NodeType::StartObject);
|
||||||
|
itsNameCounter.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Designates the most recently added node as finished
|
||||||
|
void finishNode()
|
||||||
|
{
|
||||||
|
// if we ended up serializing an empty object or array, writeName
|
||||||
|
// will never have been called - so start and then immediately end
|
||||||
|
// the object/array.
|
||||||
|
//
|
||||||
|
// We'll also end any object/arrays we happen to be in
|
||||||
|
switch(itsNodeStack.top())
|
||||||
|
{
|
||||||
|
case NodeType::StartArray:
|
||||||
|
itsWriter.StartArray();
|
||||||
|
/* FALLTHRU */
|
||||||
|
case NodeType::InArray:
|
||||||
|
itsWriter.EndArray();
|
||||||
|
break;
|
||||||
|
case NodeType::StartObject:
|
||||||
|
itsWriter.StartObject();
|
||||||
|
/* FALLTHRU */
|
||||||
|
case NodeType::InObject:
|
||||||
|
itsWriter.EndObject();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
itsNodeStack.pop();
|
||||||
|
itsNameCounter.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the name for the next node created with startNode
|
||||||
|
void setNextName( const char * name )
|
||||||
|
{
|
||||||
|
itsNextName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saves a bool to the current node
|
||||||
|
void saveValue(bool b) { itsWriter.Bool(b); }
|
||||||
|
//! Saves an int to the current node
|
||||||
|
void saveValue(int i) { itsWriter.Int(i); }
|
||||||
|
//! Saves a uint to the current node
|
||||||
|
void saveValue(unsigned u) { itsWriter.Uint(u); }
|
||||||
|
//! Saves an int64 to the current node
|
||||||
|
void saveValue(int64_t i64) { itsWriter.Int64(i64); }
|
||||||
|
//! Saves a uint64 to the current node
|
||||||
|
void saveValue(uint64_t u64) { itsWriter.Uint64(u64); }
|
||||||
|
//! Saves a double to the current node
|
||||||
|
void saveValue(double d) { itsWriter.Double(d); }
|
||||||
|
//! Saves a string to the current node
|
||||||
|
void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast<rapidjson::SizeType>( s.size() )); }
|
||||||
|
//! Saves a const char * to the current node
|
||||||
|
void saveValue(char const * s) { itsWriter.String(s); }
|
||||||
|
//! Saves a nullptr to the current node
|
||||||
|
void saveValue(std::nullptr_t) { itsWriter.Null(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Some compilers/OS have difficulty disambiguating the above for various flavors of longs, so we provide
|
||||||
|
// special overloads to handle these cases.
|
||||||
|
|
||||||
|
//! 32 bit signed long saving to current node
|
||||||
|
template <class T, traits::EnableIf<sizeof(T) == sizeof(std::int32_t),
|
||||||
|
std::is_signed<T>::value> = traits::sfinae> inline
|
||||||
|
void saveLong(T l){ saveValue( static_cast<std::int32_t>( l ) ); }
|
||||||
|
|
||||||
|
//! non 32 bit signed long saving to current node
|
||||||
|
template <class T, traits::EnableIf<sizeof(T) != sizeof(std::int32_t),
|
||||||
|
std::is_signed<T>::value> = traits::sfinae> inline
|
||||||
|
void saveLong(T l){ saveValue( static_cast<std::int64_t>( l ) ); }
|
||||||
|
|
||||||
|
//! 32 bit unsigned long saving to current node
|
||||||
|
template <class T, traits::EnableIf<sizeof(T) == sizeof(std::int32_t),
|
||||||
|
std::is_unsigned<T>::value> = traits::sfinae> inline
|
||||||
|
void saveLong(T lu){ saveValue( static_cast<std::uint32_t>( lu ) ); }
|
||||||
|
|
||||||
|
//! non 32 bit unsigned long saving to current node
|
||||||
|
template <class T, traits::EnableIf<sizeof(T) != sizeof(std::int32_t),
|
||||||
|
std::is_unsigned<T>::value> = traits::sfinae> inline
|
||||||
|
void saveLong(T lu){ saveValue( static_cast<std::uint64_t>( lu ) ); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
//! MSVC only long overload to current node
|
||||||
|
void saveValue( unsigned long lu ){ saveLong( lu ); };
|
||||||
|
#else // _MSC_VER
|
||||||
|
//! Serialize a long if it would not be caught otherwise
|
||||||
|
template <class T, traits::EnableIf<std::is_same<T, long>::value,
|
||||||
|
!std::is_same<T, std::int32_t>::value,
|
||||||
|
!std::is_same<T, std::int64_t>::value> = traits::sfinae> inline
|
||||||
|
void saveValue( T t ){ saveLong( t ); }
|
||||||
|
|
||||||
|
//! Serialize an unsigned long if it would not be caught otherwise
|
||||||
|
template <class T, traits::EnableIf<std::is_same<T, unsigned long>::value,
|
||||||
|
!std::is_same<T, std::uint32_t>::value,
|
||||||
|
!std::is_same<T, std::uint64_t>::value> = traits::sfinae> inline
|
||||||
|
void saveValue( T t ){ saveLong( t ); }
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
//! Save exotic arithmetic as strings to current node
|
||||||
|
/*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value,
|
||||||
|
!std::is_same<T, long>::value,
|
||||||
|
!std::is_same<T, unsigned long>::value,
|
||||||
|
!std::is_same<T, std::int64_t>::value,
|
||||||
|
!std::is_same<T, std::uint64_t>::value,
|
||||||
|
(sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae> inline
|
||||||
|
void saveValue(T const & t)
|
||||||
|
{
|
||||||
|
std::stringstream ss; ss.precision( std::numeric_limits<long double>::max_digits10 );
|
||||||
|
ss << t;
|
||||||
|
saveValue( ss.str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Write the name of the upcoming node and prepare object/array state
|
||||||
|
/*! Since writeName is called for every value that is output, regardless of
|
||||||
|
whether it has a name or not, it is the place where we will do a deferred
|
||||||
|
check of our node state and decide whether we are in an array or an object.
|
||||||
|
|
||||||
|
The general workflow of saving to the JSON archive is:
|
||||||
|
|
||||||
|
1. (optional) Set the name for the next node to be created, usually done by an NVP
|
||||||
|
2. Start the node
|
||||||
|
3. (if there is data to save) Write the name of the node (this function)
|
||||||
|
4. (if there is data to save) Save the data (with saveValue)
|
||||||
|
5. Finish the node
|
||||||
|
*/
|
||||||
|
void writeName()
|
||||||
|
{
|
||||||
|
NodeType const & nodeType = itsNodeStack.top();
|
||||||
|
|
||||||
|
// Start up either an object or an array, depending on state
|
||||||
|
if(nodeType == NodeType::StartArray)
|
||||||
|
{
|
||||||
|
itsWriter.StartArray();
|
||||||
|
itsNodeStack.top() = NodeType::InArray;
|
||||||
|
}
|
||||||
|
else if(nodeType == NodeType::StartObject)
|
||||||
|
{
|
||||||
|
itsNodeStack.top() = NodeType::InObject;
|
||||||
|
itsWriter.StartObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array types do not output names
|
||||||
|
if(nodeType == NodeType::InArray) return;
|
||||||
|
|
||||||
|
if(itsNextName == nullptr)
|
||||||
|
{
|
||||||
|
std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0";
|
||||||
|
saveValue(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveValue(itsNextName);
|
||||||
|
itsNextName = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Designates that the current node should be output as an array, not an object
|
||||||
|
void makeArray()
|
||||||
|
{
|
||||||
|
itsNodeStack.top() = NodeType::StartArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WriteStream itsWriteStream; //!< Rapidjson write stream
|
||||||
|
JSONWriter itsWriter; //!< Rapidjson writer
|
||||||
|
char const * itsNextName; //!< The next name
|
||||||
|
std::stack<uint32_t> itsNameCounter; //!< Counter for creating unique names for unnamed nodes
|
||||||
|
std::stack<NodeType> itsNodeStack;
|
||||||
|
}; // JSONOutputArchive
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! An input archive designed to load data from JSON
|
||||||
|
/*! This archive uses RapidJSON to read in a JSON archive.
|
||||||
|
|
||||||
|
As with the output JSON archive, the preferred way to use this archive is in
|
||||||
|
an RAII fashion, ensuring its destruction after all data has been read.
|
||||||
|
|
||||||
|
Input JSON should have been produced by the JSONOutputArchive. Data can
|
||||||
|
only be added to dynamically sized containers (marked by JSON arrays) -
|
||||||
|
the input archive will determine their size by looking at the number of child nodes.
|
||||||
|
Only JSON originating from a JSONOutputArchive is officially supported, but data
|
||||||
|
from other sources may work if properly formatted.
|
||||||
|
|
||||||
|
The JSONInputArchive does not require that nodes are loaded in the same
|
||||||
|
order they were saved by JSONOutputArchive. Using name value pairs (NVPs),
|
||||||
|
it is possible to load in an out of order fashion or otherwise skip/select
|
||||||
|
specific nodes to load.
|
||||||
|
|
||||||
|
The default behavior of the input archive is to read sequentially starting
|
||||||
|
with the first node and exploring its children. When a given NVP does
|
||||||
|
not match the read in name for a node, the archive will search for that
|
||||||
|
node at the current level and load it if it exists. After loading an out of
|
||||||
|
order node, the archive will then proceed back to loading sequentially from
|
||||||
|
its new position.
|
||||||
|
|
||||||
|
Consider this simple example where loading of some data is skipped:
|
||||||
|
|
||||||
|
@code{cpp}
|
||||||
|
// imagine the input file has someData(1-9) saved in order at the top level node
|
||||||
|
ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file
|
||||||
|
ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
|
||||||
|
// match expected NVP name, so we search
|
||||||
|
// for the given NVP and load that value
|
||||||
|
ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its
|
||||||
|
// current location, proceeding sequentially
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class JSONInputArchive : public InputArchive<JSONInputArchive>, public traits::TextArchive
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using ReadStream = rapidjson::IStreamWrapper;
|
||||||
|
typedef rapidjson::GenericValue<rapidjson::UTF8<>> JSONValue;
|
||||||
|
typedef JSONValue::ConstMemberIterator MemberIterator;
|
||||||
|
typedef JSONValue::ConstValueIterator ValueIterator;
|
||||||
|
typedef rapidjson::Document::GenericValue GenericValue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @name Common Functionality
|
||||||
|
Common use cases for directly interacting with an JSONInputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Construct, reading from the provided stream
|
||||||
|
/*! @param stream The stream to read from */
|
||||||
|
JSONInputArchive(std::istream & stream) :
|
||||||
|
InputArchive<JSONInputArchive>(this),
|
||||||
|
itsNextName( nullptr ),
|
||||||
|
itsReadStream(stream)
|
||||||
|
{
|
||||||
|
itsDocument.ParseStream<>(itsReadStream);
|
||||||
|
if (itsDocument.IsArray())
|
||||||
|
itsIteratorStack.emplace_back(itsDocument.Begin(), itsDocument.End());
|
||||||
|
else
|
||||||
|
itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
~JSONInputArchive() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Loads some binary data, encoded as a base64 string
|
||||||
|
/*! This will automatically start and finish a node to load the data, and can be called directly by
|
||||||
|
users.
|
||||||
|
|
||||||
|
Note that this follows the same ordering rules specified in the class description in regards
|
||||||
|
to loading in/out of order */
|
||||||
|
void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
|
||||||
|
{
|
||||||
|
itsNextName = name;
|
||||||
|
|
||||||
|
std::string encoded;
|
||||||
|
loadValue( encoded );
|
||||||
|
auto decoded = base64::decode( encoded );
|
||||||
|
|
||||||
|
if( size != decoded.size() )
|
||||||
|
throw Exception("Decoded binary data size does not match specified size");
|
||||||
|
|
||||||
|
std::memcpy( data, decoded.data(), decoded.size() );
|
||||||
|
itsNextName = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! @}
|
||||||
|
/*! @name Internal Functionality
|
||||||
|
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||||
|
the JSONInputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! An internal iterator that handles both array and object types
|
||||||
|
/*! This class is a variant and holds both types of iterators that
|
||||||
|
rapidJSON supports - one for arrays and one for objects. */
|
||||||
|
class Iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Iterator() : itsIndex( 0 ), itsType(Null_) {}
|
||||||
|
|
||||||
|
Iterator(MemberIterator begin, MemberIterator end) :
|
||||||
|
itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Iterator(ValueIterator begin, ValueIterator end) :
|
||||||
|
itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Advance to the next node
|
||||||
|
Iterator & operator++()
|
||||||
|
{
|
||||||
|
++itsIndex;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Get the value of the current node
|
||||||
|
GenericValue const & value()
|
||||||
|
{
|
||||||
|
switch(itsType)
|
||||||
|
{
|
||||||
|
case Value : return itsValueItBegin[itsIndex];
|
||||||
|
case Member: return itsMemberItBegin[itsIndex].value;
|
||||||
|
default: throw cereal::Exception("Invalid Iterator Type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Get the name of the current node, or nullptr if it has no name
|
||||||
|
const char * name() const
|
||||||
|
{
|
||||||
|
if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd )
|
||||||
|
return itsMemberItBegin[itsIndex].name.GetString();
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Adjust our position such that we are at the node with the given name
|
||||||
|
/*! @throws Exception if no such named node exists */
|
||||||
|
/*! @throws Exception if more than one such named node exists */
|
||||||
|
inline void search( const char * searchName )
|
||||||
|
{
|
||||||
|
const auto len = std::strlen( searchName );
|
||||||
|
size_t index = 0;
|
||||||
|
bool found = false;
|
||||||
|
for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index )
|
||||||
|
{
|
||||||
|
const auto currentName = it->name.GetString();
|
||||||
|
if( ( std::strncmp( searchName, currentName, len ) == 0 ) &&
|
||||||
|
( std::strlen( currentName ) == len ) )
|
||||||
|
{
|
||||||
|
itsIndex = index;
|
||||||
|
if( found )
|
||||||
|
throw Exception("JSON Parsing failed - provided NVP (" + std::string(searchName) + ") found more than once");
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( found ) return;
|
||||||
|
|
||||||
|
throw Exception("JSON Parsing failed - provided NVP (" + std::string(searchName) + ") not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object)
|
||||||
|
ValueIterator itsValueItBegin, itsValueItEnd; //!< The value iterator (array)
|
||||||
|
size_t itsIndex; //!< The current index of this iterator
|
||||||
|
enum Type {Value, Member, Null_} itsType; //!< Whether this holds values (array) or members (objects) or nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Searches for the expectedName node if it doesn't match the actualName
|
||||||
|
/*! This needs to be called before every load or node start occurs. This function will
|
||||||
|
check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual
|
||||||
|
next name given. If the names do not match, it will search in the current level of the JSON for that name.
|
||||||
|
If the name is not found, an exception will be thrown.
|
||||||
|
|
||||||
|
Resets the NVP name after called.
|
||||||
|
|
||||||
|
@throws Exception if an expectedName is given and not found */
|
||||||
|
inline void search()
|
||||||
|
{
|
||||||
|
// The name an NVP provided with setNextName()
|
||||||
|
if( itsNextName )
|
||||||
|
itsIteratorStack.back().search( itsNextName );
|
||||||
|
|
||||||
|
itsNextName = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Starts a new node, going into its proper iterator
|
||||||
|
/*! This places an iterator for the next node to be parsed onto the iterator stack. If the next
|
||||||
|
node is an array, this will be a value iterator, otherwise it will be a member iterator.
|
||||||
|
|
||||||
|
By default our strategy is to start with the document root node and then recursively iterate through
|
||||||
|
all children in the order they show up in the document.
|
||||||
|
We don't need to know NVPs to do this; we'll just blindly load in the order things appear in.
|
||||||
|
|
||||||
|
If we were given an NVP, we will search for it if it does not match our the name of the next node
|
||||||
|
that would normally be loaded. This functionality is provided by search(). */
|
||||||
|
void startNode()
|
||||||
|
{
|
||||||
|
search();
|
||||||
|
|
||||||
|
if(itsIteratorStack.back().value().IsArray())
|
||||||
|
itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End());
|
||||||
|
else
|
||||||
|
itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Finishes the most recently started node
|
||||||
|
void finishNode()
|
||||||
|
{
|
||||||
|
itsIteratorStack.pop_back();
|
||||||
|
++itsIteratorStack.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Retrieves the current node name
|
||||||
|
/*! @return nullptr if no name exists */
|
||||||
|
const char * getNodeName() const
|
||||||
|
{
|
||||||
|
return itsIteratorStack.back().name();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the name for the next node created with startNode
|
||||||
|
void setNextName( const char * name )
|
||||||
|
{
|
||||||
|
itsNextName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a value from the current node - small signed overload
|
||||||
|
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||||
|
sizeof(T) < sizeof(int64_t)> = traits::sfinae> inline
|
||||||
|
void loadValue(T & val)
|
||||||
|
{
|
||||||
|
search();
|
||||||
|
|
||||||
|
val = static_cast<T>( itsIteratorStack.back().value().GetInt() );
|
||||||
|
++itsIteratorStack.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a value from the current node - small unsigned overload
|
||||||
|
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||||
|
sizeof(T) < sizeof(uint64_t),
|
||||||
|
!std::is_same<bool, T>::value> = traits::sfinae> inline
|
||||||
|
void loadValue(T & val)
|
||||||
|
{
|
||||||
|
search();
|
||||||
|
|
||||||
|
val = static_cast<T>( itsIteratorStack.back().value().GetUint() );
|
||||||
|
++itsIteratorStack.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a value from the current node - bool overload
|
||||||
|
void loadValue(bool & val) { search(); val = itsIteratorStack.back().value().GetBool(); ++itsIteratorStack.back(); }
|
||||||
|
//! Loads a value from the current node - int64 overload
|
||||||
|
void loadValue(int64_t & val) { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); }
|
||||||
|
//! Loads a value from the current node - uint64 overload
|
||||||
|
void loadValue(uint64_t & val) { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); }
|
||||||
|
//! Loads a value from the current node - float overload
|
||||||
|
void loadValue(float & val) { search(); val = static_cast<float>(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); }
|
||||||
|
//! Loads a value from the current node - double overload
|
||||||
|
void loadValue(double & val) { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); }
|
||||||
|
//! Loads a value from the current node - string overload
|
||||||
|
void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); }
|
||||||
|
//! Loads a nullptr from the current node
|
||||||
|
void loadValue(std::nullptr_t&) { search(); CEREAL_RAPIDJSON_ASSERT(itsIteratorStack.back().value().IsNull()); ++itsIteratorStack.back(); }
|
||||||
|
|
||||||
|
// Special cases to handle various flavors of long, which tend to conflict with
|
||||||
|
// the int32_t or int64_t on various compiler/OS combinations. MSVC doesn't need any of this.
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
private:
|
||||||
|
//! 32 bit signed long loading from current node
|
||||||
|
template <class T> inline
|
||||||
|
typename std::enable_if<sizeof(T) == sizeof(std::int32_t) && std::is_signed<T>::value, void>::type
|
||||||
|
loadLong(T & l){ loadValue( reinterpret_cast<std::int32_t&>( l ) ); }
|
||||||
|
|
||||||
|
//! non 32 bit signed long loading from current node
|
||||||
|
template <class T> inline
|
||||||
|
typename std::enable_if<sizeof(T) == sizeof(std::int64_t) && std::is_signed<T>::value, void>::type
|
||||||
|
loadLong(T & l){ loadValue( reinterpret_cast<std::int64_t&>( l ) ); }
|
||||||
|
|
||||||
|
//! 32 bit unsigned long loading from current node
|
||||||
|
template <class T> inline
|
||||||
|
typename std::enable_if<sizeof(T) == sizeof(std::uint32_t) && !std::is_signed<T>::value, void>::type
|
||||||
|
loadLong(T & lu){ loadValue( reinterpret_cast<std::uint32_t&>( lu ) ); }
|
||||||
|
|
||||||
|
//! non 32 bit unsigned long loading from current node
|
||||||
|
template <class T> inline
|
||||||
|
typename std::enable_if<sizeof(T) == sizeof(std::uint64_t) && !std::is_signed<T>::value, void>::type
|
||||||
|
loadLong(T & lu){ loadValue( reinterpret_cast<std::uint64_t&>( lu ) ); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Serialize a long if it would not be caught otherwise
|
||||||
|
template <class T> inline
|
||||||
|
typename std::enable_if<std::is_same<T, long>::value &&
|
||||||
|
sizeof(T) >= sizeof(std::int64_t) &&
|
||||||
|
!std::is_same<T, std::int64_t>::value, void>::type
|
||||||
|
loadValue( T & t ){ loadLong(t); }
|
||||||
|
|
||||||
|
//! Serialize an unsigned long if it would not be caught otherwise
|
||||||
|
template <class T> inline
|
||||||
|
typename std::enable_if<std::is_same<T, unsigned long>::value &&
|
||||||
|
sizeof(T) >= sizeof(std::uint64_t) &&
|
||||||
|
!std::is_same<T, std::uint64_t>::value, void>::type
|
||||||
|
loadValue( T & t ){ loadLong(t); }
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Convert a string to a long long
|
||||||
|
void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); }
|
||||||
|
//! Convert a string to an unsigned long long
|
||||||
|
void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); }
|
||||||
|
//! Convert a string to a long double
|
||||||
|
void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Loads a value from the current node - long double and long long overloads
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value,
|
||||||
|
!std::is_same<T, long>::value,
|
||||||
|
!std::is_same<T, unsigned long>::value,
|
||||||
|
!std::is_same<T, std::int64_t>::value,
|
||||||
|
!std::is_same<T, std::uint64_t>::value,
|
||||||
|
(sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae>
|
||||||
|
inline void loadValue(T & val)
|
||||||
|
{
|
||||||
|
std::string encoded;
|
||||||
|
loadValue( encoded );
|
||||||
|
stringToNumber( encoded, val );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads the size for a SizeTag
|
||||||
|
void loadSize(size_type & size)
|
||||||
|
{
|
||||||
|
if (itsIteratorStack.size() == 1)
|
||||||
|
size = itsDocument.Size();
|
||||||
|
else
|
||||||
|
size = (itsIteratorStack.rbegin() + 1)->value().Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char * itsNextName; //!< Next name set by NVP
|
||||||
|
ReadStream itsReadStream; //!< Rapidjson write stream
|
||||||
|
std::vector<Iterator> itsIteratorStack; //!< 'Stack' of rapidJSON iterators
|
||||||
|
rapidjson::Document itsDocument; //!< Rapidjson document
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// JSONArchive prologue and epilogue functions
|
||||||
|
// ######################################################################
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for NVPs for JSON archives
|
||||||
|
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( JSONOutputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Prologue for NVPs for JSON archives
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( JSONInputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for NVPs for JSON archives
|
||||||
|
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( JSONOutputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for NVPs for JSON archives
|
||||||
|
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( JSONInputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for SizeTags for JSON archives
|
||||||
|
/*! SizeTags are strictly ignored for JSON, they just indicate
|
||||||
|
that the current node should be made into an array */
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( JSONOutputArchive & ar, SizeTag<T> const & )
|
||||||
|
{
|
||||||
|
ar.makeArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prologue for SizeTags for JSON archives
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( JSONInputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for SizeTags for JSON archives
|
||||||
|
/*! SizeTags are strictly ignored for JSON */
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( JSONOutputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for SizeTags for JSON archives
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( JSONInputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for all other types for JSON archives (except minimal types)
|
||||||
|
/*! Starts a new node, named either automatically or by some NVP,
|
||||||
|
that may be given data by the type about to be archived
|
||||||
|
|
||||||
|
Minimal types do not start or finish nodes */
|
||||||
|
template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
|
||||||
|
!traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, JSONOutputArchive>::value,
|
||||||
|
!traits::has_minimal_output_serialization<T, JSONOutputArchive>::value> = traits::sfinae>
|
||||||
|
inline void prologue( JSONOutputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.startNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prologue for all other types for JSON archives
|
||||||
|
template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
|
||||||
|
!traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, JSONInputArchive>::value,
|
||||||
|
!traits::has_minimal_input_serialization<T, JSONInputArchive>::value> = traits::sfinae>
|
||||||
|
inline void prologue( JSONInputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.startNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for all other types other for JSON archives (except minimal types)
|
||||||
|
/*! Finishes the node created in the prologue
|
||||||
|
|
||||||
|
Minimal types do not start or finish nodes */
|
||||||
|
template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
|
||||||
|
!traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, JSONOutputArchive>::value,
|
||||||
|
!traits::has_minimal_output_serialization<T, JSONOutputArchive>::value> = traits::sfinae>
|
||||||
|
inline void epilogue( JSONOutputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.finishNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Epilogue for all other types other for JSON archives
|
||||||
|
template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
|
||||||
|
!traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, JSONInputArchive>::value,
|
||||||
|
!traits::has_minimal_input_serialization<T, JSONInputArchive>::value> = traits::sfinae>
|
||||||
|
inline void epilogue( JSONInputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.finishNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for arithmetic types for JSON archives
|
||||||
|
inline
|
||||||
|
void prologue( JSONOutputArchive & ar, std::nullptr_t const & )
|
||||||
|
{
|
||||||
|
ar.writeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prologue for arithmetic types for JSON archives
|
||||||
|
inline
|
||||||
|
void prologue( JSONInputArchive &, std::nullptr_t const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for arithmetic types for JSON archives
|
||||||
|
inline
|
||||||
|
void epilogue( JSONOutputArchive &, std::nullptr_t const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for arithmetic types for JSON archives
|
||||||
|
inline
|
||||||
|
void epilogue( JSONInputArchive &, std::nullptr_t const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for arithmetic types for JSON archives
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void prologue( JSONOutputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.writeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prologue for arithmetic types for JSON archives
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void prologue( JSONInputArchive &, T const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for arithmetic types for JSON archives
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void epilogue( JSONOutputArchive &, T const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for arithmetic types for JSON archives
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void epilogue( JSONInputArchive &, T const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for strings for JSON archives
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void prologue(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const &)
|
||||||
|
{
|
||||||
|
ar.writeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prologue for strings for JSON archives
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void prologue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for strings for JSON archives
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void epilogue(JSONOutputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for strings for JSON archives
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void epilogue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// Common JSONArchive serialization functions
|
||||||
|
// ######################################################################
|
||||||
|
//! Serializing NVP types to JSON
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive & ar, NameValuePair<T> const & t )
|
||||||
|
{
|
||||||
|
ar.setNextName( t.name );
|
||||||
|
ar( t.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, NameValuePair<T> & t )
|
||||||
|
{
|
||||||
|
ar.setNextName( t.name );
|
||||||
|
ar( t.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saving for nullptr to JSON
|
||||||
|
inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::nullptr_t const & t)
|
||||||
|
{
|
||||||
|
ar.saveValue( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading arithmetic from JSON
|
||||||
|
inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::nullptr_t & t)
|
||||||
|
{
|
||||||
|
ar.loadValue( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saving for arithmetic to JSON
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, T const & t)
|
||||||
|
{
|
||||||
|
ar.saveValue( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading arithmetic from JSON
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, T & t)
|
||||||
|
{
|
||||||
|
ar.loadValue( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! saving string to JSON
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
|
||||||
|
{
|
||||||
|
ar.saveValue( str );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! loading string from JSON
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
|
||||||
|
{
|
||||||
|
ar.loadValue( str );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Saving SizeTags to JSON
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive &, SizeTag<T> const & )
|
||||||
|
{
|
||||||
|
// nothing to do here, we don't explicitly save the size
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading SizeTags from JSON
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, SizeTag<T> & st )
|
||||||
|
{
|
||||||
|
ar.loadSize( st.size );
|
||||||
|
}
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
// register archives for polymorphic support
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive)
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive)
|
||||||
|
|
||||||
|
// tie input and output archives together
|
||||||
|
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::JSONInputArchive, cereal::JSONOutputArchive)
|
||||||
|
|
||||||
|
#endif // CEREAL_ARCHIVES_JSON_HPP_
|
||||||
334
external/cereal/archives/portable_binary.hpp
vendored
Normal file
334
external/cereal/archives/portable_binary.hpp
vendored
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
/*! \file binary.hpp
|
||||||
|
\brief Binary input and output archives */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
|
||||||
|
#define CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <sstream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace portable_binary_detail
|
||||||
|
{
|
||||||
|
//! Returns true if the current machine is little endian
|
||||||
|
/*! @ingroup Internal */
|
||||||
|
inline std::uint8_t is_little_endian()
|
||||||
|
{
|
||||||
|
static std::int32_t test = 1;
|
||||||
|
return *reinterpret_cast<std::int8_t*>( &test ) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Swaps the order of bytes for some chunk of memory
|
||||||
|
/*! @param data The data as a uint8_t pointer
|
||||||
|
@tparam DataSize The true size of the data
|
||||||
|
@ingroup Internal */
|
||||||
|
template <std::size_t DataSize>
|
||||||
|
inline void swap_bytes( std::uint8_t * data )
|
||||||
|
{
|
||||||
|
for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
|
||||||
|
std::swap( data[i], data[DataSize - i - 1] );
|
||||||
|
}
|
||||||
|
} // end namespace portable_binary_detail
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! An output archive designed to save data in a compact binary representation portable over different architectures
|
||||||
|
/*! This archive outputs data to a stream in an extremely compact binary
|
||||||
|
representation with as little extra metadata as possible.
|
||||||
|
|
||||||
|
This archive will record the endianness of the data as well as the desired in/out endianness
|
||||||
|
and assuming that the user takes care of ensuring serialized types are the same size
|
||||||
|
across machines, is portable over different architectures.
|
||||||
|
|
||||||
|
When using a binary archive and a file stream, you must use the
|
||||||
|
std::ios::binary format flag to avoid having your data altered
|
||||||
|
inadvertently.
|
||||||
|
|
||||||
|
\warning This archive has not been thoroughly tested across different architectures.
|
||||||
|
Please report any issues, optimizations, or feature requests at
|
||||||
|
<a href="www.github.com/USCiLab/cereal">the project github</a>.
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class PortableBinaryOutputArchive : public OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! A class containing various advanced options for the PortableBinaryOutput archive
|
||||||
|
class Options
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Represents desired endianness
|
||||||
|
enum class Endianness : std::uint8_t
|
||||||
|
{ big, little };
|
||||||
|
|
||||||
|
//! Default options, preserve system endianness
|
||||||
|
static Options Default(){ return Options(); }
|
||||||
|
|
||||||
|
//! Save as little endian
|
||||||
|
static Options LittleEndian(){ return Options( Endianness::little ); }
|
||||||
|
|
||||||
|
//! Save as big endian
|
||||||
|
static Options BigEndian(){ return Options( Endianness::big ); }
|
||||||
|
|
||||||
|
//! Specify specific options for the PortableBinaryOutputArchive
|
||||||
|
/*! @param outputEndian The desired endianness of saved (output) data */
|
||||||
|
explicit Options( Endianness outputEndian = getEndianness() ) :
|
||||||
|
itsOutputEndianness( outputEndian ) { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Gets the endianness of the system
|
||||||
|
inline static Endianness getEndianness()
|
||||||
|
{ return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
|
||||||
|
|
||||||
|
//! Checks if Options is set for little endian
|
||||||
|
inline std::uint8_t is_little_endian() const
|
||||||
|
{ return itsOutputEndianness == Endianness::little; }
|
||||||
|
|
||||||
|
friend class PortableBinaryOutputArchive;
|
||||||
|
Endianness itsOutputEndianness;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Construct, outputting to the provided stream
|
||||||
|
/*! @param stream The stream to output to. Should be opened with std::ios::binary flag.
|
||||||
|
@param options The PortableBinary specific options to use. See the Options struct
|
||||||
|
for the values of default parameters */
|
||||||
|
PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) :
|
||||||
|
OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
|
||||||
|
itsStream(stream),
|
||||||
|
itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() )
|
||||||
|
{
|
||||||
|
this->operator()( options.is_little_endian() );
|
||||||
|
}
|
||||||
|
|
||||||
|
~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Writes size bytes of data to the output stream
|
||||||
|
template <std::size_t DataSize> inline
|
||||||
|
void saveBinary( const void * data, std::size_t size )
|
||||||
|
{
|
||||||
|
std::size_t writtenSize = 0;
|
||||||
|
|
||||||
|
if( itsConvertEndianness )
|
||||||
|
{
|
||||||
|
for( std::size_t i = 0; i < size; i += DataSize )
|
||||||
|
for( std::size_t j = 0; j < DataSize; ++j )
|
||||||
|
writtenSize += static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ) );
|
||||||
|
|
||||||
|
if(writtenSize != size)
|
||||||
|
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream & itsStream;
|
||||||
|
const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! An input archive designed to load data saved using PortableBinaryOutputArchive
|
||||||
|
/*! This archive outputs data to a stream in an extremely compact binary
|
||||||
|
representation with as little extra metadata as possible.
|
||||||
|
|
||||||
|
This archive will load the endianness of the serialized data and
|
||||||
|
if necessary transform it to match that of the local machine. This comes
|
||||||
|
at a significant performance cost compared to non portable archives if
|
||||||
|
the transformation is necessary, and also causes a small performance hit
|
||||||
|
even if it is not necessary.
|
||||||
|
|
||||||
|
It is recommended to use portable archives only if you know that you will
|
||||||
|
be sending binary data to machines with different endianness.
|
||||||
|
|
||||||
|
The archive will do nothing to ensure types are the same size - that is
|
||||||
|
the responsibility of the user.
|
||||||
|
|
||||||
|
When using a binary archive and a file stream, you must use the
|
||||||
|
std::ios::binary format flag to avoid having your data altered
|
||||||
|
inadvertently.
|
||||||
|
|
||||||
|
\warning This archive has not been thoroughly tested across different architectures.
|
||||||
|
Please report any issues, optimizations, or feature requests at
|
||||||
|
<a href="www.github.com/USCiLab/cereal">the project github</a>.
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class PortableBinaryInputArchive : public InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! A class containing various advanced options for the PortableBinaryInput archive
|
||||||
|
class Options
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Represents desired endianness
|
||||||
|
enum class Endianness : std::uint8_t
|
||||||
|
{ big, little };
|
||||||
|
|
||||||
|
//! Default options, preserve system endianness
|
||||||
|
static Options Default(){ return Options(); }
|
||||||
|
|
||||||
|
//! Load into little endian
|
||||||
|
static Options LittleEndian(){ return Options( Endianness::little ); }
|
||||||
|
|
||||||
|
//! Load into big endian
|
||||||
|
static Options BigEndian(){ return Options( Endianness::big ); }
|
||||||
|
|
||||||
|
//! Specify specific options for the PortableBinaryInputArchive
|
||||||
|
/*! @param inputEndian The desired endianness of loaded (input) data */
|
||||||
|
explicit Options( Endianness inputEndian = getEndianness() ) :
|
||||||
|
itsInputEndianness( inputEndian ) { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Gets the endianness of the system
|
||||||
|
inline static Endianness getEndianness()
|
||||||
|
{ return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
|
||||||
|
|
||||||
|
//! Checks if Options is set for little endian
|
||||||
|
inline std::uint8_t is_little_endian() const
|
||||||
|
{ return itsInputEndianness == Endianness::little; }
|
||||||
|
|
||||||
|
friend class PortableBinaryInputArchive;
|
||||||
|
Endianness itsInputEndianness;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Construct, loading from the provided stream
|
||||||
|
/*! @param stream The stream to read from. Should be opened with std::ios::binary flag.
|
||||||
|
@param options The PortableBinary specific options to use. See the Options struct
|
||||||
|
for the values of default parameters */
|
||||||
|
PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) :
|
||||||
|
InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
|
||||||
|
itsStream(stream),
|
||||||
|
itsConvertEndianness( false )
|
||||||
|
{
|
||||||
|
uint8_t streamLittleEndian;
|
||||||
|
this->operator()( streamLittleEndian );
|
||||||
|
itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
|
||||||
|
}
|
||||||
|
|
||||||
|
~PortableBinaryInputArchive() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Reads size bytes of data from the input stream
|
||||||
|
/*! @param data The data to save
|
||||||
|
@param size The number of bytes in the data
|
||||||
|
@tparam DataSize T The size of the actual type of the data elements being loaded */
|
||||||
|
template <std::size_t DataSize> inline
|
||||||
|
void loadBinary( void * const data, std::size_t size )
|
||||||
|
{
|
||||||
|
// load data
|
||||||
|
auto const readSize = static_cast<std::size_t>( itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size ) );
|
||||||
|
|
||||||
|
if(readSize != size)
|
||||||
|
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
|
||||||
|
|
||||||
|
// flip bits if needed
|
||||||
|
if( itsConvertEndianness )
|
||||||
|
{
|
||||||
|
std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
|
||||||
|
for( std::size_t i = 0; i < size; i += DataSize )
|
||||||
|
portable_binary_detail::swap_bytes<DataSize>( ptr + i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::istream & itsStream;
|
||||||
|
uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// Common BinaryArchive serialization functions
|
||||||
|
|
||||||
|
//! Saving for POD types to portable binary
|
||||||
|
template<class T> inline
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||||
|
CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t)
|
||||||
|
{
|
||||||
|
static_assert( !std::is_floating_point<T>::value ||
|
||||||
|
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
|
||||||
|
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||||
|
ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading for POD types from portable binary
|
||||||
|
template<class T> inline
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||||
|
CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t)
|
||||||
|
{
|
||||||
|
static_assert( !std::is_floating_point<T>::value ||
|
||||||
|
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
|
||||||
|
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||||
|
ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Serializing NVP types to portable binary
|
||||||
|
template <class Archive, class T> inline
|
||||||
|
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
|
||||||
|
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
|
||||||
|
{
|
||||||
|
ar( t.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Serializing SizeTags to portable binary
|
||||||
|
template <class Archive, class T> inline
|
||||||
|
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
|
||||||
|
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
|
||||||
|
{
|
||||||
|
ar( t.size );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saving binary data to portable binary
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData<T> const & bd)
|
||||||
|
{
|
||||||
|
typedef typename std::remove_pointer<T>::type TT;
|
||||||
|
static_assert( !std::is_floating_point<TT>::value ||
|
||||||
|
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
|
||||||
|
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||||
|
|
||||||
|
ar.template saveBinary<sizeof(TT)>( bd.data, static_cast<std::size_t>( bd.size ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading binary data from portable binary
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData<T> & bd)
|
||||||
|
{
|
||||||
|
typedef typename std::remove_pointer<T>::type TT;
|
||||||
|
static_assert( !std::is_floating_point<TT>::value ||
|
||||||
|
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
|
||||||
|
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||||
|
|
||||||
|
ar.template loadBinary<sizeof(TT)>( bd.data, static_cast<std::size_t>( bd.size ) );
|
||||||
|
}
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
// register archives for polymorphic support
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryOutputArchive)
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryInputArchive)
|
||||||
|
|
||||||
|
// tie input and output archives together
|
||||||
|
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive)
|
||||||
|
|
||||||
|
#endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
|
||||||
897
external/cereal/archives/xml.hpp
vendored
Normal file
897
external/cereal/archives/xml.hpp
vendored
Normal file
@@ -0,0 +1,897 @@
|
|||||||
|
/*! \file xml.hpp
|
||||||
|
\brief XML input and output archives */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_ARCHIVES_XML_HPP_
|
||||||
|
#define CEREAL_ARCHIVES_XML_HPP_
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <cereal/details/util.hpp>
|
||||||
|
|
||||||
|
#include <cereal/external/rapidxml/rapidxml.hpp>
|
||||||
|
#include <cereal/external/rapidxml/rapidxml_print.hpp>
|
||||||
|
#include <cereal/external/base64.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace xml_detail
|
||||||
|
{
|
||||||
|
#ifndef CEREAL_XML_STRING_VALUE
|
||||||
|
//! The default name for the root node in a cereal xml archive.
|
||||||
|
/*! You can define CEREAL_XML_STRING_VALUE to be different assuming you do so
|
||||||
|
before this file is included. */
|
||||||
|
#define CEREAL_XML_STRING_VALUE "cereal"
|
||||||
|
#endif // CEREAL_XML_STRING_VALUE
|
||||||
|
|
||||||
|
//! The name given to the root node in a cereal xml archive
|
||||||
|
static const char * CEREAL_XML_STRING = CEREAL_XML_STRING_VALUE;
|
||||||
|
|
||||||
|
//! Returns true if the character is whitespace
|
||||||
|
inline bool isWhitespace( char c )
|
||||||
|
{
|
||||||
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! An output archive designed to save data to XML
|
||||||
|
/*! This archive uses RapidXML to build an in memory XML tree of the
|
||||||
|
data it serializes before outputting it to its stream upon destruction.
|
||||||
|
This archive should be used in an RAII fashion, letting
|
||||||
|
the automatic destruction of the object cause the flush to its stream.
|
||||||
|
|
||||||
|
XML archives provides a human readable output but at decreased
|
||||||
|
performance (both in time and space) compared to binary archives.
|
||||||
|
|
||||||
|
XML benefits greatly from name-value pairs, which if present, will
|
||||||
|
name the nodes in the output. If these are not present, each level
|
||||||
|
of the output tree will be given an automatically generated delimited name.
|
||||||
|
|
||||||
|
The precision of the output archive controls the number of decimals output
|
||||||
|
for floating point numbers and should be sufficiently large (i.e. at least 20)
|
||||||
|
if there is a desire to have binary equality between the numbers output and
|
||||||
|
those read in. In general you should expect a loss of precision when going
|
||||||
|
from floating point to text and back.
|
||||||
|
|
||||||
|
XML archives can optionally print the type of everything they serialize, which
|
||||||
|
adds an attribute to each node.
|
||||||
|
|
||||||
|
XML archives do not output the size information for any dynamically sized structure
|
||||||
|
and instead infer it from the number of children for a node. This means that data
|
||||||
|
can be hand edited for dynamic sized structures and will still be readable. This
|
||||||
|
is accomplished through the cereal::SizeTag object, which will also add an attribute
|
||||||
|
to its parent field.
|
||||||
|
\ingroup Archives */
|
||||||
|
class XMLOutputArchive : public OutputArchive<XMLOutputArchive>, public traits::TextArchive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! @name Common Functionality
|
||||||
|
Common use cases for directly interacting with an XMLOutputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! A class containing various advanced options for the XML archive
|
||||||
|
class Options
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Default options
|
||||||
|
static Options Default(){ return Options(); }
|
||||||
|
|
||||||
|
//! Default options with no indentation
|
||||||
|
static Options NoIndent(){ return Options( std::numeric_limits<double>::max_digits10, false ); }
|
||||||
|
|
||||||
|
//! Specify specific options for the XMLOutputArchive
|
||||||
|
/*! @param precision The precision used for floating point numbers
|
||||||
|
@param indent Whether to indent each line of XML
|
||||||
|
@param outputType Whether to output the type of each serialized object as an attribute */
|
||||||
|
explicit Options( int precision = std::numeric_limits<double>::max_digits10,
|
||||||
|
bool indent = true,
|
||||||
|
bool outputType = false ) :
|
||||||
|
itsPrecision( precision ),
|
||||||
|
itsIndent( indent ),
|
||||||
|
itsOutputType( outputType ) { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class XMLOutputArchive;
|
||||||
|
int itsPrecision;
|
||||||
|
bool itsIndent;
|
||||||
|
bool itsOutputType;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Construct, outputting to the provided stream upon destruction
|
||||||
|
/*! @param stream The stream to output to. Note that XML is only guaranteed to flush
|
||||||
|
its output to the stream upon destruction.
|
||||||
|
@param options The XML specific options to use. See the Options struct
|
||||||
|
for the values of default parameters */
|
||||||
|
XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) :
|
||||||
|
OutputArchive<XMLOutputArchive>(this),
|
||||||
|
itsStream(stream),
|
||||||
|
itsOutputType( options.itsOutputType ),
|
||||||
|
itsIndent( options.itsIndent )
|
||||||
|
{
|
||||||
|
// rapidxml will delete all allocations when xml_document is cleared
|
||||||
|
auto node = itsXML.allocate_node( rapidxml::node_declaration );
|
||||||
|
node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
|
||||||
|
node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
|
||||||
|
itsXML.append_node( node );
|
||||||
|
|
||||||
|
// allocate root node
|
||||||
|
auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
|
||||||
|
itsXML.append_node( root );
|
||||||
|
itsNodes.emplace( root );
|
||||||
|
|
||||||
|
// set attributes on the streams
|
||||||
|
itsStream << std::boolalpha;
|
||||||
|
itsStream.precision( options.itsPrecision );
|
||||||
|
itsOS << std::boolalpha;
|
||||||
|
itsOS.precision( options.itsPrecision );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructor, flushes the XML
|
||||||
|
~XMLOutputArchive() CEREAL_NOEXCEPT
|
||||||
|
{
|
||||||
|
const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
|
||||||
|
rapidxml::print( itsStream, itsXML, flags );
|
||||||
|
itsXML.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saves some binary data, encoded as a base64 string, with an optional name
|
||||||
|
/*! This can be called directly by users and it will automatically create a child node for
|
||||||
|
the current XML node, populate it with a base64 encoded string, and optionally name
|
||||||
|
it. The node will be finished after it has been populated. */
|
||||||
|
void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
|
||||||
|
{
|
||||||
|
itsNodes.top().name = name;
|
||||||
|
|
||||||
|
startNode();
|
||||||
|
|
||||||
|
auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
|
||||||
|
saveValue( base64string );
|
||||||
|
|
||||||
|
if( itsOutputType )
|
||||||
|
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) );
|
||||||
|
|
||||||
|
finishNode();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
/*! @name Internal Functionality
|
||||||
|
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||||
|
the XMLOutputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Creates a new node that is a child of the node at the top of the stack
|
||||||
|
/*! Nodes will be given a name that has either been pre-set by a name value pair,
|
||||||
|
or generated based upon a counter unique to the parent node. If you want to
|
||||||
|
give a node a specific name, use setNextName prior to calling startNode.
|
||||||
|
|
||||||
|
The node will then be pushed onto the node stack. */
|
||||||
|
void startNode()
|
||||||
|
{
|
||||||
|
// generate a name for this new node
|
||||||
|
const auto nameString = itsNodes.top().getValueName();
|
||||||
|
|
||||||
|
// allocate strings for all of the data in the XML object
|
||||||
|
auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
|
||||||
|
|
||||||
|
// insert into the XML
|
||||||
|
auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
|
||||||
|
itsNodes.top().node->append_node( node );
|
||||||
|
itsNodes.emplace( node );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Designates the most recently added node as finished
|
||||||
|
void finishNode()
|
||||||
|
{
|
||||||
|
itsNodes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the name for the next node created with startNode
|
||||||
|
void setNextName( const char * name )
|
||||||
|
{
|
||||||
|
itsNodes.top().name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Saves some data, encoded as a string, into the current top level node
|
||||||
|
/*! The data will be be named with the most recent name if one exists,
|
||||||
|
otherwise it will be given some default delimited value that depends upon
|
||||||
|
the parent node */
|
||||||
|
template <class T> inline
|
||||||
|
void saveValue( T const & value )
|
||||||
|
{
|
||||||
|
itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
|
||||||
|
itsOS << value << std::ends;
|
||||||
|
|
||||||
|
auto strValue = itsOS.str();
|
||||||
|
|
||||||
|
// itsOS.str() may contain data from previous calls after the first '\0' that was just inserted
|
||||||
|
// and this data is counted in the length call. We make sure to remove that section so that the
|
||||||
|
// whitespace validation is done properly
|
||||||
|
strValue.resize(std::strlen(strValue.c_str()));
|
||||||
|
|
||||||
|
// If the first or last character is a whitespace, add xml:space attribute
|
||||||
|
const auto len = strValue.length();
|
||||||
|
if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
|
||||||
|
{
|
||||||
|
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate strings for all of the data in the XML object
|
||||||
|
auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
|
||||||
|
|
||||||
|
// insert into the XML
|
||||||
|
itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Overload for uint8_t prevents them from being serialized as characters
|
||||||
|
void saveValue( uint8_t const & value )
|
||||||
|
{
|
||||||
|
saveValue( static_cast<uint32_t>( value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Overload for int8_t prevents them from being serialized as characters
|
||||||
|
void saveValue( int8_t const & value )
|
||||||
|
{
|
||||||
|
saveValue( static_cast<int32_t>( value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Causes the type to be appended as an attribute to the most recently made node if output type is set to true
|
||||||
|
template <class T> inline
|
||||||
|
void insertType()
|
||||||
|
{
|
||||||
|
if( !itsOutputType )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// generate a name for this new node
|
||||||
|
const auto nameString = util::demangledName<T>();
|
||||||
|
|
||||||
|
// allocate strings for all of the data in the XML object
|
||||||
|
auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
|
||||||
|
|
||||||
|
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Appends an attribute to the current top level node
|
||||||
|
void appendAttribute( const char * name, const char * value )
|
||||||
|
{
|
||||||
|
auto namePtr = itsXML.allocate_string( name );
|
||||||
|
auto valuePtr = itsXML.allocate_string( value );
|
||||||
|
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! A struct that contains metadata about a node
|
||||||
|
struct NodeInfo
|
||||||
|
{
|
||||||
|
NodeInfo( rapidxml::xml_node<> * n = nullptr,
|
||||||
|
const char * nm = nullptr ) :
|
||||||
|
node( n ),
|
||||||
|
counter( 0 ),
|
||||||
|
name( nm )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
rapidxml::xml_node<> * node; //!< A pointer to this node
|
||||||
|
size_t counter; //!< The counter for naming child nodes
|
||||||
|
const char * name; //!< The name for the next child node
|
||||||
|
|
||||||
|
//! Gets the name for the next child node created from this node
|
||||||
|
/*! The name will be automatically generated using the counter if
|
||||||
|
a name has not been previously set. If a name has been previously
|
||||||
|
set, that name will be returned only once */
|
||||||
|
std::string getValueName()
|
||||||
|
{
|
||||||
|
if( name )
|
||||||
|
{
|
||||||
|
auto n = name;
|
||||||
|
name = nullptr;
|
||||||
|
return {n};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return "value" + std::to_string( counter++ ) + "\0";
|
||||||
|
}
|
||||||
|
}; // NodeInfo
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream & itsStream; //!< The output stream
|
||||||
|
rapidxml::xml_document<> itsXML; //!< The XML document
|
||||||
|
std::stack<NodeInfo> itsNodes; //!< A stack of nodes added to the document
|
||||||
|
std::ostringstream itsOS; //!< Used to format strings internally
|
||||||
|
bool itsOutputType; //!< Controls whether type information is printed
|
||||||
|
bool itsIndent; //!< Controls whether indenting is used
|
||||||
|
}; // XMLOutputArchive
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! An output archive designed to load data from XML
|
||||||
|
/*! This archive uses RapidXML to build an in memory XML tree of the
|
||||||
|
data in the stream it is given before loading any types serialized.
|
||||||
|
|
||||||
|
As with the output XML archive, the preferred way to use this archive is in
|
||||||
|
an RAII fashion, ensuring its destruction after all data has been read.
|
||||||
|
|
||||||
|
Input XML should have been produced by the XMLOutputArchive. Data can
|
||||||
|
only be added to dynamically sized containers - the input archive will
|
||||||
|
determine their size by looking at the number of child nodes. Data that
|
||||||
|
did not originate from an XMLOutputArchive is not officially supported,
|
||||||
|
but may be possible to use if properly formatted.
|
||||||
|
|
||||||
|
The XMLInputArchive does not require that nodes are loaded in the same
|
||||||
|
order they were saved by XMLOutputArchive. Using name value pairs (NVPs),
|
||||||
|
it is possible to load in an out of order fashion or otherwise skip/select
|
||||||
|
specific nodes to load.
|
||||||
|
|
||||||
|
The default behavior of the input archive is to read sequentially starting
|
||||||
|
with the first node and exploring its children. When a given NVP does
|
||||||
|
not match the read in name for a node, the archive will search for that
|
||||||
|
node at the current level and load it if it exists. After loading an out of
|
||||||
|
order node, the archive will then proceed back to loading sequentially from
|
||||||
|
its new position.
|
||||||
|
|
||||||
|
Consider this simple example where loading of some data is skipped:
|
||||||
|
|
||||||
|
@code{cpp}
|
||||||
|
// imagine the input file has someData(1-9) saved in order at the top level node
|
||||||
|
ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file
|
||||||
|
ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
|
||||||
|
// match expected NVP name, so we search
|
||||||
|
// for the given NVP and load that value
|
||||||
|
ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its
|
||||||
|
// current location, proceeding sequentially
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
\ingroup Archives */
|
||||||
|
class XMLInputArchive : public InputArchive<XMLInputArchive>, public traits::TextArchive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! @name Common Functionality
|
||||||
|
Common use cases for directly interacting with an XMLInputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Construct, reading in from the provided stream
|
||||||
|
/*! Reads in an entire XML document from some stream and parses it as soon
|
||||||
|
as serialization starts
|
||||||
|
|
||||||
|
@param stream The stream to read from. Can be a stringstream or a file. */
|
||||||
|
XMLInputArchive( std::istream & stream ) :
|
||||||
|
InputArchive<XMLInputArchive>( this ),
|
||||||
|
itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
itsData.push_back('\0'); // rapidxml will do terrible things without the data being null terminated
|
||||||
|
itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
|
||||||
|
}
|
||||||
|
catch( rapidxml::parse_error const & )
|
||||||
|
{
|
||||||
|
//std::cerr << "-----Original-----" << std::endl;
|
||||||
|
//stream.seekg(0);
|
||||||
|
//std::cout << std::string( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() ) << std::endl;
|
||||||
|
|
||||||
|
//std::cerr << "-----Error-----" << std::endl;
|
||||||
|
//std::cerr << e.what() << std::endl;
|
||||||
|
//std::cerr << e.where<char>() << std::endl;
|
||||||
|
throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the root
|
||||||
|
auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
|
||||||
|
if( root == nullptr )
|
||||||
|
throw Exception("Could not detect cereal root node - likely due to empty or invalid input");
|
||||||
|
else
|
||||||
|
itsNodes.emplace( root );
|
||||||
|
}
|
||||||
|
|
||||||
|
~XMLInputArchive() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Loads some binary data, encoded as a base64 string, optionally specified by some name
|
||||||
|
/*! This will automatically start and finish a node to load the data, and can be called directly by
|
||||||
|
users.
|
||||||
|
|
||||||
|
Note that this follows the same ordering rules specified in the class description in regards
|
||||||
|
to loading in/out of order */
|
||||||
|
void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
|
||||||
|
{
|
||||||
|
setNextName( name );
|
||||||
|
startNode();
|
||||||
|
|
||||||
|
std::string encoded;
|
||||||
|
loadValue( encoded );
|
||||||
|
|
||||||
|
auto decoded = base64::decode( encoded );
|
||||||
|
|
||||||
|
if( size != decoded.size() )
|
||||||
|
throw Exception("Decoded binary data size does not match specified size");
|
||||||
|
|
||||||
|
std::memcpy( data, decoded.data(), decoded.size() );
|
||||||
|
|
||||||
|
finishNode();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
/*! @name Internal Functionality
|
||||||
|
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||||
|
the XMLInputArchive */
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Prepares to start reading the next node
|
||||||
|
/*! This places the next node to be parsed onto the nodes stack.
|
||||||
|
|
||||||
|
By default our strategy is to start with the document root node and then
|
||||||
|
recursively iterate through all children in the order they show up in the document.
|
||||||
|
We don't need to know NVPs do to this; we'll just blindly load in the order things appear in.
|
||||||
|
|
||||||
|
We check to see if the specified NVP matches what the next automatically loaded node is. If they
|
||||||
|
match, we just continue as normal, going in order. If they don't match, we attempt to find a node
|
||||||
|
named after the NVP that is being loaded. If that NVP does not exist, we throw an exception. */
|
||||||
|
void startNode()
|
||||||
|
{
|
||||||
|
auto next = itsNodes.top().child; // By default we would move to the next child node
|
||||||
|
auto const expectedName = itsNodes.top().name; // this is the expected name from the NVP, if provided
|
||||||
|
|
||||||
|
// If we were given an NVP name, look for it in the current level of the document.
|
||||||
|
// We only need to do this if either we have exhausted the siblings of the current level or
|
||||||
|
// the NVP name does not match the name of the node we would normally read next
|
||||||
|
if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
|
||||||
|
{
|
||||||
|
next = itsNodes.top().search( expectedName );
|
||||||
|
|
||||||
|
if( next == nullptr )
|
||||||
|
throw Exception("XML Parsing failed - provided NVP (" + std::string(expectedName) + ") not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
itsNodes.emplace( next );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Finishes reading the current node
|
||||||
|
void finishNode()
|
||||||
|
{
|
||||||
|
// remove current
|
||||||
|
itsNodes.pop();
|
||||||
|
|
||||||
|
// advance parent
|
||||||
|
itsNodes.top().advance();
|
||||||
|
|
||||||
|
// Reset name
|
||||||
|
itsNodes.top().name = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Retrieves the current node name
|
||||||
|
//! will return @c nullptr if the node does not have a name
|
||||||
|
const char * getNodeName() const
|
||||||
|
{
|
||||||
|
return itsNodes.top().getChildName();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the name for the next node created with startNode
|
||||||
|
void setNextName( const char * name )
|
||||||
|
{
|
||||||
|
itsNodes.top().name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a bool from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||||
|
std::is_same<T, bool>::value> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
std::istringstream is( itsNodes.top().node->value() );
|
||||||
|
is.setf( std::ios::boolalpha );
|
||||||
|
is >> value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a char (signed or unsigned) from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_integral<T>::value,
|
||||||
|
!std::is_same<T, bool>::value,
|
||||||
|
sizeof(T) == sizeof(char)> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
value = *reinterpret_cast<T*>( itsNodes.top().node->value() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Load an int8_t from the current top node (ensures we parse entire number)
|
||||||
|
void loadValue( int8_t & value )
|
||||||
|
{
|
||||||
|
int32_t val; loadValue( val ); value = static_cast<int8_t>( val );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Load a uint8_t from the current top node (ensures we parse entire number)
|
||||||
|
void loadValue( uint8_t & value )
|
||||||
|
{
|
||||||
|
uint32_t val; loadValue( val ); value = static_cast<uint8_t>( val );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as an unsigned long from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||||
|
!std::is_same<T, bool>::value,
|
||||||
|
!std::is_same<T, char>::value,
|
||||||
|
!std::is_same<T, unsigned char>::value,
|
||||||
|
sizeof(T) < sizeof(long long)> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
value = static_cast<T>( std::stoul( itsNodes.top().node->value() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as an unsigned long long from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||||
|
!std::is_same<T, bool>::value,
|
||||||
|
sizeof(T) >= sizeof(long long)> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
value = static_cast<T>( std::stoull( itsNodes.top().node->value() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as an int from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||||
|
!std::is_same<T, char>::value,
|
||||||
|
sizeof(T) <= sizeof(int)> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
value = static_cast<T>( std::stoi( itsNodes.top().node->value() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as a long from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||||
|
(sizeof(T) > sizeof(int)),
|
||||||
|
sizeof(T) <= sizeof(long)> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
value = static_cast<T>( std::stol( itsNodes.top().node->value() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as a long long from the current top node
|
||||||
|
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||||
|
(sizeof(T) > sizeof(long)),
|
||||||
|
sizeof(T) <= sizeof(long long)> = traits::sfinae> inline
|
||||||
|
void loadValue( T & value )
|
||||||
|
{
|
||||||
|
value = static_cast<T>( std::stoll( itsNodes.top().node->value() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as a float from the current top node
|
||||||
|
void loadValue( float & value )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = std::stof( itsNodes.top().node->value() );
|
||||||
|
}
|
||||||
|
catch( std::out_of_range const & )
|
||||||
|
{
|
||||||
|
// special case for denormalized values
|
||||||
|
std::istringstream is( itsNodes.top().node->value() );
|
||||||
|
is >> value;
|
||||||
|
if( std::fpclassify( value ) != FP_SUBNORMAL )
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as a double from the current top node
|
||||||
|
void loadValue( double & value )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = std::stod( itsNodes.top().node->value() );
|
||||||
|
}
|
||||||
|
catch( std::out_of_range const & )
|
||||||
|
{
|
||||||
|
// special case for denormalized values
|
||||||
|
std::istringstream is( itsNodes.top().node->value() );
|
||||||
|
is >> value;
|
||||||
|
if( std::fpclassify( value ) != FP_SUBNORMAL )
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a type best represented as a long double from the current top node
|
||||||
|
void loadValue( long double & value )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = std::stold( itsNodes.top().node->value() );
|
||||||
|
}
|
||||||
|
catch( std::out_of_range const & )
|
||||||
|
{
|
||||||
|
// special case for denormalized values
|
||||||
|
std::istringstream is( itsNodes.top().node->value() );
|
||||||
|
is >> value;
|
||||||
|
if( std::fpclassify( value ) != FP_SUBNORMAL )
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads a string from the current node from the current top node
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
|
||||||
|
{
|
||||||
|
std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
|
||||||
|
|
||||||
|
str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
|
||||||
|
std::istreambuf_iterator<CharT, Traits>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads the size of the current top node
|
||||||
|
template <class T> inline
|
||||||
|
void loadSize( T & value )
|
||||||
|
{
|
||||||
|
value = getNumChildren( itsNodes.top().node );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! Gets the number of children (usually interpreted as size) for the specified node
|
||||||
|
static size_t getNumChildren( rapidxml::xml_node<> * node )
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
node = node->first_node(); // get first child
|
||||||
|
|
||||||
|
while( node != nullptr )
|
||||||
|
{
|
||||||
|
++size;
|
||||||
|
node = node->next_sibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! A struct that contains metadata about a node
|
||||||
|
/*! Keeps track of some top level node, its number of
|
||||||
|
remaining children, and the current active child node */
|
||||||
|
struct NodeInfo
|
||||||
|
{
|
||||||
|
NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
|
||||||
|
node( n ),
|
||||||
|
child( n->first_node() ),
|
||||||
|
size( XMLInputArchive::getNumChildren( n ) ),
|
||||||
|
name( nullptr )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Advances to the next sibling node of the child
|
||||||
|
/*! If this is the last sibling child will be null after calling */
|
||||||
|
void advance()
|
||||||
|
{
|
||||||
|
if( size > 0 )
|
||||||
|
{
|
||||||
|
--size;
|
||||||
|
child = child->next_sibling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Searches for a child with the given name in this node
|
||||||
|
/*! @param searchName The name to search for (must be null terminated)
|
||||||
|
@return The node if found, nullptr otherwise */
|
||||||
|
rapidxml::xml_node<> * search( const char * searchName )
|
||||||
|
{
|
||||||
|
if( searchName )
|
||||||
|
{
|
||||||
|
size_t new_size = XMLInputArchive::getNumChildren( node );
|
||||||
|
const size_t name_size = rapidxml::internal::measure( searchName );
|
||||||
|
|
||||||
|
for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() )
|
||||||
|
{
|
||||||
|
if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
|
||||||
|
{
|
||||||
|
size = new_size;
|
||||||
|
child = new_child;
|
||||||
|
|
||||||
|
return new_child;
|
||||||
|
}
|
||||||
|
--new_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns the actual name of the next child node, if it exists
|
||||||
|
const char * getChildName() const
|
||||||
|
{
|
||||||
|
return child ? child->name() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
rapidxml::xml_node<> * node; //!< A pointer to this node
|
||||||
|
rapidxml::xml_node<> * child; //!< A pointer to its current child
|
||||||
|
size_t size; //!< The remaining number of children for this node
|
||||||
|
const char * name; //!< The NVP name for next child node
|
||||||
|
}; // NodeInfo
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<char> itsData; //!< The raw data loaded
|
||||||
|
rapidxml::xml_document<> itsXML; //!< The XML document
|
||||||
|
std::stack<NodeInfo> itsNodes; //!< A stack of nodes read from the document
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// XMLArchive prologue and epilogue functions
|
||||||
|
// ######################################################################
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for NVPs for XML output archives
|
||||||
|
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( XMLOutputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Prologue for NVPs for XML input archives
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( XMLInputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for NVPs for XML output archives
|
||||||
|
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for NVPs for XML input archives
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( XMLInputArchive &, NameValuePair<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for SizeTags for XML output archives
|
||||||
|
/*! SizeTags do not start or finish nodes */
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( XMLOutputArchive & ar, SizeTag<T> const & )
|
||||||
|
{
|
||||||
|
ar.appendAttribute( "size", "dynamic" );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> inline
|
||||||
|
void prologue( XMLInputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Epilogue for SizeTags for XML output archives
|
||||||
|
/*! SizeTags do not start or finish nodes */
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( XMLOutputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template <class T> inline
|
||||||
|
void epilogue( XMLInputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Prologue for all other types for XML output archives (except minimal types)
|
||||||
|
/*! Starts a new node, named either automatically or by some NVP,
|
||||||
|
that may be given data by the type about to be archived
|
||||||
|
|
||||||
|
Minimal types do not start or end nodes */
|
||||||
|
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
|
||||||
|
traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
|
||||||
|
void prologue( XMLOutputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.startNode();
|
||||||
|
ar.insertType<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prologue for all other types for XML input archives (except minimal types)
|
||||||
|
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
|
||||||
|
traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
|
||||||
|
void prologue( XMLInputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.startNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Epilogue for all other types other for XML output archives (except minimal types)
|
||||||
|
/*! Finishes the node created in the prologue
|
||||||
|
|
||||||
|
Minimal types do not start or end nodes */
|
||||||
|
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
|
||||||
|
traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
|
||||||
|
void epilogue( XMLOutputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.finishNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Epilogue for all other types other for XML output archives (except minimal types)
|
||||||
|
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
|
||||||
|
traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
|
||||||
|
void epilogue( XMLInputArchive & ar, T const & )
|
||||||
|
{
|
||||||
|
ar.finishNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
// Common XMLArchive serialization functions
|
||||||
|
// ######################################################################
|
||||||
|
|
||||||
|
//! Saving NVP types to XML
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair<T> const & t )
|
||||||
|
{
|
||||||
|
ar.setNextName( t.name );
|
||||||
|
ar( t.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading NVP types from XML
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair<T> & t )
|
||||||
|
{
|
||||||
|
ar.setNextName( t.name );
|
||||||
|
ar( t.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Saving SizeTags to XML
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag<T> const & )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Loading SizeTags from XML
|
||||||
|
template <class T> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag<T> & st )
|
||||||
|
{
|
||||||
|
ar.loadSize( st.size );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Saving for POD types to xml
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t)
|
||||||
|
{
|
||||||
|
ar.saveValue( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loading for POD types from xml
|
||||||
|
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t)
|
||||||
|
{
|
||||||
|
ar.loadValue( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! saving string to xml
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
|
||||||
|
{
|
||||||
|
ar.saveValue( str );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! loading string from xml
|
||||||
|
template<class CharT, class Traits, class Alloc> inline
|
||||||
|
void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
|
||||||
|
{
|
||||||
|
ar.loadValue( str );
|
||||||
|
}
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
// register archives for polymorphic support
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
|
||||||
|
CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
|
||||||
|
|
||||||
|
// tie input and output archives together
|
||||||
|
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
|
||||||
|
|
||||||
|
#endif // CEREAL_ARCHIVES_XML_HPP_
|
||||||
1063
external/cereal/cereal.hpp
vendored
Normal file
1063
external/cereal/cereal.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
381
external/cereal/details/helpers.hpp
vendored
Normal file
381
external/cereal/details/helpers.hpp
vendored
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
/*! \file helpers.hpp
|
||||||
|
\brief Internal helper functionality
|
||||||
|
\ingroup Internal */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_DETAILS_HELPERS_HPP_
|
||||||
|
#define CEREAL_DETAILS_HELPERS_HPP_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <cereal/macros.hpp>
|
||||||
|
#include <cereal/details/static_object.hpp>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
// ######################################################################
|
||||||
|
//! An exception class thrown when things go wrong at runtime
|
||||||
|
/*! @ingroup Utility */
|
||||||
|
struct Exception : public std::runtime_error
|
||||||
|
{
|
||||||
|
explicit Exception( const std::string & what_ ) : std::runtime_error(what_) {}
|
||||||
|
explicit Exception( const char * what_ ) : std::runtime_error(what_) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! The size type used by cereal
|
||||||
|
/*! To ensure compatability between 32, 64, etc bit machines, we need to use
|
||||||
|
a fixed size type instead of size_t, which may vary from machine to
|
||||||
|
machine. */
|
||||||
|
using size_type = uint64_t;
|
||||||
|
|
||||||
|
// forward decls
|
||||||
|
class BinaryOutputArchive;
|
||||||
|
class BinaryInputArchive;
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
struct NameValuePairCore {}; //!< Traits struct for NVPs
|
||||||
|
}
|
||||||
|
|
||||||
|
//! For holding name value pairs
|
||||||
|
/*! This pairs a name (some string) with some value such that an archive
|
||||||
|
can potentially take advantage of the pairing.
|
||||||
|
|
||||||
|
In serialization functions, NameValuePairs are usually created like so:
|
||||||
|
@code{.cpp}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int a, b, c, d, e;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & archive)
|
||||||
|
{
|
||||||
|
archive( CEREAL_NVP(a),
|
||||||
|
CEREAL_NVP(b),
|
||||||
|
CEREAL_NVP(c),
|
||||||
|
CEREAL_NVP(d),
|
||||||
|
CEREAL_NVP(e) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Alternatively, you can give you data members custom names like so:
|
||||||
|
@code{.cpp}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int a, b, my_embarrassing_variable_name, d, e;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & archive)
|
||||||
|
{
|
||||||
|
archive( CEREAL_NVP(a),
|
||||||
|
CEREAL_NVP(b),
|
||||||
|
cereal::make_nvp("var", my_embarrassing_variable_name) );
|
||||||
|
CEREAL_NVP(d),
|
||||||
|
CEREAL_NVP(e) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
There is a slight amount of overhead to creating NameValuePairs, so there
|
||||||
|
is a third method which will elide the names when they are not used by
|
||||||
|
the Archive:
|
||||||
|
|
||||||
|
@code{.cpp}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int a, b;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & archive)
|
||||||
|
{
|
||||||
|
archive( cereal::make_nvp<Archive>(a),
|
||||||
|
cereal::make_nvp<Archive>(b) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
This third method is generally only used when providing generic type
|
||||||
|
support. Users writing their own serialize functions will normally
|
||||||
|
explicitly control whether they want to use NVPs or not.
|
||||||
|
|
||||||
|
@internal */
|
||||||
|
template <class T>
|
||||||
|
class NameValuePair : detail::NameValuePairCore
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// If we get passed an array, keep the type as is, otherwise store
|
||||||
|
// a reference if we were passed an l value reference, else copy the value
|
||||||
|
using Type = typename std::conditional<std::is_array<typename std::remove_reference<T>::type>::value,
|
||||||
|
typename std::remove_cv<T>::type,
|
||||||
|
typename std::conditional<std::is_lvalue_reference<T>::value,
|
||||||
|
T,
|
||||||
|
typename std::decay<T>::type>::type>::type;
|
||||||
|
|
||||||
|
// prevent nested nvps
|
||||||
|
static_assert( !std::is_base_of<detail::NameValuePairCore, T>::value,
|
||||||
|
"Cannot pair a name to a NameValuePair" );
|
||||||
|
|
||||||
|
NameValuePair & operator=( NameValuePair const & ) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Constructs a new NameValuePair
|
||||||
|
/*! @param n The name of the pair
|
||||||
|
@param v The value to pair. Ideally this should be an l-value reference so that
|
||||||
|
the value can be both loaded and saved to. If you pass an r-value reference,
|
||||||
|
the NameValuePair will store a copy of it instead of a reference. Thus you should
|
||||||
|
only pass r-values in cases where this makes sense, such as the result of some
|
||||||
|
size() call.
|
||||||
|
@internal */
|
||||||
|
NameValuePair( char const * n, T && v ) : name(n), value(std::forward<T>(v)) {}
|
||||||
|
|
||||||
|
char const * name;
|
||||||
|
Type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A specialization of make_nvp<> that simply forwards the value for binary archives
|
||||||
|
/*! @relates NameValuePair
|
||||||
|
@internal */
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
typename
|
||||||
|
std::enable_if<std::is_same<Archive, ::cereal::BinaryInputArchive>::value ||
|
||||||
|
std::is_same<Archive, ::cereal::BinaryOutputArchive>::value,
|
||||||
|
T && >::type
|
||||||
|
make_nvp( const char *, T && value )
|
||||||
|
{
|
||||||
|
return std::forward<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! A specialization of make_nvp<> that actually creates an nvp for non-binary archives
|
||||||
|
/*! @relates NameValuePair
|
||||||
|
@internal */
|
||||||
|
template<class Archive, class T> inline
|
||||||
|
typename
|
||||||
|
std::enable_if<!std::is_same<Archive, ::cereal::BinaryInputArchive>::value &&
|
||||||
|
!std::is_same<Archive, ::cereal::BinaryOutputArchive>::value,
|
||||||
|
NameValuePair<T> >::type
|
||||||
|
make_nvp( const char * name, T && value)
|
||||||
|
{
|
||||||
|
return {name, std::forward<T>(value)};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Convenience for creating a templated NVP
|
||||||
|
/*! For use in internal generic typing functions which have an
|
||||||
|
Archive type declared
|
||||||
|
@internal */
|
||||||
|
#define CEREAL_NVP_(name, value) ::cereal::make_nvp<Archive>(name, value)
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! A wrapper around data that can be serialized in a binary fashion
|
||||||
|
/*! This class is used to demarcate data that can safely be serialized
|
||||||
|
as a binary chunk of data. Individual archives can then choose how
|
||||||
|
best represent this during serialization.
|
||||||
|
|
||||||
|
@internal */
|
||||||
|
template <class T>
|
||||||
|
struct BinaryData
|
||||||
|
{
|
||||||
|
//! Internally store the pointer as a void *, keeping const if created with
|
||||||
|
//! a const pointer
|
||||||
|
using PT = typename std::conditional<std::is_const<typename std::remove_pointer<T>::type>::value,
|
||||||
|
const void *,
|
||||||
|
void *>::type;
|
||||||
|
|
||||||
|
BinaryData( T && d, uint64_t s ) : data(std::forward<T>(d)), size(s) {}
|
||||||
|
|
||||||
|
PT data; //!< pointer to beginning of data
|
||||||
|
uint64_t size; //!< size in bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// base classes for type checking
|
||||||
|
/* The rtti virtual function only exists to enable an archive to
|
||||||
|
be used in a polymorphic fashion, if necessary. See the
|
||||||
|
archive adapters for an example of this */
|
||||||
|
class OutputArchiveBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OutputArchiveBase() = default;
|
||||||
|
OutputArchiveBase( OutputArchiveBase && ) CEREAL_NOEXCEPT {}
|
||||||
|
OutputArchiveBase & operator=( OutputArchiveBase && ) CEREAL_NOEXCEPT { return *this; }
|
||||||
|
virtual ~OutputArchiveBase() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void rtti() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputArchiveBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputArchiveBase() = default;
|
||||||
|
InputArchiveBase( InputArchiveBase && ) CEREAL_NOEXCEPT {}
|
||||||
|
InputArchiveBase & operator=( InputArchiveBase && ) CEREAL_NOEXCEPT { return *this; }
|
||||||
|
virtual ~InputArchiveBase() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void rtti() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// forward decls for polymorphic support
|
||||||
|
template <class Archive, class T> struct polymorphic_serialization_support;
|
||||||
|
struct adl_tag;
|
||||||
|
|
||||||
|
// used during saving pointers
|
||||||
|
static const int32_t msb_32bit = 0x80000000;
|
||||||
|
static const int32_t msb2_32bit = 0x40000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! A wrapper around size metadata
|
||||||
|
/*! This class provides a way for archives to have more flexibility over how
|
||||||
|
they choose to serialize size metadata for containers. For some archive
|
||||||
|
types, the size may be implicitly encoded in the output (e.g. JSON) and
|
||||||
|
not need an explicit entry. Specializing serialize or load/save for
|
||||||
|
your archive and SizeTags allows you to choose what happens.
|
||||||
|
|
||||||
|
@internal */
|
||||||
|
template <class T>
|
||||||
|
class SizeTag
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Store a reference if passed an lvalue reference, otherwise
|
||||||
|
// make a copy of the data
|
||||||
|
using Type = typename std::conditional<std::is_lvalue_reference<T>::value,
|
||||||
|
T,
|
||||||
|
typename std::decay<T>::type>::type;
|
||||||
|
|
||||||
|
SizeTag & operator=( SizeTag const & ) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SizeTag( T && sz ) : size(std::forward<T>(sz)) {}
|
||||||
|
|
||||||
|
Type size;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! A wrapper around a key and value for serializing data into maps.
|
||||||
|
/*! This class just provides a grouping of keys and values into a struct for
|
||||||
|
human readable archives. For example, XML archives will use this wrapper
|
||||||
|
to write maps like so:
|
||||||
|
|
||||||
|
@code{.xml}
|
||||||
|
<mymap>
|
||||||
|
<item0>
|
||||||
|
<key>MyFirstKey</key>
|
||||||
|
<value>MyFirstValue</value>
|
||||||
|
</item0>
|
||||||
|
<item1>
|
||||||
|
<key>MySecondKey</key>
|
||||||
|
<value>MySecondValue</value>
|
||||||
|
</item1>
|
||||||
|
</mymap>
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
\sa make_map_item
|
||||||
|
@internal */
|
||||||
|
template <class Key, class Value>
|
||||||
|
struct MapItem
|
||||||
|
{
|
||||||
|
using KeyType = typename std::conditional<
|
||||||
|
std::is_lvalue_reference<Key>::value,
|
||||||
|
Key,
|
||||||
|
typename std::decay<Key>::type>::type;
|
||||||
|
|
||||||
|
using ValueType = typename std::conditional<
|
||||||
|
std::is_lvalue_reference<Value>::value,
|
||||||
|
Value,
|
||||||
|
typename std::decay<Value>::type>::type;
|
||||||
|
|
||||||
|
//! Construct a MapItem from a key and a value
|
||||||
|
/*! @internal */
|
||||||
|
MapItem( Key && key_, Value && value_ ) : key(std::forward<Key>(key_)), value(std::forward<Value>(value_)) {}
|
||||||
|
|
||||||
|
MapItem & operator=( MapItem const & ) = delete;
|
||||||
|
|
||||||
|
KeyType key;
|
||||||
|
ValueType value;
|
||||||
|
|
||||||
|
//! Serialize the MapItem with the NVPs "key" and "value"
|
||||||
|
template <class Archive> inline
|
||||||
|
void CEREAL_SERIALIZE_FUNCTION_NAME(Archive & archive)
|
||||||
|
{
|
||||||
|
archive( make_nvp<Archive>("key", key),
|
||||||
|
make_nvp<Archive>("value", value) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Create a MapItem so that human readable archives will group keys and values together
|
||||||
|
/*! @internal
|
||||||
|
@relates MapItem */
|
||||||
|
template <class KeyType, class ValueType> inline
|
||||||
|
MapItem<KeyType, ValueType> make_map_item(KeyType && key, ValueType && value)
|
||||||
|
{
|
||||||
|
return {std::forward<KeyType>(key), std::forward<ValueType>(value)};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//! Tag for Version, which due to its anonymous namespace, becomes a different
|
||||||
|
//! type in each translation unit
|
||||||
|
/*! This allows CEREAL_CLASS_VERSION to be safely called in a header file */
|
||||||
|
namespace{ struct version_binding_tag {}; }
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
//! Version information class
|
||||||
|
/*! This is the base case for classes that have not been explicitly
|
||||||
|
registered */
|
||||||
|
template <class T, class BindingTag = version_binding_tag> struct Version
|
||||||
|
{
|
||||||
|
static const std::uint32_t version = 0;
|
||||||
|
// we don't need to explicitly register these types since they
|
||||||
|
// always get a version number of 0
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Holds all registered version information
|
||||||
|
struct Versions
|
||||||
|
{
|
||||||
|
std::unordered_map<std::size_t, std::uint32_t> mapping;
|
||||||
|
|
||||||
|
std::uint32_t find( std::size_t hash, std::uint32_t version )
|
||||||
|
{
|
||||||
|
const auto result = mapping.emplace( hash, version );
|
||||||
|
return result.first->second;
|
||||||
|
}
|
||||||
|
}; // struct Versions
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_DETAILS_HELPERS_HPP_
|
||||||
688
external/cereal/details/polymorphic_impl.hpp
vendored
Normal file
688
external/cereal/details/polymorphic_impl.hpp
vendored
Normal file
@@ -0,0 +1,688 @@
|
|||||||
|
/*! \file polymorphic_impl.hpp
|
||||||
|
\brief Internal polymorphism support
|
||||||
|
\ingroup Internal */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This code is heavily inspired by the boost serialization implementation by the following authors
|
||||||
|
|
||||||
|
(C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
|
||||||
|
Use, modification and distribution is subject to the Boost Software
|
||||||
|
License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
See http://www.boost.org for updates, documentation, and revision history.
|
||||||
|
|
||||||
|
(C) Copyright 2006 David Abrahams - http://www.boost.org.
|
||||||
|
|
||||||
|
See /boost/serialization/export.hpp, /boost/archive/detail/register_archive.hpp,
|
||||||
|
and /boost/serialization/void_cast.hpp for their implementation. Additional details
|
||||||
|
found in other files split across serialization and archive.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
|
||||||
|
#define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
|
||||||
|
|
||||||
|
#include <cereal/details/polymorphic_impl_fwd.hpp>
|
||||||
|
#include <cereal/details/static_object.hpp>
|
||||||
|
#include <cereal/types/memory.hpp>
|
||||||
|
#include <cereal/types/string.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <typeindex>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
//! Binds a polymorhic type to all registered archives
|
||||||
|
/*! This binds a polymorphic type to all compatible registered archives that
|
||||||
|
have been registered with CEREAL_REGISTER_ARCHIVE. This must be called
|
||||||
|
after all archives are registered (usually after the archives themselves
|
||||||
|
have been included). */
|
||||||
|
#define CEREAL_BIND_TO_ARCHIVES(...) \
|
||||||
|
namespace cereal { \
|
||||||
|
namespace detail { \
|
||||||
|
template<> \
|
||||||
|
struct init_binding<__VA_ARGS__> { \
|
||||||
|
static bind_to_archives<__VA_ARGS__> const & b; \
|
||||||
|
static void unused() { (void)b; } \
|
||||||
|
}; \
|
||||||
|
bind_to_archives<__VA_ARGS__> const & init_binding<__VA_ARGS__>::b = \
|
||||||
|
::cereal::detail::StaticObject< \
|
||||||
|
bind_to_archives<__VA_ARGS__> \
|
||||||
|
>::getInstance().bind(); \
|
||||||
|
}} /* end namespaces */
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
/* Polymorphic casting support */
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//! Base type for polymorphic void casting
|
||||||
|
/*! Contains functions for casting between registered base and derived types.
|
||||||
|
|
||||||
|
This is necessary so that cereal can properly cast between polymorphic types
|
||||||
|
even though void pointers are used, which normally have no type information.
|
||||||
|
Runtime type information is used instead to index a compile-time made mapping
|
||||||
|
that can perform the proper cast. In the case of multiple levels of inheritance,
|
||||||
|
cereal will attempt to find the shortest path by using registered relationships to
|
||||||
|
perform the cast.
|
||||||
|
|
||||||
|
This class will be allocated as a StaticObject and only referenced by pointer,
|
||||||
|
allowing a templated derived version of it to define strongly typed functions
|
||||||
|
that cast between registered base and derived types. */
|
||||||
|
struct PolymorphicCaster
|
||||||
|
{
|
||||||
|
PolymorphicCaster() = default;
|
||||||
|
PolymorphicCaster( const PolymorphicCaster & ) = default;
|
||||||
|
PolymorphicCaster & operator=( const PolymorphicCaster & ) = default;
|
||||||
|
PolymorphicCaster( PolymorphicCaster && ) CEREAL_NOEXCEPT {}
|
||||||
|
PolymorphicCaster & operator=( PolymorphicCaster && ) CEREAL_NOEXCEPT { return *this; }
|
||||||
|
virtual ~PolymorphicCaster() CEREAL_NOEXCEPT = default;
|
||||||
|
|
||||||
|
//! Downcasts to the proper derived type
|
||||||
|
virtual void const * downcast( void const * const ptr ) const = 0;
|
||||||
|
//! Upcast to proper base type
|
||||||
|
virtual void * upcast( void * const ptr ) const = 0;
|
||||||
|
//! Upcast to proper base type, shared_ptr version
|
||||||
|
virtual std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Holds registered mappings between base and derived types for casting
|
||||||
|
/*! This will be allocated as a StaticObject and holds a map containing
|
||||||
|
all registered mappings between base and derived types. */
|
||||||
|
struct PolymorphicCasters
|
||||||
|
{
|
||||||
|
//! Maps from base type index to a map from derived type index to caster
|
||||||
|
std::map<std::type_index, std::map<std::type_index, std::vector<PolymorphicCaster const*>>> map;
|
||||||
|
|
||||||
|
//! Error message used for unregistered polymorphic casts
|
||||||
|
#define UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(LoadSave) \
|
||||||
|
throw cereal::Exception("Trying to " #LoadSave " a registered polymorphic type with an unregistered polymorphic cast.\n" \
|
||||||
|
"Could not find a path to a base class (" + util::demangle(baseInfo.name()) + ") for type: " + ::cereal::util::demangledName<Derived>() + "\n" \
|
||||||
|
"Make sure you either serialize the base class at some point via cereal::base_class or cereal::virtual_base_class.\n" \
|
||||||
|
"Alternatively, manually register the association with CEREAL_REGISTER_POLYMORPHIC_RELATION.");
|
||||||
|
|
||||||
|
//! Checks if the mapping object that can perform the upcast or downcast
|
||||||
|
/*! Uses the type index from the base and derived class to find the matching
|
||||||
|
registered caster. If no matching caster exists, returns false. */
|
||||||
|
static bool exists( std::type_index const & baseIndex, std::type_index const & derivedIndex )
|
||||||
|
{
|
||||||
|
// First phase of lookup - match base type index
|
||||||
|
auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
|
||||||
|
auto baseIter = baseMap.find( baseIndex );
|
||||||
|
if (baseIter == baseMap.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Second phase - find a match from base to derived
|
||||||
|
auto & derivedMap = baseIter->second;
|
||||||
|
auto derivedIter = derivedMap.find( derivedIndex );
|
||||||
|
if (derivedIter == derivedMap.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the mapping object that can perform the upcast or downcast
|
||||||
|
/*! Uses the type index from the base and derived class to find the matching
|
||||||
|
registered caster. If no matching caster exists, calls the exception function.
|
||||||
|
|
||||||
|
The returned PolymorphicCaster is capable of upcasting or downcasting between the two types. */
|
||||||
|
template <class F> inline
|
||||||
|
static std::vector<PolymorphicCaster const *> const & lookup( std::type_index const & baseIndex, std::type_index const & derivedIndex, F && exceptionFunc )
|
||||||
|
{
|
||||||
|
// First phase of lookup - match base type index
|
||||||
|
auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
|
||||||
|
auto baseIter = baseMap.find( baseIndex );
|
||||||
|
if( baseIter == baseMap.end() )
|
||||||
|
exceptionFunc();
|
||||||
|
|
||||||
|
// Second phase - find a match from base to derived
|
||||||
|
auto & derivedMap = baseIter->second;
|
||||||
|
auto derivedIter = derivedMap.find( derivedIndex );
|
||||||
|
if( derivedIter == derivedMap.end() )
|
||||||
|
exceptionFunc();
|
||||||
|
|
||||||
|
return derivedIter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Performs a downcast to the derived type using a registered mapping
|
||||||
|
template <class Derived> inline
|
||||||
|
static const Derived * downcast( const void * dptr, std::type_info const & baseInfo )
|
||||||
|
{
|
||||||
|
auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } );
|
||||||
|
|
||||||
|
for( auto const * map : mapping )
|
||||||
|
dptr = map->downcast( dptr );
|
||||||
|
|
||||||
|
return static_cast<Derived const *>( dptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Performs an upcast to the registered base type using the given a derived type
|
||||||
|
/*! The return is untyped because the final casting to the base type must happen in the polymorphic
|
||||||
|
serialization function, where the type is known at compile time */
|
||||||
|
template <class Derived> inline
|
||||||
|
static void * upcast( Derived * const dptr, std::type_info const & baseInfo )
|
||||||
|
{
|
||||||
|
auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } );
|
||||||
|
|
||||||
|
void * uptr = dptr;
|
||||||
|
for( auto const * map : mapping )
|
||||||
|
uptr = map->upcast( uptr );
|
||||||
|
|
||||||
|
return uptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Upcasts for shared pointers
|
||||||
|
template <class Derived> inline
|
||||||
|
static std::shared_ptr<void> upcast( std::shared_ptr<Derived> const & dptr, std::type_info const & baseInfo )
|
||||||
|
{
|
||||||
|
auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } );
|
||||||
|
|
||||||
|
std::shared_ptr<void> uptr = dptr;
|
||||||
|
for( auto const * map : mapping )
|
||||||
|
uptr = map->upcast( uptr );
|
||||||
|
|
||||||
|
return uptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Strongly typed derivation of PolymorphicCaster
|
||||||
|
template <class Base, class Derived>
|
||||||
|
struct PolymorphicVirtualCaster : PolymorphicCaster
|
||||||
|
{
|
||||||
|
//! Inserts an entry in the polymorphic casting map for this pairing
|
||||||
|
/*! Creates an explicit mapping between Base and Derived in both upwards and
|
||||||
|
downwards directions, allowing void pointers to either to be properly cast
|
||||||
|
assuming dynamic type information is available */
|
||||||
|
PolymorphicVirtualCaster()
|
||||||
|
{
|
||||||
|
const auto lock = StaticObject<PolymorphicCasters>::lock();
|
||||||
|
auto & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
|
||||||
|
auto baseKey = std::type_index(typeid(Base));
|
||||||
|
auto lb = baseMap.lower_bound(baseKey);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto & derivedMap = baseMap.insert( lb, {baseKey, {}} )->second;
|
||||||
|
auto derivedKey = std::type_index(typeid(Derived));
|
||||||
|
auto lbd = derivedMap.lower_bound(derivedKey);
|
||||||
|
auto & derivedVec = derivedMap.insert( lbd, { std::move(derivedKey), {}} )->second;
|
||||||
|
derivedVec.push_back( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all chainable unregistered relations
|
||||||
|
std::map<std::type_index, std::pair<std::type_index, std::vector<PolymorphicCaster const *>>> unregisteredRelations;
|
||||||
|
{
|
||||||
|
auto checkRelation = [](std::type_index const & baseInfo, std::type_index const & derivedInfo)
|
||||||
|
{
|
||||||
|
const bool exists = PolymorphicCasters::exists( baseInfo, derivedInfo );
|
||||||
|
return std::make_pair( exists, exists ? PolymorphicCasters::lookup( baseInfo, derivedInfo, [](){} ) :
|
||||||
|
std::vector<PolymorphicCaster const *>{} );
|
||||||
|
};
|
||||||
|
|
||||||
|
for( auto baseIt : baseMap )
|
||||||
|
for( auto derivedIt : baseIt.second )
|
||||||
|
{
|
||||||
|
for( auto otherBaseIt : baseMap )
|
||||||
|
{
|
||||||
|
if( baseIt.first == otherBaseIt.first ) // only interested in chained relations
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if there exists a mapping otherBase -> base -> derived that is shorter than
|
||||||
|
// any existing otherBase -> derived direct mapping
|
||||||
|
auto otherBaseItToDerived = checkRelation( otherBaseIt.first, derivedIt.first );
|
||||||
|
auto baseToDerived = checkRelation( baseIt.first, derivedIt.first );
|
||||||
|
auto otherBaseToBase = checkRelation( otherBaseIt.first, baseIt.first );
|
||||||
|
|
||||||
|
const size_t newLength = otherBaseToBase.second.size() + baseToDerived.second.size();
|
||||||
|
const bool isShorterOrFirstPath = !otherBaseItToDerived.first || (newLength < derivedIt.second.size());
|
||||||
|
|
||||||
|
if( isShorterOrFirstPath &&
|
||||||
|
baseToDerived.first &&
|
||||||
|
otherBaseToBase.first )
|
||||||
|
{
|
||||||
|
std::vector<PolymorphicCaster const *> path = otherBaseToBase.second;
|
||||||
|
path.insert( path.end(), baseToDerived.second.begin(), baseToDerived.second.end() );
|
||||||
|
|
||||||
|
#ifdef CEREAL_OLDER_GCC
|
||||||
|
unregisteredRelations.insert( std::make_pair(otherBaseIt.first,
|
||||||
|
std::pair<std::type_index, std::vector<PolymorphicCaster const *>>{derivedIt.first, std::move(path)}) );
|
||||||
|
#else // NOT CEREAL_OLDER_GCC
|
||||||
|
unregisteredRelations.emplace( otherBaseIt.first,
|
||||||
|
std::pair<std::type_index, std::vector<PolymorphicCaster const *>>{derivedIt.first, std::move(path)} );
|
||||||
|
#endif // NOT CEREAL_OLDER_GCC
|
||||||
|
}
|
||||||
|
} // end otherBaseIt
|
||||||
|
} // end derivedIt
|
||||||
|
} // end chain lookup
|
||||||
|
|
||||||
|
// Insert chained relations
|
||||||
|
for( auto it : unregisteredRelations )
|
||||||
|
{
|
||||||
|
auto & derivedMap = baseMap.find( it.first )->second;
|
||||||
|
derivedMap[it.second.first] = it.second.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Performs the proper downcast with the templated types
|
||||||
|
void const * downcast( void const * const ptr ) const override
|
||||||
|
{
|
||||||
|
return dynamic_cast<Derived const*>( static_cast<Base const*>( ptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Performs the proper upcast with the templated types
|
||||||
|
void * upcast( void * const ptr ) const override
|
||||||
|
{
|
||||||
|
return dynamic_cast<Base*>( static_cast<Derived*>( ptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Performs the proper upcast with the templated types (shared_ptr version)
|
||||||
|
std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const override
|
||||||
|
{
|
||||||
|
return std::dynamic_pointer_cast<Base>( std::static_pointer_cast<Derived>( ptr ) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Registers a polymorphic casting relation between a Base and Derived type
|
||||||
|
/*! Registering a relation allows cereal to properly cast between the two types
|
||||||
|
given runtime type information and void pointers.
|
||||||
|
|
||||||
|
Registration happens automatically via cereal::base_class and cereal::virtual_base_class
|
||||||
|
instantiations. For cases where neither is called, see the CEREAL_REGISTER_POLYMORPHIC_RELATION
|
||||||
|
macro */
|
||||||
|
template <class Base, class Derived>
|
||||||
|
struct RegisterPolymorphicCaster
|
||||||
|
{
|
||||||
|
static PolymorphicCaster const * bind( std::true_type /* is_polymorphic<Base> */)
|
||||||
|
{
|
||||||
|
return &StaticObject<PolymorphicVirtualCaster<Base, Derived>>::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
static PolymorphicCaster const * bind( std::false_type /* is_polymorphic<Base> */ )
|
||||||
|
{ return nullptr; }
|
||||||
|
|
||||||
|
//! Performs registration (binding) between Base and Derived
|
||||||
|
/*! If the type is not polymorphic, nothing will happen */
|
||||||
|
static PolymorphicCaster const * bind()
|
||||||
|
{ return bind( typename std::is_polymorphic<Base>::type() ); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* General polymorphism support */
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//! Binds a compile time type with a user defined string
|
||||||
|
template <class T>
|
||||||
|
struct binding_name {};
|
||||||
|
|
||||||
|
//! A structure holding a map from type_indices to output serializer functions
|
||||||
|
/*! A static object of this map should be created for each registered archive
|
||||||
|
type, containing entries for every registered type that describe how to
|
||||||
|
properly cast the type to its real type in polymorphic scenarios for
|
||||||
|
shared_ptr, weak_ptr, and unique_ptr. */
|
||||||
|
template <class Archive>
|
||||||
|
struct OutputBindingMap
|
||||||
|
{
|
||||||
|
//! A serializer function
|
||||||
|
/*! Serializer functions return nothing and take an archive as
|
||||||
|
their first parameter (will be cast properly inside the function,
|
||||||
|
a pointer to actual data (contents of smart_ptr's get() function)
|
||||||
|
as their second parameter, and the type info of the owning smart_ptr
|
||||||
|
as their final parameter */
|
||||||
|
typedef std::function<void(void*, void const *, std::type_info const &)> Serializer;
|
||||||
|
|
||||||
|
//! Struct containing the serializer functions for all pointer types
|
||||||
|
struct Serializers
|
||||||
|
{
|
||||||
|
Serializer shared_ptr, //!< Serializer function for shared/weak pointers
|
||||||
|
unique_ptr; //!< Serializer function for unique pointers
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A map of serializers for pointers of all registered types
|
||||||
|
std::map<std::type_index, Serializers> map;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! An empty noop deleter
|
||||||
|
template<class T> struct EmptyDeleter { void operator()(T *) const {} };
|
||||||
|
|
||||||
|
//! A structure holding a map from type name strings to input serializer functions
|
||||||
|
/*! A static object of this map should be created for each registered archive
|
||||||
|
type, containing entries for every registered type that describe how to
|
||||||
|
properly cast the type to its real type in polymorphic scenarios for
|
||||||
|
shared_ptr, weak_ptr, and unique_ptr. */
|
||||||
|
template <class Archive>
|
||||||
|
struct InputBindingMap
|
||||||
|
{
|
||||||
|
//! Shared ptr serializer function
|
||||||
|
/*! Serializer functions return nothing and take an archive as
|
||||||
|
their first parameter (will be cast properly inside the function,
|
||||||
|
a shared_ptr (or unique_ptr for the unique case) of any base
|
||||||
|
type, and the type id of said base type as the third parameter.
|
||||||
|
Internally it will properly be loaded and cast to the correct type. */
|
||||||
|
typedef std::function<void(void*, std::shared_ptr<void> &, std::type_info const &)> SharedSerializer;
|
||||||
|
//! Unique ptr serializer function
|
||||||
|
typedef std::function<void(void*, std::unique_ptr<void, EmptyDeleter<void>> &, std::type_info const &)> UniqueSerializer;
|
||||||
|
|
||||||
|
//! Struct containing the serializer functions for all pointer types
|
||||||
|
struct Serializers
|
||||||
|
{
|
||||||
|
SharedSerializer shared_ptr; //!< Serializer function for shared/weak pointers
|
||||||
|
UniqueSerializer unique_ptr; //!< Serializer function for unique pointers
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A map of serializers for pointers of all registered types
|
||||||
|
std::map<std::string, Serializers> map;
|
||||||
|
};
|
||||||
|
|
||||||
|
// forward decls for archives from cereal.hpp
|
||||||
|
class InputArchiveBase;
|
||||||
|
class OutputArchiveBase;
|
||||||
|
|
||||||
|
//! Creates a binding (map entry) between an input archive type and a polymorphic type
|
||||||
|
/*! Bindings are made when types are registered, assuming that at least one
|
||||||
|
archive has already been registered. When this struct is created,
|
||||||
|
it will insert (at run time) an entry into a map that properly handles
|
||||||
|
casting for serializing polymorphic objects */
|
||||||
|
template <class Archive, class T> struct InputBindingCreator
|
||||||
|
{
|
||||||
|
//! Initialize the binding
|
||||||
|
InputBindingCreator()
|
||||||
|
{
|
||||||
|
auto & map = StaticObject<InputBindingMap<Archive>>::getInstance().map;
|
||||||
|
auto lock = StaticObject<InputBindingMap<Archive>>::lock();
|
||||||
|
auto key = std::string(binding_name<T>::name());
|
||||||
|
auto lb = map.lower_bound(key);
|
||||||
|
|
||||||
|
if (lb != map.end() && lb->first == key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
typename InputBindingMap<Archive>::Serializers serializers;
|
||||||
|
|
||||||
|
serializers.shared_ptr =
|
||||||
|
[](void * arptr, std::shared_ptr<void> & dptr, std::type_info const & baseInfo)
|
||||||
|
{
|
||||||
|
Archive & ar = *static_cast<Archive*>(arptr);
|
||||||
|
std::shared_ptr<T> ptr;
|
||||||
|
|
||||||
|
ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
|
||||||
|
|
||||||
|
dptr = PolymorphicCasters::template upcast<T>( ptr, baseInfo );
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.unique_ptr =
|
||||||
|
[](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr, std::type_info const & baseInfo)
|
||||||
|
{
|
||||||
|
Archive & ar = *static_cast<Archive*>(arptr);
|
||||||
|
std::unique_ptr<T> ptr;
|
||||||
|
|
||||||
|
ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
|
||||||
|
|
||||||
|
dptr.reset( PolymorphicCasters::template upcast<T>( ptr.release(), baseInfo ));
|
||||||
|
};
|
||||||
|
|
||||||
|
map.insert( lb, { std::move(key), std::move(serializers) } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Creates a binding (map entry) between an output archive type and a polymorphic type
|
||||||
|
/*! Bindings are made when types are registered, assuming that at least one
|
||||||
|
archive has already been registered. When this struct is created,
|
||||||
|
it will insert (at run time) an entry into a map that properly handles
|
||||||
|
casting for serializing polymorphic objects */
|
||||||
|
template <class Archive, class T> struct OutputBindingCreator
|
||||||
|
{
|
||||||
|
//! Writes appropriate metadata to the archive for this polymorphic type
|
||||||
|
static void writeMetadata(Archive & ar)
|
||||||
|
{
|
||||||
|
// Register the polymorphic type name with the archive, and get the id
|
||||||
|
char const * name = binding_name<T>::name();
|
||||||
|
std::uint32_t id = ar.registerPolymorphicType(name);
|
||||||
|
|
||||||
|
// Serialize the id
|
||||||
|
ar( CEREAL_NVP_("polymorphic_id", id) );
|
||||||
|
|
||||||
|
// If the msb of the id is 1, then the type name is new, and we should serialize it
|
||||||
|
if( id & detail::msb_32bit )
|
||||||
|
{
|
||||||
|
std::string namestring(name);
|
||||||
|
ar( CEREAL_NVP_("polymorphic_name", namestring) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Holds a properly typed shared_ptr to the polymorphic type
|
||||||
|
class PolymorphicSharedPointerWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! Wrap a raw polymorphic pointer in a shared_ptr to its true type
|
||||||
|
|
||||||
|
The wrapped pointer will not be responsible for ownership of the held pointer
|
||||||
|
so it will not attempt to destroy it; instead the refcount of the wrapped
|
||||||
|
pointer will be tied to a fake 'ownership pointer' that will do nothing
|
||||||
|
when it ultimately goes out of scope.
|
||||||
|
|
||||||
|
The main reason for doing this, other than not to destroy the true object
|
||||||
|
with our wrapper pointer, is to avoid meddling with the internal reference
|
||||||
|
count in a polymorphic type that inherits from std::enable_shared_from_this.
|
||||||
|
|
||||||
|
@param dptr A void pointer to the contents of the shared_ptr to serialize */
|
||||||
|
PolymorphicSharedPointerWrapper( T const * dptr ) : refCount(), wrappedPtr( refCount, dptr )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Get the wrapped shared_ptr */
|
||||||
|
inline std::shared_ptr<T const> const & operator()() const { return wrappedPtr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<void> refCount; //!< The ownership pointer
|
||||||
|
std::shared_ptr<T const> wrappedPtr; //!< The wrapped pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Does the actual work of saving a polymorphic shared_ptr
|
||||||
|
/*! This function will properly create a shared_ptr from the void * that is passed in
|
||||||
|
before passing it to the archive for serialization.
|
||||||
|
|
||||||
|
In addition, this will also preserve the state of any internal enable_shared_from_this mechanisms
|
||||||
|
|
||||||
|
@param ar The archive to serialize to
|
||||||
|
@param dptr Pointer to the actual data held by the shared_ptr */
|
||||||
|
static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::true_type /* has_shared_from_this */ )
|
||||||
|
{
|
||||||
|
::cereal::memory_detail::EnableSharedStateHelper<T> state( const_cast<T *>(dptr) );
|
||||||
|
PolymorphicSharedPointerWrapper psptr( dptr );
|
||||||
|
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Does the actual work of saving a polymorphic shared_ptr
|
||||||
|
/*! This function will properly create a shared_ptr from the void * that is passed in
|
||||||
|
before passing it to the archive for serialization.
|
||||||
|
|
||||||
|
This version is for types that do not inherit from std::enable_shared_from_this.
|
||||||
|
|
||||||
|
@param ar The archive to serialize to
|
||||||
|
@param dptr Pointer to the actual data held by the shared_ptr */
|
||||||
|
static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::false_type /* has_shared_from_this */ )
|
||||||
|
{
|
||||||
|
PolymorphicSharedPointerWrapper psptr( dptr );
|
||||||
|
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Initialize the binding
|
||||||
|
OutputBindingCreator()
|
||||||
|
{
|
||||||
|
auto & map = StaticObject<OutputBindingMap<Archive>>::getInstance().map;
|
||||||
|
auto key = std::type_index(typeid(T));
|
||||||
|
auto lb = map.lower_bound(key);
|
||||||
|
|
||||||
|
if (lb != map.end() && lb->first == key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
typename OutputBindingMap<Archive>::Serializers serializers;
|
||||||
|
|
||||||
|
serializers.shared_ptr =
|
||||||
|
[&](void * arptr, void const * dptr, std::type_info const & baseInfo)
|
||||||
|
{
|
||||||
|
Archive & ar = *static_cast<Archive*>(arptr);
|
||||||
|
writeMetadata(ar);
|
||||||
|
|
||||||
|
auto ptr = PolymorphicCasters::template downcast<T>( dptr, baseInfo );
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
savePolymorphicSharedPtr( ar, ptr, ::cereal::traits::has_shared_from_this<T>::type() ); // MSVC doesn't like typename here
|
||||||
|
#else // not _MSC_VER
|
||||||
|
savePolymorphicSharedPtr( ar, ptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
|
||||||
|
#endif // _MSC_VER
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.unique_ptr =
|
||||||
|
[&](void * arptr, void const * dptr, std::type_info const & baseInfo)
|
||||||
|
{
|
||||||
|
Archive & ar = *static_cast<Archive*>(arptr);
|
||||||
|
writeMetadata(ar);
|
||||||
|
|
||||||
|
std::unique_ptr<T const, EmptyDeleter<T const>> const ptr( PolymorphicCasters::template downcast<T>( dptr, baseInfo ) );
|
||||||
|
|
||||||
|
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
|
||||||
|
};
|
||||||
|
|
||||||
|
map.insert( { std::move(key), std::move(serializers) } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Used to help out argument dependent lookup for finding potential overloads
|
||||||
|
//! of instantiate_polymorphic_binding
|
||||||
|
struct adl_tag {};
|
||||||
|
|
||||||
|
//! Tag for init_binding, bind_to_archives and instantiate_polymorphic_binding. Due to the use of anonymous
|
||||||
|
//! namespace it becomes a different type in each translation unit.
|
||||||
|
namespace { struct polymorphic_binding_tag {}; }
|
||||||
|
|
||||||
|
//! Causes the static object bindings between an archive type and a serializable type T
|
||||||
|
template <class Archive, class T>
|
||||||
|
struct create_bindings
|
||||||
|
{
|
||||||
|
static const InputBindingCreator<Archive, T> &
|
||||||
|
load(std::true_type)
|
||||||
|
{
|
||||||
|
return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const OutputBindingCreator<Archive, T> &
|
||||||
|
save(std::true_type)
|
||||||
|
{
|
||||||
|
return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void load(std::false_type) {}
|
||||||
|
inline static void save(std::false_type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! When specialized, causes the compiler to instantiate its parameter
|
||||||
|
template <void(*)()>
|
||||||
|
struct instantiate_function {};
|
||||||
|
|
||||||
|
/*! This struct is used as the return type of instantiate_polymorphic_binding
|
||||||
|
for specific Archive types. When the compiler looks for overloads of
|
||||||
|
instantiate_polymorphic_binding, it will be forced to instantiate this
|
||||||
|
struct during overload resolution, even though it will not be part of a valid
|
||||||
|
overload */
|
||||||
|
template <class Archive, class T>
|
||||||
|
struct polymorphic_serialization_support
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||||
|
//! Creates the appropriate bindings depending on whether the archive supports
|
||||||
|
//! saving or loading
|
||||||
|
virtual CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
|
||||||
|
#else // NOT _MSC_VER
|
||||||
|
//! Creates the appropriate bindings depending on whether the archive supports
|
||||||
|
//! saving or loading
|
||||||
|
static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
|
||||||
|
//! This typedef causes the compiler to instantiate this static function
|
||||||
|
typedef instantiate_function<instantiate> unused;
|
||||||
|
#endif // _MSC_VER
|
||||||
|
};
|
||||||
|
|
||||||
|
// instantiate implementation
|
||||||
|
template <class Archive, class T>
|
||||||
|
CEREAL_DLL_EXPORT void polymorphic_serialization_support<Archive,T>::instantiate()
|
||||||
|
{
|
||||||
|
create_bindings<Archive,T>::save( std::integral_constant<bool,
|
||||||
|
std::is_base_of<detail::OutputArchiveBase, Archive>::value &&
|
||||||
|
traits::is_output_serializable<T, Archive>::value>{} );
|
||||||
|
|
||||||
|
create_bindings<Archive,T>::load( std::integral_constant<bool,
|
||||||
|
std::is_base_of<detail::InputArchiveBase, Archive>::value &&
|
||||||
|
traits::is_input_serializable<T, Archive>::value>{} );
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Begins the binding process of a type to all registered archives
|
||||||
|
/*! Archives need to be registered prior to this struct being instantiated via
|
||||||
|
the CEREAL_REGISTER_ARCHIVE macro. Overload resolution will then force
|
||||||
|
several static objects to be made that allow us to bind together all
|
||||||
|
registered archive types with the parameter type T. */
|
||||||
|
template <class T, class Tag = polymorphic_binding_tag>
|
||||||
|
struct bind_to_archives
|
||||||
|
{
|
||||||
|
//! Binding for non abstract types
|
||||||
|
void bind(std::false_type) const
|
||||||
|
{
|
||||||
|
instantiate_polymorphic_binding((T*) 0, 0, Tag{}, adl_tag{});
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Binding for abstract types
|
||||||
|
void bind(std::true_type) const
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//! Binds the type T to all registered archives
|
||||||
|
/*! If T is abstract, we will not serialize it and thus
|
||||||
|
do not need to make a binding */
|
||||||
|
bind_to_archives const & bind() const
|
||||||
|
{
|
||||||
|
static_assert( std::is_polymorphic<T>::value,
|
||||||
|
"Attempting to register non polymorphic type" );
|
||||||
|
bind( std::is_abstract<T>() );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Used to hide the static object used to bind T to registered archives
|
||||||
|
template <class T, class Tag = polymorphic_binding_tag>
|
||||||
|
struct init_binding;
|
||||||
|
|
||||||
|
//! Base case overload for instantiation
|
||||||
|
/*! This will end up always being the best overload due to the second
|
||||||
|
parameter always being passed as an int. All other overloads will
|
||||||
|
accept pointers to archive types and have lower precedence than int.
|
||||||
|
|
||||||
|
Since the compiler needs to check all possible overloads, the
|
||||||
|
other overloads created via CEREAL_REGISTER_ARCHIVE, which will have
|
||||||
|
lower precedence due to requring a conversion from int to (Archive*),
|
||||||
|
will cause their return types to be instantiated through the static object
|
||||||
|
mechanisms even though they are never called.
|
||||||
|
|
||||||
|
See the documentation for the other functions to try and understand this */
|
||||||
|
template <class T, typename BindingTag>
|
||||||
|
void instantiate_polymorphic_binding( T*, int, BindingTag, adl_tag ) {}
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
|
||||||
65
external/cereal/details/polymorphic_impl_fwd.hpp
vendored
Normal file
65
external/cereal/details/polymorphic_impl_fwd.hpp
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*! \file polymorphic_impl_fwd.hpp
|
||||||
|
\brief Internal polymorphism support forward declarations
|
||||||
|
\ingroup Internal */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This code is heavily inspired by the boost serialization implementation by the following authors
|
||||||
|
|
||||||
|
(C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
|
||||||
|
Use, modification and distribution is subject to the Boost Software
|
||||||
|
License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
See http://www.boost.org for updates, documentation, and revision history.
|
||||||
|
|
||||||
|
(C) Copyright 2006 David Abrahams - http://www.boost.org.
|
||||||
|
|
||||||
|
See /boost/serialization/export.hpp and /boost/archive/detail/register_archive.hpp for their
|
||||||
|
implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_FWD_HPP_
|
||||||
|
#define CEREAL_DETAILS_POLYMORPHIC_IMPL_FWD_HPP_
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//! Forward declaration, see polymorphic_impl.hpp for more information
|
||||||
|
template <class Base, class Derived>
|
||||||
|
struct RegisterPolymorphicCaster;
|
||||||
|
|
||||||
|
//! Forward declaration, see polymorphic_impl.hpp for more information
|
||||||
|
struct PolymorphicCasters;
|
||||||
|
|
||||||
|
//! Forward declaration, see polymorphic_impl.hpp for more information
|
||||||
|
template <class Base, class Derived>
|
||||||
|
struct PolymorphicRelation;
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_FWD_HPP_
|
||||||
132
external/cereal/details/static_object.hpp
vendored
Normal file
132
external/cereal/details/static_object.hpp
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*! \file static_object.hpp
|
||||||
|
\brief Internal polymorphism static object support
|
||||||
|
\ingroup Internal */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_DETAILS_STATIC_OBJECT_HPP_
|
||||||
|
#define CEREAL_DETAILS_STATIC_OBJECT_HPP_
|
||||||
|
|
||||||
|
#include <cereal/macros.hpp>
|
||||||
|
|
||||||
|
#if CEREAL_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Prevent link optimization from removing non-referenced static objects
|
||||||
|
/*! Especially for polymorphic support, we create static objects which
|
||||||
|
may not ever be explicitly referenced. Most linkers will detect this
|
||||||
|
and remove the code causing various unpleasant runtime errors. These
|
||||||
|
macros, adopted from Boost (see force_include.hpp) prevent this
|
||||||
|
(C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
|
||||||
|
Use, modification and distribution is subject to the Boost Software
|
||||||
|
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
http://www.boost.org/LICENSE_1_0.txt) */
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# define CEREAL_DLL_EXPORT __declspec(dllexport)
|
||||||
|
# define CEREAL_USED
|
||||||
|
#else // clang or gcc
|
||||||
|
# define CEREAL_DLL_EXPORT
|
||||||
|
# define CEREAL_USED __attribute__ ((__used__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//! A static, pre-execution object
|
||||||
|
/*! This class will create a single copy (singleton) of some
|
||||||
|
type and ensures that merely referencing this type will
|
||||||
|
cause it to be instantiated and initialized pre-execution.
|
||||||
|
For example, this is used heavily in the polymorphic pointer
|
||||||
|
serialization mechanisms to bind various archive types with
|
||||||
|
different polymorphic classes */
|
||||||
|
template <class T>
|
||||||
|
class CEREAL_DLL_EXPORT StaticObject
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
//! Forces instantiation at pre-execution time
|
||||||
|
static void instantiate( T const & ) {}
|
||||||
|
|
||||||
|
static T & create()
|
||||||
|
{
|
||||||
|
static T t;
|
||||||
|
instantiate(instance);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticObject( StaticObject const & /*other*/ ) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static T & getInstance()
|
||||||
|
{
|
||||||
|
return create();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! A class that acts like std::lock_guard
|
||||||
|
class LockGuard
|
||||||
|
{
|
||||||
|
#if CEREAL_THREAD_SAFE
|
||||||
|
public:
|
||||||
|
LockGuard(std::mutex & m) : lock(m) {}
|
||||||
|
private:
|
||||||
|
std::unique_lock<std::mutex> lock;
|
||||||
|
#else
|
||||||
|
public:
|
||||||
|
~LockGuard() CEREAL_NOEXCEPT {} // prevents variable not used
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Attempts to lock this static object for the current scope
|
||||||
|
/*! @note This function is a no-op if cereal is not compiled with
|
||||||
|
thread safety enabled (CEREAL_THREAD_SAFE = 1).
|
||||||
|
|
||||||
|
This function returns an object that holds a lock for
|
||||||
|
this StaticObject that will release its lock upon destruction. This
|
||||||
|
call will block until the lock is available. */
|
||||||
|
static LockGuard lock()
|
||||||
|
{
|
||||||
|
#if CEREAL_THREAD_SAFE
|
||||||
|
return LockGuard{instanceMutex};
|
||||||
|
#else
|
||||||
|
return LockGuard{};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static T & instance;
|
||||||
|
#if CEREAL_THREAD_SAFE
|
||||||
|
static std::mutex instanceMutex;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> T & StaticObject<T>::instance = StaticObject<T>::create();
|
||||||
|
#if CEREAL_THREAD_SAFE
|
||||||
|
template <class T> std::mutex StaticObject<T>::instanceMutex;
|
||||||
|
#endif
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_DETAILS_STATIC_OBJECT_HPP_
|
||||||
1389
external/cereal/details/traits.hpp
vendored
Normal file
1389
external/cereal/details/traits.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
84
external/cereal/details/util.hpp
vendored
Normal file
84
external/cereal/details/util.hpp
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*! \file util.hpp
|
||||||
|
\brief Internal misc utilities
|
||||||
|
\ingroup Internal */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of cereal nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_DETAILS_UTIL_HPP_
|
||||||
|
#define CEREAL_DETAILS_UTIL_HPP_
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
//! Demangles the type encoded in a string
|
||||||
|
/*! @internal */
|
||||||
|
inline std::string demangle( std::string const & name )
|
||||||
|
{ return name; }
|
||||||
|
|
||||||
|
//! Gets the demangled name of a type
|
||||||
|
/*! @internal */
|
||||||
|
template <class T> inline
|
||||||
|
std::string demangledName()
|
||||||
|
{ return typeid( T ).name(); }
|
||||||
|
} // namespace util
|
||||||
|
} // namespace cereal
|
||||||
|
#else // clang or gcc
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
//! Demangles the type encoded in a string
|
||||||
|
/*! @internal */
|
||||||
|
inline std::string demangle(std::string mangledName)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
char *demangledName = nullptr;
|
||||||
|
std::size_t len;
|
||||||
|
|
||||||
|
demangledName = abi::__cxa_demangle(mangledName.c_str(), 0, &len, &status);
|
||||||
|
|
||||||
|
std::string retName(demangledName);
|
||||||
|
free(demangledName);
|
||||||
|
|
||||||
|
return retName;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the demangled name of a type
|
||||||
|
/*! @internal */
|
||||||
|
template<class T> inline
|
||||||
|
std::string demangledName()
|
||||||
|
{ return demangle(typeid(T).name()); }
|
||||||
|
}
|
||||||
|
} // namespace cereal
|
||||||
|
#endif // clang or gcc branch of _MSC_VER
|
||||||
|
#endif // CEREAL_DETAILS_UTIL_HPP_
|
||||||
129
external/cereal/external/base64.hpp
vendored
Normal file
129
external/cereal/external/base64.hpp
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2004-2008 René Nyffenegger
|
||||||
|
|
||||||
|
This source code is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the author be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this source code must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original source code. If you use this source code
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original source code.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CEREAL_EXTERNAL_BASE64_HPP_
|
||||||
|
#define CEREAL_EXTERNAL_BASE64_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace cereal
|
||||||
|
{
|
||||||
|
namespace base64
|
||||||
|
{
|
||||||
|
static const std::string chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
|
||||||
|
static inline bool is_base64(unsigned char c) {
|
||||||
|
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string encode(unsigned char const* bytes_to_encode, size_t in_len) {
|
||||||
|
std::string ret;
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
unsigned char char_array_3[3];
|
||||||
|
unsigned char char_array_4[4];
|
||||||
|
|
||||||
|
while (in_len--) {
|
||||||
|
char_array_3[i++] = *(bytes_to_encode++);
|
||||||
|
if (i == 3) {
|
||||||
|
char_array_4[0] = (unsigned char) ((char_array_3[0] & 0xfc) >> 2);
|
||||||
|
char_array_4[1] = (unsigned char) ( ( ( char_array_3[0] & 0x03 ) << 4 ) + ( ( char_array_3[1] & 0xf0 ) >> 4 ) );
|
||||||
|
char_array_4[2] = (unsigned char) ( ( ( char_array_3[1] & 0x0f ) << 2 ) + ( ( char_array_3[2] & 0xc0 ) >> 6 ) );
|
||||||
|
char_array_4[3] = (unsigned char) ( char_array_3[2] & 0x3f );
|
||||||
|
|
||||||
|
for(i = 0; (i <4) ; i++)
|
||||||
|
ret += chars[char_array_4[i]];
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i)
|
||||||
|
{
|
||||||
|
for(j = i; j < 3; j++)
|
||||||
|
char_array_3[j] = '\0';
|
||||||
|
|
||||||
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
|
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||||
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
|
for (j = 0; (j < i + 1); j++)
|
||||||
|
ret += chars[char_array_4[j]];
|
||||||
|
|
||||||
|
while((i++ < 3))
|
||||||
|
ret += '=';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string decode(std::string const& encoded_string) {
|
||||||
|
size_t in_len = encoded_string.size();
|
||||||
|
size_t i = 0;
|
||||||
|
size_t j = 0;
|
||||||
|
int in_ = 0;
|
||||||
|
unsigned char char_array_4[4], char_array_3[3];
|
||||||
|
std::string ret;
|
||||||
|
|
||||||
|
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||||
|
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||||
|
if (i ==4) {
|
||||||
|
for (i = 0; i <4; i++)
|
||||||
|
char_array_4[i] = (unsigned char) chars.find( char_array_4[i] );
|
||||||
|
|
||||||
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
|
for (i = 0; (i < 3); i++)
|
||||||
|
ret += char_array_3[i];
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i) {
|
||||||
|
for (j = i; j <4; j++)
|
||||||
|
char_array_4[j] = 0;
|
||||||
|
|
||||||
|
for (j = 0; j <4; j++)
|
||||||
|
char_array_4[j] = (unsigned char) chars.find( char_array_4[j] );
|
||||||
|
|
||||||
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
|
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} // namespace base64
|
||||||
|
} // namespace cereal
|
||||||
|
|
||||||
|
#endif // CEREAL_EXTERNAL_BASE64_HPP_
|
||||||
271
external/cereal/external/rapidjson/allocators.h
vendored
Normal file
271
external/cereal/external/rapidjson/allocators.h
vendored
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ALLOCATORS_H_
|
||||||
|
#define CEREAL_RAPIDJSON_ALLOCATORS_H_
|
||||||
|
|
||||||
|
#include "rapidjson.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Allocator
|
||||||
|
|
||||||
|
/*! \class rapidjson::Allocator
|
||||||
|
\brief Concept for allocating, resizing and freeing memory block.
|
||||||
|
|
||||||
|
Note that Malloc() and Realloc() are non-static but Free() is static.
|
||||||
|
|
||||||
|
So if an allocator need to support Free(), it needs to put its pointer in
|
||||||
|
the header of memory block.
|
||||||
|
|
||||||
|
\code
|
||||||
|
concept Allocator {
|
||||||
|
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
|
||||||
|
|
||||||
|
// Allocate a memory block.
|
||||||
|
// \param size of the memory block in bytes.
|
||||||
|
// \returns pointer to the memory block.
|
||||||
|
void* Malloc(size_t size);
|
||||||
|
|
||||||
|
// Resize a memory block.
|
||||||
|
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
|
||||||
|
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
|
||||||
|
// \param newSize the new size in bytes.
|
||||||
|
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
|
||||||
|
|
||||||
|
// Free a memory block.
|
||||||
|
// \param pointer to the memory block. Null pointer is permitted.
|
||||||
|
static void Free(void *ptr);
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CrtAllocator
|
||||||
|
|
||||||
|
//! C-runtime library allocator.
|
||||||
|
/*! This class is just wrapper for standard C library memory routines.
|
||||||
|
\note implements Allocator concept
|
||||||
|
*/
|
||||||
|
class CrtAllocator {
|
||||||
|
public:
|
||||||
|
static const bool kNeedFree = true;
|
||||||
|
void* Malloc(size_t size) {
|
||||||
|
if (size) // behavior of malloc(0) is implementation defined.
|
||||||
|
return std::malloc(size);
|
||||||
|
else
|
||||||
|
return NULL; // standardize to returning NULL.
|
||||||
|
}
|
||||||
|
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||||
|
(void)originalSize;
|
||||||
|
if (newSize == 0) {
|
||||||
|
std::free(originalPtr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return std::realloc(originalPtr, newSize);
|
||||||
|
}
|
||||||
|
static void Free(void *ptr) { std::free(ptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MemoryPoolAllocator
|
||||||
|
|
||||||
|
//! Default memory allocator used by the parser and DOM.
|
||||||
|
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
|
||||||
|
|
||||||
|
It does not free memory blocks. And Realloc() only allocate new memory.
|
||||||
|
|
||||||
|
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
|
||||||
|
|
||||||
|
User may also supply a buffer as the first chunk.
|
||||||
|
|
||||||
|
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
|
||||||
|
|
||||||
|
The user-buffer is not deallocated by this allocator.
|
||||||
|
|
||||||
|
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
|
||||||
|
\note implements Allocator concept
|
||||||
|
*/
|
||||||
|
template <typename BaseAllocator = CrtAllocator>
|
||||||
|
class MemoryPoolAllocator {
|
||||||
|
public:
|
||||||
|
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
||||||
|
|
||||||
|
//! Constructor with chunkSize.
|
||||||
|
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||||
|
\param baseAllocator The allocator for allocating memory chunks.
|
||||||
|
*/
|
||||||
|
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||||
|
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Constructor with user-supplied buffer.
|
||||||
|
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
|
||||||
|
|
||||||
|
The user buffer will not be deallocated when this allocator is destructed.
|
||||||
|
|
||||||
|
\param buffer User supplied buffer.
|
||||||
|
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
|
||||||
|
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||||
|
\param baseAllocator The allocator for allocating memory chunks.
|
||||||
|
*/
|
||||||
|
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||||
|
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||||
|
{
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(buffer != 0);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
|
||||||
|
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
|
||||||
|
chunkHead_->capacity = size - sizeof(ChunkHeader);
|
||||||
|
chunkHead_->size = 0;
|
||||||
|
chunkHead_->next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructor.
|
||||||
|
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
||||||
|
*/
|
||||||
|
~MemoryPoolAllocator() {
|
||||||
|
Clear();
|
||||||
|
CEREAL_RAPIDJSON_DELETE(ownBaseAllocator_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Deallocates all memory chunks, excluding the user-supplied buffer.
|
||||||
|
void Clear() {
|
||||||
|
while (chunkHead_ && chunkHead_ != userBuffer_) {
|
||||||
|
ChunkHeader* next = chunkHead_->next;
|
||||||
|
baseAllocator_->Free(chunkHead_);
|
||||||
|
chunkHead_ = next;
|
||||||
|
}
|
||||||
|
if (chunkHead_ && chunkHead_ == userBuffer_)
|
||||||
|
chunkHead_->size = 0; // Clear user buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Computes the total capacity of allocated memory chunks.
|
||||||
|
/*! \return total capacity in bytes.
|
||||||
|
*/
|
||||||
|
size_t Capacity() const {
|
||||||
|
size_t capacity = 0;
|
||||||
|
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||||
|
capacity += c->capacity;
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Computes the memory blocks allocated.
|
||||||
|
/*! \return total used bytes.
|
||||||
|
*/
|
||||||
|
size_t Size() const {
|
||||||
|
size_t size = 0;
|
||||||
|
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||||
|
size += c->size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Allocates a memory block. (concept Allocator)
|
||||||
|
void* Malloc(size_t size) {
|
||||||
|
if (!size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = CEREAL_RAPIDJSON_ALIGN(size);
|
||||||
|
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
||||||
|
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
void *buffer = reinterpret_cast<char *>(chunkHead_) + CEREAL_RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
|
||||||
|
chunkHead_->size += size;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Resizes a memory block (concept Allocator)
|
||||||
|
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||||
|
if (originalPtr == 0)
|
||||||
|
return Malloc(newSize);
|
||||||
|
|
||||||
|
if (newSize == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
originalSize = CEREAL_RAPIDJSON_ALIGN(originalSize);
|
||||||
|
newSize = CEREAL_RAPIDJSON_ALIGN(newSize);
|
||||||
|
|
||||||
|
// Do not shrink if new size is smaller than original
|
||||||
|
if (originalSize >= newSize)
|
||||||
|
return originalPtr;
|
||||||
|
|
||||||
|
// Simply expand it if it is the last allocation and there is sufficient space
|
||||||
|
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + CEREAL_RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
|
||||||
|
size_t increment = static_cast<size_t>(newSize - originalSize);
|
||||||
|
if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
||||||
|
chunkHead_->size += increment;
|
||||||
|
return originalPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||||
|
if (void* newBuffer = Malloc(newSize)) {
|
||||||
|
if (originalSize)
|
||||||
|
std::memcpy(newBuffer, originalPtr, originalSize);
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Frees a memory block (concept Allocator)
|
||||||
|
static void Free(void *ptr) { (void)ptr; } // Do nothing
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Copy constructor is not permitted.
|
||||||
|
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||||
|
//! Copy assignment operator is not permitted.
|
||||||
|
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||||
|
|
||||||
|
//! Creates a new chunk.
|
||||||
|
/*! \param capacity Capacity of the chunk in bytes.
|
||||||
|
\return true if success.
|
||||||
|
*/
|
||||||
|
bool AddChunk(size_t capacity) {
|
||||||
|
if (!baseAllocator_)
|
||||||
|
ownBaseAllocator_ = baseAllocator_ = CEREAL_RAPIDJSON_NEW(BaseAllocator());
|
||||||
|
if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(CEREAL_RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
|
||||||
|
chunk->capacity = capacity;
|
||||||
|
chunk->size = 0;
|
||||||
|
chunk->next = chunkHead_;
|
||||||
|
chunkHead_ = chunk;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
|
||||||
|
|
||||||
|
//! Chunk header for perpending to each chunk.
|
||||||
|
/*! Chunks are stored as a singly linked list.
|
||||||
|
*/
|
||||||
|
struct ChunkHeader {
|
||||||
|
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
||||||
|
size_t size; //!< Current size of allocated memory in bytes.
|
||||||
|
ChunkHeader *next; //!< Next chunk in the linked list.
|
||||||
|
};
|
||||||
|
|
||||||
|
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
||||||
|
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
||||||
|
void *userBuffer_; //!< User supplied buffer.
|
||||||
|
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
||||||
|
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
|
||||||
|
};
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_ENCODINGS_H_
|
||||||
2575
external/cereal/external/rapidjson/document.h
vendored
Normal file
2575
external/cereal/external/rapidjson/document.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
299
external/cereal/external/rapidjson/encodedstream.h
vendored
Normal file
299
external/cereal/external/rapidjson/encodedstream.h
vendored
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ENCODEDSTREAM_H_
|
||||||
|
#define CEREAL_RAPIDJSON_ENCODEDSTREAM_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include "memorystream.h"
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Input byte stream wrapper with a statically bound encoding.
|
||||||
|
/*!
|
||||||
|
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||||
|
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
|
||||||
|
*/
|
||||||
|
template <typename Encoding, typename InputByteStream>
|
||||||
|
class EncodedInputStream {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
public:
|
||||||
|
typedef typename Encoding::Ch Ch;
|
||||||
|
|
||||||
|
EncodedInputStream(InputByteStream& is) : is_(is) {
|
||||||
|
current_ = Encoding::TakeBOM(is_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ch Peek() const { return current_; }
|
||||||
|
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
|
||||||
|
size_t Tell() const { return is_.Tell(); }
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
void Flush() { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EncodedInputStream(const EncodedInputStream&);
|
||||||
|
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||||
|
|
||||||
|
InputByteStream& is_;
|
||||||
|
Ch current_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Specialized for UTF8 MemoryStream.
|
||||||
|
template <>
|
||||||
|
class EncodedInputStream<UTF8<>, MemoryStream> {
|
||||||
|
public:
|
||||||
|
typedef UTF8<>::Ch Ch;
|
||||||
|
|
||||||
|
EncodedInputStream(MemoryStream& is) : is_(is) {
|
||||||
|
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
|
||||||
|
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
|
||||||
|
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
|
||||||
|
}
|
||||||
|
Ch Peek() const { return is_.Peek(); }
|
||||||
|
Ch Take() { return is_.Take(); }
|
||||||
|
size_t Tell() const { return is_.Tell(); }
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
void Put(Ch) {}
|
||||||
|
void Flush() {}
|
||||||
|
Ch* PutBegin() { return 0; }
|
||||||
|
size_t PutEnd(Ch*) { return 0; }
|
||||||
|
|
||||||
|
MemoryStream& is_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EncodedInputStream(const EncodedInputStream&);
|
||||||
|
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Output byte stream wrapper with statically bound encoding.
|
||||||
|
/*!
|
||||||
|
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||||
|
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
|
||||||
|
*/
|
||||||
|
template <typename Encoding, typename OutputByteStream>
|
||||||
|
class EncodedOutputStream {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
public:
|
||||||
|
typedef typename Encoding::Ch Ch;
|
||||||
|
|
||||||
|
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
|
||||||
|
if (putBOM)
|
||||||
|
Encoding::PutBOM(os_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Put(Ch c) { Encoding::Put(os_, c); }
|
||||||
|
void Flush() { os_.Flush(); }
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
Ch Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0;}
|
||||||
|
Ch Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0;}
|
||||||
|
size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EncodedOutputStream(const EncodedOutputStream&);
|
||||||
|
EncodedOutputStream& operator=(const EncodedOutputStream&);
|
||||||
|
|
||||||
|
OutputByteStream& os_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||||
|
|
||||||
|
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||||
|
/*!
|
||||||
|
\tparam CharType Type of character for reading.
|
||||||
|
\tparam InputByteStream type of input byte stream to be wrapped.
|
||||||
|
*/
|
||||||
|
template <typename CharType, typename InputByteStream>
|
||||||
|
class AutoUTFInputStream {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
public:
|
||||||
|
typedef CharType Ch;
|
||||||
|
|
||||||
|
//! Constructor.
|
||||||
|
/*!
|
||||||
|
\param is input stream to be wrapped.
|
||||||
|
\param type UTF encoding type if it is not detected from the stream.
|
||||||
|
*/
|
||||||
|
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||||
|
DetectType();
|
||||||
|
static const TakeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||||
|
takeFunc_ = f[type_];
|
||||||
|
current_ = takeFunc_(*is_);
|
||||||
|
}
|
||||||
|
|
||||||
|
UTFType GetType() const { return type_; }
|
||||||
|
bool HasBOM() const { return hasBOM_; }
|
||||||
|
|
||||||
|
Ch Peek() const { return current_; }
|
||||||
|
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
|
||||||
|
size_t Tell() const { return is_->Tell(); }
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
void Flush() { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoUTFInputStream(const AutoUTFInputStream&);
|
||||||
|
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
|
||||||
|
|
||||||
|
// Detect encoding type with BOM or RFC 4627
|
||||||
|
void DetectType() {
|
||||||
|
// BOM (Byte Order Mark):
|
||||||
|
// 00 00 FE FF UTF-32BE
|
||||||
|
// FF FE 00 00 UTF-32LE
|
||||||
|
// FE FF UTF-16BE
|
||||||
|
// FF FE UTF-16LE
|
||||||
|
// EF BB BF UTF-8
|
||||||
|
|
||||||
|
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
|
||||||
|
hasBOM_ = false;
|
||||||
|
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||||
|
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||||
|
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||||
|
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||||
|
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
|
||||||
|
|
||||||
|
// RFC 4627: Section 3
|
||||||
|
// "Since the first two characters of a JSON text will always be ASCII
|
||||||
|
// characters [RFC0020], it is possible to determine whether an octet
|
||||||
|
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||||
|
// at the pattern of nulls in the first four octets."
|
||||||
|
// 00 00 00 xx UTF-32BE
|
||||||
|
// 00 xx 00 xx UTF-16BE
|
||||||
|
// xx 00 00 00 UTF-32LE
|
||||||
|
// xx 00 xx 00 UTF-16LE
|
||||||
|
// xx xx xx xx UTF-8
|
||||||
|
|
||||||
|
if (!hasBOM_) {
|
||||||
|
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
|
||||||
|
switch (pattern) {
|
||||||
|
case 0x08: type_ = kUTF32BE; break;
|
||||||
|
case 0x0A: type_ = kUTF16BE; break;
|
||||||
|
case 0x01: type_ = kUTF32LE; break;
|
||||||
|
case 0x05: type_ = kUTF16LE; break;
|
||||||
|
case 0x0F: type_ = kUTF8; break;
|
||||||
|
default: break; // Use type defined by user.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||||
|
if (type_ == kUTF16LE || type_ == kUTF16BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||||
|
if (type_ == kUTF32LE || type_ == kUTF32BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||||
|
InputByteStream* is_;
|
||||||
|
UTFType type_;
|
||||||
|
Ch current_;
|
||||||
|
TakeFunc takeFunc_;
|
||||||
|
bool hasBOM_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||||
|
/*!
|
||||||
|
\tparam CharType Type of character for writing.
|
||||||
|
\tparam OutputByteStream type of output byte stream to be wrapped.
|
||||||
|
*/
|
||||||
|
template <typename CharType, typename OutputByteStream>
|
||||||
|
class AutoUTFOutputStream {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
public:
|
||||||
|
typedef CharType Ch;
|
||||||
|
|
||||||
|
//! Constructor.
|
||||||
|
/*!
|
||||||
|
\param os output stream to be wrapped.
|
||||||
|
\param type UTF encoding type.
|
||||||
|
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||||
|
*/
|
||||||
|
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||||
|
|
||||||
|
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||||
|
if (type_ == kUTF16LE || type_ == kUTF16BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||||
|
if (type_ == kUTF32LE || type_ == kUTF32BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||||
|
|
||||||
|
static const PutFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||||
|
putFunc_ = f[type_];
|
||||||
|
|
||||||
|
if (putBOM)
|
||||||
|
PutBOM();
|
||||||
|
}
|
||||||
|
|
||||||
|
UTFType GetType() const { return type_; }
|
||||||
|
|
||||||
|
void Put(Ch c) { putFunc_(*os_, c); }
|
||||||
|
void Flush() { os_->Flush(); }
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
Ch Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0;}
|
||||||
|
Ch Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0;}
|
||||||
|
size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoUTFOutputStream(const AutoUTFOutputStream&);
|
||||||
|
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
|
||||||
|
|
||||||
|
void PutBOM() {
|
||||||
|
typedef void (*PutBOMFunc)(OutputByteStream&);
|
||||||
|
static const PutBOMFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
|
||||||
|
f[type_](*os_);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*PutFunc)(OutputByteStream&, Ch);
|
||||||
|
|
||||||
|
OutputByteStream* os_;
|
||||||
|
UTFType type_;
|
||||||
|
PutFunc putFunc_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef CEREAL_RAPIDJSON_ENCODINGS_FUNC
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_FILESTREAM_H_
|
||||||
716
external/cereal/external/rapidjson/encodings.h
vendored
Normal file
716
external/cereal/external/rapidjson/encodings.h
vendored
Normal file
@@ -0,0 +1,716 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ENCODINGS_H_
|
||||||
|
#define CEREAL_RAPIDJSON_ENCODINGS_H_
|
||||||
|
|
||||||
|
#include "rapidjson.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(overflow)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Encoding
|
||||||
|
|
||||||
|
/*! \class rapidjson::Encoding
|
||||||
|
\brief Concept for encoding of Unicode characters.
|
||||||
|
|
||||||
|
\code
|
||||||
|
concept Encoding {
|
||||||
|
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
|
||||||
|
|
||||||
|
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
|
||||||
|
|
||||||
|
//! \brief Encode a Unicode codepoint to an output stream.
|
||||||
|
//! \param os Output stream.
|
||||||
|
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void Encode(OutputStream& os, unsigned codepoint);
|
||||||
|
|
||||||
|
//! \brief Decode a Unicode codepoint from an input stream.
|
||||||
|
//! \param is Input stream.
|
||||||
|
//! \param codepoint Output of the unicode codepoint.
|
||||||
|
//! \return true if a valid codepoint can be decoded from the stream.
|
||||||
|
template <typename InputStream>
|
||||||
|
static bool Decode(InputStream& is, unsigned* codepoint);
|
||||||
|
|
||||||
|
//! \brief Validate one Unicode codepoint from an encoded stream.
|
||||||
|
//! \param is Input stream to obtain codepoint.
|
||||||
|
//! \param os Output for copying one codepoint.
|
||||||
|
//! \return true if it is valid.
|
||||||
|
//! \note This function just validating and copying the codepoint without actually decode it.
|
||||||
|
template <typename InputStream, typename OutputStream>
|
||||||
|
static bool Validate(InputStream& is, OutputStream& os);
|
||||||
|
|
||||||
|
// The following functions are deal with byte streams.
|
||||||
|
|
||||||
|
//! Take a character from input byte stream, skip BOM if exist.
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is);
|
||||||
|
|
||||||
|
//! Take a character from input byte stream.
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static Ch Take(InputByteStream& is);
|
||||||
|
|
||||||
|
//! Put BOM to output byte stream.
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os);
|
||||||
|
|
||||||
|
//! Put a character to output byte stream.
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, Ch c);
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UTF8
|
||||||
|
|
||||||
|
//! UTF-8 encoding.
|
||||||
|
/*! http://en.wikipedia.org/wiki/UTF-8
|
||||||
|
http://tools.ietf.org/html/rfc3629
|
||||||
|
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
|
||||||
|
\note implements Encoding concept
|
||||||
|
*/
|
||||||
|
template<typename CharType = char>
|
||||||
|
struct UTF8 {
|
||||||
|
typedef CharType Ch;
|
||||||
|
|
||||||
|
enum { supportUnicode = 1 };
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||||
|
if (codepoint <= 0x7F)
|
||||||
|
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||||
|
else if (codepoint <= 0x7FF) {
|
||||||
|
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||||
|
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||||
|
}
|
||||||
|
else if (codepoint <= 0xFFFF) {
|
||||||
|
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||||
|
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||||
|
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||||
|
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||||
|
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||||
|
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||||
|
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||||
|
if (codepoint <= 0x7F)
|
||||||
|
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||||
|
else if (codepoint <= 0x7FF) {
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||||
|
}
|
||||||
|
else if (codepoint <= 0xFFFF) {
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||||
|
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||||
|
#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
|
||||||
|
#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||||
|
#define TAIL() COPY(); TRANS(0x70)
|
||||||
|
typename InputStream::Ch c = is.Take();
|
||||||
|
if (!(c & 0x80)) {
|
||||||
|
*codepoint = static_cast<unsigned char>(c);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||||
|
if (type >= 32) {
|
||||||
|
*codepoint = 0;
|
||||||
|
} else {
|
||||||
|
*codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
|
||||||
|
}
|
||||||
|
bool result = true;
|
||||||
|
switch (type) {
|
||||||
|
case 2: TAIL(); return result;
|
||||||
|
case 3: TAIL(); TAIL(); return result;
|
||||||
|
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||||
|
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||||
|
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||||
|
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||||
|
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
#undef COPY
|
||||||
|
#undef TRANS
|
||||||
|
#undef TAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream, typename OutputStream>
|
||||||
|
static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
#define COPY() os.Put(c = is.Take())
|
||||||
|
#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||||
|
#define TAIL() COPY(); TRANS(0x70)
|
||||||
|
Ch c;
|
||||||
|
COPY();
|
||||||
|
if (!(c & 0x80))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
switch (GetRange(static_cast<unsigned char>(c))) {
|
||||||
|
case 2: TAIL(); return result;
|
||||||
|
case 3: TAIL(); TAIL(); return result;
|
||||||
|
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||||
|
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||||
|
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||||
|
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||||
|
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
#undef COPY
|
||||||
|
#undef TRANS
|
||||||
|
#undef TAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char GetRange(unsigned char c) {
|
||||||
|
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||||
|
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
|
||||||
|
static const unsigned char type[] = {
|
||||||
|
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,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,0,0,0,0,0,0,0,0,
|
||||||
|
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
|
||||||
|
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||||
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||||
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||||
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||||
|
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||||
|
};
|
||||||
|
return type[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
typename InputByteStream::Ch c = Take(is);
|
||||||
|
if (static_cast<unsigned char>(c) != 0xEFu) return c;
|
||||||
|
c = is.Take();
|
||||||
|
if (static_cast<unsigned char>(c) != 0xBBu) return c;
|
||||||
|
c = is.Take();
|
||||||
|
if (static_cast<unsigned char>(c) != 0xBFu) return c;
|
||||||
|
c = is.Take();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static Ch Take(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
return static_cast<Ch>(is.Take());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, Ch c) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UTF16
|
||||||
|
|
||||||
|
//! UTF-16 encoding.
|
||||||
|
/*! http://en.wikipedia.org/wiki/UTF-16
|
||||||
|
http://tools.ietf.org/html/rfc2781
|
||||||
|
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
|
||||||
|
\note implements Encoding concept
|
||||||
|
|
||||||
|
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||||
|
For streaming, use UTF16LE and UTF16BE, which handle endianness.
|
||||||
|
*/
|
||||||
|
template<typename CharType = wchar_t>
|
||||||
|
struct UTF16 {
|
||||||
|
typedef CharType Ch;
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||||
|
|
||||||
|
enum { supportUnicode = 1 };
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||||
|
if (codepoint <= 0xFFFF) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||||
|
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||||
|
unsigned v = codepoint - 0x10000;
|
||||||
|
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||||
|
os.Put((v & 0x3FF) | 0xDC00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||||
|
if (codepoint <= 0xFFFF) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||||
|
PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||||
|
unsigned v = codepoint - 0x10000;
|
||||||
|
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||||
|
PutUnsafe(os, (v & 0x3FF) | 0xDC00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||||
|
typename InputStream::Ch c = is.Take();
|
||||||
|
if (c < 0xD800 || c > 0xDFFF) {
|
||||||
|
*codepoint = static_cast<unsigned>(c);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (c <= 0xDBFF) {
|
||||||
|
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
|
||||||
|
c = is.Take();
|
||||||
|
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
|
||||||
|
*codepoint += 0x10000;
|
||||||
|
return c >= 0xDC00 && c <= 0xDFFF;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream, typename OutputStream>
|
||||||
|
static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||||
|
typename InputStream::Ch c;
|
||||||
|
os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
|
||||||
|
if (c < 0xD800 || c > 0xDFFF)
|
||||||
|
return true;
|
||||||
|
else if (c <= 0xDBFF) {
|
||||||
|
os.Put(c = is.Take());
|
||||||
|
return c >= 0xDC00 && c <= 0xDFFF;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! UTF-16 little endian encoding.
|
||||||
|
template<typename CharType = wchar_t>
|
||||||
|
struct UTF16LE : UTF16<CharType> {
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
CharType c = Take(is);
|
||||||
|
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType Take(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
unsigned c = static_cast<uint8_t>(is.Take());
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||||
|
return static_cast<CharType>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, CharType c) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! UTF-16 big endian encoding.
|
||||||
|
template<typename CharType = wchar_t>
|
||||||
|
struct UTF16BE : UTF16<CharType> {
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
CharType c = Take(is);
|
||||||
|
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType Take(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||||
|
c |= static_cast<uint8_t>(is.Take());
|
||||||
|
return static_cast<CharType>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, CharType c) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UTF32
|
||||||
|
|
||||||
|
//! UTF-32 encoding.
|
||||||
|
/*! http://en.wikipedia.org/wiki/UTF-32
|
||||||
|
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
|
||||||
|
\note implements Encoding concept
|
||||||
|
|
||||||
|
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||||
|
For streaming, use UTF32LE and UTF32BE, which handle endianness.
|
||||||
|
*/
|
||||||
|
template<typename CharType = unsigned>
|
||||||
|
struct UTF32 {
|
||||||
|
typedef CharType Ch;
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||||
|
|
||||||
|
enum { supportUnicode = 1 };
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||||
|
os.Put(codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||||
|
PutUnsafe(os, codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||||
|
Ch c = is.Take();
|
||||||
|
*codepoint = c;
|
||||||
|
return c <= 0x10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream, typename OutputStream>
|
||||||
|
static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||||
|
Ch c;
|
||||||
|
os.Put(c = is.Take());
|
||||||
|
return c <= 0x10FFFF;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! UTF-32 little endian enocoding.
|
||||||
|
template<typename CharType = unsigned>
|
||||||
|
struct UTF32LE : UTF32<CharType> {
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
CharType c = Take(is);
|
||||||
|
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType Take(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
unsigned c = static_cast<uint8_t>(is.Take());
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||||
|
return static_cast<CharType>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, CharType c) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! UTF-32 big endian encoding.
|
||||||
|
template<typename CharType = unsigned>
|
||||||
|
struct UTF32BE : UTF32<CharType> {
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
CharType c = Take(is);
|
||||||
|
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType Take(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||||
|
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||||
|
return static_cast<CharType>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, CharType c) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ASCII
|
||||||
|
|
||||||
|
//! ASCII encoding.
|
||||||
|
/*! http://en.wikipedia.org/wiki/ASCII
|
||||||
|
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
|
||||||
|
\note implements Encoding concept
|
||||||
|
*/
|
||||||
|
template<typename CharType = char>
|
||||||
|
struct ASCII {
|
||||||
|
typedef CharType Ch;
|
||||||
|
|
||||||
|
enum { supportUnicode = 0 };
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||||
|
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||||
|
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||||
|
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||||
|
*codepoint = c;
|
||||||
|
return c <= 0X7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream, typename OutputStream>
|
||||||
|
static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||||
|
os.Put(static_cast<typename OutputStream::Ch>(c));
|
||||||
|
return c <= 0x7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static CharType TakeBOM(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
uint8_t c = static_cast<uint8_t>(Take(is));
|
||||||
|
return static_cast<Ch>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputByteStream>
|
||||||
|
static Ch Take(InputByteStream& is) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
|
return static_cast<Ch>(is.Take());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void PutBOM(OutputByteStream& os) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
(void)os;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputByteStream>
|
||||||
|
static void Put(OutputByteStream& os, Ch c) {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||||
|
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// AutoUTF
|
||||||
|
|
||||||
|
//! Runtime-specified UTF encoding type of a stream.
|
||||||
|
enum UTFType {
|
||||||
|
kUTF8 = 0, //!< UTF-8.
|
||||||
|
kUTF16LE = 1, //!< UTF-16 little endian.
|
||||||
|
kUTF16BE = 2, //!< UTF-16 big endian.
|
||||||
|
kUTF32LE = 3, //!< UTF-32 little endian.
|
||||||
|
kUTF32BE = 4 //!< UTF-32 big endian.
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
|
||||||
|
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
|
||||||
|
*/
|
||||||
|
template<typename CharType>
|
||||||
|
struct AutoUTF {
|
||||||
|
typedef CharType Ch;
|
||||||
|
|
||||||
|
enum { supportUnicode = 1 };
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
|
||||||
|
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||||
|
static const EncodeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Encode) };
|
||||||
|
(*f[os.GetType()])(os, codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||||
|
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||||
|
static const EncodeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
|
||||||
|
(*f[os.GetType()])(os, codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||||
|
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||||
|
static const DecodeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Decode) };
|
||||||
|
return (*f[is.GetType()])(is, codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
|
||||||
|
static const ValidateFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Validate) };
|
||||||
|
return (*f[is.GetType()])(is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CEREAL_RAPIDJSON_ENCODINGS_FUNC
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Transcoder
|
||||||
|
|
||||||
|
//! Encoding conversion.
|
||||||
|
template<typename SourceEncoding, typename TargetEncoding>
|
||||||
|
struct Transcoder {
|
||||||
|
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||||
|
template<typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||||
|
unsigned codepoint;
|
||||||
|
if (!SourceEncoding::Decode(is, &codepoint))
|
||||||
|
return false;
|
||||||
|
TargetEncoding::Encode(os, codepoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||||
|
unsigned codepoint;
|
||||||
|
if (!SourceEncoding::Decode(is, &codepoint))
|
||||||
|
return false;
|
||||||
|
TargetEncoding::EncodeUnsafe(os, codepoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Validate one Unicode codepoint from an encoded stream.
|
||||||
|
template<typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Forward declaration.
|
||||||
|
template<typename Stream>
|
||||||
|
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
|
||||||
|
|
||||||
|
//! Specialization of Transcoder with same source and target encoding.
|
||||||
|
template<typename Encoding>
|
||||||
|
struct Transcoder<Encoding, Encoding> {
|
||||||
|
template<typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||||
|
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||||
|
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename InputStream, typename OutputStream>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||||
|
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_ENCODINGS_H_
|
||||||
74
external/cereal/external/rapidjson/error/en.h
vendored
Normal file
74
external/cereal/external/rapidjson/error/en.h
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ERROR_EN_H_
|
||||||
|
#define CEREAL_RAPIDJSON_ERROR_EN_H_
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(switch-enum)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(covered-switch-default)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Maps error code of parsing into error message.
|
||||||
|
/*!
|
||||||
|
\ingroup CEREAL_RAPIDJSON_ERRORS
|
||||||
|
\param parseErrorCode Error code obtained in parsing.
|
||||||
|
\return the error message.
|
||||||
|
\note User can make a copy of this function for localization.
|
||||||
|
Using switch-case is safer for future modification of error codes.
|
||||||
|
*/
|
||||||
|
inline const CEREAL_RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
|
||||||
|
switch (parseErrorCode) {
|
||||||
|
case kParseErrorNone: return CEREAL_RAPIDJSON_ERROR_STRING("No error.");
|
||||||
|
|
||||||
|
case kParseErrorDocumentEmpty: return CEREAL_RAPIDJSON_ERROR_STRING("The document is empty.");
|
||||||
|
case kParseErrorDocumentRootNotSingular: return CEREAL_RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
|
||||||
|
|
||||||
|
case kParseErrorValueInvalid: return CEREAL_RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||||
|
|
||||||
|
case kParseErrorObjectMissName: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
||||||
|
case kParseErrorObjectMissColon: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
||||||
|
case kParseErrorObjectMissCommaOrCurlyBracket: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
||||||
|
|
||||||
|
case kParseErrorArrayMissCommaOrSquareBracket: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
||||||
|
|
||||||
|
case kParseErrorStringUnicodeEscapeInvalidHex: return CEREAL_RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
||||||
|
case kParseErrorStringUnicodeSurrogateInvalid: return CEREAL_RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
|
||||||
|
case kParseErrorStringEscapeInvalid: return CEREAL_RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
|
||||||
|
case kParseErrorStringMissQuotationMark: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
|
||||||
|
case kParseErrorStringInvalidEncoding: return CEREAL_RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
|
||||||
|
|
||||||
|
case kParseErrorNumberTooBig: return CEREAL_RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
|
||||||
|
case kParseErrorNumberMissFraction: return CEREAL_RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
|
||||||
|
case kParseErrorNumberMissExponent: return CEREAL_RAPIDJSON_ERROR_STRING("Miss exponent in number.");
|
||||||
|
|
||||||
|
case kParseErrorTermination: return CEREAL_RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||||
|
case kParseErrorUnspecificSyntaxError: return CEREAL_RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||||
|
|
||||||
|
default: return CEREAL_RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_ERROR_EN_H_
|
||||||
155
external/cereal/external/rapidjson/error/error.h
vendored
Normal file
155
external/cereal/external/rapidjson/error/error.h
vendored
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ERROR_ERROR_H_
|
||||||
|
#define CEREAL_RAPIDJSON_ERROR_ERROR_H_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! \file error.h */
|
||||||
|
|
||||||
|
/*! \defgroup CEREAL_RAPIDJSON_ERRORS RapidJSON error handling */
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_ERROR_CHARTYPE
|
||||||
|
|
||||||
|
//! Character type of error messages.
|
||||||
|
/*! \ingroup CEREAL_RAPIDJSON_ERRORS
|
||||||
|
The default character type is \c char.
|
||||||
|
On Windows, user can define this macro as \c TCHAR for supporting both
|
||||||
|
unicode/non-unicode settings.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ERROR_CHARTYPE
|
||||||
|
#define CEREAL_RAPIDJSON_ERROR_CHARTYPE char
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_ERROR_STRING
|
||||||
|
|
||||||
|
//! Macro for converting string literial to \ref CEREAL_RAPIDJSON_ERROR_CHARTYPE[].
|
||||||
|
/*! \ingroup CEREAL_RAPIDJSON_ERRORS
|
||||||
|
By default this conversion macro does nothing.
|
||||||
|
On Windows, user can define this macro as \c _T(x) for supporting both
|
||||||
|
unicode/non-unicode settings.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ERROR_STRING
|
||||||
|
#define CEREAL_RAPIDJSON_ERROR_STRING(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ParseErrorCode
|
||||||
|
|
||||||
|
//! Error code of parsing.
|
||||||
|
/*! \ingroup CEREAL_RAPIDJSON_ERRORS
|
||||||
|
\see GenericReader::Parse, GenericReader::GetParseErrorCode
|
||||||
|
*/
|
||||||
|
enum ParseErrorCode {
|
||||||
|
kParseErrorNone = 0, //!< No error.
|
||||||
|
|
||||||
|
kParseErrorDocumentEmpty, //!< The document is empty.
|
||||||
|
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
|
||||||
|
|
||||||
|
kParseErrorValueInvalid, //!< Invalid value.
|
||||||
|
|
||||||
|
kParseErrorObjectMissName, //!< Missing a name for object member.
|
||||||
|
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
|
||||||
|
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
|
||||||
|
|
||||||
|
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
|
||||||
|
|
||||||
|
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
|
||||||
|
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
|
||||||
|
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
|
||||||
|
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
|
||||||
|
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
|
||||||
|
|
||||||
|
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
|
||||||
|
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
|
||||||
|
kParseErrorNumberMissExponent, //!< Miss exponent in number.
|
||||||
|
|
||||||
|
kParseErrorTermination, //!< Parsing was terminated.
|
||||||
|
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Result of parsing (wraps ParseErrorCode)
|
||||||
|
/*!
|
||||||
|
\ingroup CEREAL_RAPIDJSON_ERRORS
|
||||||
|
\code
|
||||||
|
Document doc;
|
||||||
|
ParseResult ok = doc.Parse("[42]");
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "JSON parse error: %s (%u)",
|
||||||
|
GetParseError_En(ok.Code()), ok.Offset());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
\see GenericReader::Parse, GenericDocument::Parse
|
||||||
|
*/
|
||||||
|
struct ParseResult {
|
||||||
|
public:
|
||||||
|
//! Default constructor, no error.
|
||||||
|
ParseResult() : code_(kParseErrorNone), offset_(0) {}
|
||||||
|
//! Constructor to set an error.
|
||||||
|
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
|
||||||
|
|
||||||
|
//! Get the error code.
|
||||||
|
ParseErrorCode Code() const { return code_; }
|
||||||
|
//! Get the error offset, if \ref IsError(), 0 otherwise.
|
||||||
|
size_t Offset() const { return offset_; }
|
||||||
|
|
||||||
|
//! Conversion to \c bool, returns \c true, iff !\ref IsError().
|
||||||
|
operator bool() const { return !IsError(); }
|
||||||
|
//! Whether the result is an error.
|
||||||
|
bool IsError() const { return code_ != kParseErrorNone; }
|
||||||
|
|
||||||
|
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
|
||||||
|
bool operator==(ParseErrorCode code) const { return code_ == code; }
|
||||||
|
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
|
||||||
|
|
||||||
|
//! Reset error code.
|
||||||
|
void Clear() { Set(kParseErrorNone); }
|
||||||
|
//! Update error code and offset.
|
||||||
|
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParseErrorCode code_;
|
||||||
|
size_t offset_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Function pointer type of GetParseError().
|
||||||
|
/*! \ingroup CEREAL_RAPIDJSON_ERRORS
|
||||||
|
|
||||||
|
This is the prototype for \c GetParseError_X(), where \c X is a locale.
|
||||||
|
User can dynamically change locale in runtime, e.g.:
|
||||||
|
\code
|
||||||
|
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
|
||||||
|
const CEREAL_RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
typedef const CEREAL_RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_ERROR_ERROR_H_
|
||||||
99
external/cereal/external/rapidjson/filereadstream.h
vendored
Normal file
99
external/cereal/external/rapidjson/filereadstream.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_FILEREADSTREAM_H_
|
||||||
|
#define CEREAL_RAPIDJSON_FILEREADSTREAM_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(missing-noreturn)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! File byte stream for input using fread().
|
||||||
|
/*!
|
||||||
|
\note implements Stream concept
|
||||||
|
*/
|
||||||
|
class FileReadStream {
|
||||||
|
public:
|
||||||
|
typedef char Ch; //!< Character type (byte).
|
||||||
|
|
||||||
|
//! Constructor.
|
||||||
|
/*!
|
||||||
|
\param fp File pointer opened for read.
|
||||||
|
\param buffer user-supplied buffer.
|
||||||
|
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||||
|
*/
|
||||||
|
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(fp_ != 0);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||||
|
Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ch Peek() const { return *current_; }
|
||||||
|
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||||
|
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
void Flush() { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
// For encoding detection only.
|
||||||
|
const Ch* Peek4() const {
|
||||||
|
return (current_ + 4 <= bufferLast_) ? current_ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Read() {
|
||||||
|
if (current_ < bufferLast_)
|
||||||
|
++current_;
|
||||||
|
else if (!eof_) {
|
||||||
|
count_ += readCount_;
|
||||||
|
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
|
||||||
|
bufferLast_ = buffer_ + readCount_ - 1;
|
||||||
|
current_ = buffer_;
|
||||||
|
|
||||||
|
if (readCount_ < bufferSize_) {
|
||||||
|
buffer_[readCount_] = '\0';
|
||||||
|
++bufferLast_;
|
||||||
|
eof_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::FILE* fp_;
|
||||||
|
Ch *buffer_;
|
||||||
|
size_t bufferSize_;
|
||||||
|
Ch *bufferLast_;
|
||||||
|
Ch *current_;
|
||||||
|
size_t readCount_;
|
||||||
|
size_t count_; //!< Number of characters read
|
||||||
|
bool eof_;
|
||||||
|
};
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_FILESTREAM_H_
|
||||||
104
external/cereal/external/rapidjson/filewritestream.h
vendored
Normal file
104
external/cereal/external/rapidjson/filewritestream.h
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_FILEWRITESTREAM_H_
|
||||||
|
#define CEREAL_RAPIDJSON_FILEWRITESTREAM_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Wrapper of C file stream for input using fread().
|
||||||
|
/*!
|
||||||
|
\note implements Stream concept
|
||||||
|
*/
|
||||||
|
class FileWriteStream {
|
||||||
|
public:
|
||||||
|
typedef char Ch; //!< Character type. Only support char.
|
||||||
|
|
||||||
|
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(fp_ != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Put(char c) {
|
||||||
|
if (current_ >= bufferEnd_)
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
*current_++ = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PutN(char c, size_t n) {
|
||||||
|
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||||
|
while (n > avail) {
|
||||||
|
std::memset(current_, c, avail);
|
||||||
|
current_ += avail;
|
||||||
|
Flush();
|
||||||
|
n -= avail;
|
||||||
|
avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
std::memset(current_, c, n);
|
||||||
|
current_ += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flush() {
|
||||||
|
if (current_ != buffer_) {
|
||||||
|
size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
||||||
|
if (result < static_cast<size_t>(current_ - buffer_)) {
|
||||||
|
// failure deliberately ignored at this time
|
||||||
|
// added to avoid warn_unused_result build errors
|
||||||
|
}
|
||||||
|
current_ = buffer_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
char Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
char Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
char* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(char*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Prohibit copy constructor & assignment operator.
|
||||||
|
FileWriteStream(const FileWriteStream&);
|
||||||
|
FileWriteStream& operator=(const FileWriteStream&);
|
||||||
|
|
||||||
|
std::FILE* fp_;
|
||||||
|
char *buffer_;
|
||||||
|
char *bufferEnd_;
|
||||||
|
char *current_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Implement specialized version of PutN() with memset() for better performance.
|
||||||
|
template<>
|
||||||
|
inline void PutN(FileWriteStream& stream, char c, size_t n) {
|
||||||
|
stream.PutN(c, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_FILESTREAM_H_
|
||||||
151
external/cereal/external/rapidjson/fwd.h
vendored
Normal file
151
external/cereal/external/rapidjson/fwd.h
vendored
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_FWD_H_
|
||||||
|
#define CEREAL_RAPIDJSON_FWD_H_
|
||||||
|
|
||||||
|
#include "rapidjson.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// encodings.h
|
||||||
|
|
||||||
|
template<typename CharType> struct UTF8;
|
||||||
|
template<typename CharType> struct UTF16;
|
||||||
|
template<typename CharType> struct UTF16BE;
|
||||||
|
template<typename CharType> struct UTF16LE;
|
||||||
|
template<typename CharType> struct UTF32;
|
||||||
|
template<typename CharType> struct UTF32BE;
|
||||||
|
template<typename CharType> struct UTF32LE;
|
||||||
|
template<typename CharType> struct ASCII;
|
||||||
|
template<typename CharType> struct AutoUTF;
|
||||||
|
|
||||||
|
template<typename SourceEncoding, typename TargetEncoding>
|
||||||
|
struct Transcoder;
|
||||||
|
|
||||||
|
// allocators.h
|
||||||
|
|
||||||
|
class CrtAllocator;
|
||||||
|
|
||||||
|
template <typename BaseAllocator>
|
||||||
|
class MemoryPoolAllocator;
|
||||||
|
|
||||||
|
// stream.h
|
||||||
|
|
||||||
|
template <typename Encoding>
|
||||||
|
struct GenericStringStream;
|
||||||
|
|
||||||
|
typedef GenericStringStream<UTF8<char> > StringStream;
|
||||||
|
|
||||||
|
template <typename Encoding>
|
||||||
|
struct GenericInsituStringStream;
|
||||||
|
|
||||||
|
typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
|
||||||
|
|
||||||
|
// stringbuffer.h
|
||||||
|
|
||||||
|
template <typename Encoding, typename Allocator>
|
||||||
|
class GenericStringBuffer;
|
||||||
|
|
||||||
|
typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
|
||||||
|
|
||||||
|
// filereadstream.h
|
||||||
|
|
||||||
|
class FileReadStream;
|
||||||
|
|
||||||
|
// filewritestream.h
|
||||||
|
|
||||||
|
class FileWriteStream;
|
||||||
|
|
||||||
|
// memorybuffer.h
|
||||||
|
|
||||||
|
template <typename Allocator>
|
||||||
|
struct GenericMemoryBuffer;
|
||||||
|
|
||||||
|
typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
|
||||||
|
|
||||||
|
// memorystream.h
|
||||||
|
|
||||||
|
struct MemoryStream;
|
||||||
|
|
||||||
|
// reader.h
|
||||||
|
|
||||||
|
template<typename Encoding, typename Derived>
|
||||||
|
struct BaseReaderHandler;
|
||||||
|
|
||||||
|
template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
|
||||||
|
class GenericReader;
|
||||||
|
|
||||||
|
typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
|
||||||
|
|
||||||
|
// writer.h
|
||||||
|
|
||||||
|
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||||
|
class Writer;
|
||||||
|
|
||||||
|
// prettywriter.h
|
||||||
|
|
||||||
|
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||||
|
class PrettyWriter;
|
||||||
|
|
||||||
|
// document.h
|
||||||
|
|
||||||
|
template <typename Encoding, typename Allocator>
|
||||||
|
struct GenericMember;
|
||||||
|
|
||||||
|
template <bool Const, typename Encoding, typename Allocator>
|
||||||
|
class GenericMemberIterator;
|
||||||
|
|
||||||
|
template<typename CharType>
|
||||||
|
struct GenericStringRef;
|
||||||
|
|
||||||
|
template <typename Encoding, typename Allocator>
|
||||||
|
class GenericValue;
|
||||||
|
|
||||||
|
typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
|
||||||
|
|
||||||
|
template <typename Encoding, typename Allocator, typename StackAllocator>
|
||||||
|
class GenericDocument;
|
||||||
|
|
||||||
|
typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
|
||||||
|
|
||||||
|
// pointer.h
|
||||||
|
|
||||||
|
template <typename ValueType, typename Allocator>
|
||||||
|
class GenericPointer;
|
||||||
|
|
||||||
|
typedef GenericPointer<Value, CrtAllocator> Pointer;
|
||||||
|
|
||||||
|
// schema.h
|
||||||
|
|
||||||
|
template <typename SchemaDocumentType>
|
||||||
|
class IGenericRemoteSchemaDocumentProvider;
|
||||||
|
|
||||||
|
template <typename ValueT, typename Allocator>
|
||||||
|
class GenericSchemaDocument;
|
||||||
|
|
||||||
|
typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
|
||||||
|
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename SchemaDocumentType,
|
||||||
|
typename OutputHandler,
|
||||||
|
typename StateAllocator>
|
||||||
|
class GenericSchemaValidator;
|
||||||
|
|
||||||
|
typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_RAPIDJSONFWD_H_
|
||||||
290
external/cereal/external/rapidjson/internal/biginteger.h
vendored
Normal file
290
external/cereal/external/rapidjson/internal/biginteger.h
vendored
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_BIGINTEGER_H_
|
||||||
|
#define CEREAL_RAPIDJSON_BIGINTEGER_H_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
#include <intrin.h> // for _umul128
|
||||||
|
#pragma intrinsic(_umul128)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class BigInteger {
|
||||||
|
public:
|
||||||
|
typedef uint64_t Type;
|
||||||
|
|
||||||
|
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
|
||||||
|
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit BigInteger(uint64_t u) : count_(1) {
|
||||||
|
digits_[0] = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger(const char* decimals, size_t length) : count_(1) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(length > 0);
|
||||||
|
digits_[0] = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
|
||||||
|
while (length >= kMaxDigitPerIteration) {
|
||||||
|
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
|
||||||
|
length -= kMaxDigitPerIteration;
|
||||||
|
i += kMaxDigitPerIteration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > 0)
|
||||||
|
AppendDecimal64(decimals + i, decimals + i + length);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& operator=(const BigInteger &rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs) {
|
||||||
|
count_ = rhs.count_;
|
||||||
|
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& operator=(uint64_t u) {
|
||||||
|
digits_[0] = u;
|
||||||
|
count_ = 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& operator+=(uint64_t u) {
|
||||||
|
Type backup = digits_[0];
|
||||||
|
digits_[0] += u;
|
||||||
|
for (size_t i = 0; i < count_ - 1; i++) {
|
||||||
|
if (digits_[i] >= backup)
|
||||||
|
return *this; // no carry
|
||||||
|
backup = digits_[i + 1];
|
||||||
|
digits_[i + 1] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last carry
|
||||||
|
if (digits_[count_ - 1] < backup)
|
||||||
|
PushBack(1);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& operator*=(uint64_t u) {
|
||||||
|
if (u == 0) return *this = 0;
|
||||||
|
if (u == 1) return *this;
|
||||||
|
if (*this == 1) return *this = u;
|
||||||
|
|
||||||
|
uint64_t k = 0;
|
||||||
|
for (size_t i = 0; i < count_; i++) {
|
||||||
|
uint64_t hi;
|
||||||
|
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
|
||||||
|
k = hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k > 0)
|
||||||
|
PushBack(k);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& operator*=(uint32_t u) {
|
||||||
|
if (u == 0) return *this = 0;
|
||||||
|
if (u == 1) return *this;
|
||||||
|
if (*this == 1) return *this = u;
|
||||||
|
|
||||||
|
uint64_t k = 0;
|
||||||
|
for (size_t i = 0; i < count_; i++) {
|
||||||
|
const uint64_t c = digits_[i] >> 32;
|
||||||
|
const uint64_t d = digits_[i] & 0xFFFFFFFF;
|
||||||
|
const uint64_t uc = u * c;
|
||||||
|
const uint64_t ud = u * d;
|
||||||
|
const uint64_t p0 = ud + k;
|
||||||
|
const uint64_t p1 = uc + (p0 >> 32);
|
||||||
|
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
|
||||||
|
k = p1 >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k > 0)
|
||||||
|
PushBack(k);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& operator<<=(size_t shift) {
|
||||||
|
if (IsZero() || shift == 0) return *this;
|
||||||
|
|
||||||
|
size_t offset = shift / kTypeBit;
|
||||||
|
size_t interShift = shift % kTypeBit;
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
|
||||||
|
|
||||||
|
if (interShift == 0) {
|
||||||
|
std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
|
||||||
|
count_ += offset;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
digits_[count_] = 0;
|
||||||
|
for (size_t i = count_; i > 0; i--)
|
||||||
|
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
|
||||||
|
digits_[offset] = digits_[0] << interShift;
|
||||||
|
count_ += offset;
|
||||||
|
if (digits_[count_])
|
||||||
|
count_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(digits_, 0, offset * sizeof(Type));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const BigInteger& rhs) const {
|
||||||
|
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Type rhs) const {
|
||||||
|
return count_ == 1 && digits_[0] == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger& MultiplyPow5(unsigned exp) {
|
||||||
|
static const uint32_t kPow5[12] = {
|
||||||
|
5,
|
||||||
|
5 * 5,
|
||||||
|
5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||||
|
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
|
||||||
|
};
|
||||||
|
if (exp == 0) return *this;
|
||||||
|
for (; exp >= 27; exp -= 27) *this *= CEREAL_RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
|
||||||
|
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
|
||||||
|
if (exp > 0) *this *= kPow5[exp - 1];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute absolute difference of this and rhs.
|
||||||
|
// Assume this != rhs
|
||||||
|
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
||||||
|
int cmp = Compare(rhs);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(cmp != 0);
|
||||||
|
const BigInteger *a, *b; // Makes a > b
|
||||||
|
bool ret;
|
||||||
|
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
||||||
|
else { a = this; b = &rhs; ret = false; }
|
||||||
|
|
||||||
|
Type borrow = 0;
|
||||||
|
for (size_t i = 0; i < a->count_; i++) {
|
||||||
|
Type d = a->digits_[i] - borrow;
|
||||||
|
if (i < b->count_)
|
||||||
|
d -= b->digits_[i];
|
||||||
|
borrow = (d > a->digits_[i]) ? 1 : 0;
|
||||||
|
out->digits_[i] = d;
|
||||||
|
if (d != 0)
|
||||||
|
out->count_ = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Compare(const BigInteger& rhs) const {
|
||||||
|
if (count_ != rhs.count_)
|
||||||
|
return count_ < rhs.count_ ? -1 : 1;
|
||||||
|
|
||||||
|
for (size_t i = count_; i-- > 0;)
|
||||||
|
if (digits_[i] != rhs.digits_[i])
|
||||||
|
return digits_[i] < rhs.digits_[i] ? -1 : 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetCount() const { return count_; }
|
||||||
|
Type GetDigit(size_t index) const { CEREAL_RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
|
||||||
|
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AppendDecimal64(const char* begin, const char* end) {
|
||||||
|
uint64_t u = ParseUint64(begin, end);
|
||||||
|
if (IsZero())
|
||||||
|
*this = u;
|
||||||
|
else {
|
||||||
|
unsigned exp = static_cast<unsigned>(end - begin);
|
||||||
|
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushBack(Type digit) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(count_ < kCapacity);
|
||||||
|
digits_[count_++] = digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t ParseUint64(const char* begin, const char* end) {
|
||||||
|
uint64_t r = 0;
|
||||||
|
for (const char* p = begin; p != end; ++p) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
|
||||||
|
r = r * 10u + static_cast<unsigned>(*p - '0');
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume a * b + k < 2^128
|
||||||
|
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
uint64_t low = _umul128(a, b, outHigh) + k;
|
||||||
|
if (low < k)
|
||||||
|
(*outHigh)++;
|
||||||
|
return low;
|
||||||
|
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||||
|
__extension__ typedef unsigned __int128 uint128;
|
||||||
|
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
|
||||||
|
p += k;
|
||||||
|
*outHigh = static_cast<uint64_t>(p >> 64);
|
||||||
|
return static_cast<uint64_t>(p);
|
||||||
|
#else
|
||||||
|
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
|
||||||
|
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
|
||||||
|
x1 += (x0 >> 32); // can't give carry
|
||||||
|
x1 += x2;
|
||||||
|
if (x1 < x2)
|
||||||
|
x3 += (static_cast<uint64_t>(1) << 32);
|
||||||
|
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
|
||||||
|
uint64_t hi = x3 + (x1 >> 32);
|
||||||
|
|
||||||
|
lo += k;
|
||||||
|
if (lo < k)
|
||||||
|
hi++;
|
||||||
|
*outHigh = hi;
|
||||||
|
return lo;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
||||||
|
static const size_t kCapacity = kBitCount / sizeof(Type);
|
||||||
|
static const size_t kTypeBit = sizeof(Type) * 8;
|
||||||
|
|
||||||
|
Type digits_[kCapacity];
|
||||||
|
size_t count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_BIGINTEGER_H_
|
||||||
258
external/cereal/external/rapidjson/internal/diyfp.h
vendored
Normal file
258
external/cereal/external/rapidjson/internal/diyfp.h
vendored
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||||
|
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||||
|
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_DIYFP_H_
|
||||||
|
#define CEREAL_RAPIDJSON_DIYFP_H_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
#include <intrin.h>
|
||||||
|
#pragma intrinsic(_BitScanReverse64)
|
||||||
|
#pragma intrinsic(_umul128)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct DiyFp {
|
||||||
|
DiyFp() : f(), e() {}
|
||||||
|
|
||||||
|
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
|
||||||
|
|
||||||
|
explicit DiyFp(double d) {
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
uint64_t u64;
|
||||||
|
} u = { d };
|
||||||
|
|
||||||
|
int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
|
||||||
|
uint64_t significand = (u.u64 & kDpSignificandMask);
|
||||||
|
if (biased_e != 0) {
|
||||||
|
f = significand + kDpHiddenBit;
|
||||||
|
e = biased_e - kDpExponentBias;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f = significand;
|
||||||
|
e = kDpMinExponent + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp operator-(const DiyFp& rhs) const {
|
||||||
|
return DiyFp(f - rhs.f, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp operator*(const DiyFp& rhs) const {
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
uint64_t h;
|
||||||
|
uint64_t l = _umul128(f, rhs.f, &h);
|
||||||
|
if (l & (uint64_t(1) << 63)) // rounding
|
||||||
|
h++;
|
||||||
|
return DiyFp(h, e + rhs.e + 64);
|
||||||
|
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||||
|
__extension__ typedef unsigned __int128 uint128;
|
||||||
|
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
|
||||||
|
uint64_t h = static_cast<uint64_t>(p >> 64);
|
||||||
|
uint64_t l = static_cast<uint64_t>(p);
|
||||||
|
if (l & (uint64_t(1) << 63)) // rounding
|
||||||
|
h++;
|
||||||
|
return DiyFp(h, e + rhs.e + 64);
|
||||||
|
#else
|
||||||
|
const uint64_t M32 = 0xFFFFFFFF;
|
||||||
|
const uint64_t a = f >> 32;
|
||||||
|
const uint64_t b = f & M32;
|
||||||
|
const uint64_t c = rhs.f >> 32;
|
||||||
|
const uint64_t d = rhs.f & M32;
|
||||||
|
const uint64_t ac = a * c;
|
||||||
|
const uint64_t bc = b * c;
|
||||||
|
const uint64_t ad = a * d;
|
||||||
|
const uint64_t bd = b * d;
|
||||||
|
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
|
||||||
|
tmp += 1U << 31; /// mult_round
|
||||||
|
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp Normalize() const {
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
unsigned long index;
|
||||||
|
_BitScanReverse64(&index, f);
|
||||||
|
return DiyFp(f << (63 - index), e - (63 - index));
|
||||||
|
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||||
|
int s = __builtin_clzll(f);
|
||||||
|
return DiyFp(f << s, e - s);
|
||||||
|
#else
|
||||||
|
DiyFp res = *this;
|
||||||
|
while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
|
||||||
|
res.f <<= 1;
|
||||||
|
res.e--;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp NormalizeBoundary() const {
|
||||||
|
DiyFp res = *this;
|
||||||
|
while (!(res.f & (kDpHiddenBit << 1))) {
|
||||||
|
res.f <<= 1;
|
||||||
|
res.e--;
|
||||||
|
}
|
||||||
|
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
|
||||||
|
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
|
||||||
|
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
|
||||||
|
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
|
||||||
|
mi.f <<= mi.e - pl.e;
|
||||||
|
mi.e = pl.e;
|
||||||
|
*plus = pl;
|
||||||
|
*minus = mi;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ToDouble() const {
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
uint64_t u64;
|
||||||
|
}u;
|
||||||
|
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||||
|
static_cast<uint64_t>(e + kDpExponentBias);
|
||||||
|
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
||||||
|
return u.d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kDiySignificandSize = 64;
|
||||||
|
static const int kDpSignificandSize = 52;
|
||||||
|
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||||
|
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
||||||
|
static const int kDpMinExponent = -kDpExponentBias;
|
||||||
|
static const int kDpDenormalExponent = -kDpExponentBias + 1;
|
||||||
|
static const uint64_t kDpExponentMask = CEREAL_RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||||
|
static const uint64_t kDpSignificandMask = CEREAL_RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||||
|
static const uint64_t kDpHiddenBit = CEREAL_RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||||
|
|
||||||
|
uint64_t f;
|
||||||
|
int e;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline DiyFp GetCachedPowerByIndex(size_t index) {
|
||||||
|
// 10^-348, 10^-340, ..., 10^340
|
||||||
|
static const uint64_t kCachedPowers_F[] = {
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), CEREAL_RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), CEREAL_RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), CEREAL_RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), CEREAL_RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), CEREAL_RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), CEREAL_RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), CEREAL_RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), CEREAL_RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), CEREAL_RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), CEREAL_RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), CEREAL_RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), CEREAL_RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), CEREAL_RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), CEREAL_RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), CEREAL_RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), CEREAL_RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), CEREAL_RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), CEREAL_RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), CEREAL_RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), CEREAL_RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), CEREAL_RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), CEREAL_RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), CEREAL_RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), CEREAL_RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), CEREAL_RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), CEREAL_RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), CEREAL_RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), CEREAL_RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), CEREAL_RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), CEREAL_RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), CEREAL_RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), CEREAL_RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), CEREAL_RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), CEREAL_RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), CEREAL_RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), CEREAL_RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), CEREAL_RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), CEREAL_RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), CEREAL_RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), CEREAL_RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), CEREAL_RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), CEREAL_RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), CEREAL_RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
|
||||||
|
CEREAL_RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
|
||||||
|
};
|
||||||
|
static const int16_t kCachedPowers_E[] = {
|
||||||
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
|
||||||
|
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
|
||||||
|
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
|
||||||
|
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
|
||||||
|
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
|
||||||
|
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
|
||||||
|
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
|
||||||
|
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
|
||||||
|
907, 933, 960, 986, 1013, 1039, 1066
|
||||||
|
};
|
||||||
|
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DiyFp GetCachedPower(int e, int* K) {
|
||||||
|
|
||||||
|
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
||||||
|
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
|
||||||
|
int k = static_cast<int>(dk);
|
||||||
|
if (dk - k > 0.0)
|
||||||
|
k++;
|
||||||
|
|
||||||
|
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||||
|
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
|
||||||
|
|
||||||
|
return GetCachedPowerByIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DiyFp GetCachedPower10(int exp, int *outExp) {
|
||||||
|
unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
|
||||||
|
*outExp = -348 + static_cast<int>(index) * 8;
|
||||||
|
return GetCachedPowerByIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_DIYFP_H_
|
||||||
245
external/cereal/external/rapidjson/internal/dtoa.h
vendored
Normal file
245
external/cereal/external/rapidjson/internal/dtoa.h
vendored
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||||
|
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||||
|
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_DTOA_
|
||||||
|
#define CEREAL_RAPIDJSON_DTOA_
|
||||||
|
|
||||||
|
#include "itoa.h" // GetDigitsLut()
|
||||||
|
#include "diyfp.h"
|
||||||
|
#include "ieee754.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
|
||||||
|
while (rest < wp_w && delta - rest >= ten_kappa &&
|
||||||
|
(rest + ten_kappa < wp_w || /// closer
|
||||||
|
wp_w - rest > rest + ten_kappa - wp_w)) {
|
||||||
|
buffer[len - 1]--;
|
||||||
|
rest += ten_kappa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned CountDecimalDigit32(uint32_t n) {
|
||||||
|
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
|
||||||
|
if (n < 10) return 1;
|
||||||
|
if (n < 100) return 2;
|
||||||
|
if (n < 1000) return 3;
|
||||||
|
if (n < 10000) return 4;
|
||||||
|
if (n < 100000) return 5;
|
||||||
|
if (n < 1000000) return 6;
|
||||||
|
if (n < 10000000) return 7;
|
||||||
|
if (n < 100000000) return 8;
|
||||||
|
// Will not reach 10 digits in DigitGen()
|
||||||
|
//if (n < 1000000000) return 9;
|
||||||
|
//return 10;
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
||||||
|
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||||
|
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
||||||
|
const DiyFp wp_w = Mp - W;
|
||||||
|
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||||
|
uint64_t p2 = Mp.f & (one.f - 1);
|
||||||
|
unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
while (kappa > 0) {
|
||||||
|
uint32_t d = 0;
|
||||||
|
switch (kappa) {
|
||||||
|
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
||||||
|
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
||||||
|
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
||||||
|
case 6: d = p1 / 100000; p1 %= 100000; break;
|
||||||
|
case 5: d = p1 / 10000; p1 %= 10000; break;
|
||||||
|
case 4: d = p1 / 1000; p1 %= 1000; break;
|
||||||
|
case 3: d = p1 / 100; p1 %= 100; break;
|
||||||
|
case 2: d = p1 / 10; p1 %= 10; break;
|
||||||
|
case 1: d = p1; p1 = 0; break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
if (d || *len)
|
||||||
|
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
||||||
|
kappa--;
|
||||||
|
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||||
|
if (tmp <= delta) {
|
||||||
|
*K += kappa;
|
||||||
|
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// kappa = 0
|
||||||
|
for (;;) {
|
||||||
|
p2 *= 10;
|
||||||
|
delta *= 10;
|
||||||
|
char d = static_cast<char>(p2 >> -one.e);
|
||||||
|
if (d || *len)
|
||||||
|
buffer[(*len)++] = static_cast<char>('0' + d);
|
||||||
|
p2 &= one.f - 1;
|
||||||
|
kappa--;
|
||||||
|
if (p2 < delta) {
|
||||||
|
*K += kappa;
|
||||||
|
int index = -static_cast<int>(kappa);
|
||||||
|
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Grisu2(double value, char* buffer, int* length, int* K) {
|
||||||
|
const DiyFp v(value);
|
||||||
|
DiyFp w_m, w_p;
|
||||||
|
v.NormalizedBoundaries(&w_m, &w_p);
|
||||||
|
|
||||||
|
const DiyFp c_mk = GetCachedPower(w_p.e, K);
|
||||||
|
const DiyFp W = v.Normalize() * c_mk;
|
||||||
|
DiyFp Wp = w_p * c_mk;
|
||||||
|
DiyFp Wm = w_m * c_mk;
|
||||||
|
Wm.f++;
|
||||||
|
Wp.f--;
|
||||||
|
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* WriteExponent(int K, char* buffer) {
|
||||||
|
if (K < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
K = -K;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (K >= 100) {
|
||||||
|
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
|
||||||
|
K %= 100;
|
||||||
|
const char* d = GetDigitsLut() + K * 2;
|
||||||
|
*buffer++ = d[0];
|
||||||
|
*buffer++ = d[1];
|
||||||
|
}
|
||||||
|
else if (K >= 10) {
|
||||||
|
const char* d = GetDigitsLut() + K * 2;
|
||||||
|
*buffer++ = d[0];
|
||||||
|
*buffer++ = d[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
|
||||||
|
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||||
|
|
||||||
|
if (0 <= k && kk <= 21) {
|
||||||
|
// 1234e7 -> 12340000000
|
||||||
|
for (int i = length; i < kk; i++)
|
||||||
|
buffer[i] = '0';
|
||||||
|
buffer[kk] = '.';
|
||||||
|
buffer[kk + 1] = '0';
|
||||||
|
return &buffer[kk + 2];
|
||||||
|
}
|
||||||
|
else if (0 < kk && kk <= 21) {
|
||||||
|
// 1234e-2 -> 12.34
|
||||||
|
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
|
||||||
|
buffer[kk] = '.';
|
||||||
|
if (0 > k + maxDecimalPlaces) {
|
||||||
|
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
|
||||||
|
// Remove extra trailing zeros (at least one) after truncation.
|
||||||
|
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
|
||||||
|
if (buffer[i] != '0')
|
||||||
|
return &buffer[i + 1];
|
||||||
|
return &buffer[kk + 2]; // Reserve one zero
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return &buffer[length + 1];
|
||||||
|
}
|
||||||
|
else if (-6 < kk && kk <= 0) {
|
||||||
|
// 1234e-6 -> 0.001234
|
||||||
|
const int offset = 2 - kk;
|
||||||
|
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '.';
|
||||||
|
for (int i = 2; i < offset; i++)
|
||||||
|
buffer[i] = '0';
|
||||||
|
if (length - kk > maxDecimalPlaces) {
|
||||||
|
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
|
||||||
|
// Remove extra trailing zeros (at least one) after truncation.
|
||||||
|
for (int i = maxDecimalPlaces + 1; i > 2; i--)
|
||||||
|
if (buffer[i] != '0')
|
||||||
|
return &buffer[i + 1];
|
||||||
|
return &buffer[3]; // Reserve one zero
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return &buffer[length + offset];
|
||||||
|
}
|
||||||
|
else if (kk < -maxDecimalPlaces) {
|
||||||
|
// Truncate to zero
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '.';
|
||||||
|
buffer[2] = '0';
|
||||||
|
return &buffer[3];
|
||||||
|
}
|
||||||
|
else if (length == 1) {
|
||||||
|
// 1e30
|
||||||
|
buffer[1] = 'e';
|
||||||
|
return WriteExponent(kk - 1, &buffer[2]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 1234e30 -> 1.234e33
|
||||||
|
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
|
||||||
|
buffer[1] = '.';
|
||||||
|
buffer[length + 1] = 'e';
|
||||||
|
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
|
||||||
|
Double d(value);
|
||||||
|
if (d.IsZero()) {
|
||||||
|
if (d.Sign())
|
||||||
|
*buffer++ = '-'; // -0.0, Issue #289
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '.';
|
||||||
|
buffer[2] = '0';
|
||||||
|
return &buffer[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (value < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
int length, K;
|
||||||
|
Grisu2(value, buffer, &length, &K);
|
||||||
|
return Prettify(buffer, length, K, maxDecimalPlaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_DTOA_
|
||||||
78
external/cereal/external/rapidjson/internal/ieee754.h
vendored
Normal file
78
external/cereal/external/rapidjson/internal/ieee754.h
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_IEEE754_
|
||||||
|
#define CEREAL_RAPIDJSON_IEEE754_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class Double {
|
||||||
|
public:
|
||||||
|
Double() {}
|
||||||
|
Double(double d) : d_(d) {}
|
||||||
|
Double(uint64_t u) : u_(u) {}
|
||||||
|
|
||||||
|
double Value() const { return d_; }
|
||||||
|
uint64_t Uint64Value() const { return u_; }
|
||||||
|
|
||||||
|
double NextPositiveDouble() const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(!Sign());
|
||||||
|
return Double(u_ + 1).Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sign() const { return (u_ & kSignMask) != 0; }
|
||||||
|
uint64_t Significand() const { return u_ & kSignificandMask; }
|
||||||
|
int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
|
||||||
|
|
||||||
|
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
|
||||||
|
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
|
||||||
|
bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
|
||||||
|
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
|
||||||
|
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
|
||||||
|
|
||||||
|
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
|
||||||
|
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
|
||||||
|
uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
|
||||||
|
|
||||||
|
static unsigned EffectiveSignificandSize(int order) {
|
||||||
|
if (order >= -1021)
|
||||||
|
return 53;
|
||||||
|
else if (order <= -1074)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return static_cast<unsigned>(order) + 1074;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int kSignificandSize = 52;
|
||||||
|
static const int kExponentBias = 0x3FF;
|
||||||
|
static const int kDenormalExponent = 1 - kExponentBias;
|
||||||
|
static const uint64_t kSignMask = CEREAL_RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
|
||||||
|
static const uint64_t kExponentMask = CEREAL_RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||||
|
static const uint64_t kSignificandMask = CEREAL_RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||||
|
static const uint64_t kHiddenBit = CEREAL_RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||||
|
|
||||||
|
union {
|
||||||
|
double d_;
|
||||||
|
uint64_t u_;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_IEEE754_
|
||||||
304
external/cereal/external/rapidjson/internal/itoa.h
vendored
Normal file
304
external/cereal/external/rapidjson/internal/itoa.h
vendored
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ITOA_
|
||||||
|
#define CEREAL_RAPIDJSON_ITOA_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
inline const char* GetDigitsLut() {
|
||||||
|
static const char cDigitsLut[200] = {
|
||||||
|
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
|
||||||
|
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
|
||||||
|
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
|
||||||
|
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
|
||||||
|
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
|
||||||
|
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
|
||||||
|
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
|
||||||
|
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
|
||||||
|
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
|
||||||
|
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
|
||||||
|
};
|
||||||
|
return cDigitsLut;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* u32toa(uint32_t value, char* buffer) {
|
||||||
|
const char* cDigitsLut = GetDigitsLut();
|
||||||
|
|
||||||
|
if (value < 10000) {
|
||||||
|
const uint32_t d1 = (value / 100) << 1;
|
||||||
|
const uint32_t d2 = (value % 100) << 1;
|
||||||
|
|
||||||
|
if (value >= 1000)
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
if (value >= 100)
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
if (value >= 10)
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
}
|
||||||
|
else if (value < 100000000) {
|
||||||
|
// value = bbbbcccc
|
||||||
|
const uint32_t b = value / 10000;
|
||||||
|
const uint32_t c = value % 10000;
|
||||||
|
|
||||||
|
const uint32_t d1 = (b / 100) << 1;
|
||||||
|
const uint32_t d2 = (b % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d3 = (c / 100) << 1;
|
||||||
|
const uint32_t d4 = (c % 100) << 1;
|
||||||
|
|
||||||
|
if (value >= 10000000)
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
if (value >= 1000000)
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
if (value >= 100000)
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
|
||||||
|
*buffer++ = cDigitsLut[d3];
|
||||||
|
*buffer++ = cDigitsLut[d3 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d4];
|
||||||
|
*buffer++ = cDigitsLut[d4 + 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// value = aabbbbcccc in decimal
|
||||||
|
|
||||||
|
const uint32_t a = value / 100000000; // 1 to 42
|
||||||
|
value %= 100000000;
|
||||||
|
|
||||||
|
if (a >= 10) {
|
||||||
|
const unsigned i = a << 1;
|
||||||
|
*buffer++ = cDigitsLut[i];
|
||||||
|
*buffer++ = cDigitsLut[i + 1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||||
|
|
||||||
|
const uint32_t b = value / 10000; // 0 to 9999
|
||||||
|
const uint32_t c = value % 10000; // 0 to 9999
|
||||||
|
|
||||||
|
const uint32_t d1 = (b / 100) << 1;
|
||||||
|
const uint32_t d2 = (b % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d3 = (c / 100) << 1;
|
||||||
|
const uint32_t d4 = (c % 100) << 1;
|
||||||
|
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d3];
|
||||||
|
*buffer++ = cDigitsLut[d3 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d4];
|
||||||
|
*buffer++ = cDigitsLut[d4 + 1];
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* i32toa(int32_t value, char* buffer) {
|
||||||
|
uint32_t u = static_cast<uint32_t>(value);
|
||||||
|
if (value < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
u = ~u + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u32toa(u, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* u64toa(uint64_t value, char* buffer) {
|
||||||
|
const char* cDigitsLut = GetDigitsLut();
|
||||||
|
const uint64_t kTen8 = 100000000;
|
||||||
|
const uint64_t kTen9 = kTen8 * 10;
|
||||||
|
const uint64_t kTen10 = kTen8 * 100;
|
||||||
|
const uint64_t kTen11 = kTen8 * 1000;
|
||||||
|
const uint64_t kTen12 = kTen8 * 10000;
|
||||||
|
const uint64_t kTen13 = kTen8 * 100000;
|
||||||
|
const uint64_t kTen14 = kTen8 * 1000000;
|
||||||
|
const uint64_t kTen15 = kTen8 * 10000000;
|
||||||
|
const uint64_t kTen16 = kTen8 * kTen8;
|
||||||
|
|
||||||
|
if (value < kTen8) {
|
||||||
|
uint32_t v = static_cast<uint32_t>(value);
|
||||||
|
if (v < 10000) {
|
||||||
|
const uint32_t d1 = (v / 100) << 1;
|
||||||
|
const uint32_t d2 = (v % 100) << 1;
|
||||||
|
|
||||||
|
if (v >= 1000)
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
if (v >= 100)
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
if (v >= 10)
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// value = bbbbcccc
|
||||||
|
const uint32_t b = v / 10000;
|
||||||
|
const uint32_t c = v % 10000;
|
||||||
|
|
||||||
|
const uint32_t d1 = (b / 100) << 1;
|
||||||
|
const uint32_t d2 = (b % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d3 = (c / 100) << 1;
|
||||||
|
const uint32_t d4 = (c % 100) << 1;
|
||||||
|
|
||||||
|
if (value >= 10000000)
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
if (value >= 1000000)
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
if (value >= 100000)
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
|
||||||
|
*buffer++ = cDigitsLut[d3];
|
||||||
|
*buffer++ = cDigitsLut[d3 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d4];
|
||||||
|
*buffer++ = cDigitsLut[d4 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (value < kTen16) {
|
||||||
|
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||||
|
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||||
|
|
||||||
|
const uint32_t b0 = v0 / 10000;
|
||||||
|
const uint32_t c0 = v0 % 10000;
|
||||||
|
|
||||||
|
const uint32_t d1 = (b0 / 100) << 1;
|
||||||
|
const uint32_t d2 = (b0 % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d3 = (c0 / 100) << 1;
|
||||||
|
const uint32_t d4 = (c0 % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t b1 = v1 / 10000;
|
||||||
|
const uint32_t c1 = v1 % 10000;
|
||||||
|
|
||||||
|
const uint32_t d5 = (b1 / 100) << 1;
|
||||||
|
const uint32_t d6 = (b1 % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d7 = (c1 / 100) << 1;
|
||||||
|
const uint32_t d8 = (c1 % 100) << 1;
|
||||||
|
|
||||||
|
if (value >= kTen15)
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
if (value >= kTen14)
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
if (value >= kTen13)
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
if (value >= kTen12)
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
if (value >= kTen11)
|
||||||
|
*buffer++ = cDigitsLut[d3];
|
||||||
|
if (value >= kTen10)
|
||||||
|
*buffer++ = cDigitsLut[d3 + 1];
|
||||||
|
if (value >= kTen9)
|
||||||
|
*buffer++ = cDigitsLut[d4];
|
||||||
|
if (value >= kTen8)
|
||||||
|
*buffer++ = cDigitsLut[d4 + 1];
|
||||||
|
|
||||||
|
*buffer++ = cDigitsLut[d5];
|
||||||
|
*buffer++ = cDigitsLut[d5 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d6];
|
||||||
|
*buffer++ = cDigitsLut[d6 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d7];
|
||||||
|
*buffer++ = cDigitsLut[d7 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d8];
|
||||||
|
*buffer++ = cDigitsLut[d8 + 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
|
||||||
|
value %= kTen16;
|
||||||
|
|
||||||
|
if (a < 10)
|
||||||
|
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||||
|
else if (a < 100) {
|
||||||
|
const uint32_t i = a << 1;
|
||||||
|
*buffer++ = cDigitsLut[i];
|
||||||
|
*buffer++ = cDigitsLut[i + 1];
|
||||||
|
}
|
||||||
|
else if (a < 1000) {
|
||||||
|
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
|
||||||
|
|
||||||
|
const uint32_t i = (a % 100) << 1;
|
||||||
|
*buffer++ = cDigitsLut[i];
|
||||||
|
*buffer++ = cDigitsLut[i + 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const uint32_t i = (a / 100) << 1;
|
||||||
|
const uint32_t j = (a % 100) << 1;
|
||||||
|
*buffer++ = cDigitsLut[i];
|
||||||
|
*buffer++ = cDigitsLut[i + 1];
|
||||||
|
*buffer++ = cDigitsLut[j];
|
||||||
|
*buffer++ = cDigitsLut[j + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||||
|
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||||
|
|
||||||
|
const uint32_t b0 = v0 / 10000;
|
||||||
|
const uint32_t c0 = v0 % 10000;
|
||||||
|
|
||||||
|
const uint32_t d1 = (b0 / 100) << 1;
|
||||||
|
const uint32_t d2 = (b0 % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d3 = (c0 / 100) << 1;
|
||||||
|
const uint32_t d4 = (c0 % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t b1 = v1 / 10000;
|
||||||
|
const uint32_t c1 = v1 % 10000;
|
||||||
|
|
||||||
|
const uint32_t d5 = (b1 / 100) << 1;
|
||||||
|
const uint32_t d6 = (b1 % 100) << 1;
|
||||||
|
|
||||||
|
const uint32_t d7 = (c1 / 100) << 1;
|
||||||
|
const uint32_t d8 = (c1 % 100) << 1;
|
||||||
|
|
||||||
|
*buffer++ = cDigitsLut[d1];
|
||||||
|
*buffer++ = cDigitsLut[d1 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d2];
|
||||||
|
*buffer++ = cDigitsLut[d2 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d3];
|
||||||
|
*buffer++ = cDigitsLut[d3 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d4];
|
||||||
|
*buffer++ = cDigitsLut[d4 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d5];
|
||||||
|
*buffer++ = cDigitsLut[d5 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d6];
|
||||||
|
*buffer++ = cDigitsLut[d6 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d7];
|
||||||
|
*buffer++ = cDigitsLut[d7 + 1];
|
||||||
|
*buffer++ = cDigitsLut[d8];
|
||||||
|
*buffer++ = cDigitsLut[d8 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* i64toa(int64_t value, char* buffer) {
|
||||||
|
uint64_t u = static_cast<uint64_t>(value);
|
||||||
|
if (value < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
u = ~u + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u64toa(u, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_ITOA_
|
||||||
181
external/cereal/external/rapidjson/internal/meta.h
vendored
Normal file
181
external/cereal/external/rapidjson/internal/meta.h
vendored
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_INTERNAL_META_H_
|
||||||
|
#define CEREAL_RAPIDJSON_INTERNAL_META_H_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
#endif
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(6334)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||||
|
#include <type_traits>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//@cond CEREAL_RAPIDJSON_INTERNAL
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
|
||||||
|
template <typename T> struct Void { typedef void Type; };
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// BoolType, TrueType, FalseType
|
||||||
|
//
|
||||||
|
template <bool Cond> struct BoolType {
|
||||||
|
static const bool Value = Cond;
|
||||||
|
typedef BoolType Type;
|
||||||
|
};
|
||||||
|
typedef BoolType<true> TrueType;
|
||||||
|
typedef BoolType<false> FalseType;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
|
||||||
|
//
|
||||||
|
|
||||||
|
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
|
||||||
|
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
|
||||||
|
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
|
||||||
|
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
|
||||||
|
|
||||||
|
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
|
||||||
|
template <> struct AndExprCond<true, true> : TrueType {};
|
||||||
|
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
|
||||||
|
template <> struct OrExprCond<false, false> : FalseType {};
|
||||||
|
|
||||||
|
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
|
||||||
|
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
|
||||||
|
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
|
||||||
|
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// AddConst, MaybeAddConst, RemoveConst
|
||||||
|
template <typename T> struct AddConst { typedef const T Type; };
|
||||||
|
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||||
|
template <typename T> struct RemoveConst { typedef T Type; };
|
||||||
|
template <typename T> struct RemoveConst<const T> { typedef T Type; };
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// IsSame, IsConst, IsMoreConst, IsPointer
|
||||||
|
//
|
||||||
|
template <typename T, typename U> struct IsSame : FalseType {};
|
||||||
|
template <typename T> struct IsSame<T, T> : TrueType {};
|
||||||
|
|
||||||
|
template <typename T> struct IsConst : FalseType {};
|
||||||
|
template <typename T> struct IsConst<const T> : TrueType {};
|
||||||
|
|
||||||
|
template <typename CT, typename T>
|
||||||
|
struct IsMoreConst
|
||||||
|
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
|
||||||
|
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
|
||||||
|
|
||||||
|
template <typename T> struct IsPointer : FalseType {};
|
||||||
|
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// IsBaseOf
|
||||||
|
//
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||||
|
|
||||||
|
template <typename B, typename D> struct IsBaseOf
|
||||||
|
: BoolType< ::std::is_base_of<B,D>::value> {};
|
||||||
|
|
||||||
|
#else // simplified version adopted from Boost
|
||||||
|
|
||||||
|
template<typename B, typename D> struct IsBaseOfImpl {
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
|
||||||
|
CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
|
||||||
|
|
||||||
|
typedef char (&Yes)[1];
|
||||||
|
typedef char (&No) [2];
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static Yes Check(const D*, T);
|
||||||
|
static No Check(const B*, int);
|
||||||
|
|
||||||
|
struct Host {
|
||||||
|
operator const B*() const;
|
||||||
|
operator const D*();
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename B, typename D> struct IsBaseOf
|
||||||
|
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// EnableIf / DisableIf
|
||||||
|
//
|
||||||
|
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
|
||||||
|
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
|
||||||
|
|
||||||
|
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
|
||||||
|
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
|
||||||
|
|
||||||
|
template <typename Condition, typename T = void>
|
||||||
|
struct EnableIf : EnableIfCond<Condition::Value, T> {};
|
||||||
|
|
||||||
|
template <typename Condition, typename T = void>
|
||||||
|
struct DisableIf : DisableIfCond<Condition::Value, T> {};
|
||||||
|
|
||||||
|
// SFINAE helpers
|
||||||
|
struct SfinaeTag {};
|
||||||
|
template <typename T> struct RemoveSfinaeTag;
|
||||||
|
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_REMOVEFPTR_(type) \
|
||||||
|
typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
|
||||||
|
< ::CEREAL_RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_ENABLEIF(cond) \
|
||||||
|
typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||||
|
<CEREAL_RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_DISABLEIF(cond) \
|
||||||
|
typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||||
|
<CEREAL_RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
|
||||||
|
typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||||
|
<CEREAL_RAPIDJSON_REMOVEFPTR_(cond), \
|
||||||
|
CEREAL_RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||||
|
typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||||
|
<CEREAL_RAPIDJSON_REMOVEFPTR_(cond), \
|
||||||
|
CEREAL_RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
//@endcond
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_INTERNAL_META_H_
|
||||||
55
external/cereal/external/rapidjson/internal/pow10.h
vendored
Normal file
55
external/cereal/external/rapidjson/internal/pow10.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_POW10_
|
||||||
|
#define CEREAL_RAPIDJSON_POW10_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! Computes integer powers of 10 in double (10.0^n).
|
||||||
|
/*! This function uses lookup table for fast and accurate results.
|
||||||
|
\param n non-negative exponent. Must <= 308.
|
||||||
|
\return 10.0^n
|
||||||
|
*/
|
||||||
|
inline double Pow10(int n) {
|
||||||
|
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
|
||||||
|
1e+0,
|
||||||
|
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
|
||||||
|
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
|
||||||
|
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
|
||||||
|
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
|
||||||
|
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
|
||||||
|
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
|
||||||
|
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
|
||||||
|
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
|
||||||
|
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
|
||||||
|
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
|
||||||
|
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
|
||||||
|
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
|
||||||
|
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
|
||||||
|
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
|
||||||
|
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
|
||||||
|
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
|
||||||
|
};
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(n >= 0 && n <= 308);
|
||||||
|
return e[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_POW10_
|
||||||
701
external/cereal/external/rapidjson/internal/regex.h
vendored
Normal file
701
external/cereal/external/rapidjson/internal/regex.h
vendored
Normal file
@@ -0,0 +1,701 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_INTERNAL_REGEX_H_
|
||||||
|
#define CEREAL_RAPIDJSON_INTERNAL_REGEX_H_
|
||||||
|
|
||||||
|
#include "../allocators.h"
|
||||||
|
#include "../stream.h"
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(switch-enum)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(implicit-fallthrough)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_REGEX_VERBOSE
|
||||||
|
#define CEREAL_RAPIDJSON_REGEX_VERBOSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GenericRegex
|
||||||
|
|
||||||
|
static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
|
||||||
|
static const SizeType kRegexInvalidRange = ~SizeType(0);
|
||||||
|
|
||||||
|
//! Regular expression engine with subset of ECMAscript grammar.
|
||||||
|
/*!
|
||||||
|
Supported regular expression syntax:
|
||||||
|
- \c ab Concatenation
|
||||||
|
- \c a|b Alternation
|
||||||
|
- \c a? Zero or one
|
||||||
|
- \c a* Zero or more
|
||||||
|
- \c a+ One or more
|
||||||
|
- \c a{3} Exactly 3 times
|
||||||
|
- \c a{3,} At least 3 times
|
||||||
|
- \c a{3,5} 3 to 5 times
|
||||||
|
- \c (ab) Grouping
|
||||||
|
- \c ^a At the beginning
|
||||||
|
- \c a$ At the end
|
||||||
|
- \c . Any character
|
||||||
|
- \c [abc] Character classes
|
||||||
|
- \c [a-c] Character class range
|
||||||
|
- \c [a-z0-9_] Character class combination
|
||||||
|
- \c [^abc] Negated character classes
|
||||||
|
- \c [^a-c] Negated character class range
|
||||||
|
- \c [\b] Backspace (U+0008)
|
||||||
|
- \c \\| \\\\ ... Escape characters
|
||||||
|
- \c \\f Form feed (U+000C)
|
||||||
|
- \c \\n Line feed (U+000A)
|
||||||
|
- \c \\r Carriage return (U+000D)
|
||||||
|
- \c \\t Tab (U+0009)
|
||||||
|
- \c \\v Vertical tab (U+000B)
|
||||||
|
|
||||||
|
\note This is a Thompson NFA engine, implemented with reference to
|
||||||
|
Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
|
||||||
|
https://swtch.com/~rsc/regexp/regexp1.html
|
||||||
|
*/
|
||||||
|
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||||
|
class GenericRegex {
|
||||||
|
public:
|
||||||
|
typedef typename Encoding::Ch Ch;
|
||||||
|
|
||||||
|
GenericRegex(const Ch* source, Allocator* allocator = 0) :
|
||||||
|
states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
|
||||||
|
stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
|
||||||
|
{
|
||||||
|
GenericStringStream<Encoding> ss(source);
|
||||||
|
DecodedStream<GenericStringStream<Encoding> > ds(ss);
|
||||||
|
Parse(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
~GenericRegex() {
|
||||||
|
Allocator::Free(stateSet_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return root_ != kRegexInvalidState;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
bool Match(InputStream& is) const {
|
||||||
|
return SearchWithAnchoring(is, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Match(const Ch* s) const {
|
||||||
|
GenericStringStream<Encoding> is(s);
|
||||||
|
return Match(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
bool Search(InputStream& is) const {
|
||||||
|
return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search(const Ch* s) const {
|
||||||
|
GenericStringStream<Encoding> is(s);
|
||||||
|
return Search(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Operator {
|
||||||
|
kZeroOrOne,
|
||||||
|
kZeroOrMore,
|
||||||
|
kOneOrMore,
|
||||||
|
kConcatenation,
|
||||||
|
kAlternation,
|
||||||
|
kLeftParenthesis
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
|
||||||
|
static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
|
||||||
|
static const unsigned kRangeNegationFlag = 0x80000000;
|
||||||
|
|
||||||
|
struct Range {
|
||||||
|
unsigned start; //
|
||||||
|
unsigned end;
|
||||||
|
SizeType next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
SizeType out; //!< Equals to kInvalid for matching state
|
||||||
|
SizeType out1; //!< Equals to non-kInvalid for split
|
||||||
|
SizeType rangeStart;
|
||||||
|
unsigned codepoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frag {
|
||||||
|
Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
|
||||||
|
SizeType start;
|
||||||
|
SizeType out; //!< link-list of all output states
|
||||||
|
SizeType minIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename SourceStream>
|
||||||
|
class DecodedStream {
|
||||||
|
public:
|
||||||
|
DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
|
||||||
|
unsigned Peek() { return codepoint_; }
|
||||||
|
unsigned Take() {
|
||||||
|
unsigned c = codepoint_;
|
||||||
|
if (c) // No further decoding when '\0'
|
||||||
|
Decode();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Decode() {
|
||||||
|
if (!Encoding::Decode(ss_, &codepoint_))
|
||||||
|
codepoint_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceStream& ss_;
|
||||||
|
unsigned codepoint_;
|
||||||
|
};
|
||||||
|
|
||||||
|
State& GetState(SizeType index) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(index < stateCount_);
|
||||||
|
return states_.template Bottom<State>()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const State& GetState(SizeType index) const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(index < stateCount_);
|
||||||
|
return states_.template Bottom<State>()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Range& GetRange(SizeType index) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(index < rangeCount_);
|
||||||
|
return ranges_.template Bottom<Range>()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Range& GetRange(SizeType index) const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(index < rangeCount_);
|
||||||
|
return ranges_.template Bottom<Range>()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
void Parse(DecodedStream<InputStream>& ds) {
|
||||||
|
Allocator allocator;
|
||||||
|
Stack<Allocator> operandStack(&allocator, 256); // Frag
|
||||||
|
Stack<Allocator> operatorStack(&allocator, 256); // Operator
|
||||||
|
Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
|
||||||
|
|
||||||
|
*atomCountStack.template Push<unsigned>() = 0;
|
||||||
|
|
||||||
|
unsigned codepoint;
|
||||||
|
while (ds.Peek() != 0) {
|
||||||
|
switch (codepoint = ds.Take()) {
|
||||||
|
case '^':
|
||||||
|
anchorBegin_ = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '$':
|
||||||
|
anchorEnd_ = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '|':
|
||||||
|
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
|
||||||
|
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||||
|
return;
|
||||||
|
*operatorStack.template Push<Operator>() = kAlternation;
|
||||||
|
*atomCountStack.template Top<unsigned>() = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '(':
|
||||||
|
*operatorStack.template Push<Operator>() = kLeftParenthesis;
|
||||||
|
*atomCountStack.template Push<unsigned>() = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ')':
|
||||||
|
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
|
||||||
|
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||||
|
return;
|
||||||
|
if (operatorStack.Empty())
|
||||||
|
return;
|
||||||
|
operatorStack.template Pop<Operator>(1);
|
||||||
|
atomCountStack.template Pop<unsigned>(1);
|
||||||
|
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
if (!Eval(operandStack, kZeroOrOne))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
if (!Eval(operandStack, kZeroOrMore))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '+':
|
||||||
|
if (!Eval(operandStack, kOneOrMore))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
{
|
||||||
|
unsigned n, m;
|
||||||
|
if (!ParseUnsigned(ds, &n))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ds.Peek() == ',') {
|
||||||
|
ds.Take();
|
||||||
|
if (ds.Peek() == '}')
|
||||||
|
m = kInfinityQuantifier;
|
||||||
|
else if (!ParseUnsigned(ds, &m) || m < n)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m = n;
|
||||||
|
|
||||||
|
if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
|
||||||
|
return;
|
||||||
|
ds.Take();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
PushOperand(operandStack, kAnyCharacterClass);
|
||||||
|
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
{
|
||||||
|
SizeType range;
|
||||||
|
if (!ParseRange(ds, &range))
|
||||||
|
return;
|
||||||
|
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
|
||||||
|
GetState(s).rangeStart = range;
|
||||||
|
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||||
|
}
|
||||||
|
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\\': // Escape character
|
||||||
|
if (!CharacterEscape(ds, &codepoint))
|
||||||
|
return; // Unsupported escape character
|
||||||
|
// fall through to default
|
||||||
|
|
||||||
|
default: // Pattern character
|
||||||
|
PushOperand(operandStack, codepoint);
|
||||||
|
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!operatorStack.Empty())
|
||||||
|
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Link the operand to matching state.
|
||||||
|
if (operandStack.GetSize() == sizeof(Frag)) {
|
||||||
|
Frag* e = operandStack.template Pop<Frag>(1);
|
||||||
|
Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
|
||||||
|
root_ = e->start;
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_REGEX_VERBOSE
|
||||||
|
printf("root: %d\n", root_);
|
||||||
|
for (SizeType i = 0; i < stateCount_ ; i++) {
|
||||||
|
State& s = GetState(i);
|
||||||
|
printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preallocate buffer for SearchWithAnchoring()
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(stateSet_ == 0);
|
||||||
|
if (stateCount_ > 0) {
|
||||||
|
stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
|
||||||
|
state0_.template Reserve<SizeType>(stateCount_);
|
||||||
|
state1_.template Reserve<SizeType>(stateCount_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
|
||||||
|
State* s = states_.template Push<State>();
|
||||||
|
s->out = out;
|
||||||
|
s->out1 = out1;
|
||||||
|
s->codepoint = codepoint;
|
||||||
|
s->rangeStart = kRegexInvalidRange;
|
||||||
|
return stateCount_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
|
||||||
|
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
|
||||||
|
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
|
||||||
|
if (*atomCountStack.template Top<unsigned>())
|
||||||
|
*operatorStack.template Push<Operator>() = kConcatenation;
|
||||||
|
(*atomCountStack.template Top<unsigned>())++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeType Append(SizeType l1, SizeType l2) {
|
||||||
|
SizeType old = l1;
|
||||||
|
while (GetState(l1).out != kRegexInvalidState)
|
||||||
|
l1 = GetState(l1).out;
|
||||||
|
GetState(l1).out = l2;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch(SizeType l, SizeType s) {
|
||||||
|
for (SizeType next; l != kRegexInvalidState; l = next) {
|
||||||
|
next = GetState(l).out;
|
||||||
|
GetState(l).out = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Eval(Stack<Allocator>& operandStack, Operator op) {
|
||||||
|
switch (op) {
|
||||||
|
case kConcatenation:
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
|
||||||
|
{
|
||||||
|
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||||
|
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||||
|
Patch(e1.out, e2.start);
|
||||||
|
*operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case kAlternation:
|
||||||
|
if (operandStack.GetSize() >= sizeof(Frag) * 2) {
|
||||||
|
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||||
|
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||||
|
SizeType s = NewState(e1.start, e2.start, 0);
|
||||||
|
*operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case kZeroOrOne:
|
||||||
|
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||||
|
Frag e = *operandStack.template Pop<Frag>(1);
|
||||||
|
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||||
|
*operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case kZeroOrMore:
|
||||||
|
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||||
|
Frag e = *operandStack.template Pop<Frag>(1);
|
||||||
|
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||||
|
Patch(e.out, s);
|
||||||
|
*operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(op == kOneOrMore);
|
||||||
|
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||||
|
Frag e = *operandStack.template Pop<Frag>(1);
|
||||||
|
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||||
|
Patch(e.out, s);
|
||||||
|
*operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(n <= m);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
if (m == 0) // a{0} not support
|
||||||
|
return false;
|
||||||
|
else if (m == kInfinityQuantifier)
|
||||||
|
Eval(operandStack, kZeroOrMore); // a{0,} -> a*
|
||||||
|
else {
|
||||||
|
Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
|
||||||
|
for (unsigned i = 0; i < m - 1; i++)
|
||||||
|
CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
|
||||||
|
for (unsigned i = 0; i < m - 1; i++)
|
||||||
|
Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
|
||||||
|
CloneTopOperand(operandStack);
|
||||||
|
|
||||||
|
if (m == kInfinityQuantifier)
|
||||||
|
Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
|
||||||
|
else if (m > n) {
|
||||||
|
CloneTopOperand(operandStack); // a{3,5} -> a a a a
|
||||||
|
Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
|
||||||
|
for (unsigned i = n; i < m - 1; i++)
|
||||||
|
CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
|
||||||
|
for (unsigned i = n; i < m; i++)
|
||||||
|
Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < n - 1; i++)
|
||||||
|
Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
|
||||||
|
|
||||||
|
void CloneTopOperand(Stack<Allocator>& operandStack) {
|
||||||
|
const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
|
||||||
|
SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
|
||||||
|
State* s = states_.template Push<State>(count);
|
||||||
|
memcpy(s, &GetState(src.minIndex), count * sizeof(State));
|
||||||
|
for (SizeType j = 0; j < count; j++) {
|
||||||
|
if (s[j].out != kRegexInvalidState)
|
||||||
|
s[j].out += count;
|
||||||
|
if (s[j].out1 != kRegexInvalidState)
|
||||||
|
s[j].out1 += count;
|
||||||
|
}
|
||||||
|
*operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
|
||||||
|
stateCount_ += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
|
||||||
|
unsigned r = 0;
|
||||||
|
if (ds.Peek() < '0' || ds.Peek() > '9')
|
||||||
|
return false;
|
||||||
|
while (ds.Peek() >= '0' && ds.Peek() <= '9') {
|
||||||
|
if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
|
||||||
|
return false; // overflow
|
||||||
|
r = r * 10 + (ds.Take() - '0');
|
||||||
|
}
|
||||||
|
*u = r;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
|
||||||
|
bool isBegin = true;
|
||||||
|
bool negate = false;
|
||||||
|
int step = 0;
|
||||||
|
SizeType start = kRegexInvalidRange;
|
||||||
|
SizeType current = kRegexInvalidRange;
|
||||||
|
unsigned codepoint;
|
||||||
|
while ((codepoint = ds.Take()) != 0) {
|
||||||
|
if (isBegin) {
|
||||||
|
isBegin = false;
|
||||||
|
if (codepoint == '^') {
|
||||||
|
negate = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (codepoint) {
|
||||||
|
case ']':
|
||||||
|
if (start == kRegexInvalidRange)
|
||||||
|
return false; // Error: nothing inside []
|
||||||
|
if (step == 2) { // Add trailing '-'
|
||||||
|
SizeType r = NewRange('-');
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(current != kRegexInvalidRange);
|
||||||
|
GetRange(current).next = r;
|
||||||
|
}
|
||||||
|
if (negate)
|
||||||
|
GetRange(start).start |= kRangeNegationFlag;
|
||||||
|
*range = start;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
if (ds.Peek() == 'b') {
|
||||||
|
ds.Take();
|
||||||
|
codepoint = 0x0008; // Escape backspace character
|
||||||
|
}
|
||||||
|
else if (!CharacterEscape(ds, &codepoint))
|
||||||
|
return false;
|
||||||
|
// fall through to default
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch (step) {
|
||||||
|
case 1:
|
||||||
|
if (codepoint == '-') {
|
||||||
|
step++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// fall through to step 0 for other characters
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
SizeType r = NewRange(codepoint);
|
||||||
|
if (current != kRegexInvalidRange)
|
||||||
|
GetRange(current).next = r;
|
||||||
|
if (start == kRegexInvalidRange)
|
||||||
|
start = r;
|
||||||
|
current = r;
|
||||||
|
}
|
||||||
|
step = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(step == 2);
|
||||||
|
GetRange(current).end = codepoint;
|
||||||
|
step = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeType NewRange(unsigned codepoint) {
|
||||||
|
Range* r = ranges_.template Push<Range>();
|
||||||
|
r->start = r->end = codepoint;
|
||||||
|
r->next = kRegexInvalidRange;
|
||||||
|
return rangeCount_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
|
||||||
|
unsigned codepoint;
|
||||||
|
switch (codepoint = ds.Take()) {
|
||||||
|
case '^':
|
||||||
|
case '$':
|
||||||
|
case '|':
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
case '?':
|
||||||
|
case '*':
|
||||||
|
case '+':
|
||||||
|
case '.':
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
|
case '\\':
|
||||||
|
*escapedCodepoint = codepoint; return true;
|
||||||
|
case 'f': *escapedCodepoint = 0x000C; return true;
|
||||||
|
case 'n': *escapedCodepoint = 0x000A; return true;
|
||||||
|
case 'r': *escapedCodepoint = 0x000D; return true;
|
||||||
|
case 't': *escapedCodepoint = 0x0009; return true;
|
||||||
|
case 'v': *escapedCodepoint = 0x000B; return true;
|
||||||
|
default:
|
||||||
|
return false; // Unsupported escape character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputStream>
|
||||||
|
bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(IsValid());
|
||||||
|
DecodedStream<InputStream> ds(is);
|
||||||
|
|
||||||
|
state0_.Clear();
|
||||||
|
Stack<Allocator> *current = &state0_, *next = &state1_;
|
||||||
|
const size_t stateSetSize = GetStateSetSize();
|
||||||
|
std::memset(stateSet_, 0, stateSetSize);
|
||||||
|
|
||||||
|
bool matched = AddState(*current, root_);
|
||||||
|
unsigned codepoint;
|
||||||
|
while (!current->Empty() && (codepoint = ds.Take()) != 0) {
|
||||||
|
std::memset(stateSet_, 0, stateSetSize);
|
||||||
|
next->Clear();
|
||||||
|
matched = false;
|
||||||
|
for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
|
||||||
|
const State& sr = GetState(*s);
|
||||||
|
if (sr.codepoint == codepoint ||
|
||||||
|
sr.codepoint == kAnyCharacterClass ||
|
||||||
|
(sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
|
||||||
|
{
|
||||||
|
matched = AddState(*next, sr.out) || matched;
|
||||||
|
if (!anchorEnd && matched)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!anchorBegin)
|
||||||
|
AddState(*next, root_);
|
||||||
|
}
|
||||||
|
internal::Swap(current, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetStateSetSize() const {
|
||||||
|
return (stateCount_ + 31) / 32 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return whether the added states is a match state
|
||||||
|
bool AddState(Stack<Allocator>& l, SizeType index) const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(index != kRegexInvalidState);
|
||||||
|
|
||||||
|
const State& s = GetState(index);
|
||||||
|
if (s.out1 != kRegexInvalidState) { // Split
|
||||||
|
bool matched = AddState(l, s.out);
|
||||||
|
return AddState(l, s.out1) || matched;
|
||||||
|
}
|
||||||
|
else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) {
|
||||||
|
stateSet_[index >> 5] |= (1 << (index & 31));
|
||||||
|
*l.template PushUnsafe<SizeType>() = index;
|
||||||
|
}
|
||||||
|
return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
|
||||||
|
bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
|
||||||
|
while (rangeIndex != kRegexInvalidRange) {
|
||||||
|
const Range& r = GetRange(rangeIndex);
|
||||||
|
if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
|
||||||
|
return yes;
|
||||||
|
rangeIndex = r.next;
|
||||||
|
}
|
||||||
|
return !yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack<Allocator> states_;
|
||||||
|
Stack<Allocator> ranges_;
|
||||||
|
SizeType root_;
|
||||||
|
SizeType stateCount_;
|
||||||
|
SizeType rangeCount_;
|
||||||
|
|
||||||
|
static const unsigned kInfinityQuantifier = ~0u;
|
||||||
|
|
||||||
|
// For SearchWithAnchoring()
|
||||||
|
uint32_t* stateSet_; // allocated by states_.GetAllocator()
|
||||||
|
mutable Stack<Allocator> state0_;
|
||||||
|
mutable Stack<Allocator> state1_;
|
||||||
|
bool anchorBegin_;
|
||||||
|
bool anchorEnd_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef GenericRegex<UTF8<> > Regex;
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_INTERNAL_REGEX_H_
|
||||||
230
external/cereal/external/rapidjson/internal/stack.h
vendored
Normal file
230
external/cereal/external/rapidjson/internal/stack.h
vendored
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_INTERNAL_STACK_H_
|
||||||
|
#define CEREAL_RAPIDJSON_INTERNAL_STACK_H_
|
||||||
|
|
||||||
|
#include "../allocators.h"
|
||||||
|
#include "swap.h"
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Stack
|
||||||
|
|
||||||
|
//! A type-unsafe stack for storing different types of data.
|
||||||
|
/*! \tparam Allocator Allocator for allocating stack memory.
|
||||||
|
*/
|
||||||
|
template <typename Allocator>
|
||||||
|
class Stack {
|
||||||
|
public:
|
||||||
|
// Optimization note: Do not allocate memory for stack_ in constructor.
|
||||||
|
// Do it lazily when first Push() -> Expand() -> Resize().
|
||||||
|
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
Stack(Stack&& rhs)
|
||||||
|
: allocator_(rhs.allocator_),
|
||||||
|
ownAllocator_(rhs.ownAllocator_),
|
||||||
|
stack_(rhs.stack_),
|
||||||
|
stackTop_(rhs.stackTop_),
|
||||||
|
stackEnd_(rhs.stackEnd_),
|
||||||
|
initialCapacity_(rhs.initialCapacity_)
|
||||||
|
{
|
||||||
|
rhs.allocator_ = 0;
|
||||||
|
rhs.ownAllocator_ = 0;
|
||||||
|
rhs.stack_ = 0;
|
||||||
|
rhs.stackTop_ = 0;
|
||||||
|
rhs.stackEnd_ = 0;
|
||||||
|
rhs.initialCapacity_ = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
~Stack() {
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
Stack& operator=(Stack&& rhs) {
|
||||||
|
if (&rhs != this)
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
|
||||||
|
allocator_ = rhs.allocator_;
|
||||||
|
ownAllocator_ = rhs.ownAllocator_;
|
||||||
|
stack_ = rhs.stack_;
|
||||||
|
stackTop_ = rhs.stackTop_;
|
||||||
|
stackEnd_ = rhs.stackEnd_;
|
||||||
|
initialCapacity_ = rhs.initialCapacity_;
|
||||||
|
|
||||||
|
rhs.allocator_ = 0;
|
||||||
|
rhs.ownAllocator_ = 0;
|
||||||
|
rhs.stack_ = 0;
|
||||||
|
rhs.stackTop_ = 0;
|
||||||
|
rhs.stackEnd_ = 0;
|
||||||
|
rhs.initialCapacity_ = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Swap(Stack& rhs) CEREAL_RAPIDJSON_NOEXCEPT {
|
||||||
|
internal::Swap(allocator_, rhs.allocator_);
|
||||||
|
internal::Swap(ownAllocator_, rhs.ownAllocator_);
|
||||||
|
internal::Swap(stack_, rhs.stack_);
|
||||||
|
internal::Swap(stackTop_, rhs.stackTop_);
|
||||||
|
internal::Swap(stackEnd_, rhs.stackEnd_);
|
||||||
|
internal::Swap(initialCapacity_, rhs.initialCapacity_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear() { stackTop_ = stack_; }
|
||||||
|
|
||||||
|
void ShrinkToFit() {
|
||||||
|
if (Empty()) {
|
||||||
|
// If the stack is empty, completely deallocate the memory.
|
||||||
|
Allocator::Free(stack_);
|
||||||
|
stack_ = 0;
|
||||||
|
stackTop_ = 0;
|
||||||
|
stackEnd_ = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Resize(GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization note: try to minimize the size of this function for force inline.
|
||||||
|
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
|
||||||
|
template<typename T>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
|
||||||
|
// Expand the stack if needed
|
||||||
|
if (CEREAL_RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
|
||||||
|
Expand<T>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
|
||||||
|
Reserve<T>(count);
|
||||||
|
return PushUnsafe<T>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
CEREAL_RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
|
||||||
|
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||||
|
stackTop_ += sizeof(T) * count;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* Pop(size_t count) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||||
|
stackTop_ -= count * sizeof(T);
|
||||||
|
return reinterpret_cast<T*>(stackTop_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* Top() {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||||
|
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T* Top() const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||||
|
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* End() { return reinterpret_cast<T*>(stackTop_); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T* End() const { return reinterpret_cast<T*>(stackTop_); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* Bottom() { return reinterpret_cast<T*>(stack_); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
|
||||||
|
|
||||||
|
bool HasAllocator() const {
|
||||||
|
return allocator_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Allocator& GetAllocator() {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(allocator_);
|
||||||
|
return *allocator_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Empty() const { return stackTop_ == stack_; }
|
||||||
|
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
|
||||||
|
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
void Expand(size_t count) {
|
||||||
|
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
|
||||||
|
size_t newCapacity;
|
||||||
|
if (stack_ == 0) {
|
||||||
|
if (!allocator_)
|
||||||
|
ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator());
|
||||||
|
newCapacity = initialCapacity_;
|
||||||
|
} else {
|
||||||
|
newCapacity = GetCapacity();
|
||||||
|
newCapacity += (newCapacity + 1) / 2;
|
||||||
|
}
|
||||||
|
size_t newSize = GetSize() + sizeof(T) * count;
|
||||||
|
if (newCapacity < newSize)
|
||||||
|
newCapacity = newSize;
|
||||||
|
|
||||||
|
Resize(newCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resize(size_t newCapacity) {
|
||||||
|
const size_t size = GetSize(); // Backup the current size
|
||||||
|
stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
|
||||||
|
stackTop_ = stack_ + size;
|
||||||
|
stackEnd_ = stack_ + newCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Destroy() {
|
||||||
|
Allocator::Free(stack_);
|
||||||
|
CEREAL_RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prohibit copy constructor & assignment operator.
|
||||||
|
Stack(const Stack&);
|
||||||
|
Stack& operator=(const Stack&);
|
||||||
|
|
||||||
|
Allocator* allocator_;
|
||||||
|
Allocator* ownAllocator_;
|
||||||
|
char *stack_;
|
||||||
|
char *stackTop_;
|
||||||
|
char *stackEnd_;
|
||||||
|
size_t initialCapacity_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_STACK_H_
|
||||||
55
external/cereal/external/rapidjson/internal/strfunc.h
vendored
Normal file
55
external/cereal/external/rapidjson/internal/strfunc.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||||
|
#define CEREAL_RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||||
|
|
||||||
|
#include "../stream.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! Custom strlen() which works on different character types.
|
||||||
|
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||||
|
\param s Null-terminated input string.
|
||||||
|
\return Number of characters in the string.
|
||||||
|
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
|
||||||
|
*/
|
||||||
|
template <typename Ch>
|
||||||
|
inline SizeType StrLen(const Ch* s) {
|
||||||
|
const Ch* p = s;
|
||||||
|
while (*p) ++p;
|
||||||
|
return SizeType(p - s);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns number of code points in a encoded string.
|
||||||
|
template<typename Encoding>
|
||||||
|
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
|
||||||
|
GenericStringStream<Encoding> is(s);
|
||||||
|
const typename Encoding::Ch* end = s + length;
|
||||||
|
SizeType count = 0;
|
||||||
|
while (is.src_ < end) {
|
||||||
|
unsigned codepoint;
|
||||||
|
if (!Encoding::Decode(is, &codepoint))
|
||||||
|
return false;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
*outCount = count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||||
269
external/cereal/external/rapidjson/internal/strtod.h
vendored
Normal file
269
external/cereal/external/rapidjson/internal/strtod.h
vendored
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_STRTOD_
|
||||||
|
#define CEREAL_RAPIDJSON_STRTOD_
|
||||||
|
|
||||||
|
#include "ieee754.h"
|
||||||
|
#include "biginteger.h"
|
||||||
|
#include "diyfp.h"
|
||||||
|
#include "pow10.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
inline double FastPath(double significand, int exp) {
|
||||||
|
if (exp < -308)
|
||||||
|
return 0.0;
|
||||||
|
else if (exp >= 0)
|
||||||
|
return significand * internal::Pow10(exp);
|
||||||
|
else
|
||||||
|
return significand / internal::Pow10(-exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double StrtodNormalPrecision(double d, int p) {
|
||||||
|
if (p < -308) {
|
||||||
|
// Prevent expSum < -308, making Pow10(p) = 0
|
||||||
|
d = FastPath(d, -308);
|
||||||
|
d = FastPath(d, p + 308);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d = FastPath(d, p);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T Min3(T a, T b, T c) {
|
||||||
|
T m = a;
|
||||||
|
if (m > b) m = b;
|
||||||
|
if (m > c) m = c;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
|
||||||
|
const Double db(b);
|
||||||
|
const uint64_t bInt = db.IntegerSignificand();
|
||||||
|
const int bExp = db.IntegerExponent();
|
||||||
|
const int hExp = bExp - 1;
|
||||||
|
|
||||||
|
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
|
||||||
|
|
||||||
|
// Adjust for decimal exponent
|
||||||
|
if (dExp >= 0) {
|
||||||
|
dS_Exp2 += dExp;
|
||||||
|
dS_Exp5 += dExp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bS_Exp2 -= dExp;
|
||||||
|
bS_Exp5 -= dExp;
|
||||||
|
hS_Exp2 -= dExp;
|
||||||
|
hS_Exp5 -= dExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust for binary exponent
|
||||||
|
if (bExp >= 0)
|
||||||
|
bS_Exp2 += bExp;
|
||||||
|
else {
|
||||||
|
dS_Exp2 -= bExp;
|
||||||
|
hS_Exp2 -= bExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust for half ulp exponent
|
||||||
|
if (hExp >= 0)
|
||||||
|
hS_Exp2 += hExp;
|
||||||
|
else {
|
||||||
|
dS_Exp2 -= hExp;
|
||||||
|
bS_Exp2 -= hExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove common power of two factor from all three scaled values
|
||||||
|
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
|
||||||
|
dS_Exp2 -= common_Exp2;
|
||||||
|
bS_Exp2 -= common_Exp2;
|
||||||
|
hS_Exp2 -= common_Exp2;
|
||||||
|
|
||||||
|
BigInteger dS = d;
|
||||||
|
dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
|
||||||
|
|
||||||
|
BigInteger bS(bInt);
|
||||||
|
bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
|
||||||
|
|
||||||
|
BigInteger hS(1);
|
||||||
|
hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
|
||||||
|
|
||||||
|
BigInteger delta(0);
|
||||||
|
dS.Difference(bS, &delta);
|
||||||
|
|
||||||
|
return delta.Compare(hS);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool StrtodFast(double d, int p, double* result) {
|
||||||
|
// Use fast path for string-to-double conversion if possible
|
||||||
|
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||||
|
if (p > 22 && p < 22 + 16) {
|
||||||
|
// Fast Path Cases In Disguise
|
||||||
|
d *= internal::Pow10(p - 22);
|
||||||
|
p = 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
|
||||||
|
*result = FastPath(d, p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute an approximation and see if it is within 1/2 ULP
|
||||||
|
inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
|
||||||
|
uint64_t significand = 0;
|
||||||
|
size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
||||||
|
for (; i < length; i++) {
|
||||||
|
if (significand > CEREAL_RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
|
||||||
|
(significand == CEREAL_RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
|
||||||
|
break;
|
||||||
|
significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < length && decimals[i] >= '5') // Rounding
|
||||||
|
significand++;
|
||||||
|
|
||||||
|
size_t remaining = length - i;
|
||||||
|
const unsigned kUlpShift = 3;
|
||||||
|
const unsigned kUlp = 1 << kUlpShift;
|
||||||
|
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
|
||||||
|
|
||||||
|
DiyFp v(significand, 0);
|
||||||
|
v = v.Normalize();
|
||||||
|
error <<= -v.e;
|
||||||
|
|
||||||
|
const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
|
||||||
|
|
||||||
|
int actualExp;
|
||||||
|
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
||||||
|
if (actualExp != dExp) {
|
||||||
|
static const DiyFp kPow10[] = {
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
|
||||||
|
DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
|
||||||
|
};
|
||||||
|
int adjustment = dExp - actualExp - 1;
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
|
||||||
|
v = v * kPow10[adjustment];
|
||||||
|
if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
|
||||||
|
error += kUlp / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = v * cachedPower;
|
||||||
|
|
||||||
|
error += kUlp + (error == 0 ? 0 : 1);
|
||||||
|
|
||||||
|
const int oldExp = v.e;
|
||||||
|
v = v.Normalize();
|
||||||
|
error <<= oldExp - v.e;
|
||||||
|
|
||||||
|
const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
|
||||||
|
unsigned precisionSize = 64 - effectiveSignificandSize;
|
||||||
|
if (precisionSize + kUlpShift >= 64) {
|
||||||
|
unsigned scaleExp = (precisionSize + kUlpShift) - 63;
|
||||||
|
v.f >>= scaleExp;
|
||||||
|
v.e += scaleExp;
|
||||||
|
error = (error >> scaleExp) + 1 + static_cast<int>(kUlp);
|
||||||
|
precisionSize -= scaleExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp rounded(v.f >> precisionSize, v.e + static_cast<int>(precisionSize));
|
||||||
|
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||||
|
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
|
||||||
|
if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
|
||||||
|
rounded.f++;
|
||||||
|
if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
|
||||||
|
rounded.f >>= 1;
|
||||||
|
rounded.e++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = rounded.ToDouble();
|
||||||
|
|
||||||
|
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||||
|
const BigInteger dInt(decimals, length);
|
||||||
|
const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
|
||||||
|
Double a(approx);
|
||||||
|
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
||||||
|
if (cmp < 0)
|
||||||
|
return a.Value(); // within half ULP
|
||||||
|
else if (cmp == 0) {
|
||||||
|
// Round towards even
|
||||||
|
if (a.Significand() & 1)
|
||||||
|
return a.NextPositiveDouble();
|
||||||
|
else
|
||||||
|
return a.Value();
|
||||||
|
}
|
||||||
|
else // adjustment
|
||||||
|
return a.NextPositiveDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(d >= 0.0);
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(length >= 1);
|
||||||
|
|
||||||
|
double result;
|
||||||
|
if (StrtodFast(d, p, &result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Trim leading zeros
|
||||||
|
while (*decimals == '0' && length > 1) {
|
||||||
|
length--;
|
||||||
|
decimals++;
|
||||||
|
decimalPosition--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim trailing zeros
|
||||||
|
while (decimals[length - 1] == '0' && length > 1) {
|
||||||
|
length--;
|
||||||
|
decimalPosition--;
|
||||||
|
exp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim right-most digits
|
||||||
|
const int kMaxDecimalDigit = 780;
|
||||||
|
if (static_cast<int>(length) > kMaxDecimalDigit) {
|
||||||
|
int delta = (static_cast<int>(length) - kMaxDecimalDigit);
|
||||||
|
exp += delta;
|
||||||
|
decimalPosition -= static_cast<unsigned>(delta);
|
||||||
|
length = kMaxDecimalDigit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too small, underflow to zero
|
||||||
|
if (int(length) + exp < -324)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
|
||||||
|
return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_STRTOD_
|
||||||
46
external/cereal/external/rapidjson/internal/swap.h
vendored
Normal file
46
external/cereal/external/rapidjson/internal/swap.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_INTERNAL_SWAP_H_
|
||||||
|
#define CEREAL_RAPIDJSON_INTERNAL_SWAP_H_
|
||||||
|
|
||||||
|
#include "../rapidjson.h"
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! Custom swap() to avoid dependency on C++ <algorithm> header
|
||||||
|
/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
|
||||||
|
\note This has the same semantics as std::swap().
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
inline void Swap(T& a, T& b) CEREAL_RAPIDJSON_NOEXCEPT {
|
||||||
|
T tmp = a;
|
||||||
|
a = b;
|
||||||
|
b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_INTERNAL_SWAP_H_
|
||||||
116
external/cereal/external/rapidjson/istreamwrapper.h
vendored
Normal file
116
external/cereal/external/rapidjson/istreamwrapper.h
vendored
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ISTREAMWRAPPER_H_
|
||||||
|
#define CEREAL_RAPIDJSON_ISTREAMWRAPPER_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(4127) // ignore assert(false) for triggering exception
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
|
||||||
|
/*!
|
||||||
|
The classes can be wrapped including but not limited to:
|
||||||
|
|
||||||
|
- \c std::istringstream
|
||||||
|
- \c std::stringstream
|
||||||
|
- \c std::wistringstream
|
||||||
|
- \c std::wstringstream
|
||||||
|
- \c std::ifstream
|
||||||
|
- \c std::fstream
|
||||||
|
- \c std::wifstream
|
||||||
|
- \c std::wfstream
|
||||||
|
|
||||||
|
\tparam StreamType Class derived from \c std::basic_istream.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename StreamType>
|
||||||
|
class BasicIStreamWrapper {
|
||||||
|
public:
|
||||||
|
typedef typename StreamType::char_type Ch;
|
||||||
|
BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {}
|
||||||
|
|
||||||
|
Ch Peek() const {
|
||||||
|
typename StreamType::int_type c = stream_.peek();
|
||||||
|
return CEREAL_RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
Ch Take() {
|
||||||
|
typename StreamType::int_type c = stream_.get();
|
||||||
|
if (CEREAL_RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) {
|
||||||
|
count_++;
|
||||||
|
return static_cast<Ch>(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// tellg() may return -1 when failed. So we count by ourself.
|
||||||
|
size_t Tell() const { return count_; }
|
||||||
|
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
void Flush() { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
// For encoding detection only.
|
||||||
|
const Ch* Peek4() const {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream.
|
||||||
|
int i;
|
||||||
|
bool hasError = false;
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
typename StreamType::int_type c = stream_.get();
|
||||||
|
if (c == StreamType::traits_type::eof()) {
|
||||||
|
hasError = true;
|
||||||
|
stream_.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
peekBuffer_[i] = static_cast<Ch>(c);
|
||||||
|
}
|
||||||
|
for (--i; i >= 0; --i)
|
||||||
|
stream_.putback(peekBuffer_[i]);
|
||||||
|
return !hasError ? peekBuffer_ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BasicIStreamWrapper(const BasicIStreamWrapper&);
|
||||||
|
BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
|
||||||
|
|
||||||
|
StreamType& stream_;
|
||||||
|
size_t count_; //!< Number of characters read. Note:
|
||||||
|
mutable Ch peekBuffer_[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
|
||||||
|
typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
|
||||||
|
|
||||||
|
#if defined(__clang__) || defined(_MSC_VER)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_ISTREAMWRAPPER_H_
|
||||||
70
external/cereal/external/rapidjson/memorybuffer.h
vendored
Normal file
70
external/cereal/external/rapidjson/memorybuffer.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_MEMORYBUFFER_H_
|
||||||
|
#define CEREAL_RAPIDJSON_MEMORYBUFFER_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include "internal/stack.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Represents an in-memory output byte stream.
|
||||||
|
/*!
|
||||||
|
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
|
||||||
|
|
||||||
|
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
|
||||||
|
|
||||||
|
Differences between MemoryBuffer and StringBuffer:
|
||||||
|
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
|
||||||
|
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
|
||||||
|
|
||||||
|
\tparam Allocator type for allocating memory buffer.
|
||||||
|
\note implements Stream concept
|
||||||
|
*/
|
||||||
|
template <typename Allocator = CrtAllocator>
|
||||||
|
struct GenericMemoryBuffer {
|
||||||
|
typedef char Ch; // byte
|
||||||
|
|
||||||
|
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||||
|
|
||||||
|
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||||
|
void Flush() {}
|
||||||
|
|
||||||
|
void Clear() { stack_.Clear(); }
|
||||||
|
void ShrinkToFit() { stack_.ShrinkToFit(); }
|
||||||
|
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||||
|
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||||
|
|
||||||
|
const Ch* GetBuffer() const {
|
||||||
|
return stack_.template Bottom<Ch>();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSize() const { return stack_.GetSize(); }
|
||||||
|
|
||||||
|
static const size_t kDefaultCapacity = 256;
|
||||||
|
mutable internal::Stack<Allocator> stack_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef GenericMemoryBuffer<> MemoryBuffer;
|
||||||
|
|
||||||
|
//! Implement specialized version of PutN() with memset() for better performance.
|
||||||
|
template<>
|
||||||
|
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
|
||||||
|
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_MEMORYBUFFER_H_
|
||||||
76
external/cereal/external/rapidjson/memorystream.h
vendored
Normal file
76
external/cereal/external/rapidjson/memorystream.h
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_MEMORYSTREAM_H_
|
||||||
|
#define CEREAL_RAPIDJSON_MEMORYSTREAM_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(missing-noreturn)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF( 4127 ) // ignore assert(false) for triggering exception
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Represents an in-memory input byte stream.
|
||||||
|
/*!
|
||||||
|
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
|
||||||
|
|
||||||
|
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
|
||||||
|
|
||||||
|
Differences between MemoryStream and StringStream:
|
||||||
|
1. StringStream has encoding but MemoryStream is a byte stream.
|
||||||
|
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
|
||||||
|
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
|
||||||
|
\note implements Stream concept
|
||||||
|
*/
|
||||||
|
struct MemoryStream {
|
||||||
|
typedef char Ch; // byte
|
||||||
|
|
||||||
|
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
|
||||||
|
|
||||||
|
Ch Peek() const { return CEREAL_RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
|
||||||
|
Ch Take() { return CEREAL_RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
|
||||||
|
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
|
||||||
|
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
void Flush() { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
// For encoding detection only.
|
||||||
|
const Ch* Peek4() const {
|
||||||
|
return Tell() + 4 <= size_ ? src_ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Ch* src_; //!< Current read position.
|
||||||
|
const Ch* begin_; //!< Original head of the string.
|
||||||
|
const Ch* end_; //!< End of stream.
|
||||||
|
size_t size_; //!< Size of the stream.
|
||||||
|
};
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if defined(__clang__) || defined(_MSC_VER)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_MEMORYBUFFER_H_
|
||||||
316
external/cereal/external/rapidjson/msinttypes/inttypes.h
vendored
Normal file
316
external/cereal/external/rapidjson/msinttypes/inttypes.h
vendored
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
|
||||||
|
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||||
|
//
|
||||||
|
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the product nor the names of its contributors may
|
||||||
|
// be used to endorse or promote products derived from this software
|
||||||
|
// without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// The above software in this distribution may have been modified by
|
||||||
|
// THL A29 Limited ("Tencent Modifications").
|
||||||
|
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||||
|
|
||||||
|
#ifndef _MSC_VER // [
|
||||||
|
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||||
|
#endif // _MSC_VER ]
|
||||||
|
|
||||||
|
#ifndef _MSC_INTTYPES_H_ // [
|
||||||
|
#define _MSC_INTTYPES_H_
|
||||||
|
|
||||||
|
#if _MSC_VER > 1000
|
||||||
|
#pragma once
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
// miloyip: VC supports inttypes.h since VC2013
|
||||||
|
#if _MSC_VER >= 1800
|
||||||
|
#include <inttypes.h>
|
||||||
|
#else
|
||||||
|
|
||||||
|
// 7.8 Format conversion of integer types
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
intmax_t quot;
|
||||||
|
intmax_t rem;
|
||||||
|
} imaxdiv_t;
|
||||||
|
|
||||||
|
// 7.8.1 Macros for format specifiers
|
||||||
|
|
||||||
|
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
|
||||||
|
|
||||||
|
// The fprintf macros for signed integers are:
|
||||||
|
#define PRId8 "d"
|
||||||
|
#define PRIi8 "i"
|
||||||
|
#define PRIdLEAST8 "d"
|
||||||
|
#define PRIiLEAST8 "i"
|
||||||
|
#define PRIdFAST8 "d"
|
||||||
|
#define PRIiFAST8 "i"
|
||||||
|
|
||||||
|
#define PRId16 "hd"
|
||||||
|
#define PRIi16 "hi"
|
||||||
|
#define PRIdLEAST16 "hd"
|
||||||
|
#define PRIiLEAST16 "hi"
|
||||||
|
#define PRIdFAST16 "hd"
|
||||||
|
#define PRIiFAST16 "hi"
|
||||||
|
|
||||||
|
#define PRId32 "I32d"
|
||||||
|
#define PRIi32 "I32i"
|
||||||
|
#define PRIdLEAST32 "I32d"
|
||||||
|
#define PRIiLEAST32 "I32i"
|
||||||
|
#define PRIdFAST32 "I32d"
|
||||||
|
#define PRIiFAST32 "I32i"
|
||||||
|
|
||||||
|
#define PRId64 "I64d"
|
||||||
|
#define PRIi64 "I64i"
|
||||||
|
#define PRIdLEAST64 "I64d"
|
||||||
|
#define PRIiLEAST64 "I64i"
|
||||||
|
#define PRIdFAST64 "I64d"
|
||||||
|
#define PRIiFAST64 "I64i"
|
||||||
|
|
||||||
|
#define PRIdMAX "I64d"
|
||||||
|
#define PRIiMAX "I64i"
|
||||||
|
|
||||||
|
#define PRIdPTR "Id"
|
||||||
|
#define PRIiPTR "Ii"
|
||||||
|
|
||||||
|
// The fprintf macros for unsigned integers are:
|
||||||
|
#define PRIo8 "o"
|
||||||
|
#define PRIu8 "u"
|
||||||
|
#define PRIx8 "x"
|
||||||
|
#define PRIX8 "X"
|
||||||
|
#define PRIoLEAST8 "o"
|
||||||
|
#define PRIuLEAST8 "u"
|
||||||
|
#define PRIxLEAST8 "x"
|
||||||
|
#define PRIXLEAST8 "X"
|
||||||
|
#define PRIoFAST8 "o"
|
||||||
|
#define PRIuFAST8 "u"
|
||||||
|
#define PRIxFAST8 "x"
|
||||||
|
#define PRIXFAST8 "X"
|
||||||
|
|
||||||
|
#define PRIo16 "ho"
|
||||||
|
#define PRIu16 "hu"
|
||||||
|
#define PRIx16 "hx"
|
||||||
|
#define PRIX16 "hX"
|
||||||
|
#define PRIoLEAST16 "ho"
|
||||||
|
#define PRIuLEAST16 "hu"
|
||||||
|
#define PRIxLEAST16 "hx"
|
||||||
|
#define PRIXLEAST16 "hX"
|
||||||
|
#define PRIoFAST16 "ho"
|
||||||
|
#define PRIuFAST16 "hu"
|
||||||
|
#define PRIxFAST16 "hx"
|
||||||
|
#define PRIXFAST16 "hX"
|
||||||
|
|
||||||
|
#define PRIo32 "I32o"
|
||||||
|
#define PRIu32 "I32u"
|
||||||
|
#define PRIx32 "I32x"
|
||||||
|
#define PRIX32 "I32X"
|
||||||
|
#define PRIoLEAST32 "I32o"
|
||||||
|
#define PRIuLEAST32 "I32u"
|
||||||
|
#define PRIxLEAST32 "I32x"
|
||||||
|
#define PRIXLEAST32 "I32X"
|
||||||
|
#define PRIoFAST32 "I32o"
|
||||||
|
#define PRIuFAST32 "I32u"
|
||||||
|
#define PRIxFAST32 "I32x"
|
||||||
|
#define PRIXFAST32 "I32X"
|
||||||
|
|
||||||
|
#define PRIo64 "I64o"
|
||||||
|
#define PRIu64 "I64u"
|
||||||
|
#define PRIx64 "I64x"
|
||||||
|
#define PRIX64 "I64X"
|
||||||
|
#define PRIoLEAST64 "I64o"
|
||||||
|
#define PRIuLEAST64 "I64u"
|
||||||
|
#define PRIxLEAST64 "I64x"
|
||||||
|
#define PRIXLEAST64 "I64X"
|
||||||
|
#define PRIoFAST64 "I64o"
|
||||||
|
#define PRIuFAST64 "I64u"
|
||||||
|
#define PRIxFAST64 "I64x"
|
||||||
|
#define PRIXFAST64 "I64X"
|
||||||
|
|
||||||
|
#define PRIoMAX "I64o"
|
||||||
|
#define PRIuMAX "I64u"
|
||||||
|
#define PRIxMAX "I64x"
|
||||||
|
#define PRIXMAX "I64X"
|
||||||
|
|
||||||
|
#define PRIoPTR "Io"
|
||||||
|
#define PRIuPTR "Iu"
|
||||||
|
#define PRIxPTR "Ix"
|
||||||
|
#define PRIXPTR "IX"
|
||||||
|
|
||||||
|
// The fscanf macros for signed integers are:
|
||||||
|
#define SCNd8 "d"
|
||||||
|
#define SCNi8 "i"
|
||||||
|
#define SCNdLEAST8 "d"
|
||||||
|
#define SCNiLEAST8 "i"
|
||||||
|
#define SCNdFAST8 "d"
|
||||||
|
#define SCNiFAST8 "i"
|
||||||
|
|
||||||
|
#define SCNd16 "hd"
|
||||||
|
#define SCNi16 "hi"
|
||||||
|
#define SCNdLEAST16 "hd"
|
||||||
|
#define SCNiLEAST16 "hi"
|
||||||
|
#define SCNdFAST16 "hd"
|
||||||
|
#define SCNiFAST16 "hi"
|
||||||
|
|
||||||
|
#define SCNd32 "ld"
|
||||||
|
#define SCNi32 "li"
|
||||||
|
#define SCNdLEAST32 "ld"
|
||||||
|
#define SCNiLEAST32 "li"
|
||||||
|
#define SCNdFAST32 "ld"
|
||||||
|
#define SCNiFAST32 "li"
|
||||||
|
|
||||||
|
#define SCNd64 "I64d"
|
||||||
|
#define SCNi64 "I64i"
|
||||||
|
#define SCNdLEAST64 "I64d"
|
||||||
|
#define SCNiLEAST64 "I64i"
|
||||||
|
#define SCNdFAST64 "I64d"
|
||||||
|
#define SCNiFAST64 "I64i"
|
||||||
|
|
||||||
|
#define SCNdMAX "I64d"
|
||||||
|
#define SCNiMAX "I64i"
|
||||||
|
|
||||||
|
#ifdef _WIN64 // [
|
||||||
|
# define SCNdPTR "I64d"
|
||||||
|
# define SCNiPTR "I64i"
|
||||||
|
#else // _WIN64 ][
|
||||||
|
# define SCNdPTR "ld"
|
||||||
|
# define SCNiPTR "li"
|
||||||
|
#endif // _WIN64 ]
|
||||||
|
|
||||||
|
// The fscanf macros for unsigned integers are:
|
||||||
|
#define SCNo8 "o"
|
||||||
|
#define SCNu8 "u"
|
||||||
|
#define SCNx8 "x"
|
||||||
|
#define SCNX8 "X"
|
||||||
|
#define SCNoLEAST8 "o"
|
||||||
|
#define SCNuLEAST8 "u"
|
||||||
|
#define SCNxLEAST8 "x"
|
||||||
|
#define SCNXLEAST8 "X"
|
||||||
|
#define SCNoFAST8 "o"
|
||||||
|
#define SCNuFAST8 "u"
|
||||||
|
#define SCNxFAST8 "x"
|
||||||
|
#define SCNXFAST8 "X"
|
||||||
|
|
||||||
|
#define SCNo16 "ho"
|
||||||
|
#define SCNu16 "hu"
|
||||||
|
#define SCNx16 "hx"
|
||||||
|
#define SCNX16 "hX"
|
||||||
|
#define SCNoLEAST16 "ho"
|
||||||
|
#define SCNuLEAST16 "hu"
|
||||||
|
#define SCNxLEAST16 "hx"
|
||||||
|
#define SCNXLEAST16 "hX"
|
||||||
|
#define SCNoFAST16 "ho"
|
||||||
|
#define SCNuFAST16 "hu"
|
||||||
|
#define SCNxFAST16 "hx"
|
||||||
|
#define SCNXFAST16 "hX"
|
||||||
|
|
||||||
|
#define SCNo32 "lo"
|
||||||
|
#define SCNu32 "lu"
|
||||||
|
#define SCNx32 "lx"
|
||||||
|
#define SCNX32 "lX"
|
||||||
|
#define SCNoLEAST32 "lo"
|
||||||
|
#define SCNuLEAST32 "lu"
|
||||||
|
#define SCNxLEAST32 "lx"
|
||||||
|
#define SCNXLEAST32 "lX"
|
||||||
|
#define SCNoFAST32 "lo"
|
||||||
|
#define SCNuFAST32 "lu"
|
||||||
|
#define SCNxFAST32 "lx"
|
||||||
|
#define SCNXFAST32 "lX"
|
||||||
|
|
||||||
|
#define SCNo64 "I64o"
|
||||||
|
#define SCNu64 "I64u"
|
||||||
|
#define SCNx64 "I64x"
|
||||||
|
#define SCNX64 "I64X"
|
||||||
|
#define SCNoLEAST64 "I64o"
|
||||||
|
#define SCNuLEAST64 "I64u"
|
||||||
|
#define SCNxLEAST64 "I64x"
|
||||||
|
#define SCNXLEAST64 "I64X"
|
||||||
|
#define SCNoFAST64 "I64o"
|
||||||
|
#define SCNuFAST64 "I64u"
|
||||||
|
#define SCNxFAST64 "I64x"
|
||||||
|
#define SCNXFAST64 "I64X"
|
||||||
|
|
||||||
|
#define SCNoMAX "I64o"
|
||||||
|
#define SCNuMAX "I64u"
|
||||||
|
#define SCNxMAX "I64x"
|
||||||
|
#define SCNXMAX "I64X"
|
||||||
|
|
||||||
|
#ifdef _WIN64 // [
|
||||||
|
# define SCNoPTR "I64o"
|
||||||
|
# define SCNuPTR "I64u"
|
||||||
|
# define SCNxPTR "I64x"
|
||||||
|
# define SCNXPTR "I64X"
|
||||||
|
#else // _WIN64 ][
|
||||||
|
# define SCNoPTR "lo"
|
||||||
|
# define SCNuPTR "lu"
|
||||||
|
# define SCNxPTR "lx"
|
||||||
|
# define SCNXPTR "lX"
|
||||||
|
#endif // _WIN64 ]
|
||||||
|
|
||||||
|
#endif // __STDC_FORMAT_MACROS ]
|
||||||
|
|
||||||
|
// 7.8.2 Functions for greatest-width integer types
|
||||||
|
|
||||||
|
// 7.8.2.1 The imaxabs function
|
||||||
|
#define imaxabs _abs64
|
||||||
|
|
||||||
|
// 7.8.2.2 The imaxdiv function
|
||||||
|
|
||||||
|
// This is modified version of div() function from Microsoft's div.c found
|
||||||
|
// in %MSVC.NET%\crt\src\div.c
|
||||||
|
#ifdef STATIC_IMAXDIV // [
|
||||||
|
static
|
||||||
|
#else // STATIC_IMAXDIV ][
|
||||||
|
_inline
|
||||||
|
#endif // STATIC_IMAXDIV ]
|
||||||
|
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||||
|
{
|
||||||
|
imaxdiv_t result;
|
||||||
|
|
||||||
|
result.quot = numer / denom;
|
||||||
|
result.rem = numer % denom;
|
||||||
|
|
||||||
|
if (numer < 0 && result.rem > 0) {
|
||||||
|
// did division wrong; must fix up
|
||||||
|
++result.quot;
|
||||||
|
result.rem -= denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||||
|
#define strtoimax _strtoi64
|
||||||
|
#define strtoumax _strtoui64
|
||||||
|
|
||||||
|
// 7.8.2.4 The wcstoimax and wcstoumax functions
|
||||||
|
#define wcstoimax _wcstoi64
|
||||||
|
#define wcstoumax _wcstoui64
|
||||||
|
|
||||||
|
#endif // _MSC_VER >= 1800
|
||||||
|
|
||||||
|
#endif // _MSC_INTTYPES_H_ ]
|
||||||
300
external/cereal/external/rapidjson/msinttypes/stdint.h
vendored
Normal file
300
external/cereal/external/rapidjson/msinttypes/stdint.h
vendored
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||||
|
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||||
|
//
|
||||||
|
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the product nor the names of its contributors may
|
||||||
|
// be used to endorse or promote products derived from this software
|
||||||
|
// without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// The above software in this distribution may have been modified by
|
||||||
|
// THL A29 Limited ("Tencent Modifications").
|
||||||
|
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||||
|
|
||||||
|
#ifndef _MSC_VER // [
|
||||||
|
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||||
|
#endif // _MSC_VER ]
|
||||||
|
|
||||||
|
#ifndef _MSC_STDINT_H_ // [
|
||||||
|
#define _MSC_STDINT_H_
|
||||||
|
|
||||||
|
#if _MSC_VER > 1000
|
||||||
|
#pragma once
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
|
||||||
|
#if _MSC_VER >= 1600 // [
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||||
|
|
||||||
|
#undef INT8_C
|
||||||
|
#undef INT16_C
|
||||||
|
#undef INT32_C
|
||||||
|
#undef INT64_C
|
||||||
|
#undef UINT8_C
|
||||||
|
#undef UINT16_C
|
||||||
|
#undef UINT32_C
|
||||||
|
#undef UINT64_C
|
||||||
|
|
||||||
|
// 7.18.4.1 Macros for minimum-width integer constants
|
||||||
|
|
||||||
|
#define INT8_C(val) val##i8
|
||||||
|
#define INT16_C(val) val##i16
|
||||||
|
#define INT32_C(val) val##i32
|
||||||
|
#define INT64_C(val) val##i64
|
||||||
|
|
||||||
|
#define UINT8_C(val) val##ui8
|
||||||
|
#define UINT16_C(val) val##ui16
|
||||||
|
#define UINT32_C(val) val##ui32
|
||||||
|
#define UINT64_C(val) val##ui64
|
||||||
|
|
||||||
|
// 7.18.4.2 Macros for greatest-width integer constants
|
||||||
|
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||||
|
// Check out Issue 9 for the details.
|
||||||
|
#ifndef INTMAX_C // [
|
||||||
|
# define INTMAX_C INT64_C
|
||||||
|
#endif // INTMAX_C ]
|
||||||
|
#ifndef UINTMAX_C // [
|
||||||
|
# define UINTMAX_C UINT64_C
|
||||||
|
#endif // UINTMAX_C ]
|
||||||
|
|
||||||
|
#endif // __STDC_CONSTANT_MACROS ]
|
||||||
|
|
||||||
|
#else // ] _MSC_VER >= 1700 [
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||||
|
// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
|
||||||
|
// or compiler would give many errors like this:
|
||||||
|
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||||
|
#if defined(__cplusplus) && !defined(_M_ARM)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
# include <wchar.h>
|
||||||
|
#if defined(__cplusplus) && !defined(_M_ARM)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||||
|
#ifndef _W64
|
||||||
|
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||||
|
# define _W64 __w64
|
||||||
|
# else
|
||||||
|
# define _W64
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 7.18.1 Integer types
|
||||||
|
|
||||||
|
// 7.18.1.1 Exact-width integer types
|
||||||
|
|
||||||
|
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||||
|
// realize that, e.g. char has the same size as __int8
|
||||||
|
// so we give up on __intX for them.
|
||||||
|
#if (_MSC_VER < 1300)
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef signed short int16_t;
|
||||||
|
typedef signed int int32_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
#else
|
||||||
|
typedef signed __int8 int8_t;
|
||||||
|
typedef signed __int16 int16_t;
|
||||||
|
typedef signed __int32 int32_t;
|
||||||
|
typedef unsigned __int8 uint8_t;
|
||||||
|
typedef unsigned __int16 uint16_t;
|
||||||
|
typedef unsigned __int32 uint32_t;
|
||||||
|
#endif
|
||||||
|
typedef signed __int64 int64_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
|
||||||
|
|
||||||
|
// 7.18.1.2 Minimum-width integer types
|
||||||
|
typedef int8_t int_least8_t;
|
||||||
|
typedef int16_t int_least16_t;
|
||||||
|
typedef int32_t int_least32_t;
|
||||||
|
typedef int64_t int_least64_t;
|
||||||
|
typedef uint8_t uint_least8_t;
|
||||||
|
typedef uint16_t uint_least16_t;
|
||||||
|
typedef uint32_t uint_least32_t;
|
||||||
|
typedef uint64_t uint_least64_t;
|
||||||
|
|
||||||
|
// 7.18.1.3 Fastest minimum-width integer types
|
||||||
|
typedef int8_t int_fast8_t;
|
||||||
|
typedef int16_t int_fast16_t;
|
||||||
|
typedef int32_t int_fast32_t;
|
||||||
|
typedef int64_t int_fast64_t;
|
||||||
|
typedef uint8_t uint_fast8_t;
|
||||||
|
typedef uint16_t uint_fast16_t;
|
||||||
|
typedef uint32_t uint_fast32_t;
|
||||||
|
typedef uint64_t uint_fast64_t;
|
||||||
|
|
||||||
|
// 7.18.1.4 Integer types capable of holding object pointers
|
||||||
|
#ifdef _WIN64 // [
|
||||||
|
typedef signed __int64 intptr_t;
|
||||||
|
typedef unsigned __int64 uintptr_t;
|
||||||
|
#else // _WIN64 ][
|
||||||
|
typedef _W64 signed int intptr_t;
|
||||||
|
typedef _W64 unsigned int uintptr_t;
|
||||||
|
#endif // _WIN64 ]
|
||||||
|
|
||||||
|
// 7.18.1.5 Greatest-width integer types
|
||||||
|
typedef int64_t intmax_t;
|
||||||
|
typedef uint64_t uintmax_t;
|
||||||
|
|
||||||
|
|
||||||
|
// 7.18.2 Limits of specified-width integer types
|
||||||
|
|
||||||
|
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||||
|
|
||||||
|
// 7.18.2.1 Limits of exact-width integer types
|
||||||
|
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||||
|
#define INT8_MAX _I8_MAX
|
||||||
|
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||||
|
#define INT16_MAX _I16_MAX
|
||||||
|
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||||
|
#define INT32_MAX _I32_MAX
|
||||||
|
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||||
|
#define INT64_MAX _I64_MAX
|
||||||
|
#define UINT8_MAX _UI8_MAX
|
||||||
|
#define UINT16_MAX _UI16_MAX
|
||||||
|
#define UINT32_MAX _UI32_MAX
|
||||||
|
#define UINT64_MAX _UI64_MAX
|
||||||
|
|
||||||
|
// 7.18.2.2 Limits of minimum-width integer types
|
||||||
|
#define INT_LEAST8_MIN INT8_MIN
|
||||||
|
#define INT_LEAST8_MAX INT8_MAX
|
||||||
|
#define INT_LEAST16_MIN INT16_MIN
|
||||||
|
#define INT_LEAST16_MAX INT16_MAX
|
||||||
|
#define INT_LEAST32_MIN INT32_MIN
|
||||||
|
#define INT_LEAST32_MAX INT32_MAX
|
||||||
|
#define INT_LEAST64_MIN INT64_MIN
|
||||||
|
#define INT_LEAST64_MAX INT64_MAX
|
||||||
|
#define UINT_LEAST8_MAX UINT8_MAX
|
||||||
|
#define UINT_LEAST16_MAX UINT16_MAX
|
||||||
|
#define UINT_LEAST32_MAX UINT32_MAX
|
||||||
|
#define UINT_LEAST64_MAX UINT64_MAX
|
||||||
|
|
||||||
|
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||||
|
#define INT_FAST8_MIN INT8_MIN
|
||||||
|
#define INT_FAST8_MAX INT8_MAX
|
||||||
|
#define INT_FAST16_MIN INT16_MIN
|
||||||
|
#define INT_FAST16_MAX INT16_MAX
|
||||||
|
#define INT_FAST32_MIN INT32_MIN
|
||||||
|
#define INT_FAST32_MAX INT32_MAX
|
||||||
|
#define INT_FAST64_MIN INT64_MIN
|
||||||
|
#define INT_FAST64_MAX INT64_MAX
|
||||||
|
#define UINT_FAST8_MAX UINT8_MAX
|
||||||
|
#define UINT_FAST16_MAX UINT16_MAX
|
||||||
|
#define UINT_FAST32_MAX UINT32_MAX
|
||||||
|
#define UINT_FAST64_MAX UINT64_MAX
|
||||||
|
|
||||||
|
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||||
|
#ifdef _WIN64 // [
|
||||||
|
# define INTPTR_MIN INT64_MIN
|
||||||
|
# define INTPTR_MAX INT64_MAX
|
||||||
|
# define UINTPTR_MAX UINT64_MAX
|
||||||
|
#else // _WIN64 ][
|
||||||
|
# define INTPTR_MIN INT32_MIN
|
||||||
|
# define INTPTR_MAX INT32_MAX
|
||||||
|
# define UINTPTR_MAX UINT32_MAX
|
||||||
|
#endif // _WIN64 ]
|
||||||
|
|
||||||
|
// 7.18.2.5 Limits of greatest-width integer types
|
||||||
|
#define INTMAX_MIN INT64_MIN
|
||||||
|
#define INTMAX_MAX INT64_MAX
|
||||||
|
#define UINTMAX_MAX UINT64_MAX
|
||||||
|
|
||||||
|
// 7.18.3 Limits of other integer types
|
||||||
|
|
||||||
|
#ifdef _WIN64 // [
|
||||||
|
# define PTRDIFF_MIN _I64_MIN
|
||||||
|
# define PTRDIFF_MAX _I64_MAX
|
||||||
|
#else // _WIN64 ][
|
||||||
|
# define PTRDIFF_MIN _I32_MIN
|
||||||
|
# define PTRDIFF_MAX _I32_MAX
|
||||||
|
#endif // _WIN64 ]
|
||||||
|
|
||||||
|
#define SIG_ATOMIC_MIN INT_MIN
|
||||||
|
#define SIG_ATOMIC_MAX INT_MAX
|
||||||
|
|
||||||
|
#ifndef SIZE_MAX // [
|
||||||
|
# ifdef _WIN64 // [
|
||||||
|
# define SIZE_MAX _UI64_MAX
|
||||||
|
# else // _WIN64 ][
|
||||||
|
# define SIZE_MAX _UI32_MAX
|
||||||
|
# endif // _WIN64 ]
|
||||||
|
#endif // SIZE_MAX ]
|
||||||
|
|
||||||
|
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||||
|
#ifndef WCHAR_MIN // [
|
||||||
|
# define WCHAR_MIN 0
|
||||||
|
#endif // WCHAR_MIN ]
|
||||||
|
#ifndef WCHAR_MAX // [
|
||||||
|
# define WCHAR_MAX _UI16_MAX
|
||||||
|
#endif // WCHAR_MAX ]
|
||||||
|
|
||||||
|
#define WINT_MIN 0
|
||||||
|
#define WINT_MAX _UI16_MAX
|
||||||
|
|
||||||
|
#endif // __STDC_LIMIT_MACROS ]
|
||||||
|
|
||||||
|
|
||||||
|
// 7.18.4 Limits of other integer types
|
||||||
|
|
||||||
|
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||||
|
|
||||||
|
// 7.18.4.1 Macros for minimum-width integer constants
|
||||||
|
|
||||||
|
#define INT8_C(val) val##i8
|
||||||
|
#define INT16_C(val) val##i16
|
||||||
|
#define INT32_C(val) val##i32
|
||||||
|
#define INT64_C(val) val##i64
|
||||||
|
|
||||||
|
#define UINT8_C(val) val##ui8
|
||||||
|
#define UINT16_C(val) val##ui16
|
||||||
|
#define UINT32_C(val) val##ui32
|
||||||
|
#define UINT64_C(val) val##ui64
|
||||||
|
|
||||||
|
// 7.18.4.2 Macros for greatest-width integer constants
|
||||||
|
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||||
|
// Check out Issue 9 for the details.
|
||||||
|
#ifndef INTMAX_C // [
|
||||||
|
# define INTMAX_C INT64_C
|
||||||
|
#endif // INTMAX_C ]
|
||||||
|
#ifndef UINTMAX_C // [
|
||||||
|
# define UINTMAX_C UINT64_C
|
||||||
|
#endif // UINTMAX_C ]
|
||||||
|
|
||||||
|
#endif // __STDC_CONSTANT_MACROS ]
|
||||||
|
|
||||||
|
#endif // _MSC_VER >= 1600 ]
|
||||||
|
|
||||||
|
#endif // _MSC_STDINT_H_ ]
|
||||||
81
external/cereal/external/rapidjson/ostreamwrapper.h
vendored
Normal file
81
external/cereal/external/rapidjson/ostreamwrapper.h
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_OSTREAMWRAPPER_H_
|
||||||
|
#define CEREAL_RAPIDJSON_OSTREAMWRAPPER_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(padded)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
|
||||||
|
/*!
|
||||||
|
The classes can be wrapped including but not limited to:
|
||||||
|
|
||||||
|
- \c std::ostringstream
|
||||||
|
- \c std::stringstream
|
||||||
|
- \c std::wpstringstream
|
||||||
|
- \c std::wstringstream
|
||||||
|
- \c std::ifstream
|
||||||
|
- \c std::fstream
|
||||||
|
- \c std::wofstream
|
||||||
|
- \c std::wfstream
|
||||||
|
|
||||||
|
\tparam StreamType Class derived from \c std::basic_ostream.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename StreamType>
|
||||||
|
class BasicOStreamWrapper {
|
||||||
|
public:
|
||||||
|
typedef typename StreamType::char_type Ch;
|
||||||
|
BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
|
||||||
|
|
||||||
|
void Put(Ch c) {
|
||||||
|
stream_.put(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flush() {
|
||||||
|
stream_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
char Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
char Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
char* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
size_t PutEnd(char*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BasicOStreamWrapper(const BasicOStreamWrapper&);
|
||||||
|
BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
|
||||||
|
|
||||||
|
StreamType& stream_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
|
||||||
|
typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_OSTREAMWRAPPER_H_
|
||||||
1358
external/cereal/external/rapidjson/pointer.h
vendored
Normal file
1358
external/cereal/external/rapidjson/pointer.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
255
external/cereal/external/rapidjson/prettywriter.h
vendored
Normal file
255
external/cereal/external/rapidjson/prettywriter.h
vendored
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_PRETTYWRITER_H_
|
||||||
|
#define CEREAL_RAPIDJSON_PRETTYWRITER_H_
|
||||||
|
|
||||||
|
#include "writer.h"
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(effc++)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Combination of PrettyWriter format flags.
|
||||||
|
/*! \see PrettyWriter::SetFormatOptions
|
||||||
|
*/
|
||||||
|
enum PrettyFormatOptions {
|
||||||
|
kFormatDefault = 0, //!< Default pretty formatting.
|
||||||
|
kFormatSingleLineArray = 1 //!< Format arrays on a single line.
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Writer with indentation and spacing.
|
||||||
|
/*!
|
||||||
|
\tparam OutputStream Type of ouptut os.
|
||||||
|
\tparam SourceEncoding Encoding of source string.
|
||||||
|
\tparam TargetEncoding Encoding of output stream.
|
||||||
|
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||||
|
*/
|
||||||
|
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
|
||||||
|
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
|
||||||
|
public:
|
||||||
|
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
|
||||||
|
typedef typename Base::Ch Ch;
|
||||||
|
|
||||||
|
//! Constructor
|
||||||
|
/*! \param os Output stream.
|
||||||
|
\param allocator User supplied allocator. If it is null, it will create a private one.
|
||||||
|
\param levelDepth Initial capacity of stack.
|
||||||
|
*/
|
||||||
|
explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||||
|
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
|
||||||
|
|
||||||
|
|
||||||
|
explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||||
|
Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
||||||
|
|
||||||
|
//! Set custom indentation.
|
||||||
|
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
||||||
|
\param indentCharCount Number of indent characters for each indentation level.
|
||||||
|
\note The default indentation is 4 spaces.
|
||||||
|
*/
|
||||||
|
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
|
||||||
|
indentChar_ = indentChar;
|
||||||
|
indentCharCount_ = indentCharCount;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Set pretty writer formatting options.
|
||||||
|
/*! \param options Formatting options.
|
||||||
|
*/
|
||||||
|
PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
|
||||||
|
formatOptions_ = options;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @name Implementation of Handler
|
||||||
|
\see Handler
|
||||||
|
*/
|
||||||
|
//@{
|
||||||
|
|
||||||
|
bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
|
||||||
|
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
|
||||||
|
bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
|
||||||
|
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
|
||||||
|
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
|
||||||
|
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
|
||||||
|
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
|
||||||
|
|
||||||
|
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
|
||||||
|
(void)copy;
|
||||||
|
PrettyPrefix(kNumberType);
|
||||||
|
return Base::WriteString(str, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||||
|
(void)copy;
|
||||||
|
PrettyPrefix(kStringType);
|
||||||
|
return Base::WriteString(str, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
bool String(const std::basic_string<Ch>& str) {
|
||||||
|
return String(str.data(), SizeType(str.size()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool StartObject() {
|
||||||
|
PrettyPrefix(kObjectType);
|
||||||
|
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
|
||||||
|
return Base::WriteStartObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
bool Key(const std::basic_string<Ch>& str) {
|
||||||
|
return Key(str.data(), SizeType(str.size()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool EndObject(SizeType memberCount = 0) {
|
||||||
|
(void)memberCount;
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||||
|
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||||
|
|
||||||
|
if (!empty) {
|
||||||
|
Base::os_->Put('\n');
|
||||||
|
WriteIndent();
|
||||||
|
}
|
||||||
|
bool ret = Base::WriteEndObject();
|
||||||
|
(void)ret;
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(ret == true);
|
||||||
|
if (Base::level_stack_.Empty()) // end of json text
|
||||||
|
Base::os_->Flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StartArray() {
|
||||||
|
PrettyPrefix(kArrayType);
|
||||||
|
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
|
||||||
|
return Base::WriteStartArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EndArray(SizeType memberCount = 0) {
|
||||||
|
(void)memberCount;
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||||
|
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||||
|
|
||||||
|
if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
|
||||||
|
Base::os_->Put('\n');
|
||||||
|
WriteIndent();
|
||||||
|
}
|
||||||
|
bool ret = Base::WriteEndArray();
|
||||||
|
(void)ret;
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(ret == true);
|
||||||
|
if (Base::level_stack_.Empty()) // end of json text
|
||||||
|
Base::os_->Flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Convenience extensions */
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Simpler but slower overload.
|
||||||
|
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||||
|
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! Write a raw JSON value.
|
||||||
|
/*!
|
||||||
|
For user to write a stringified JSON as a value.
|
||||||
|
|
||||||
|
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
|
||||||
|
\param length Length of the json.
|
||||||
|
\param type Type of the root of json.
|
||||||
|
\note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
|
||||||
|
*/
|
||||||
|
bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void PrettyPrefix(Type type) {
|
||||||
|
(void)type;
|
||||||
|
if (Base::level_stack_.GetSize() != 0) { // this value is not at root
|
||||||
|
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
|
||||||
|
|
||||||
|
if (level->inArray) {
|
||||||
|
if (level->valueCount > 0) {
|
||||||
|
Base::os_->Put(','); // add comma if it is not the first element in array
|
||||||
|
if (formatOptions_ & kFormatSingleLineArray)
|
||||||
|
Base::os_->Put(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(formatOptions_ & kFormatSingleLineArray)) {
|
||||||
|
Base::os_->Put('\n');
|
||||||
|
WriteIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // in object
|
||||||
|
if (level->valueCount > 0) {
|
||||||
|
if (level->valueCount % 2 == 0) {
|
||||||
|
Base::os_->Put(',');
|
||||||
|
Base::os_->Put('\n');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Base::os_->Put(':');
|
||||||
|
Base::os_->Put(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Base::os_->Put('\n');
|
||||||
|
|
||||||
|
if (level->valueCount % 2 == 0)
|
||||||
|
WriteIndent();
|
||||||
|
}
|
||||||
|
if (!level->inArray && level->valueCount % 2 == 0)
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||||
|
level->valueCount++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CEREAL_RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
|
||||||
|
Base::hasRoot_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteIndent() {
|
||||||
|
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
|
||||||
|
PutN(*Base::os_, static_cast<typename TargetEncoding::Ch>(indentChar_), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ch indentChar_;
|
||||||
|
unsigned indentCharCount_;
|
||||||
|
PrettyFormatOptions formatOptions_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Prohibit copy constructor & assignment operator.
|
||||||
|
PrettyWriter(const PrettyWriter&);
|
||||||
|
PrettyWriter& operator=(const PrettyWriter&);
|
||||||
|
};
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_
|
||||||
615
external/cereal/external/rapidjson/rapidjson.h
vendored
Normal file
615
external/cereal/external/rapidjson/rapidjson.h
vendored
Normal file
@@ -0,0 +1,615 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_
|
||||||
|
#define CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_
|
||||||
|
|
||||||
|
/*!\file rapidjson.h
|
||||||
|
\brief common definitions and configuration
|
||||||
|
|
||||||
|
\see CEREAL_RAPIDJSON_CONFIG
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \defgroup CEREAL_RAPIDJSON_CONFIG RapidJSON configuration
|
||||||
|
\brief Configuration macros for library features
|
||||||
|
|
||||||
|
Some RapidJSON features are configurable to adapt the library to a wide
|
||||||
|
variety of platforms, environments and usage scenarios. Most of the
|
||||||
|
features can be configured in terms of overriden or predefined
|
||||||
|
preprocessor macros at compile-time.
|
||||||
|
|
||||||
|
Some additional customization is available in the \ref CEREAL_RAPIDJSON_ERRORS APIs.
|
||||||
|
|
||||||
|
\note These macros should be given on the compiler command-line
|
||||||
|
(where applicable) to avoid inconsistent values when compiling
|
||||||
|
different translation units of a single application.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdlib> // malloc(), realloc(), free(), size_t
|
||||||
|
#include <cstring> // memset(), memcpy(), memmove(), memcmp()
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_VERSION_STRING
|
||||||
|
//
|
||||||
|
// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt.
|
||||||
|
//
|
||||||
|
|
||||||
|
//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||||
|
// token stringification
|
||||||
|
#define CEREAL_RAPIDJSON_STRINGIFY(x) CEREAL_RAPIDJSON_DO_STRINGIFY(x)
|
||||||
|
#define CEREAL_RAPIDJSON_DO_STRINGIFY(x) #x
|
||||||
|
//!@endcond
|
||||||
|
|
||||||
|
/*! \def CEREAL_RAPIDJSON_MAJOR_VERSION
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Major version of RapidJSON in integer.
|
||||||
|
*/
|
||||||
|
/*! \def CEREAL_RAPIDJSON_MINOR_VERSION
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Minor version of RapidJSON in integer.
|
||||||
|
*/
|
||||||
|
/*! \def CEREAL_RAPIDJSON_PATCH_VERSION
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Patch version of RapidJSON in integer.
|
||||||
|
*/
|
||||||
|
/*! \def CEREAL_RAPIDJSON_VERSION_STRING
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
|
||||||
|
*/
|
||||||
|
#define CEREAL_RAPIDJSON_MAJOR_VERSION 1
|
||||||
|
#define CEREAL_RAPIDJSON_MINOR_VERSION 0
|
||||||
|
#define CEREAL_RAPIDJSON_PATCH_VERSION 2
|
||||||
|
#define CEREAL_RAPIDJSON_VERSION_STRING \
|
||||||
|
CEREAL_RAPIDJSON_STRINGIFY(CEREAL_RAPIDJSON_MAJOR_VERSION.CEREAL_RAPIDJSON_MINOR_VERSION.CEREAL_RAPIDJSON_PATCH_VERSION)
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_NAMESPACE_(BEGIN|END)
|
||||||
|
/*! \def CEREAL_RAPIDJSON_NAMESPACE
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief provide custom rapidjson namespace
|
||||||
|
|
||||||
|
In order to avoid symbol clashes and/or "One Definition Rule" errors
|
||||||
|
between multiple inclusions of (different versions of) RapidJSON in
|
||||||
|
a single binary, users can customize the name of the main RapidJSON
|
||||||
|
namespace.
|
||||||
|
|
||||||
|
In case of a single nesting level, defining \c CEREAL_RAPIDJSON_NAMESPACE
|
||||||
|
to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
|
||||||
|
levels are needed, both \ref CEREAL_RAPIDJSON_NAMESPACE_BEGIN and \ref
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END need to be defined as well:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// in some .cpp file
|
||||||
|
#define CEREAL_RAPIDJSON_NAMESPACE my::rapidjson
|
||||||
|
#define CEREAL_RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
|
||||||
|
#define CEREAL_RAPIDJSON_NAMESPACE_END } }
|
||||||
|
#include "rapidjson/..."
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\see rapidjson
|
||||||
|
*/
|
||||||
|
/*! \def CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief provide custom rapidjson namespace (opening expression)
|
||||||
|
\see CEREAL_RAPIDJSON_NAMESPACE
|
||||||
|
*/
|
||||||
|
/*! \def CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief provide custom rapidjson namespace (closing expression)
|
||||||
|
\see CEREAL_RAPIDJSON_NAMESPACE
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_NAMESPACE
|
||||||
|
#define CEREAL_RAPIDJSON_NAMESPACE rapidjson
|
||||||
|
#endif
|
||||||
|
#ifndef CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
#define CEREAL_RAPIDJSON_NAMESPACE_BEGIN namespace CEREAL_RAPIDJSON_NAMESPACE {
|
||||||
|
#endif
|
||||||
|
#ifndef CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
#define CEREAL_RAPIDJSON_NAMESPACE_END }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
#ifdef CEREAL_RAPIDJSON_DOXYGEN_RUNNING
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
|
||||||
|
#endif
|
||||||
|
/*! \def CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Enable RapidJSON support for \c std::string
|
||||||
|
|
||||||
|
By defining this preprocessor symbol to \c 1, several convenience functions for using
|
||||||
|
\ref rapidjson::GenericValue with \c std::string are enabled, especially
|
||||||
|
for construction and comparison.
|
||||||
|
|
||||||
|
\hideinitializer
|
||||||
|
*/
|
||||||
|
#endif // !defined(CEREAL_RAPIDJSON_HAS_STDSTRING)
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
#include <string>
|
||||||
|
#endif // CEREAL_RAPIDJSON_HAS_STDSTRING
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_NO_INT64DEFINE
|
||||||
|
|
||||||
|
/*! \def CEREAL_RAPIDJSON_NO_INT64DEFINE
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Use external 64-bit integer types.
|
||||||
|
|
||||||
|
RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
|
||||||
|
to be available at global scope.
|
||||||
|
|
||||||
|
If users have their own definition, define CEREAL_RAPIDJSON_NO_INT64DEFINE to
|
||||||
|
prevent RapidJSON from defining its own types.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_NO_INT64DEFINE
|
||||||
|
//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
|
||||||
|
#include "msinttypes/stdint.h"
|
||||||
|
#include "msinttypes/inttypes.h"
|
||||||
|
#else
|
||||||
|
// Other compilers should have this.
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#endif
|
||||||
|
//!@endcond
|
||||||
|
#ifdef CEREAL_RAPIDJSON_DOXYGEN_RUNNING
|
||||||
|
#define CEREAL_RAPIDJSON_NO_INT64DEFINE
|
||||||
|
#endif
|
||||||
|
#endif // CEREAL_RAPIDJSON_NO_INT64TYPEDEF
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_FORCEINLINE
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_FORCEINLINE
|
||||||
|
//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||||
|
#if defined(_MSC_VER) && defined(NDEBUG)
|
||||||
|
#define CEREAL_RAPIDJSON_FORCEINLINE __forceinline
|
||||||
|
#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
|
||||||
|
#define CEREAL_RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_FORCEINLINE
|
||||||
|
#endif
|
||||||
|
//!@endcond
|
||||||
|
#endif // CEREAL_RAPIDJSON_FORCEINLINE
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_ENDIAN
|
||||||
|
#define CEREAL_RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
|
||||||
|
#define CEREAL_RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
|
||||||
|
|
||||||
|
//! Endianness of the machine.
|
||||||
|
/*!
|
||||||
|
\def CEREAL_RAPIDJSON_ENDIAN
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
|
||||||
|
GCC 4.6 provided macro for detecting endianness of the target machine. But other
|
||||||
|
compilers may not have this. User can define CEREAL_RAPIDJSON_ENDIAN to either
|
||||||
|
\ref CEREAL_RAPIDJSON_LITTLEENDIAN or \ref CEREAL_RAPIDJSON_BIGENDIAN.
|
||||||
|
|
||||||
|
Default detection implemented with reference to
|
||||||
|
\li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
|
||||||
|
\li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ENDIAN
|
||||||
|
// Detect with GCC 4.6's macro
|
||||||
|
# ifdef __BYTE_ORDER__
|
||||||
|
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN
|
||||||
|
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN
|
||||||
|
# else
|
||||||
|
# error Unknown machine endianess detected. User needs to define CEREAL_RAPIDJSON_ENDIAN.
|
||||||
|
# endif // __BYTE_ORDER__
|
||||||
|
// Detect with GLIBC's endian.h
|
||||||
|
# elif defined(__GLIBC__)
|
||||||
|
# include <endian.h>
|
||||||
|
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN
|
||||||
|
# elif (__BYTE_ORDER == __BIG_ENDIAN)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN
|
||||||
|
# else
|
||||||
|
# error Unknown machine endianess detected. User needs to define CEREAL_RAPIDJSON_ENDIAN.
|
||||||
|
# endif // __GLIBC__
|
||||||
|
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
|
||||||
|
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN
|
||||||
|
# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN
|
||||||
|
// Detect with architecture macros
|
||||||
|
# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN
|
||||||
|
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN
|
||||||
|
# elif defined(_MSC_VER) && defined(_M_ARM)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN
|
||||||
|
# elif defined(CEREAL_RAPIDJSON_DOXYGEN_RUNNING)
|
||||||
|
# define CEREAL_RAPIDJSON_ENDIAN
|
||||||
|
# else
|
||||||
|
# error Unknown machine endianess detected. User needs to define CEREAL_RAPIDJSON_ENDIAN.
|
||||||
|
# endif
|
||||||
|
#endif // CEREAL_RAPIDJSON_ENDIAN
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_64BIT
|
||||||
|
|
||||||
|
//! Whether using 64-bit architecture
|
||||||
|
#ifndef CEREAL_RAPIDJSON_64BIT
|
||||||
|
#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__)
|
||||||
|
#define CEREAL_RAPIDJSON_64BIT 1
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_64BIT 0
|
||||||
|
#endif
|
||||||
|
#endif // CEREAL_RAPIDJSON_64BIT
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_ALIGN
|
||||||
|
|
||||||
|
//! Data alignment of the machine.
|
||||||
|
/*! \ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\param x pointer to align
|
||||||
|
|
||||||
|
Some machines require strict data alignment. Currently the default uses 4 bytes
|
||||||
|
alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
|
||||||
|
User can customize by defining the CEREAL_RAPIDJSON_ALIGN function macro.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ALIGN
|
||||||
|
#if CEREAL_RAPIDJSON_64BIT == 1
|
||||||
|
#define CEREAL_RAPIDJSON_ALIGN(x) (((x) + static_cast<uint64_t>(7u)) & ~static_cast<uint64_t>(7u))
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_UINT64_C2
|
||||||
|
|
||||||
|
//! Construct a 64-bit literal by a pair of 32-bit integer.
|
||||||
|
/*!
|
||||||
|
64-bit literal with or without ULL suffix is prone to compiler warnings.
|
||||||
|
UINT64_C() is C macro which cause compilation problems.
|
||||||
|
Use this macro to define 64-bit constants by a pair of 32-bit integer.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_UINT64_C2
|
||||||
|
#define CEREAL_RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
||||||
|
|
||||||
|
//! Use only lower 48-bit address for some pointers.
|
||||||
|
/*!
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
|
||||||
|
This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
|
||||||
|
The higher 16-bit can be used for storing other data.
|
||||||
|
\c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
||||||
|
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
||||||
|
#define CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
|
||||||
|
#endif
|
||||||
|
#endif // CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
|
||||||
|
#if CEREAL_RAPIDJSON_64BIT != 1
|
||||||
|
#error CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when CEREAL_RAPIDJSON_64BIT=1
|
||||||
|
#endif
|
||||||
|
#define CEREAL_RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(CEREAL_RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
|
||||||
|
#define CEREAL_RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(CEREAL_RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
|
||||||
|
#define CEREAL_RAPIDJSON_GETPOINTER(type, p) (p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_SSE2/CEREAL_RAPIDJSON_SSE42/CEREAL_RAPIDJSON_SIMD
|
||||||
|
|
||||||
|
/*! \def CEREAL_RAPIDJSON_SIMD
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief Enable SSE2/SSE4.2 optimization.
|
||||||
|
|
||||||
|
RapidJSON supports optimized implementations for some parsing operations
|
||||||
|
based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible
|
||||||
|
processors.
|
||||||
|
|
||||||
|
To enable these optimizations, two different symbols can be defined;
|
||||||
|
\code
|
||||||
|
// Enable SSE2 optimization.
|
||||||
|
#define CEREAL_RAPIDJSON_SSE2
|
||||||
|
|
||||||
|
// Enable SSE4.2 optimization.
|
||||||
|
#define CEREAL_RAPIDJSON_SSE42
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\c CEREAL_RAPIDJSON_SSE42 takes precedence, if both are defined.
|
||||||
|
|
||||||
|
If any of these symbols is defined, RapidJSON defines the macro
|
||||||
|
\c CEREAL_RAPIDJSON_SIMD to indicate the availability of the optimized code.
|
||||||
|
*/
|
||||||
|
#if defined(CEREAL_RAPIDJSON_SSE2) || defined(CEREAL_RAPIDJSON_SSE42) \
|
||||||
|
|| defined(CEREAL_RAPIDJSON_DOXYGEN_RUNNING)
|
||||||
|
#define CEREAL_RAPIDJSON_SIMD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE
|
||||||
|
/*! \def CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\brief User-provided \c SizeType definition.
|
||||||
|
|
||||||
|
In order to avoid using 32-bit size types for indexing strings and arrays,
|
||||||
|
define this preprocessor symbol and provide the type rapidjson::SizeType
|
||||||
|
before including RapidJSON:
|
||||||
|
\code
|
||||||
|
#define CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE
|
||||||
|
namespace rapidjson { typedef ::std::size_t SizeType; }
|
||||||
|
#include "rapidjson/..."
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\see rapidjson::SizeType
|
||||||
|
*/
|
||||||
|
#ifdef CEREAL_RAPIDJSON_DOXYGEN_RUNNING
|
||||||
|
#define CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE
|
||||||
|
#endif
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
//! Size type (for string lengths, array sizes, etc.)
|
||||||
|
/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
|
||||||
|
instead of using \c size_t. Users may override the SizeType by defining
|
||||||
|
\ref CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE.
|
||||||
|
*/
|
||||||
|
typedef unsigned SizeType;
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// always import std::size_t to rapidjson namespace
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
using std::size_t;
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_ASSERT
|
||||||
|
|
||||||
|
//! Assertion.
|
||||||
|
/*! \ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
By default, rapidjson uses C \c assert() for internal assertions.
|
||||||
|
User can override it by defining CEREAL_RAPIDJSON_ASSERT(x) macro.
|
||||||
|
|
||||||
|
\note Parsing errors are handled and can be customized by the
|
||||||
|
\ref CEREAL_RAPIDJSON_ERRORS APIs.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_ASSERT
|
||||||
|
#include <cassert>
|
||||||
|
#define CEREAL_RAPIDJSON_ASSERT(x) assert(x)
|
||||||
|
#endif // CEREAL_RAPIDJSON_ASSERT
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_STATIC_ASSERT
|
||||||
|
|
||||||
|
// Adopt from boost
|
||||||
|
#ifndef CEREAL_RAPIDJSON_STATIC_ASSERT
|
||||||
|
#ifndef __clang__
|
||||||
|
//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||||
|
#endif
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
template <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||||
|
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||||
|
template<int x> struct StaticAssertTest {};
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_JOIN(X, Y) CEREAL_RAPIDJSON_DO_JOIN(X, Y)
|
||||||
|
#define CEREAL_RAPIDJSON_DO_JOIN(X, Y) CEREAL_RAPIDJSON_DO_JOIN2(X, Y)
|
||||||
|
#define CEREAL_RAPIDJSON_DO_JOIN2(X, Y) X##Y
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define CEREAL_RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||||
|
#endif
|
||||||
|
#ifndef __clang__
|
||||||
|
//!@endcond
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! \def CEREAL_RAPIDJSON_STATIC_ASSERT
|
||||||
|
\brief (Internal) macro to check for conditions at compile-time
|
||||||
|
\param x compile-time condition
|
||||||
|
\hideinitializer
|
||||||
|
*/
|
||||||
|
#define CEREAL_RAPIDJSON_STATIC_ASSERT(x) \
|
||||||
|
typedef ::CEREAL_RAPIDJSON_NAMESPACE::StaticAssertTest< \
|
||||||
|
sizeof(::CEREAL_RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
|
||||||
|
CEREAL_RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) CEREAL_RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_LIKELY, CEREAL_RAPIDJSON_UNLIKELY
|
||||||
|
|
||||||
|
//! Compiler branching hint for expression with high probability to be true.
|
||||||
|
/*!
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\param x Boolean expression likely to be true.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_LIKELY
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define CEREAL_RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_LIKELY(x) (x)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Compiler branching hint for expression with low probability to be true.
|
||||||
|
/*!
|
||||||
|
\ingroup CEREAL_RAPIDJSON_CONFIG
|
||||||
|
\param x Boolean expression unlikely to be true.
|
||||||
|
*/
|
||||||
|
#ifndef CEREAL_RAPIDJSON_UNLIKELY
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define CEREAL_RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_UNLIKELY(x) (x)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
||||||
|
#define CEREAL_RAPIDJSON_MULTILINEMACRO_END \
|
||||||
|
} while((void)0, 0)
|
||||||
|
|
||||||
|
// adopted from Boost
|
||||||
|
#define CEREAL_RAPIDJSON_VERSION_CODE(x,y,z) \
|
||||||
|
(((x)*100000) + ((y)*100) + (z))
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CEREAL_RAPIDJSON_DIAG_PUSH/POP, CEREAL_RAPIDJSON_DIAG_OFF
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define CEREAL_RAPIDJSON_GNUC \
|
||||||
|
CEREAL_RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__) || (defined(CEREAL_RAPIDJSON_GNUC) && CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,2,0))
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_PRAGMA(x) _Pragma(CEREAL_RAPIDJSON_STRINGIFY(x))
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_PRAGMA(x) CEREAL_RAPIDJSON_PRAGMA(GCC diagnostic x)
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_OFF(x) \
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PRAGMA(ignored CEREAL_RAPIDJSON_STRINGIFY(CEREAL_RAPIDJSON_JOIN(-W,x)))
|
||||||
|
|
||||||
|
// push/pop support in Clang and GCC>=4.6
|
||||||
|
#if defined(__clang__) || (defined(CEREAL_RAPIDJSON_GNUC) && CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,6,0))
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_PUSH CEREAL_RAPIDJSON_DIAG_PRAGMA(push)
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_POP CEREAL_RAPIDJSON_DIAG_PRAGMA(pop)
|
||||||
|
#else // GCC >= 4.2, < 4.6
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_PUSH /* ignored */
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_POP /* ignored */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
|
||||||
|
// pragma (MSVC specific)
|
||||||
|
#define CEREAL_RAPIDJSON_PRAGMA(x) __pragma(x)
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_PRAGMA(x) CEREAL_RAPIDJSON_PRAGMA(warning(x))
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_OFF(x) CEREAL_RAPIDJSON_DIAG_PRAGMA(disable: x)
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_PUSH CEREAL_RAPIDJSON_DIAG_PRAGMA(push)
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_POP CEREAL_RAPIDJSON_DIAG_PRAGMA(pop)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_OFF(x) /* ignored */
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_PUSH /* ignored */
|
||||||
|
#define CEREAL_RAPIDJSON_DIAG_POP /* ignored */
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_DIAG_*
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// C++11 features
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
#if defined(__clang__)
|
||||||
|
#if __has_feature(cxx_rvalue_references) && \
|
||||||
|
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
||||||
|
#endif
|
||||||
|
#elif (defined(CEREAL_RAPIDJSON_GNUC) && (CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||||
|
(defined(_MSC_VER) && _MSC_VER >= 1600)
|
||||||
|
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
||||||
|
#endif
|
||||||
|
#endif // CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
|
||||||
|
#elif (defined(CEREAL_RAPIDJSON_GNUC) && (CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||||
|
// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||||
|
#define CEREAL_RAPIDJSON_NOEXCEPT noexcept
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_NOEXCEPT /* noexcept */
|
||||||
|
#endif // CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||||
|
|
||||||
|
// no automatic detection, yet
|
||||||
|
#ifndef CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
|
||||||
|
#elif (defined(CEREAL_RAPIDJSON_GNUC) && (CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||||
|
(defined(_MSC_VER) && _MSC_VER >= 1700)
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR 1
|
||||||
|
#else
|
||||||
|
#define CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR 0
|
||||||
|
#endif
|
||||||
|
#endif // CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR
|
||||||
|
|
||||||
|
//!@endcond
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// new/delete
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_NEW
|
||||||
|
///! customization point for global \c new
|
||||||
|
#define CEREAL_RAPIDJSON_NEW(x) new x
|
||||||
|
#endif
|
||||||
|
#ifndef CEREAL_RAPIDJSON_DELETE
|
||||||
|
///! customization point for global \c delete
|
||||||
|
#define CEREAL_RAPIDJSON_DELETE(x) delete x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Type
|
||||||
|
|
||||||
|
/*! \namespace rapidjson
|
||||||
|
\brief main RapidJSON namespace
|
||||||
|
\see CEREAL_RAPIDJSON_NAMESPACE
|
||||||
|
*/
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Type of JSON value
|
||||||
|
enum Type {
|
||||||
|
kNullType = 0, //!< null
|
||||||
|
kFalseType = 1, //!< false
|
||||||
|
kTrueType = 2, //!< true
|
||||||
|
kObjectType = 3, //!< object
|
||||||
|
kArrayType = 4, //!< array
|
||||||
|
kStringType = 5, //!< string
|
||||||
|
kNumberType = 6 //!< number
|
||||||
|
};
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_
|
||||||
1879
external/cereal/external/rapidjson/reader.h
vendored
Normal file
1879
external/cereal/external/rapidjson/reader.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2006
external/cereal/external/rapidjson/schema.h
vendored
Normal file
2006
external/cereal/external/rapidjson/schema.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
179
external/cereal/external/rapidjson/stream.h
vendored
Normal file
179
external/cereal/external/rapidjson/stream.h
vendored
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#include "rapidjson.h"
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_STREAM_H_
|
||||||
|
#define CEREAL_RAPIDJSON_STREAM_H_
|
||||||
|
|
||||||
|
#include "encodings.h"
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Stream
|
||||||
|
|
||||||
|
/*! \class rapidjson::Stream
|
||||||
|
\brief Concept for reading and writing characters.
|
||||||
|
|
||||||
|
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
|
||||||
|
|
||||||
|
For write-only stream, only need to implement Put() and Flush().
|
||||||
|
|
||||||
|
\code
|
||||||
|
concept Stream {
|
||||||
|
typename Ch; //!< Character type of the stream.
|
||||||
|
|
||||||
|
//! Read the current character from stream without moving the read cursor.
|
||||||
|
Ch Peek() const;
|
||||||
|
|
||||||
|
//! Read the current character from stream and moving the read cursor to next character.
|
||||||
|
Ch Take();
|
||||||
|
|
||||||
|
//! Get the current read cursor.
|
||||||
|
//! \return Number of characters read from start.
|
||||||
|
size_t Tell();
|
||||||
|
|
||||||
|
//! Begin writing operation at the current read pointer.
|
||||||
|
//! \return The begin writer pointer.
|
||||||
|
Ch* PutBegin();
|
||||||
|
|
||||||
|
//! Write a character.
|
||||||
|
void Put(Ch c);
|
||||||
|
|
||||||
|
//! Flush the buffer.
|
||||||
|
void Flush();
|
||||||
|
|
||||||
|
//! End the writing operation.
|
||||||
|
//! \param begin The begin write pointer returned by PutBegin().
|
||||||
|
//! \return Number of characters written.
|
||||||
|
size_t PutEnd(Ch* begin);
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! Provides additional information for stream.
|
||||||
|
/*!
|
||||||
|
By using traits pattern, this type provides a default configuration for stream.
|
||||||
|
For custom stream, this type can be specialized for other configuration.
|
||||||
|
See TEST(Reader, CustomStringStream) in readertest.cpp for example.
|
||||||
|
*/
|
||||||
|
template<typename Stream>
|
||||||
|
struct StreamTraits {
|
||||||
|
//! Whether to make local copy of stream for optimization during parsing.
|
||||||
|
/*!
|
||||||
|
By default, for safety, streams do not use local copy optimization.
|
||||||
|
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
|
||||||
|
*/
|
||||||
|
enum { copyOptimization = 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Reserve n characters for writing to a stream.
|
||||||
|
template<typename Stream>
|
||||||
|
inline void PutReserve(Stream& stream, size_t count) {
|
||||||
|
(void)stream;
|
||||||
|
(void)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Write character to a stream, presuming buffer is reserved.
|
||||||
|
template<typename Stream>
|
||||||
|
inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
|
||||||
|
stream.Put(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Put N copies of a character to a stream.
|
||||||
|
template<typename Stream, typename Ch>
|
||||||
|
inline void PutN(Stream& stream, Ch c, size_t n) {
|
||||||
|
PutReserve(stream, n);
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
PutUnsafe(stream, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// StringStream
|
||||||
|
|
||||||
|
//! Read-only string stream.
|
||||||
|
/*! \note implements Stream concept
|
||||||
|
*/
|
||||||
|
template <typename Encoding>
|
||||||
|
struct GenericStringStream {
|
||||||
|
typedef typename Encoding::Ch Ch;
|
||||||
|
|
||||||
|
GenericStringStream(const Ch *src) : src_(src), head_(src) {}
|
||||||
|
|
||||||
|
Ch Peek() const { return *src_; }
|
||||||
|
Ch Take() { return *src_++; }
|
||||||
|
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
|
||||||
|
|
||||||
|
Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
void Flush() { CEREAL_RAPIDJSON_ASSERT(false); }
|
||||||
|
size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
const Ch* src_; //!< Current read position.
|
||||||
|
const Ch* head_; //!< Original head of the string.
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Encoding>
|
||||||
|
struct StreamTraits<GenericStringStream<Encoding> > {
|
||||||
|
enum { copyOptimization = 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
//! String stream with UTF8 encoding.
|
||||||
|
typedef GenericStringStream<UTF8<> > StringStream;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// InsituStringStream
|
||||||
|
|
||||||
|
//! A read-write string stream.
|
||||||
|
/*! This string stream is particularly designed for in-situ parsing.
|
||||||
|
\note implements Stream concept
|
||||||
|
*/
|
||||||
|
template <typename Encoding>
|
||||||
|
struct GenericInsituStringStream {
|
||||||
|
typedef typename Encoding::Ch Ch;
|
||||||
|
|
||||||
|
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
|
||||||
|
|
||||||
|
// Read
|
||||||
|
Ch Peek() { return *src_; }
|
||||||
|
Ch Take() { return *src_++; }
|
||||||
|
size_t Tell() { return static_cast<size_t>(src_ - head_); }
|
||||||
|
|
||||||
|
// Write
|
||||||
|
void Put(Ch c) { CEREAL_RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
|
||||||
|
|
||||||
|
Ch* PutBegin() { return dst_ = src_; }
|
||||||
|
size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
|
||||||
|
void Flush() {}
|
||||||
|
|
||||||
|
Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
|
||||||
|
void Pop(size_t count) { dst_ -= count; }
|
||||||
|
|
||||||
|
Ch* src_;
|
||||||
|
Ch* dst_;
|
||||||
|
Ch* head_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Encoding>
|
||||||
|
struct StreamTraits<GenericInsituStringStream<Encoding> > {
|
||||||
|
enum { copyOptimization = 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Insitu string stream with UTF8 encoding.
|
||||||
|
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_STREAM_H_
|
||||||
117
external/cereal/external/rapidjson/stringbuffer.h
vendored
Normal file
117
external/cereal/external/rapidjson/stringbuffer.h
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||||
|
// in compliance with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CEREAL_RAPIDJSON_STRINGBUFFER_H_
|
||||||
|
#define CEREAL_RAPIDJSON_STRINGBUFFER_H_
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include "internal/stack.h"
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
#include <utility> // std::move
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "internal/stack.h"
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_PUSH
|
||||||
|
CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
//! Represents an in-memory output stream.
|
||||||
|
/*!
|
||||||
|
\tparam Encoding Encoding of the stream.
|
||||||
|
\tparam Allocator type for allocating memory buffer.
|
||||||
|
\note implements Stream concept
|
||||||
|
*/
|
||||||
|
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||||
|
class GenericStringBuffer {
|
||||||
|
public:
|
||||||
|
typedef typename Encoding::Ch Ch;
|
||||||
|
|
||||||
|
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||||
|
|
||||||
|
#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
|
||||||
|
GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
|
||||||
|
if (&rhs != this)
|
||||||
|
stack_ = std::move(rhs.stack_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||||
|
void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
|
||||||
|
void Flush() {}
|
||||||
|
|
||||||
|
void Clear() { stack_.Clear(); }
|
||||||
|
void ShrinkToFit() {
|
||||||
|
// Push and pop a null terminator. This is safe.
|
||||||
|
*stack_.template Push<Ch>() = '\0';
|
||||||
|
stack_.ShrinkToFit();
|
||||||
|
stack_.template Pop<Ch>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
|
||||||
|
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||||
|
Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
|
||||||
|
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||||
|
|
||||||
|
const Ch* GetString() const {
|
||||||
|
// Push and pop a null terminator. This is safe.
|
||||||
|
*stack_.template Push<Ch>() = '\0';
|
||||||
|
stack_.template Pop<Ch>(1);
|
||||||
|
|
||||||
|
return stack_.template Bottom<Ch>();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSize() const { return stack_.GetSize(); }
|
||||||
|
|
||||||
|
static const size_t kDefaultCapacity = 256;
|
||||||
|
mutable internal::Stack<Allocator> stack_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Prohibit copy constructor & assignment operator.
|
||||||
|
GenericStringBuffer(const GenericStringBuffer&);
|
||||||
|
GenericStringBuffer& operator=(const GenericStringBuffer&);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! String buffer with UTF8 encoding
|
||||||
|
typedef GenericStringBuffer<UTF8<> > StringBuffer;
|
||||||
|
|
||||||
|
template<typename Encoding, typename Allocator>
|
||||||
|
inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
|
||||||
|
stream.Reserve(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Encoding, typename Allocator>
|
||||||
|
inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
|
||||||
|
stream.PutUnsafe(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Implement specialized version of PutN() with memset() for better performance.
|
||||||
|
template<>
|
||||||
|
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
||||||
|
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
CEREAL_RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
CEREAL_RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CEREAL_RAPIDJSON_STRINGBUFFER_H_
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user