Uploading attachment code

This commit is contained in:
noam 2022-06-29 21:50:10 +03:00
parent feefcbcd6b
commit 7e8e141c53
133 changed files with 40692 additions and 62 deletions

60
.gitignore vendored
View File

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

@ -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
View 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/).

View File

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

View File

@ -0,0 +1 @@
add_subdirectory(nginx)

View File

@ -0,0 +1 @@
add_subdirectory(nginx_attachment_util)

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

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

View 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, &params);
}
///
/// @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;
}

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

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

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

View 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(&current_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(&current_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(&current_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(&current_periodic_failure.failing_interval)) return;
if (current_periodic_failure.current_failed_requests != 0 || should_update_timeout == 1) return;
current_fail_state = 0;
}

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

View 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+IDwvcmRmOlJERj4gPC94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' /> \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, 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' />\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: &#39;Arial&#39;, &#39;sans-serif&#39;\"><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>";

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

@ -0,0 +1,2 @@
add_subdirectory(shmem_ipc)
add_subdirectory(compression)

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

381
external/cereal/details/helpers.hpp vendored Normal file
View 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_

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

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

View 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

File diff suppressed because it is too large Load Diff

84
external/cereal/details/util.hpp vendored Normal file
View 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
View 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_

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,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_

View 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