From 53440de663a332b72bcca55309ff568a5b3ac787 Mon Sep 17 00:00:00 2001 From: dongl <2725096176@qq.com> Date: Mon, 5 Jun 2023 16:28:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E7=9D=80=20server=E7=9A=84=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E9=A1=BA=E4=BE=BF=E6=8F=90=E4=BA=A4=20=20=E5=A4=9A?= =?UTF-8?q?=E4=BA=86=E4=B8=80=E4=B8=81=E7=82=B9=E9=A1=B5=E9=9D=A2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 11 + api/CMakeLists.txt | 37 + api/ClientExample.cpp | 15 + api/ClientExample.h | 19 + api/MP/Body.h | 59 + api/MP/Cqi.h | 31 + api/MP/EnumMapping.h | 61 + api/MP/Mph.h | 30 + api/MP/Packet.h | 8 + api/MP/Request.cpp | 56 + api/MP/Request.h | 31 + api/MP/Response.cpp | 32 + api/MP/Response.h | 30 + api/MP/Sri.h | 32 + api/MP/proto/mp.body.pb.cc | 466 +++ api/MP/proto/mp.body.pb.h | 633 ++++ api/MP/proto/mp.cqi.pb.cc | 334 ++ api/MP/proto/mp.cqi.pb.h | 313 ++ api/MP/proto/mp.im.pb.cc | 672 ++++ api/MP/proto/mp.im.pb.h | 773 +++++ api/MP/proto/mp.mp.pb.cc | 164 + api/MP/proto/mp.mp.pb.h | 202 ++ api/MP/proto/mp.mph.pb.cc | 411 +++ api/MP/proto/mp.mph.pb.h | 448 +++ api/MP/proto/mp.request.pb.cc | 371 ++ api/MP/proto/mp.request.pb.h | 407 +++ api/MP/proto/mp.response.pb.cc | 311 ++ api/MP/proto/mp.response.pb.h | 311 ++ api/MP/proto/mp.sri.pb.cc | 544 +++ api/MP/proto/mp.sri.pb.h | 849 +++++ api/MP/protohuf/mp.body.proto | 13 + api/MP/protohuf/mp.cqi.proto | 13 + api/MP/protohuf/mp.im.proto | 30 + api/MP/protohuf/mp.mp.proto | 99 + api/MP/protohuf/mp.mph.proto | 12 + api/MP/protohuf/mp.request.proto | 10 + api/MP/protohuf/mp.response.proto | 8 + api/MP/protohuf/mp.sri.proto | 15 + api/core/Client.cpp | 109 + api/core/Client.h | 43 + api/core/agreement.cpp | 34 + api/core/agreement.h | 54 + api/core/management.cpp | 88 + api/core/management.h | 23 + api/user/User.h | 19 + api/user/UserOperation.cpp | 141 + api/user/UserOperation.h | 48 + include/libevent/evdns.h | 45 + include/libevent/event.h | 83 + include/libevent/event2/buffer.h | 1098 ++++++ include/libevent/event2/buffer_compat.h | 116 + include/libevent/event2/bufferevent.h | 1061 ++++++ include/libevent/event2/bufferevent_compat.h | 112 + include/libevent/event2/bufferevent_ssl.h | 275 ++ include/libevent/event2/bufferevent_struct.h | 116 + include/libevent/event2/dns.h | 819 +++++ include/libevent/event2/dns_compat.h | 358 ++ include/libevent/event2/dns_struct.h | 80 + include/libevent/event2/event-config.h | 528 +++ include/libevent/event2/event.h | 1704 +++++++++ include/libevent/event2/event_compat.h | 230 ++ include/libevent/event2/event_struct.h | 182 + include/libevent/event2/http.h | 1482 ++++++++ include/libevent/event2/http_compat.h | 94 + include/libevent/event2/http_struct.h | 152 + include/libevent/event2/keyvalq_struct.h | 80 + include/libevent/event2/listener.h | 193 ++ include/libevent/event2/rpc.h | 626 ++++ include/libevent/event2/rpc_compat.h | 61 + include/libevent/event2/rpc_struct.h | 114 + include/libevent/event2/tag.h | 146 + include/libevent/event2/tag_compat.h | 49 + include/libevent/event2/thread.h | 268 ++ include/libevent/event2/util.h | 888 +++++ include/libevent/event2/visibility.h | 68 + include/libevent/event2/watch.h | 136 + include/libevent/event2/ws.h | 58 + include/libevent/evhttp.h | 45 + include/libevent/evrpc.h | 45 + include/libevent/evutil.h | 39 + include/rapidjson/allocators.h | 693 ++++ include/rapidjson/cursorstreamwrapper.h | 78 + include/rapidjson/document.h | 3043 ++++++++++++++++ include/rapidjson/encodedstream.h | 299 ++ include/rapidjson/encodings.h | 716 ++++ include/rapidjson/error/en.h | 176 + include/rapidjson/error/error.h | 285 ++ include/rapidjson/filereadstream.h | 99 + include/rapidjson/filewritestream.h | 104 + include/rapidjson/fwd.h | 151 + include/rapidjson/internal/biginteger.h | 297 ++ include/rapidjson/internal/clzll.h | 71 + include/rapidjson/internal/diyfp.h | 261 ++ include/rapidjson/internal/dtoa.h | 249 ++ include/rapidjson/internal/ieee754.h | 78 + include/rapidjson/internal/itoa.h | 308 ++ include/rapidjson/internal/meta.h | 186 + include/rapidjson/internal/pow10.h | 55 + include/rapidjson/internal/regex.h | 739 ++++ include/rapidjson/internal/stack.h | 232 ++ include/rapidjson/internal/strfunc.h | 83 + include/rapidjson/internal/strtod.h | 293 ++ include/rapidjson/internal/swap.h | 46 + include/rapidjson/istreamwrapper.h | 128 + include/rapidjson/memorybuffer.h | 70 + include/rapidjson/memorystream.h | 71 + include/rapidjson/msinttypes/inttypes.h | 316 ++ include/rapidjson/msinttypes/stdint.h | 300 ++ include/rapidjson/ostreamwrapper.h | 81 + include/rapidjson/pointer.h | 1470 ++++++++ include/rapidjson/prettywriter.h | 277 ++ include/rapidjson/rapidjson.h | 741 ++++ include/rapidjson/reader.h | 2246 ++++++++++++ include/rapidjson/schema.h | 3262 ++++++++++++++++++ include/rapidjson/stream.h | 223 ++ include/rapidjson/stringbuffer.h | 121 + include/rapidjson/uri.h | 481 +++ include/rapidjson/writer.h | 710 ++++ lib/libevent/libevent.a | Bin 0 -> 666308 bytes src/CMakeLists.txt | 47 + src/Forms/addfrindwindow.ui | 42 + src/Forms/imwindow.ui | 156 + src/Forms/mainwindow.ui | 173 + src/Header/addfrindwindow.h | 30 + src/Header/imwindow.h | 36 + src/Header/mainwindow.h | 37 + src/Source/addfrindwindow.cpp | 41 + src/Source/imwindow.cpp | 74 + src/Source/mainwindow.cpp | 109 + src/main.cpp | 14 + 130 files changed, 39569 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 api/CMakeLists.txt create mode 100644 api/ClientExample.cpp create mode 100644 api/ClientExample.h create mode 100644 api/MP/Body.h create mode 100644 api/MP/Cqi.h create mode 100644 api/MP/EnumMapping.h create mode 100644 api/MP/Mph.h create mode 100644 api/MP/Packet.h create mode 100644 api/MP/Request.cpp create mode 100644 api/MP/Request.h create mode 100644 api/MP/Response.cpp create mode 100644 api/MP/Response.h create mode 100644 api/MP/Sri.h create mode 100644 api/MP/proto/mp.body.pb.cc create mode 100644 api/MP/proto/mp.body.pb.h create mode 100644 api/MP/proto/mp.cqi.pb.cc create mode 100644 api/MP/proto/mp.cqi.pb.h create mode 100644 api/MP/proto/mp.im.pb.cc create mode 100644 api/MP/proto/mp.im.pb.h create mode 100644 api/MP/proto/mp.mp.pb.cc create mode 100644 api/MP/proto/mp.mp.pb.h create mode 100644 api/MP/proto/mp.mph.pb.cc create mode 100644 api/MP/proto/mp.mph.pb.h create mode 100644 api/MP/proto/mp.request.pb.cc create mode 100644 api/MP/proto/mp.request.pb.h create mode 100644 api/MP/proto/mp.response.pb.cc create mode 100644 api/MP/proto/mp.response.pb.h create mode 100644 api/MP/proto/mp.sri.pb.cc create mode 100644 api/MP/proto/mp.sri.pb.h create mode 100644 api/MP/protohuf/mp.body.proto create mode 100644 api/MP/protohuf/mp.cqi.proto create mode 100644 api/MP/protohuf/mp.im.proto create mode 100644 api/MP/protohuf/mp.mp.proto create mode 100644 api/MP/protohuf/mp.mph.proto create mode 100644 api/MP/protohuf/mp.request.proto create mode 100644 api/MP/protohuf/mp.response.proto create mode 100644 api/MP/protohuf/mp.sri.proto create mode 100644 api/core/Client.cpp create mode 100644 api/core/Client.h create mode 100644 api/core/agreement.cpp create mode 100644 api/core/agreement.h create mode 100644 api/core/management.cpp create mode 100644 api/core/management.h create mode 100644 api/user/User.h create mode 100644 api/user/UserOperation.cpp create mode 100644 api/user/UserOperation.h create mode 100644 include/libevent/evdns.h create mode 100644 include/libevent/event.h create mode 100644 include/libevent/event2/buffer.h create mode 100644 include/libevent/event2/buffer_compat.h create mode 100644 include/libevent/event2/bufferevent.h create mode 100644 include/libevent/event2/bufferevent_compat.h create mode 100644 include/libevent/event2/bufferevent_ssl.h create mode 100644 include/libevent/event2/bufferevent_struct.h create mode 100644 include/libevent/event2/dns.h create mode 100644 include/libevent/event2/dns_compat.h create mode 100644 include/libevent/event2/dns_struct.h create mode 100644 include/libevent/event2/event-config.h create mode 100644 include/libevent/event2/event.h create mode 100644 include/libevent/event2/event_compat.h create mode 100644 include/libevent/event2/event_struct.h create mode 100644 include/libevent/event2/http.h create mode 100644 include/libevent/event2/http_compat.h create mode 100644 include/libevent/event2/http_struct.h create mode 100644 include/libevent/event2/keyvalq_struct.h create mode 100644 include/libevent/event2/listener.h create mode 100644 include/libevent/event2/rpc.h create mode 100644 include/libevent/event2/rpc_compat.h create mode 100644 include/libevent/event2/rpc_struct.h create mode 100644 include/libevent/event2/tag.h create mode 100644 include/libevent/event2/tag_compat.h create mode 100644 include/libevent/event2/thread.h create mode 100644 include/libevent/event2/util.h create mode 100644 include/libevent/event2/visibility.h create mode 100644 include/libevent/event2/watch.h create mode 100644 include/libevent/event2/ws.h create mode 100644 include/libevent/evhttp.h create mode 100644 include/libevent/evrpc.h create mode 100644 include/libevent/evutil.h create mode 100644 include/rapidjson/allocators.h create mode 100644 include/rapidjson/cursorstreamwrapper.h create mode 100644 include/rapidjson/document.h create mode 100644 include/rapidjson/encodedstream.h create mode 100644 include/rapidjson/encodings.h create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h create mode 100644 include/rapidjson/filereadstream.h create mode 100644 include/rapidjson/filewritestream.h create mode 100644 include/rapidjson/fwd.h create mode 100644 include/rapidjson/internal/biginteger.h create mode 100644 include/rapidjson/internal/clzll.h create mode 100644 include/rapidjson/internal/diyfp.h create mode 100644 include/rapidjson/internal/dtoa.h create mode 100644 include/rapidjson/internal/ieee754.h create mode 100644 include/rapidjson/internal/itoa.h create mode 100644 include/rapidjson/internal/meta.h create mode 100644 include/rapidjson/internal/pow10.h create mode 100644 include/rapidjson/internal/regex.h create mode 100644 include/rapidjson/internal/stack.h create mode 100644 include/rapidjson/internal/strfunc.h create mode 100644 include/rapidjson/internal/strtod.h create mode 100644 include/rapidjson/internal/swap.h create mode 100644 include/rapidjson/istreamwrapper.h create mode 100644 include/rapidjson/memorybuffer.h create mode 100644 include/rapidjson/memorystream.h create mode 100644 include/rapidjson/msinttypes/inttypes.h create mode 100644 include/rapidjson/msinttypes/stdint.h create mode 100644 include/rapidjson/ostreamwrapper.h create mode 100644 include/rapidjson/pointer.h create mode 100644 include/rapidjson/prettywriter.h create mode 100644 include/rapidjson/rapidjson.h create mode 100644 include/rapidjson/reader.h create mode 100644 include/rapidjson/schema.h create mode 100644 include/rapidjson/stream.h create mode 100644 include/rapidjson/stringbuffer.h create mode 100644 include/rapidjson/uri.h create mode 100644 include/rapidjson/writer.h create mode 100644 lib/libevent/libevent.a create mode 100644 src/CMakeLists.txt create mode 100644 src/Forms/addfrindwindow.ui create mode 100644 src/Forms/imwindow.ui create mode 100644 src/Forms/mainwindow.ui create mode 100644 src/Header/addfrindwindow.h create mode 100644 src/Header/imwindow.h create mode 100644 src/Header/mainwindow.h create mode 100644 src/Source/addfrindwindow.cpp create mode 100644 src/Source/imwindow.cpp create mode 100644 src/Source/mainwindow.cpp create mode 100644 src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9f7ba91 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.24) +project(IM) + +set(CMAKE_CXX_STANDARD 17) + +add_subdirectory(api) +add_subdirectory(src) + + + + diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt new file mode 100644 index 0000000..34bc0a1 --- /dev/null +++ b/api/CMakeLists.txt @@ -0,0 +1,37 @@ +project(client_api) + +set(CMAKE_PREFIX_PATH "/home/dongl/tools/qt/qt5.9.5/5.9.5/gcc_64/lib/cmake/Qt5") +find_package(Qt5 COMPONENTS + Core + Gui + Widgets + REQUIRED + ) + +include_directories(${CMAKE_SOURCE_DIR}/include/libevent) +include_directories(${CMAKE_SOURCE_DIR}/api) +include_directories(${CMAKE_SOURCE_DIR}/api/core) +include_directories(${CMAKE_SOURCE_DIR}/api/MP/mp) +include_directories(${CMAKE_SOURCE_DIR}/api/MP/proto) +include_directories(${CMAKE_SOURCE_DIR}/user) + +link_directories(${CMAKE_SOURCE_DIR}/lib/libevent) + +aux_source_directory(user DIR_USER) +aux_source_directory(core DIR_CORE) +aux_source_directory(MP DIR_MP) +aux_source_directory(MP/proto DIR_PROTO) + +add_library(client_api + ${DIR_USER} + ${DIR_CORE} + ${DIR_PROTO} + ${DIR_MP} + ClientExample.cpp + ) + +target_link_libraries(client_api + event + protobuf + tbb + ) \ No newline at end of file diff --git a/api/ClientExample.cpp b/api/ClientExample.cpp new file mode 100644 index 0000000..5930243 --- /dev/null +++ b/api/ClientExample.cpp @@ -0,0 +1,15 @@ +// +// Created by dongl on 23-5-16. +// + +#include "ClientExample.h" + +Client* ClientExample::client; + +Client *ClientExample::run(const std::string& ip, int port) { + if (client == nullptr) { + client = new Client(std::move(ip), port); + client->run(); + } + return client; +} diff --git a/api/ClientExample.h b/api/ClientExample.h new file mode 100644 index 0000000..f0fa31f --- /dev/null +++ b/api/ClientExample.h @@ -0,0 +1,19 @@ +// +// Created by dongl on 23-5-16. +// + +#ifndef IM_CLIENTEXAMPLE_H +#define IM_CLIENTEXAMPLE_H + + +#include "core/Client.h" + +class ClientExample { +public: + static Client* run(const std::string& ip = "127.0.0.1", int port = 9999); +private: + static Client* client; +}; + + +#endif //IM_CLIENTEXAMPLE_H diff --git a/api/MP/Body.h b/api/MP/Body.h new file mode 100644 index 0000000..3bd681c --- /dev/null +++ b/api/MP/Body.h @@ -0,0 +1,59 @@ +// +// Created by dongl on 23-4-21. +// + +#ifndef IM2_BODY_H +#define IM2_BODY_H + +#include "proto/mp.body.pb.h" + +class Body { +public: + + // account 可能是 账户 手机号 邮箱 + Body(mp::MP_SUB_TYPE subType, const std::string& account, const std::string& password) { + body = new mp::body(); + + body->set_subcommand(subType); + body->set_account(account); + body->set_password(password); + } + + Body(mp::MP_SUB_TYPE subType, const std::string& account, const std::string& password, const std::string& data) { + body = new mp::body(); + + body->set_subcommand(subType); + body->set_account(account); + body->set_password(password); + body->set_data(data); + } + + Body(mp::MP_SUB_TYPE subType, const std::string& account) { + body = new mp::body(); + + body->set_subcommand(subType); + body->set_account(account); + } + + Body(mp::MP_SUB_TYPE subType, uint64_t target, uint64_t source, const std::string& data) { + body = new mp::body(); + + body->set_target(target); + body->set_source(source); + body->set_data(data); + body->set_subcommand(subType); + } + + Body() { + body = new mp::body(); + } + + virtual ~Body() { + + } + +protected: + mp::body* body; +}; + +#endif //IM2_BODY_H diff --git a/api/MP/Cqi.h b/api/MP/Cqi.h new file mode 100644 index 0000000..ec7a8ec --- /dev/null +++ b/api/MP/Cqi.h @@ -0,0 +1,31 @@ +// +// Created by dongl on 23-4-21. +// + +#ifndef IM2_CQI_H +#define IM2_CQI_H + +#include "proto/mp.cqi.pb.h" + +class Cqi { +public: + Cqi() { + cqi = new mp::cqi(); + // 后期读配置文件 +#ifdef _WIN32 + cqi->set_cqi_type(mp::MC_TYPE_WINDOWS); +#else + cqi->set_cqi_type(mp::MC_TYPE_LINUX); +#endif + cqi->set_cqi_version(3.01); + } + + virtual ~Cqi() { + + } + +protected: + mp::cqi* cqi; +}; + +#endif //IM2_CQI_H diff --git a/api/MP/EnumMapping.h b/api/MP/EnumMapping.h new file mode 100644 index 0000000..f2ecade --- /dev/null +++ b/api/MP/EnumMapping.h @@ -0,0 +1,61 @@ +// +// Created by dongl on 23-4-25. +// + +#ifndef IM2_ENUMMAPPING_H +#define IM2_ENUMMAPPING_H + +#include "proto/mp.mph.pb.h" + + + + + + + + + +// 定义一个宏来包含需要用到的枚举值 +#define MY_ENUM_VALUES \ + X(MP_REQUEST_LOGIN) \ + X(MP_REQUEST_LOGOUT) \ + X(MP_REQUEST_REGISTER) \ + X(MP_REQUEST_CODE) \ + X(MP_REQUEST_FRIENDS) \ + X(MP_REQUEST_GROUPS) + +#if 0 +// method 1 +// 直接定义枚举值 +enum { + SUMMER, + AUTUMN, + WINTER, +}; + +#else + +// method 2 +// 利用宏展开获取并定义枚举 +#undef X +#define X(x) x, +enum { + MY_ENUM_VALUES +}; +#endif + +// 重新定义宏函数并获取新的展开形式,以获取枚举值对应的字符串; +const char *myenumToString(int n) { +#undef X +#define X(x) case (x): { return #x; } +#define MAKE_ENUM_CASES \ + MY_ENUM_VALUES \ + default: { return "unknown enum string."; } + + switch (n) { + MAKE_ENUM_CASES + } +} + + +#endif //IM2_ENUMMAPPING_H diff --git a/api/MP/Mph.h b/api/MP/Mph.h new file mode 100644 index 0000000..4779672 --- /dev/null +++ b/api/MP/Mph.h @@ -0,0 +1,30 @@ +// +// Created by dongl on 23-4-21. +// + +#ifndef IM2_MPH_H +#define IM2_MPH_H + + +#include "proto/mp.mp.pb.h" +#include "proto/mp.mph.pb.h" + +class Mph { +public: + Mph(mp::MP_TYPE type) { + mph = new mp::mph(); + mph->set_mp_sum(1); + mph->set_mp_id(1); + mph->set_mp_type(type); + mph->set_mpb_size(1); + } + + virtual ~Mph() { + delete mph; + } + +protected: + mp::mph* mph; +}; + +#endif //IM2_MPH_H diff --git a/api/MP/Packet.h b/api/MP/Packet.h new file mode 100644 index 0000000..62e15f3 --- /dev/null +++ b/api/MP/Packet.h @@ -0,0 +1,8 @@ +// +// Created by dongl on 23-4-26. +// + +#ifndef IM2_PACKET_H +#define IM2_PACKET_H + +#endif //IM2_PACKET_H diff --git a/api/MP/Request.cpp b/api/MP/Request.cpp new file mode 100644 index 0000000..c55248d --- /dev/null +++ b/api/MP/Request.cpp @@ -0,0 +1,56 @@ +// +// Created by dongl on 23-4-21. +// + +#include "Request.h" + +// 账户或者 手机号|邮箱|账户 请求 登陆注册 +Request::Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string& account, const std::string& password) : + Mph(type), Body(subType, account, password), Cqi() { + +} + +// IM 文本聊天请求 +Request::Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, uint64_t target, uint64_t source, const std::string& data) : + Mph(type), Body(subType, target, source, data), Cqi() { + +} + +Request::Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType,const std::string &account, + const std::string &password,const std::string &data) : + Mph(type), Body(subType, account, password, data) { +} + +Request::Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string &account) : + Mph(type), Body(subType, account), Cqi(){ + +} + + +Request::~Request() { + delete request; +} + +void Request::init() { + request = new mp::request(); + request->set_allocated_body(body); + request->set_allocated_cqi(cqi); + mph->set_mpb_size(request->ByteSizeLong()); +} + +std::string Request::packet() { + init(); + std::string temp; + + temp.push_back(mph->ByteSizeLong()); + mph->AppendToString(&temp); + request->AppendToString(&temp); + return temp; +} + + + + + + + diff --git a/api/MP/Request.h b/api/MP/Request.h new file mode 100644 index 0000000..dc827e3 --- /dev/null +++ b/api/MP/Request.h @@ -0,0 +1,31 @@ +// +// Created by dongl on 23-4-21. +// + +#ifndef IM2_REQUEST_H +#define IM2_REQUEST_H + +#include "proto/mp.request.pb.h" +#include "Mph.h" +#include "Body.h" +#include "Cqi.h" + + +class Request : public Mph, Body, Cqi { +public: + Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string& account, const std::string& password); + Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string& account, const std::string& password, const std::string& data); + Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, uint64_t target, uint64_t source, const std::string& data); + Request(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string& account); + + ~Request() override; +public: + std::string packet(); +private: + void init(); +private: + mp::request* request = nullptr; +}; + + +#endif //IM2_REQUEST_H diff --git a/api/MP/Response.cpp b/api/MP/Response.cpp new file mode 100644 index 0000000..349c8a3 --- /dev/null +++ b/api/MP/Response.cpp @@ -0,0 +1,32 @@ +// +// Created by dongl on 23-4-21. +// + +#include "Response.h" + +Response::Response(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string &username, const std::string &msg, const std::string &token) : + Mph(type), + Sri(subType, username, msg, token) { +} + +Response::~Response() { + delete response; +} + +void Response::init() { + response = new mp::response(); + response->set_allocated_sri(sri); + mph->set_mpb_size( response->ByteSizeLong()); +} + +std::string Response::packet () { + init(); + std::string temp; + + temp.push_back(mph->ByteSizeLong()); + mph->AppendToString(&temp); + response->AppendToString(&temp); + + return temp; +} + diff --git a/api/MP/Response.h b/api/MP/Response.h new file mode 100644 index 0000000..d189619 --- /dev/null +++ b/api/MP/Response.h @@ -0,0 +1,30 @@ +// +// Created by dongl on 23-4-21. +// + +#ifndef IM2_RESPONSE_H +#define IM2_RESPONSE_H + + + +#include "proto/mp.response.pb.h" +#include "Mph.h" +#include "Body.h" +#include "Sri.h" + +class Response : public Mph, Sri{ +public: + Response(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string &username, const std::string &msg, const std::string &token); + + ~Response() override; + + std::string packet (); + +private: + void init(); +private: + mp::response* response = nullptr; +}; + + +#endif //IM2_RESPONSE_H diff --git a/api/MP/Sri.h b/api/MP/Sri.h new file mode 100644 index 0000000..542992c --- /dev/null +++ b/api/MP/Sri.h @@ -0,0 +1,32 @@ +// +// Created by dongl on 23-4-23. +// + +#ifndef IM2_SRI_H +#define IM2_SRI_H + + +#include "proto/mp.sri.pb.h" + +class Sri { +public: + Sri(mp::MP_SUB_TYPE code, const std::string& username, const std::string& msg, const std::string& token) { + sri = new mp::sri(); + sri->set_subcommand(code); + sri->set_msg(msg); + sri->set_token(token); + sri->set_username(username); + } + + Sri() {} + + virtual ~Sri() { + + } + +public: + mp::sri* sri = nullptr; +}; + + +#endif //IM2_SRI_H diff --git a/api/MP/proto/mp.body.pb.cc b/api/MP/proto/mp.body.pb.cc new file mode 100644 index 0000000..2b28fa0 --- /dev/null +++ b/api/MP/proto/mp.body.pb.cc @@ -0,0 +1,466 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.body.proto + +#include "mp.body.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +namespace mp { +class bodyDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _body_default_instance_; +} // namespace mp +static void InitDefaultsscc_info_body_mp_2ebody_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::_body_default_instance_; + new (ptr) ::mp::body(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::body::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_body_mp_2ebody_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_body_mp_2ebody_2eproto}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2ebody_2eproto[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_mp_2ebody_2eproto = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2ebody_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2ebody_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::body, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::body, subcommand_), + PROTOBUF_FIELD_OFFSET(::mp::body, account_), + PROTOBUF_FIELD_OFFSET(::mp::body, password_), + PROTOBUF_FIELD_OFFSET(::mp::body, target_), + PROTOBUF_FIELD_OFFSET(::mp::body, source_), + PROTOBUF_FIELD_OFFSET(::mp::body, data_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::body)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::_body_default_instance_), +}; + +const char descriptor_table_protodef_mp_2ebody_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\rmp.body.proto\022\002mp\032\013mp.mp.proto\"|\n\004body" + "\022#\n\nsubcommand\030\001 \001(\0162\017.mp.MP_SUB_TYPE\022\017\n" + "\007account\030\002 \001(\t\022\020\n\010password\030\003 \001(\t\022\016\n\006targ" + "et\030\004 \001(\004\022\016\n\006source\030\005 \001(\004\022\014\n\004data\030\006 \001(\tb\006" + "proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2ebody_2eproto_deps[1] = { + &::descriptor_table_mp_2emp_2eproto, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2ebody_2eproto_sccs[1] = { + &scc_info_body_mp_2ebody_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2ebody_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2ebody_2eproto = { + false, false, descriptor_table_protodef_mp_2ebody_2eproto, "mp.body.proto", 166, + &descriptor_table_mp_2ebody_2eproto_once, descriptor_table_mp_2ebody_2eproto_sccs, descriptor_table_mp_2ebody_2eproto_deps, 1, 1, + schemas, file_default_instances, TableStruct_mp_2ebody_2eproto::offsets, + file_level_metadata_mp_2ebody_2eproto, 1, file_level_enum_descriptors_mp_2ebody_2eproto, file_level_service_descriptors_mp_2ebody_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2ebody_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2ebody_2eproto)), true); +namespace mp { + +// =================================================================== + +void body::InitAsDefaultInstance() { +} +class body::_Internal { + public: +}; + +body::body(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.body) +} +body::body(const body& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + account_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_account().empty()) { + account_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_account(), + GetArena()); + } + password_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_password().empty()) { + password_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_password(), + GetArena()); + } + data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_data().empty()) { + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_data(), + GetArena()); + } + ::memcpy(&target_, &from.target_, + static_cast(reinterpret_cast(&subcommand_) - + reinterpret_cast(&target_)) + sizeof(subcommand_)); + // @@protoc_insertion_point(copy_constructor:mp.body) +} + +void body::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_body_mp_2ebody_2eproto.base); + account_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + password_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + ::memset(&target_, 0, static_cast( + reinterpret_cast(&subcommand_) - + reinterpret_cast(&target_)) + sizeof(subcommand_)); +} + +body::~body() { + // @@protoc_insertion_point(destructor:mp.body) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void body::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + account_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + password_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void body::ArenaDtor(void* object) { + body* _this = reinterpret_cast< body* >(object); + (void)_this; +} +void body::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void body::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const body& body::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_body_mp_2ebody_2eproto.base); + return *internal_default_instance(); +} + + +void body::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.body) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + account_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + password_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::memset(&target_, 0, static_cast( + reinterpret_cast(&subcommand_) - + reinterpret_cast(&target_)) + sizeof(subcommand_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* body::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // .mp.MP_SUB_TYPE subcommand = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_subcommand(static_cast<::mp::MP_SUB_TYPE>(val)); + } else goto handle_unusual; + continue; + // string account = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { + auto str = _internal_mutable_account(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.body.account")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string password = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { + auto str = _internal_mutable_password(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.body.password")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint64 target = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { + target_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint64 source = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { + source_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string data = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { + auto str = _internal_mutable_data(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.body.data")); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* body::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.body) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // .mp.MP_SUB_TYPE subcommand = 1; + if (this->subcommand() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_subcommand(), target); + } + + // string account = 2; + if (this->account().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_account().data(), static_cast(this->_internal_account().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.body.account"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_account(), target); + } + + // string password = 3; + if (this->password().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_password().data(), static_cast(this->_internal_password().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.body.password"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_password(), target); + } + + // uint64 target = 4; + if (this->target() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(4, this->_internal_target(), target); + } + + // uint64 source = 5; + if (this->source() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(5, this->_internal_source(), target); + } + + // string data = 6; + if (this->data().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_data().data(), static_cast(this->_internal_data().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.body.data"); + target = stream->WriteStringMaybeAliased( + 6, this->_internal_data(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.body) + return target; +} + +size_t body::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.body) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string account = 2; + if (this->account().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_account()); + } + + // string password = 3; + if (this->password().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_password()); + } + + // string data = 6; + if (this->data().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_data()); + } + + // uint64 target = 4; + if (this->target() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_target()); + } + + // uint64 source = 5; + if (this->source() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_source()); + } + + // .mp.MP_SUB_TYPE subcommand = 1; + if (this->subcommand() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_subcommand()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void body::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.body) + GOOGLE_DCHECK_NE(&from, this); + const body* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.body) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.body) + MergeFrom(*source); + } +} + +void body::MergeFrom(const body& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.body) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.account().size() > 0) { + _internal_set_account(from._internal_account()); + } + if (from.password().size() > 0) { + _internal_set_password(from._internal_password()); + } + if (from.data().size() > 0) { + _internal_set_data(from._internal_data()); + } + if (from.target() != 0) { + _internal_set_target(from._internal_target()); + } + if (from.source() != 0) { + _internal_set_source(from._internal_source()); + } + if (from.subcommand() != 0) { + _internal_set_subcommand(from._internal_subcommand()); + } +} + +void body::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.body) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void body::CopyFrom(const body& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.body) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool body::IsInitialized() const { + return true; +} + +void body::InternalSwap(body* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + account_.Swap(&other->account_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + password_.Swap(&other->password_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + data_.Swap(&other->data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(body, subcommand_) + + sizeof(body::subcommand_) + - PROTOBUF_FIELD_OFFSET(body, target_)>( + reinterpret_cast(&target_), + reinterpret_cast(&other->target_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata body::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::body* Arena::CreateMaybeMessage< ::mp::body >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::body >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.body.pb.h b/api/MP/proto/mp.body.pb.h new file mode 100644 index 0000000..86aeb35 --- /dev/null +++ b/api/MP/proto/mp.body.pb.h @@ -0,0 +1,633 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.body.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2ebody_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2ebody_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include "mp.mp.pb.h" +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2ebody_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2ebody_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2ebody_2eproto; +namespace mp { +class body; +class bodyDefaultTypeInternal; +extern bodyDefaultTypeInternal _body_default_instance_; +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::body* Arena::CreateMaybeMessage<::mp::body>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +// =================================================================== + +class body PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.body) */ { + public: + inline body() : body(nullptr) {}; + virtual ~body(); + + body(const body& from); + body(body&& from) noexcept + : body() { + *this = ::std::move(from); + } + + inline body& operator=(const body& from) { + CopyFrom(from); + return *this; + } + inline body& operator=(body&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const body& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const body* internal_default_instance() { + return reinterpret_cast( + &_body_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(body& a, body& b) { + a.Swap(&b); + } + inline void Swap(body* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(body* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline body* New() const final { + return CreateMaybeMessage(nullptr); + } + + body* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const body& from); + void MergeFrom(const body& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(body* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.body"; + } + protected: + explicit body(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2ebody_2eproto); + return ::descriptor_table_mp_2ebody_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kAccountFieldNumber = 2, + kPasswordFieldNumber = 3, + kDataFieldNumber = 6, + kTargetFieldNumber = 4, + kSourceFieldNumber = 5, + kSubcommandFieldNumber = 1, + }; + // string account = 2; + void clear_account(); + const std::string& account() const; + void set_account(const std::string& value); + void set_account(std::string&& value); + void set_account(const char* value); + void set_account(const char* value, size_t size); + std::string* mutable_account(); + std::string* release_account(); + void set_allocated_account(std::string* account); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_account(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_account( + std::string* account); + private: + const std::string& _internal_account() const; + void _internal_set_account(const std::string& value); + std::string* _internal_mutable_account(); + public: + + // string password = 3; + void clear_password(); + const std::string& password() const; + void set_password(const std::string& value); + void set_password(std::string&& value); + void set_password(const char* value); + void set_password(const char* value, size_t size); + std::string* mutable_password(); + std::string* release_password(); + void set_allocated_password(std::string* password); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_password(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_password( + std::string* password); + private: + const std::string& _internal_password() const; + void _internal_set_password(const std::string& value); + std::string* _internal_mutable_password(); + public: + + // string data = 6; + void clear_data(); + const std::string& data() const; + void set_data(const std::string& value); + void set_data(std::string&& value); + void set_data(const char* value); + void set_data(const char* value, size_t size); + std::string* mutable_data(); + std::string* release_data(); + void set_allocated_data(std::string* data); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_data(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_data( + std::string* data); + private: + const std::string& _internal_data() const; + void _internal_set_data(const std::string& value); + std::string* _internal_mutable_data(); + public: + + // uint64 target = 4; + void clear_target(); + ::PROTOBUF_NAMESPACE_ID::uint64 target() const; + void set_target(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_target() const; + void _internal_set_target(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // uint64 source = 5; + void clear_source(); + ::PROTOBUF_NAMESPACE_ID::uint64 source() const; + void set_source(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_source() const; + void _internal_set_source(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // .mp.MP_SUB_TYPE subcommand = 1; + void clear_subcommand(); + ::mp::MP_SUB_TYPE subcommand() const; + void set_subcommand(::mp::MP_SUB_TYPE value); + private: + ::mp::MP_SUB_TYPE _internal_subcommand() const; + void _internal_set_subcommand(::mp::MP_SUB_TYPE value); + public: + + // @@protoc_insertion_point(class_scope:mp.body) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr account_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr password_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr data_; + ::PROTOBUF_NAMESPACE_ID::uint64 target_; + ::PROTOBUF_NAMESPACE_ID::uint64 source_; + int subcommand_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2ebody_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// body + +// .mp.MP_SUB_TYPE subcommand = 1; +inline void body::clear_subcommand() { + subcommand_ = 0; +} +inline ::mp::MP_SUB_TYPE body::_internal_subcommand() const { + return static_cast< ::mp::MP_SUB_TYPE >(subcommand_); +} +inline ::mp::MP_SUB_TYPE body::subcommand() const { + // @@protoc_insertion_point(field_get:mp.body.subcommand) + return _internal_subcommand(); +} +inline void body::_internal_set_subcommand(::mp::MP_SUB_TYPE value) { + + subcommand_ = value; +} +inline void body::set_subcommand(::mp::MP_SUB_TYPE value) { + _internal_set_subcommand(value); + // @@protoc_insertion_point(field_set:mp.body.subcommand) +} + +// string account = 2; +inline void body::clear_account() { + account_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& body::account() const { + // @@protoc_insertion_point(field_get:mp.body.account) + return _internal_account(); +} +inline void body::set_account(const std::string& value) { + _internal_set_account(value); + // @@protoc_insertion_point(field_set:mp.body.account) +} +inline std::string* body::mutable_account() { + // @@protoc_insertion_point(field_mutable:mp.body.account) + return _internal_mutable_account(); +} +inline const std::string& body::_internal_account() const { + return account_.Get(); +} +inline void body::_internal_set_account(const std::string& value) { + + account_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void body::set_account(std::string&& value) { + + account_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.body.account) +} +inline void body::set_account(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + account_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.body.account) +} +inline void body::set_account(const char* value, + size_t size) { + + account_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.body.account) +} +inline std::string* body::_internal_mutable_account() { + + return account_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* body::release_account() { + // @@protoc_insertion_point(field_release:mp.body.account) + return account_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void body::set_allocated_account(std::string* account) { + if (account != nullptr) { + + } else { + + } + account_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), account, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.body.account) +} +inline std::string* body::unsafe_arena_release_account() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.body.account) + GOOGLE_DCHECK(GetArena() != nullptr); + + return account_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void body::unsafe_arena_set_allocated_account( + std::string* account) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (account != nullptr) { + + } else { + + } + account_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + account, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.body.account) +} + +// string password = 3; +inline void body::clear_password() { + password_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& body::password() const { + // @@protoc_insertion_point(field_get:mp.body.password) + return _internal_password(); +} +inline void body::set_password(const std::string& value) { + _internal_set_password(value); + // @@protoc_insertion_point(field_set:mp.body.password) +} +inline std::string* body::mutable_password() { + // @@protoc_insertion_point(field_mutable:mp.body.password) + return _internal_mutable_password(); +} +inline const std::string& body::_internal_password() const { + return password_.Get(); +} +inline void body::_internal_set_password(const std::string& value) { + + password_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void body::set_password(std::string&& value) { + + password_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.body.password) +} +inline void body::set_password(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + password_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.body.password) +} +inline void body::set_password(const char* value, + size_t size) { + + password_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.body.password) +} +inline std::string* body::_internal_mutable_password() { + + return password_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* body::release_password() { + // @@protoc_insertion_point(field_release:mp.body.password) + return password_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void body::set_allocated_password(std::string* password) { + if (password != nullptr) { + + } else { + + } + password_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), password, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.body.password) +} +inline std::string* body::unsafe_arena_release_password() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.body.password) + GOOGLE_DCHECK(GetArena() != nullptr); + + return password_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void body::unsafe_arena_set_allocated_password( + std::string* password) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (password != nullptr) { + + } else { + + } + password_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + password, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.body.password) +} + +// uint64 target = 4; +inline void body::clear_target() { + target_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 body::_internal_target() const { + return target_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 body::target() const { + // @@protoc_insertion_point(field_get:mp.body.target) + return _internal_target(); +} +inline void body::_internal_set_target(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + target_ = value; +} +inline void body::set_target(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_target(value); + // @@protoc_insertion_point(field_set:mp.body.target) +} + +// uint64 source = 5; +inline void body::clear_source() { + source_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 body::_internal_source() const { + return source_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 body::source() const { + // @@protoc_insertion_point(field_get:mp.body.source) + return _internal_source(); +} +inline void body::_internal_set_source(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + source_ = value; +} +inline void body::set_source(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_source(value); + // @@protoc_insertion_point(field_set:mp.body.source) +} + +// string data = 6; +inline void body::clear_data() { + data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& body::data() const { + // @@protoc_insertion_point(field_get:mp.body.data) + return _internal_data(); +} +inline void body::set_data(const std::string& value) { + _internal_set_data(value); + // @@protoc_insertion_point(field_set:mp.body.data) +} +inline std::string* body::mutable_data() { + // @@protoc_insertion_point(field_mutable:mp.body.data) + return _internal_mutable_data(); +} +inline const std::string& body::_internal_data() const { + return data_.Get(); +} +inline void body::_internal_set_data(const std::string& value) { + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void body::set_data(std::string&& value) { + + data_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.body.data) +} +inline void body::set_data(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.body.data) +} +inline void body::set_data(const char* value, + size_t size) { + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.body.data) +} +inline std::string* body::_internal_mutable_data() { + + return data_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* body::release_data() { + // @@protoc_insertion_point(field_release:mp.body.data) + return data_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void body::set_allocated_data(std::string* data) { + if (data != nullptr) { + + } else { + + } + data_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), data, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.body.data) +} +inline std::string* body::unsafe_arena_release_data() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.body.data) + GOOGLE_DCHECK(GetArena() != nullptr); + + return data_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void body::unsafe_arena_set_allocated_data( + std::string* data) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (data != nullptr) { + + } else { + + } + data_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + data, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.body.data) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2ebody_2eproto diff --git a/api/MP/proto/mp.cqi.pb.cc b/api/MP/proto/mp.cqi.pb.cc new file mode 100644 index 0000000..e5d57ed --- /dev/null +++ b/api/MP/proto/mp.cqi.pb.cc @@ -0,0 +1,334 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.cqi.proto + +#include "mp.cqi.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +namespace mp { +class cqiDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _cqi_default_instance_; +} // namespace mp +static void InitDefaultsscc_info_cqi_mp_2ecqi_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::_cqi_default_instance_; + new (ptr) ::mp::cqi(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::cqi::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_cqi_mp_2ecqi_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_cqi_mp_2ecqi_2eproto}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2ecqi_2eproto[1]; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_mp_2ecqi_2eproto[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2ecqi_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2ecqi_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::cqi, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::cqi, cqi_type_), + PROTOBUF_FIELD_OFFSET(::mp::cqi, cqi_version_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::cqi)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::_cqi_default_instance_), +}; + +const char descriptor_table_protodef_mp_2ecqi_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\014mp.cqi.proto\022\002mp\";\n\003cqi\022\037\n\010cqi_type\030\001 " + "\001(\0162\r.mp.MP_C_TYPE\022\023\n\013cqi_version\030\002 \001(\002*" + "3\n\tMP_C_TYPE\022\021\n\rMC_TYPE_LINUX\020\000\022\023\n\017MC_TY" + "PE_WINDOWS\020\001b\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2ecqi_2eproto_deps[1] = { +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2ecqi_2eproto_sccs[1] = { + &scc_info_cqi_mp_2ecqi_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2ecqi_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2ecqi_2eproto = { + false, false, descriptor_table_protodef_mp_2ecqi_2eproto, "mp.cqi.proto", 140, + &descriptor_table_mp_2ecqi_2eproto_once, descriptor_table_mp_2ecqi_2eproto_sccs, descriptor_table_mp_2ecqi_2eproto_deps, 1, 0, + schemas, file_default_instances, TableStruct_mp_2ecqi_2eproto::offsets, + file_level_metadata_mp_2ecqi_2eproto, 1, file_level_enum_descriptors_mp_2ecqi_2eproto, file_level_service_descriptors_mp_2ecqi_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2ecqi_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2ecqi_2eproto)), true); +namespace mp { +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_C_TYPE_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_mp_2ecqi_2eproto); + return file_level_enum_descriptors_mp_2ecqi_2eproto[0]; +} +bool MP_C_TYPE_IsValid(int value) { + switch (value) { + case 0: + case 1: + return true; + default: + return false; + } +} + + +// =================================================================== + +void cqi::InitAsDefaultInstance() { +} +class cqi::_Internal { + public: +}; + +cqi::cqi(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.cqi) +} +cqi::cqi(const cqi& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::memcpy(&cqi_type_, &from.cqi_type_, + static_cast(reinterpret_cast(&cqi_version_) - + reinterpret_cast(&cqi_type_)) + sizeof(cqi_version_)); + // @@protoc_insertion_point(copy_constructor:mp.cqi) +} + +void cqi::SharedCtor() { + ::memset(&cqi_type_, 0, static_cast( + reinterpret_cast(&cqi_version_) - + reinterpret_cast(&cqi_type_)) + sizeof(cqi_version_)); +} + +cqi::~cqi() { + // @@protoc_insertion_point(destructor:mp.cqi) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void cqi::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); +} + +void cqi::ArenaDtor(void* object) { + cqi* _this = reinterpret_cast< cqi* >(object); + (void)_this; +} +void cqi::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void cqi::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const cqi& cqi::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_cqi_mp_2ecqi_2eproto.base); + return *internal_default_instance(); +} + + +void cqi::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.cqi) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + ::memset(&cqi_type_, 0, static_cast( + reinterpret_cast(&cqi_version_) - + reinterpret_cast(&cqi_type_)) + sizeof(cqi_version_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* cqi::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // .mp.MP_C_TYPE cqi_type = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_cqi_type(static_cast<::mp::MP_C_TYPE>(val)); + } else goto handle_unusual; + continue; + // float cqi_version = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 21)) { + cqi_version_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* cqi::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.cqi) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // .mp.MP_C_TYPE cqi_type = 1; + if (this->cqi_type() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_cqi_type(), target); + } + + // float cqi_version = 2; + if (!(this->cqi_version() <= 0 && this->cqi_version() >= 0)) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(2, this->_internal_cqi_version(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.cqi) + return target; +} + +size_t cqi::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.cqi) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // .mp.MP_C_TYPE cqi_type = 1; + if (this->cqi_type() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_cqi_type()); + } + + // float cqi_version = 2; + if (!(this->cqi_version() <= 0 && this->cqi_version() >= 0)) { + total_size += 1 + 4; + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void cqi::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.cqi) + GOOGLE_DCHECK_NE(&from, this); + const cqi* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.cqi) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.cqi) + MergeFrom(*source); + } +} + +void cqi::MergeFrom(const cqi& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.cqi) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.cqi_type() != 0) { + _internal_set_cqi_type(from._internal_cqi_type()); + } + if (!(from.cqi_version() <= 0 && from.cqi_version() >= 0)) { + _internal_set_cqi_version(from._internal_cqi_version()); + } +} + +void cqi::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.cqi) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void cqi::CopyFrom(const cqi& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.cqi) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool cqi::IsInitialized() const { + return true; +} + +void cqi::InternalSwap(cqi* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(cqi, cqi_version_) + + sizeof(cqi::cqi_version_) + - PROTOBUF_FIELD_OFFSET(cqi, cqi_type_)>( + reinterpret_cast(&cqi_type_), + reinterpret_cast(&other->cqi_type_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata cqi::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::cqi* Arena::CreateMaybeMessage< ::mp::cqi >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::cqi >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.cqi.pb.h b/api/MP/proto/mp.cqi.pb.h new file mode 100644 index 0000000..948c89d --- /dev/null +++ b/api/MP/proto/mp.cqi.pb.h @@ -0,0 +1,313 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.cqi.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2ecqi_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2ecqi_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2ecqi_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2ecqi_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2ecqi_2eproto; +namespace mp { +class cqi; +class cqiDefaultTypeInternal; +extern cqiDefaultTypeInternal _cqi_default_instance_; +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::cqi* Arena::CreateMaybeMessage<::mp::cqi>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +enum MP_C_TYPE : int { + MC_TYPE_LINUX = 0, + MC_TYPE_WINDOWS = 1, + MP_C_TYPE_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + MP_C_TYPE_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool MP_C_TYPE_IsValid(int value); +constexpr MP_C_TYPE MP_C_TYPE_MIN = MC_TYPE_LINUX; +constexpr MP_C_TYPE MP_C_TYPE_MAX = MC_TYPE_WINDOWS; +constexpr int MP_C_TYPE_ARRAYSIZE = MP_C_TYPE_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_C_TYPE_descriptor(); +template +inline const std::string& MP_C_TYPE_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function MP_C_TYPE_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + MP_C_TYPE_descriptor(), enum_t_value); +} +inline bool MP_C_TYPE_Parse( + const std::string& name, MP_C_TYPE* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + MP_C_TYPE_descriptor(), name, value); +} +// =================================================================== + +class cqi PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.cqi) */ { + public: + inline cqi() : cqi(nullptr) {}; + virtual ~cqi(); + + cqi(const cqi& from); + cqi(cqi&& from) noexcept + : cqi() { + *this = ::std::move(from); + } + + inline cqi& operator=(const cqi& from) { + CopyFrom(from); + return *this; + } + inline cqi& operator=(cqi&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const cqi& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const cqi* internal_default_instance() { + return reinterpret_cast( + &_cqi_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(cqi& a, cqi& b) { + a.Swap(&b); + } + inline void Swap(cqi* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(cqi* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline cqi* New() const final { + return CreateMaybeMessage(nullptr); + } + + cqi* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const cqi& from); + void MergeFrom(const cqi& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(cqi* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.cqi"; + } + protected: + explicit cqi(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2ecqi_2eproto); + return ::descriptor_table_mp_2ecqi_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCqiTypeFieldNumber = 1, + kCqiVersionFieldNumber = 2, + }; + // .mp.MP_C_TYPE cqi_type = 1; + void clear_cqi_type(); + ::mp::MP_C_TYPE cqi_type() const; + void set_cqi_type(::mp::MP_C_TYPE value); + private: + ::mp::MP_C_TYPE _internal_cqi_type() const; + void _internal_set_cqi_type(::mp::MP_C_TYPE value); + public: + + // float cqi_version = 2; + void clear_cqi_version(); + float cqi_version() const; + void set_cqi_version(float value); + private: + float _internal_cqi_version() const; + void _internal_set_cqi_version(float value); + public: + + // @@protoc_insertion_point(class_scope:mp.cqi) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + int cqi_type_; + float cqi_version_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2ecqi_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// cqi + +// .mp.MP_C_TYPE cqi_type = 1; +inline void cqi::clear_cqi_type() { + cqi_type_ = 0; +} +inline ::mp::MP_C_TYPE cqi::_internal_cqi_type() const { + return static_cast< ::mp::MP_C_TYPE >(cqi_type_); +} +inline ::mp::MP_C_TYPE cqi::cqi_type() const { + // @@protoc_insertion_point(field_get:mp.cqi.cqi_type) + return _internal_cqi_type(); +} +inline void cqi::_internal_set_cqi_type(::mp::MP_C_TYPE value) { + + cqi_type_ = value; +} +inline void cqi::set_cqi_type(::mp::MP_C_TYPE value) { + _internal_set_cqi_type(value); + // @@protoc_insertion_point(field_set:mp.cqi.cqi_type) +} + +// float cqi_version = 2; +inline void cqi::clear_cqi_version() { + cqi_version_ = 0; +} +inline float cqi::_internal_cqi_version() const { + return cqi_version_; +} +inline float cqi::cqi_version() const { + // @@protoc_insertion_point(field_get:mp.cqi.cqi_version) + return _internal_cqi_version(); +} +inline void cqi::_internal_set_cqi_version(float value) { + + cqi_version_ = value; +} +inline void cqi::set_cqi_version(float value) { + _internal_set_cqi_version(value); + // @@protoc_insertion_point(field_set:mp.cqi.cqi_version) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +PROTOBUF_NAMESPACE_OPEN + +template <> struct is_proto_enum< ::mp::MP_C_TYPE> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::mp::MP_C_TYPE>() { + return ::mp::MP_C_TYPE_descriptor(); +} + +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2ecqi_2eproto diff --git a/api/MP/proto/mp.im.pb.cc b/api/MP/proto/mp.im.pb.cc new file mode 100644 index 0000000..535a961 --- /dev/null +++ b/api/MP/proto/mp.im.pb.cc @@ -0,0 +1,672 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.im.proto + +#include "mp.im.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +namespace mp { +namespace im { +class noticeDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _notice_default_instance_; +class dataDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _data_default_instance_; +} // namespace im +} // namespace mp +static void InitDefaultsscc_info_data_mp_2eim_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::im::_data_default_instance_; + new (ptr) ::mp::im::data(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::im::data::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_data_mp_2eim_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_data_mp_2eim_2eproto}, {}}; + +static void InitDefaultsscc_info_notice_mp_2eim_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::im::_notice_default_instance_; + new (ptr) ::mp::im::notice(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::im::notice::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_notice_mp_2eim_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_notice_mp_2eim_2eproto}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2eim_2eproto[2]; +static constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_mp_2eim_2eproto = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2eim_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2eim_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::im::notice, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::im::notice, message_id_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::im::data, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::im::data, msg_type_), + PROTOBUF_FIELD_OFFSET(::mp::im::data, session_type_), + PROTOBUF_FIELD_OFFSET(::mp::im::data, message_id_), + PROTOBUF_FIELD_OFFSET(::mp::im::data, account_), + PROTOBUF_FIELD_OFFSET(::mp::im::data, msg_data_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::im::notice)}, + { 6, -1, sizeof(::mp::im::data)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::im::_notice_default_instance_), + reinterpret_cast(&::mp::im::_data_default_instance_), +}; + +const char descriptor_table_protodef_mp_2eim_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\013mp.im.proto\022\005mp.im\032\013mp.mp.proto\"\034\n\006not" + "ice\022\022\n\nmessage_id\030\001 \001(\t\"\207\001\n\004data\022!\n\010msg_" + "type\030\001 \001(\0162\017.mp.MP_SUB_TYPE\022%\n\014session_t" + "ype\030\002 \001(\0162\017.mp.MP_SUB_TYPE\022\022\n\nmessage_id" + "\030\003 \001(\t\022\017\n\007account\030\004 \001(\004\022\020\n\010msg_data\030\005 \001(" + "\tb\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2eim_2eproto_deps[1] = { + &::descriptor_table_mp_2emp_2eproto, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2eim_2eproto_sccs[2] = { + &scc_info_data_mp_2eim_2eproto.base, + &scc_info_notice_mp_2eim_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2eim_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2eim_2eproto = { + false, false, descriptor_table_protodef_mp_2eim_2eproto, "mp.im.proto", 209, + &descriptor_table_mp_2eim_2eproto_once, descriptor_table_mp_2eim_2eproto_sccs, descriptor_table_mp_2eim_2eproto_deps, 2, 1, + schemas, file_default_instances, TableStruct_mp_2eim_2eproto::offsets, + file_level_metadata_mp_2eim_2eproto, 2, file_level_enum_descriptors_mp_2eim_2eproto, file_level_service_descriptors_mp_2eim_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2eim_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2eim_2eproto)), true); +namespace mp { +namespace im { + +// =================================================================== + +void notice::InitAsDefaultInstance() { +} +class notice::_Internal { + public: +}; + +notice::notice(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.im.notice) +} +notice::notice(const notice& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + message_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_message_id().empty()) { + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_message_id(), + GetArena()); + } + // @@protoc_insertion_point(copy_constructor:mp.im.notice) +} + +void notice::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_notice_mp_2eim_2eproto.base); + message_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +notice::~notice() { + // @@protoc_insertion_point(destructor:mp.im.notice) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void notice::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + message_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void notice::ArenaDtor(void* object) { + notice* _this = reinterpret_cast< notice* >(object); + (void)_this; +} +void notice::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void notice::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const notice& notice::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_notice_mp_2eim_2eproto.base); + return *internal_default_instance(); +} + + +void notice::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.im.notice) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + message_id_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* notice::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // string message_id = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { + auto str = _internal_mutable_message_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.im.notice.message_id")); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* notice::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.im.notice) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // string message_id = 1; + if (this->message_id().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_message_id().data(), static_cast(this->_internal_message_id().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.im.notice.message_id"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_message_id(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.im.notice) + return target; +} + +size_t notice::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.im.notice) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string message_id = 1; + if (this->message_id().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_message_id()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void notice::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.im.notice) + GOOGLE_DCHECK_NE(&from, this); + const notice* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.im.notice) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.im.notice) + MergeFrom(*source); + } +} + +void notice::MergeFrom(const notice& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.im.notice) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.message_id().size() > 0) { + _internal_set_message_id(from._internal_message_id()); + } +} + +void notice::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.im.notice) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void notice::CopyFrom(const notice& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.im.notice) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool notice::IsInitialized() const { + return true; +} + +void notice::InternalSwap(notice* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + message_id_.Swap(&other->message_id_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} + +::PROTOBUF_NAMESPACE_ID::Metadata notice::GetMetadata() const { + return GetMetadataStatic(); +} + + +// =================================================================== + +void data::InitAsDefaultInstance() { +} +class data::_Internal { + public: +}; + +data::data(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.im.data) +} +data::data(const data& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + message_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_message_id().empty()) { + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_message_id(), + GetArena()); + } + msg_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_msg_data().empty()) { + msg_data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_msg_data(), + GetArena()); + } + ::memcpy(&msg_type_, &from.msg_type_, + static_cast(reinterpret_cast(&account_) - + reinterpret_cast(&msg_type_)) + sizeof(account_)); + // @@protoc_insertion_point(copy_constructor:mp.im.data) +} + +void data::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_data_mp_2eim_2eproto.base); + message_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + msg_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + ::memset(&msg_type_, 0, static_cast( + reinterpret_cast(&account_) - + reinterpret_cast(&msg_type_)) + sizeof(account_)); +} + +data::~data() { + // @@protoc_insertion_point(destructor:mp.im.data) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void data::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + message_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + msg_data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void data::ArenaDtor(void* object) { + data* _this = reinterpret_cast< data* >(object); + (void)_this; +} +void data::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void data::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const data& data::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_data_mp_2eim_2eproto.base); + return *internal_default_instance(); +} + + +void data::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.im.data) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + message_id_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + msg_data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::memset(&msg_type_, 0, static_cast( + reinterpret_cast(&account_) - + reinterpret_cast(&msg_type_)) + sizeof(account_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* data::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // .mp.MP_SUB_TYPE msg_type = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_msg_type(static_cast<::mp::MP_SUB_TYPE>(val)); + } else goto handle_unusual; + continue; + // .mp.MP_SUB_TYPE session_type = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_session_type(static_cast<::mp::MP_SUB_TYPE>(val)); + } else goto handle_unusual; + continue; + // string message_id = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { + auto str = _internal_mutable_message_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.im.data.message_id")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint64 account = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { + account_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string msg_data = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { + auto str = _internal_mutable_msg_data(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.im.data.msg_data")); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* data::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.im.data) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // .mp.MP_SUB_TYPE msg_type = 1; + if (this->msg_type() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_msg_type(), target); + } + + // .mp.MP_SUB_TYPE session_type = 2; + if (this->session_type() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 2, this->_internal_session_type(), target); + } + + // string message_id = 3; + if (this->message_id().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_message_id().data(), static_cast(this->_internal_message_id().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.im.data.message_id"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_message_id(), target); + } + + // uint64 account = 4; + if (this->account() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(4, this->_internal_account(), target); + } + + // string msg_data = 5; + if (this->msg_data().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_msg_data().data(), static_cast(this->_internal_msg_data().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.im.data.msg_data"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_msg_data(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.im.data) + return target; +} + +size_t data::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.im.data) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string message_id = 3; + if (this->message_id().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_message_id()); + } + + // string msg_data = 5; + if (this->msg_data().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_msg_data()); + } + + // .mp.MP_SUB_TYPE msg_type = 1; + if (this->msg_type() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_msg_type()); + } + + // .mp.MP_SUB_TYPE session_type = 2; + if (this->session_type() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_session_type()); + } + + // uint64 account = 4; + if (this->account() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_account()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void data::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.im.data) + GOOGLE_DCHECK_NE(&from, this); + const data* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.im.data) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.im.data) + MergeFrom(*source); + } +} + +void data::MergeFrom(const data& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.im.data) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.message_id().size() > 0) { + _internal_set_message_id(from._internal_message_id()); + } + if (from.msg_data().size() > 0) { + _internal_set_msg_data(from._internal_msg_data()); + } + if (from.msg_type() != 0) { + _internal_set_msg_type(from._internal_msg_type()); + } + if (from.session_type() != 0) { + _internal_set_session_type(from._internal_session_type()); + } + if (from.account() != 0) { + _internal_set_account(from._internal_account()); + } +} + +void data::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.im.data) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void data::CopyFrom(const data& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.im.data) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool data::IsInitialized() const { + return true; +} + +void data::InternalSwap(data* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + message_id_.Swap(&other->message_id_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + msg_data_.Swap(&other->msg_data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(data, account_) + + sizeof(data::account_) + - PROTOBUF_FIELD_OFFSET(data, msg_type_)>( + reinterpret_cast(&msg_type_), + reinterpret_cast(&other->msg_type_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata data::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace im +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::im::notice* Arena::CreateMaybeMessage< ::mp::im::notice >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::im::notice >(arena); +} +template<> PROTOBUF_NOINLINE ::mp::im::data* Arena::CreateMaybeMessage< ::mp::im::data >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::im::data >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.im.pb.h b/api/MP/proto/mp.im.pb.h new file mode 100644 index 0000000..80ba768 --- /dev/null +++ b/api/MP/proto/mp.im.pb.h @@ -0,0 +1,773 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.im.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2eim_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2eim_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include "mp.mp.pb.h" +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2eim_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2eim_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[2] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2eim_2eproto; +namespace mp { +namespace im { +class data; +class dataDefaultTypeInternal; +extern dataDefaultTypeInternal _data_default_instance_; +class notice; +class noticeDefaultTypeInternal; +extern noticeDefaultTypeInternal _notice_default_instance_; +} // namespace im +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::im::data* Arena::CreateMaybeMessage<::mp::im::data>(Arena*); +template<> ::mp::im::notice* Arena::CreateMaybeMessage<::mp::im::notice>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { +namespace im { + +// =================================================================== + +class notice PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.im.notice) */ { + public: + inline notice() : notice(nullptr) {}; + virtual ~notice(); + + notice(const notice& from); + notice(notice&& from) noexcept + : notice() { + *this = ::std::move(from); + } + + inline notice& operator=(const notice& from) { + CopyFrom(from); + return *this; + } + inline notice& operator=(notice&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const notice& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const notice* internal_default_instance() { + return reinterpret_cast( + &_notice_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(notice& a, notice& b) { + a.Swap(&b); + } + inline void Swap(notice* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(notice* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline notice* New() const final { + return CreateMaybeMessage(nullptr); + } + + notice* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const notice& from); + void MergeFrom(const notice& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(notice* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.im.notice"; + } + protected: + explicit notice(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2eim_2eproto); + return ::descriptor_table_mp_2eim_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kMessageIdFieldNumber = 1, + }; + // string message_id = 1; + void clear_message_id(); + const std::string& message_id() const; + void set_message_id(const std::string& value); + void set_message_id(std::string&& value); + void set_message_id(const char* value); + void set_message_id(const char* value, size_t size); + std::string* mutable_message_id(); + std::string* release_message_id(); + void set_allocated_message_id(std::string* message_id); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_message_id(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_message_id( + std::string* message_id); + private: + const std::string& _internal_message_id() const; + void _internal_set_message_id(const std::string& value); + std::string* _internal_mutable_message_id(); + public: + + // @@protoc_insertion_point(class_scope:mp.im.notice) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr message_id_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2eim_2eproto; +}; +// ------------------------------------------------------------------- + +class data PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.im.data) */ { + public: + inline data() : data(nullptr) {}; + virtual ~data(); + + data(const data& from); + data(data&& from) noexcept + : data() { + *this = ::std::move(from); + } + + inline data& operator=(const data& from) { + CopyFrom(from); + return *this; + } + inline data& operator=(data&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const data& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const data* internal_default_instance() { + return reinterpret_cast( + &_data_default_instance_); + } + static constexpr int kIndexInFileMessages = + 1; + + friend void swap(data& a, data& b) { + a.Swap(&b); + } + inline void Swap(data* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(data* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline data* New() const final { + return CreateMaybeMessage(nullptr); + } + + data* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const data& from); + void MergeFrom(const data& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(data* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.im.data"; + } + protected: + explicit data(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2eim_2eproto); + return ::descriptor_table_mp_2eim_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kMessageIdFieldNumber = 3, + kMsgDataFieldNumber = 5, + kMsgTypeFieldNumber = 1, + kSessionTypeFieldNumber = 2, + kAccountFieldNumber = 4, + }; + // string message_id = 3; + void clear_message_id(); + const std::string& message_id() const; + void set_message_id(const std::string& value); + void set_message_id(std::string&& value); + void set_message_id(const char* value); + void set_message_id(const char* value, size_t size); + std::string* mutable_message_id(); + std::string* release_message_id(); + void set_allocated_message_id(std::string* message_id); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_message_id(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_message_id( + std::string* message_id); + private: + const std::string& _internal_message_id() const; + void _internal_set_message_id(const std::string& value); + std::string* _internal_mutable_message_id(); + public: + + // string msg_data = 5; + void clear_msg_data(); + const std::string& msg_data() const; + void set_msg_data(const std::string& value); + void set_msg_data(std::string&& value); + void set_msg_data(const char* value); + void set_msg_data(const char* value, size_t size); + std::string* mutable_msg_data(); + std::string* release_msg_data(); + void set_allocated_msg_data(std::string* msg_data); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_msg_data(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_msg_data( + std::string* msg_data); + private: + const std::string& _internal_msg_data() const; + void _internal_set_msg_data(const std::string& value); + std::string* _internal_mutable_msg_data(); + public: + + // .mp.MP_SUB_TYPE msg_type = 1; + void clear_msg_type(); + ::mp::MP_SUB_TYPE msg_type() const; + void set_msg_type(::mp::MP_SUB_TYPE value); + private: + ::mp::MP_SUB_TYPE _internal_msg_type() const; + void _internal_set_msg_type(::mp::MP_SUB_TYPE value); + public: + + // .mp.MP_SUB_TYPE session_type = 2; + void clear_session_type(); + ::mp::MP_SUB_TYPE session_type() const; + void set_session_type(::mp::MP_SUB_TYPE value); + private: + ::mp::MP_SUB_TYPE _internal_session_type() const; + void _internal_set_session_type(::mp::MP_SUB_TYPE value); + public: + + // uint64 account = 4; + void clear_account(); + ::PROTOBUF_NAMESPACE_ID::uint64 account() const; + void set_account(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_account() const; + void _internal_set_account(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // @@protoc_insertion_point(class_scope:mp.im.data) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr message_id_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr msg_data_; + int msg_type_; + int session_type_; + ::PROTOBUF_NAMESPACE_ID::uint64 account_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2eim_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// notice + +// string message_id = 1; +inline void notice::clear_message_id() { + message_id_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& notice::message_id() const { + // @@protoc_insertion_point(field_get:mp.im.notice.message_id) + return _internal_message_id(); +} +inline void notice::set_message_id(const std::string& value) { + _internal_set_message_id(value); + // @@protoc_insertion_point(field_set:mp.im.notice.message_id) +} +inline std::string* notice::mutable_message_id() { + // @@protoc_insertion_point(field_mutable:mp.im.notice.message_id) + return _internal_mutable_message_id(); +} +inline const std::string& notice::_internal_message_id() const { + return message_id_.Get(); +} +inline void notice::_internal_set_message_id(const std::string& value) { + + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void notice::set_message_id(std::string&& value) { + + message_id_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.im.notice.message_id) +} +inline void notice::set_message_id(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.im.notice.message_id) +} +inline void notice::set_message_id(const char* value, + size_t size) { + + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.im.notice.message_id) +} +inline std::string* notice::_internal_mutable_message_id() { + + return message_id_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* notice::release_message_id() { + // @@protoc_insertion_point(field_release:mp.im.notice.message_id) + return message_id_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void notice::set_allocated_message_id(std::string* message_id) { + if (message_id != nullptr) { + + } else { + + } + message_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), message_id, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.im.notice.message_id) +} +inline std::string* notice::unsafe_arena_release_message_id() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.im.notice.message_id) + GOOGLE_DCHECK(GetArena() != nullptr); + + return message_id_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void notice::unsafe_arena_set_allocated_message_id( + std::string* message_id) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (message_id != nullptr) { + + } else { + + } + message_id_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + message_id, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.im.notice.message_id) +} + +// ------------------------------------------------------------------- + +// data + +// .mp.MP_SUB_TYPE msg_type = 1; +inline void data::clear_msg_type() { + msg_type_ = 0; +} +inline ::mp::MP_SUB_TYPE data::_internal_msg_type() const { + return static_cast< ::mp::MP_SUB_TYPE >(msg_type_); +} +inline ::mp::MP_SUB_TYPE data::msg_type() const { + // @@protoc_insertion_point(field_get:mp.im.data.msg_type) + return _internal_msg_type(); +} +inline void data::_internal_set_msg_type(::mp::MP_SUB_TYPE value) { + + msg_type_ = value; +} +inline void data::set_msg_type(::mp::MP_SUB_TYPE value) { + _internal_set_msg_type(value); + // @@protoc_insertion_point(field_set:mp.im.data.msg_type) +} + +// .mp.MP_SUB_TYPE session_type = 2; +inline void data::clear_session_type() { + session_type_ = 0; +} +inline ::mp::MP_SUB_TYPE data::_internal_session_type() const { + return static_cast< ::mp::MP_SUB_TYPE >(session_type_); +} +inline ::mp::MP_SUB_TYPE data::session_type() const { + // @@protoc_insertion_point(field_get:mp.im.data.session_type) + return _internal_session_type(); +} +inline void data::_internal_set_session_type(::mp::MP_SUB_TYPE value) { + + session_type_ = value; +} +inline void data::set_session_type(::mp::MP_SUB_TYPE value) { + _internal_set_session_type(value); + // @@protoc_insertion_point(field_set:mp.im.data.session_type) +} + +// string message_id = 3; +inline void data::clear_message_id() { + message_id_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& data::message_id() const { + // @@protoc_insertion_point(field_get:mp.im.data.message_id) + return _internal_message_id(); +} +inline void data::set_message_id(const std::string& value) { + _internal_set_message_id(value); + // @@protoc_insertion_point(field_set:mp.im.data.message_id) +} +inline std::string* data::mutable_message_id() { + // @@protoc_insertion_point(field_mutable:mp.im.data.message_id) + return _internal_mutable_message_id(); +} +inline const std::string& data::_internal_message_id() const { + return message_id_.Get(); +} +inline void data::_internal_set_message_id(const std::string& value) { + + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void data::set_message_id(std::string&& value) { + + message_id_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.im.data.message_id) +} +inline void data::set_message_id(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.im.data.message_id) +} +inline void data::set_message_id(const char* value, + size_t size) { + + message_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.im.data.message_id) +} +inline std::string* data::_internal_mutable_message_id() { + + return message_id_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* data::release_message_id() { + // @@protoc_insertion_point(field_release:mp.im.data.message_id) + return message_id_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void data::set_allocated_message_id(std::string* message_id) { + if (message_id != nullptr) { + + } else { + + } + message_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), message_id, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.im.data.message_id) +} +inline std::string* data::unsafe_arena_release_message_id() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.im.data.message_id) + GOOGLE_DCHECK(GetArena() != nullptr); + + return message_id_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void data::unsafe_arena_set_allocated_message_id( + std::string* message_id) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (message_id != nullptr) { + + } else { + + } + message_id_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + message_id, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.im.data.message_id) +} + +// uint64 account = 4; +inline void data::clear_account() { + account_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 data::_internal_account() const { + return account_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 data::account() const { + // @@protoc_insertion_point(field_get:mp.im.data.account) + return _internal_account(); +} +inline void data::_internal_set_account(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + account_ = value; +} +inline void data::set_account(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_account(value); + // @@protoc_insertion_point(field_set:mp.im.data.account) +} + +// string msg_data = 5; +inline void data::clear_msg_data() { + msg_data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& data::msg_data() const { + // @@protoc_insertion_point(field_get:mp.im.data.msg_data) + return _internal_msg_data(); +} +inline void data::set_msg_data(const std::string& value) { + _internal_set_msg_data(value); + // @@protoc_insertion_point(field_set:mp.im.data.msg_data) +} +inline std::string* data::mutable_msg_data() { + // @@protoc_insertion_point(field_mutable:mp.im.data.msg_data) + return _internal_mutable_msg_data(); +} +inline const std::string& data::_internal_msg_data() const { + return msg_data_.Get(); +} +inline void data::_internal_set_msg_data(const std::string& value) { + + msg_data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void data::set_msg_data(std::string&& value) { + + msg_data_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.im.data.msg_data) +} +inline void data::set_msg_data(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + msg_data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.im.data.msg_data) +} +inline void data::set_msg_data(const char* value, + size_t size) { + + msg_data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.im.data.msg_data) +} +inline std::string* data::_internal_mutable_msg_data() { + + return msg_data_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* data::release_msg_data() { + // @@protoc_insertion_point(field_release:mp.im.data.msg_data) + return msg_data_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void data::set_allocated_msg_data(std::string* msg_data) { + if (msg_data != nullptr) { + + } else { + + } + msg_data_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), msg_data, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.im.data.msg_data) +} +inline std::string* data::unsafe_arena_release_msg_data() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.im.data.msg_data) + GOOGLE_DCHECK(GetArena() != nullptr); + + return msg_data_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void data::unsafe_arena_set_allocated_msg_data( + std::string* msg_data) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (msg_data != nullptr) { + + } else { + + } + msg_data_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + msg_data, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.im.data.msg_data) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ +// ------------------------------------------------------------------- + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace im +} // namespace mp + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2eim_2eproto diff --git a/api/MP/proto/mp.mp.pb.cc b/api/MP/proto/mp.mp.pb.cc new file mode 100644 index 0000000..4ab8080 --- /dev/null +++ b/api/MP/proto/mp.mp.pb.cc @@ -0,0 +1,164 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.mp.proto + +#include "mp.mp.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +namespace mp { +} // namespace mp +static constexpr ::PROTOBUF_NAMESPACE_ID::Metadata* file_level_metadata_mp_2emp_2eproto = nullptr; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_mp_2emp_2eproto[2]; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2emp_2eproto = nullptr; +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2emp_2eproto::offsets[1] = {}; +static constexpr ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema* schemas = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::Message* const* file_default_instances = nullptr; + +const char descriptor_table_protodef_mp_2emp_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\013mp.mp.proto\022\002mp*\360\002\n\007MP_TYPE\022\024\n\020MP_REQU" + "EST_LOGIN\020\000\022\025\n\021MP_REQUEST_LOGOUT\020\001\022\027\n\023MP" + "_REQUEST_REGISTER\020\002\022\023\n\017MP_REQUEST_CODE\020\003" + "\022\026\n\022MP_REQUEST_FRIENDS\020\004\022\025\n\021MP_REQUEST_G" + "ROUPS\020\005\022\025\n\021MP_RESPONSE_LOGIN\020\024\022\026\n\022MP_RES" + "PONSE_LOGOUT\020\025\022\030\n\024MP_RESPONSE_REGISTER\020\026" + "\022\024\n\020MP_RESPONSE_CODE\020\027\022\027\n\023MP_RESPONSE_FR" + "IENDS\020\030\022\026\n\022MP_RESPONSE_GROUPS\020\031\022\021\n\014MP_IM" + "_NOTICE\020\310\001\022\023\n\016MP_IM_PUSH_MSG\020\311\001\022\023\n\016MP_IM" + "_PULL_MSG\020\312\001\022\016\n\tMP_IM_MSG\020\313\001*\353\006\n\013MP_SUB_" + "TYPE\022\022\n\016MP_LOGIN_EMAIL\020\000\022\022\n\016MP_LOGIN_PHO" + "NE\020\001\022\024\n\020MP_LOGIN_ACCOUNT\020\002\022\025\n\021MP_REGISTE" + "R_EMAIL\020\003\022\025\n\021MP_REGISTER_PHONE\020\004\022\024\n\020MP_L" + "OGIN_SUCCESS\020\005\022\021\n\rMP_LOGIN_FAIL\020\006\022\027\n\023MP_" + "REGISTER_SUCCESS\020\007\022\024\n\020MP_REGISTER_FAIL\020\010" + "\022\021\n\rMP_CODE_EMAIL\020\017\022\021\n\rMP_CODE_PHONE\020\020\022\023" + "\n\017MP_CODE_SUCCESS\020\021\022\020\n\014MP_CODE_FAIL\020\034\022\024\n" + "\020MP_ADD_FRIENDS_0\0203\022\024\n\020MP_ADD_FRIENDS_1\020" + "4\022\024\n\020MP_ADD_FRIENDS_2\0205\022\032\n\026MP_ADD_FRIEND" + "S_ACCOUNT\0206\022\030\n\024MP_ADD_FRIENDS_EMAIL\0207\022\030\n" + "\024MP_ADD_FRIENDS_PHONE\0208\022\025\n\021MP_ADD_BLACK_" + "LIST\020;\022\025\n\021MP_ADD_WHITE_LIST\020<\022\024\n\020MP_REMO" + "VE_FRIEND\020=\022\022\n\016MP_GET_FRIENDS\020>\022\022\n\016MP_AD" + "D_GROUP_0\020\?\022\022\n\016MP_ADD_GROUP_1\020@\022\022\n\016MP_AD" + "D_GROUP_2\020A\022\020\n\014MP_ADD_GROUP\020B\022\023\n\017MP_REMO" + "VE_GROUP\020C\022\021\n\rMP_GET_GROUPS\020D\022\022\n\016MP_ADD_" + "SUCCESS\020E\022\025\n\021MP_REMOVE_SUCCESS\020F\022\022\n\016MP_G" + "ET_SUCCESS\020G\022\017\n\013MP_ADD_FAIL\020H\022\022\n\016MP_REMO" + "VE_FAIL\020I\022\017\n\013MP_GET_FAIL\020J\022\020\n\014MP_ADD_CHE" + "CK\020P\022\017\n\nMP_IM_TEXT\020\310\001\022\020\n\013MP_IM_PHOTO\020\311\001\022" + "\020\n\013MP_IM_AUDIO\020\312\001\022\020\n\013MP_IM_VIDEO\020\313\001\022\016\n\tM" + "P_FRIEND\020\254\002\022\r\n\010MP_GROUP\020\255\002b\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2emp_2eproto_deps[1] = { +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2emp_2eproto_sccs[1] = { +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2emp_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2emp_2eproto = { + false, false, descriptor_table_protodef_mp_2emp_2eproto, "mp.mp.proto", 1274, + &descriptor_table_mp_2emp_2eproto_once, descriptor_table_mp_2emp_2eproto_sccs, descriptor_table_mp_2emp_2eproto_deps, 0, 0, + schemas, file_default_instances, TableStruct_mp_2emp_2eproto::offsets, + file_level_metadata_mp_2emp_2eproto, 0, file_level_enum_descriptors_mp_2emp_2eproto, file_level_service_descriptors_mp_2emp_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2emp_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2emp_2eproto)), true); +namespace mp { +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_TYPE_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_mp_2emp_2eproto); + return file_level_enum_descriptors_mp_2emp_2eproto[0]; +} +bool MP_TYPE_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 200: + case 201: + case 202: + case 203: + return true; + default: + return false; + } +} + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_SUB_TYPE_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_mp_2emp_2eproto); + return file_level_enum_descriptors_mp_2emp_2eproto[1]; +} +bool MP_SUB_TYPE_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 15: + case 16: + case 17: + case 28: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 59: + case 60: + case 61: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 80: + case 200: + case 201: + case 202: + case 203: + case 300: + case 301: + return true; + default: + return false; + } +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.mp.pb.h b/api/MP/proto/mp.mp.pb.h new file mode 100644 index 0000000..9f8288b --- /dev/null +++ b/api/MP/proto/mp.mp.pb.h @@ -0,0 +1,202 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.mp.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2emp_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2emp_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2emp_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2emp_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2emp_2eproto; +PROTOBUF_NAMESPACE_OPEN +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +enum MP_TYPE : int { + MP_REQUEST_LOGIN = 0, + MP_REQUEST_LOGOUT = 1, + MP_REQUEST_REGISTER = 2, + MP_REQUEST_CODE = 3, + MP_REQUEST_FRIENDS = 4, + MP_REQUEST_GROUPS = 5, + MP_RESPONSE_LOGIN = 20, + MP_RESPONSE_LOGOUT = 21, + MP_RESPONSE_REGISTER = 22, + MP_RESPONSE_CODE = 23, + MP_RESPONSE_FRIENDS = 24, + MP_RESPONSE_GROUPS = 25, + MP_IM_NOTICE = 200, + MP_IM_PUSH_MSG = 201, + MP_IM_PULL_MSG = 202, + MP_IM_MSG = 203, + MP_TYPE_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + MP_TYPE_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool MP_TYPE_IsValid(int value); +constexpr MP_TYPE MP_TYPE_MIN = MP_REQUEST_LOGIN; +constexpr MP_TYPE MP_TYPE_MAX = MP_IM_MSG; +constexpr int MP_TYPE_ARRAYSIZE = MP_TYPE_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_TYPE_descriptor(); +template +inline const std::string& MP_TYPE_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function MP_TYPE_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + MP_TYPE_descriptor(), enum_t_value); +} +inline bool MP_TYPE_Parse( + const std::string& name, MP_TYPE* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + MP_TYPE_descriptor(), name, value); +} +enum MP_SUB_TYPE : int { + MP_LOGIN_EMAIL = 0, + MP_LOGIN_PHONE = 1, + MP_LOGIN_ACCOUNT = 2, + MP_REGISTER_EMAIL = 3, + MP_REGISTER_PHONE = 4, + MP_LOGIN_SUCCESS = 5, + MP_LOGIN_FAIL = 6, + MP_REGISTER_SUCCESS = 7, + MP_REGISTER_FAIL = 8, + MP_CODE_EMAIL = 15, + MP_CODE_PHONE = 16, + MP_CODE_SUCCESS = 17, + MP_CODE_FAIL = 28, + MP_ADD_FRIENDS_0 = 51, + MP_ADD_FRIENDS_1 = 52, + MP_ADD_FRIENDS_2 = 53, + MP_ADD_FRIENDS_ACCOUNT = 54, + MP_ADD_FRIENDS_EMAIL = 55, + MP_ADD_FRIENDS_PHONE = 56, + MP_ADD_BLACK_LIST = 59, + MP_ADD_WHITE_LIST = 60, + MP_REMOVE_FRIEND = 61, + MP_GET_FRIENDS = 62, + MP_ADD_GROUP_0 = 63, + MP_ADD_GROUP_1 = 64, + MP_ADD_GROUP_2 = 65, + MP_ADD_GROUP = 66, + MP_REMOVE_GROUP = 67, + MP_GET_GROUPS = 68, + MP_ADD_SUCCESS = 69, + MP_REMOVE_SUCCESS = 70, + MP_GET_SUCCESS = 71, + MP_ADD_FAIL = 72, + MP_REMOVE_FAIL = 73, + MP_GET_FAIL = 74, + MP_ADD_CHECK = 80, + MP_IM_TEXT = 200, + MP_IM_PHOTO = 201, + MP_IM_AUDIO = 202, + MP_IM_VIDEO = 203, + MP_FRIEND = 300, + MP_GROUP = 301, + MP_SUB_TYPE_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + MP_SUB_TYPE_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool MP_SUB_TYPE_IsValid(int value); +constexpr MP_SUB_TYPE MP_SUB_TYPE_MIN = MP_LOGIN_EMAIL; +constexpr MP_SUB_TYPE MP_SUB_TYPE_MAX = MP_GROUP; +constexpr int MP_SUB_TYPE_ARRAYSIZE = MP_SUB_TYPE_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_SUB_TYPE_descriptor(); +template +inline const std::string& MP_SUB_TYPE_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function MP_SUB_TYPE_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + MP_SUB_TYPE_descriptor(), enum_t_value); +} +inline bool MP_SUB_TYPE_Parse( + const std::string& name, MP_SUB_TYPE* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + MP_SUB_TYPE_descriptor(), name, value); +} +// =================================================================== + + +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +PROTOBUF_NAMESPACE_OPEN + +template <> struct is_proto_enum< ::mp::MP_TYPE> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::mp::MP_TYPE>() { + return ::mp::MP_TYPE_descriptor(); +} +template <> struct is_proto_enum< ::mp::MP_SUB_TYPE> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::mp::MP_SUB_TYPE>() { + return ::mp::MP_SUB_TYPE_descriptor(); +} + +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2emp_2eproto diff --git a/api/MP/proto/mp.mph.pb.cc b/api/MP/proto/mp.mph.pb.cc new file mode 100644 index 0000000..a3b270d --- /dev/null +++ b/api/MP/proto/mp.mph.pb.cc @@ -0,0 +1,411 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.mph.proto + +#include "mp.mph.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +namespace mp { +class mphDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _mph_default_instance_; +} // namespace mp +static void InitDefaultsscc_info_mph_mp_2emph_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::_mph_default_instance_; + new (ptr) ::mp::mph(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::mph::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_mph_mp_2emph_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_mph_mp_2emph_2eproto}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2emph_2eproto[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_mp_2emph_2eproto = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2emph_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2emph_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::mph, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::mph, mpb_size_), + PROTOBUF_FIELD_OFFSET(::mp::mph, mp_id_), + PROTOBUF_FIELD_OFFSET(::mp::mph, mp_sum_), + PROTOBUF_FIELD_OFFSET(::mp::mph, mp_type_), + PROTOBUF_FIELD_OFFSET(::mp::mph, path_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::mph)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::_mph_default_instance_), +}; + +const char descriptor_table_protodef_mp_2emph_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\014mp.mph.proto\022\002mp\032\013mp.mp.proto\"b\n\003mph\022\020" + "\n\010mpb_size\030\001 \001(\r\022\r\n\005mp_id\030\002 \001(\004\022\016\n\006mp_su" + "m\030\003 \001(\r\022\034\n\007mp_type\030\004 \001(\0162\013.mp.MP_TYPE\022\014\n" + "\004path\030\005 \001(\tb\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2emph_2eproto_deps[1] = { + &::descriptor_table_mp_2emp_2eproto, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2emph_2eproto_sccs[1] = { + &scc_info_mph_mp_2emph_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2emph_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2emph_2eproto = { + false, false, descriptor_table_protodef_mp_2emph_2eproto, "mp.mph.proto", 139, + &descriptor_table_mp_2emph_2eproto_once, descriptor_table_mp_2emph_2eproto_sccs, descriptor_table_mp_2emph_2eproto_deps, 1, 1, + schemas, file_default_instances, TableStruct_mp_2emph_2eproto::offsets, + file_level_metadata_mp_2emph_2eproto, 1, file_level_enum_descriptors_mp_2emph_2eproto, file_level_service_descriptors_mp_2emph_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2emph_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2emph_2eproto)), true); +namespace mp { + +// =================================================================== + +void mph::InitAsDefaultInstance() { +} +class mph::_Internal { + public: +}; + +mph::mph(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.mph) +} +mph::mph(const mph& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + path_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_path().empty()) { + path_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_path(), + GetArena()); + } + ::memcpy(&mp_id_, &from.mp_id_, + static_cast(reinterpret_cast(&mp_type_) - + reinterpret_cast(&mp_id_)) + sizeof(mp_type_)); + // @@protoc_insertion_point(copy_constructor:mp.mph) +} + +void mph::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_mph_mp_2emph_2eproto.base); + path_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + ::memset(&mp_id_, 0, static_cast( + reinterpret_cast(&mp_type_) - + reinterpret_cast(&mp_id_)) + sizeof(mp_type_)); +} + +mph::~mph() { + // @@protoc_insertion_point(destructor:mp.mph) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void mph::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + path_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void mph::ArenaDtor(void* object) { + mph* _this = reinterpret_cast< mph* >(object); + (void)_this; +} +void mph::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void mph::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const mph& mph::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_mph_mp_2emph_2eproto.base); + return *internal_default_instance(); +} + + +void mph::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.mph) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + path_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::memset(&mp_id_, 0, static_cast( + reinterpret_cast(&mp_type_) - + reinterpret_cast(&mp_id_)) + sizeof(mp_type_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* mph::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // uint32 mpb_size = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { + mpb_size_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint64 mp_id = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { + mp_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint32 mp_sum = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { + mp_sum_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // .mp.MP_TYPE mp_type = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_mp_type(static_cast<::mp::MP_TYPE>(val)); + } else goto handle_unusual; + continue; + // string path = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { + auto str = _internal_mutable_path(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.mph.path")); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* mph::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.mph) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // uint32 mpb_size = 1; + if (this->mpb_size() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt32ToArray(1, this->_internal_mpb_size(), target); + } + + // uint64 mp_id = 2; + if (this->mp_id() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(2, this->_internal_mp_id(), target); + } + + // uint32 mp_sum = 3; + if (this->mp_sum() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt32ToArray(3, this->_internal_mp_sum(), target); + } + + // .mp.MP_TYPE mp_type = 4; + if (this->mp_type() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 4, this->_internal_mp_type(), target); + } + + // string path = 5; + if (this->path().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_path().data(), static_cast(this->_internal_path().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.mph.path"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_path(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.mph) + return target; +} + +size_t mph::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.mph) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string path = 5; + if (this->path().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_path()); + } + + // uint64 mp_id = 2; + if (this->mp_id() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_mp_id()); + } + + // uint32 mpb_size = 1; + if (this->mpb_size() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt32Size( + this->_internal_mpb_size()); + } + + // uint32 mp_sum = 3; + if (this->mp_sum() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt32Size( + this->_internal_mp_sum()); + } + + // .mp.MP_TYPE mp_type = 4; + if (this->mp_type() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_mp_type()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void mph::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.mph) + GOOGLE_DCHECK_NE(&from, this); + const mph* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.mph) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.mph) + MergeFrom(*source); + } +} + +void mph::MergeFrom(const mph& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.mph) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.path().size() > 0) { + _internal_set_path(from._internal_path()); + } + if (from.mp_id() != 0) { + _internal_set_mp_id(from._internal_mp_id()); + } + if (from.mpb_size() != 0) { + _internal_set_mpb_size(from._internal_mpb_size()); + } + if (from.mp_sum() != 0) { + _internal_set_mp_sum(from._internal_mp_sum()); + } + if (from.mp_type() != 0) { + _internal_set_mp_type(from._internal_mp_type()); + } +} + +void mph::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.mph) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void mph::CopyFrom(const mph& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.mph) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool mph::IsInitialized() const { + return true; +} + +void mph::InternalSwap(mph* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + path_.Swap(&other->path_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(mph, mp_type_) + + sizeof(mph::mp_type_) + - PROTOBUF_FIELD_OFFSET(mph, mp_id_)>( + reinterpret_cast(&mp_id_), + reinterpret_cast(&other->mp_id_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata mph::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::mph* Arena::CreateMaybeMessage< ::mp::mph >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::mph >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.mph.pb.h b/api/MP/proto/mp.mph.pb.h new file mode 100644 index 0000000..48accf3 --- /dev/null +++ b/api/MP/proto/mp.mph.pb.h @@ -0,0 +1,448 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.mph.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2emph_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2emph_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include "mp.mp.pb.h" +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2emph_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2emph_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2emph_2eproto; +namespace mp { +class mph; +class mphDefaultTypeInternal; +extern mphDefaultTypeInternal _mph_default_instance_; +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::mph* Arena::CreateMaybeMessage<::mp::mph>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +// =================================================================== + +class mph PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.mph) */ { + public: + inline mph() : mph(nullptr) {}; + virtual ~mph(); + + mph(const mph& from); + mph(mph&& from) noexcept + : mph() { + *this = ::std::move(from); + } + + inline mph& operator=(const mph& from) { + CopyFrom(from); + return *this; + } + inline mph& operator=(mph&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const mph& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const mph* internal_default_instance() { + return reinterpret_cast( + &_mph_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(mph& a, mph& b) { + a.Swap(&b); + } + inline void Swap(mph* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(mph* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline mph* New() const final { + return CreateMaybeMessage(nullptr); + } + + mph* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const mph& from); + void MergeFrom(const mph& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(mph* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.mph"; + } + protected: + explicit mph(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2emph_2eproto); + return ::descriptor_table_mp_2emph_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPathFieldNumber = 5, + kMpIdFieldNumber = 2, + kMpbSizeFieldNumber = 1, + kMpSumFieldNumber = 3, + kMpTypeFieldNumber = 4, + }; + // string path = 5; + void clear_path(); + const std::string& path() const; + void set_path(const std::string& value); + void set_path(std::string&& value); + void set_path(const char* value); + void set_path(const char* value, size_t size); + std::string* mutable_path(); + std::string* release_path(); + void set_allocated_path(std::string* path); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_path(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_path( + std::string* path); + private: + const std::string& _internal_path() const; + void _internal_set_path(const std::string& value); + std::string* _internal_mutable_path(); + public: + + // uint64 mp_id = 2; + void clear_mp_id(); + ::PROTOBUF_NAMESPACE_ID::uint64 mp_id() const; + void set_mp_id(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_mp_id() const; + void _internal_set_mp_id(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // uint32 mpb_size = 1; + void clear_mpb_size(); + ::PROTOBUF_NAMESPACE_ID::uint32 mpb_size() const; + void set_mpb_size(::PROTOBUF_NAMESPACE_ID::uint32 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint32 _internal_mpb_size() const; + void _internal_set_mpb_size(::PROTOBUF_NAMESPACE_ID::uint32 value); + public: + + // uint32 mp_sum = 3; + void clear_mp_sum(); + ::PROTOBUF_NAMESPACE_ID::uint32 mp_sum() const; + void set_mp_sum(::PROTOBUF_NAMESPACE_ID::uint32 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint32 _internal_mp_sum() const; + void _internal_set_mp_sum(::PROTOBUF_NAMESPACE_ID::uint32 value); + public: + + // .mp.MP_TYPE mp_type = 4; + void clear_mp_type(); + ::mp::MP_TYPE mp_type() const; + void set_mp_type(::mp::MP_TYPE value); + private: + ::mp::MP_TYPE _internal_mp_type() const; + void _internal_set_mp_type(::mp::MP_TYPE value); + public: + + // @@protoc_insertion_point(class_scope:mp.mph) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr path_; + ::PROTOBUF_NAMESPACE_ID::uint64 mp_id_; + ::PROTOBUF_NAMESPACE_ID::uint32 mpb_size_; + ::PROTOBUF_NAMESPACE_ID::uint32 mp_sum_; + int mp_type_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2emph_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// mph + +// uint32 mpb_size = 1; +inline void mph::clear_mpb_size() { + mpb_size_ = 0u; +} +inline ::PROTOBUF_NAMESPACE_ID::uint32 mph::_internal_mpb_size() const { + return mpb_size_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint32 mph::mpb_size() const { + // @@protoc_insertion_point(field_get:mp.mph.mpb_size) + return _internal_mpb_size(); +} +inline void mph::_internal_set_mpb_size(::PROTOBUF_NAMESPACE_ID::uint32 value) { + + mpb_size_ = value; +} +inline void mph::set_mpb_size(::PROTOBUF_NAMESPACE_ID::uint32 value) { + _internal_set_mpb_size(value); + // @@protoc_insertion_point(field_set:mp.mph.mpb_size) +} + +// uint64 mp_id = 2; +inline void mph::clear_mp_id() { + mp_id_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 mph::_internal_mp_id() const { + return mp_id_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 mph::mp_id() const { + // @@protoc_insertion_point(field_get:mp.mph.mp_id) + return _internal_mp_id(); +} +inline void mph::_internal_set_mp_id(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + mp_id_ = value; +} +inline void mph::set_mp_id(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_mp_id(value); + // @@protoc_insertion_point(field_set:mp.mph.mp_id) +} + +// uint32 mp_sum = 3; +inline void mph::clear_mp_sum() { + mp_sum_ = 0u; +} +inline ::PROTOBUF_NAMESPACE_ID::uint32 mph::_internal_mp_sum() const { + return mp_sum_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint32 mph::mp_sum() const { + // @@protoc_insertion_point(field_get:mp.mph.mp_sum) + return _internal_mp_sum(); +} +inline void mph::_internal_set_mp_sum(::PROTOBUF_NAMESPACE_ID::uint32 value) { + + mp_sum_ = value; +} +inline void mph::set_mp_sum(::PROTOBUF_NAMESPACE_ID::uint32 value) { + _internal_set_mp_sum(value); + // @@protoc_insertion_point(field_set:mp.mph.mp_sum) +} + +// .mp.MP_TYPE mp_type = 4; +inline void mph::clear_mp_type() { + mp_type_ = 0; +} +inline ::mp::MP_TYPE mph::_internal_mp_type() const { + return static_cast< ::mp::MP_TYPE >(mp_type_); +} +inline ::mp::MP_TYPE mph::mp_type() const { + // @@protoc_insertion_point(field_get:mp.mph.mp_type) + return _internal_mp_type(); +} +inline void mph::_internal_set_mp_type(::mp::MP_TYPE value) { + + mp_type_ = value; +} +inline void mph::set_mp_type(::mp::MP_TYPE value) { + _internal_set_mp_type(value); + // @@protoc_insertion_point(field_set:mp.mph.mp_type) +} + +// string path = 5; +inline void mph::clear_path() { + path_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& mph::path() const { + // @@protoc_insertion_point(field_get:mp.mph.path) + return _internal_path(); +} +inline void mph::set_path(const std::string& value) { + _internal_set_path(value); + // @@protoc_insertion_point(field_set:mp.mph.path) +} +inline std::string* mph::mutable_path() { + // @@protoc_insertion_point(field_mutable:mp.mph.path) + return _internal_mutable_path(); +} +inline const std::string& mph::_internal_path() const { + return path_.Get(); +} +inline void mph::_internal_set_path(const std::string& value) { + + path_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void mph::set_path(std::string&& value) { + + path_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.mph.path) +} +inline void mph::set_path(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + path_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.mph.path) +} +inline void mph::set_path(const char* value, + size_t size) { + + path_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.mph.path) +} +inline std::string* mph::_internal_mutable_path() { + + return path_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* mph::release_path() { + // @@protoc_insertion_point(field_release:mp.mph.path) + return path_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void mph::set_allocated_path(std::string* path) { + if (path != nullptr) { + + } else { + + } + path_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), path, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.mph.path) +} +inline std::string* mph::unsafe_arena_release_path() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.mph.path) + GOOGLE_DCHECK(GetArena() != nullptr); + + return path_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void mph::unsafe_arena_set_allocated_path( + std::string* path) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (path != nullptr) { + + } else { + + } + path_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + path, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.mph.path) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2emph_2eproto diff --git a/api/MP/proto/mp.request.pb.cc b/api/MP/proto/mp.request.pb.cc new file mode 100644 index 0000000..439ef5c --- /dev/null +++ b/api/MP/proto/mp.request.pb.cc @@ -0,0 +1,371 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.request.proto + +#include "mp.request.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +extern PROTOBUF_INTERNAL_EXPORT_mp_2ebody_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_body_mp_2ebody_2eproto; +extern PROTOBUF_INTERNAL_EXPORT_mp_2ecqi_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_cqi_mp_2ecqi_2eproto; +namespace mp { +class requestDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _request_default_instance_; +} // namespace mp +static void InitDefaultsscc_info_request_mp_2erequest_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::_request_default_instance_; + new (ptr) ::mp::request(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::request::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_request_mp_2erequest_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, 0, InitDefaultsscc_info_request_mp_2erequest_2eproto}, { + &scc_info_body_mp_2ebody_2eproto.base, + &scc_info_cqi_mp_2ecqi_2eproto.base,}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2erequest_2eproto[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_mp_2erequest_2eproto = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2erequest_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2erequest_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::request, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::request, body_), + PROTOBUF_FIELD_OFFSET(::mp::request, cqi_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::request)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::_request_default_instance_), +}; + +const char descriptor_table_protodef_mp_2erequest_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\020mp.request.proto\022\002mp\032\rmp.body.proto\032\014m" + "p.cqi.proto\"7\n\007request\022\026\n\004body\030\001 \001(\0132\010.m" + "p.body\022\024\n\003cqi\030\002 \001(\0132\007.mp.cqib\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2erequest_2eproto_deps[2] = { + &::descriptor_table_mp_2ebody_2eproto, + &::descriptor_table_mp_2ecqi_2eproto, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2erequest_2eproto_sccs[1] = { + &scc_info_request_mp_2erequest_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2erequest_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2erequest_2eproto = { + false, false, descriptor_table_protodef_mp_2erequest_2eproto, "mp.request.proto", 116, + &descriptor_table_mp_2erequest_2eproto_once, descriptor_table_mp_2erequest_2eproto_sccs, descriptor_table_mp_2erequest_2eproto_deps, 1, 2, + schemas, file_default_instances, TableStruct_mp_2erequest_2eproto::offsets, + file_level_metadata_mp_2erequest_2eproto, 1, file_level_enum_descriptors_mp_2erequest_2eproto, file_level_service_descriptors_mp_2erequest_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2erequest_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2erequest_2eproto)), true); +namespace mp { + +// =================================================================== + +void request::InitAsDefaultInstance() { + ::mp::_request_default_instance_._instance.get_mutable()->body_ = const_cast< ::mp::body*>( + ::mp::body::internal_default_instance()); + ::mp::_request_default_instance_._instance.get_mutable()->cqi_ = const_cast< ::mp::cqi*>( + ::mp::cqi::internal_default_instance()); +} +class request::_Internal { + public: + static const ::mp::body& body(const request* msg); + static const ::mp::cqi& cqi(const request* msg); +}; + +const ::mp::body& +request::_Internal::body(const request* msg) { + return *msg->body_; +} +const ::mp::cqi& +request::_Internal::cqi(const request* msg) { + return *msg->cqi_; +} +void request::clear_body() { + if (GetArena() == nullptr && body_ != nullptr) { + delete body_; + } + body_ = nullptr; +} +void request::clear_cqi() { + if (GetArena() == nullptr && cqi_ != nullptr) { + delete cqi_; + } + cqi_ = nullptr; +} +request::request(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.request) +} +request::request(const request& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + if (from._internal_has_body()) { + body_ = new ::mp::body(*from.body_); + } else { + body_ = nullptr; + } + if (from._internal_has_cqi()) { + cqi_ = new ::mp::cqi(*from.cqi_); + } else { + cqi_ = nullptr; + } + // @@protoc_insertion_point(copy_constructor:mp.request) +} + +void request::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_request_mp_2erequest_2eproto.base); + ::memset(&body_, 0, static_cast( + reinterpret_cast(&cqi_) - + reinterpret_cast(&body_)) + sizeof(cqi_)); +} + +request::~request() { + // @@protoc_insertion_point(destructor:mp.request) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void request::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + if (this != internal_default_instance()) delete body_; + if (this != internal_default_instance()) delete cqi_; +} + +void request::ArenaDtor(void* object) { + request* _this = reinterpret_cast< request* >(object); + (void)_this; +} +void request::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void request::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const request& request::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_request_mp_2erequest_2eproto.base); + return *internal_default_instance(); +} + + +void request::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.request) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + if (GetArena() == nullptr && body_ != nullptr) { + delete body_; + } + body_ = nullptr; + if (GetArena() == nullptr && cqi_ != nullptr) { + delete cqi_; + } + cqi_ = nullptr; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* request::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // .mp.body body = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { + ptr = ctx->ParseMessage(_internal_mutable_body(), ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // .mp.cqi cqi = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { + ptr = ctx->ParseMessage(_internal_mutable_cqi(), ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* request::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.request) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // .mp.body body = 1; + if (this->has_body()) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 1, _Internal::body(this), target, stream); + } + + // .mp.cqi cqi = 2; + if (this->has_cqi()) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 2, _Internal::cqi(this), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.request) + return target; +} + +size_t request::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.request) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // .mp.body body = 1; + if (this->has_body()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *body_); + } + + // .mp.cqi cqi = 2; + if (this->has_cqi()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *cqi_); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void request::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.request) + GOOGLE_DCHECK_NE(&from, this); + const request* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.request) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.request) + MergeFrom(*source); + } +} + +void request::MergeFrom(const request& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.request) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.has_body()) { + _internal_mutable_body()->::mp::body::MergeFrom(from._internal_body()); + } + if (from.has_cqi()) { + _internal_mutable_cqi()->::mp::cqi::MergeFrom(from._internal_cqi()); + } +} + +void request::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.request) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void request::CopyFrom(const request& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.request) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool request::IsInitialized() const { + return true; +} + +void request::InternalSwap(request* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(request, cqi_) + + sizeof(request::cqi_) + - PROTOBUF_FIELD_OFFSET(request, body_)>( + reinterpret_cast(&body_), + reinterpret_cast(&other->body_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata request::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::request* Arena::CreateMaybeMessage< ::mp::request >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::request >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.request.pb.h b/api/MP/proto/mp.request.pb.h new file mode 100644 index 0000000..6ac1fc2 --- /dev/null +++ b/api/MP/proto/mp.request.pb.h @@ -0,0 +1,407 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.request.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2erequest_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2erequest_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include "mp.body.pb.h" +#include "mp.cqi.pb.h" +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2erequest_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2erequest_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2erequest_2eproto; +namespace mp { +class request; +class requestDefaultTypeInternal; +extern requestDefaultTypeInternal _request_default_instance_; +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::request* Arena::CreateMaybeMessage<::mp::request>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +// =================================================================== + +class request PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.request) */ { + public: + inline request() : request(nullptr) {}; + virtual ~request(); + + request(const request& from); + request(request&& from) noexcept + : request() { + *this = ::std::move(from); + } + + inline request& operator=(const request& from) { + CopyFrom(from); + return *this; + } + inline request& operator=(request&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const request& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const request* internal_default_instance() { + return reinterpret_cast( + &_request_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(request& a, request& b) { + a.Swap(&b); + } + inline void Swap(request* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(request* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline request* New() const final { + return CreateMaybeMessage(nullptr); + } + + request* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const request& from); + void MergeFrom(const request& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(request* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.request"; + } + protected: + explicit request(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2erequest_2eproto); + return ::descriptor_table_mp_2erequest_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kBodyFieldNumber = 1, + kCqiFieldNumber = 2, + }; + // .mp.body body = 1; + bool has_body() const; + private: + bool _internal_has_body() const; + public: + void clear_body(); + const ::mp::body& body() const; + ::mp::body* release_body(); + ::mp::body* mutable_body(); + void set_allocated_body(::mp::body* body); + private: + const ::mp::body& _internal_body() const; + ::mp::body* _internal_mutable_body(); + public: + void unsafe_arena_set_allocated_body( + ::mp::body* body); + ::mp::body* unsafe_arena_release_body(); + + // .mp.cqi cqi = 2; + bool has_cqi() const; + private: + bool _internal_has_cqi() const; + public: + void clear_cqi(); + const ::mp::cqi& cqi() const; + ::mp::cqi* release_cqi(); + ::mp::cqi* mutable_cqi(); + void set_allocated_cqi(::mp::cqi* cqi); + private: + const ::mp::cqi& _internal_cqi() const; + ::mp::cqi* _internal_mutable_cqi(); + public: + void unsafe_arena_set_allocated_cqi( + ::mp::cqi* cqi); + ::mp::cqi* unsafe_arena_release_cqi(); + + // @@protoc_insertion_point(class_scope:mp.request) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::mp::body* body_; + ::mp::cqi* cqi_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2erequest_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// request + +// .mp.body body = 1; +inline bool request::_internal_has_body() const { + return this != internal_default_instance() && body_ != nullptr; +} +inline bool request::has_body() const { + return _internal_has_body(); +} +inline const ::mp::body& request::_internal_body() const { + const ::mp::body* p = body_; + return p != nullptr ? *p : *reinterpret_cast( + &::mp::_body_default_instance_); +} +inline const ::mp::body& request::body() const { + // @@protoc_insertion_point(field_get:mp.request.body) + return _internal_body(); +} +inline void request::unsafe_arena_set_allocated_body( + ::mp::body* body) { + if (GetArena() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(body_); + } + body_ = body; + if (body) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.request.body) +} +inline ::mp::body* request::release_body() { + auto temp = unsafe_arena_release_body(); + if (GetArena() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + return temp; +} +inline ::mp::body* request::unsafe_arena_release_body() { + // @@protoc_insertion_point(field_release:mp.request.body) + + ::mp::body* temp = body_; + body_ = nullptr; + return temp; +} +inline ::mp::body* request::_internal_mutable_body() { + + if (body_ == nullptr) { + auto* p = CreateMaybeMessage<::mp::body>(GetArena()); + body_ = p; + } + return body_; +} +inline ::mp::body* request::mutable_body() { + // @@protoc_insertion_point(field_mutable:mp.request.body) + return _internal_mutable_body(); +} +inline void request::set_allocated_body(::mp::body* body) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena(); + if (message_arena == nullptr) { + delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(body_); + } + if (body) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(body)->GetArena(); + if (message_arena != submessage_arena) { + body = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, body, submessage_arena); + } + + } else { + + } + body_ = body; + // @@protoc_insertion_point(field_set_allocated:mp.request.body) +} + +// .mp.cqi cqi = 2; +inline bool request::_internal_has_cqi() const { + return this != internal_default_instance() && cqi_ != nullptr; +} +inline bool request::has_cqi() const { + return _internal_has_cqi(); +} +inline const ::mp::cqi& request::_internal_cqi() const { + const ::mp::cqi* p = cqi_; + return p != nullptr ? *p : *reinterpret_cast( + &::mp::_cqi_default_instance_); +} +inline const ::mp::cqi& request::cqi() const { + // @@protoc_insertion_point(field_get:mp.request.cqi) + return _internal_cqi(); +} +inline void request::unsafe_arena_set_allocated_cqi( + ::mp::cqi* cqi) { + if (GetArena() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(cqi_); + } + cqi_ = cqi; + if (cqi) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.request.cqi) +} +inline ::mp::cqi* request::release_cqi() { + auto temp = unsafe_arena_release_cqi(); + if (GetArena() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + return temp; +} +inline ::mp::cqi* request::unsafe_arena_release_cqi() { + // @@protoc_insertion_point(field_release:mp.request.cqi) + + ::mp::cqi* temp = cqi_; + cqi_ = nullptr; + return temp; +} +inline ::mp::cqi* request::_internal_mutable_cqi() { + + if (cqi_ == nullptr) { + auto* p = CreateMaybeMessage<::mp::cqi>(GetArena()); + cqi_ = p; + } + return cqi_; +} +inline ::mp::cqi* request::mutable_cqi() { + // @@protoc_insertion_point(field_mutable:mp.request.cqi) + return _internal_mutable_cqi(); +} +inline void request::set_allocated_cqi(::mp::cqi* cqi) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena(); + if (message_arena == nullptr) { + delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(cqi_); + } + if (cqi) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(cqi)->GetArena(); + if (message_arena != submessage_arena) { + cqi = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, cqi, submessage_arena); + } + + } else { + + } + cqi_ = cqi; + // @@protoc_insertion_point(field_set_allocated:mp.request.cqi) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2erequest_2eproto diff --git a/api/MP/proto/mp.response.pb.cc b/api/MP/proto/mp.response.pb.cc new file mode 100644 index 0000000..9ee16ef --- /dev/null +++ b/api/MP/proto/mp.response.pb.cc @@ -0,0 +1,311 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.response.proto + +#include "mp.response.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +extern PROTOBUF_INTERNAL_EXPORT_mp_2esri_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_sri_mp_2esri_2eproto; +namespace mp { +class responseDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _response_default_instance_; +} // namespace mp +static void InitDefaultsscc_info_response_mp_2eresponse_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::_response_default_instance_; + new (ptr) ::mp::response(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::response::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_response_mp_2eresponse_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, 0, InitDefaultsscc_info_response_mp_2eresponse_2eproto}, { + &scc_info_sri_mp_2esri_2eproto.base,}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2eresponse_2eproto[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_mp_2eresponse_2eproto = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2eresponse_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2eresponse_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::response, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::response, sri_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::response)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::_response_default_instance_), +}; + +const char descriptor_table_protodef_mp_2eresponse_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\021mp.response.proto\022\002mp\032\014mp.sri.proto\" \n" + "\010response\022\024\n\003sri\030\001 \001(\0132\007.mp.srib\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2eresponse_2eproto_deps[1] = { + &::descriptor_table_mp_2esri_2eproto, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2eresponse_2eproto_sccs[1] = { + &scc_info_response_mp_2eresponse_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2eresponse_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2eresponse_2eproto = { + false, false, descriptor_table_protodef_mp_2eresponse_2eproto, "mp.response.proto", 79, + &descriptor_table_mp_2eresponse_2eproto_once, descriptor_table_mp_2eresponse_2eproto_sccs, descriptor_table_mp_2eresponse_2eproto_deps, 1, 1, + schemas, file_default_instances, TableStruct_mp_2eresponse_2eproto::offsets, + file_level_metadata_mp_2eresponse_2eproto, 1, file_level_enum_descriptors_mp_2eresponse_2eproto, file_level_service_descriptors_mp_2eresponse_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2eresponse_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2eresponse_2eproto)), true); +namespace mp { + +// =================================================================== + +void response::InitAsDefaultInstance() { + ::mp::_response_default_instance_._instance.get_mutable()->sri_ = const_cast< ::mp::sri*>( + ::mp::sri::internal_default_instance()); +} +class response::_Internal { + public: + static const ::mp::sri& sri(const response* msg); +}; + +const ::mp::sri& +response::_Internal::sri(const response* msg) { + return *msg->sri_; +} +void response::clear_sri() { + if (GetArena() == nullptr && sri_ != nullptr) { + delete sri_; + } + sri_ = nullptr; +} +response::response(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.response) +} +response::response(const response& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + if (from._internal_has_sri()) { + sri_ = new ::mp::sri(*from.sri_); + } else { + sri_ = nullptr; + } + // @@protoc_insertion_point(copy_constructor:mp.response) +} + +void response::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_response_mp_2eresponse_2eproto.base); + sri_ = nullptr; +} + +response::~response() { + // @@protoc_insertion_point(destructor:mp.response) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void response::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + if (this != internal_default_instance()) delete sri_; +} + +void response::ArenaDtor(void* object) { + response* _this = reinterpret_cast< response* >(object); + (void)_this; +} +void response::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void response::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const response& response::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_response_mp_2eresponse_2eproto.base); + return *internal_default_instance(); +} + + +void response::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.response) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + if (GetArena() == nullptr && sri_ != nullptr) { + delete sri_; + } + sri_ = nullptr; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* response::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // .mp.sri sri = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { + ptr = ctx->ParseMessage(_internal_mutable_sri(), ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* response::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.response) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // .mp.sri sri = 1; + if (this->has_sri()) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 1, _Internal::sri(this), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.response) + return target; +} + +size_t response::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.response) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // .mp.sri sri = 1; + if (this->has_sri()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *sri_); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void response::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.response) + GOOGLE_DCHECK_NE(&from, this); + const response* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.response) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.response) + MergeFrom(*source); + } +} + +void response::MergeFrom(const response& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.response) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.has_sri()) { + _internal_mutable_sri()->::mp::sri::MergeFrom(from._internal_sri()); + } +} + +void response::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.response) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void response::CopyFrom(const response& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.response) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool response::IsInitialized() const { + return true; +} + +void response::InternalSwap(response* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + swap(sri_, other->sri_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata response::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::response* Arena::CreateMaybeMessage< ::mp::response >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::response >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.response.pb.h b/api/MP/proto/mp.response.pb.h new file mode 100644 index 0000000..1779880 --- /dev/null +++ b/api/MP/proto/mp.response.pb.h @@ -0,0 +1,311 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.response.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2eresponse_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2eresponse_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include "mp.sri.pb.h" +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2eresponse_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2eresponse_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2eresponse_2eproto; +namespace mp { +class response; +class responseDefaultTypeInternal; +extern responseDefaultTypeInternal _response_default_instance_; +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::response* Arena::CreateMaybeMessage<::mp::response>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +// =================================================================== + +class response PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.response) */ { + public: + inline response() : response(nullptr) {}; + virtual ~response(); + + response(const response& from); + response(response&& from) noexcept + : response() { + *this = ::std::move(from); + } + + inline response& operator=(const response& from) { + CopyFrom(from); + return *this; + } + inline response& operator=(response&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const response& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const response* internal_default_instance() { + return reinterpret_cast( + &_response_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(response& a, response& b) { + a.Swap(&b); + } + inline void Swap(response* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(response* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline response* New() const final { + return CreateMaybeMessage(nullptr); + } + + response* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const response& from); + void MergeFrom(const response& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(response* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.response"; + } + protected: + explicit response(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2eresponse_2eproto); + return ::descriptor_table_mp_2eresponse_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kSriFieldNumber = 1, + }; + // .mp.sri sri = 1; + bool has_sri() const; + private: + bool _internal_has_sri() const; + public: + void clear_sri(); + const ::mp::sri& sri() const; + ::mp::sri* release_sri(); + ::mp::sri* mutable_sri(); + void set_allocated_sri(::mp::sri* sri); + private: + const ::mp::sri& _internal_sri() const; + ::mp::sri* _internal_mutable_sri(); + public: + void unsafe_arena_set_allocated_sri( + ::mp::sri* sri); + ::mp::sri* unsafe_arena_release_sri(); + + // @@protoc_insertion_point(class_scope:mp.response) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::mp::sri* sri_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2eresponse_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// response + +// .mp.sri sri = 1; +inline bool response::_internal_has_sri() const { + return this != internal_default_instance() && sri_ != nullptr; +} +inline bool response::has_sri() const { + return _internal_has_sri(); +} +inline const ::mp::sri& response::_internal_sri() const { + const ::mp::sri* p = sri_; + return p != nullptr ? *p : *reinterpret_cast( + &::mp::_sri_default_instance_); +} +inline const ::mp::sri& response::sri() const { + // @@protoc_insertion_point(field_get:mp.response.sri) + return _internal_sri(); +} +inline void response::unsafe_arena_set_allocated_sri( + ::mp::sri* sri) { + if (GetArena() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(sri_); + } + sri_ = sri; + if (sri) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.response.sri) +} +inline ::mp::sri* response::release_sri() { + auto temp = unsafe_arena_release_sri(); + if (GetArena() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + return temp; +} +inline ::mp::sri* response::unsafe_arena_release_sri() { + // @@protoc_insertion_point(field_release:mp.response.sri) + + ::mp::sri* temp = sri_; + sri_ = nullptr; + return temp; +} +inline ::mp::sri* response::_internal_mutable_sri() { + + if (sri_ == nullptr) { + auto* p = CreateMaybeMessage<::mp::sri>(GetArena()); + sri_ = p; + } + return sri_; +} +inline ::mp::sri* response::mutable_sri() { + // @@protoc_insertion_point(field_mutable:mp.response.sri) + return _internal_mutable_sri(); +} +inline void response::set_allocated_sri(::mp::sri* sri) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena(); + if (message_arena == nullptr) { + delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(sri_); + } + if (sri) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(sri)->GetArena(); + if (message_arena != submessage_arena) { + sri = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, sri, submessage_arena); + } + + } else { + + } + sri_ = sri; + // @@protoc_insertion_point(field_set_allocated:mp.response.sri) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2eresponse_2eproto diff --git a/api/MP/proto/mp.sri.pb.cc b/api/MP/proto/mp.sri.pb.cc new file mode 100644 index 0000000..8f30aeb --- /dev/null +++ b/api/MP/proto/mp.sri.pb.cc @@ -0,0 +1,544 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.sri.proto + +#include "mp.sri.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +namespace mp { +class sriDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _sri_default_instance_; +} // namespace mp +static void InitDefaultsscc_info_sri_mp_2esri_2eproto() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::mp::_sri_default_instance_; + new (ptr) ::mp::sri(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::mp::sri::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_sri_mp_2esri_2eproto = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_sri_mp_2esri_2eproto}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2esri_2eproto[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_mp_2esri_2eproto = nullptr; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2esri_2eproto = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2esri_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mp::sri, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::sri, subcommand_), + PROTOBUF_FIELD_OFFSET(::mp::sri, username_), + PROTOBUF_FIELD_OFFSET(::mp::sri, account_), + PROTOBUF_FIELD_OFFSET(::mp::sri, email_), + PROTOBUF_FIELD_OFFSET(::mp::sri, phone_), + PROTOBUF_FIELD_OFFSET(::mp::sri, msg_), + PROTOBUF_FIELD_OFFSET(::mp::sri, token_), + PROTOBUF_FIELD_OFFSET(::mp::sri, data_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::mp::sri)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::mp::_sri_default_instance_), +}; + +const char descriptor_table_protodef_mp_2esri_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\014mp.sri.proto\022\002mp\032\013mp.mp.proto\"\225\001\n\003sri\022" + "#\n\nsubcommand\030\001 \001(\0162\017.mp.MP_SUB_TYPE\022\020\n\010" + "username\030\002 \001(\t\022\017\n\007account\030\003 \001(\004\022\r\n\005email" + "\030\004 \001(\t\022\r\n\005phone\030\005 \001(\004\022\013\n\003msg\030\006 \001(\t\022\r\n\005to" + "ken\030\007 \001(\t\022\014\n\004data\030\010 \001(\tb\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2esri_2eproto_deps[1] = { + &::descriptor_table_mp_2emp_2eproto, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2esri_2eproto_sccs[1] = { + &scc_info_sri_mp_2esri_2eproto.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2esri_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2esri_2eproto = { + false, false, descriptor_table_protodef_mp_2esri_2eproto, "mp.sri.proto", 191, + &descriptor_table_mp_2esri_2eproto_once, descriptor_table_mp_2esri_2eproto_sccs, descriptor_table_mp_2esri_2eproto_deps, 1, 1, + schemas, file_default_instances, TableStruct_mp_2esri_2eproto::offsets, + file_level_metadata_mp_2esri_2eproto, 1, file_level_enum_descriptors_mp_2esri_2eproto, file_level_service_descriptors_mp_2esri_2eproto, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_mp_2esri_2eproto = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_mp_2esri_2eproto)), true); +namespace mp { + +// =================================================================== + +void sri::InitAsDefaultInstance() { +} +class sri::_Internal { + public: +}; + +sri::sri(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:mp.sri) +} +sri::sri(const sri& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + username_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_username().empty()) { + username_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_username(), + GetArena()); + } + email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_email().empty()) { + email_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_email(), + GetArena()); + } + msg_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_msg().empty()) { + msg_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_msg(), + GetArena()); + } + token_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_token().empty()) { + token_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_token(), + GetArena()); + } + data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_data().empty()) { + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_data(), + GetArena()); + } + ::memcpy(&account_, &from.account_, + static_cast(reinterpret_cast(&subcommand_) - + reinterpret_cast(&account_)) + sizeof(subcommand_)); + // @@protoc_insertion_point(copy_constructor:mp.sri) +} + +void sri::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_sri_mp_2esri_2eproto.base); + username_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + msg_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + token_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + ::memset(&account_, 0, static_cast( + reinterpret_cast(&subcommand_) - + reinterpret_cast(&account_)) + sizeof(subcommand_)); +} + +sri::~sri() { + // @@protoc_insertion_point(destructor:mp.sri) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void sri::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + username_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + email_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + msg_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + token_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void sri::ArenaDtor(void* object) { + sri* _this = reinterpret_cast< sri* >(object); + (void)_this; +} +void sri::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void sri::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const sri& sri::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_sri_mp_2esri_2eproto.base); + return *internal_default_instance(); +} + + +void sri::Clear() { +// @@protoc_insertion_point(message_clear_start:mp.sri) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + username_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + email_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + msg_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + token_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::memset(&account_, 0, static_cast( + reinterpret_cast(&subcommand_) - + reinterpret_cast(&account_)) + sizeof(subcommand_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* sri::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // .mp.MP_SUB_TYPE subcommand = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_subcommand(static_cast<::mp::MP_SUB_TYPE>(val)); + } else goto handle_unusual; + continue; + // string username = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { + auto str = _internal_mutable_username(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.sri.username")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint64 account = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { + account_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string email = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { + auto str = _internal_mutable_email(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.sri.email")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // uint64 phone = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { + phone_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string msg = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { + auto str = _internal_mutable_msg(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.sri.msg")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string token = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 58)) { + auto str = _internal_mutable_token(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.sri.token")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string data = 8; + case 8: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 66)) { + auto str = _internal_mutable_data(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.sri.data")); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* sri::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:mp.sri) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // .mp.MP_SUB_TYPE subcommand = 1; + if (this->subcommand() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_subcommand(), target); + } + + // string username = 2; + if (this->username().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_username().data(), static_cast(this->_internal_username().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.sri.username"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_username(), target); + } + + // uint64 account = 3; + if (this->account() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(3, this->_internal_account(), target); + } + + // string email = 4; + if (this->email().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_email().data(), static_cast(this->_internal_email().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.sri.email"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_email(), target); + } + + // uint64 phone = 5; + if (this->phone() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(5, this->_internal_phone(), target); + } + + // string msg = 6; + if (this->msg().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_msg().data(), static_cast(this->_internal_msg().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.sri.msg"); + target = stream->WriteStringMaybeAliased( + 6, this->_internal_msg(), target); + } + + // string token = 7; + if (this->token().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_token().data(), static_cast(this->_internal_token().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.sri.token"); + target = stream->WriteStringMaybeAliased( + 7, this->_internal_token(), target); + } + + // string data = 8; + if (this->data().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_data().data(), static_cast(this->_internal_data().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "mp.sri.data"); + target = stream->WriteStringMaybeAliased( + 8, this->_internal_data(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mp.sri) + return target; +} + +size_t sri::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:mp.sri) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string username = 2; + if (this->username().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_username()); + } + + // string email = 4; + if (this->email().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_email()); + } + + // string msg = 6; + if (this->msg().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_msg()); + } + + // string token = 7; + if (this->token().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_token()); + } + + // string data = 8; + if (this->data().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_data()); + } + + // uint64 account = 3; + if (this->account() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_account()); + } + + // uint64 phone = 5; + if (this->phone() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_phone()); + } + + // .mp.MP_SUB_TYPE subcommand = 1; + if (this->subcommand() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_subcommand()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void sri::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:mp.sri) + GOOGLE_DCHECK_NE(&from, this); + const sri* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:mp.sri) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:mp.sri) + MergeFrom(*source); + } +} + +void sri::MergeFrom(const sri& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:mp.sri) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.username().size() > 0) { + _internal_set_username(from._internal_username()); + } + if (from.email().size() > 0) { + _internal_set_email(from._internal_email()); + } + if (from.msg().size() > 0) { + _internal_set_msg(from._internal_msg()); + } + if (from.token().size() > 0) { + _internal_set_token(from._internal_token()); + } + if (from.data().size() > 0) { + _internal_set_data(from._internal_data()); + } + if (from.account() != 0) { + _internal_set_account(from._internal_account()); + } + if (from.phone() != 0) { + _internal_set_phone(from._internal_phone()); + } + if (from.subcommand() != 0) { + _internal_set_subcommand(from._internal_subcommand()); + } +} + +void sri::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:mp.sri) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void sri::CopyFrom(const sri& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mp.sri) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool sri::IsInitialized() const { + return true; +} + +void sri::InternalSwap(sri* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + username_.Swap(&other->username_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + email_.Swap(&other->email_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + msg_.Swap(&other->msg_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + token_.Swap(&other->token_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + data_.Swap(&other->data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(sri, subcommand_) + + sizeof(sri::subcommand_) + - PROTOBUF_FIELD_OFFSET(sri, account_)>( + reinterpret_cast(&account_), + reinterpret_cast(&other->account_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata sri::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::mp::sri* Arena::CreateMaybeMessage< ::mp::sri >(Arena* arena) { + return Arena::CreateMessageInternal< ::mp::sri >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/api/MP/proto/mp.sri.pb.h b/api/MP/proto/mp.sri.pb.h new file mode 100644 index 0000000..bcbfab3 --- /dev/null +++ b/api/MP/proto/mp.sri.pb.h @@ -0,0 +1,849 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: mp.sri.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_mp_2esri_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_mp_2esri_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include "mp.mp.pb.h" +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_mp_2esri_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_mp_2esri_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2esri_2eproto; +namespace mp { +class sri; +class sriDefaultTypeInternal; +extern sriDefaultTypeInternal _sri_default_instance_; +} // namespace mp +PROTOBUF_NAMESPACE_OPEN +template<> ::mp::sri* Arena::CreateMaybeMessage<::mp::sri>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace mp { + +// =================================================================== + +class sri PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:mp.sri) */ { + public: + inline sri() : sri(nullptr) {}; + virtual ~sri(); + + sri(const sri& from); + sri(sri&& from) noexcept + : sri() { + *this = ::std::move(from); + } + + inline sri& operator=(const sri& from) { + CopyFrom(from); + return *this; + } + inline sri& operator=(sri&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const sri& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const sri* internal_default_instance() { + return reinterpret_cast( + &_sri_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(sri& a, sri& b) { + a.Swap(&b); + } + inline void Swap(sri* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(sri* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline sri* New() const final { + return CreateMaybeMessage(nullptr); + } + + sri* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const sri& from); + void MergeFrom(const sri& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(sri* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "mp.sri"; + } + protected: + explicit sri(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_mp_2esri_2eproto); + return ::descriptor_table_mp_2esri_2eproto.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kUsernameFieldNumber = 2, + kEmailFieldNumber = 4, + kMsgFieldNumber = 6, + kTokenFieldNumber = 7, + kDataFieldNumber = 8, + kAccountFieldNumber = 3, + kPhoneFieldNumber = 5, + kSubcommandFieldNumber = 1, + }; + // string username = 2; + void clear_username(); + const std::string& username() const; + void set_username(const std::string& value); + void set_username(std::string&& value); + void set_username(const char* value); + void set_username(const char* value, size_t size); + std::string* mutable_username(); + std::string* release_username(); + void set_allocated_username(std::string* username); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_username(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_username( + std::string* username); + private: + const std::string& _internal_username() const; + void _internal_set_username(const std::string& value); + std::string* _internal_mutable_username(); + public: + + // string email = 4; + void clear_email(); + const std::string& email() const; + void set_email(const std::string& value); + void set_email(std::string&& value); + void set_email(const char* value); + void set_email(const char* value, size_t size); + std::string* mutable_email(); + std::string* release_email(); + void set_allocated_email(std::string* email); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_email(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_email( + std::string* email); + private: + const std::string& _internal_email() const; + void _internal_set_email(const std::string& value); + std::string* _internal_mutable_email(); + public: + + // string msg = 6; + void clear_msg(); + const std::string& msg() const; + void set_msg(const std::string& value); + void set_msg(std::string&& value); + void set_msg(const char* value); + void set_msg(const char* value, size_t size); + std::string* mutable_msg(); + std::string* release_msg(); + void set_allocated_msg(std::string* msg); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_msg(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_msg( + std::string* msg); + private: + const std::string& _internal_msg() const; + void _internal_set_msg(const std::string& value); + std::string* _internal_mutable_msg(); + public: + + // string token = 7; + void clear_token(); + const std::string& token() const; + void set_token(const std::string& value); + void set_token(std::string&& value); + void set_token(const char* value); + void set_token(const char* value, size_t size); + std::string* mutable_token(); + std::string* release_token(); + void set_allocated_token(std::string* token); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_token(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_token( + std::string* token); + private: + const std::string& _internal_token() const; + void _internal_set_token(const std::string& value); + std::string* _internal_mutable_token(); + public: + + // string data = 8; + void clear_data(); + const std::string& data() const; + void set_data(const std::string& value); + void set_data(std::string&& value); + void set_data(const char* value); + void set_data(const char* value, size_t size); + std::string* mutable_data(); + std::string* release_data(); + void set_allocated_data(std::string* data); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_data(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_data( + std::string* data); + private: + const std::string& _internal_data() const; + void _internal_set_data(const std::string& value); + std::string* _internal_mutable_data(); + public: + + // uint64 account = 3; + void clear_account(); + ::PROTOBUF_NAMESPACE_ID::uint64 account() const; + void set_account(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_account() const; + void _internal_set_account(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // uint64 phone = 5; + void clear_phone(); + ::PROTOBUF_NAMESPACE_ID::uint64 phone() const; + void set_phone(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_phone() const; + void _internal_set_phone(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // .mp.MP_SUB_TYPE subcommand = 1; + void clear_subcommand(); + ::mp::MP_SUB_TYPE subcommand() const; + void set_subcommand(::mp::MP_SUB_TYPE value); + private: + ::mp::MP_SUB_TYPE _internal_subcommand() const; + void _internal_set_subcommand(::mp::MP_SUB_TYPE value); + public: + + // @@protoc_insertion_point(class_scope:mp.sri) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr username_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr email_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr msg_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr token_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr data_; + ::PROTOBUF_NAMESPACE_ID::uint64 account_; + ::PROTOBUF_NAMESPACE_ID::uint64 phone_; + int subcommand_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_mp_2esri_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// sri + +// .mp.MP_SUB_TYPE subcommand = 1; +inline void sri::clear_subcommand() { + subcommand_ = 0; +} +inline ::mp::MP_SUB_TYPE sri::_internal_subcommand() const { + return static_cast< ::mp::MP_SUB_TYPE >(subcommand_); +} +inline ::mp::MP_SUB_TYPE sri::subcommand() const { + // @@protoc_insertion_point(field_get:mp.sri.subcommand) + return _internal_subcommand(); +} +inline void sri::_internal_set_subcommand(::mp::MP_SUB_TYPE value) { + + subcommand_ = value; +} +inline void sri::set_subcommand(::mp::MP_SUB_TYPE value) { + _internal_set_subcommand(value); + // @@protoc_insertion_point(field_set:mp.sri.subcommand) +} + +// string username = 2; +inline void sri::clear_username() { + username_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& sri::username() const { + // @@protoc_insertion_point(field_get:mp.sri.username) + return _internal_username(); +} +inline void sri::set_username(const std::string& value) { + _internal_set_username(value); + // @@protoc_insertion_point(field_set:mp.sri.username) +} +inline std::string* sri::mutable_username() { + // @@protoc_insertion_point(field_mutable:mp.sri.username) + return _internal_mutable_username(); +} +inline const std::string& sri::_internal_username() const { + return username_.Get(); +} +inline void sri::_internal_set_username(const std::string& value) { + + username_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void sri::set_username(std::string&& value) { + + username_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.sri.username) +} +inline void sri::set_username(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + username_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.sri.username) +} +inline void sri::set_username(const char* value, + size_t size) { + + username_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.sri.username) +} +inline std::string* sri::_internal_mutable_username() { + + return username_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* sri::release_username() { + // @@protoc_insertion_point(field_release:mp.sri.username) + return username_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void sri::set_allocated_username(std::string* username) { + if (username != nullptr) { + + } else { + + } + username_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), username, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.sri.username) +} +inline std::string* sri::unsafe_arena_release_username() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.sri.username) + GOOGLE_DCHECK(GetArena() != nullptr); + + return username_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void sri::unsafe_arena_set_allocated_username( + std::string* username) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (username != nullptr) { + + } else { + + } + username_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + username, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.sri.username) +} + +// uint64 account = 3; +inline void sri::clear_account() { + account_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 sri::_internal_account() const { + return account_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 sri::account() const { + // @@protoc_insertion_point(field_get:mp.sri.account) + return _internal_account(); +} +inline void sri::_internal_set_account(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + account_ = value; +} +inline void sri::set_account(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_account(value); + // @@protoc_insertion_point(field_set:mp.sri.account) +} + +// string email = 4; +inline void sri::clear_email() { + email_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& sri::email() const { + // @@protoc_insertion_point(field_get:mp.sri.email) + return _internal_email(); +} +inline void sri::set_email(const std::string& value) { + _internal_set_email(value); + // @@protoc_insertion_point(field_set:mp.sri.email) +} +inline std::string* sri::mutable_email() { + // @@protoc_insertion_point(field_mutable:mp.sri.email) + return _internal_mutable_email(); +} +inline const std::string& sri::_internal_email() const { + return email_.Get(); +} +inline void sri::_internal_set_email(const std::string& value) { + + email_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void sri::set_email(std::string&& value) { + + email_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.sri.email) +} +inline void sri::set_email(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + email_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.sri.email) +} +inline void sri::set_email(const char* value, + size_t size) { + + email_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.sri.email) +} +inline std::string* sri::_internal_mutable_email() { + + return email_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* sri::release_email() { + // @@protoc_insertion_point(field_release:mp.sri.email) + return email_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void sri::set_allocated_email(std::string* email) { + if (email != nullptr) { + + } else { + + } + email_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), email, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.sri.email) +} +inline std::string* sri::unsafe_arena_release_email() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.sri.email) + GOOGLE_DCHECK(GetArena() != nullptr); + + return email_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void sri::unsafe_arena_set_allocated_email( + std::string* email) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (email != nullptr) { + + } else { + + } + email_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + email, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.sri.email) +} + +// uint64 phone = 5; +inline void sri::clear_phone() { + phone_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 sri::_internal_phone() const { + return phone_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 sri::phone() const { + // @@protoc_insertion_point(field_get:mp.sri.phone) + return _internal_phone(); +} +inline void sri::_internal_set_phone(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + phone_ = value; +} +inline void sri::set_phone(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_phone(value); + // @@protoc_insertion_point(field_set:mp.sri.phone) +} + +// string msg = 6; +inline void sri::clear_msg() { + msg_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& sri::msg() const { + // @@protoc_insertion_point(field_get:mp.sri.msg) + return _internal_msg(); +} +inline void sri::set_msg(const std::string& value) { + _internal_set_msg(value); + // @@protoc_insertion_point(field_set:mp.sri.msg) +} +inline std::string* sri::mutable_msg() { + // @@protoc_insertion_point(field_mutable:mp.sri.msg) + return _internal_mutable_msg(); +} +inline const std::string& sri::_internal_msg() const { + return msg_.Get(); +} +inline void sri::_internal_set_msg(const std::string& value) { + + msg_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void sri::set_msg(std::string&& value) { + + msg_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.sri.msg) +} +inline void sri::set_msg(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + msg_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.sri.msg) +} +inline void sri::set_msg(const char* value, + size_t size) { + + msg_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.sri.msg) +} +inline std::string* sri::_internal_mutable_msg() { + + return msg_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* sri::release_msg() { + // @@protoc_insertion_point(field_release:mp.sri.msg) + return msg_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void sri::set_allocated_msg(std::string* msg) { + if (msg != nullptr) { + + } else { + + } + msg_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), msg, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.sri.msg) +} +inline std::string* sri::unsafe_arena_release_msg() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.sri.msg) + GOOGLE_DCHECK(GetArena() != nullptr); + + return msg_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void sri::unsafe_arena_set_allocated_msg( + std::string* msg) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (msg != nullptr) { + + } else { + + } + msg_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + msg, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.sri.msg) +} + +// string token = 7; +inline void sri::clear_token() { + token_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& sri::token() const { + // @@protoc_insertion_point(field_get:mp.sri.token) + return _internal_token(); +} +inline void sri::set_token(const std::string& value) { + _internal_set_token(value); + // @@protoc_insertion_point(field_set:mp.sri.token) +} +inline std::string* sri::mutable_token() { + // @@protoc_insertion_point(field_mutable:mp.sri.token) + return _internal_mutable_token(); +} +inline const std::string& sri::_internal_token() const { + return token_.Get(); +} +inline void sri::_internal_set_token(const std::string& value) { + + token_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void sri::set_token(std::string&& value) { + + token_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.sri.token) +} +inline void sri::set_token(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + token_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.sri.token) +} +inline void sri::set_token(const char* value, + size_t size) { + + token_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.sri.token) +} +inline std::string* sri::_internal_mutable_token() { + + return token_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* sri::release_token() { + // @@protoc_insertion_point(field_release:mp.sri.token) + return token_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void sri::set_allocated_token(std::string* token) { + if (token != nullptr) { + + } else { + + } + token_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), token, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.sri.token) +} +inline std::string* sri::unsafe_arena_release_token() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.sri.token) + GOOGLE_DCHECK(GetArena() != nullptr); + + return token_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void sri::unsafe_arena_set_allocated_token( + std::string* token) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (token != nullptr) { + + } else { + + } + token_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + token, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.sri.token) +} + +// string data = 8; +inline void sri::clear_data() { + data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& sri::data() const { + // @@protoc_insertion_point(field_get:mp.sri.data) + return _internal_data(); +} +inline void sri::set_data(const std::string& value) { + _internal_set_data(value); + // @@protoc_insertion_point(field_set:mp.sri.data) +} +inline std::string* sri::mutable_data() { + // @@protoc_insertion_point(field_mutable:mp.sri.data) + return _internal_mutable_data(); +} +inline const std::string& sri::_internal_data() const { + return data_.Get(); +} +inline void sri::_internal_set_data(const std::string& value) { + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void sri::set_data(std::string&& value) { + + data_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:mp.sri.data) +} +inline void sri::set_data(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:mp.sri.data) +} +inline void sri::set_data(const char* value, + size_t size) { + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:mp.sri.data) +} +inline std::string* sri::_internal_mutable_data() { + + return data_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* sri::release_data() { + // @@protoc_insertion_point(field_release:mp.sri.data) + return data_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void sri::set_allocated_data(std::string* data) { + if (data != nullptr) { + + } else { + + } + data_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), data, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:mp.sri.data) +} +inline std::string* sri::unsafe_arena_release_data() { + // @@protoc_insertion_point(field_unsafe_arena_release:mp.sri.data) + GOOGLE_DCHECK(GetArena() != nullptr); + + return data_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void sri::unsafe_arena_set_allocated_data( + std::string* data) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (data != nullptr) { + + } else { + + } + data_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + data, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.sri.data) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + +} // namespace mp + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_mp_2esri_2eproto diff --git a/api/MP/protohuf/mp.body.proto b/api/MP/protohuf/mp.body.proto new file mode 100644 index 0000000..22d8d45 --- /dev/null +++ b/api/MP/protohuf/mp.body.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package mp; + +import "mp.mp.proto"; + +message body { + MP_SUB_TYPE subcommand = 1; + string account = 2; + string password = 3; + uint64 target = 4; + uint64 source = 5; + string data = 6; +} \ No newline at end of file diff --git a/api/MP/protohuf/mp.cqi.proto b/api/MP/protohuf/mp.cqi.proto new file mode 100644 index 0000000..6066597 --- /dev/null +++ b/api/MP/protohuf/mp.cqi.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package mp; + +enum MP_C_TYPE { + MC_TYPE_LINUX = 0; + MC_TYPE_WINDOWS = 1; +}; + +// 客户端请求消息 +message cqi { + MP_C_TYPE cqi_type = 1; + float cqi_version = 2; +} \ No newline at end of file diff --git a/api/MP/protohuf/mp.im.proto b/api/MP/protohuf/mp.im.proto new file mode 100644 index 0000000..9bf04c3 --- /dev/null +++ b/api/MP/protohuf/mp.im.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package im; + +import "mp.mp.proto"; + +/// 两个包 使用message_id 保证消息的同步 ***************************************************************//// + +// 推送通知包 +message notice { // pull push + mp.MP_SUB_TYPE subcommand = 1; + repeated string message_id = 2; // 消息id[] 数组 字节序 转成 uint64 +} + +// 推送消息包 +message push_data { + mp.MP_SUB_TYPE subcommand = 1; // 子命令 分辨什么类型的消息 文本 视频 音频? + uint32 target_type = 2; // 目标类型 分辨是群消息还是 单体消息 + string message_id = 3; // 消息id 字节序 转成 uint64 + uint64 target = 4; // 消息目标 + string data = 5; // 消息数据字节序 +} + +// 获取消息包 +message pull_data { + mp.MP_SUB_TYPE subcommand = 1; // 子命令 分辨什么类型的消息 文本 视频 音频? + uint32 source_type = 2; // 来源类型 分辨是群消息还是 单体消息 + string message_id = 3; // 消息id 字节序 转成 uint64 + uint64 source = 4; // 消息来源 + string data = 5; // 消息数据字节序 +} diff --git a/api/MP/protohuf/mp.mp.proto b/api/MP/protohuf/mp.mp.proto new file mode 100644 index 0000000..458f50b --- /dev/null +++ b/api/MP/protohuf/mp.mp.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; +package mp; + +enum MP_TYPE { + // 0 - 19 + MP_REQUEST_LOGIN = 0; // 登陆 + MP_REQUEST_LOGOUT = 1; // 注销 + MP_REQUEST_REGISTER = 2; // 注册 + MP_REQUEST_CODE = 3; // 请求验证码 + MP_REQUEST_FRIENDS = 4; // 好友相关操作 + MP_REQUEST_GROUPS = 5; // 群相关操作 + + + // 20 - 39 + MP_RESPONSE_LOGIN = 20; // 登陆 + MP_RESPONSE_LOGOUT = 21; // 注销 + MP_RESPONSE_REGISTER = 22; // 注册 + MP_RESPONSE_CODE = 23; // 请求验证码 + MP_RESPONSE_FRIENDS = 24; // 添加相关操作 + MP_RESPONSE_GROUPS = 25; // 群相关操作 + + + // 200 以后为 聊天消息包 + MP_IM_NOTICE = 200; // 消息推送通知 + MP_IM_PUSH_MSG = 201; // 推消息 ---> 推至储存库/同步库 + MP_IM_PULL_MSG = 202; // 拉消息 ---> 同步库/储存库拉取 + MP_IM_MSG = 203; // 消息包本体 包含数据 +} + +enum MP_SUB_TYPE { + /// 登陆注册 ***********************************************************************************/// + // login/register request + MP_LOGIN_EMAIL = 0; // 邮箱登陆 + MP_LOGIN_PHONE = 1; // 手机登陆 + MP_LOGIN_ACCOUNT = 2; // 帐号登陆 + MP_REGISTER_EMAIL = 3; // 邮箱注册 + MP_REGISTER_PHONE = 4; // 手机注册 + + // login/register response + MP_LOGIN_SUCCESS = 5; // 登陆成功 + MP_LOGIN_FAIL = 6; // 登陆失败 + MP_REGISTER_SUCCESS = 7; // 注册成功 + MP_REGISTER_FAIL = 8; // 注册失败 + ///***********************************************************************************/// + + + /// 验证码 ***********************************************************************************/// + // email/phone get code request + MP_CODE_EMAIL = 15; // 获取邮箱验证码 + MP_CODE_PHONE = 16; // 获取手机验证码 + // email/phone get code response + MP_CODE_SUCCESS = 17; // 验证码获取 + MP_CODE_FAIL = 28; // 验证码获取失败 + ///***********************************************************************************/// + + + /// 添加用户/群组 ***********************************************************************************/// + MP_ADD_FRIENDS_0 = 51; // 添加权限 0 直接添加 + MP_ADD_FRIENDS_1 = 52; // 添加权限 1 回答问题 + MP_ADD_FRIENDS_2 = 53; // 添加权限 2 账户审核 + MP_ADD_FRIENDS_ACCOUNT = 54; // 按账户添加 + MP_ADD_FRIENDS_EMAIL = 55; // 按邮箱添加 + MP_ADD_FRIENDS_PHONE = 56; // 按手机添加 + MP_ADD_BLACK_LIST = 59; // 添加黑名单 + MP_ADD_WHITE_LIST = 60; // 添加白名单 + MP_REMOVE_FRIEND = 61; // 删除好友 + MP_GET_FRIENDS = 62; // 取账户好友列表 + + MP_ADD_GROUP_0 = 63; // 添加权限 0 直接添加 + MP_ADD_GROUP_1 = 64; // 添加权限 1 回答问题 + MP_ADD_GROUP_2 = 65; // 添加权限 2 账户审核 + MP_ADD_GROUP = 66; // 添加群组 + MP_REMOVE_GROUP = 67; // 删除群组 + MP_GET_GROUPS = 68; // 取账户群组列表 + + MP_ADD_SUCCESS = 69; // 添加成功 + MP_REMOVE_SUCCESS = 70; // 删除成功 + MP_GET_SUCCESS = 71; // 获取成功 + MP_ADD_FAIL = 72; // 添加失败 + MP_REMOVE_FAIL = 73; // 删除失败 + MP_GET_FAIL = 74; // 获取失败 + + MP_ADD_CHECK = 80; // 需要回答问题的好友/群组 验证问题 + + ///***********************************************************************************/// + + + /// 200+ IM ***********************************************************************************/// + + MP_IM_TEXT = 200; // 文本 + MP_IM_PHOTO = 201; // 图片 + MP_IM_AUDIO = 202; // 语音 + MP_IM_VIDEO = 203; // 视频 + + MP_FRIEND = 300; // 单体会话 + MP_GROUP = 301; + + ///***********************************************************************************/// +} \ No newline at end of file diff --git a/api/MP/protohuf/mp.mph.proto b/api/MP/protohuf/mp.mph.proto new file mode 100644 index 0000000..dda0f61 --- /dev/null +++ b/api/MP/protohuf/mp.mph.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package mp; + +import "mp.mp.proto"; + +message mph { + uint32 mpb_size = 1; // 包体大小 + uint64 mp_id = 2; // 包id + uint32 mp_sum = 3; // 包序号 + MP_TYPE mp_type = 4; // 包类型 + string path = 5; +} \ No newline at end of file diff --git a/api/MP/protohuf/mp.request.proto b/api/MP/protohuf/mp.request.proto new file mode 100644 index 0000000..83bcb8a --- /dev/null +++ b/api/MP/protohuf/mp.request.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package mp; + +import "mp.body.proto"; +import "mp.cqi.proto"; + +message request { + body body = 1; + cqi cqi = 2; +} \ No newline at end of file diff --git a/api/MP/protohuf/mp.response.proto b/api/MP/protohuf/mp.response.proto new file mode 100644 index 0000000..8eb4763 --- /dev/null +++ b/api/MP/protohuf/mp.response.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package mp; + +import "mp.sri.proto"; + +message response { + sri sri = 1; +} \ No newline at end of file diff --git a/api/MP/protohuf/mp.sri.proto b/api/MP/protohuf/mp.sri.proto new file mode 100644 index 0000000..5d7d49b --- /dev/null +++ b/api/MP/protohuf/mp.sri.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package mp; + +import "mp.mp.proto"; + +message sri { + MP_SUB_TYPE subcommand = 1; + string username = 2; + uint64 account = 3; + string email = 4; + uint64 phone = 5; + string msg = 6; + string token = 7; + string data = 8; +} \ No newline at end of file diff --git a/api/core/Client.cpp b/api/core/Client.cpp new file mode 100644 index 0000000..9a75f8a --- /dev/null +++ b/api/core/Client.cpp @@ -0,0 +1,109 @@ +// +// Created by dongl on 23-5-10. +// + +#include +#include +#include "Client.h" +#include "management.h" + + +Client::Client(const std::string&& ip, int port) : ip(ip), port(port) { + +} + +void Client::init(const std::string &ip, int port) { + // 初始化时间集合 + base = event_base_new(); + // init socket 初始化socket + bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); + + // 添加事件 + bufferevent_setcb(bev, readcb, writecb, eventcb, base); + // 启用事件 + bufferevent_enable(bev, EV_READ | EV_WRITE); + + // 服务器地址 + auto c_sin = addr(ip, port); + // 连接 + int ret = bufferevent_socket_connect(bev, (sockaddr *)&c_sin, sizeof(c_sin)); + // 是否链接成功 + if (ret == 0) { + printf("server connected success!\n"); + fflush(stdout); + } +} + +sockaddr_in Client::addr(const std::string &ip, int port) { + sockaddr_in c_sin = {0}; + memset(&c_sin, 0, sizeof(c_sin)); + c_sin = { + AF_INET, + htons(port), + + }; + evutil_inet_pton(AF_INET, ip.c_str(), &c_sin.sin_addr.s_addr); + return c_sin; +} + +void Client::run() { + std::thread thread([&]() { + init(ip, port); + event_base_loop(base, EVLOOP_NO_EXIT_ON_EMPTY); + }); + thread.detach(); +} + +Client::~Client() { + bufferevent_free(bev); + event_base_free(base); +} + +void Client::send_data(const std::string &data) { + bufferevent_write(bev, data.c_str(), data.size()); +} + +/// ************ 三个回调 **************//// +void Client::readcb(struct bufferevent *bev, void *ctx) { + management::read_packet(bev); +} + +void Client::writecb(struct bufferevent *bev, void *ctx) { + printf("[write]: %p\n", ctx); + fflush(stdout); +} + +void Client::eventcb(struct bufferevent *bev, short what, void *ctx) { + printf("[event]: %p\n", ctx); + + if (what == BEV_EVENT_ERROR) { + printf("[BEV_EVENT_ERROR]: %p\n", ctx); + } + else if (what == BEV_EVENT_EOF) { + printf("[BEV_EVENT_ERROR]\n"); + } + else if (what == BEV_EVENT_CONNECTED) { + printf("[BEV_EVENT_ERROR]\n"); + } + fflush(stdout); +} + + + + + + + + + + + + + + + + + + + + diff --git a/api/core/Client.h b/api/core/Client.h new file mode 100644 index 0000000..47d4e14 --- /dev/null +++ b/api/core/Client.h @@ -0,0 +1,43 @@ +// +// Created by dongl on 23-5-10. +// + +#ifndef IM2_CLIENT_H +#define IM2_CLIENT_H + +#include +#include "event2/event.h" +#include "event2/bufferevent.h" + +class Client { +public: + Client(const std::string&& ip, int port); + virtual ~Client(); + void run(); + + + +private: + void init(const std::string& ip, int port); + sockaddr_in addr(const std::string& ip, int port); + + /// 用户操作函数 +public: + void send_data(const std::string &data); + + // 三个回调 +private: + static void readcb(struct bufferevent *bev, void *ctx); + static void writecb(struct bufferevent *bev, void *ctx); + static void eventcb(struct bufferevent *bev, short what, void *ctx); + +private: + event_base* base{}; + bufferevent* bev{}; + event* ev{}; + std::string ip; + int port; +}; + + +#endif //IM2_CLIENT_H diff --git a/api/core/agreement.cpp b/api/core/agreement.cpp new file mode 100644 index 0000000..2da5c26 --- /dev/null +++ b/api/core/agreement.cpp @@ -0,0 +1,34 @@ +// +// Created by dongl on 23-4-20. +// + +#include +#include "agreement.h" + + +agreement_request::agreement_request() : agreement() {} +agreement_request::~agreement_request() {} + +void agreement_request::set (std::shared_ptr &mph, std::shared_ptr& request, bufferevent* bev, sockaddr_in* addr) { + m_mph = mph; + m_body = request->body(); + m_cqi = request->cqi(); + m_bev = bev; + m_addr = addr; +} + +agreement_response::agreement_response() : agreement() {} +agreement_response::~agreement_response() {} + + +void agreement_response::set(mp::response* response) { + m_sri = response->sri(); +} + + + + + + + + diff --git a/api/core/agreement.h b/api/core/agreement.h new file mode 100644 index 0000000..f0a4f0b --- /dev/null +++ b/api/core/agreement.h @@ -0,0 +1,54 @@ +// +// Created by dongl on 23-4-20. +// + +#ifndef IM2_AGREEMENT_H +#define IM2_AGREEMENT_H + + +#include +#include "event2/bufferevent.h" +#include "MP/Mph.h" +#include "MP/Body.h" +#include "MP/Sri.h" +#include "MP/Cqi.h" +#include "MP/Request.h" +#include "MP/Response.h" + + +class agreement { +public: + agreement(){} + virtual ~agreement() {} +}; + + +class agreement_request : public agreement{ +public: + agreement_request(); + ~agreement_request(); + +public: + void set (std::shared_ptr &mph, std::shared_ptr& request, bufferevent* bev, sockaddr_in* addr); + +public: + std::shared_ptr m_mph; + mp::body m_body; + mp::cqi m_cqi; + bufferevent* m_bev; + sockaddr_in* m_addr; +}; + + +class agreement_response : public agreement { +public: + agreement_response(); + ~agreement_response() override; + +public: + void set (mp::response* response); +public: + mp::sri m_sri; +}; + +#endif //IM2_AGREEMENT_H diff --git a/api/core/management.cpp b/api/core/management.cpp new file mode 100644 index 0000000..7830075 --- /dev/null +++ b/api/core/management.cpp @@ -0,0 +1,88 @@ +// +// Created by dongl on 23-5-12. +// + +#include +#include +#include +#include "management.h" +#include "MP/Response.h" + +std::queue management::resp_msg; + +void management::read_packet(bufferevent *bev) { + char buff[2048] = {0}; + std::atomic len_index = bufferevent_read(bev, buff, 2048); + + // 处理粘包 + char packet_h[50] = {0}; + char packet_b[256] = {0}; + while (len_index > 0) { + memset(packet_h, 0, sizeof(packet_h)); + memset(packet_b, 0, sizeof(packet_b)); + + /// read L 读包长度 + uint8_t packetLen; + // 取包长度 + memcpy(&packetLen, buff, 1); + // 更新buffer + memcpy(buff, buff + 1, strlen(buff) - 1); + // 更新buffer长度 + len_index -= 1; + + /// read V 读包头 + // 取包头 + memcpy(packet_h, buff, packetLen); + // 更新buffer + memcpy(buff, buff + packetLen, strlen(buff) - packetLen); + // 更新buffer长度 + len_index -= packetLen; + + // 解包头 + auto mph = new mp::mph(); + mph->ParseFromString(packet_h); + + /// read V 读包体 包头内含有包体长度 + // 取包体 + memcpy(packet_b, buff, mph->mpb_size()); + // 更新buffer + memcpy(buff, buff + mph->mpb_size(), strlen(buff) - mph->mpb_size()); + // 更新buffer长度 + len_index -= mph->mpb_size(); + + // 解包体 + auto response = new mp::response(); + response->ParseFromString(packet_b); + + auto resp = new agreement_response(); + resp->set(response); + resp_msg.push(resp); + } +} + +agreement_response management::fetch_packet() { + agreement_response ret; + + auto beforeTime = std::chrono::steady_clock::now(); +// double duration = 0; + while (resp_msg.empty()) { +// auto afterTime = std::chrono::steady_clock::now(); +// duration += std::chrono::duration(afterTime - beforeTime).count(); +// +// if (duration > 5000) { +// +// } + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + auto afterTime = std::chrono::steady_clock::now(); + + + ret = *resp_msg.front(); + resp_msg.pop(); + + //毫秒级 + double duration_millsecond = std::chrono::duration(afterTime - beforeTime).count(); + std::cout << "" << duration_millsecond << "毫秒" << std::endl; + + return ret; +} diff --git a/api/core/management.h b/api/core/management.h new file mode 100644 index 0000000..4945a16 --- /dev/null +++ b/api/core/management.h @@ -0,0 +1,23 @@ +// +// Created by dongl on 23-5-12. +// + +#ifndef IM_GUI_MANAGEMENT_H +#define IM_GUI_MANAGEMENT_H + + +#include +#include "event2/bufferevent.h" +#include "agreement.h" + +class management { +public: + static void read_packet(bufferevent* bev); + static agreement_response fetch_packet(); + +private: + static std::queue resp_msg; +}; + + +#endif //IM_GUI_MANAGEMENT_H diff --git a/api/user/User.h b/api/user/User.h new file mode 100644 index 0000000..9cd951f --- /dev/null +++ b/api/user/User.h @@ -0,0 +1,19 @@ +// +// Created by dongl on 23-5-16. +// + +#ifndef IM_USER_H +#define IM_USER_H + +#include + +class User : QObject { +public: + uint64_t account; + std::string username; + uint64_t phone; + std::string email; + std::string token; +}; + +#endif //IM_USER_H diff --git a/api/user/UserOperation.cpp b/api/user/UserOperation.cpp new file mode 100644 index 0000000..5bcdb1d --- /dev/null +++ b/api/user/UserOperation.cpp @@ -0,0 +1,141 @@ +// +// Created by dongl on 23-5-10. +// + +#include +#include "UserOperation.h" + + +UserOperation::UserOperation(Client *client) { + this->client = client; +} + +UserOperation::~UserOperation() { + +} + +//// packet +std::string UserOperation::packet_base(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string &account, + const std::string &password) { + auto request = new Request(type, subType, account, password); + std::string temp = request->packet(); + delete request; + return temp; +} + +std::string +UserOperation::packet_base(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string &account, const std::string &password, + const std::string &data) { + auto request = new Request(type, subType, account, password, data); + std::string temp = request->packet(); + delete request; + return temp; +} + +std::string UserOperation::packet_base(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string& account) { + auto request = new Request(type,subType,account); + std::string temp = request->packet(); + delete request; + return temp; +} + + + +//// login +std::string UserOperation::login_packet(mp::MP_SUB_TYPE subType, const std::string &account, const std::string &password) { + return packet_base(mp::MP_REQUEST_LOGIN, subType, account, password); +} + +//// register +std::string +UserOperation::register_packet(mp::MP_SUB_TYPE subType, const std::string &account, const std::string &password, + const std::string &code) { + return packet_base(mp::MP_REQUEST_REGISTER, subType, account, password, code); +} + + +std::optional UserOperation::login(const std::string &account, const std::string &password) { + std::regex pattern_email(R"(\w+@(\w+\.)+\w+)"); + std::regex pattern_account("[1-9](\\d{5,11})"); + std::regex pattern_phone("1(3\\d|47|5([0-3]|[5-9])|8(0|2|[5-9]))\\d{8}$"); + + std::string packet; + if (std::regex_match(account, pattern_email)) { + packet = login_packet(LOGIN_EMAIL, account, password); + } else if (std::regex_match(account, pattern_account)) { + packet = login_packet(LOGIN_ACCOUNT, account, password); + } else if (std::regex_match(account, pattern_phone)) { + packet = login_packet(LOGIN_PHONE, account, password); + } else { + return std::nullopt; + } + + client->send_data(packet); + + return management::fetch_packet(); +} + +std::optional UserOperation::register_(const std::string &account, const std::string &password, const std::string& code) { + std::regex pattern_email(R"(\w+@(\w+\.)+\w+)"); + std::regex pattern_phone("1(3\\d|47|5([0-3]|[5-9])|8(0|2|[5-9]))\\d{8}$"); + + std::string packet; + if (std::regex_match(account, pattern_email)) { + packet = register_packet(REGISTER_EMAIL, account, password, code); + } else if (std::regex_match(account, pattern_phone)) { + packet = register_packet(REGISTER_PHONE, account, password, code); + } else { + return std::nullopt; + } + + client->send_data(packet); + + return management::fetch_packet(); +} + +std::optional UserOperation::get_code(const std::string& account) { + std::regex pattern_email(R"(\w+@(\w+\.)+\w+)"); + std::regex pattern_phone("1(3\\d|47|5([0-3]|[5-9])|8(0|2|[5-9]))\\d{8}$"); + + std::string packet; + if (std::regex_match(account, pattern_email)) { + packet = packet_base(mp::MP_TYPE::MP_REQUEST_CODE, mp::MP_SUB_TYPE::MP_CODE_EMAIL, account); + } else if (std::regex_match(account, pattern_phone)) { + + } else { + return std::nullopt; + } + + client->send_data(packet); + + return management::fetch_packet(); +} + +std::optional UserOperation::FetchFriends(uint64_t account) { + std::regex pattern_account("[1-9](\\d{5,11})"); + + std::string packet; + if (std::regex_match(std::to_string(account), pattern_account)) { + packet = packet_base(mp::MP_TYPE::MP_REQUEST_FRIENDS, mp::MP_SUB_TYPE::MP_GET_FRIENDS, std::to_string(account)); + } else { + return std::nullopt; + } + + client->send_data(packet); + + return management::fetch_packet(); +} + + + + + + +//// login + + + + + + + diff --git a/api/user/UserOperation.h b/api/user/UserOperation.h new file mode 100644 index 0000000..2cb3b5a --- /dev/null +++ b/api/user/UserOperation.h @@ -0,0 +1,48 @@ +// +// Created by dongl on 23-5-10. +// + +#ifndef IM2_USEROPERATION_H +#define IM2_USEROPERATION_H + + +#include +#include +#include "event2/bufferevent.h" +#include "core/Client.h" +#include "MP/Request.h" +#include "core/management.h" + + +#define LOGIN_ACCOUNT mp::MP_LOGIN_ACCOUNT +#define LOGIN_PHONE mp::MP_LOGIN_PHONE +#define LOGIN_EMAIL mp::MP_LOGIN_EMAIL + +#define REGISTER_EMAIL mp::MP_SUB_TYPE::MP_REGISTER_EMAIL +#define REGISTER_PHONE mp::MP_SUB_TYPE::MP_REGISTER_PHONE + + +class UserOperation { +public: + explicit UserOperation(Client* client); + virtual ~UserOperation(); + +public: + std::optional login(const std::string& account, const std::string& password); + std::optional register_(const std::string& account, const std::string& password, const std::string& code); + std::optional get_code(const std::string& account); + std::optional FetchFriends(uint64_t account); + +private: + std::string packet_base(mp::MP_TYPE, mp::MP_SUB_TYPE, const std::string &account, const std::string &password); + std::string packet_base(mp::MP_TYPE, mp::MP_SUB_TYPE, const std::string &account, const std::string &password, const std::string &data); + std::string packet_base(mp::MP_TYPE type, mp::MP_SUB_TYPE subType, const std::string& account); + + std::string login_packet(mp::MP_SUB_TYPE subType, const std::string &account, const std::string &password); + std::string register_packet(mp::MP_SUB_TYPE subType, const std::string &account, const std::string &password, const std::string &code); +private: + Client* client; +}; + + +#endif //IM2_USEROPERATION_H diff --git a/include/libevent/evdns.h b/include/libevent/evdns.h new file mode 100644 index 0000000..8672db0 --- /dev/null +++ b/include/libevent/evdns.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT1_EVDNS_H_INCLUDED_ +#define EVENT1_EVDNS_H_INCLUDED_ + +/** @file evdns.h + + A dns subsystem for Libevent. + + The header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other + headers. + */ + +#include +#include +#include +#include + +#endif /* EVENT1_EVDNS_H_INCLUDED_ */ diff --git a/include/libevent/event.h b/include/libevent/event.h new file mode 100644 index 0000000..0e33f90 --- /dev/null +++ b/include/libevent/event.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT1_EVENT_H_INCLUDED_ +#define EVENT1_EVENT_H_INCLUDED_ + +/** @file event.h + + A library for writing event-driven network servers. + + The header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other event2/ + headers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_STDINT_H +#include +#endif +#include + +/* For int types. */ +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#undef WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT1_EVENT_H_INCLUDED_ */ diff --git a/include/libevent/event2/buffer.h b/include/libevent/event2/buffer.h new file mode 100644 index 0000000..5a76cf2 --- /dev/null +++ b/include/libevent/event2/buffer.h @@ -0,0 +1,1098 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_BUFFER_H_INCLUDED_ +#define EVENT2_BUFFER_H_INCLUDED_ + +/** @file event2/buffer.h + + @brief Functions for buffering data for network sending or receiving. + + An evbuffer can be used for preparing data before sending it to + the network or conversely for reading data from the network. + Evbuffers try to avoid memory copies as much as possible. As a + result, evbuffers can be used to pass data around without actually + incurring the overhead of copying the data. + + A new evbuffer can be allocated with evbuffer_new(), and can be + freed with evbuffer_free(). Most users will be using evbuffers via + the bufferevent interface. To access a bufferevent's evbuffers, use + bufferevent_get_input() and bufferevent_get_output(). + + There are several guidelines for using evbuffers. + + - if you already know how much data you are going to add as a result + of calling evbuffer_add() multiple times, it makes sense to use + evbuffer_expand() first to make sure that enough memory is allocated + before hand. + + - evbuffer_add_buffer() adds the contents of one buffer to the other + without incurring any unnecessary memory copies. + + - evbuffer_add() and evbuffer_add_buffer() do not mix very well: + if you use them, you will wind up with fragmented memory in your + buffer. + + - For high-performance code, you may want to avoid copying data into and out + of buffers. You can skip the copy step by using + evbuffer_reserve_space()/evbuffer_commit_space() when writing into a + buffer, and evbuffer_peek() when reading. + + In Libevent 2.0 and later, evbuffers are represented using a linked + list of memory chunks, with pointers to the first and last chunk in + the chain. + + As the contents of an evbuffer can be stored in multiple different + memory blocks, it cannot be accessed directly. Instead, evbuffer_pullup() + can be used to force a specified number of bytes to be contiguous. This + will cause memory reallocation and memory copies if the data is split + across multiple blocks. It is more efficient, however, to use + evbuffer_peek() if you don't require that the memory to be contiguous. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_UIO_H +#include +#endif +#include + +/** + An evbuffer is an opaque data type for efficiently buffering data to be + sent or received on the network. + + @see event2/event.h for more information +*/ +struct evbuffer +#ifdef EVENT_IN_DOXYGEN_ +{} +#endif +; + +/** + Pointer to a position within an evbuffer. + + Used when repeatedly searching through a buffer. Calling any function + that modifies or re-packs the buffer contents may invalidate all + evbuffer_ptrs for that buffer. Do not modify or contruct these values + except with evbuffer_ptr_set. + + An evbuffer_ptr can represent any position from the start of a buffer up + to a position immediately after the end of a buffer. + + @see evbuffer_ptr_set() + */ +struct evbuffer_ptr { + ev_ssize_t pos; + + /* Do not alter or rely on the values of fields: they are for internal + * use */ + struct { + void *chain; + size_t pos_in_chain; + } internal_; +}; + +/** Describes a single extent of memory inside an evbuffer. Used for + direct-access functions. + + @see evbuffer_reserve_space, evbuffer_commit_space, evbuffer_peek + */ +#ifdef EVENT__HAVE_SYS_UIO_H +#define evbuffer_iovec iovec +/* Internal use -- defined only if we are using the native struct iovec */ +#define EVBUFFER_IOVEC_IS_NATIVE_ +#else +struct evbuffer_iovec { + /** The start of the extent of memory. */ + void *iov_base; + /** The length of the extent of memory. */ + size_t iov_len; +}; +#endif + +/** + Allocate storage for a new evbuffer. + + @return a pointer to a newly allocated evbuffer struct, or NULL if an error + occurred + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evbuffer_new(void); +/** + Deallocate storage for an evbuffer. + + @param buf pointer to the evbuffer to be freed + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_free(struct evbuffer *buf); + + +/** + Set maximum read buffer size + + Default is 4096 and it works fine most of time, so before increasing the + default check carefully, since this has some negative effects (like memory + fragmentation and unfair resource distribution, i.e. some events will make + less progress than others). + + @param buf pointer to the evbuffer + @param max buffer size + @return 0 on success, -1 on failure (if max > INT_MAX). + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_set_max_read(struct evbuffer *buf, size_t max); +/** + Get maximum read buffer size + + @param buf pointer to the evbuffer + @return current maximum buffer read + */ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_max_read(struct evbuffer *buf); + +/** + Enable locking on an evbuffer so that it can safely be used by multiple + threads at the same time. + + NOTE: when locking is enabled, the lock will be held when callbacks are + invoked. This could result in deadlock if you aren't careful. Plan + accordingly! + + @param buf An evbuffer to make lockable. + @param lock A lock object, or NULL if we should allocate our own. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_enable_locking(struct evbuffer *buf, void *lock); + +/** + Acquire the lock on an evbuffer. Has no effect if locking was not enabled + with evbuffer_enable_locking. +*/ +EVENT2_EXPORT_SYMBOL +void evbuffer_lock(struct evbuffer *buf); + +/** + Release the lock on an evbuffer. Has no effect if locking was not enabled + with evbuffer_enable_locking. +*/ +EVENT2_EXPORT_SYMBOL +void evbuffer_unlock(struct evbuffer *buf); + + +/** If this flag is set, then we will not use evbuffer_peek(), + * evbuffer_remove(), evbuffer_remove_buffer(), and so on to read bytes + * from this buffer: we'll only take bytes out of this buffer by + * writing them to the network (as with evbuffer_write_atmost), by + * removing them without observing them (as with evbuffer_drain), + * or by copying them all out at once (as with evbuffer_add_buffer). + * + * Using this option allows the implementation to use sendfile-based + * operations for evbuffer_add_file(); see that function for more + * information. + * + * This flag is on by default for bufferevents that can take advantage + * of it; you should never actually need to set it on a bufferevent's + * output buffer. + */ +#define EVBUFFER_FLAG_DRAINS_TO_FD 1 + +/** Change the flags that are set for an evbuffer by adding more. + * + * @param buf the evbuffer that the callback is watching. + * @param flags One or more EVBUFFER_FLAG_* options + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_set_flags(struct evbuffer *buf, ev_uint64_t flags); +/** Change the flags that are set for an evbuffer by removing some. + * + * @param buf the evbuffer that the callback is watching. + * @param flags One or more EVBUFFER_FLAG_* options + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_clear_flags(struct evbuffer *buf, ev_uint64_t flags); + +/** + Returns the total number of bytes stored in the evbuffer + + @param buf pointer to the evbuffer + @return the number of bytes stored in the evbuffer +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_length(const struct evbuffer *buf); + +/** + Returns the number of contiguous available bytes in the first buffer chain. + + This is useful when processing data that might be split into multiple + chains, or that might all be in the first chain. Calls to + evbuffer_pullup() that cause reallocation and copying of data can thus be + avoided. + + @param buf pointer to the evbuffer + @return 0 if no data is available, otherwise the number of available bytes + in the first buffer chain. +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_contiguous_space(const struct evbuffer *buf); + +/** + Expands the available space in an evbuffer. + + Expands the available space in the evbuffer to at least datlen, so that + appending datlen additional bytes will not require any new allocations. + + @param buf the evbuffer to be expanded + @param datlen the new minimum length requirement + @return 0 if successful, or -1 if an error occurred +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_expand(struct evbuffer *buf, size_t datlen); + +/** + Reserves space in the last chain or chains of an evbuffer. + + Makes space available in the last chain or chains of an evbuffer that can + be arbitrarily written to by a user. The space does not become + available for reading until it has been committed with + evbuffer_commit_space(). + + The space is made available as one or more extents, represented by + an initial pointer and a length. You can force the memory to be + available as only one extent. Allowing more extents, however, makes the + function more efficient. + + Multiple subsequent calls to this function will make the same space + available until evbuffer_commit_space() has been called. + + It is an error to do anything that moves around the buffer's internal + memory structures before committing the space. + + NOTE: The code currently does not ever use more than two extents. + This may change in future versions. + + @param buf the evbuffer in which to reserve space. + @param size how much space to make available, at minimum. The + total length of the extents may be greater than the requested + length. + @param vec an array of one or more evbuffer_iovec structures to + hold pointers to the reserved extents of memory. + @param n_vec The length of the vec array. Must be at least 1; + 2 is more efficient. + @return the number of provided extents, or -1 on error. + @see evbuffer_commit_space() +*/ +EVENT2_EXPORT_SYMBOL +int +evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, + struct evbuffer_iovec *vec, int n_vec); + +/** + Commits previously reserved space. + + Commits some of the space previously reserved with + evbuffer_reserve_space(). It then becomes available for reading. + + This function may return an error if the pointer in the extents do + not match those returned from evbuffer_reserve_space, or if data + has been added to the buffer since the space was reserved. + + If you want to commit less data than you got reserved space for, + modify the iov_len pointer of the appropriate extent to a smaller + value. Note that you may have received more space than you + requested if it was available! + + @param buf the evbuffer in which to reserve space. + @param vec one or two extents returned by evbuffer_reserve_space. + @param n_vecs the number of extents. + @return 0 on success, -1 on error + @see evbuffer_reserve_space() +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_commit_space(struct evbuffer *buf, + struct evbuffer_iovec *vec, int n_vecs); + +/** + Append data to the end of an evbuffer. + + @param buf the evbuffer to be appended to + @param data pointer to the beginning of the data buffer + @param datlen the number of bytes to be copied from the data buffer + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); + + +/** + Read data from an evbuffer and drain the bytes read. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); + +/** + Read data from an evbuffer, and leave the buffer unchanged. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param data_out the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen); + +/** + Read data from the middle of an evbuffer, and leave the buffer unchanged. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param pos the position to start reading from + @param data_out the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen); + +/** + Read data from an evbuffer into another evbuffer, draining + the bytes from the source buffer. This function avoids copy + operations to the extent possible. + + If more bytes are requested than are available in src, the src + buffer is drained completely. + + @param src the evbuffer to be read from + @param dst the destination evbuffer to store the result into + @param datlen the maximum numbers of bytes to transfer + @return the number of bytes read + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, + size_t datlen); + +/** Used to tell evbuffer_readln what kind of line-ending to look for. + */ +enum evbuffer_eol_style { + /** Any sequence of CR and LF characters is acceptable as an + * EOL. + * + * Note that this style can produce ambiguous results: the + * sequence "CRLF" will be treated as a single EOL if it is + * all in the buffer at once, but if you first read a CR from + * the network and later read an LF from the network, it will + * be treated as two EOLs. + */ + EVBUFFER_EOL_ANY, + /** An EOL is an LF, optionally preceded by a CR. This style is + * most useful for implementing text-based internet protocols. */ + EVBUFFER_EOL_CRLF, + /** An EOL is a CR followed by an LF. */ + EVBUFFER_EOL_CRLF_STRICT, + /** An EOL is a LF. */ + EVBUFFER_EOL_LF, + /** An EOL is a NUL character (that is, a single byte with value 0) */ + EVBUFFER_EOL_NUL +}; + +/** + * Read a single line from an evbuffer. + * + * Reads a line terminated by an EOL as determined by the evbuffer_eol_style + * argument. Returns a newly allocated nul-terminated string; the caller must + * free the returned value. The EOL is not included in the returned string. + * + * @param buffer the evbuffer to read from + * @param n_read_out if non-NULL, points to a size_t that is set to the + * number of characters in the returned string. This is useful for + * strings that can contain NUL characters. + * @param eol_style the style of line-ending to use. + * @return pointer to a single line, or NULL if an error occurred + */ +EVENT2_EXPORT_SYMBOL +char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style); + +/** + Move all data from one evbuffer into another evbuffer. + + This is a destructive add. The data from one buffer moves into + the other buffer. However, no unnecessary memory copies occur. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + + @see evbuffer_remove_buffer() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf); + +/** + Copy data from one evbuffer into another evbuffer. + + This is a non-destructive add. The data from one buffer is copied + into the other buffer. However, no unnecessary memory copies occur. + + Note that buffers already containing buffer references can't be added + to other buffers. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_buffer_reference(struct evbuffer *outbuf, + struct evbuffer *inbuf); + +/** + A cleanup function for a piece of memory added to an evbuffer by + reference. + + @see evbuffer_add_reference() + */ +typedef void (*evbuffer_ref_cleanup_cb)(const void *data, + size_t datalen, void *extra); + +/** + Reference memory into an evbuffer without copying. + + The memory needs to remain valid until all the added data has been + read. This function keeps just a reference to the memory without + actually incurring the overhead of a copy. + + @param outbuf the output buffer + @param data the memory to reference + @param datlen how memory to reference + @param cleanupfn callback to be invoked when the memory is no longer + referenced by this evbuffer. + @param cleanupfn_arg optional argument to the cleanup callback + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_reference(struct evbuffer *outbuf, + const void *data, size_t datlen, + evbuffer_ref_cleanup_cb cleanupfn, void *cleanupfn_arg); + +/** + Copy data from a file into the evbuffer for writing to a socket. + + This function avoids unnecessary data copies between userland and + kernel. If sendfile is available and the EVBUFFER_FLAG_DRAINS_TO_FD + flag is set, it uses those functions. Otherwise, it tries to use + mmap (or CreateFileMapping on Windows). + + The function owns the resulting file descriptor and will close it + when finished transferring data. + + The results of using evbuffer_remove() or evbuffer_pullup() on + evbuffers whose data was added using this function are undefined. + + For more fine-grained control, use evbuffer_add_file_segment. + + @param outbuf the output buffer + @param fd the file descriptor + @param offset the offset from which to read data + @param length how much data to read, or -1 to read as much as possible. + (-1 requires that 'fd' support fstat.) + @return 0 if successful, or -1 if an error occurred +*/ + +EVENT2_EXPORT_SYMBOL +int evbuffer_add_file(struct evbuffer *outbuf, int fd, ev_off_t offset, + ev_off_t length); + +/** + An evbuffer_file_segment holds a reference to a range of a file -- + possibly the whole file! -- for use in writing from an evbuffer to a + socket. It could be implemented with mmap or sendfile, or (if all else + fails) by just pulling all the data into RAM. A single evbuffer_file_segment + can be added more than once, and to more than one evbuffer. + */ +struct evbuffer_file_segment; + +/** + Flag for creating evbuffer_file_segment: If this flag is set, then when + the evbuffer_file_segment is freed and no longer in use by any + evbuffer, the underlying fd is closed. + */ +#define EVBUF_FS_CLOSE_ON_FREE 0x01 +/** + Flag for creating evbuffer_file_segment: Disable memory-map based + implementations. + */ +#define EVBUF_FS_DISABLE_MMAP 0x02 +/** + Flag for creating evbuffer_file_segment: Disable direct fd-to-fd + implementations (sendfile). + + You might want to use this option if data needs to be taken from the + evbuffer by any means other than writing it to the network: the sendfile + backend is fast, but it only works for sending files directly to the + network. + */ +#define EVBUF_FS_DISABLE_SENDFILE 0x04 +/** + Flag for creating evbuffer_file_segment: Do not allocate a lock for this + segment. If this option is set, then neither the segment nor any + evbuffer it is added to may ever be accessed from more than one thread + at a time. + */ +#define EVBUF_FS_DISABLE_LOCKING 0x08 + +/** + A cleanup function for a evbuffer_file_segment added to an evbuffer + for reference. + */ +typedef void (*evbuffer_file_segment_cleanup_cb)( + struct evbuffer_file_segment const* seg, int flags, void* arg); + +/** + Create and return a new evbuffer_file_segment for reading data from a + file and sending it out via an evbuffer. + + This function avoids unnecessary data copies between userland and + kernel. Where available, it uses sendfile. + + The file descriptor must not be closed so long as any evbuffer is using + this segment. + + The results of using evbuffer_remove() or evbuffer_pullup() or any other + function that reads bytes from an evbuffer on any evbuffer containing + the newly returned segment are undefined, unless you pass the + EVBUF_FS_DISABLE_SENDFILE flag to this function. + + @param fd an open file to read from. + @param offset an index within the file at which to start reading + @param length how much data to read, or -1 to read as much as possible. + (-1 requires that 'fd' support fstat.) + @param flags any number of the EVBUF_FS_* flags + @return a new evbuffer_file_segment, or NULL on failure. + **/ +EVENT2_EXPORT_SYMBOL +struct evbuffer_file_segment *evbuffer_file_segment_new( + int fd, ev_off_t offset, ev_off_t length, unsigned flags); + +/** + Free an evbuffer_file_segment + + It is safe to call this function even if the segment has been added to + one or more evbuffers. The evbuffer_file_segment will not be freed + until no more references to it exist. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_file_segment_free(struct evbuffer_file_segment *seg); + +/** + Add cleanup callback and argument for the callback to an + evbuffer_file_segment. + + The cleanup callback will be invoked when no more references to the + evbuffer_file_segment exist. + **/ +EVENT2_EXPORT_SYMBOL +void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, + evbuffer_file_segment_cleanup_cb cb, void* arg); + +/** + Insert some or all of an evbuffer_file_segment at the end of an evbuffer + + Note that the offset and length parameters of this function have a + different meaning from those provided to evbuffer_file_segment_new: When + you create the segment, the offset is the offset _within the file_, and + the length is the length _of the segment_, whereas when you add a + segment to an evbuffer, the offset is _within the segment_ and the + length is the length of the _part of the segment you want to use. + + In other words, if you have a 10 KiB file, and you create an + evbuffer_file_segment for it with offset 20 and length 1000, it will + refer to bytes 20..1019 inclusive. If you then pass this segment to + evbuffer_add_file_segment and specify an offset of 20 and a length of + 50, you will be adding bytes 40..99 inclusive. + + @param buf the evbuffer to append to + @param seg the segment to add + @param offset the offset within the segment to start from + @param length the amount of data to add, or -1 to add it all. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_file_segment(struct evbuffer *buf, + struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length); + +/** + Append a formatted string to the end of an evbuffer. + + The string is formated as printf. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ... arguments that will be passed to printf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + + @see evutil_printf(), evbuffer_add_vprintf() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; + +/** + Append a va_list formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ap a varargs va_list argument array that will be passed to vprintf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 0))) +#endif +; + + +/** + Remove a specified number of bytes data from the beginning of an evbuffer. + + @param buf the evbuffer to be drained + @param len the number of bytes to drain from the beginning of the buffer + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_drain(struct evbuffer *buf, size_t len); + + +/** + Write the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd); + +/** + Write some of the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @param howmuch the largest allowable number of bytes to write, or -1 + to write as many bytes as we can. + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, + ev_ssize_t howmuch); + +/** + Read from a file descriptor and store the result in an evbuffer. + + @param buffer the evbuffer to store the result + @param fd the file descriptor to read from + @param howmuch the number of bytes to be read. If the given number is negative + or out of maximum bytes per one read, as many bytes as we can will be read. + @return the number of bytes read, or -1 if an error occurred + @see evbuffer_write() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch); + +/** + Search for a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @param start NULL or a pointer to a valid struct evbuffer_ptr. + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence of the string in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start); + +/** + Search for a string within part of an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @param start NULL or a pointer to a valid struct evbuffer_ptr that + indicates where we should start searching. + @param end NULL or a pointer to a valid struct evbuffer_ptr that + indicates where we should stop searching. + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence of the string in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end); + +/** + Defines how to adjust an evbuffer_ptr by evbuffer_ptr_set() + + @see evbuffer_ptr_set() */ +enum evbuffer_ptr_how { + /** Sets the pointer to the position; can be called on with an + uninitialized evbuffer_ptr. */ + EVBUFFER_PTR_SET, + /** Advances the pointer by adding to the current position. */ + EVBUFFER_PTR_ADD +}; + +/** + Sets the search pointer in the buffer to position. + + There are two ways to use this function: you can call + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET) + to move 'pos' to a position 'N' bytes after the start of the buffer, or + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_ADD) + to move 'pos' forward by 'N' bytes. + + If evbuffer_ptr is not initialized, this function can only be called + with EVBUFFER_PTR_SET. + + An evbuffer_ptr can represent any position from the start of the buffer to + a position immediately after the end of the buffer. + + @param buffer the evbuffer to be search + @param ptr a pointer to a struct evbuffer_ptr + @param position the position at which to start the next search + @param how determines how the pointer should be manipulated. + @returns 0 on success or -1 otherwise +*/ +EVENT2_EXPORT_SYMBOL +int +evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *ptr, + size_t position, enum evbuffer_ptr_how how); + +/** + Search for an end-of-line string within an evbuffer. + + @param buffer the evbuffer to be searched + @param start NULL or a pointer to a valid struct evbuffer_ptr to start + searching at. + @param eol_len_out If non-NULL, the pointed-to value will be set to + the length of the end-of-line string. + @param eol_style The kind of EOL to look for; see evbuffer_readln() for + more information + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence EOL in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer, + struct evbuffer_ptr *start, size_t *eol_len_out, + enum evbuffer_eol_style eol_style); + +/** Function to peek at data inside an evbuffer without removing it or + copying it out. + + Pointers to the data are returned by filling the 'vec_out' array + with pointers to one or more extents of data inside the buffer. + + The total data in the extents that you get back may be more than + you requested (if there is more data last extent than you asked + for), or less (if you do not provide enough evbuffer_iovecs, or if + the buffer does not have as much data as you asked to see). + + @param buffer the evbuffer to peek into, + @param len the number of bytes to try to peek. If len is negative, we + will try to fill as much of vec_out as we can. If len is negative + and vec_out is not provided, we return the number of evbuffer_iovecs + that would be needed to get all the data in the buffer. + @param start_at an evbuffer_ptr indicating the point at which we + should start looking for data. NULL means, "At the start of the + buffer." + @param vec_out an array of evbuffer_iovec + @param n_vec the length of vec_out. If 0, we only count how many + extents would be necessary to point to the requested amount of + data. + @return The number of extents needed. This may be less than n_vec + if we didn't need all the evbuffer_iovecs we were given, or more + than n_vec if we would need more to return all the data that was + requested. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, + struct evbuffer_ptr *start_at, + struct evbuffer_iovec *vec_out, int n_vec); + + +/** Structure passed to an evbuffer_cb_func evbuffer callback + + @see evbuffer_cb_func, evbuffer_add_cb() + */ +struct evbuffer_cb_info { + /** The number of bytes in this evbuffer when callbacks were last + * invoked. */ + size_t orig_size; + /** The number of bytes added since callbacks were last invoked. */ + size_t n_added; + /** The number of bytes removed since callbacks were last invoked. */ + size_t n_deleted; +}; + +/** Type definition for a callback that is invoked whenever data is added or + removed from an evbuffer. + + An evbuffer may have one or more callbacks set at a time. The order + in which they are executed is undefined. + + A callback function may add more callbacks, or remove itself from the + list of callbacks, or add or remove data from the buffer. It may not + remove another callback from the list. + + If a callback adds or removes data from the buffer or from another + buffer, this can cause a recursive invocation of your callback or + other callbacks. If you ask for an infinite loop, you might just get + one: watch out! + + @param buffer the buffer whose size has changed + @param info a structure describing how the buffer changed. + @param arg a pointer to user data +*/ +typedef void (*evbuffer_cb_func)(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *arg); + +struct evbuffer_cb_entry; +/** Add a new callback to an evbuffer. + + Subsequent calls to evbuffer_add_cb() add new callbacks. To remove this + callback, call evbuffer_remove_cb or evbuffer_remove_cb_entry. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified, + or NULL to remove all callbacks. + @param cbarg an argument to be provided to the callback function + @return a handle to the callback on success, or NULL on failure. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg); + +/** Remove a callback from an evbuffer, given a handle returned from + evbuffer_add_cb. + + Calling this function invalidates the handle. + + @return 0 if a callback was removed, or -1 if no matching callback was + found. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_cb_entry(struct evbuffer *buffer, + struct evbuffer_cb_entry *ent); + +/** Remove a callback from an evbuffer, given the function and argument + used to add it. + + @return 0 if a callback was removed, or -1 if no matching callback was + found. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg); + +/** If this flag is not set, then a callback is temporarily disabled, and + * should not be invoked. + * + * @see evbuffer_cb_set_flags(), evbuffer_cb_clear_flags() + */ +#define EVBUFFER_CB_ENABLED 1 + +/** Change the flags that are set for a callback on a buffer by adding more. + + @param buffer the evbuffer that the callback is watching. + @param cb the callback whose status we want to change. + @param flags EVBUFFER_CB_ENABLED to re-enable the callback. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_cb_set_flags(struct evbuffer *buffer, + struct evbuffer_cb_entry *cb, ev_uint32_t flags); + +/** Change the flags that are set for a callback on a buffer by removing some + + @param buffer the evbuffer that the callback is watching. + @param cb the callback whose status we want to change. + @param flags EVBUFFER_CB_ENABLED to disable the callback. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_cb_clear_flags(struct evbuffer *buffer, + struct evbuffer_cb_entry *cb, ev_uint32_t flags); + +#if 0 +/** Postpone calling a given callback until unsuspend is called later. + + This is different from disabling the callback, since the callback will get + invoked later if the buffer size changes between now and when we unsuspend + it. + + @param the buffer that the callback is watching. + @param cb the callback we want to suspend. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_cb_suspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb); +/** Stop postponing a callback that we postponed with evbuffer_cb_suspend. + + If data was added to or removed from the buffer while the callback was + suspended, the callback will get called once now. + + @param the buffer that the callback is watching. + @param cb the callback we want to stop suspending. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb); +#endif + +/** + Makes the data at the beginning of an evbuffer contiguous. + + @param buf the evbuffer to make contiguous + @param size the number of bytes to make contiguous, or -1 to make the + entire buffer contiguous. + @return a pointer to the contiguous memory array, or NULL if param size + requested more data than is present in the buffer. +*/ + +EVENT2_EXPORT_SYMBOL +unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size); + +/** + Prepends data to the beginning of the evbuffer + + @param buf the evbuffer to which to prepend data + @param data a pointer to the memory to prepend + @param size the number of bytes to prepend + @return 0 if successful, or -1 otherwise +*/ + +EVENT2_EXPORT_SYMBOL +int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); + +/** + Prepends all data from the src evbuffer to the beginning of the dst + evbuffer. + + @param dst the evbuffer to which to prepend data + @param src the evbuffer to prepend; it will be emptied as a result + @return 0 if successful, or -1 otherwise +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src); + +/** + Prevent calls that modify an evbuffer from succeeding. A buffer may + frozen at the front, at the back, or at both the front and the back. + + If the front of a buffer is frozen, operations that drain data from + the front of the buffer, or that prepend data to the buffer, will + fail until it is unfrozen. If the back a buffer is frozen, operations + that append data from the buffer will fail until it is unfrozen. + + @param buf The buffer to freeze + @param at_front If true, we freeze the front of the buffer. If false, + we freeze the back. + @return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_freeze(struct evbuffer *buf, int at_front); +/** + Re-enable calls that modify an evbuffer. + + @param buf The buffer to un-freeze + @param at_front If true, we unfreeze the front of the buffer. If false, + we unfreeze the back. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_unfreeze(struct evbuffer *buf, int at_front); + +struct event_base; +/** + Force all the callbacks on an evbuffer to be run, not immediately after + the evbuffer is altered, but instead from inside the event loop. + + This can be used to serialize all the callbacks to a single thread + of execution. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base); + +/** + Append data from 1 or more iovec's to an evbuffer + + Calculates the number of bytes needed for an iovec structure and guarantees + all data will fit into a single chain. Can be used in lieu of functionality + which calls evbuffer_add() constantly before being used to increase + performance. + + @param buffer the destination buffer + @param vec the source iovec + @param n_vec the number of iovec structures. + @return the number of bytes successfully written to the output buffer. +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_add_iovec(struct evbuffer * buffer, struct evbuffer_iovec * vec, int n_vec); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFER_H_INCLUDED_ */ diff --git a/include/libevent/event2/buffer_compat.h b/include/libevent/event2/buffer_compat.h new file mode 100644 index 0000000..c4092bf --- /dev/null +++ b/include/libevent/event2/buffer_compat.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ + +#ifndef EVENT2_BUFFER_COMPAT_H_INCLUDED_ +#define EVENT2_BUFFER_COMPAT_H_INCLUDED_ + +#include + +/** @file event2/buffer_compat.h + + @brief Obsolete and deprecated versions of the functions in buffer.h: provided + only for backward compatibility. + */ + + +/** + Obsolete alias for evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY). + + @deprecated This function is deprecated because its behavior is not correct + for almost any protocol, and also because it's wholly subsumed by + evbuffer_readln(). + + @param buffer the evbuffer to read from + @return pointer to a single line, or NULL if an error occurred + +*/ +EVENT2_EXPORT_SYMBOL +char *evbuffer_readline(struct evbuffer *buffer); + +/** Type definition for a callback that is invoked whenever data is added or + removed from an evbuffer. + + An evbuffer may have one or more callbacks set at a time. The order + in which they are executed is undefined. + + A callback function may add more callbacks, or remove itself from the + list of callbacks, or add or remove data from the buffer. It may not + remove another callback from the list. + + If a callback adds or removes data from the buffer or from another + buffer, this can cause a recursive invocation of your callback or + other callbacks. If you ask for an infinite loop, you might just get + one: watch out! + + @param buffer the buffer whose size has changed + @param old_len the previous length of the buffer + @param new_len the current length of the buffer + @param arg a pointer to user data +*/ +typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len, void *arg); + +/** + Replace all callbacks on an evbuffer with a single new callback, or + remove them. + + Subsequent calls to evbuffer_setcb() replace callbacks set by previous + calls. Setting the callback to NULL removes any previously set callback. + + @deprecated This function is deprecated because it clears all previous + callbacks set on the evbuffer, which can cause confusing behavior if + multiple parts of the code all want to add their own callbacks on a + buffer. Instead, use evbuffer_add(), evbuffer_del(), and + evbuffer_setflags() to manage your own evbuffer callbacks without + interfering with callbacks set by others. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified, + or NULL to remove all callbacks. + @param cbarg an argument to be provided to the callback function + @return 0 if successful, or -1 on error + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg); + + +/** + Find a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @return a pointer to the beginning of the search string, or NULL if the search failed. + */ +EVENT2_EXPORT_SYMBOL +unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len); + +/** deprecated in favor of calling the functions directly */ +#define EVBUFFER_LENGTH(x) evbuffer_get_length(x) +/** deprecated in favor of calling the functions directly */ +#define EVBUFFER_DATA(x) evbuffer_pullup((x), -1) + +#endif + diff --git a/include/libevent/event2/bufferevent.h b/include/libevent/event2/bufferevent.h new file mode 100644 index 0000000..a50944f --- /dev/null +++ b/include/libevent/event2/bufferevent.h @@ -0,0 +1,1061 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_BUFFEREVENT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_H_INCLUDED_ + +/** + @file event2/bufferevent.h + + @brief Functions for buffering data for network sending or receiving. + + Bufferevents are higher level than evbuffers: each has an underlying evbuffer for reading + and one for writing, and callbacks that are invoked under certain + circumstances. + + A bufferevent provides input and output buffers that get filled and + drained automatically. The user of a bufferevent no longer deals + directly with the I/O, but instead is reading from input and writing + to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly + with bufferevent_enable() and bufferevent_disable(). + + When reading is enabled, the bufferevent will try to read from the + file descriptor onto its input buffer, and call the read callback. + When writing is enabled, the bufferevent will try to write data onto its + file descriptor when the output buffer has enough data, and call the write + callback when the output buffer is sufficiently drained. + + Bufferevents come in several flavors, including: + +
+
Socket-based bufferevents
+
A bufferevent that reads and writes data onto a network + socket. Created with bufferevent_socket_new().
+ +
Paired bufferevents
+
A pair of bufferevents that send and receive data to one + another without touching the network. Created with + bufferevent_pair_new().
+ +
Filtering bufferevents
+
A bufferevent that transforms data, and sends or receives it + over another underlying bufferevent. Created with + bufferevent_filter_new().
+ +
SSL-backed bufferevents
+
A bufferevent that uses the openssl library to send and + receive data over an encrypted connection. Created with + bufferevent_openssl_socket_new() or + bufferevent_openssl_filter_new().
+
+ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** @name Bufferevent event codes + + These flags are passed as arguments to a bufferevent's event callback. + + @{ +*/ +#define BEV_EVENT_READING 0x01 /**< error encountered while reading */ +#define BEV_EVENT_WRITING 0x02 /**< error encountered while writing */ +#define BEV_EVENT_EOF 0x10 /**< eof file reached */ +#define BEV_EVENT_ERROR 0x20 /**< unrecoverable error encountered */ +#define BEV_EVENT_TIMEOUT 0x40 /**< user-specified timeout reached */ +#define BEV_EVENT_CONNECTED 0x80 /**< connect operation finished. */ +/**@}*/ + +/** + An opaque type for handling buffered IO + + @see event2/bufferevent.h + */ +struct bufferevent +#ifdef EVENT_IN_DOXYGEN_ +{} +#endif +; +struct event_base; +struct evbuffer; +struct sockaddr; + +/** + A read or write callback for a bufferevent. + + The read callback is triggered when new data arrives in the input + buffer and the amount of readable data exceed the low watermark + which is 0 by default. + + The write callback is triggered if the write buffer has been + exhausted or fell below its low watermark. + + @param bev the bufferevent that triggered the callback + @param ctx the user-specified context for this bufferevent + */ +typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx); + +/** + An event/error callback for a bufferevent. + + The event callback is triggered if either an EOF condition or another + unrecoverable error was encountered. + + For bufferevents with deferred callbacks, this is a bitwise OR of all errors + that have happened on the bufferevent since the last callback invocation. + + @param bev the bufferevent for which the error condition was reached + @param what a conjunction of flags: BEV_EVENT_READING or BEV_EVENT_WRITING + to indicate if the error was encountered on the read or write path, + and one of the following flags: BEV_EVENT_EOF, BEV_EVENT_ERROR, + BEV_EVENT_TIMEOUT, BEV_EVENT_CONNECTED. + + @param ctx the user-specified context for this bufferevent +*/ +typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx); + +/** Options that can be specified when creating a bufferevent */ +enum bufferevent_options { + /** If set, we close the underlying file + * descriptor/bufferevent/whatever when this bufferevent is freed. */ + BEV_OPT_CLOSE_ON_FREE = (1<<0), + + /** If set, and threading is enabled, operations on this bufferevent + * are protected by a lock */ + BEV_OPT_THREADSAFE = (1<<1), + + /** If set, callbacks are run deferred in the event loop. */ + BEV_OPT_DEFER_CALLBACKS = (1<<2), + + /** If set, callbacks are executed without locks being held on the + * bufferevent. This option currently requires that + * BEV_OPT_DEFER_CALLBACKS also be set; a future version of Libevent + * might remove the requirement.*/ + BEV_OPT_UNLOCK_CALLBACKS = (1<<3) +}; + +/** + Create a new socket bufferevent over an existing socket. + + @param base the event base to associate with the new bufferevent. + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + It is safe to set the fd to -1, so long as you later + set it with bufferevent_setfd or bufferevent_socket_connect(). + @param options Zero or more BEV_OPT_* flags + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_free() + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options); + +/** + Launch a connect() attempt with a socket-based bufferevent. + + When the connect succeeds, the eventcb will be invoked with + BEV_EVENT_CONNECTED set. + + If the bufferevent does not already have a socket set, we allocate a new + socket here and make it nonblocking before we begin. + + If no address is provided, we assume that the socket is already connecting, + and configure the bufferevent so that a BEV_EVENT_CONNECTED event will be + yielded when it is done connecting. + + @param bufev an existing bufferevent allocated with + bufferevent_socket_new(). + @param addr the address we should connect to + @param socklen The length of the address + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect(struct bufferevent *bufev, const struct sockaddr *addr, int socklen); + +struct evdns_base; +/** + Resolve the hostname 'hostname' and connect to it as with + bufferevent_socket_connect(). + + @param bufev An existing bufferevent allocated with bufferevent_socket_new() + @param evdns_base Optionally, an evdns_base to use for resolving hostnames + asynchronously. May be set to NULL for a blocking resolve. + @param family A preferred address family to resolve addresses to, or + AF_UNSPEC for no preference. Only AF_INET, AF_INET6, and AF_UNSPEC are + supported. + @param hostname The hostname to resolve; see below for notes on recognized + formats + @param port The port to connect to on the resolved address. + @return 0 if successful, -1 on failure. + + @see bufferevent_socket_connect_hostname_hints() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect_hostname(struct bufferevent *bufev, + struct evdns_base *evdns_base, int family, const char *hostname, int port); + +/** + Resolve the hostname 'hostname' and connect to it as with + bufferevent_socket_connect(). + + @param bufev An existing bufferevent allocated with bufferevent_socket_new() + @param evdns_base Optionally, an evdns_base to use for resolving hostnames + asynchronously. May be set to NULL for a blocking resolve. + @param hints_in points to an addrinfo structure that specifies criteria for + selecting the socket address structures to be used + @param hostname The hostname to resolve; see below for notes on recognized + formats + @param port The port to connect to on the resolved address. + @return 0 if successful, -1 on failure. + + Recognized hostname formats are: + + www.example.com (hostname) + 1.2.3.4 (ipv4address) + ::1 (ipv6address) + [::1] ([ipv6address]) + + Performance note: If you do not provide an evdns_base, this function + may block while it waits for a DNS response. This is probably not + what you want. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect_hostname_hints(struct bufferevent *bufev, + struct evdns_base *evdns_base, const struct evutil_addrinfo *hints_in, const char *hostname, int port); + + +/** + Return the error code for the last failed DNS lookup attempt made by + bufferevent_socket_connect_hostname(). + + @param bev The bufferevent object. + @return DNS error code. + @see evutil_gai_strerror() +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_get_dns_error(struct bufferevent *bev); + +/** + Assign a bufferevent to a specific event_base. + + NOTE that only socket bufferevents support this function. + + @param base an event_base returned by event_init() + @param bufev a bufferevent struct returned by bufferevent_new() + or bufferevent_socket_new() + @return 0 if successful, or -1 if an error occurred + @see bufferevent_new() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); + +/** + Return the event_base used by a bufferevent +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *bufferevent_get_base(struct bufferevent *bev); + +/** + Assign a priority to a bufferevent. + + Only supported for socket bufferevents. + + @param bufev a bufferevent struct + @param pri the priority to be assigned + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_priority_set(struct bufferevent *bufev, int pri); + +/** + Return the priority of a bufferevent. + + Only supported for socket bufferevents + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_get_priority(const struct bufferevent *bufev); + +/** + Deallocate the storage associated with a bufferevent structure. + + If there is pending data to write on the bufferevent, it probably won't be + flushed before the bufferevent is freed. + + @param bufev the bufferevent structure to be freed. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_free(struct bufferevent *bufev); + + +/** + Changes the callbacks for a bufferevent. + + @param bufev the bufferevent object for which to change callbacks + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param eventcb callback to invoke when there is an event on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @see bufferevent_new() + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_setcb(struct bufferevent *bufev, + bufferevent_data_cb readcb, bufferevent_data_cb writecb, + bufferevent_event_cb eventcb, void *cbarg); + +/** + Retrieves the callbacks for a bufferevent. + + @param bufev the bufferevent to examine. + @param readcb_ptr if readcb_ptr is nonnull, *readcb_ptr is set to the current + read callback for the bufferevent. + @param writecb_ptr if writecb_ptr is nonnull, *writecb_ptr is set to the + current write callback for the bufferevent. + @param eventcb_ptr if eventcb_ptr is nonnull, *eventcb_ptr is set to the + current event callback for the bufferevent. + @param cbarg_ptr if cbarg_ptr is nonnull, *cbarg_ptr is set to the current + callback argument for the bufferevent. + @see buffervent_setcb() +*/ +EVENT2_EXPORT_SYMBOL +void bufferevent_getcb(struct bufferevent *bufev, + bufferevent_data_cb *readcb_ptr, + bufferevent_data_cb *writecb_ptr, + bufferevent_event_cb *eventcb_ptr, + void **cbarg_ptr); + +/** + Changes the file descriptor on which the bufferevent operates. + Not supported for all bufferevent types. + + @param bufev the bufferevent object for which to change the file descriptor + @param fd the file descriptor to operate on +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd); + +/** + Replaces the file descriptor on which the bufferevent operates. + Not supported for all bufferevent types. + + Unlike bufferevent_setfd() it will close previous file descriptor (if any). + + @param bufev the bufferevent object for which to change the file descriptor + @param fd the file descriptor to operate on +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_replacefd(struct bufferevent *bufev, evutil_socket_t fd); + +/** + Returns the file descriptor associated with a bufferevent, or -1 if + no file descriptor is associated with the bufferevent. + */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t bufferevent_getfd(struct bufferevent *bufev); + +/** + Returns the underlying bufferevent associated with a bufferevent (if + the bufferevent is a wrapper), or NULL if there is no underlying bufferevent. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev); + +/** + Write data to a bufferevent buffer. + + The bufferevent_write() function can be used to write data to the file + descriptor. The data is appended to the output buffer and written to the + descriptor automatically as it becomes available for writing. + + @param bufev the bufferevent to be written to + @param data a pointer to the data to be written + @param size the length of the data, in bytes + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write_buffer() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_write(struct bufferevent *bufev, + const void *data, size_t size); + + +/** + Write data from an evbuffer to a bufferevent buffer. The evbuffer is + being drained as a result. + + @param bufev the bufferevent to be written to + @param buf the evbuffer to be written + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf); + + +/** + Read data from a bufferevent buffer. + + The bufferevent_read() function is used to read data from the input buffer. + + @param bufev the bufferevent to be read from + @param data pointer to a buffer that will store the data + @param size the size of the data buffer, in bytes + @return the amount of data read, in bytes. If 0 is returned, it is possible + that there is no data in the buffer or that the read failed. + */ +EVENT2_EXPORT_SYMBOL +size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); + +/** + Read data from a bufferevent buffer into an evbuffer. This avoids + memory copies. + + @param bufev the bufferevent to be read from + @param buf the evbuffer to which to add data + @return 0 if successful, or -1 if an error occurred. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_read_buffer(struct bufferevent *bufev, struct evbuffer *buf); + +/** + Returns the input buffer. + + The user MUST NOT set the callback on this buffer. + + @param bufev the bufferevent from which to get the evbuffer + @return the evbuffer object for the input buffer + */ + +EVENT2_EXPORT_SYMBOL +struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); + +/** + Returns the output buffer. + + The user MUST NOT set the callback on this buffer. + + When filters are being used, the filters need to be manually + triggered if the output buffer was manipulated. + + @param bufev the bufferevent from which to get the evbuffer + @return the evbuffer object for the output buffer + */ + +EVENT2_EXPORT_SYMBOL +struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); + +/** + Enable a bufferevent. + + @param bufev the bufferevent to be enabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_disable() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_enable(struct bufferevent *bufev, short event); + +/** + Disable a bufferevent. + + @param bufev the bufferevent to be disabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_enable() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_disable(struct bufferevent *bufev, short event); + +/** + Return the events that are enabled on a given bufferevent. + + @param bufev the bufferevent to inspect + @return A combination of EV_READ | EV_WRITE + */ +EVENT2_EXPORT_SYMBOL +short bufferevent_get_enabled(struct bufferevent *bufev); + +/** + Set the read and write timeout for a bufferevent. + + A bufferevent's timeout will fire the first time that the indicated + amount of time has elapsed since a successful read or write operation, + during which the bufferevent was trying to read or write. + + (In other words, if reading or writing is disabled, or if the + bufferevent's read or write operation has been suspended because + there's no data to write, or not enough bandwidth, or so on, the + timeout isn't active. The timeout only becomes active when we we're + willing to actually read or write.) + + Calling bufferevent_enable or setting a timeout for a bufferevent + whose timeout is already pending resets its timeout. + + If the timeout elapses, the corresponding operation (EV_READ or + EV_WRITE) becomes disabled until you re-enable it again. The + bufferevent's event callback is called with the + BEV_EVENT_TIMEOUT|BEV_EVENT_READING or + BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout, or NULL + @param timeout_write the write timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_timeouts(struct bufferevent *bufev, + const struct timeval *timeout_read, const struct timeval *timeout_write); + +/** + Sets the watermarks for read and write events. + + On input, a bufferevent does not invoke the user read callback unless + there is at least low watermark data in the buffer. If the read buffer + is beyond the high watermark, the bufferevent stops reading from the network. + But be aware that bufferevent input/read buffer can overrun high watermark + limit (typical example is openssl bufferevent), so you should not relay in + this. + + On output, the user write callback is invoked whenever the buffered data + falls below the low watermark. Filters that write to this bufev will try + not to write more bytes to this buffer than the high watermark would allow, + except when flushing. + + @param bufev the bufferevent to be modified + @param events EV_READ, EV_WRITE or both + @param lowmark the lower watermark to set + @param highmark the high watermark to set +*/ + +EVENT2_EXPORT_SYMBOL +void bufferevent_setwatermark(struct bufferevent *bufev, short events, + size_t lowmark, size_t highmark); + +/** + Retrieves the watermarks for read or write events. + Returns non-zero if events contains not only EV_READ or EV_WRITE. + Returns zero if events equal EV_READ or EV_WRITE + + @param bufev the bufferevent to be examined + @param events EV_READ or EV_WRITE + @param lowmark receives the lower watermark if not NULL + @param highmark receives the high watermark if not NULL +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_getwatermark(struct bufferevent *bufev, short events, + size_t *lowmark, size_t *highmark); + +/** + Acquire the lock on a bufferevent. Has no effect if locking was not + enabled with BEV_OPT_THREADSAFE. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_lock(struct bufferevent *bufev); + +/** + Release the lock on a bufferevent. Has no effect if locking was not + enabled with BEV_OPT_THREADSAFE. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_unlock(struct bufferevent *bufev); + + +/** + * Public interface to manually increase the reference count of a bufferevent + * this is useful in situations where a user may reference the bufferevent + * somewhere else (unknown to libevent) + * + * @param bufev the bufferevent to increase the refcount on + * + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_incref(struct bufferevent *bufev); + +/** + * Public interface to manually decrement the reference count of a bufferevent + * + * Warning: make sure you know what you're doing. This is mainly used in + * conjunction with bufferevent_incref(). This will free up all data associated + * with a bufferevent if the reference count hits 0. + * + * @param bufev the bufferevent to decrement the refcount on + * + * @return 1 if the bufferevent was freed, otherwise 0 (still referenced) + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decref(struct bufferevent *bufev); + +/** + Flags that can be passed into filters to let them know how to + deal with the incoming data. +*/ +enum bufferevent_flush_mode { + /** usually set when processing data */ + BEV_NORMAL = 0, + + /** want to checkpoint all data sent. */ + BEV_FLUSH = 1, + + /** encountered EOF on read or done sending data */ + BEV_FINISHED = 2 +}; + +/** + Triggers the bufferevent to produce more data if possible. + + @param bufev the bufferevent object + @param iotype either EV_READ or EV_WRITE or both. + @param mode either BEV_NORMAL or BEV_FLUSH or BEV_FINISHED + @return -1 on failure, 0 if no data was produces, 1 if data was produced + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_flush(struct bufferevent *bufev, + short iotype, + enum bufferevent_flush_mode mode); + +/** + Flags for bufferevent_trigger(_event) that modify when and how to trigger + the callback. +*/ +enum bufferevent_trigger_options { + /** trigger the callback regardless of the watermarks */ + BEV_TRIG_IGNORE_WATERMARKS = (1<<16), + + /** defer even if the callbacks are not */ + BEV_TRIG_DEFER_CALLBACKS = BEV_OPT_DEFER_CALLBACKS + + /* (Note: for internal reasons, these need to be disjoint from + * bufferevent_options, except when they mean the same thing. */ +}; + +/** + Triggers bufferevent data callbacks. + + The function will honor watermarks unless options contain + BEV_TRIG_IGNORE_WATERMARKS. If the options contain BEV_OPT_DEFER_CALLBACKS, + the callbacks are deferred. + + @param bufev the bufferevent object + @param iotype either EV_READ or EV_WRITE or both. + @param options + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_trigger(struct bufferevent *bufev, short iotype, + int options); + +/** + Triggers the bufferevent event callback. + + If the options contain BEV_OPT_DEFER_CALLBACKS, the callbacks are deferred. + + @param bufev the bufferevent object + @param what the flags to pass onto the event callback + @param options + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_trigger_event(struct bufferevent *bufev, short what, + int options); + +/** + @name Filtering support + + @{ +*/ +/** + Values that filters can return. + */ +enum bufferevent_filter_result { + /** everything is okay */ + BEV_OK = 0, + + /** the filter needs to read more data before output */ + BEV_NEED_MORE = 1, + + /** the filter encountered a critical error, no further data + can be processed. */ + BEV_ERROR = 2 +}; + +/** A callback function to implement a filter for a bufferevent. + + @param src An evbuffer to drain data from. + @param dst An evbuffer to add data to. + @param limit A suggested upper bound of bytes to write to dst. + The filter may ignore this value, but doing so means that + it will overflow the high-water mark associated with dst. + -1 means "no limit". + @param mode Whether we should write data as may be convenient + (BEV_NORMAL), or flush as much data as we can (BEV_FLUSH), + or flush as much as we can, possibly including an end-of-stream + marker (BEV_FINISH). + @param ctx A user-supplied pointer. + + @return BEV_OK if we wrote some data; BEV_NEED_MORE if we can't + produce any more output until we get some input; and BEV_ERROR + on an error. + */ +typedef enum bufferevent_filter_result (*bufferevent_filter_cb)( + struct evbuffer *src, struct evbuffer *dst, ev_ssize_t dst_limit, + enum bufferevent_flush_mode mode, void *ctx); + +/** + Allocate a new filtering bufferevent on top of an existing bufferevent. + + @param underlying the underlying bufferevent. + @param input_filter The filter to apply to data we read from the underlying + bufferevent + @param output_filter The filer to apply to data we write to the underlying + bufferevent + @param options A bitfield of bufferevent options. + @param free_context A function to use to free the filter context when + this bufferevent is freed. + @param ctx A context pointer to pass to the filter functions. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_filter_new(struct bufferevent *underlying, + bufferevent_filter_cb input_filter, + bufferevent_filter_cb output_filter, + int options, + void (*free_context)(void *), + void *ctx); +/**@}*/ + +/** + Allocate a pair of linked bufferevents. The bufferevents behave as would + two bufferevent_sock instances connected to opposite ends of a + socketpair(), except that no internal socketpair is allocated. + + @param base The event base to associate with the socketpair. + @param options A set of options for this bufferevent + @param pair A pointer to an array to hold the two new bufferevent objects. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_pair_new(struct event_base *base, int options, + struct bufferevent *pair[2]); + +/** + Given one bufferevent returned by bufferevent_pair_new(), returns the + other one if it still exists. Otherwise returns NULL. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev); + +/** + Abstract type used to configure rate-limiting on a bufferevent or a group + of bufferevents. + */ +struct ev_token_bucket_cfg; + +/** + A group of bufferevents which are configured to respect the same rate + limit. +*/ +struct bufferevent_rate_limit_group; + +/** Maximum configurable rate- or burst-limit. */ +#define EV_RATE_LIMIT_MAX EV_SSIZE_MAX + +/** + Initialize and return a new object to configure the rate-limiting behavior + of bufferevents. + + @param read_rate The maximum number of bytes to read per tick on + average. + @param read_burst The maximum number of bytes to read in any single tick. + @param write_rate The maximum number of bytes to write per tick on + average. + @param write_burst The maximum number of bytes to write in any single tick. + @param tick_len The length of a single tick. Defaults to one second. + Any fractions of a millisecond are ignored. + + Note that all rate-limits hare are currently best-effort: future versions + of Libevent may implement them more tightly. + */ +EVENT2_EXPORT_SYMBOL +struct ev_token_bucket_cfg *ev_token_bucket_cfg_new( + size_t read_rate, size_t read_burst, + size_t write_rate, size_t write_burst, + const struct timeval *tick_len); + +/** Free all storage held in 'cfg'. + + Note: 'cfg' is not currently reference-counted; it is not safe to free it + until no bufferevent is using it. + */ +EVENT2_EXPORT_SYMBOL +void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg); + +/** + Set the rate-limit of a the bufferevent 'bev' to the one specified in + 'cfg'. If 'cfg' is NULL, disable any per-bufferevent rate-limiting on + 'bev'. + + Note that only some bufferevent types currently respect rate-limiting. + They are: socket-based bufferevents (normal and IOCP-based), and SSL-based + bufferevents. + + Return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_rate_limit(struct bufferevent *bev, + struct ev_token_bucket_cfg *cfg); + +/** + Create a new rate-limit group for bufferevents. A rate-limit group + constrains the maximum number of bytes sent and received, in toto, + by all of its bufferevents. + + @param base An event_base to run any necessary timeouts for the group. + Note that all bufferevents in the group do not necessarily need to share + this event_base. + @param cfg The rate-limit for this group. + + Note that all rate-limits hare are currently best-effort: future versions + of Libevent may implement them more tightly. + + Note also that only some bufferevent types currently respect rate-limiting. + They are: socket-based bufferevents (normal and IOCP-based), and SSL-based + bufferevents. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new( + struct event_base *base, + const struct ev_token_bucket_cfg *cfg); +/** + Change the rate-limiting settings for a given rate-limiting group. + + Return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_set_cfg( + struct bufferevent_rate_limit_group *, + const struct ev_token_bucket_cfg *); + +/** + Change the smallest quantum we're willing to allocate to any single + bufferevent in a group for reading or writing at a time. + + The rationale is that, because of TCP/IP protocol overheads and kernel + behavior, if a rate-limiting group is so tight on bandwidth that you're + only willing to send 1 byte per tick per bufferevent, you might instead + want to batch up the reads and writes so that you send N bytes per + 1/N of the bufferevents (chosen at random) each tick, so you still wind + up send 1 byte per tick per bufferevent on average, but you don't send + so many tiny packets. + + The default min-share is currently 64 bytes. + + Returns 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_set_min_share( + struct bufferevent_rate_limit_group *, size_t); + +/** + Free a rate-limiting group. The group must have no members when + this function is called. +*/ +EVENT2_EXPORT_SYMBOL +void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *); + +/** + Add 'bev' to the list of bufferevents whose aggregate reading and writing + is restricted by 'g'. If 'g' is NULL, remove 'bev' from its current group. + + A bufferevent may belong to no more than one rate-limit group at a time. + If 'bev' is already a member of a group, it will be removed from its old + group before being added to 'g'. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_add_to_rate_limit_group(struct bufferevent *bev, + struct bufferevent_rate_limit_group *g); + +/** Remove 'bev' from its current rate-limit group (if any). */ +EVENT2_EXPORT_SYMBOL +int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev); + +/** + Set the size limit for single read operation. + + Set to 0 for a reasonable default. + + Return 0 on success and -1 on failure. + + @see evbuffer_set_max_read() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size); + +/** + Set the size limit for single write operation. + + Set to 0 for a reasonable default. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size); + +/** Get the current size limit for single read operation. */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev); + +/** Get the current size limit for single write operation. */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev); + +/** + @name Rate limit inspection + + Return the current read or write bucket size for a bufferevent. + If it is not configured with a per-bufferevent ratelimit, return + EV_SSIZE_MAX. This function does not inspect the group limit, if any. + Note that it can return a negative value if the bufferevent has been + made to read or write more than its limit. + + @{ + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_write_limit(struct bufferevent *bev); +/*@}*/ + +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_to_read(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_to_write(struct bufferevent *bev); + +EVENT2_EXPORT_SYMBOL +const struct ev_token_bucket_cfg *bufferevent_get_token_bucket_cfg(const struct bufferevent * bev); + +/** + @name Group Rate limit inspection + + Return the read or write bucket size for a bufferevent rate limit + group. Note that it can return a negative value if bufferevents in + the group have been made to read or write more than their limits. + + @{ + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_rate_limit_group_get_read_limit( + struct bufferevent_rate_limit_group *); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_rate_limit_group_get_write_limit( + struct bufferevent_rate_limit_group *); +/*@}*/ + +/** + @name Rate limit manipulation + + Subtract a number of bytes from a bufferevent's read or write bucket. + The decrement value can be negative, if you want to manually refill + the bucket. If the change puts the bucket above or below zero, the + bufferevent will resume or suspend reading writing as appropriate. + These functions make no change in the buckets for the bufferevent's + group, if any. + + Returns 0 on success, -1 on internal error. + + @{ + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr); +EVENT2_EXPORT_SYMBOL +int bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr); +/*@}*/ + +/** + @name Group rate limit manipulation + + Subtract a number of bytes from a bufferevent rate-limiting group's + read or write bucket. The decrement value can be negative, if you + want to manually refill the bucket. If the change puts the bucket + above or below zero, the bufferevents in the group will resume or + suspend reading writing as appropriate. + + Returns 0 on success, -1 on internal error. + + @{ + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_decrement_read( + struct bufferevent_rate_limit_group *, ev_ssize_t); +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_decrement_write( + struct bufferevent_rate_limit_group *, ev_ssize_t); +/*@}*/ + + +/** + * Inspect the total bytes read/written on a group. + * + * Set the variable pointed to by total_read_out to the total number of bytes + * ever read on grp, and the variable pointed to by total_written_out to the + * total number of bytes ever written on grp. */ +EVENT2_EXPORT_SYMBOL +void bufferevent_rate_limit_group_get_totals( + struct bufferevent_rate_limit_group *grp, + ev_uint64_t *total_read_out, ev_uint64_t *total_written_out); + +/** + * Reset the total bytes read/written on a group. + * + * Reset the number of bytes read or written on grp as given by + * bufferevent_rate_limit_group_reset_totals(). */ +EVENT2_EXPORT_SYMBOL +void +bufferevent_rate_limit_group_reset_totals( + struct bufferevent_rate_limit_group *grp); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_H_INCLUDED_ */ diff --git a/include/libevent/event2/bufferevent_compat.h b/include/libevent/event2/bufferevent_compat.h new file mode 100644 index 0000000..e071322 --- /dev/null +++ b/include/libevent/event2/bufferevent_compat.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2007-2012 Niels Provos, Nick Mathewson + * Copyright (c) 2000-2007 Niels Provos + * All rights reserved. + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_BUFFEREVENT_COMPAT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_COMPAT_H_INCLUDED_ + +/** @file event2/bufferevent_compat.h + * + * @brief Deprecated versions of the functions in bufferevent.h: provided + * only for backwards compatibility. + */ + +#include + +#define evbuffercb bufferevent_data_cb +#define everrorcb bufferevent_event_cb + +/** + Create a new bufferevent for an fd. + + This function is deprecated. Use bufferevent_socket_new and + bufferevent_set_callbacks instead. + + Libevent provides an abstraction on top of the regular event callbacks. + This abstraction is called a buffered event. A buffered event provides + input and output buffers that get filled and drained automatically. The + user of a buffered event no longer deals directly with the I/O, but + instead is reading from input and writing to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly with + bufferevent_enable() and bufferevent_disable(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + If multiple bases are in use, bufferevent_base_set() must be called before + enabling the bufferevent for the first time. + + @deprecated This function is deprecated because it uses the current + event base, and as such can be error prone for multithreaded programs. + Use bufferevent_socket_new() instead. + + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param errorcb callback to invoke when there is an error on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_base_set(), bufferevent_free() + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_new(evutil_socket_t fd, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); + + +/** + Set the read and write timeout for a buffered event. + + @deprecated Use bufferevent_set_timeouts instead. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout + @param timeout_write the write timeout + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_settimeout(struct bufferevent *bufev, + int timeout_read, int timeout_write); + +#define EVBUFFER_READ BEV_EVENT_READING +#define EVBUFFER_WRITE BEV_EVENT_WRITING +#define EVBUFFER_EOF BEV_EVENT_EOF +#define EVBUFFER_ERROR BEV_EVENT_ERROR +#define EVBUFFER_TIMEOUT BEV_EVENT_TIMEOUT + +/** macro for getting access to the input buffer of a bufferevent */ +#define EVBUFFER_INPUT(x) bufferevent_get_input(x) +/** macro for getting access to the output buffer of a bufferevent */ +#define EVBUFFER_OUTPUT(x) bufferevent_get_output(x) + +#endif diff --git a/include/libevent/event2/bufferevent_ssl.h b/include/libevent/event2/bufferevent_ssl.h new file mode 100644 index 0000000..009de2b --- /dev/null +++ b/include/libevent/event2/bufferevent_ssl.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ + +/** @file event2/bufferevent_ssl.h + + @brief OpenSSL support for bufferevents. + */ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The state of an SSL object to be used when creating a new + SSL bufferevent. + */ +enum bufferevent_ssl_state { + BUFFEREVENT_SSL_OPEN = 0, + BUFFEREVENT_SSL_CONNECTING = 1, + BUFFEREVENT_SSL_ACCEPTING = 2 +}; + +/** Control how to report dirty SSL shutdowns. + + If the peer (or the network, or an attacker) closes the TCP + connection before closing the SSL channel, and the protocol is SSL >= v3, + this is a "dirty" shutdown. If BUFFEREVENT_SSL_DIRTY_SHUTDOWN is not set + (default), this is reported as BEV_EVENT_ERROR. + + If instead BUFFEREVENT_SSL_DIRTY_SHUTDOWN is set, a dirty shutdown is + reported as BEV_EVENT_EOF. + + (Note that if the protocol is < SSLv3, you will always receive + BEV_EVENT_EOF, since SSL 2 and earlier cannot distinguish a secure + connection close from a dirty one. This is one reason (among many) + not to use SSL 2.) +*/ +#define BUFFEREVENT_SSL_DIRTY_SHUTDOWN 1 +/** Control writes in the SSL bufferevents. + + By default SSL bufferevent will peek bytes from the buffer as the arrived. + with respect to the segment boundaries in the buffer. + However, by ignoring these segment boundaries number of packets to send + can be decreased. + + This flags will ignore the segment boundaries. + + Useful in conjunction with http layer. +*/ +#define BUFFEREVENT_SSL_BATCH_WRITE 2 + +#if defined(EVENT__HAVE_OPENSSL) || defined(EVENT__HAVE_MBEDTLS) +/** + * Get flags of the SSL bufferevent. + * + * @see BUFFEREVENT_SSL_* + * @return flags or SIZE_MAX in case of error (if bufferevent is not SSL). + */ +EVENT2_EXPORT_SYMBOL +ev_uint64_t bufferevent_ssl_get_flags(struct bufferevent *bev); +/** Change the flags that are set for an ssl bufferevent by adding more. + * + * @param bev the ssl bufferevent. + * @param flags One or more BUFFEREVENT_SSL_* options + * @return old flags success, EV_UINT64_MAX on failure. + */ +EVENT2_EXPORT_SYMBOL +ev_uint64_t bufferevent_ssl_set_flags(struct bufferevent *bev, ev_uint64_t flags); +/** Change the flags that are set for an ssl bufferevent by removing some. + * + * @param bev the bufferevent. + * @param flags One or more BUFFEREVENT_SSL_* options + * @return old flags success, EV_UINT64_MAX on failure. + */ +EVENT2_EXPORT_SYMBOL +ev_uint64_t bufferevent_ssl_clear_flags(struct bufferevent *bev, ev_uint64_t flags); + +#endif /* defined(EVENT__HAVE_OPENSSL) || defined(EVENT__HAVE_MBEDTLS) */ + +#if defined(EVENT__HAVE_OPENSSL) || defined(EVENT_IN_DOXYGEN_) +/* This is what openssl's SSL objects are underneath. */ +struct ssl_st; + +/** + Create a new SSL bufferevent to send its data over another bufferevent. + + @param base An event_base to use to detect reading and writing. It + must also be the base for the underlying bufferevent. + @param underlying A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_openssl_filter_new(struct event_base *base, + struct bufferevent *underlying, + struct ssl_st *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + Create a new SSL bufferevent to send its data over an SSL * on a socket. + + @param base An event_base to use to detect reading and writing + @param fd A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure. +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_openssl_socket_new(struct event_base *base, + evutil_socket_t fd, + struct ssl_st *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + * Get value of the BUFFEREVENT_SSL_DIRTY_SHUTDOWN flag. + * + * @see BUFFEREVENT_SSL_DIRTY_SHUTDOWN + * @deprecated This function is deprecated, use bufferevent_ssl_get_flags() instead. + * @see bufferevent_ssl_get_flags() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev); +/** + * Set value of the BUFFEREVENT_SSL_DIRTY_SHUTDOWN flag. + * + * @see BUFFEREVENT_SSL_DIRTY_SHUTDOWN + * @deprecated This function is deprecated, use bufferevent_ssl_set_flags() instead. + * @see bufferevent_ssl_set_flags() + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, + int allow_dirty_shutdown); + +/** Return the underlying openssl SSL * object for an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +struct ssl_st * +bufferevent_openssl_get_ssl(struct bufferevent *bufev); + +/** Tells a bufferevent to begin SSL renegotiation. */ +EVENT2_EXPORT_SYMBOL +int bufferevent_ssl_renegotiate(struct bufferevent *bev); + +/** Return the most recent OpenSSL error reported on an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +unsigned long bufferevent_get_openssl_error(struct bufferevent *bev); + +#endif +#if defined(EVENT__HAVE_MBEDTLS) || defined(EVENT_IN_DOXYGEN_) +struct mbedtls_ssl_context; +struct mbedtls_ssl_config; +typedef struct mbedtls_ssl_context mbedtls_dyncontext; + +/** + Create a new SSL bufferevent to send its data over another bufferevent. + + @param base An event_base to use to detect reading and writing. It + must also be the base for the underlying bufferevent. + @param underlying A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_mbedtls_filter_new(struct event_base *base, + struct bufferevent *underlying, + mbedtls_dyncontext *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + Create a new SSL bufferevent to send its data over an SSL * on a socket. + + @param base An event_base to use to detect reading and writing + @param fd A socket to use for this SSL + @param ssl A SSL* object from mbedtls. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure. +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_mbedtls_socket_new(struct event_base *base, + evutil_socket_t fd, + mbedtls_dyncontext *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + * Get value of the BUFFEREVENT_SSL_DIRTY_SHUTDOWN flag. + * + * @see BUFFEREVENT_SSL_DIRTY_SHUTDOWN + * @deprecated This function is deprecated, use bufferevent_ssl_get_flags() instead. + * @see bufferevent_ssl_get_flags() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_mbedtls_get_allow_dirty_shutdown(struct bufferevent *bev); +/** + * Set value of the BUFFEREVENT_SSL_DIRTY_SHUTDOWN flag. + * + * @see BUFFEREVENT_SSL_DIRTY_SHUTDOWN + * @deprecated This function is deprecated, use bufferevent_ssl_set_flags() instead. + * @see bufferevent_ssl_set_flags() + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_mbedtls_set_allow_dirty_shutdown(struct bufferevent *bev, + int allow_dirty_shutdown); + +/** Return the underlying mbedtls SSL * object for an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +struct mbedtls_ssl_context * +bufferevent_mbedtls_get_ssl(struct bufferevent *bufev); + +/** Tells a bufferevent to begin SSL renegotiation. */ +EVENT2_EXPORT_SYMBOL +int bufferevent_mbedtls_renegotiate(struct bufferevent *bev); + +/** Return the most recent MbedTLS error reported on an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +unsigned long bufferevent_get_mbedtls_error(struct bufferevent *bev); + +/** Create a new heap-based MbedTLS context for use it in bufferevent_mbedtls_* functions */ +EVENT2_EXPORT_SYMBOL +mbedtls_dyncontext * +bufferevent_mbedtls_dyncontext_new(struct mbedtls_ssl_config *conf); + +/** Deallocate heap-based MbedTLS context */ +EVENT2_EXPORT_SYMBOL +void +bufferevent_mbedtls_dyncontext_free(mbedtls_dyncontext *ctx); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ */ diff --git a/include/libevent/event2/bufferevent_struct.h b/include/libevent/event2/bufferevent_struct.h new file mode 100644 index 0000000..546773a --- /dev/null +++ b/include/libevent/event2/bufferevent_struct.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ + +/** @file event2/bufferevent_struct.h + + Data structures for bufferevents. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + @deprecated Use of bufferevent_struct.h is completely deprecated; these + structures are only exposed for backward compatibility with programs + written before Libevent 2.0 that used them. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include +/* For struct event */ +#include + +struct event_watermark { + size_t low; + size_t high; +}; + +/** + Shared implementation of a bufferevent. + + This type is exposed only because it was exposed in previous versions, + and some people's code may rely on manipulating it. Otherwise, you + should really not rely on the layout, size, or contents of this structure: + it is fairly volatile, and WILL change in future versions of the code. +**/ +struct bufferevent { + /** Event base for which this bufferevent was created. */ + struct event_base *ev_base; + /** Pointer to a table of function pointers to set up how this + bufferevent behaves. */ + const struct bufferevent_ops *be_ops; + + /** A read event that triggers when a timeout has happened or a socket + is ready to read data. Only used by some subtypes of + bufferevent. */ + struct event ev_read; + /** A write event that triggers when a timeout has happened or a socket + is ready to write data. Only used by some subtypes of + bufferevent. */ + struct event ev_write; + + /** An input buffer. Only the bufferevent is allowed to add data to + this buffer, though the user is allowed to drain it. */ + struct evbuffer *input; + + /** An output buffer. Only the bufferevent is allowed to drain data + from this buffer, though the user is allowed to add it. */ + struct evbuffer *output; + + struct event_watermark wm_read; + struct event_watermark wm_write; + + bufferevent_data_cb readcb; + bufferevent_data_cb writecb; + /* This should be called 'eventcb', but renaming it would break + * backward compatibility */ + bufferevent_event_cb errorcb; + void *cbarg; + + struct timeval timeout_read; + struct timeval timeout_write; + + /** Events that are currently enabled: currently EV_READ and EV_WRITE + are supported. */ + short enabled; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ */ diff --git a/include/libevent/event2/dns.h b/include/libevent/event2/dns.h new file mode 100644 index 0000000..be61e78 --- /dev/null +++ b/include/libevent/event2/dns.h @@ -0,0 +1,819 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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 original DNS code is due to Adam Langley with heavy + * modifications by Nick Mathewson. Adam put his DNS software in the + * public domain. You can find his original copyright below. Please, + * aware that the code as part of Libevent is governed by the 3-clause + * BSD license above. + * + * This software is Public Domain. To view a copy of the public domain dedication, + * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to + * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + * + * I ask and expect, but do not require, that all derivative works contain an + * attribution similar to: + * Parts developed by Adam Langley + * + * You may wish to replace the word "Parts" with something else depending on + * the amount of original code. + * + * (Derivative works does not include programs which link against, run or include + * the source verbatim in their source distributions) + */ + +/** @file event2/dns.h + * + * @brief Provides a few APIs to use for resolving DNS names, and a facility + * for implementing simple DNS servers. + * + * Welcome, gentle reader + * + * Async DNS lookups are really a whole lot harder than they should be, + * mostly stemming from the fact that the libc resolver has never been + * very good at them. Before you use this library you should see if libc + * can do the job for you with the modern async call getaddrinfo_a + * (see http://www.imperialviolet.org/page25.html#e498). Otherwise, + * please continue. + * + * The library keeps track of the state of nameservers and will avoid + * them when they go down. Otherwise it will round robin between them. + * + * Quick start guide: + * @code + * #include "evdns.h" + * void callback(int result, char type, int count, int ttl, + * void *addresses, void *arg); + * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); + * evdns_resolve("www.hostname.com", 0, callback, NULL); + *@endcode + * When the lookup is complete the callback function is called. The + * first argument will be one of the DNS_ERR_* defines in evdns.h. + * Hopefully it will be DNS_ERR_NONE, in which case type will be + * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time + * which the data can be cached for (in seconds), addresses will point + * to an array of uint32_t's and arg will be whatever you passed to + * evdns_resolve. + * + * Searching: + * + * In order for this library to be a good replacement for glibc's resolver it + * supports searching. This involves setting a list of default domains, in + * which names will be queried for. The number of dots in the query name + * determines the order in which this list is used. + * + * Searching appears to be a single lookup from the point of view of the API, + * although many DNS queries may be generated from a single call to + * evdns_resolve. Searching can also drastically slow down the resolution + * of names. + * + * To disable searching: + * 1. Never set it up. If you never call evdns_resolv_conf_parse or + * evdns_search_add then no searching will occur. + * + * 2. If you do call evdns_resolv_conf_parse then don't pass + * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it). + * + * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag. + * + * The order of searches depends on the number of dots in the name. If the + * number is greater than the ndots setting then the names is first tried + * globally. Otherwise each search domain is appended in turn. + * + * The ndots setting can either be set from a resolv.conf, or by calling + * evdns_search_ndots_set. + * + * For example, with ndots set to 1 (the default) and a search domain list of + * ["myhome.net"]: + * + *
+ *  Query: www
+ *  Order: www.myhome.net, www.
+ *
+ *  Query: www.abc
+ *  Order: www.abc., www.abc.myhome.net
+ * 
+ * Internals: + * + * Requests are kept in two queues. The first is the inflight queue. In + * this queue requests have an allocated transaction id and nameserver. + * They will soon be transmitted if they haven't already been. + * + * The second is the waiting queue. The size of the inflight ring is + * limited and all other requests wait in waiting queue for space. This + * bounds the number of concurrent requests so that we don't flood the + * nameserver. Several algorithms require a full walk of the inflight + * queue and so bounding its size keeps thing going nicely under huge + * (many thousands of requests) loads. + * + * If a nameserver loses too many requests it is considered down and we + * try not to use it. After a while we send a probe to that nameserver + * (a lookup for google.com) and, if it replies, we consider it working + * again. If the nameserver fails a probe we wait longer to try again + * with the next probe. + */ + +#ifndef EVENT2_DNS_H_INCLUDED_ +#define EVENT2_DNS_H_INCLUDED_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* For integer types. */ +#include + +/** Error codes 0-5 are as described in RFC 1035. */ +#define DNS_ERR_NONE 0 +/** The name server was unable to interpret the query */ +#define DNS_ERR_FORMAT 1 +/** The name server was unable to process this query due to a problem with the + * name server */ +#define DNS_ERR_SERVERFAILED 2 +/** The domain name does not exist */ +#define DNS_ERR_NOTEXIST 3 +/** The name server does not support the requested kind of query */ +#define DNS_ERR_NOTIMPL 4 +/** The name server refuses to reform the specified operation for policy + * reasons */ +#define DNS_ERR_REFUSED 5 +/** The reply was truncated or ill-formatted */ +#define DNS_ERR_TRUNCATED 65 +/** An unknown error occurred */ +#define DNS_ERR_UNKNOWN 66 +/** Communication with the server timed out */ +#define DNS_ERR_TIMEOUT 67 +/** The request was canceled because the DNS subsystem was shut down. */ +#define DNS_ERR_SHUTDOWN 68 +/** The request was canceled via a call to evdns_cancel_request */ +#define DNS_ERR_CANCEL 69 +/** There were no answers and no error condition in the DNS packet. + * This can happen when you ask for an address that exists, but a record + * type that doesn't. */ +#define DNS_ERR_NODATA 70 + +#define DNS_IPv4_A 1 +#define DNS_PTR 2 +#define DNS_IPv6_AAAA 3 +#define DNS_CNAME 4 + +/** Disable searching for the query. */ +#define DNS_QUERY_NO_SEARCH 0x01 +/** Use TCP connections ("virtual circuits") for queries rather than UDP datagrams. */ +#define DNS_QUERY_USEVC 0x02 +/** Ignore trancation flag in responses (don't fallback to TCP connections). */ +#define DNS_QUERY_IGNTC 0x04 +/** Make a separate callback for CNAME in answer */ +#define DNS_CNAME_CALLBACK 0x80 + +/* Allow searching */ +#define DNS_OPTION_SEARCH 1 +/* Parse "nameserver" and add default if no such section */ +#define DNS_OPTION_NAMESERVERS 2 +/* Parse additional options like: + * - timeout: + * - getaddrinfo-allow-skew: + * - max-timeouts: + * - max-inflight: + * - attempts: + * - randomize-case: + * - initial-probe-timeout: + * - max-probe-timeout: + * - probe-backoff-factor: + * - tcp-idle-timeout: + * - edns-udp-size: + * - use-vc + * - ignore-tc + */ +#define DNS_OPTION_MISC 4 +/* Load hosts file (i.e. "/etc/hosts") */ +#define DNS_OPTION_HOSTSFILE 8 +/** + * All above: + * - DNS_OPTION_SEARCH + * - DNS_OPTION_NAMESERVERS + * - DNS_OPTION_MISC + * - DNS_OPTION_HOSTSFILE + */ +#define DNS_OPTIONS_ALL ( \ + DNS_OPTION_SEARCH | \ + DNS_OPTION_NAMESERVERS | \ + DNS_OPTION_MISC | \ + DNS_OPTION_HOSTSFILE | \ + 0 \ +) +/* Do not "default" nameserver (i.e. "127.0.0.1:53") if there is no nameservers + * in resolv.conf, (iff DNS_OPTION_NAMESERVERS is set) */ +#define DNS_OPTION_NAMESERVERS_NO_DEFAULT 16 + +/* Obsolete name for DNS_QUERY_NO_SEARCH */ +#define DNS_NO_SEARCH DNS_QUERY_NO_SEARCH + +/** + * The callback that contains the results from a lookup. + * - result is one of the DNS_ERR_* values (DNS_ERR_NONE for success) + * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA + * - count contains the number of addresses of form type + * - ttl is the number of seconds the resolution may be cached for. + * - addresses needs to be cast according to type. It will be an array of + * 4-byte sequences for ipv4, or an array of 16-byte sequences for ipv6, + * or a nul-terminated string for PTR. + */ +typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg); + +struct evdns_base; +struct event_base; + +/** Flag for evdns_base_new: process resolv.conf. */ +#define EVDNS_BASE_INITIALIZE_NAMESERVERS 1 +/** Flag for evdns_base_new: Do not prevent the libevent event loop from + * exiting when we have no active dns requests. */ +#define EVDNS_BASE_DISABLE_WHEN_INACTIVE 0x8000 +/** Flag for evdns_base_new: If EVDNS_BASE_INITIALIZE_NAMESERVERS isset, do not + * add default nameserver if there are no nameservers in resolv.conf + * @see DNS_OPTION_NAMESERVERS_NO_DEFAULT */ +#define EVDNS_BASE_NAMESERVERS_NO_DEFAULT 0x10000 + +/** + Initialize the asynchronous DNS library. + + This function initializes support for non-blocking name resolution by + calling evdns_resolv_conf_parse() on UNIX and + evdns_config_windows_nameservers() on Windows. + + @param event_base the event base to associate the dns client with + @param flags any of EVDNS_BASE_INITIALIZE_NAMESERVERS| + EVDNS_BASE_DISABLE_WHEN_INACTIVE|EVDNS_BASE_NAMESERVERS_NO_DEFAULT + @return evdns_base object if successful, or NULL if an error occurred. + @see evdns_base_free() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_base * evdns_base_new(struct event_base *event_base, int flags); + + +/** + Shut down the asynchronous DNS resolver and terminate all active requests. + + If the 'fail_requests' option is enabled, all active requests will return + an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, + the requests will be silently discarded. + + @param base the evdns base to free + @param fail_requests if zero, active requests will be aborted; if non-zero, + active requests will return DNS_ERR_SHUTDOWN. + @see evdns_base_new() + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_free(struct evdns_base *base, int fail_requests); + +/** + Remove all hosts entries that have been loaded into the event_base via + evdns_base_load_hosts or via event_base_resolv_conf_parse. + + @param base the evdns base to remove outdated host addresses from + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_clear_host_addresses(struct evdns_base *base); + +/** + Convert a DNS error code to a string. + + @param err the DNS error code + @return a string containing an explanation of the error code +*/ +EVENT2_EXPORT_SYMBOL +const char *evdns_err_to_string(int err); + + +/** + Add a nameserver. + + The address should be an IPv4 address in network byte order. + The type of address is chosen so that it matches in_addr.s_addr. + + @param base the evdns_base to which to add the name server + @param address an IP address in network byte order + @return 0 if successful, or -1 if an error occurred + @see evdns_base_nameserver_ip_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_nameserver_add(struct evdns_base *base, + unsigned long int address); + +/** + Get the number of configured nameservers. + + This returns the number of configured nameservers (not necessarily the + number of running nameservers). This is useful for double-checking + whether our calls to the various nameserver configuration functions + have been successful. + + @param base the evdns_base to which to apply this operation + @return the number of configured nameservers + @see evdns_base_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_count_nameservers(struct evdns_base *base); + +/** + Remove all configured nameservers, and suspend all pending resolves. + + Resolves will not necessarily be re-attempted until evdns_base_resume() is called. + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_resume() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_clear_nameservers_and_suspend(struct evdns_base *base); + + +/** + Resume normal operation and continue any suspended resolve requests. + + Re-attempt resolves left in limbo after an earlier call to + evdns_base_clear_nameservers_and_suspend(). + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_clear_nameservers_and_suspend() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_resume(struct evdns_base *base); + +/** + Add a nameserver by string address. + + This function parses a n IPv4 or IPv6 address from a string and adds it as a + nameserver. It supports the following formats: + - [IPv6Address]:port + - [IPv6Address] + - IPv6Address + - IPv4Address:port + - IPv4Address + + If no port is specified, it defaults to 53. + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_nameserver_ip_add(struct evdns_base *base, + const char *ip_as_string); + +/** + Add a nameserver by sockaddr. + **/ +EVENT2_EXPORT_SYMBOL +int +evdns_base_nameserver_sockaddr_add(struct evdns_base *base, + const struct sockaddr *sa, ev_socklen_t len, unsigned flags); + +struct evdns_request; + +/** + Lookup an A record for a given name. + + @param base the evdns_base to which to apply this operation + @param name a DNS hostname + @param flags either 0, or combination of DNS_QUERY_* flags. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_ipv4(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup an AAAA record for a given name. + + @param base the evdns_base to which to apply this operation + @param name a DNS hostname + @param flags either 0, or combination of DNS_QUERY_* flags. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_ipv6(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); + +struct in_addr; +struct in6_addr; + +/** + Lookup a PTR record for a given IP address. + + @param base the evdns_base to which to apply this operation + @param in an IPv4 address + @param flags either 0, or combination of DNS_QUERY_* flags. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_reverse(struct evdns_base *base, const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); + + +/** + Lookup a PTR record for a given IPv6 address. + + @param base the evdns_base to which to apply this operation + @param in an IPv6 address + @param flags either 0, or combination of DNS_QUERY_* flags. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_reverse_ipv6(struct evdns_base *base, const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Cancels a pending DNS resolution request. + + @param base the evdns_base that was used to make the request + @param req the evdns_request that was returned by calling a resolve function + @see evdns_base_resolve_ipv4(), evdns_base_resolve_ipv6, evdns_base_resolve_reverse +*/ +EVENT2_EXPORT_SYMBOL +void evdns_cancel_request(struct evdns_base *base, struct evdns_request *req); + +/** + Set the value of a configuration option. + + The currently available configuration options are: + + ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case, + bind-to, initial-probe-timeout, max-probe-timeout, probe-backoff-factor, + getaddrinfo-allow-skew, so-rcvbuf, so-sndbuf, tcp-idle-timeout, use-vc, + ignore-tc, edns-udp-size. + + - probe-backoff-factor + Backoff factor of probe timeout + + - max-probe-timeout + Maximum timeout between two probe packets will change initial-probe-timeout + when this value is smaller + + In versions before Libevent 2.0.3-alpha, the option name needed to end with + a colon. + + In case of options without values (use-vc, ingore-tc) val should be an empty + string or NULL. + + @param base the evdns_base to which to apply this operation + @param option the name of the configuration option to be modified + @param val the value to be set + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_set_option(struct evdns_base *base, const char *option, const char *val); + + +/** + Parse a resolv.conf file. + + The 'flags' parameter determines what information is parsed from the + resolv.conf file. See the man page for resolv.conf for the format of this + file. + + The following directives are not parsed from the file: sortlist, rotate, + no-check-names, inet6, debug. + + If this function encounters an error, the possible return values are: 1 = + failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of + memory, 5 = short read from file, 6 = no nameservers listed in the file + + @param base the evdns_base to which to apply this operation + @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| + DNS_OPTION_HOSTSFILE|DNS_OPTIONS_ALL|DNS_OPTION_NAMESERVERS_NO_DEFAULT + @param filename the path to the resolv.conf file + @return 0 if successful, or various positive error codes if an error + occurred (see above) + @see resolv.conf(3), evdns_config_windows_nameservers() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *const filename); + +/** + Load an /etc/hosts-style file from 'hosts_fname' into 'base'. + + If hosts_fname is NULL, add minimal entries for localhost, and nothing + else. + + Note that only evdns_getaddrinfo uses the /etc/hosts entries. + + This function does not replace previously loaded hosts entries; to do that, + call evdns_base_clear_host_addresses first. + + Return 0 on success, negative on failure. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname); + +#if defined(EVENT_IN_DOXYGEN_) || defined(_WIN32) +/** + Obtain nameserver information using the Windows API. + + Attempt to configure a set of nameservers based on platform settings on + a win32 host. Preferentially tries to use GetNetworkParams; if that fails, + looks in the registry. + + @return 0 if successful, or -1 if an error occurred + @see evdns_resolv_conf_parse() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_config_windows_nameservers(struct evdns_base *); +#define EVDNS_BASE_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED +#endif + + +/** + Clear the list of search domains. + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_clear(struct evdns_base *base); + + +/** + Add a domain to the list of search domains + + @param domain the domain to be added to the search list + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_add(struct evdns_base *base, const char *domain); + + +/** + Set the 'ndots' parameter for searches. + + Sets the number of dots which, when found in a name, causes + the first query to be without any search domain. + + @param ndots the new ndots parameter + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_ndots_set(struct evdns_base *base, const int ndots); + +/** + A callback that is invoked when a log message is generated + + @param is_warning indicates if the log message is a 'warning' + @param msg the content of the log message + */ +typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg); + + +/** + Set the callback function to handle DNS log messages. If this + callback is not set, evdns log messages are handled with the regular + Libevent logging system. + + @param fn the callback to be invoked when a log message is generated + */ +EVENT2_EXPORT_SYMBOL +void evdns_set_log_fn(evdns_debug_log_fn_type fn); + +/* + * Functions used to implement a DNS server. + */ + +struct evdns_server_request; +struct evdns_server_question; + +/** + A callback to implement a DNS server. The callback function receives a DNS + request. It should then optionally add a number of answers to the reply + using the evdns_server_request_add_*_reply functions, before calling either + evdns_server_request_respond to send the reply back, or + evdns_server_request_drop to decline to answer the request. + + @param req A newly received request + @param user_data A pointer that was passed to + evdns_add_server_port_with_base(). + */ +typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *); +#define EVDNS_ANSWER_SECTION 0 +#define EVDNS_AUTHORITY_SECTION 1 +#define EVDNS_ADDITIONAL_SECTION 2 + +#define EVDNS_TYPE_A 1 +#define EVDNS_TYPE_NS 2 +#define EVDNS_TYPE_CNAME 5 +#define EVDNS_TYPE_SOA 6 +#define EVDNS_TYPE_PTR 12 +#define EVDNS_TYPE_MX 15 +#define EVDNS_TYPE_TXT 16 +#define EVDNS_TYPE_AAAA 28 + +#define EVDNS_QTYPE_AXFR 252 +#define EVDNS_QTYPE_ALL 255 + +#define EVDNS_CLASS_INET 1 + +/* flags that can be set in answers; as part of the err parameter */ +#define EVDNS_FLAGS_AA 0x400 +#define EVDNS_FLAGS_RD 0x080 + +/** Create a new UDP DNS server port. + + @param base The event base to handle events for the server port. + @param socket A UDP socket to accept DNS requests. + @param flags Always 0 for now. + @param callback A function to invoke whenever we get a DNS request + on the socket. + @param user_data Data to pass to the callback. + @return an evdns_server_port structure for this server port or NULL if + an error occurred. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_server_port *evdns_add_server_port_with_base(struct event_base *base, evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); + +struct evconnlistener; + +/** Create a new TCP DNS server port. + + @param base The event base to handle events for the server port. + @param listener A TCP listener to accept DNS requests. + @param flags Always 0 for now. + @param callback A function to invoke whenever we get a DNS request + on the socket. + @param user_data Data to pass to the callback. + @return an evdns_server_port structure for this server port or NULL if + an error occurred. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_server_port *evdns_add_server_port_with_listener( + struct event_base *base, struct evconnlistener *listener, int flags, + evdns_request_callback_fn_type callback, void *user_data); + +/** Close down a DNS server port, and free associated structures. */ +EVENT2_EXPORT_SYMBOL +void evdns_close_server_port(struct evdns_server_port *port); + +/** + * List of configurable evdns_server_port options. + * + * @see evdns_server_port_set_option() + */ +enum evdns_server_option { + /** + * Maximum number of simultaneous tcp connections (clients) + * that server can hold. Can be set only for TCP DNS servers. + */ + EVDNS_SOPT_TCP_MAX_CLIENTS, + /** + * Idle timeout (in seconds) of incoming TCP connections. + * If client doesn't send any requests via the connection + * during this period connection is closed by the server. + * Can be set only for TCP DNS servers. + */ + EVDNS_SOPT_TCP_IDLE_TIMEOUT, +}; + +/** + Configure DNS server. + + @param port the evdns_server_port to which to apply this operation + @param option @see evdns_server_option for the list of possible options + @param val value of the option + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_port_set_option(struct evdns_server_port *port, enum evdns_server_option option, size_t value); + +/** Sets some flags in a reply we're building. + Allows setting of the AA or RD flags + */ +EVENT2_EXPORT_SYMBOL +void evdns_server_request_set_flags(struct evdns_server_request *req, int flags); + +/* Functions to add an answer to an in-progress DNS reply. + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char *data); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); + +/** + Send back a response to a DNS request, and free the request structure. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_respond(struct evdns_server_request *req, int err); +/** + Free a DNS request without sending back a reply. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_drop(struct evdns_server_request *req); +struct sockaddr; +/** + Get the address that made a DNS request. + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_get_requesting_addr(struct evdns_server_request *req, struct sockaddr *sa, int addr_len); + +/** Callback for evdns_getaddrinfo. */ +typedef void (*evdns_getaddrinfo_cb)(int result, struct evutil_addrinfo *res, void *arg); + +struct evdns_base; +struct evdns_getaddrinfo_request; +/** Make a non-blocking getaddrinfo request using the dns_base in 'dns_base'. + * + * If we can answer the request immediately (with an error or not!), then we + * invoke cb immediately and return NULL. Otherwise we return + * an evdns_getaddrinfo_request and invoke cb later. + * + * When the callback is invoked, we pass as its first argument the error code + * that getaddrinfo would return (or 0 for no error). As its second argument, + * we pass the evutil_addrinfo structures we found (or NULL on error). We + * pass 'arg' as the third argument. + * + * Limitations: + * + * - The AI_V4MAPPED and AI_ALL flags are not currently implemented. + * - For ai_socktype, we only handle SOCKTYPE_STREAM, SOCKTYPE_UDP, and 0. + * - For ai_protocol, we only handle IPPROTO_TCP, IPPROTO_UDP, and 0. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_getaddrinfo_request *evdns_getaddrinfo( + struct evdns_base *dns_base, + const char *nodename, const char *servname, + const struct evutil_addrinfo *hints_in, + evdns_getaddrinfo_cb cb, void *arg); + +/* Cancel an in-progress evdns_getaddrinfo. This MUST NOT be called after the + * getaddrinfo's callback has been invoked. The resolves will be canceled, + * and the callback will be invoked with the error EVUTIL_EAI_CANCEL. */ +EVENT2_EXPORT_SYMBOL +void evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *req); + +/** + Retrieve the address of the 'idx'th configured nameserver. + + @param base The evdns_base to examine. + @param idx The index of the nameserver to get the address of. + @param sa A location to receive the server's address. + @param len The number of bytes available at sa. + + @return the number of bytes written into sa on success. On failure, returns + -1 if idx is greater than the number of configured nameservers, or a + value greater than 'len' if len was not high enough. + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_get_nameserver_addr(struct evdns_base *base, int idx, + struct sockaddr *sa, ev_socklen_t len); + +/** + Retrieve the fd of the 'idx'th configured nameserver. + + @param base The evdns_base to examine. + @param idx The index of the nameserver to get the address of. + + @return the fd value. On failure, returns + -1 if idx is greater than the number of configured nameservers + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_get_nameserver_fd(struct evdns_base *base, int idx); + +#ifdef __cplusplus +} +#endif + +#endif /* !EVENT2_DNS_H_INCLUDED_ */ diff --git a/include/libevent/event2/dns_compat.h b/include/libevent/event2/dns_compat.h new file mode 100644 index 0000000..ea00266 --- /dev/null +++ b/include/libevent/event2/dns_compat.h @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_DNS_COMPAT_H_INCLUDED_ +#define EVENT2_DNS_COMPAT_H_INCLUDED_ + +/** @file event2/dns_compat.h + + @brief Potentially non-threadsafe versions of the functions in dns.h: provided + only for backwards compatibility. + + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include +#include + +/** + Initialize the asynchronous DNS library. + + This function initializes support for non-blocking name resolution by + calling evdns_resolv_conf_parse() on UNIX and + evdns_config_windows_nameservers() on Windows. + + @deprecated This function is deprecated because it always uses the current + event base, and is easily confused by multiple calls to event_init(), and + so is not safe for multithreaded use. Additionally, it allocates a global + structure that only one thread can use. The replacement is + evdns_base_new(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_shutdown() + */ +EVENT2_EXPORT_SYMBOL +int evdns_init(void); + +struct evdns_base; +/** + Return the global evdns_base created by event_init() and used by the other + deprecated functions. + + @deprecated This function is deprecated because use of the global + evdns_base is error-prone. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_base *evdns_get_global_base(void); + +/** + Shut down the asynchronous DNS resolver and terminate all active requests. + + If the 'fail_requests' option is enabled, all active requests will return + an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, + the requests will be silently discarded. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_shutdown(). + + @param fail_requests if zero, active requests will be aborted; if non-zero, + active requests will return DNS_ERR_SHUTDOWN. + @see evdns_init() + */ +EVENT2_EXPORT_SYMBOL +void evdns_shutdown(int fail_requests); + +/** + Add a nameserver. + + The address should be an IPv4 address in network byte order. + The type of address is chosen so that it matches in_addr.s_addr. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_nameserver_add(). + + @param address an IP address in network byte order + @return 0 if successful, or -1 if an error occurred + @see evdns_nameserver_ip_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_nameserver_add(unsigned long int address); + +/** + Get the number of configured nameservers. + + This returns the number of configured nameservers (not necessarily the + number of running nameservers). This is useful for double-checking + whether our calls to the various nameserver configuration functions + have been successful. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_count_nameservers(). + + @return the number of configured nameservers + @see evdns_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_count_nameservers(void); + +/** + Remove all configured nameservers, and suspend all pending resolves. + + Resolves will not necessarily be re-attempted until evdns_resume() is called. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_clear_nameservers_and_suspend(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_resume() + */ +EVENT2_EXPORT_SYMBOL +int evdns_clear_nameservers_and_suspend(void); + +/** + Resume normal operation and continue any suspended resolve requests. + + Re-attempt resolves left in limbo after an earlier call to + evdns_clear_nameservers_and_suspend(). + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resume(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_clear_nameservers_and_suspend() + */ +EVENT2_EXPORT_SYMBOL +int evdns_resume(void); + +/** + Add a nameserver. + + This wraps the evdns_nameserver_add() function by parsing a string as an IP + address and adds it as a nameserver. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_nameserver_ip_add(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_nameserver_ip_add(const char *ip_as_string); + +/** + Lookup an A record for a given name. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_ipv4(). + + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() + */ +EVENT2_EXPORT_SYMBOL +int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup an AAAA record for a given name. + + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() + */ +EVENT2_EXPORT_SYMBOL +int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr); + +struct in_addr; +struct in6_addr; + +/** + Lookup a PTR record for a given IP address. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_reverse(). + + @param in an IPv4 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_reverse_ipv6() + */ +EVENT2_EXPORT_SYMBOL +int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup a PTR record for a given IPv6 address. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_reverse_ipv6(). + + @param in an IPv6 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_reverse_ipv6() + */ +EVENT2_EXPORT_SYMBOL +int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Set the value of a configuration option. + + The currently available configuration options are: + + ndots, timeout, max-timeouts, max-inflight, and attempts + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_set_option(). + + @param option the name of the configuration option to be modified + @param val the value to be set + @param flags Ignored. + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evdns_set_option(const char *option, const char *val, int flags); + +/** + Parse a resolv.conf file. + + The 'flags' parameter determines what information is parsed from the + resolv.conf file. See the man page for resolv.conf for the format of this + file. + + The following directives are not parsed from the file: sortlist, rotate, + no-check-names, inet6, debug. + + If this function encounters an error, the possible return values are: 1 = + failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of + memory, 5 = short read from file, 6 = no nameservers listed in the file + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolv_conf_parse(). + + @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| + DNS_OPTIONS_ALL + @param filename the path to the resolv.conf file + @return 0 if successful, or various positive error codes if an error + occurred (see above) + @see resolv.conf(3), evdns_config_windows_nameservers() + */ +EVENT2_EXPORT_SYMBOL +int evdns_resolv_conf_parse(int flags, const char *const filename); + +/** + Clear the list of search domains. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_clear(). + */ +EVENT2_EXPORT_SYMBOL +void evdns_search_clear(void); + +/** + Add a domain to the list of search domains + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_add(). + + @param domain the domain to be added to the search list + */ +EVENT2_EXPORT_SYMBOL +void evdns_search_add(const char *domain); + +/** + Set the 'ndots' parameter for searches. + + Sets the number of dots which, when found in a name, causes + the first query to be without any search domain. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_ndots_set(). + + @param ndots the new ndots parameter + */ +EVENT2_EXPORT_SYMBOL +void evdns_search_ndots_set(const int ndots); + +/** + As evdns_server_new_with_base. + + @deprecated This function is deprecated because it does not allow the + caller to specify which even_base it uses. The recommended + function is evdns_add_server_port_with_base(). + +*/ +EVENT2_EXPORT_SYMBOL +struct evdns_server_port * +evdns_add_server_port(evutil_socket_t socket, int flags, + evdns_request_callback_fn_type callback, void *user_data); + +#ifdef _WIN32 +EVENT2_EXPORT_SYMBOL +int evdns_config_windows_nameservers(void); +#define EVDNS_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/include/libevent/event2/dns_struct.h b/include/libevent/event2/dns_struct.h new file mode 100644 index 0000000..593a8a7 --- /dev/null +++ b/include/libevent/event2/dns_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_DNS_STRUCT_H_INCLUDED_ +#define EVENT2_DNS_STRUCT_H_INCLUDED_ + +/** @file event2/dns_struct.h + + Data structures for dns. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/* + * Structures used to implement a DNS server. + */ + +struct evdns_server_request { + int flags; + int nquestions; + struct evdns_server_question **questions; +}; +struct evdns_server_question { + int type; +#ifdef __cplusplus + int dns_question_class; +#else + /* You should refer to this field as "dns_question_class". The + * name "class" works in C for backward compatibility, and will be + * removed in a future version. (1.5 or later). */ + int class; +#define dns_question_class class +#endif + char name[1]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_DNS_STRUCT_H_INCLUDED_ */ + diff --git a/include/libevent/event2/event-config.h b/include/libevent/event2/event-config.h new file mode 100644 index 0000000..c76e42f --- /dev/null +++ b/include/libevent/event2/event-config.h @@ -0,0 +1,528 @@ +/* event-config.h + * + * This file was generated by cmake when the makefiles were generated. + * + * DO NOT EDIT THIS FILE. + * + * Do not rely on macros in this file existing in later versions. + */ +#ifndef EVENT2_EVENT_CONFIG_H_INCLUDED_ +#define EVENT2_EVENT_CONFIG_H_INCLUDED_ + +/* Numeric representation of the version */ +#define EVENT__NUMERIC_VERSION 0x02020001 +#define EVENT__PACKAGE_VERSION "2.2.0" + +#define EVENT__VERSION_MAJOR 2 +#define EVENT__VERSION_MINOR 2 +#define EVENT__VERSION_PATCH 0 + +/* Version number of package */ +#define EVENT__VERSION "2.2.0-alpha-dev" + +/* Name of package */ +#define EVENT__PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define EVENT__PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define EVENT__PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define EVENT__PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define EVENT__PACKAGE_TARNAME "" + +/* Define if libevent should build without support for a debug mode */ +/* #undef EVENT__DISABLE_DEBUG_MODE */ + +/* Define if libevent should not allow replacing the mm functions */ +/* #undef EVENT__DISABLE_MM_REPLACEMENT */ + +/* Define if libevent should not be compiled with thread support */ +/* #undef EVENT__DISABLE_THREAD_SUPPORT */ + +/* Define to 1 if you have the `accept4' function. */ +#define EVENT__HAVE_ACCEPT4 1 + +/* Define to 1 if you have the `arc4random' function. */ +/* #undef EVENT__HAVE_ARC4RANDOM */ + +/* Define to 1 if you have the `arc4random_buf' function. */ +/* #undef EVENT__HAVE_ARC4RANDOM_BUF */ + +/* Define to 1 if you have the `arc4random_addrandom' function. */ +/* #undef EVENT__HAVE_ARC4RANDOM_ADDRANDOM */ + +/* Define if clock_gettime is available in libc */ +#define EVENT__DNS_USE_CPU_CLOCK_FOR_ID 1 + +/* Define is no secure id variant is available */ +/* #undef EVENT__DNS_USE_GETTIMEOFDAY_FOR_ID */ +/* #undef EVENT__DNS_USE_FTIME_FOR_ID */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +#define EVENT__HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the declaration of `CTL_KERN'. */ +#define EVENT__HAVE_DECL_CTL_KERN 0 + +/* Define to 1 if you have the declaration of `KERN_ARND'. */ +#define EVENT__HAVE_DECL_KERN_ARND 0 + +/* Define to 1 if you have `getrandom' function. */ +#define EVENT__HAVE_GETRANDOM 1 + +/* Define if /dev/poll is available */ +/* #undef EVENT__HAVE_DEVPOLL */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETDB_H 1 + +/* Define to 1 if fd_mask type is defined */ +#define EVENT__HAVE_FD_MASK 1 + +/* Define to 1 if the header file defines TAILQ_FOREACH. */ +#define EVENT__HAVE_TAILQFOREACH 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_DLFCN_H 1 + +/* Define if your system supports the epoll system calls */ +#define EVENT__HAVE_EPOLL 1 + +/* Define to 1 if you have the `epoll_create1' function. */ +#define EVENT__HAVE_EPOLL_CREATE1 1 + +/* Define to 1 if you have the `epoll_pwait2' function. */ +#define EVENT__HAVE_EPOLL_PWAIT2 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +#define EVENT__HAVE_EPOLL_CTL 1 + +/* Define if your system supports the wepoll module */ +/* #undef EVENT__HAVE_WEPOLL */ + +/* Define to 1 if you have the `eventfd' function. */ +#define EVENT__HAVE_EVENTFD 1 + +/* Define if your system supports event ports */ +/* #undef EVENT__HAVE_EVENT_PORTS */ + +/* Define to 1 if you have the `fcntl' function. */ +#define EVENT__HAVE_FCNTL 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define EVENT__HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getegid' function. */ +#define EVENT__HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define EVENT__HAVE_GETEUID 1 + +/* TODO: Check for different gethostname argument counts. CheckPrototypeDefinition.cmake can be used. */ +/* Define this if you have any gethostbyname_r() */ +#define EVENT__HAVE_GETHOSTBYNAME_R 1 + +/* Define this if gethostbyname_r takes 3 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_3_ARG */ + +/* Define this if gethostbyname_r takes 5 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_5_ARG */ + +/* Define this if gethostbyname_r takes 6 arguments */ +#define EVENT__HAVE_GETHOSTBYNAME_R_6_ARG 1 + +/* Define to 1 if you have the `getifaddrs' function. */ +#define EVENT__HAVE_GETIFADDRS 1 + +/* Define to 1 if you have the `getnameinfo' function. */ +#define EVENT__HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getprotobynumber' function. */ +#define EVENT__HAVE_GETPROTOBYNUMBER 1 + +/* Define to 1 if you have the `getservbyname' function. */ +#define EVENT__HAVE_GETSERVBYNAME 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define EVENT__HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_IFADDRS_H 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define EVENT__HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define EVENT__HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `issetugid' function. */ +/* #undef EVENT__HAVE_ISSETUGID */ + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef EVENT__HAVE_KQUEUE */ + +/* Define if the system has zlib */ +#define EVENT__HAVE_LIBZ 1 + +/* Define to 1 if you have the `mach_absolute_time' function. */ +/* #undef EVENT__HAVE_MACH_ABSOLUTE_TIME */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_MACH_MACH_TIME_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_MACH_MACH_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mmap' function. */ +#define EVENT__HAVE_MMAP 1 + +/* Define to 1 if you have the `mmap64' function. */ +#define EVENT__HAVE_MMAP64 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define EVENT__HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the `usleep' function. */ +#define EVENT__HAVE_USLEEP 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_AFUNIX_H */ + +/* Define if the system has openssl */ +#define EVENT__HAVE_OPENSSL 1 + +/* Define if the system has mbedtls */ +/* #undef EVENT__HAVE_MBEDTLS */ + +/* Define to 1 if you have the `pipe' function. */ +#define EVENT__HAVE_PIPE 1 + +/* Define to 1 if you have the `pipe2' function. */ +#define EVENT__HAVE_PIPE2 1 + +/* Define to 1 if you have the `poll' function. */ +#define EVENT__HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef EVENT__HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_PORT_H */ + +/* Define if we have pthreads on this system */ +#define EVENT__HAVE_PTHREADS 1 + +/* Define to 1 if you have the `pthread_mutexattr_setprotocol' function. */ +#define EVENT__HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL 1 + +/* Define to 1 if you have the `putenv' function. */ +#define EVENT__HAVE_PUTENV 1 + +/* Define to 1 if the system has the type `sa_family_t'. */ +#define EVENT__HAVE_SA_FAMILY_T 1 + +/* Define to 1 if you have the `select' function. */ +#define EVENT__HAVE_SELECT 1 + +/* Define to 1 if you have the `setenv' function. */ +#define EVENT__HAVE_SETENV 1 + +/* Define if F_SETFD is defined in */ +#define EVENT__HAVE_SETFD 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#define EVENT__HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `sendfile' function. */ +#define EVENT__HAVE_SENDFILE 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define EVENT__HAVE_SIGACTION 1 + +/* Define to 1 if you have the `signal' function. */ +#define EVENT__HAVE_SIGNAL 1 + +/* Define to 1 if you have the `strsignal' function. */ +#define EVENT__HAVE_STRSIGNAL 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef EVENT__HAVE_STRLCPY */ + +/* Define to 1 if you have the `strsep' function. */ +#define EVENT__HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtok_r' function. */ +#define EVENT__HAVE_STRTOK_R 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define EVENT__HAVE_STRTOLL 1 + +/* Define to 1 if you have the `_gmtime64_s' function. */ +/* #undef EVENT__HAVE__GMTIME64_S */ + +/* Define to 1 if you have the `_gmtime64' function. */ +/* #undef EVENT__HAVE__GMTIME64 */ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#define EVENT__HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if `s6_addr16' is member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 1 + +/* Define to 1 if `s6_addr32' is member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 1 + +/* Define to 1 if the system has the type `struct sockaddr_in6'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN6 1 + +/* Define to 1 if `sin6_len' is member of `struct sockaddr_in6'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN */ + +/* Define to 1 if `sin_len' is member of `struct sockaddr_in'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_un'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_UN 1 + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 + +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if the system has the type `struct linger'. */ +#define EVENT__HAVE_STRUCT_LINGER 1 + +/* Define to 1 if you have the `sysctl' function. */ +/* #undef EVENT__HAVE_SYSCTL */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EPOLL_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EVENTFD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SENDFILE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_RANDOM_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SYSCTL_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TIMERFD_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SIGNALFD_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ERRNO_H 1 + +/* Define if TAILQ_FOREACH is defined in */ +#define EVENT__HAVE_TAILQFOREACH 1 + +/* Define if timeradd is defined in */ +#define EVENT__HAVE_TIMERADD 1 + +/* Define if timerclear is defined in */ +#define EVENT__HAVE_TIMERCLEAR 1 + +/* Define if timercmp is defined in */ +#define EVENT__HAVE_TIMERCMP 1 + + +/* Define to 1 if you have the `timerfd_create' function. */ +#define EVENT__HAVE_TIMERFD_CREATE 1 + +/* Define if timerisset is defined in */ +#define EVENT__HAVE_TIMERISSET 1 + +/* Define to 1 if the system has the type `uint8_t'. */ +#define EVENT__HAVE_UINT8_T 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +#define EVENT__HAVE_UINT16_T 1 + +/* Define to 1 if the system has the type `uint32_t'. */ +#define EVENT__HAVE_UINT32_T 1 + +/* Define to 1 if the system has the type `uint64_t'. */ +#define EVENT__HAVE_UINT64_T 1 + +/* Define to 1 if the system has the type `uintptr_t'. */ +#define EVENT__HAVE_UINTPTR_T 1 + +/* Define to 1 if you have the `umask' function. */ +#define EVENT__HAVE_UMASK 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `unsetenv' function. */ +#define EVENT__HAVE_UNSETENV 1 + +/* Define if kqueue works correctly with pipes */ +/* #undef EVENT__HAVE_WORKING_KQUEUE */ + +#ifdef __USE_UNUSED_DEFINITIONS__ +/* Define to necessary symbol if this constant uses a non-standard name on your system. */ +/* XXX: Hello, this isn't even used, nor is it defined anywhere... - Ellzey */ +#define EVENT__PTHREAD_CREATE_JOINABLE +#endif + +/* The size of `pthread_t', as computed by sizeof. */ +#define EVENT__SIZEOF_PTHREAD_T 8 + +/* The size of a `int', as computed by sizeof. */ +#define EVENT__SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG 8 + +/* The size of a `long long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG_LONG 8 + +/* The size of `off_t', as computed by sizeof. */ +#define EVENT__SIZEOF_OFF_T 8 + +#define EVENT__SIZEOF_SSIZE_T 8 + + +/* The size of a `short', as computed by sizeof. */ +#define EVENT__SIZEOF_SHORT 2 + +/* The size of `size_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SIZE_T 8 + +/* The size of `socklen_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SOCKLEN_T 4 + +/* The size of 'void *', as computer by sizeof */ +#define EVENT__SIZEOF_VOID_P 8 + +/* The size of 'time_t', as computer by sizeof */ +#define EVENT__SIZEOF_TIME_T 8 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* why not c++? + * + * and are we really expected to use EVENT__inline everywhere, + * shouldn't we just do: + * ifdef EVENT__inline + * define inline EVENT__inline + * + * - Ellzey + */ + +#define EVENT__inline inline +#endif + +#define EVENT__HAVE___func__ 1 +#define EVENT__HAVE___FUNCTION__ 1 + +/* Define to `unsigned' if does not define. */ +#define EVENT__size_t size_t + +/* Define to unsigned int if you dont have it */ +#define EVENT__socklen_t socklen_t + +/* Define to `int' if does not define. */ +#define EVENT__ssize_t ssize_t + +#endif /* \EVENT2_EVENT_CONFIG_H_INCLUDED_ */ diff --git a/include/libevent/event2/event.h b/include/libevent/event2/event.h new file mode 100644 index 0000000..955a206 --- /dev/null +++ b/include/libevent/event2/event.h @@ -0,0 +1,1704 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_EVENT_H_INCLUDED_ +#define EVENT2_EVENT_H_INCLUDED_ + +/** + @mainpage + + @section intro Introduction + + Libevent is an event notification library for developing scalable network + servers. The Libevent API provides a mechanism to execute a callback + function when a specific event occurs on a file descriptor or after a + timeout has been reached. Furthermore, Libevent also support callbacks due + to signals or regular timeouts. + + Libevent is meant to replace the event loop found in event driven network + servers. An application just needs to call event_base_dispatch() and then add or + remove events dynamically without having to change the event loop. + + + Currently, Libevent supports /dev/poll, kqueue(2), select(2), poll(2), + epoll(4), and evports. The internal event mechanism is completely + independent of the exposed event API, and a simple update of Libevent can + provide new functionality without having to redesign the applications. As a + result, Libevent allows for portable application development and provides + the most scalable event notification mechanism available on an operating + system. Libevent can also be used for multithreaded programs. Libevent + should compile on Linux, *BSD, Mac OS X, Solaris and, Windows. + + @section usage Standard usage + + Every program that uses Libevent must include the + header, and pass the -levent flag to the linker. (You can instead safe_grab + -levent_core if you only want the main event and buffered IO-based code, + and don't want to safe_grab any protocol code.) + + @section setup Library setup + + Before you call any other Libevent functions, you need to set up the + library. If you're going to use Libevent from multiple threads_ in a + multithreaded application, you need to initialize thread support -- + typically by using evthread_use_pthreads() or + evthread_use_windows_threads(). See for more + information. + + This is also the point where you can replace Libevent's memory + management functions with event_set_mem_functions, and enable debug mode + with event_enable_debug_mode(). + + @section base Creating an event base + + Next, you need to create an event_base structure, using event_base_new() + or event_base_new_with_config(). The event_base is responsible for + keeping track of which events are "pending" (that is to say, being + watched to see if they become active) and which events are "active". + Every event is associated with a single event_base. + + @section event Event notification + + For each file descriptor that you wish to monitor, you must create an + event structure with event_new(). (You may also declare an event + structure and call event_assign() to initialize the members of the + structure.) To enable notification, you add the structure to the list + of monitored events by calling event_add(). The event structure must + remain allocated as long as it is active, so it should generally be + allocated on the heap. + + @section loop Dispatching events. + + Finally, you call event_base_dispatch() to loop and dispatch events. + You can also use event_base_loop() for more fine-grained control. + + Currently, only one thread can be dispatching a given event_base at a + time. If you want to run events in multiple threads_ at once, you can + either have a single event_base whose events add work to a work queue, + or you can create multiple event_base objects. + + @section bufferevent I/O Buffers + + Libevent provides a buffered I/O abstraction on top of the regular event + callbacks. This abstraction is called a bufferevent. A bufferevent + provides input and output buffers that get filled and drained + automatically. The user of a buffered event no longer deals directly + with the I/O, but instead is reading from input and writing to output + buffers. + + Once initialized via bufferevent_socket_new(), the bufferevent structure + can be used repeatedly with bufferevent_enable() and + bufferevent_disable(). Instead of reading and writing directly to a + socket, you would call bufferevent_read() and bufferevent_write(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + See for more information. + + @section timers Timers + + Libevent can also be used to create timers that invoke a callback after a + certain amount of time has expired. The evtimer_new() macro returns + an event struct to use as a timer. To activate the timer, call + evtimer_add(). Timers can be deactivated by calling evtimer_del(). + (These macros are thin wrappers around event_new(), event_add(), + and event_del(); you can also use those instead.) + + @section evdns Asynchronous DNS resolution + + Libevent provides an asynchronous DNS resolver that should be used instead + of the standard DNS resolver functions. See the + functions for more detail. + + @section evhttp Event-driven HTTP servers + + Libevent provides a very simple event-driven HTTP server that can be + embedded in your program and used to service HTTP requests. + + To use this capability, you need to include the header in your + program. See that header for more information. + + @section evrpc A framework for RPC servers and clients + + Libevent provides a framework for creating RPC servers and clients. It + takes care of marshaling and unmarshaling all data structures. + + @section api API Reference + + To browse the complete documentation of the libevent API, click on any of + the following links. + + event2/event.h + The primary libevent header + + event2/thread.h + Functions for use by multithreaded programs + + event2/buffer.h and event2/bufferevent.h + Buffer management for network reading and writing + + event2/util.h + Utility functions for portable nonblocking network code + + event2/dns.h + Asynchronous DNS resolution + + event2/http.h + An embedded libevent-based HTTP server + + event2/rpc.h + A framework for creating RPC servers and clients + + event2/watch.h + "Prepare" and "check" watchers. + */ + +/** @file event2/event.h + + @brief Core functions for waiting for and receiving events, and using event bases. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +#include + +/* For int types. */ +#include + +/** + * Structure to hold information and state for a Libevent dispatch loop. + * + * The event_base lies at the center of Libevent; every application will + * have one. It keeps track of all pending and active events, and + * notifies your application of the active ones. + * + * This is an opaque structure; you can allocate one using + * event_base_new() or event_base_new_with_config(). + * + * @see event_base_new(), event_base_free(), event_base_loop(), + * event_base_new_with_config() + */ +struct event_base +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Structure to represent a single event. + * + * An event can have some underlying condition it represents: a socket + * becoming readable or writeable (or both), or a signal becoming raised. + * (An event that represents no underlying condition is still useful: you + * can use one to implement a timer, or to communicate between threads_.) + * + * Generally, you can create events with event_new(), then make them + * pending with event_add(). As your event_base runs, it will run the + * callbacks of an events whose conditions are triggered. When you no + * longer want the event, free it with event_free(). + * + * In more depth: + * + * An event may be "pending" (one whose condition we are watching), + * "active" (one whose condition has triggered and whose callback is about + * to run), neither, or both. Events come into existence via + * event_assign() or event_new(), and are then neither active nor pending. + * + * To make an event pending, pass it to event_add(). When doing so, you + * can also set a timeout for the event. + * + * Events become active during an event_base_loop() call when either their + * condition has triggered, or when their timeout has elapsed. You can + * also activate an event manually using event_active(). The even_base + * loop will run the callbacks of active events; after it has done so, it + * marks them as no longer active. + * + * You can make an event non-pending by passing it to event_del(). This + * also makes the event non-active. + * + * Events can be "persistent" or "non-persistent". A non-persistent event + * becomes non-pending as soon as it is triggered: thus, it only runs at + * most once per call to event_add(). A persistent event remains pending + * even when it becomes active: you'll need to event_del() it manually in + * order to make it non-pending. When a persistent event with a timeout + * becomes active, its timeout is reset: this means you can use persistent + * events to implement periodic timeouts. + * + * This should be treated as an opaque structure; you should never read or + * write any of its fields directly. For backward compatibility with old + * code, it is defined in the event2/event_struct.h header; including this + * header may make your code incompatible with other versions of Libevent. + * + * @see event_new(), event_free(), event_assign(), event_get_assignment(), + * event_add(), event_del(), event_active(), event_pending(), + * event_get_fd(), event_get_base(), event_get_events(), + * event_get_callback(), event_get_callback_arg(), + * event_priority_set() + */ +struct event +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Configuration for an event_base. + * + * There are many options that can be used to alter the behavior and + * implementation of an event_base. To avoid having to pass them all in a + * complex many-argument constructor, we provide an abstract data type + * where you set up configuration information before passing it to + * event_base_new_with_config(). + * + * @see event_config_new(), event_config_free(), event_base_new_with_config(), + * event_config_avoid_method(), event_config_require_features(), + * event_config_set_flag(), event_config_set_num_cpus_hint() + */ +struct event_config +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Enable some relatively expensive debugging checks in Libevent that + * would normally be turned off. Generally, these checks cause code that + * would otherwise crash mysteriously to fail earlier with an assertion + * failure. Note that this method MUST be called before any events or + * event_bases have been created. + * + * Debug mode can currently catch the following errors: + * An event is re-assigned while it is added + * Any function is called on a non-assigned event + * + * Note that debugging mode uses memory to track every event that has been + * initialized (via event_assign, event_set, or event_new) but not yet + * released (via event_free or event_debug_unassign). If you want to use + * debug mode, and you find yourself running out of memory, you will need + * to use event_debug_unassign to explicitly stop tracking events that + * are no longer considered set-up. + * + * @see event_debug_unassign() + */ +EVENT2_EXPORT_SYMBOL +void event_enable_debug_mode(void); + +/** + * When debugging mode is enabled, informs Libevent that an event should no + * longer be considered as assigned. When debugging mode is not enabled, does + * nothing. + * + * This function must only be called on a non-added event. + * + * @see event_enable_debug_mode() + */ +EVENT2_EXPORT_SYMBOL +void event_debug_unassign(struct event *); + +/** + * Create and return a new event_base to use with the rest of Libevent. + * + * @return a new event_base on success, or NULL on failure. + * + * @see event_base_free(), event_base_new_with_config() + */ +EVENT2_EXPORT_SYMBOL +struct event_base *event_base_new(void); + +/** + Reinitialize the event base after a fork + + Some event mechanisms do not survive across fork. The event base needs + to be reinitialized with the event_reinit() function. + + @param base the event base that needs to be re-initialized + @return 0 if successful, or -1 if some events could not be re-added. + @see event_base_new() +*/ +EVENT2_EXPORT_SYMBOL +int event_reinit(struct event_base *base); + +/** + Event dispatching loop + + This loop will run the event base until either there are no more pending or + active, or until something calls event_base_loopbreak() or + event_base_loopexit(). + + @param base the event_base structure returned by event_base_new() or + event_base_new_with_config() + @return 0 if successful, -1 if an error occurred, or 1 if we exited because + no events were pending or active. + @see event_base_loop() + */ +EVENT2_EXPORT_SYMBOL +int event_base_dispatch(struct event_base *base); + +/** + Get the kernel event notification mechanism used by Libevent. + + @param eb the event_base structure returned by event_base_new() + @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) + */ +EVENT2_EXPORT_SYMBOL +const char *event_base_get_method(const struct event_base *eb); + +/** + Get the kernel signal handling mechanism used by Libevent. + + @param eb the event_base structure returned by event_base_new() + @return a string identifying the kernel signal handling mechanism, + which is "signal" for traditional UNIX signal handlers, + "kqueue_signal" for kqueue(2)-based method on *BSD and macOS, + and "signalfd_signal" for Linux-only signalfd(2)-based method. + */ +EVENT2_EXPORT_SYMBOL +const char *event_base_get_signal_method(const struct event_base *eb); + +/** + Gets all event notification mechanisms supported by Libevent. + + This functions returns the event mechanism in order preferred by + Libevent. Note that this list will include all backends that + Libevent has compiled-in support for, and will not necessarily check + your OS to see whether it has the required resources. + + @return an array with pointers to the names of support methods. + The end of the array is indicated by a NULL pointer. If an + error is encountered NULL is returned. +*/ +EVENT2_EXPORT_SYMBOL +const char **event_get_supported_methods(void); + +/** Query the current monotonic time from the timer for a struct + * event_base. + */ +EVENT2_EXPORT_SYMBOL +int event_gettime_monotonic(struct event_base *base, struct timeval *tp); + +/** + @name event type flag + + Flags to pass to event_base_get_num_events() to specify the kinds of events + we want to aggregate counts for +*/ +/**@{*/ +/** count the number of active events, which have been triggered.*/ +#define EVENT_BASE_COUNT_ACTIVE 1U +/** count the number of virtual events, which is used to represent an internal + * condition, other than a pending event, that keeps the loop from exiting. */ +#define EVENT_BASE_COUNT_VIRTUAL 2U +/** count the number of events which have been added to event base, including + * internal events. */ +#define EVENT_BASE_COUNT_ADDED 4U +/**@}*/ + +/** + Gets the number of events in event_base, as specified in the flags. + + Since event base has some internal events added to make some of its + functionalities work, EVENT_BASE_COUNT_ADDED may return more than the + number of events you added using event_add(). + + If you pass EVENT_BASE_COUNT_ACTIVE and EVENT_BASE_COUNT_ADDED together, an + active event will be counted twice. However, this might not be the case in + future libevent versions. The return value is an indication of the work + load, but the user shouldn't rely on the exact value as this may change in + the future. + + @param eb the event_base structure returned by event_base_new() + @param flags a bitwise combination of the kinds of events to aggregate + counts for + @return the number of events specified in the flags +*/ +EVENT2_EXPORT_SYMBOL +int event_base_get_num_events(struct event_base *eb, unsigned int flags); + +/** + Get the maximum number of events in a given event_base as specified in the + flags. + + @param eb the event_base structure returned by event_base_new() + @param flags a bitwise combination of the kinds of events to aggregate + counts for + @param clear option used to reset the maximum count. + @return the number of events specified in the flags + */ +EVENT2_EXPORT_SYMBOL +int event_base_get_max_events(struct event_base *eb, unsigned int flags, int clear); + +/** + Allocates a new event configuration object. + + The event configuration object can be used to change the behavior of + an event base. + + @return an event_config object that can be used to store configuration, or + NULL if an error is encountered. + @see event_base_new_with_config(), event_config_free(), event_config +*/ +EVENT2_EXPORT_SYMBOL +struct event_config *event_config_new(void); + +/** + Deallocates all memory associated with an event configuration object + + @param cfg the event configuration object to be freed. +*/ +EVENT2_EXPORT_SYMBOL +void event_config_free(struct event_config *cfg); + +/** + Enters an event method that should be avoided into the configuration. + + This can be used to avoid event mechanisms that do not support certain + file descriptor types, or for debugging to avoid certain event + mechanisms. An application can make use of multiple event bases to + accommodate incompatible file descriptor types. + + @param cfg the event configuration object + @param method the name of the event method to avoid + @return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int event_config_avoid_method(struct event_config *cfg, const char *method); + +/** + A flag used to describe which features an event_base (must) provide. + + Because of OS limitations, not every Libevent backend supports every + possible feature. You can use this type with + event_config_require_features() to tell Libevent to only proceed if your + event_base implements a given feature, and you can receive this type from + event_base_get_features() to see which features are available. +*/ +enum event_method_feature { + /** Require an event method that allows edge-triggered events with EV_ET. */ + EV_FEATURE_ET = 0x01, + /** Require an event method where having one event triggered among + * many is [approximately] an O(1) operation. This excludes (for + * example) select and poll, which are approximately O(N) for N + * equal to the total number of possible events. */ + EV_FEATURE_O1 = 0x02, + /** Require an event method that allows file descriptors as well as + * sockets. */ + EV_FEATURE_FDS = 0x04, + /** Require an event method that allows you to use EV_CLOSED to detect + * connection close without the necessity of reading all the pending data. + * + * Methods that do support EV_CLOSED may not be able to provide support on + * all kernel versions. + **/ + EV_FEATURE_EARLY_CLOSE = 0x08 +}; + +/** + A flag passed to event_config_set_flag(). + + These flags change the behavior of an allocated event_base. + + @see event_config_set_flag(), event_base_new_with_config(), + event_method_feature + */ +enum event_base_config_flag { + /** Do not allocate a lock for the event base, even if we have + locking set up. + + Setting this option will make it unsafe and nonfunctional to call + functions on the base concurrently from multiple threads_. + */ + EVENT_BASE_FLAG_NOLOCK = 0x01, + /** Do not check the EVENT_* environment variables when configuring + an event_base */ + EVENT_BASE_FLAG_IGNORE_ENV = 0x02, + /** Windows only: enable the IOCP dispatcher at startup + + If this flag is set then bufferevent_socket_new() and + evconn_listener_new() will use IOCP-backed implementations + instead of the usual select-based one on Windows. + + Note: it is experimental feature, and has some bugs. + */ + EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, + /** Instead of checking the current time every time the event loop is + ready to run timeout callbacks, check after each timeout callback. + */ + EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, + + /** If we are using the epoll backend, this flag says that it is + safe to use Libevent's internal change-list code to batch up + adds and deletes in order to try to do as few syscalls as + possible. Setting this flag can make your code run faster, but + it may trigger a Linux bug: it is not safe to use this flag + if you have any fds cloned by dup() or its variants. Doing so + will produce strange and hard-to-diagnose bugs. + + This flag can also be activated by setting the + EVENT_EPOLL_USE_CHANGELIST environment variable. + + This flag has no effect if you wind up using a backend other than + epoll. + */ + EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, + + /** Ordinarily, Libevent implements its time and timeout code using + the fastest monotonic timer that we have. If this flag is set, + however, we use less efficient more precise timer, assuming one is + present. + */ + EVENT_BASE_FLAG_PRECISE_TIMER = 0x20, + + /** With EVENT_BASE_FLAG_PRECISE_TIMER, + epoll backend will use timerfd for more accurate timers, this will + allows to disable this. + + That said that this is something in between lack of + (CLOCK_MONOTONIC_COARSE) and enabled EVENT_BASE_FLAG_PRECISE_TIMER + (CLOCK_MONOTONIC + timerfd). + + This flag has no effect if you wind up using a backend other than + epoll and if you do not have EVENT_BASE_FLAG_PRECISE_TIMER enabled. + */ + EVENT_BASE_FLAG_EPOLL_DISALLOW_TIMERFD = 0x40, + + /** Do not use signalfd(2) to handle signals even if supported. + */ + EVENT_BASE_FLAG_DISALLOW_SIGNALFD = 0x80, +}; + +/** + Return a bitmask of the features implemented by an event base. This + will be a bitwise OR of one or more of the values of + event_method_feature + + @see event_method_feature + */ +EVENT2_EXPORT_SYMBOL +int event_base_get_features(const struct event_base *base); + +/** + Enters a required event method feature that the application demands. + + Note that not every feature or combination of features is supported + on every platform. Code that requests features should be prepared + to handle the case where event_base_new_with_config() returns NULL, as in: +
+     event_config_require_features(cfg, EV_FEATURE_ET);
+     base = event_base_new_with_config(cfg);
+     if (base == NULL) {
+       // We can't get edge-triggered behavior here.
+       event_config_require_features(cfg, 0);
+       base = event_base_new_with_config(cfg);
+     }
+   
+ + @param cfg the event configuration object + @param feature a bitfield of one or more event_method_feature values. + Replaces values from previous calls to this function. + @return 0 on success, -1 on failure. + @see event_method_feature, event_base_new_with_config() +*/ +EVENT2_EXPORT_SYMBOL +int event_config_require_features(struct event_config *cfg, int feature); + +/** + * Sets one or more flags to configure what parts of the eventual event_base + * will be initialized, and how they'll work. + * + * @see event_base_config_flags, event_base_new_with_config() + **/ +EVENT2_EXPORT_SYMBOL +int event_config_set_flag(struct event_config *cfg, int flag); + +/** + * Records a hint for the number of CPUs in the system. This is used for + * tuning thread pools, etc, for optimal performance. In Libevent 2.0, + * it is only on Windows, and only when IOCP is in use. + * + * @param cfg the event configuration object + * @param cpus the number of cpus + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus); + +/** + * Record an interval and/or a number of callbacks after which the event base + * should check for new events. By default, the event base will run as many + * events are as activated at the highest activated priority before checking + * for new events. If you configure it by setting max_interval, it will check + * the time after each callback, and not allow more than max_interval to + * elapse before checking for new events. If you configure it by setting + * max_callbacks to a value >= 0, it will run no more than max_callbacks + * callbacks before checking for new events. + * + * This option can decrease the latency of high-priority events, and + * avoid priority inversions where multiple low-priority events keep us from + * polling for high-priority events, but at the expense of slightly decreasing + * the throughput. Use it with caution! + * + * @param cfg The event_base configuration object. + * @param max_interval An interval after which Libevent should stop running + * callbacks and check for more events, or NULL if there should be + * no such interval. + * @param max_callbacks A number of callbacks after which Libevent should + * stop running callbacks and check for more events, or -1 if there + * should be no such limit. + * @param min_priority A priority below which max_interval and max_callbacks + * should not be enforced. If this is set to 0, they are enforced + * for events of every priority; if it's set to 1, they're enforced + * for events of priority 1 and above, and so on. + * @return 0 on success, -1 on failure. + **/ +EVENT2_EXPORT_SYMBOL +int event_config_set_max_dispatch_interval(struct event_config *cfg, + const struct timeval *max_interval, int max_callbacks, + int min_priority); + +/** + Initialize the event API. + + Use event_base_new_with_config() to initialize a new event base, taking + the specified configuration under consideration. The configuration object + can currently be used to avoid certain event notification mechanisms. + + @param cfg the event configuration object + @return an initialized event_base that can be used to registering events, + or NULL if no event base can be created with the requested event_config. + @see event_base_new(), event_base_free(), event_init(), event_assign() +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *event_base_new_with_config(const struct event_config *cfg); + +/** + Deallocate all memory associated with an event_base, and free the base. + + Note that this function will not close any fds or free any memory passed + to event_new as the argument to callback. + + If there are any pending finalizer callbacks, this function will invoke + them. + + @param eb an event_base to be freed + */ +EVENT2_EXPORT_SYMBOL +void event_base_free(struct event_base *eb); + +/** + As event_base_free, but do not run finalizers. + */ +EVENT2_EXPORT_SYMBOL +void event_base_free_nofinalize(struct event_base *); + +/** @name Log severities + */ +/**@{*/ +#define EVENT_LOG_DEBUG 0 +#define EVENT_LOG_MSG 1 +#define EVENT_LOG_WARN 2 +#define EVENT_LOG_ERR 3 +/**@}*/ + +/* Obsolete names: these are deprecated, but older programs might use them. + * They violate the reserved-identifier namespace. */ +#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG +#define _EVENT_LOG_MSG EVENT_LOG_MSG +#define _EVENT_LOG_WARN EVENT_LOG_WARN +#define _EVENT_LOG_ERR EVENT_LOG_ERR + +/** + A callback function used to intercept Libevent's log messages. + + @see event_set_log_callback + */ +typedef void (*event_log_cb)(int severity, const char *msg); +/** + Redirect Libevent's log messages. + + @param cb a function taking two arguments: an integer severity between + EVENT_LOG_DEBUG and EVENT_LOG_ERR, and a string. If cb is NULL, + then the default log is used. + + NOTE: The function you provide *must not* call any other libevent + functionality. Doing so can produce undefined behavior. + */ +EVENT2_EXPORT_SYMBOL +void event_set_log_callback(event_log_cb cb); + +/** + A function to be called if Libevent encounters a fatal internal error. + + @see event_set_fatal_callback + */ +typedef void (*event_fatal_cb)(int err); + +/** + Override Libevent's behavior in the event of a fatal internal error. + + By default, Libevent will call exit(1) if a programming error makes it + impossible to continue correct operation. This function allows you to supply + another callback instead. Note that if the function is ever invoked, + something is wrong with your program, or with Libevent: any subsequent calls + to Libevent may result in undefined behavior. + + Libevent will (almost) always log an EVENT_LOG_ERR message before calling + this function; look at the last log message to see why Libevent has died. + */ +EVENT2_EXPORT_SYMBOL +void event_set_fatal_callback(event_fatal_cb cb); + +#define EVENT_DBG_ALL 0xffffffffu +#define EVENT_DBG_NONE 0 + +/** + Turn on debugging logs and have them sent to the default log handler. + + This is a global setting; if you are going to call it, you must call this + before any calls that create an event-base. You must call it before any + multithreaded use of Libevent. + + Debug logs are verbose. + + @param which Controls which debug messages are turned on. This option is + unused for now; for forward compatibility, you must pass in the constant + "EVENT_DBG_ALL" to turn debugging logs on, or "EVENT_DBG_NONE" to turn + debugging logs off. + */ +EVENT2_EXPORT_SYMBOL +void event_enable_debug_logging(ev_uint32_t which); + +/** + Associate a different event base with an event. + + The event to be associated must not be currently active or pending. + + @param eb the event base + @param ev the event + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_base_set(struct event_base *eb, struct event *ev); + +/** @name Loop flags + + These flags control the behavior of event_base_loop(). + */ +/**@{*/ +/** Block until we have an active event, then exit once all active events + * have had their callbacks run. */ +#define EVLOOP_ONCE 0x01 +/** Do not block: see which events are ready now, run the callbacks + * of the highest-priority ones, then exit. */ +#define EVLOOP_NONBLOCK 0x02 +/** Do not exit the loop because we have no pending events. Instead, keep + * running until event_base_loopexit() or event_base_loopbreak() makes us + * stop. + */ +#define EVLOOP_NO_EXIT_ON_EMPTY 0x04 +/**@}*/ + +/** + Wait for events to become active, and run their callbacks. + + This is a more flexible version of event_base_dispatch(). + + By default, this loop will run the event base until either there are no more + pending or active events, or until something calls event_base_loopbreak() or + event_base_loopexit(). You can override this behavior with the 'flags' + argument. + + @param eb the event_base structure returned by event_base_new() or + event_base_new_with_config() + @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK | + EVLOOP_NO_EXIT_ON_EMPTY + @return 0 if successful, -1 if an error occurred, or 1 if we exited because + no events were pending or active. + @see event_base_loopexit(), event_base_dispatch(), EVLOOP_ONCE, + EVLOOP_NONBLOCK + */ +EVENT2_EXPORT_SYMBOL +int event_base_loop(struct event_base *eb, int flags); + +/** + Exit the event loop after the specified time + + The next event_base_loop() iteration after the given timer expires will + complete normally (handling all queued events) then exit without + blocking for events again. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @param tv the amount of time after which the loop should terminate, + or NULL to exit after running all currently active events. + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopexit(struct event_base *eb, const struct timeval *tv); + +/** + Abort the active event_base_loop() immediately. + + event_base_loop() will abort the loop after the next event is completed; + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopexit() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopbreak(struct event_base *eb); + +/** + Tell the active event_base_loop() to scan for new events immediately. + + Calling this function makes the currently active event_base_loop() + start the loop over again (scanning for new events) after the current + event callback finishes. If the event loop is not running, this + function has no effect. + + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "continue;" statement. + + Subsequent invocations of event loop will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopcontinue(struct event_base *eb); + +/** + Checks if the event loop was told to exit by event_base_loopexit(). + + This function will return true for an event_base at every point after + event_loopexit() is called, until the event loop is next entered. + + @param eb the event_base structure returned by event_init() + @return true if event_base_loopexit() was called on this event base, + or 0 otherwise + @see event_base_loopexit() + @see event_base_got_break() + */ +EVENT2_EXPORT_SYMBOL +int event_base_got_exit(struct event_base *eb); + +/** + Checks if the event loop was told to abort immediately by event_base_loopbreak(). + + This function will return true for an event_base at every point after + event_base_loopbreak() is called, until the event loop is next entered. + + @param eb the event_base structure returned by event_init() + @return true if event_base_loopbreak() was called on this event base, + or 0 otherwise + @see event_base_loopbreak() + @see event_base_got_exit() + */ +EVENT2_EXPORT_SYMBOL +int event_base_got_break(struct event_base *eb); + +/** + * @name event flags + * + * Flags to pass to event_new(), event_assign(), event_pending(), and + * anything else with an argument of the form "short events" + */ +/**@{*/ +/** Indicates that a timeout has occurred. It's not necessary to pass + * this flag to event_for new()/event_assign() to get a timeout. */ +#define EV_TIMEOUT 0x01 +/** Wait for a socket or FD to become readable */ +#define EV_READ 0x02 +/** Wait for a socket or FD to become writeable */ +#define EV_WRITE 0x04 +/** Wait for a POSIX signal to be raised*/ +#define EV_SIGNAL 0x08 +/** + * Persistent event: won't get removed automatically when activated. + * + * When a persistent event with a timeout becomes activated, its timeout + * is reset to 0. + */ +#define EV_PERSIST 0x10 +/** Select edge-triggered behavior, if supported by the backend. */ +#define EV_ET 0x20 +/** + * If this option is provided, then event_del() will not block in one thread + * while waiting for the event callback to complete in another thread. + * + * To use this option safely, you may need to use event_finalize() or + * event_free_finalize() in order to safely tear down an event in a + * multithreaded application. See those functions for more information. + **/ +#define EV_FINALIZE 0x40 +/** + * Detects connection close events. You can use this to detect when a + * connection has been closed, without having to read all the pending data + * from a connection. + * + * Not all backends support EV_CLOSED. To detect or require it, use the + * feature flag EV_FEATURE_EARLY_CLOSE. + **/ +#define EV_CLOSED 0x80 +/**@}*/ + +/** + @name evtimer_* macros + + Aliases for working with one-shot timer events + If you need EV_PERSIST timer use event_*() functions. + */ +/**@{*/ +#define evtimer_assign(ev, b, cb, arg) \ + event_assign((ev), (b), -1, 0, (cb), (arg)) +#define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg)) +#define evtimer_add(ev, tv) event_add((ev), (tv)) +#define evtimer_del(ev) event_del(ev) +#define evtimer_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv)) +#define evtimer_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name evsignal_* macros + + Aliases for working with signal events + */ +/**@{*/ +#define evsignal_add(ev, tv) event_add((ev), (tv)) +#define evsignal_assign(ev, b, x, cb, arg) \ + event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg)) +#define evsignal_new(b, x, cb, arg) \ + event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) +#define evsignal_del(ev) event_del(ev) +#define evsignal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv)) +#define evsignal_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name evuser_* macros + + Aliases for working with user-triggered events + If you need EV_PERSIST event use event_*() functions. + */ +/**@{*/ +#define evuser_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg)) +#define evuser_del(ev) event_del(ev) +#define evuser_pending(ev, tv) event_pending((ev), 0, (tv)) +#define evuser_initialized(ev) event_initialized(ev) +#define evuser_trigger(ev) event_active((ev), 0, 0) +/**@}*/ + +/** + A callback function for an event. + + It receives three arguments: + + @param fd An fd or signal + @param events One or more EV_* flags + @param arg A user-supplied argument. + + @see event_new() + */ +typedef void (*event_callback_fn)(evutil_socket_t, short, void *); + +/** + Return a value used to specify that the event itself must be used as the callback argument. + + The function event_new() takes a callback argument which is passed + to the event's callback function. To specify that the argument to be + passed to the callback function is the event that event_new() returns, + pass in the return value of event_self_cbarg() as the callback argument + for event_new(). + + For example: +
+      struct event *ev = event_new(base, sock, events, callback, %event_self_cbarg());
+  
+ + For consistency with event_new(), it is possible to pass the return value + of this function as the callback argument for event_assign() – this + achieves the same result as passing the event in directly. + + @return a value to be passed as the callback argument to event_new() or + event_assign(). + @see event_new(), event_assign() + */ +EVENT2_EXPORT_SYMBOL +void *event_self_cbarg(void); + +/** + Allocate and assign a new event structure, ready to be added. + + The function event_new() returns a new event that can be used in + future calls to event_add() and event_del(). The fd and events + arguments determine which conditions will trigger the event; the + callback and callback_arg arguments tell Libevent what to do when the + event becomes active. + + If events contains one of EV_READ, EV_WRITE, or EV_READ|EV_WRITE, then + fd is a file descriptor or socket that should get monitored for + readiness to read, readiness to write, or readiness for either operation + (respectively). If events contains EV_SIGNAL, then fd is a signal + number to wait for. If events contains none of those flags, then the + event can be triggered only by a timeout or by manual activation with + event_active(): In this case, fd must be -1. + + The EV_PERSIST flag can also be passed in the events argument: it makes + event_add() persistent until event_del() is called. + + The EV_ET flag is compatible with EV_READ and EV_WRITE, and supported + only by certain backends. It tells Libevent to use edge-triggered + events. + + The EV_TIMEOUT flag has no effect here. + + It is okay to have multiple events all listening on the same fds; but + they must either all be edge-triggered, or not be edge-triggered at all. + + When the event becomes active, the event loop will run the provided + callback function, with three arguments. The first will be the provided + fd value. The second will be a bitfield of the events that triggered: + EV_READ, EV_WRITE, or EV_SIGNAL. Here the EV_TIMEOUT flag indicates + that a timeout occurred, and EV_ET indicates that an edge-triggered + event occurred. The third event will be the callback_arg pointer that + you provide. + + @param base the event base to which the event should be attached. + @param fd the file descriptor or signal to be monitored, or -1. + @param events desired events to monitor: bitfield of EV_READ, EV_WRITE, + EV_SIGNAL, EV_PERSIST, EV_ET. + @param callback callback function to be invoked when the event occurs + @param callback_arg an argument to be passed to the callback function + + @return a newly allocated struct event that must later be freed with + event_free() or NULL if an error occurred. + @see event_free(), event_add(), event_del(), event_assign() + */ +EVENT2_EXPORT_SYMBOL +struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *callback_arg); + + +/** + Prepare a new, already-allocated event structure to be added. + + The function event_assign() prepares the event structure ev to be used + in future calls to event_add() and event_del(). Unlike event_new(), it + doesn't allocate memory itself: it requires that you have already + allocated a struct event, probably on the heap. Doing this will + typically make your code depend on the size of the event structure, and + thereby create incompatibility with future versions of Libevent. + + The easiest way to avoid this problem is just to use event_new() and + event_free() instead. + + A slightly harder way to future-proof your code is to use + event_get_struct_event_size() to determine the required size of an event + at runtime. + + Note that it is NOT safe to call this function on an event that is + active or pending. Doing so WILL corrupt internal data structures in + Libevent, and lead to strange, hard-to-diagnose bugs. You _can_ use + event_assign to change an existing event, but only if it is not active + or pending! + + The arguments for this function, and the behavior of the events that it + makes, are as for event_new(). + + @param ev an event struct to be modified + @param base the event base to which ev should be attached. + @param fd the file descriptor to be monitored + @param events desired events to monitor; can be EV_READ and/or EV_WRITE + @param callback callback function to be invoked when the event occurs + @param callback_arg an argument to be passed to the callback function + + @return 0 if success, or -1 on invalid arguments. + + @see event_new(), event_add(), event_del(), event_base_once(), + event_get_struct_event_size() + */ +EVENT2_EXPORT_SYMBOL +int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *callback_arg); + +/** + Deallocate a struct event * returned by event_new(). + + If the event is pending or active, this function makes it non-pending + and non-active first. + */ +EVENT2_EXPORT_SYMBOL +void event_free(struct event *); + +/** + * Callback type for event_finalize and event_free_finalize(). + **/ +typedef void (*event_finalize_callback_fn)(struct event *, void *); +/** + @name Finalization functions + + These functions are used to safely tear down an event in a multithreaded + application. If you construct your events with EV_FINALIZE to avoid + deadlocks, you will need a way to remove an event in the certainty that + it will definitely not be running its callback when you deallocate it + and its callback argument. + + To do this, call one of event_finalize() or event_free_finalize with + 0 for its first argument, the event to tear down as its second argument, + and a callback function as its third argument. The callback will be + invoked as part of the event loop, with the event's priority. + + After you call a finalizer function, event_add() and event_active() will + no longer work on the event, and event_del() will produce a no-op. You + must not try to change the event's fields with event_assign() or + event_set() while the finalize callback is in progress. Once the + callback has been invoked, you should treat the event structure as + containing uninitialized memory. + + The event_free_finalize() function frees the event after it's finalized; + event_finalize() does not. + + A finalizer callback must not make events pending or active. It must not + add events, activate events, or attempt to "resuscitate" the event being + finalized in any way. + + @return 0 on success, -1 on failure. + */ +/**@{*/ +EVENT2_EXPORT_SYMBOL +int event_finalize(unsigned, struct event *, event_finalize_callback_fn); +EVENT2_EXPORT_SYMBOL +int event_free_finalize(unsigned, struct event *, event_finalize_callback_fn); +/**@}*/ + +/** + Schedule a one-time event + + The function event_base_once() is similar to event_new(). However, it + schedules a callback to be called exactly once, and does not require the + caller to prepare an event structure. + + Note that in Libevent 2.0 and earlier, if the event is never triggered, the + internal memory used to hold it will never be freed. In Libevent 2.1, + the internal memory will get freed by event_base_free() if the event + is never triggered. The 'arg' value, however, will not get freed in either + case--you'll need to free that on your own if you want it to go away. + + @param base an event_base + @param fd a file descriptor to monitor, or -1 for no fd. + @param events event(s) to monitor; can be any of EV_READ | + EV_WRITE, or EV_TIMEOUT + @param callback callback function to be invoked when the event occurs + @param arg an argument to be passed to the callback function + @param timeout the maximum amount of time to wait for the event. NULL + makes an EV_READ/EV_WRITE event make forever; NULL makes an + EV_TIMEOUT event success immediately. + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int event_base_once(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *arg, const struct timeval *timeout); + +/** + Add an event to the set of pending events. + + The function event_add() schedules the execution of the event 'ev' when the + condition specified by event_assign() or event_new() occurs, or when the time + specified in timeout has elapsed. If a timeout is NULL, no timeout + occurs and the function will only be + called if a matching event occurs. The event in the + ev argument must be already initialized by event_assign() or event_new() + and may not be used + in calls to event_assign() until it is no longer pending. + + If the event in the ev argument already has a scheduled timeout, calling + event_add() replaces the old timeout with the new one if tv is non-NULL. + + @param ev an event struct initialized via event_assign() or event_new() + @param timeout the maximum amount of time to wait for the event, or NULL + to wait forever + @return 0 if successful, or -1 if an error occurred + @see event_del(), event_assign(), event_new() + */ +EVENT2_EXPORT_SYMBOL +int event_add(struct event *ev, const struct timeval *timeout); + +/** + Remove a timer from a pending event without removing the event itself. + + If the event has a scheduled timeout, this function unschedules it but + leaves the event otherwise pending. + + @param ev an event struct initialized via event_assign() or event_new() + @return 0 on success, or -1 if an error occurred. +*/ +EVENT2_EXPORT_SYMBOL +int event_remove_timer(struct event *ev); + +/** + Remove an event from the set of monitored events. + + The function event_del() will cancel the event in the argument ev. If the + event has already executed or has never been added the call will have no + effect. + + @param ev an event struct to be removed from the working set + @return 0 if successful, or -1 if an error occurred + @see event_add() + */ +EVENT2_EXPORT_SYMBOL +int event_del(struct event *ev); + +/** + As event_del(), but never blocks while the event's callback is running + in another thread, even if the event was constructed without the + EV_FINALIZE flag. + */ +EVENT2_EXPORT_SYMBOL +int event_del_noblock(struct event *ev); +/** + As event_del(), but always blocks while the event's callback is running + in another thread, even if the event was constructed with the + EV_FINALIZE flag. + */ +EVENT2_EXPORT_SYMBOL +int event_del_block(struct event *ev); + +/** + Make an event active. + + You can use this function on a pending or a non-pending event to make it + active, so that its callback will be run by event_base_dispatch() or + event_base_loop(). + + One common use in multithreaded programs is to wake the thread running + event_base_loop() from another thread. + + @param ev an event to make active. + @param res a set of flags to pass to the event's callback. + @param ncalls an obsolete argument: this is ignored. + **/ +EVENT2_EXPORT_SYMBOL +void event_active(struct event *ev, int res, short ncalls); + +/** + Checks if a specific event is pending or scheduled. + + @param ev an event struct previously passed to event_add() + @param events the requested event type; any of EV_TIMEOUT|EV_READ| + EV_WRITE|EV_SIGNAL + @param tv if this field is not NULL, and the event has a timeout, + this field is set to hold the time at which the timeout will + expire. + + @return true if the event is pending on any of the events in 'what', (that + is to say, it has been added), or 0 if the event is not added. + */ +EVENT2_EXPORT_SYMBOL +int event_pending(const struct event *ev, short events, struct timeval *tv); + +/** + If called from within the callback for an event, returns that event. + + The behavior of this function is not defined when called from outside the + callback function for an event. + */ +EVENT2_EXPORT_SYMBOL +struct event *event_base_get_running_event(struct event_base *base); + +/** + Test if an event structure might be initialized. + + The event_initialized() function can be used to check if an event has been + initialized. + + Warning: This function is only useful for distinguishing a zeroed-out + piece of memory from an initialized event, it can easily be confused by + uninitialized memory. Thus, it should ONLY be used to distinguish an + initialized event from zero. + + @param ev an event structure to be tested + @return 1 if the structure might be initialized, or 0 if it has not been + initialized + */ +EVENT2_EXPORT_SYMBOL +int event_initialized(const struct event *ev); + +/** + Get the signal number assigned to a signal event +*/ +#define event_get_signal(ev) ((int)event_get_fd(ev)) + +/** + Get the socket or signal assigned to an event, or -1 if the event has + no socket. +*/ +EVENT2_EXPORT_SYMBOL +evutil_socket_t event_get_fd(const struct event *ev); + +/** + Get the event_base associated with an event. +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *event_get_base(const struct event *ev); + +/** + Return the events (EV_READ, EV_WRITE, etc) assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +short event_get_events(const struct event *ev); + +/** + Return the callback assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +event_callback_fn event_get_callback(const struct event *ev); + +/** + Return the callback argument assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +void *event_get_callback_arg(const struct event *ev); + +/** + Return the priority of an event. + @see event_priority_init(), event_get_priority() +*/ +EVENT2_EXPORT_SYMBOL +int event_get_priority(const struct event *ev); + +/** + Extract _all_ of arguments given to construct a given event. The + event_base is copied into *base_out, the fd is copied into *fd_out, and so + on. + + If any of the "_out" arguments is NULL, it will be ignored. + */ +EVENT2_EXPORT_SYMBOL +void event_get_assignment(const struct event *event, + struct event_base **base_out, evutil_socket_t *fd_out, short *events_out, + event_callback_fn *callback_out, void **arg_out); + +/** + Return the size of struct event that the Libevent library was compiled + with. + + This will be NO GREATER than sizeof(struct event) if you're running with + the same version of Libevent that your application was built with, but + otherwise might not. + + Note that it might be SMALLER than sizeof(struct event) if some future + version of Libevent adds extra padding to the end of struct event. + We might do this to help ensure ABI-compatibility between different + versions of Libevent. + */ +EVENT2_EXPORT_SYMBOL +size_t event_get_struct_event_size(void); + +/** + Get the Libevent version. + + Note that this will give you the version of the library that you're + currently linked against, not the version of the headers that you've + compiled against. + + @return a string containing the version number of Libevent +*/ +EVENT2_EXPORT_SYMBOL +const char *event_get_version(void); + +/** + Return a numeric representation of Libevent's version. + + Note that this will give you the version of the library that you're + currently linked against, not the version of the headers you've used to + compile. + + The format uses one byte each for the major, minor, and patchlevel parts of + the version number. The low-order byte is unused. For example, version + 2.0.1-alpha has a numeric representation of 0x02000100 +*/ +EVENT2_EXPORT_SYMBOL +ev_uint32_t event_get_version_number(void); + +/** As event_get_version, but gives the version of Libevent's headers. */ +#define LIBEVENT_VERSION EVENT__VERSION +/** As event_get_version_number, but gives the version number of Libevent's + * headers. */ +#define LIBEVENT_VERSION_NUMBER EVENT__NUMERIC_VERSION + +/** Largest number of priorities that Libevent can support. */ +#define EVENT_MAX_PRIORITIES 256 +/** + Set the number of different event priorities + + By default Libevent schedules all active events with the same priority. + However, some time it is desirable to process some events with a higher + priority than others. For that reason, Libevent supports strict priority + queues. Active events with a lower priority are always processed before + events with a higher priority. + + The number of different priorities can be set initially with the + event_base_priority_init() function. This function should be called + before the first call to event_base_dispatch(). The + event_priority_set() function can be used to assign a priority to an + event. By default, Libevent assigns the middle priority to all events + unless their priority is explicitly set. + + Note that urgent-priority events can starve less-urgent events: after + running all urgent-priority callbacks, Libevent checks for more urgent + events again, before running less-urgent events. Less-urgent events + will not have their callbacks run until there are no events more urgent + than them that want to be active. + + @param eb the event_base structure returned by event_base_new() + @param npriorities the maximum number of priorities + @return 0 if successful, or -1 if an error occurred + @see event_priority_set() + */ +EVENT2_EXPORT_SYMBOL +int event_base_priority_init(struct event_base *eb, int npriorities); + +/** + Get the number of different event priorities. + + @param eb the event_base structure returned by event_base_new() + @return Number of different event priorities + @see event_base_priority_init() +*/ +EVENT2_EXPORT_SYMBOL +int event_base_get_npriorities(struct event_base *eb); + +/** + Assign a priority to an event. + + @param ev an event struct + @param priority the new priority to be assigned + @return 0 if successful, or -1 if an error occurred + @see event_priority_init(), event_get_priority() + */ +EVENT2_EXPORT_SYMBOL +int event_priority_set(struct event *ev, int priority); + +/** + Prepare an event_base to use a large number of timeouts with the same + duration. + + Libevent's default scheduling algorithm is optimized for having a large + number of timeouts with their durations more or less randomly + distributed. But if you have a large number of timeouts that all have + the same duration (for example, if you have a large number of + connections that all have a 10-second timeout), then you can improve + Libevent's performance by telling Libevent about it. + + To do this, call this function with the common duration. It will return a + pointer to a different, opaque timeout value. (Don't depend on its actual + contents!) When you use this timeout value in event_add(), Libevent will + schedule the event more efficiently. + + (This optimization probably will not be worthwhile until you have thousands + or tens of thousands of events with the same timeout.) + */ +EVENT2_EXPORT_SYMBOL +const struct timeval *event_base_init_common_timeout(struct event_base *base, + const struct timeval *duration); + +#if !defined(EVENT__DISABLE_MM_REPLACEMENT) || defined(EVENT_IN_DOXYGEN_) +/** + Override the functions that Libevent uses for memory management. + + Usually, Libevent uses the standard libc functions malloc, realloc, and + free to allocate memory. Passing replacements for those functions to + event_set_mem_functions() overrides this behavior. + + Note that all memory returned from Libevent will be allocated by the + replacement functions rather than by malloc() and realloc(). Thus, if you + have replaced those functions, it will not be appropriate to free() memory + that you get from Libevent. Instead, you must use the free_fn replacement + that you provided. + + Note also that if you are going to call this function, you should do so + before any call to any Libevent function that does allocation. + Otherwise, those functions will allocate their memory using malloc(), but + then later free it using your provided free_fn. + + @param malloc_fn A replacement for malloc. + @param realloc_fn A replacement for realloc + @param free_fn A replacement for free. + **/ +EVENT2_EXPORT_SYMBOL +void event_set_mem_functions( + void *(*malloc_fn)(size_t sz), + void *(*realloc_fn)(void *ptr, size_t sz), + void (*free_fn)(void *ptr)); +/** This definition is present if Libevent was built with support for + event_set_mem_functions() */ +#define EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED +#endif + +/** + Writes a human-readable description of all inserted and/or active + events to a provided stdio stream. + + This is intended for debugging; its format is not guaranteed to be the same + between libevent versions. + + @param base An event_base on which to scan the events. + @param output A stdio file to write on. + */ +EVENT2_EXPORT_SYMBOL +void event_base_dump_events(struct event_base *base, FILE *output); + + +/** + Activates all pending events for the given fd and event mask. + + This function activates pending events only. Events which have not been + added will not become active. + + @param base the event_base on which to activate the events. + @param fd An fd to active events on. + @param events One or more of EV_{READ,WRITE,TIMEOUT}. + */ +EVENT2_EXPORT_SYMBOL +void event_base_active_by_fd(struct event_base *base, evutil_socket_t fd, short events); + +/** + Activates all pending signals with a given signal number + + This function activates pending events only. Events which have not been + added will not become active. + + @param base the event_base on which to activate the events. + @param sig The signal to active events on. + */ +EVENT2_EXPORT_SYMBOL +void event_base_active_by_signal(struct event_base *base, int sig); + +/** + * Callback for iterating events in an event base via event_base_foreach_event + */ +typedef int (*event_base_foreach_event_cb)(const struct event_base *, const struct event *, void *); + +/** + Iterate over all added or active events events in an event loop, and invoke + a given callback on each one. + + The callback must not call any function that modifies the event base, that + modifies any event in the event base, or that adds or removes any event to + the event base. Doing so is unsupported and will lead to undefined + behavior -- likely, to crashes. + + event_base_foreach_event() holds a lock on the event_base() for the whole + time it's running: slow callbacks are not advisable. + + Note that Libevent adds some events of its own to make pieces of its + functionality work. You must not assume that the only events you'll + encounter will be the ones you added yourself. + + The callback function must return 0 to continue iteration, or some other + integer to stop iterating. + + @param base An event_base on which to scan the events. + @param fn A callback function to receive the events. + @param arg An argument passed to the callback function. + @return 0 if we iterated over every event, or the value returned by the + callback function if the loop exited early. +*/ +EVENT2_EXPORT_SYMBOL +int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg); + + +/** Sets 'tv' to the current time (as returned by gettimeofday()), + looking at the cached value in 'base' if possible, and calling + gettimeofday() or clock_gettime() as appropriate if there is no + cached time. + + Generally, this value will only be cached while actually + processing event callbacks, and may be very inaccurate if your + callbacks take a long time to execute. + + Returns 0 on success, negative on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_base_gettimeofday_cached(struct event_base *base, + struct timeval *tv); + +/** Update cached_tv in the 'base' to the current time + * + * You can use this function is useful for selectively increasing + * the accuracy of the cached time value in 'base' during callbacks + * that take a long time to execute. + * + * This function has no effect if the base is currently not in its + * event loop, or if timeval caching is disabled via + * EVENT_BASE_FLAG_NO_CACHE_TIME. + * + * @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int event_base_update_cache_time(struct event_base *base); + +/** Release up all globally-allocated resources allocated by Libevent. + + This function does not free developer-controlled resources like + event_bases, events, bufferevents, listeners, and so on. It only releases + resources like global locks that there is no other way to free. + + It is not actually necessary to call this function before exit: every + resource that it frees would be released anyway on exit. It mainly exists + so that resource-leak debugging tools don't see Libevent as holding + resources at exit. + + You should only call this function when no other Libevent functions will + be invoked -- e.g., when cleanly exiting a program. + */ +EVENT2_EXPORT_SYMBOL +void libevent_global_shutdown(void); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_H_INCLUDED_ */ diff --git a/include/libevent/event2/event_compat.h b/include/libevent/event2/event_compat.h new file mode 100644 index 0000000..fecdb3b --- /dev/null +++ b/include/libevent/event2/event_compat.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_EVENT_COMPAT_H_INCLUDED_ +#define EVENT2_EVENT_COMPAT_H_INCLUDED_ + +/** @file event2/event_compat.h + + @brief Potentially non-threadsafe versions of the functions in event.h: provided + only for backwards compatibility. + + In the oldest versions of Libevent, event_base was not a first-class + structure. Instead, there was a single event base that every function + manipulated. Later, when separate event bases were added, the old functions + that didn't take an event_base argument needed to work by manipulating the + "current" event base. This could lead to thread-safety issues, and obscure, + hard-to-diagnose bugs. + + @deprecated All functions in this file are by definition deprecated. + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + Initialize the event API. + + The event API needs to be initialized with event_init() before it can be + used. Sets the global current base that gets used for events that have no + base associated with them. + + @deprecated This function is deprecated because it replaces the "current" + event_base, and is totally unsafe for multithreaded use. The replacement + is event_base_new(). + + @see event_base_set(), event_base_new() + */ +EVENT2_EXPORT_SYMBOL +struct event_base *event_init(void); + +/** + Loop to process events. + + Like event_base_dispatch(), but uses the "current" base. + + @deprecated This function is deprecated because it is easily confused by + multiple calls to event_init(), and because it is not safe for + multithreaded use. The replacement is event_base_dispatch(). + + @see event_base_dispatch(), event_init() + */ +EVENT2_EXPORT_SYMBOL +int event_dispatch(void); + +/** + Handle events. + + This function behaves like event_base_loop(), but uses the "current" base + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loop(). + + @see event_base_loop(), event_init() +*/ +EVENT2_EXPORT_SYMBOL +int event_loop(int); + + +/** + Exit the event loop after the specified time. + + This function behaves like event_base_loopexit(), except that it uses the + "current" base. + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loopexit(). + + @see event_init, event_base_loopexit() + */ +EVENT2_EXPORT_SYMBOL +int event_loopexit(const struct timeval *); + + +/** + Abort the active event_loop() immediately. + + This function behaves like event_base_loopbreakt(), except that it uses the + "current" base. + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loopbreak(). + + @see event_base_loopbreak(), event_init() + */ +EVENT2_EXPORT_SYMBOL +int event_loopbreak(void); + +/** + Schedule a one-time event to occur. + + @deprecated This function is obsolete, and has been replaced by + event_base_once(). Its use is deprecated because it relies on the + "current" base configured by event_init(). + + @see event_base_once() + */ +EVENT2_EXPORT_SYMBOL +int event_once(evutil_socket_t , short, + void (*)(evutil_socket_t, short, void *), void *, const struct timeval *); + + +/** + Get the kernel event notification mechanism used by Libevent. + + @deprecated This function is obsolete, and has been replaced by + event_base_get_method(). Its use is deprecated because it relies on the + "current" base configured by event_init(). + + @see event_base_get_method() + */ +EVENT2_EXPORT_SYMBOL +const char *event_get_method(void); + + +/** + Set the number of different event priorities. + + @deprecated This function is deprecated because it is easily confused by + multiple calls to event_init(), and because it is not safe for + multithreaded use. The replacement is event_base_priority_init(). + + @see event_base_priority_init() + */ +EVENT2_EXPORT_SYMBOL +int event_priority_init(int); + +/** + Prepare an event structure to be added. + + @deprecated event_set() is not recommended for new code, because it requires + a subsequent call to event_base_set() to be safe under most circumstances. + Use event_assign() or event_new() instead. + */ +EVENT2_EXPORT_SYMBOL +void event_set(struct event *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *); + +#define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) +#define evsignal_set(ev, x, cb, arg) \ + event_set((ev), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) + + +/** + @name timeout_* macros + + @deprecated These macros are deprecated because their naming is inconsistent + with the rest of Libevent. Use the evtimer_* macros instead. + @{ + */ +#define timeout_add(ev, tv) event_add((ev), (tv)) +#define timeout_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) +#define timeout_del(ev) event_del(ev) +#define timeout_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv)) +#define timeout_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name signal_* macros + + @deprecated These macros are deprecated because their naming is inconsistent + with the rest of Libevent. Use the evsignal_* macros instead. + @{ + */ +#define signal_add(ev, tv) event_add((ev), (tv)) +#define signal_set(ev, x, cb, arg) \ + event_set((ev), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) +#define signal_del(ev) event_del(ev) +#define signal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv)) +#define signal_initialized(ev) event_initialized(ev) +/**@}*/ + +#ifndef EVENT_FD +/* These macros are obsolete; use event_get_fd and event_get_signal instead. */ +#define EVENT_FD(ev) ((int)event_get_fd(ev)) +#define EVENT_SIGNAL(ev) event_get_signal(ev) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/include/libevent/event2/event_struct.h b/include/libevent/event2/event_struct.h new file mode 100644 index 0000000..34672e8 --- /dev/null +++ b/include/libevent/event2/event_struct.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_EVENT_STRUCT_H_INCLUDED_ +#define EVENT2_EVENT_STRUCT_H_INCLUDED_ + +/** @file event2/event_struct.h + + Structures used by event.h. Using these structures directly WILL harm + forward compatibility: be careful. + + No field declared in this file should be used directly in user code. Except + for historical reasons, these fields would not be exposed at all. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/* For evkeyvalq */ +#include + +#define EVLIST_TIMEOUT 0x01 +#define EVLIST_INSERTED 0x02 +#define EVLIST_SIGNAL 0x04 +#define EVLIST_ACTIVE 0x08 +#define EVLIST_INTERNAL 0x10 +#define EVLIST_ACTIVE_LATER 0x20 +#define EVLIST_FINALIZING 0x40 +#define EVLIST_INIT 0x80 + +#define EVLIST_ALL 0xff + +/* Fix so that people don't have to run with */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +#ifndef TAILQ_HEAD +#define EVENT_DEFINED_TQHEAD_ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; \ + struct type **tqh_last; \ +} +#endif + +/* Fix so that people don't have to run with */ +#ifndef LIST_ENTRY +#define EVENT_DEFINED_LISTENTRY_ +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} +#endif /* !LIST_ENTRY */ + +#ifndef LIST_HEAD +#define EVENT_DEFINED_LISTHEAD_ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ + } +#endif /* !LIST_HEAD */ + +struct event; + +struct event_callback { + TAILQ_ENTRY(event_callback) evcb_active_next; + short evcb_flags; + ev_uint8_t evcb_pri; /* smaller numbers are higher priority */ + ev_uint8_t evcb_closure; + /* allows us to adopt for different types of events */ + union { + void (*evcb_callback)(evutil_socket_t, short, void *); + void (*evcb_selfcb)(struct event_callback *, void *); + void (*evcb_evfinalize)(struct event *, void *); + void (*evcb_cbfinalize)(struct event_callback *, void *); + } evcb_cb_union; + void *evcb_arg; +}; + +struct event_base; +struct event { + struct event_callback ev_evcallback; + + /* for managing timeouts */ + union { + TAILQ_ENTRY(event) ev_next_with_common_timeout; + size_t min_heap_idx; + } ev_timeout_pos; + evutil_socket_t ev_fd; + + short ev_events; + short ev_res; /* result passed to event callback */ + + struct event_base *ev_base; + + union { + /* used for io events */ + struct { + LIST_ENTRY (event) ev_io_next; + struct timeval ev_timeout; + } ev_io; + + /* used by signal events */ + struct { + LIST_ENTRY (event) ev_signal_next; + short ev_ncalls; + /* Allows deletes in callback */ + short *ev_pncalls; + } ev_signal; + } ev_; + + + struct timeval ev_timeout; +}; + +TAILQ_HEAD (event_list, event); + +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef EVENT_DEFINED_TQHEAD_ +#undef TAILQ_HEAD +#endif + +LIST_HEAD (event_dlist, event); + +#ifdef EVENT_DEFINED_LISTENTRY_ +#undef LIST_ENTRY +#endif + +#ifdef EVENT_DEFINED_LISTHEAD_ +#undef LIST_HEAD +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_STRUCT_H_INCLUDED_ */ diff --git a/include/libevent/event2/http.h b/include/libevent/event2/http.h new file mode 100644 index 0000000..50c0a27 --- /dev/null +++ b/include/libevent/event2/http.h @@ -0,0 +1,1482 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_HTTP_H_INCLUDED_ +#define EVENT2_HTTP_H_INCLUDED_ + +/* For int types. */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* In case we haven't included the right headers yet. */ +struct evbuffer; +struct event_base; +struct bufferevent; +struct evhttp_connection; + +/** @file event2/http.h + * + * @brief Basic support for HTTP serving. + * + * As Libevent is a library for dealing with event notification and most + * interesting applications are networked today, I have often found the + * need to write HTTP code. The following prototypes and definitions provide + * an application with a minimal interface for making HTTP requests and for + * creating a very simple HTTP server. + */ + +/* Response codes */ +#define HTTP_CONTINUE 100 /**< client should proceed to send */ +#define HTTP_SWITCH_PROTOCOLS 101 /**< switching to another protocol */ +#define HTTP_PROCESSING 102 /**< processing the request, but no response is available yet */ +#define HTTP_EARLYHINTS 103 /**< return some response headers */ +#define HTTP_OK 200 /**< request completed ok */ +#define HTTP_CREATED 201 /**< new resource is created */ +#define HTTP_ACCEPTED 202 /**< accepted for processing */ +#define HTTP_NONAUTHORITATIVE 203 /**< returning a modified version of the origin's response */ +#define HTTP_NOCONTENT 204 /**< request does not have content */ +#define HTTP_MOVEPERM 301 /**< the uri moved permanently */ +#define HTTP_MOVETEMP 302 /**< the uri moved temporarily */ +#define HTTP_NOTMODIFIED 304 /**< page was not modified from last */ +#define HTTP_BADREQUEST 400 /**< invalid http request was made */ +#define HTTP_UNAUTHORIZED 401 /**< authentication is required */ +#define HTTP_PAYMENTREQUIRED 402 /**< user exceeded limit on requests */ +#define HTTP_FORBIDDEN 403 /**< user not having the necessary permissions */ +#define HTTP_NOTFOUND 404 /**< could not find content for uri */ +#define HTTP_BADMETHOD 405 /**< method not allowed for this uri */ +#define HTTP_ENTITYTOOLARGE 413 /**< request is larger than the server is able to process */ +#define HTTP_EXPECTATIONFAILED 417 /**< we can't handle this expectation */ +#define HTTP_INTERNAL 500 /**< internal error */ +#define HTTP_NOTIMPLEMENTED 501 /**< not implemented */ +#define HTTP_BADGATEWAY 502 /**< received an invalid response from the upstream */ +#define HTTP_SERVUNAVAIL 503 /**< the server is not available */ + +struct evhttp; +struct evhttp_request; +struct evkeyvalq; +struct evhttp_bound_socket; +struct evconnlistener; +struct evdns_base; +struct evhttp_ext_method; + +/** + * Create a new HTTP server. + * + * @param base (optional) the event base to receive the HTTP events + * @return a pointer to a newly initialized evhttp server structure or NULL + * on error + * @see evhttp_free() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_new(struct event_base *base); + +/** + * Binds an HTTP server on the specified address and port. + * + * Can be called multiple times to bind the same http server + * to multiple different ports. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return 0 on success, -1 on failure. + * @see evhttp_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +int evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port); + +/** + * Like evhttp_bind_socket(), but returns a handle for referencing the socket. + * + * The returned pointer is not valid after \a http is freed. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return Handle for the socket on success, NULL on failure. + * @see evhttp_bind_socket(), evhttp_del_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port); + +/** + * Makes an HTTP server accept connections on the specified socket. + * + * This may be useful to create a socket and then fork multiple instances + * of an http server, or when a socket has been communicated via file + * descriptor passing in situations where an http servers does not have + * permissions to bind to a low-numbered port. + * + * Can be called multiple times to have the http server listen to + * multiple different sockets. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return 0 on success, -1 on failure. + * @see evhttp_bind_socket() + */ +EVENT2_EXPORT_SYMBOL +int evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd); + +/** + * Like evhttp_accept_socket(), but returns a handle for referencing the socket. + * + * The returned pointer is not valid after \a http is freed. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return Handle for the socket on success, NULL on failure. + * @see evhttp_accept_socket(), evhttp_del_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd); + +/** + * The most low-level evhttp_bind/accept method: takes an evconnlistener, and + * returns an evhttp_bound_socket. The listener will be freed when the bound + * socket is freed. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener); + +/** + * Return the listener used to implement a bound socket. + */ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound); + +/* + * Like evhttp_set_bevcb. + * If cb returns a non-NULL bufferevent, * the callback supplied through + * evhttp_set_bevcb isn't used. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_bound_set_bevcb(struct evhttp_bound_socket *bound, struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg); + +typedef void evhttp_bound_socket_foreach_fn(struct evhttp_bound_socket *, void *); +/** + * Applies the function specified in the first argument to all + * evhttp_bound_sockets associated with "http". The user must not + * attempt to free or remove any connections, sockets or listeners + * in the callback "function". + * + * @param http pointer to an evhttp object + * @param function function to apply to every bound socket + * @param argument pointer value passed to function for every socket iterated + */ +EVENT2_EXPORT_SYMBOL +void evhttp_foreach_bound_socket(struct evhttp *http, evhttp_bound_socket_foreach_fn *function, void *argument); + +/** + * Makes an HTTP server stop accepting connections on the specified socket + * + * This may be useful when a socket has been sent via file descriptor passing + * and is no longer needed by the current process. + * + * If you created this bound socket with evhttp_bind_socket_with_handle or + * evhttp_accept_socket_with_handle, this function closes the fd you provided. + * If you created this bound socket with evhttp_bind_listener, this function + * frees the listener you provided. + * + * \a bound_socket is an invalid pointer after this call returns. + * + * @param http a pointer to an evhttp object + * @param bound_socket a handle returned by evhttp_{bind,accept}_socket_with_handle + * @see evhttp_bind_socket_with_handle(), evhttp_accept_socket_with_handle() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound_socket); + +/** + * Get the raw file descriptor referenced by an evhttp_bound_socket. + * + * @param bound_socket a handle returned by evhttp_{bind,accept}_socket_with_handle + * @return the file descriptor used by the bound socket + * @see evhttp_bind_socket_with_handle(), evhttp_accept_socket_with_handle() + */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound_socket); + +/** + * Free the previously created HTTP server. + * + * Works only if no requests are currently being served. + * + * @param http the evhttp server object to be freed + * @see evhttp_start() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_free(struct evhttp* http); + +/** XXX Document. */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size); +/** XXX Document. */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size); + +/** + * Set the maximum number of simultaneous connections for this server. + * A value of zero or less disables the limit. + * + * @param http the http server on which to set the max connection limit + * @param max_connections the maximum number of simultaneous connections or 0 + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_connections(struct evhttp* http, int max_connections); + +/** + * Get the current number of connections. + * + * @return The current number of connections for this server. + */ +EVENT2_EXPORT_SYMBOL +int evhttp_get_connection_count(struct evhttp* http); + +/** + Set the value to use for the Content-Type header when none was provided. If + the content type string is NULL, the Content-Type header will not be + automatically added. + + @param http the http server on which to set the default content type + @param content_type the value for the Content-Type header +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_default_content_type(struct evhttp *http, + const char *content_type); + +/** + Sets the what HTTP methods are supported in requests accepted by this + server, and passed to user callbacks. + + If not supported they will generate a "405 Method not allowed" response. + + By default this includes the following methods: GET, POST, HEAD, PUT, DELETE + + @param http the http server on which to set the methods + @param methods bit mask constructed from evhttp_cmd_type values +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_allowed_methods(struct evhttp* http, ev_uint32_t methods); + +typedef int (*evhttp_ext_method_cb)(struct evhttp_ext_method *); +/** + Sets the callback function which allows HTTP extended methods + to be supported by this server. + + The callback should : + - if method field is NULL : set method field according to type field + - else : set type and flags fields according to method string + - return 0 for success (known method / type) + - return -1 for error (unknown method / type) + + evhttp_set_allowed_methods still needs to be called. + + @param http the http server on which to add support to the methods + @param cmp the extended method callback + @see evhttp_ext_method +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_ext_method_cmp(struct evhttp *http, evhttp_ext_method_cb cmp); + +/** + Set a callback for a specified URI + + @param http the http sever on which to set the callback + @param path the path for which to invoke the callback + @param cb the callback function that gets invoked on requesting path + @param cb_arg an additional context argument for the callback + @return 0 on success, -1 if the callback existed already, -2 on failure +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_set_cb(struct evhttp *http, const char *path, + void (*cb)(struct evhttp_request *, void *), void *cb_arg); + +/** Removes the callback for a specified URI */ +EVENT2_EXPORT_SYMBOL +int evhttp_del_cb(struct evhttp *, const char *); + +/** + Set a callback for all requests that are not caught by specific callbacks + + Invokes the specified callback for all requests that do not match any of + the previously specified request paths. This is catchall for requests not + specifically configured with evhttp_set_cb(). + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for any unmatched requests + @param arg an context argument for the callback +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_gencb(struct evhttp *http, + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** + Set a callback used to create new bufferevents for connections + to a given evhttp object. + cb is not called if a non-NULL bufferevent was supplied by + evhttp_bound_set_bevcb. + + You can use this to override the default bufferevent type -- for example, + to make this evhttp object use SSL bufferevents rather than unencrypted + ones. + + New bufferevents must be allocated with no fd set on them. + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for incoming connections + @param arg an context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_bevcb(struct evhttp *http, + struct bufferevent *(*cb)(struct event_base *, void *), void *arg); + + +/** + Set a callback which allows the user to note or throttle incoming requests. + + The requests are not populated with HTTP level information. They + are just associated to a connection. + + If the callback returns -1, the associated connection is terminated + and the request is closed. + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for incoming connections + @param arg an context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_newreqcb(struct evhttp *http, + int (*cb)(struct evhttp_request*, void *), void *arg); + +/** + Set a callback to output for any error pages sent for requests of a given + evhttp object. + + You can use this to override the default error pages sent, allowing such + things as multi-lingual support or customization to match other pages. + + The callback should use the supplied buffer to output the text for an + error page. If the callback returns a negative value or doesn't output + anything to the buffer, the default error page will be sent instead. The + buffer will be automatically be sent when the callback returns, so the + callback shouldn't do so itself. + + Microsoft Internet Explorer may display its own error pages if ones sent by + an HTTP server are smaller than certain sizes, depending on the status code. + To reliably suppress this feature an error page should be at least 512 + bytes in size. + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke to format error pages + @param arg an context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_errorcb(struct evhttp *http, + int (*cb)(struct evhttp_request *req, struct evbuffer *buffer, int error, const char *reason, void *cbarg), + void *cbarg); + +/** + Adds a virtual host to the http server. + + A virtual host is a newly initialized evhttp object that has request + callbacks set on it via evhttp_set_cb() or evhttp_set_gencb(). It + most not have any listing sockets associated with it. + + If the virtual host has not been removed by the time that evhttp_free() + is called on the main http server, it will be automatically freed, too. + + It is possible to have hierarchical vhosts. For example: A vhost + with the pattern *.example.com may have other vhosts with patterns + foo.example.com and bar.example.com associated with it. + + @param http the evhttp object to which to add a virtual host + @param pattern the glob pattern against which the hostname is matched. + The match is case insensitive and follows otherwise regular shell + matching. + @param vhost the virtual host to add the regular http server. + @return 0 on success, -1 on failure + @see evhttp_remove_virtual_host() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_virtual_host(struct evhttp* http, const char *pattern, + struct evhttp* vhost); + +/** + Removes a virtual host from the http server. + + @param http the evhttp object from which to remove the virtual host + @param vhost the virtual host to remove from the regular http server. + @return 0 on success, -1 on failure + @see evhttp_add_virtual_host() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost); + +/** + Add a server alias to an http object. The http object can be a virtual + host or the main server. + + @param http the evhttp object + @param alias the alias to add + @see evhttp_add_remove_alias() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_server_alias(struct evhttp *http, const char *alias); + +/** + Remove a server alias from an http object. + + @param http the evhttp object + @param alias the alias to remove + @see evhttp_add_server_alias() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_server_alias(struct evhttp *http, const char *alias); + +/** + * Set the timeout for an HTTP request. + * + * @param http an evhttp object + * @param timeout the timeout, in seconds + * @see evhttp_set_timeout_tv() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_timeout(struct evhttp *http, int timeout); + +/** + * Set read and write timeout for an HTTP request. + * + * @param http an evhttp object + * @param tv the timeout, or NULL + * + * For more precise control: + * @see evhttp_set_read_timeout_tv() + * @see evhttp_set_write_timeout_tv() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_timeout_tv(struct evhttp *http, const struct timeval* tv); + +/** + * Set read timeout for an HTTP request. + * + * @param http an evhttp object + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_read_timeout_tv(struct evhttp *http, const struct timeval* tv); + +/** + * Set write timeout for an HTTP request. + * + * @param http an evhttp object + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_write_timeout_tv(struct evhttp *http, const struct timeval* tv); + +/* Read all the clients body, and only after this respond with an error if the + * clients body exceed max_body_size */ +#define EVHTTP_SERVER_LINGERING_CLOSE 0x0001 +/** + * Set connection flags for HTTP server. + * + * @see EVHTTP_SERVER_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_set_flags(struct evhttp *http, int flags); + +/* Request/Response functionality */ + +/** + * Send an HTML error message to the client. + * + * @param req a request object + * @param error the HTTP error code + * @param reason a brief explanation of the error. If this is NULL, we'll + * just use the standard meaning of the error code. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_send_error(struct evhttp_request *req, int error, + const char *reason); + +/** + * Send an HTML reply to the client. + * + * The body of the reply consists of the data in databuf. After calling + * evhttp_send_reply() databuf will be empty, but the buffer is still + * owned by the caller and needs to be deallocated by the caller if + * necessary. + * + * @param req a request object + * @param code the HTTP response code to send + * @param reason a brief message to send with the response code + * @param databuf the body of the response + */ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply(struct evhttp_request *req, int code, + const char *reason, struct evbuffer *databuf); + +/* Low-level response interface, for streaming/chunked replies */ + +/** + Initiate a reply that uses Transfer-Encoding chunked. + + This allows the caller to stream the reply back to the client and is + useful when either not all of the reply data is immediately available + or when sending very large replies. + + The caller needs to supply data chunks with evhttp_send_reply_chunk() + and complete the reply by calling evhttp_send_reply_end(). + + @param req a request object + @param code the HTTP response code to send + @param reason a brief message to send with the response code +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_start(struct evhttp_request *req, int code, + const char *reason); + +/** + Send another data chunk as part of an ongoing chunked reply. + + The reply chunk consists of the data in databuf. After calling + evhttp_send_reply_chunk() databuf will be empty, but the buffer is + still owned by the caller and needs to be deallocated by the caller + if necessary. + + @param req a request object + @param databuf the data chunk to send as part of the reply. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_chunk(struct evhttp_request *req, + struct evbuffer *databuf); + +/** + Send another data chunk as part of an ongoing chunked reply. + + The reply chunk consists of the data in databuf. After calling + evhttp_send_reply_chunk() databuf will be empty, but the buffer is + still owned by the caller and needs to be deallocated by the caller + if necessary. + + @param req a request object + @param databuf the data chunk to send as part of the reply. + @param cb callback funcion + @param arg call back's argument. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_chunk_with_cb(struct evhttp_request *req, struct evbuffer *databuf, + void (*cb)(struct evhttp_connection *, void *), void *arg); + +/** + Complete a chunked reply, freeing the request as appropriate. + + @param req a request object +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_end(struct evhttp_request *req); + +/* + * Interfaces for making requests + */ + +/** The different request types supported by evhttp. These are as specified + * in RFC2616, except for: + * - PATCH which is specified by RFC5789 + * - PROPFIND, PROPPATCH, MKCOL, LOCK, UNLOCK, COPY, MOVE + * which are specified by RFC4918 + * + * By default, only some of these methods are accepted and passed to user + * callbacks; use evhttp_set_allowed_methods() to change which methods + * are allowed. + */ +enum evhttp_cmd_type { + EVHTTP_REQ_GET = 1 << 0, + EVHTTP_REQ_POST = 1 << 1, + EVHTTP_REQ_HEAD = 1 << 2, + EVHTTP_REQ_PUT = 1 << 3, + EVHTTP_REQ_DELETE = 1 << 4, + EVHTTP_REQ_OPTIONS = 1 << 5, + EVHTTP_REQ_TRACE = 1 << 6, + EVHTTP_REQ_CONNECT = 1 << 7, + EVHTTP_REQ_PATCH = 1 << 8, + EVHTTP_REQ_PROPFIND= 1 << 9, + EVHTTP_REQ_PROPPATCH=1 << 10, + EVHTTP_REQ_MKCOL = 1 << 11, + EVHTTP_REQ_LOCK = 1 << 12, + EVHTTP_REQ_UNLOCK = 1 << 13, + EVHTTP_REQ_COPY = 1 << 14, + EVHTTP_REQ_MOVE = 1 << 15, +}; + +#define EVHTTP_REQ_MAX EVHTTP_REQ_MOVE + +/** + * @brief stucture that is passed to (and modified by) the + * extended method callback function + * + * @see evhttp_set_ext_method_cmp + * @see evhttp_connection_set_ext_method_cmp + */ +struct evhttp_ext_method { + const char *method; + ev_uint32_t type; /* @see enum evhttp_cmd_type */ + ev_uint16_t flags; /* Available flag : EVHTTP_METHOD_HAS_BODY */ +}; + +#define EVHTTP_METHOD_HAS_BODY 0x0001 + +/** a request object can represent either a request or a reply */ +enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE }; + +/** + * Create and return a connection object that can be used to for making HTTP + * requests. The connection object tries to resolve address and establish the + * connection when it is given an http request object. + * + * Connection also has default timeouts for the following events: + * - connect HTTP_CONNECT_TIMEOUT, which is 45 seconds + * - read HTTP_READ_TIMEOUT which is 50 seconds + * - write HTTP_WRITE_TIMEOUT, which is 50 seconds + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param bev a bufferevent to use for connecting to the server; if NULL, a + * socket-based bufferevent will be created. This bufferevent will be freed + * when the connection closes. It must have no fd set on it. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests or + * NULL on error + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_bufferevent_new( + struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev, const char *address, ev_uint16_t port); + +/** + * Create and return a connection object that can be used to for making HTTP + * requests over an unix domain socket. + * + * @param base the event_base to use for handling the connection + * @param bev a bufferevent to use for connecting to the server; if NULL, a + * socket-based bufferevent will be created. This bufferevent will be freed + * when the connection closes. It must have no fd set on it. + * @param path path of unix domain socket + * @return an evhttp_connection object that can be used for making requests + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_bufferevent_unix_new( + struct event_base *base, struct bufferevent* bev, const char *path); + +/** + * Return the bufferevent that an evhttp_connection is using. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon); + +/** + * Return the HTTP server associated with this connection, or NULL. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_connection_get_server(struct evhttp_connection *evcon); + +/** + * Creates a new request object that needs to be filled in with the request + * parameters. The callback is executed when the request completed or an + * error occurred. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_request *evhttp_request_new( + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** + * Enable delivery of chunks to requestor. + * @param cb will be called after every read of data with the same argument + * as the completion callback. Will never be called on an empty + * response. May drain the input buffer; it will be drained + * automatically on return. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_chunked_cb(struct evhttp_request *, + void (*cb)(struct evhttp_request *, void *)); + +/** + * Register callback for additional parsing of request headers. + * @param cb will be called after receiving and parsing the full header. + * It allows analyzing the header and possibly closing the connection + * by returning a value < 0. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_header_cb(struct evhttp_request *, + int (*cb)(struct evhttp_request *, void *)); + +/** + * The different error types supported by evhttp + * + * @see evhttp_request_set_error_cb() + */ +enum evhttp_request_error { + /** + * Timeout reached, also @see evhttp_connection_set_timeout() + */ + EVREQ_HTTP_TIMEOUT, + /** + * EOF reached + */ + EVREQ_HTTP_EOF, + /** + * Error while reading header, or invalid header + */ + EVREQ_HTTP_INVALID_HEADER, + /** + * Error encountered while reading or writing + */ + EVREQ_HTTP_BUFFER_ERROR, + /** + * The evhttp_cancel_request() called on this request. + */ + EVREQ_HTTP_REQUEST_CANCEL, + /** + * Body is greater then evhttp_connection_set_max_body_size() + */ + EVREQ_HTTP_DATA_TOO_LONG +}; +/** + * Set a callback for errors + * @see evhttp_request_error for error types. + * + * On error, both the error callback and the regular callback will be called, + * error callback is called before the regular callback. + **/ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_error_cb(struct evhttp_request *, + void (*)(enum evhttp_request_error, void *)); + +/** + * Set a callback to be called on request completion of evhttp_send_* function. + * + * The callback function will be called on the completion of the request after + * the output data has been written and before the evhttp_request object + * is destroyed. This can be useful for tracking resources associated with a + * request (ex: timing metrics). + * + * @param req a request object + * @param cb callback function that will be called on request completion + * @param cb_arg an additional context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_on_complete_cb(struct evhttp_request *req, + void (*cb)(struct evhttp_request *, void *), void *cb_arg); + +/** Frees the request object and removes associated events. */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_free(struct evhttp_request *req); + +/** + * Create and return a connection object that can be used to for making HTTP + * requests. The connection object tries to resolve address and establish the + * connection when it is given an http request object. + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests or + * NULL on error + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_new( + struct event_base *base, struct evdns_base *dnsbase, + const char *address, ev_uint16_t port); + +/** + * Set family hint for DNS requests. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_family(struct evhttp_connection *evcon, + int family); + +/* reuse connection address on retry */ +#define EVHTTP_CON_REUSE_CONNECTED_ADDR 0x0008 +/* Try to read error, since server may already send and close + * connection, but if at that time we have some data to send then we + * can send get EPIPE and fail, while we can read that HTTP error. */ +#define EVHTTP_CON_READ_ON_WRITE_ERROR 0x0010 +/* @see EVHTTP_SERVER_LINGERING_CLOSE */ +#define EVHTTP_CON_LINGERING_CLOSE 0x0020 +/* Padding for public flags, @see EVHTTP_CON_* in http-internal.h */ +#define EVHTTP_CON_PUBLIC_FLAGS_END 0x100000 +/** + * Set connection flags. + * + * @see EVHTTP_CON_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_connection_set_flags(struct evhttp_connection *evcon, + int flags); + +/** Takes ownership of the request object + * + * Can be used in a request callback to keep onto the request until + * evhttp_request_free() is explicitly called by the user. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_own(struct evhttp_request *req); + +/** Returns 1 if the request is owned by the user */ +EVENT2_EXPORT_SYMBOL +int evhttp_request_is_owned(struct evhttp_request *req); + +/** + * Sets extended method cmp callback for this http connection. + * + * @see evhttp_set_ext_method_cmp + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_ext_method_cmp(struct evhttp_connection *evcon, + evhttp_ext_method_cb cmp); + +/** + * Returns the connection object associated with the request or NULL + * + * The user needs to either free the request explicitly or call + * evhttp_send_reply_end(). + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_request_get_connection(struct evhttp_request *req); + +/** + * Returns the underlying event_base for this connection + */ +EVENT2_EXPORT_SYMBOL +struct event_base *evhttp_connection_get_base(struct evhttp_connection *req); + +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_max_headers_size(struct evhttp_connection *evcon, + ev_ssize_t new_max_headers_size); + +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_max_body_size(struct evhttp_connection* evcon, + ev_ssize_t new_max_body_size); + +/** Frees an http connection */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_free(struct evhttp_connection *evcon); + +/** Disowns a given connection object + * + * Can be used to tell libevent to free the connection object after + * the last request has completed or failed. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_free_on_completion(struct evhttp_connection *evcon); + +/** Sets the IP address from which http connections are made + * + * Note this resets internal bufferevent fd, so any options that had been + * installed will be flushed. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_local_address(struct evhttp_connection *evcon, + const char *address); + +/** sets the local port from which http connections are made */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_local_port(struct evhttp_connection *evcon, + ev_uint16_t port); + +/** + * Sets the timeout for this connection. + * + * @see evhttp_connection_set_timeout_tv() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_timeout(struct evhttp_connection *evcon, + int timeout); + +/** + * Sets the timeout for this connection for the following events: + * - read, if tv==NULL then it uses default timeout (HTTP_READ_TIMEOUT) + * - write, if tv==NULL then it uses default timeout (HTTP_WRITE_TIMEOUT) + * + * But it does not adjust timeout for the "connect" (for historical reasons). + * + * @param tv the timeout, or NULL + * + * For more precise control: + * @see evhttp_connection_set_connect_timeout_tv() + * @see evhttp_connection_set_read_timeout_tv() + * @see evhttp_connection_set_write_timeout_tv() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** + * Sets the connect timeout for this connection + * + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_connect_timeout_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** + * Sets the read timeout for this connection + * + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_read_timeout_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** + * Sets the write timeout for this connection + * + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_write_timeout_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** + * Sets the delay before retrying requests on this connection. + * + * This is only used if evhttp_connection_set_retries is used to make the + * number of retries at least one. Each retry after the first is twice as long + * as the one before it. + * + * Default delay is HTTP_INITIAL_RETRY_TIMEOUT, which is 2 seconds. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_initial_retry_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** Sets the retry limit for this connection - -1 repeats indefinitely */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_retries(struct evhttp_connection *evcon, + int retry_max); + +/** Set a callback for connection close. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_closecb(struct evhttp_connection *evcon, + void (*)(struct evhttp_connection *, void *), void *); + +/** Get the remote address and port associated with this connection. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_get_peer(struct evhttp_connection *evcon, + const char **address, ev_uint16_t *port); + +/** Get the remote address associated with this connection. + * extracted from getpeername() OR from nameserver. + * + * @return NULL if getpeername() return non success, + * or connection is not connected, + * otherwise it return pointer to struct sockaddr_storage */ +EVENT2_EXPORT_SYMBOL +const struct sockaddr* +evhttp_connection_get_addr(struct evhttp_connection *evcon); + +/** + Make an HTTP request over the specified connection. + + The connection gets ownership of the request. On failure, the + request object is no longer valid as it has been freed. + + @param evcon the evhttp_connection object over which to send the request + @param req the previously created and configured request object + @param type the request type EVHTTP_REQ_GET, EVHTTP_REQ_POST, etc. + @param uri the URI associated with the request + @return 0 on success, -1 on failure + @see evhttp_cancel_request() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri); + +/** + Cancels a pending HTTP request. + + Cancels an ongoing HTTP request. The callback associated with this request + is not executed and the request object is freed. If the request is + currently being processed, e.g. it is ongoing, the corresponding + evhttp_connection object is going to get reset. + + A request cannot be canceled if its callback has executed already. A request + may be canceled reentrantly from its chunked callback. + + @param req the evhttp_request to cancel; req becomes invalid after this call. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_cancel_request(struct evhttp_request *req); + +/** + * A structure to hold a parsed URI or Relative-Ref conforming to RFC3986. + */ +struct evhttp_uri; + +/** Returns the request URI */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_request_get_uri(const struct evhttp_request *req); +/** Returns the request URI (parsed) */ +EVENT2_EXPORT_SYMBOL +const struct evhttp_uri *evhttp_request_get_evhttp_uri(const struct evhttp_request *req); +/** Returns the request command */ +EVENT2_EXPORT_SYMBOL +enum evhttp_cmd_type evhttp_request_get_command(const struct evhttp_request *req); + +EVENT2_EXPORT_SYMBOL +int evhttp_request_get_response_code(const struct evhttp_request *req); +EVENT2_EXPORT_SYMBOL +const char * evhttp_request_get_response_code_line(const struct evhttp_request *req); + +/** Returns the input headers */ +EVENT2_EXPORT_SYMBOL +struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req); +/** Returns the output headers */ +EVENT2_EXPORT_SYMBOL +struct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req); +/** Returns the input buffer */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req); +/** Returns the output buffer */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req); +/** Returns the host associated with the request. If a client sends an absolute + URI, the host part of that is preferred. Otherwise, the input headers are + searched for a Host: header. NULL is returned if no absolute URI or Host: + header is provided. */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_request_get_host(struct evhttp_request *req); + +/* Interfaces for dealing with HTTP headers */ + +/** + Finds the value belonging to a header. + + @param headers the evkeyvalq object in which to find the header + @param key the name of the header to find + @returns a pointer to the value for the header or NULL if the header + could not be found. + @see evhttp_add_header(), evhttp_remove_header() +*/ +EVENT2_EXPORT_SYMBOL +const char *evhttp_find_header(const struct evkeyvalq *headers, + const char *key); + +/** + Removes a header from a list of existing headers. + + @param headers the evkeyvalq object from which to remove a header + @param key the name of the header to remove + @returns 0 if the header was removed, -1 otherwise. + @see evhttp_find_header(), evhttp_add_header() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_header(struct evkeyvalq *headers, const char *key); + +/** + Adds a header to a list of existing headers. + + @param headers the evkeyvalq object to which to add a header + @param key the name of the header + @param value the value belonging to the header + @returns 0 on success, -1 otherwise. + @see evhttp_find_header(), evhttp_clear_headers() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value); + +/** + Removes all headers from the header list. + + @param headers the evkeyvalq object from which to remove all headers +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_clear_headers(struct evkeyvalq *headers); + +/* Miscellaneous utility functions */ + + +/** + Helper function to encode a string for inclusion in a URI. All + characters are replaced by their hex-escaped (%22) equivalents, + except for characters explicitly unreserved by RFC3986 -- that is, + ASCII alphanumeric characters, hyphen, dot, underscore, and tilde. + + The returned string must be freed by the caller. + + @param str an unencoded string + @return a newly allocated URI-encoded string or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_encode_uri(const char *str); + +/** + As evhttp_encode_uri, but if 'size' is nonnegative, treat the string + as being 'size' bytes long. This allows you to encode strings that + may contain 0-valued bytes. + + The returned string must be freed by the caller. + + @param str an unencoded string + @param size the length of the string to encode, or -1 if the string + is NUL-terminated + @param space_to_plus if true, space characters in 'str' are encoded + as +, not %20. + @return a newly allocate URI-encoded string, or NULL on failure. + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uriencode(const char *str, ev_ssize_t size, int space_to_plus); + +/** + Helper function to sort of decode a URI-encoded string. Unlike + evhttp_uridecode, it decodes all plus characters that appear + _after_ the first question mark character, but no plusses that occur + before. This is not a good way to decode URIs in whole or in part. + + The returned string must be freed by the caller + + @deprecated This function is deprecated; you probably want to use + evhttp_uridecode instead. + + @param uri an encoded URI + @return a newly allocated unencoded URI or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_decode_uri(const char *uri); + +/** + Helper function to decode a URI-escaped string or HTTP parameter. + + If 'decode_plus' is 1, then we decode the string as an HTTP parameter + value, and convert all plus ('+') characters to spaces. If + 'decode_plus' is 0, we leave all plus characters unchanged. + + The returned string must be freed by the caller. + + @param uri a URI-encode encoded URI + @param decode_plus determines whether we convert '+' to space. + @param size_out if size_out is not NULL, *size_out is set to the size of the + returned string + @return a newly allocated unencoded URI or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uridecode(const char *uri, int decode_plus, + size_t *size_out); + +/** + Helper function to parse out arguments in a query. + + Parsing a URI like + + http://foo.com/?q=test&s=some+thing + + will result in two entries in the key value queue. + + The first entry is: key="q", value="test" + The second entry is: key="s", value="some thing" + + @deprecated This function is deprecated as of Libevent 2.0.9. Use + evhttp_uri_parse and evhttp_parse_query_str instead. + + @param uri the request URI + @param headers the head of the evkeyval queue + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query(const char *uri, struct evkeyvalq *headers); + +/** @see evhttp_parse_query_str_flags() */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers); + +/** Tolerate queries that are not standard conformant. + * + * Here are some examples: + * + * - test=123&test2 + * with with this flag test2 will be present in the output headers + * + * - test=123&&test2=1 + * will parse the query with this flag + * + * - test=123&=456&test2=1 + * will parse the queyr with this flag, however there won't be empty key + * present + */ +#define EVHTTP_URI_QUERY_NONCONFORMANT 0x01 +/** Prefer last value over the first from query args + * + * Example: test=123&test=456 + * Without: test=123 + * With : test=456 + */ +#define EVHTTP_URI_QUERY_LAST_VAL 0x02 + +/** + Helper function to parse out arguments from the query portion of an + HTTP URI. + + Parsing a query string like + + q=test&s=some+thing + + will result in two entries in the key value queue. + + The first entry is: key="q", value="test" + The second entry is: key="s", value="some thing" + + @param uri the query portion of the URI + @param headers the head of the evkeyval queue + @param flags one or more of EVHTTP_URI_QUERY_* + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query_str_flags(const char *uri, struct evkeyvalq *headers, unsigned flags); + +/** + * Escape HTML character entities in a string. + * + * Replaces <, >, ", ' and & with <, >, ", + * ' and & correspondingly. + * + * The returned string needs to be freed by the caller. + * + * @param html an unescaped HTML string + * @return an escaped HTML string or NULL on error + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_htmlescape(const char *html); + +/** + * Return a new empty evhttp_uri with no fields set. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_new(void); + +/** + * Changes the flags set on a given URI. See EVHTTP_URI_* for + * a list of flags. + **/ +EVENT2_EXPORT_SYMBOL +void evhttp_uri_set_flags(struct evhttp_uri *uri, unsigned flags); + +/** Return the scheme of an evhttp_uri, or NULL if there is no scheme has + * been set and the evhttp_uri contains a Relative-Ref. */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_scheme(const struct evhttp_uri *uri); +/** + * Return the userinfo part of an evhttp_uri, or NULL if it has no userinfo + * set. + */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_userinfo(const struct evhttp_uri *uri); +/** + * Return the host part of an evhttp_uri, or NULL if it has no host set. + * The host may either be a regular hostname (conforming to the RFC 3986 + * "regname" production), or an IPv4 address, or the empty string, or a + * bracketed IPv6 address, or a bracketed 'IP-Future' address. + * + * Note that having a NULL host means that the URI has no authority + * section, but having an empty-string host means that the URI has an + * authority section with no host part. For example, + * "mailto:user@example.com" has a host of NULL, but "file:///etc/motd" + * has a host of "". + */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_host(const struct evhttp_uri *uri); +/** Return the unix socket part of an evhttp_uri, or NULL if there is no unix + * socket set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_unixsocket(const struct evhttp_uri *uri); +/** Return the port part of an evhttp_uri, or -1 if there is no port set. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_get_port(const struct evhttp_uri *uri); +/** Return the path part of an evhttp_uri, or NULL if it has no path set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_path(const struct evhttp_uri *uri); +/** Return the query part of an evhttp_uri (excluding the leading "?"), or + * NULL if it has no query set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_query(const struct evhttp_uri *uri); +/** Return the fragment part of an evhttp_uri (excluding the leading "#"), + * or NULL if it has no fragment set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_fragment(const struct evhttp_uri *uri); + +/** Set the scheme of an evhttp_uri, or clear the scheme if scheme==NULL. + * Returns 0 on success, -1 if scheme is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme); +/** Set the userinfo of an evhttp_uri, or clear the userinfo if userinfo==NULL. + * Returns 0 on success, -1 if userinfo is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo); +/** Set the host of an evhttp_uri, or clear the host if host==NULL. + * Returns 0 on success, -1 if host is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_host(struct evhttp_uri *uri, const char *host); +/** Set the unix socket of an evhttp_uri, or clear the unix socket if unixsocket==NULL. + * Returns 0 on success, -1 if unixsocket is not well-formed */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_unixsocket(struct evhttp_uri *uri, const char *unixsocket); + +/** Set the port of an evhttp_uri, or clear the port if port==-1. + * Returns 0 on success, -1 if port is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_port(struct evhttp_uri *uri, int port); +/** Set the path of an evhttp_uri, or clear the path if path==NULL. + * Returns 0 on success, -1 if path is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_path(struct evhttp_uri *uri, const char *path); +/** Set the query of an evhttp_uri, or clear the query if query==NULL. + * The query should not include a leading "?". + * Returns 0 on success, -1 if query is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_query(struct evhttp_uri *uri, const char *query); +/** Set the fragment of an evhttp_uri, or clear the fragment if fragment==NULL. + * The fragment should not include a leading "#". + * Returns 0 on success, -1 if fragment is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment); + +/** + * Helper function to parse a URI-Reference as specified by RFC3986. + * + * This function matches the URI-Reference production from RFC3986, + * which includes both URIs like + * + * scheme://[[userinfo]@]foo.com[:port]]/[path][?query][#fragment] + * + * and relative-refs like + * + * [path][?query][#fragment] + * + * Any optional elements portions not present in the original URI are + * left set to NULL in the resulting evhttp_uri. If no port is + * specified, the port is set to -1. + * + * Note that no decoding is performed on percent-escaped characters in + * the string; if you want to parse them, use evhttp_uridecode or + * evhttp_parse_query_str as appropriate. + * + * Note also that most URI schemes will have additional constraints that + * this function does not know about, and cannot check. For example, + * mailto://www.example.com/cgi-bin/fortune.pl is not a reasonable + * mailto url, http://www.example.com:99999/ is not a reasonable HTTP + * URL, and ftp:username@example.com is not a reasonable FTP URL. + * Nevertheless, all of these URLs conform to RFC3986, and this function + * accepts all of them as valid. + * + * @param source_uri the request URI + * @param flags Zero or more EVHTTP_URI_* flags to affect the behavior + * of the parser. + * @return uri container to hold parsed data, or NULL if there is error + * @see evhttp_uri_free() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_parse_with_flags(const char *source_uri, + unsigned flags); + +/** Tolerate URIs that do not conform to RFC3986. + * + * Unfortunately, some HTTP clients generate URIs that, according to RFC3986, + * are not conformant URIs. If you need to support these URIs, you can + * do so by passing this flag to evhttp_uri_parse_with_flags. + * + * Currently, these changes are: + *
    + *
  • Nonconformant URIs are allowed to contain otherwise unreasonable + * characters in their path, query, and fragment components. + *
+ */ +#define EVHTTP_URI_NONCONFORMANT 0x01 + +/** + * Strip brackets from the IPv6 address and only for evhttp_uri_get_host(), + * evhttp_uri_join() returns the host with brackets. + * + * Thus you can use host part of the evhttp_uri for getaddrinfo(). + * + * @see also _EVHTTP_URI_HOST_HAS_BRACKETS + */ +#define EVHTTP_URI_HOST_STRIP_BRACKETS 0x04 + +/** + * Parse unix domain socket URIs, for example: + * + * http://unix:/run/control.sock:/controller + */ +#define EVHTTP_URI_UNIX_SOCKET 0x08 + +/** Alias for evhttp_uri_parse_with_flags(source_uri, 0) */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_parse(const char *source_uri); + +/** + * Free all memory allocated for a parsed uri. Only use this for URIs + * generated by evhttp_uri_parse. + * + * @param uri container with parsed data + * @see evhttp_uri_parse() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_uri_free(struct evhttp_uri *uri); + +/** + * Join together the uri parts from parsed data to form a URI-Reference. + * + * Note that no escaping of reserved characters is done on the members + * of the evhttp_uri, so the generated string might not be a valid URI + * unless the members of evhttp_uri are themselves valid. + * + * @param uri container with parsed data + * @param buf destination buffer + * @param limit destination buffer size + * @return an joined uri as string or NULL on error + * @see evhttp_uri_parse() + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_HTTP_H_INCLUDED_ */ diff --git a/include/libevent/event2/http_compat.h b/include/libevent/event2/http_compat.h new file mode 100644 index 0000000..979a3a4 --- /dev/null +++ b/include/libevent/event2/http_compat.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_HTTP_COMPAT_H_INCLUDED_ +#define EVENT2_HTTP_COMPAT_H_INCLUDED_ + +/** @file event2/http_compat.h + + @brief Potentially non-threadsafe versions of the functions in http.h: provided + only for backwards compatibility. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + * Start an HTTP server on the specified address and port + * + * @deprecated It does not allow an event base to be specified + * + * @param address the address to which the HTTP server should be bound + * @param port the port number on which the HTTP server should listen + * @return a pointer to a newly initialized evhttp server structure + * or NULL on error + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_start(const char *address, ev_uint16_t port); + +/** + * A connection object that can be used to for making HTTP requests. The + * connection object tries to establish the connection when it is given an + * http request object. + * + * @deprecated It does not allow an event base to be specified + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_new( + const char *address, ev_uint16_t port); + +/** + * Associates an event base with the connection - can only be called + * on a freshly created connection object that has not been used yet. + * + * @deprecated XXXX Why? + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_base(struct evhttp_connection *evcon, + struct event_base *base); + + +/** Returns the request URI */ +#define evhttp_request_uri evhttp_request_get_uri + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/include/libevent/event2/http_struct.h b/include/libevent/event2/http_struct.h new file mode 100644 index 0000000..b828180 --- /dev/null +++ b/include/libevent/event2/http_struct.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_HTTP_STRUCT_H_INCLUDED_ +#define EVENT2_HTTP_STRUCT_H_INCLUDED_ + +/** @file event2/http_struct.h + + Data structures for http. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + * the request structure that a server receives. + * WARNING: expect this structure to change. I will try to provide + * reasonable accessors. + */ +struct evhttp_request { +#if defined(TAILQ_ENTRY) + TAILQ_ENTRY(evhttp_request) next; +#else +struct { + struct evhttp_request *tqe_next; + struct evhttp_request **tqe_prev; +} next; +#endif + + /* the connection object that this request belongs to */ + struct evhttp_connection *evcon; + int flags; +/** The request obj owns the evhttp connection and needs to free it */ +#define EVHTTP_REQ_OWN_CONNECTION 0x0001 +/** Request was made via a proxy */ +#define EVHTTP_PROXY_REQUEST 0x0002 +/** The request object is owned by the user; the user must free it */ +#define EVHTTP_USER_OWNED 0x0004 +/** The request will be used again upstack; freeing must be deferred */ +#define EVHTTP_REQ_DEFER_FREE 0x0008 +/** The request should be freed upstack */ +#define EVHTTP_REQ_NEEDS_FREE 0x0010 + + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; + + /* address of the remote host and the port connection came from */ + char *remote_host; + ev_uint16_t remote_port; + + /* cache of the hostname for evhttp_request_get_host */ + char *host_cache; + + enum evhttp_request_kind kind; + enum evhttp_cmd_type type; + + size_t headers_size; + size_t body_size; + + char *uri; /* uri after HTTP request was parsed */ + struct evhttp_uri *uri_elems; /* uri elements */ + + char major; /* HTTP Major number */ + char minor; /* HTTP Minor number */ + + int response_code; /* HTTP Response code */ + char *response_code_line; /* Readable response */ + + struct evbuffer *input_buffer; /* read data */ + ev_int64_t ntoread; + unsigned chunked:1, /* a chunked request */ + userdone:1; /* the user has sent all data */ + + struct evbuffer *output_buffer; /* outgoing post or data */ + + /* Callback */ + void (*cb)(struct evhttp_request *, void *); + void *cb_arg; + + /* + * Chunked data callback - call for each completed chunk if + * specified. If not specified, all the data is delivered via + * the regular callback. + */ + void (*chunk_cb)(struct evhttp_request *, void *); + + /* + * Callback added for forked-daapd so they can collect ICY + * (shoutcast) metadata from the http header. If return + * int is negative the connection will be closed. + */ + int (*header_cb)(struct evhttp_request *, void *); + + /* + * Error callback - called when error is occurred. + * @see evhttp_request_error for error types. + * + * @see evhttp_request_set_error_cb() + */ + void (*error_cb)(enum evhttp_request_error, void *); + + /* + * Send complete callback - called when the request is actually + * sent and completed. + */ + void (*on_complete_cb)(struct evhttp_request *, void *); + void *on_complete_cb_arg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_HTTP_STRUCT_H_INCLUDED_ */ + diff --git a/include/libevent/event2/keyvalq_struct.h b/include/libevent/event2/keyvalq_struct.h new file mode 100644 index 0000000..bffa54b --- /dev/null +++ b/include/libevent/event2/keyvalq_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_KEYVALQ_STRUCT_H_INCLUDED_ +#define EVENT2_KEYVALQ_STRUCT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Fix so that people don't have to run with */ +/* XXXX This code is duplicated with event_struct.h */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +#ifndef TAILQ_HEAD +#define EVENT_DEFINED_TQHEAD_ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; \ + struct type **tqh_last; \ +} +#endif + +/* + * Key-Value pairs. Can be used for HTTP headers but also for + * query argument parsing. + */ +struct evkeyval { + TAILQ_ENTRY(evkeyval) next; + + char *key; + char *value; +}; + +TAILQ_HEAD (evkeyvalq, evkeyval); + +/* XXXX This code is duplicated with event_struct.h */ +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef EVENT_DEFINED_TQHEAD_ +#undef TAILQ_HEAD +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libevent/event2/listener.h b/include/libevent/event2/listener.h new file mode 100644 index 0000000..d7f638b --- /dev/null +++ b/include/libevent/event2/listener.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_LISTENER_H_INCLUDED_ +#define EVENT2_LISTENER_H_INCLUDED_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct sockaddr; +struct evconnlistener; + +/**@file event2/listener.h + + @brief A callback that we invoke when a listener has a new connection. + + @param listener The evconnlistener + @param fd The new file descriptor + @param addr The source address of the connection + @param socklen The length of addr + @param user_arg the pointer passed to evconnlistener_new() + */ +typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *); + +/** + A callback that we invoke when a listener encounters a non-retriable error. + + @param listener The evconnlistener + @param user_arg the pointer passed to evconnlistener_new() + */ +typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *); + +/** Flag: Indicates that we should not make incoming sockets nonblocking + * before passing them to the callback. */ +#define LEV_OPT_LEAVE_SOCKETS_BLOCKING (1u<<0) +/** Flag: Indicates that freeing the listener should close the underlying + * socket. */ +#define LEV_OPT_CLOSE_ON_FREE (1u<<1) +/** Flag: Indicates that we should set the close-on-exec flag, if possible */ +#define LEV_OPT_CLOSE_ON_EXEC (1u<<2) +/** Flag: Indicates that we should disable the timeout (if any) between when + * this socket is closed and when we can listen again on the same port. */ +#define LEV_OPT_REUSEABLE (1u<<3) +/** Flag: Indicates that the listener should be locked so it's safe to use + * from multiple threadcs at once. */ +#define LEV_OPT_THREADSAFE (1u<<4) +/** Flag: Indicates that the listener should be created in disabled + * state. Use evconnlistener_enable() to enable it later. */ +#define LEV_OPT_DISABLED (1u<<5) +/** Flag: Indicates that the listener should defer accept() until data is + * available, if possible. Ignored on platforms that do not support this. + * + * This option can help performance for protocols where the client transmits + * immediately after connecting. Do not use this option if your protocol + * _doesn't_ start out with the client transmitting data, since in that case + * this option will sometimes cause the kernel to never tell you about the + * connection. + * + * This option is only supported by evconnlistener_new_bind(): it can't + * work with evconnlistener_new_fd(), since the listener needs to be told + * to use the option before it is actually bound. + */ +#define LEV_OPT_DEFERRED_ACCEPT (1u<<6) +/** Flag: Indicates that we ask to allow multiple servers (processes or + * threads_) to bind to the same port if they each set the option. + * + * SO_REUSEPORT is what most people would expect SO_REUSEADDR to be, however + * SO_REUSEPORT does not imply SO_REUSEADDR. + * + * This is only available on Linux and kernel 3.9+ + */ +#define LEV_OPT_REUSEABLE_PORT (1u<<7) +/** Flag: Indicates that the listener wants to work only in IPv6 socket. + * + * According to RFC3493 and most Linux distributions, default value is to + * work in IPv4-mapped mode. If there is a requirement to bind same port + * on same ip addresses but different handlers for both IPv4 and IPv6, + * it is required to set IPV6_V6ONLY socket option to be sure that the + * code works as expected without affected by bindv6only sysctl setting in + * system. + * + * This socket option also supported by Windows. + */ +#define LEV_OPT_BIND_IPV6ONLY (1u<<8) + +/** + Allocate a new evconnlistener object to listen for incoming TCP connections + on a given file descriptor. + + @param base The event base to associate the listener with. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. + @param ptr A user-supplied pointer to give to the callback. + @param flags Any number of LEV_OPT_* flags + @param backlog Passed to the listen() call to determine the length of the + acceptable connection backlog. Set to -1 for a reasonable default. + Set to 0 if the socket is already listening. + @param fd The file descriptor to listen on. It must be a nonblocking + file descriptor, and it should already be bound to an appropriate + port and address. +*/ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evconnlistener_new(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + evutil_socket_t fd); +/** + Allocate a new evconnlistener object to listen for incoming TCP connections + on a given address. + + @param base The event base to associate the listener with. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. + @param ptr A user-supplied pointer to give to the callback. + @param flags Any number of LEV_OPT_* flags + @param backlog Passed to the listen() call to determine the length of the + acceptable connection backlog. Set to -1 for a reasonable default. + @param sa The address to listen for connections on. + @param socklen The length of the address. + */ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evconnlistener_new_bind(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + const struct sockaddr *sa, int socklen); +/** + Disable and deallocate an evconnlistener. + */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_free(struct evconnlistener *lev); +/** + Re-enable an evconnlistener that has been disabled. + */ +EVENT2_EXPORT_SYMBOL +int evconnlistener_enable(struct evconnlistener *lev); +/** + Stop listening for connections on an evconnlistener. + */ +EVENT2_EXPORT_SYMBOL +int evconnlistener_disable(struct evconnlistener *lev); + +/** Return an evconnlistener's associated event_base. */ +EVENT2_EXPORT_SYMBOL +struct event_base *evconnlistener_get_base(struct evconnlistener *lev); + +/** Return the socket that an evconnlistner is listening on. */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev); + +/** Change the callback on the listener to cb and its user_data to arg. + */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_set_cb(struct evconnlistener *lev, + evconnlistener_cb cb, void *arg); + +/** Set an evconnlistener's error callback. */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_set_error_cb(struct evconnlistener *lev, + evconnlistener_errorcb errorcb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libevent/event2/rpc.h b/include/libevent/event2/rpc.h new file mode 100644 index 0000000..9e8830c --- /dev/null +++ b/include/libevent/event2/rpc.h @@ -0,0 +1,626 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_RPC_H_INCLUDED_ +#define EVENT2_RPC_H_INCLUDED_ + +/* For int types. */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file event2/rpc.h + * + * @brief This header files provides basic support for an RPC server and client. + * + * To support RPCs in a server, every supported RPC command needs to be + * defined and registered. + * + * EVRPC_HEADER(SendCommand, Request, Reply); + * + * SendCommand is the name of the RPC command. + * Request is the name of a structure generated by event_rpcgen.py. + * It contains all parameters relating to the SendCommand RPC. The + * server needs to fill in the Reply structure. + * Reply is the name of a structure generated by event_rpcgen.py. It + * contains the answer to the RPC. + * + * To register an RPC with an HTTP server, you need to first create an RPC + * base with: + * + * struct evrpc_base *base = evrpc_init(http); + * + * A specific RPC can then be registered with + * + * EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg); + * + * when the server receives an appropriately formatted RPC, the user callback + * is invoked. The callback needs to fill in the reply structure. + * + * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg); + * + * To send the reply, call EVRPC_REQUEST_DONE(rpc); + * + * See the regression test for an example. + */ + +/** + Determines if the member has been set in the message + + @param msg the message to inspect + @param member the member variable to test for presences + @return 1 if it's present or 0 otherwise. +*/ +#define EVTAG_HAS(msg, member) \ + ((msg)->member##_set == 1) + +#ifndef EVENT2_RPC_COMPAT_H_INCLUDED_ + +/** + Assigns a value to the member in the message. + + @param msg the message to which to assign a value + @param member the name of the member variable + @param value the value to assign +*/ +#define EVTAG_ASSIGN(msg, member, value) \ + (*(msg)->base->member##_assign)((msg), (value)) +/** + Assigns a value to the member in the message. + + @param msg the message to which to assign a value + @param member the name of the member variable + @param value the value to assign + @param len the length of the value +*/ +#define EVTAG_ASSIGN_WITH_LEN(msg, member, value, len) \ + (*(msg)->base->member##_assign)((msg), (value), (len)) +/** + Returns the value for a member. + + @param msg the message from which to get the value + @param member the name of the member variable + @param pvalue a pointer to the variable to hold the value + @return 0 on success, -1 otherwise. +*/ +#define EVTAG_GET(msg, member, pvalue) \ + (*(msg)->base->member##_get)((msg), (pvalue)) +/** + Returns the value for a member. + + @param msg the message from which to get the value + @param member the name of the member variable + @param pvalue a pointer to the variable to hold the value + @param plen a pointer to the length of the value + @return 0 on success, -1 otherwise. +*/ +#define EVTAG_GET_WITH_LEN(msg, member, pvalue, plen) \ + (*(msg)->base->member##_get)((msg), (pvalue), (plen)) + +#endif /* EVENT2_RPC_COMPAT_H_INCLUDED_ */ + +/** + Adds a value to an array. +*/ +#define EVTAG_ARRAY_ADD_VALUE(msg, member, value) \ + (*(msg)->base->member##_add)((msg), (value)) +/** + Allocates a new entry in the array and returns it. +*/ +#define EVTAG_ARRAY_ADD(msg, member) \ + (*(msg)->base->member##_add)(msg) +/** + Gets a variable at the specified offset from the array. +*/ +#define EVTAG_ARRAY_GET(msg, member, offset, pvalue) \ + (*(msg)->base->member##_get)((msg), (offset), (pvalue)) +/** + Returns the number of entries in the array. +*/ +#define EVTAG_ARRAY_LEN(msg, member) ((msg)->member##_length) + + +struct evbuffer; +struct event_base; +struct evrpc_req_generic; +struct evrpc_request_wrapper; +struct evrpc; + +/** The type of a specific RPC Message + * + * @param rpcname the name of the RPC message + */ +#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname + +struct evhttp_request; +struct evrpc_status; +struct evrpc_hook_meta; + +/** Creates the definitions and prototypes for an RPC + * + * You need to use EVRPC_HEADER to create structures and function prototypes + * needed by the server and client implementation. The structures have to be + * defined in an .rpc file and converted to source code via event_rpcgen.py + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param rplystruct the name of the RPC reply structure + * @see EVRPC_GENERATE() + */ +#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \ +EVRPC_STRUCT(rpcname) { \ + struct evrpc_hook_meta *hook_meta; \ + struct reqstruct* request; \ + struct rplystruct* reply; \ + struct evrpc* rpc; \ + struct evhttp_request* http_req; \ + struct evbuffer* rpc_data; \ +}; \ +EVENT2_EXPORT_SYMBOL \ +int evrpc_send_request_##rpcname(struct evrpc_pool *, \ + struct reqstruct *, struct rplystruct *, \ + void (*)(struct evrpc_status *, \ + struct reqstruct *, struct rplystruct *, void *cbarg), \ + void *); + +struct evrpc_pool; + +/** use EVRPC_GENERATE instead */ +EVENT2_EXPORT_SYMBOL +struct evrpc_request_wrapper *evrpc_make_request_ctx( + struct evrpc_pool *pool, void *request, void *reply, + const char *rpcname, + void (*req_marshal)(struct evbuffer*, void *), + void (*rpl_clear)(void *), + int (*rpl_unmarshal)(void *, struct evbuffer *), + void (*cb)(struct evrpc_status *, void *, void *, void *), + void *cbarg); + +/** Creates a context structure that contains rpc specific information. + * + * EVRPC_MAKE_CTX is used to populate a RPC specific context that + * contains information about marshaling the RPC data types. + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param rplystruct the name of the RPC reply structure + * @param pool the evrpc_pool over which to make the request + * @param request a pointer to the RPC request structure object + * @param reply a pointer to the RPC reply structure object + * @param cb the callback function to call when the RPC has completed + * @param cbarg the argument to supply to the callback + */ +#define EVRPC_MAKE_CTX(rpcname, reqstruct, rplystruct, \ + pool, request, reply, cb, cbarg) \ + evrpc_make_request_ctx(pool, request, reply, \ + #rpcname, \ + (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \ + (void (*)(void *))rplystruct##_clear, \ + (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal, \ + (void (*)(struct evrpc_status *, void *, void *, void *))cb, \ + cbarg) + +/** Generates the code for receiving and sending an RPC message + * + * EVRPC_GENERATE is used to create the code corresponding to sending + * and receiving a particular RPC message + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param rplystruct the name of the RPC reply structure + * @see EVRPC_HEADER() + */ +#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \ + int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \ + struct reqstruct *request, struct rplystruct *reply, \ + void (*cb)(struct evrpc_status *, \ + struct reqstruct *, struct rplystruct *, void *cbarg), \ + void *cbarg) { \ + return evrpc_send_request_generic(pool, request, reply, \ + (void (*)(struct evrpc_status *, void *, void *, void *))cb, \ + cbarg, \ + #rpcname, \ + (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \ + (void (*)(void *))rplystruct##_clear, \ + (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal); \ +} + +/** Provides access to the HTTP request object underlying an RPC + * + * Access to the underlying http object; can be used to look at headers or + * for getting the remote ip address + * + * @param rpc_req the rpc request structure provided to the server callback + * @return an struct evhttp_request object that can be inspected for + * HTTP headers or sender information. + */ +#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req + +/** completes the server response to an rpc request */ +EVENT2_EXPORT_SYMBOL +void evrpc_request_done(struct evrpc_req_generic *req); + +/** accessors for request and reply */ +EVENT2_EXPORT_SYMBOL +void *evrpc_get_request(struct evrpc_req_generic *req); +EVENT2_EXPORT_SYMBOL +void *evrpc_get_reply(struct evrpc_req_generic *req); + +/** Creates the reply to an RPC request + * + * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected + * to have been filled in. The request and reply pointers become invalid + * after this call has finished. + * + * @param rpc_req the rpc request structure provided to the server callback + */ +#define EVRPC_REQUEST_DONE(rpc_req) do { \ + struct evrpc_req_generic *req_ = (struct evrpc_req_generic *)(rpc_req); \ + evrpc_request_done(req_); \ +} while (0) + + +struct evrpc_base; +struct evhttp; + +/* functions to start up the rpc system */ + +/** Creates a new rpc base from which RPC requests can be received + * + * @param server a pointer to an existing HTTP server + * @return a newly allocated evrpc_base struct or NULL if an error occurred + * @see evrpc_free() + */ +EVENT2_EXPORT_SYMBOL +struct evrpc_base *evrpc_init(struct evhttp *server); + +/** + * Frees the evrpc base + * + * For now, you are responsible for making sure that no rpcs are ongoing. + * + * @param base the evrpc_base object to be freed + * @see evrpc_init + */ +EVENT2_EXPORT_SYMBOL +void evrpc_free(struct evrpc_base *base); + +/** register RPCs with the HTTP Server + * + * registers a new RPC with the HTTP server, each RPC needs to have + * a unique name under which it can be identified. + * + * @param base the evrpc_base structure in which the RPC should be + * registered. + * @param name the name of the RPC + * @param request the name of the RPC request structure + * @param reply the name of the RPC reply structure + * @param callback the callback that should be invoked when the RPC + * is received. The callback has the following prototype + * void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg) + * @param cbarg an additional parameter that can be passed to the callback. + * The parameter can be used to carry around state. + */ +#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \ + evrpc_register_generic(base, #name, \ + (void (*)(struct evrpc_req_generic *, void *))callback, cbarg, \ + (void *(*)(void *))request##_new_with_arg, NULL, \ + (void (*)(void *))request##_free, \ + (int (*)(void *, struct evbuffer *))request##_unmarshal, \ + (void *(*)(void *))reply##_new_with_arg, NULL, \ + (void (*)(void *))reply##_free, \ + (int (*)(void *))reply##_complete, \ + (void (*)(struct evbuffer *, void *))reply##_marshal) + +/** + Low level function for registering an RPC with a server. + + Use EVRPC_REGISTER() instead. + + @see EVRPC_REGISTER() +*/ +EVENT2_EXPORT_SYMBOL +int evrpc_register_rpc(struct evrpc_base *, struct evrpc *, + void (*)(struct evrpc_req_generic*, void *), void *); + +/** + * Unregisters an already registered RPC + * + * @param base the evrpc_base object from which to unregister an RPC + * @param name the name of the rpc to unregister + * @return -1 on error or 0 when successful. + * @see EVRPC_REGISTER() + */ +#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc((base), #name) + +EVENT2_EXPORT_SYMBOL +int evrpc_unregister_rpc(struct evrpc_base *base, const char *name); + +/* + * Client-side RPC support + */ + +struct evhttp_connection; +struct evrpc_status; + +/** launches an RPC and sends it to the server + * + * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server. + * + * @param name the name of the RPC + * @param pool the evrpc_pool that contains the connection objects over which + * the request should be sent. + * @param request a pointer to the RPC request structure - it contains the + * data to be sent to the server. + * @param reply a pointer to the RPC reply structure. It is going to be filled + * if the request was answered successfully + * @param cb the callback to invoke when the RPC request has been answered + * @param cbarg an additional argument to be passed to the client + * @return 0 on success, -1 on failure + */ +#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \ + evrpc_send_request_##name((pool), (request), (reply), (cb), (cbarg)) + +/** + Makes an RPC request based on the provided context. + + This is a low-level function and should not be used directly + unless a custom context object is provided. Use EVRPC_MAKE_REQUEST() + instead. + + @param ctx a context from EVRPC_MAKE_CTX() + @returns 0 on success, -1 otherwise. + @see EVRPC_MAKE_REQUEST(), EVRPC_MAKE_CTX() +*/ +EVENT2_EXPORT_SYMBOL +int evrpc_make_request(struct evrpc_request_wrapper *ctx); + +/** creates an rpc connection pool + * + * a pool has a number of connections associated with it. + * rpc requests are always made via a pool. + * + * @param base a pointer to an struct event_based object; can be left NULL + * in singled-threaded applications + * @return a newly allocated struct evrpc_pool object or NULL if an error + * occurred + * @see evrpc_pool_free() + */ +EVENT2_EXPORT_SYMBOL +struct evrpc_pool *evrpc_pool_new(struct event_base *base); +/** frees an rpc connection pool + * + * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new() + * @see evrpc_pool_new() + */ +EVENT2_EXPORT_SYMBOL +void evrpc_pool_free(struct evrpc_pool *pool); + +/** + * Adds a connection over which rpc can be dispatched to the pool. + * + * The connection object must have been newly created. + * + * @param pool the pool to which to add the connection + * @param evcon the connection to add to the pool. + */ +EVENT2_EXPORT_SYMBOL +void evrpc_pool_add_connection(struct evrpc_pool *pool, + struct evhttp_connection *evcon); + +/** + * Removes a connection from the pool. + * + * The connection object must have been newly created. + * + * @param pool the pool from which to remove the connection + * @param evcon the connection to remove from the pool. + */ +EVENT2_EXPORT_SYMBOL +void evrpc_pool_remove_connection(struct evrpc_pool *pool, + struct evhttp_connection *evcon); + +/** + * Sets the timeout in secs after which a request has to complete. The + * RPC is completely aborted if it does not complete by then. Setting + * the timeout to 0 means that it never timeouts and can be used to + * implement callback type RPCs. + * + * Any connection already in the pool will be updated with the new + * timeout. Connections added to the pool after set_timeout has be + * called receive the pool timeout only if no timeout has been set + * for the connection itself. + * + * @param pool a pointer to a struct evrpc_pool object + * @param timeout_in_secs the number of seconds after which a request should + * timeout and a failure be returned to the callback. + */ +EVENT2_EXPORT_SYMBOL +void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs); + +/** + * Hooks for changing the input and output of RPCs; this can be used to + * implement compression, authentication, encryption, ... + */ + +enum EVRPC_HOOK_TYPE { + EVRPC_INPUT, /**< apply the function to an input hook */ + EVRPC_OUTPUT /**< apply the function to an output hook */ +}; + +#ifndef _WIN32 +/** Deprecated alias for EVRPC_INPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define INPUT EVRPC_INPUT +/** Deprecated alias for EVRPC_OUTPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define OUTPUT EVRPC_OUTPUT +#endif + +/** + * Return value from hook processing functions + */ + +enum EVRPC_HOOK_RESULT { + EVRPC_TERMINATE = -1, /**< indicates the rpc should be terminated */ + EVRPC_CONTINUE = 0, /**< continue processing the rpc */ + EVRPC_PAUSE = 1 /**< pause processing request until resumed */ +}; + +/** adds a processing hook to either an rpc base or rpc pool + * + * If a hook returns TERMINATE, the processing is aborted. On CONTINUE, + * the request is immediately processed after the hook returns. If the + * hook returns PAUSE, request processing stops until evrpc_resume_request() + * has been called. + * + * The add functions return handles that can be used for removing hooks. + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param hook_type either INPUT or OUTPUT + * @param cb the callback to call when the hook is activated + * @param cb_arg an additional argument for the callback + * @return a handle to the hook so it can be removed later + * @see evrpc_remove_hook() + */ +EVENT2_EXPORT_SYMBOL +void *evrpc_add_hook(void *vbase, + enum EVRPC_HOOK_TYPE hook_type, + int (*cb)(void *, struct evhttp_request *, struct evbuffer *, void *), + void *cb_arg); + +/** removes a previously added hook + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param hook_type either INPUT or OUTPUT + * @param handle a handle returned by evrpc_add_hook() + * @return 1 on success or 0 on failure + * @see evrpc_add_hook() + */ +EVENT2_EXPORT_SYMBOL +int evrpc_remove_hook(void *vbase, + enum EVRPC_HOOK_TYPE hook_type, + void *handle); + +/** resume a paused request + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param ctx the context pointer provided to the original hook call + */ +EVENT2_EXPORT_SYMBOL +int evrpc_resume_request(void *vbase, void *ctx, enum EVRPC_HOOK_RESULT res); + +/** adds meta data to request + * + * evrpc_hook_add_meta() allows hooks to add meta data to a request. for + * a client request, the meta data can be inserted by an outgoing request hook + * and retrieved by the incoming request hook. + * + * @param ctx the context provided to the hook call + * @param key a NUL-terminated c-string + * @param data the data to be associated with the key + * @param data_size the size of the data + */ +EVENT2_EXPORT_SYMBOL +void evrpc_hook_add_meta(void *ctx, const char *key, + const void *data, size_t data_size); + +/** retrieves meta data previously associated + * + * evrpc_hook_find_meta() can be used to retrieve meta data associated to a + * request by a previous hook. + * @param ctx the context provided to the hook call + * @param key a NUL-terminated c-string + * @param data pointer to a data pointer that will contain the retrieved data + * @param data_size pointer to the size of the data + * @return 0 on success or -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evrpc_hook_find_meta(void *ctx, const char *key, + void **data, size_t *data_size); + +/** + * returns the connection object associated with the request + * + * @param ctx the context provided to the hook call + * @return a pointer to the evhttp_connection object or NULL if an error + * occurred + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evrpc_hook_get_connection(void *ctx); + +/** + Function for sending a generic RPC request. + + Do not call this function directly, use EVRPC_MAKE_REQUEST() instead. + + @see EVRPC_MAKE_REQUEST() + */ +EVENT2_EXPORT_SYMBOL +int evrpc_send_request_generic(struct evrpc_pool *pool, + void *request, void *reply, + void (*cb)(struct evrpc_status *, void *, void *, void *), + void *cb_arg, + const char *rpcname, + void (*req_marshal)(struct evbuffer *, void *), + void (*rpl_clear)(void *), + int (*rpl_unmarshal)(void *, struct evbuffer *)); + +/** + Function for registering a generic RPC with the RPC base. + + Do not call this function directly, use EVRPC_REGISTER() instead. + + @see EVRPC_REGISTER() + */ +EVENT2_EXPORT_SYMBOL +int evrpc_register_generic(struct evrpc_base *base, const char *name, + void (*callback)(struct evrpc_req_generic *, void *), void *cbarg, + void *(*req_new)(void *), void *req_new_arg, void (*req_free)(void *), + int (*req_unmarshal)(void *, struct evbuffer *), + void *(*rpl_new)(void *), void *rpl_new_arg, void (*rpl_free)(void *), + int (*rpl_complete)(void *), + void (*rpl_marshal)(struct evbuffer *, void *)); + +/** accessors for obscure and undocumented functionality */ +EVENT2_EXPORT_SYMBOL +struct evrpc_pool* evrpc_request_get_pool(struct evrpc_request_wrapper *ctx); +EVENT2_EXPORT_SYMBOL +void evrpc_request_set_pool(struct evrpc_request_wrapper *ctx, + struct evrpc_pool *pool); +EVENT2_EXPORT_SYMBOL +void evrpc_request_set_cb(struct evrpc_request_wrapper *ctx, + void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg), + void *cb_arg); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_RPC_H_INCLUDED_ */ diff --git a/include/libevent/event2/rpc_compat.h b/include/libevent/event2/rpc_compat.h new file mode 100644 index 0000000..b3d3de3 --- /dev/null +++ b/include/libevent/event2/rpc_compat.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_RPC_COMPAT_H_INCLUDED_ +#define EVENT2_RPC_COMPAT_H_INCLUDED_ + +/** @file event2/rpc_compat.h + + @brief Deprecated versions of the functions in rpc.h: provided only for + backwards compatibility. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** backwards compatible accessors that work only with gcc */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + +#undef EVTAG_ASSIGN +#undef EVTAG_GET +#undef EVTAG_ADD + +#define EVTAG_ASSIGN(msg, member, args...) \ + (*(msg)->base->member##_assign)(msg, ## args) +#define EVTAG_GET(msg, member, args...) \ + (*(msg)->base->member##_get)(msg, ## args) +#define EVTAG_ADD(msg, member, args...) \ + (*(msg)->base->member##_add)(msg, ## args) +#endif +#define EVTAG_LEN(msg, member) ((msg)->member##_length) + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/include/libevent/event2/rpc_struct.h b/include/libevent/event2/rpc_struct.h new file mode 100644 index 0000000..f3cb460 --- /dev/null +++ b/include/libevent/event2/rpc_struct.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_RPC_STRUCT_H_INCLUDED_ +#define EVENT2_RPC_STRUCT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file event2/rpc_struct.h + + Structures used by rpc.h. Using these structures directly may harm + forward compatibility: be careful! + + */ + +/* Fix so that people don't have to run with */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +/** + * provides information about the completed RPC request. + */ +struct evrpc_status { +#define EVRPC_STATUS_ERR_NONE 0 +#define EVRPC_STATUS_ERR_TIMEOUT 1 +#define EVRPC_STATUS_ERR_BADPAYLOAD 2 +#define EVRPC_STATUS_ERR_UNSTARTED 3 +#define EVRPC_STATUS_ERR_HOOKABORTED 4 + int error; + + /* for looking at headers or other information */ + struct evhttp_request *http_req; +}; + +/* the structure below needs to be synchronized with evrpc_req_generic */ + +/* Encapsulates a request */ +struct evrpc { + TAILQ_ENTRY(evrpc) next; + + /* the URI at which the request handler lives */ + const char* uri; + + /* creates a new request structure */ + void *(*request_new)(void *); + void *request_new_arg; + + /* frees the request structure */ + void (*request_free)(void *); + + /* unmarshals the buffer into the proper request structure */ + int (*request_unmarshal)(void *, struct evbuffer *); + + /* creates a new reply structure */ + void *(*reply_new)(void *); + void *reply_new_arg; + + /* frees the reply structure */ + void (*reply_free)(void *); + + /* verifies that the reply is valid */ + int (*reply_complete)(void *); + + /* marshals the reply into a buffer */ + void (*reply_marshal)(struct evbuffer*, void *); + + /* the callback invoked for each received rpc */ + void (*cb)(struct evrpc_req_generic *, void *); + void *cb_arg; + + /* reference for further configuration */ + struct evrpc_base *base; +}; + +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_RPC_STRUCT_H_INCLUDED_ */ diff --git a/include/libevent/event2/tag.h b/include/libevent/event2/tag.h new file mode 100644 index 0000000..e765ea8 --- /dev/null +++ b/include/libevent/event2/tag.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_TAG_H_INCLUDED_ +#define EVENT2_TAG_H_INCLUDED_ + +/** @file event2/tag.h + + @brief Helper functions for reading and writing tagged data onto buffers. + + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +struct evbuffer; + +/* + * Marshaling tagged data - We assume that all tags are inserted in their + * numeric order - so that unknown tags will always be higher than the + * known ones - and we can just ignore the end of an event buffer. + */ + +EVENT2_EXPORT_SYMBOL +void evtag_init(void); + +/** + Unmarshals the header and returns the length of the payload + + @param evbuf the buffer from which to unmarshal data + @param ptag a pointer in which the tag id is being stored + @returns -1 on failure or the number of bytes in the remaining payload. +*/ +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_header(struct evbuffer *evbuf, ev_uint32_t *ptag); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, + ev_uint32_t len); +EVENT2_EXPORT_SYMBOL +void evtag_marshal_buffer(struct evbuffer *evbuf, ev_uint32_t tag, + struct evbuffer *data); + +/** + Encode an integer and store it in an evbuffer. + + We encode integers by nybbles; the first nibble contains the number + of significant nibbles - 1; this allows us to encode up to 64-bit + integers. This function is byte-order independent. + + @param evbuf evbuffer to store the encoded number + @param number a 32-bit integer + */ +EVENT2_EXPORT_SYMBOL +void evtag_encode_int(struct evbuffer *evbuf, ev_uint32_t number); +EVENT2_EXPORT_SYMBOL +void evtag_encode_int64(struct evbuffer *evbuf, ev_uint64_t number); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint32_t integer); +EVENT2_EXPORT_SYMBOL +void evtag_marshal_int64(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint64_t integer); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, + const char *string); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, + struct timeval *tv); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, + struct evbuffer *dst); +EVENT2_EXPORT_SYMBOL +int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag); +EVENT2_EXPORT_SYMBOL +int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength); +EVENT2_EXPORT_SYMBOL +int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength); +EVENT2_EXPORT_SYMBOL +int evtag_consume(struct evbuffer *evbuf); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint32_t *pinteger); +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_int64(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint64_t *pinteger); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, + void *data, size_t len); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, + char **pstring); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct timeval *ptv); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_TAG_H_INCLUDED_ */ diff --git a/include/libevent/event2/tag_compat.h b/include/libevent/event2/tag_compat.h new file mode 100644 index 0000000..51a09e4 --- /dev/null +++ b/include/libevent/event2/tag_compat.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_TAG_COMPAT_H_INCLUDED_ +#define EVENT2_TAG_COMPAT_H_INCLUDED_ + +/** @file event2/tag_compat.h + + @brief Obsolete/deprecated functions from tag.h; provided only for backwards + compatibility. + */ + +/** + @name Misnamed functions + + @deprecated These macros are deprecated because their names don't follow + Libevent's naming conventions. Use evtag_encode_int and + evtag_encode_int64 instead. + + @{ +*/ +#define encode_int(evbuf, number) evtag_encode_int((evbuf), (number)) +#define encode_int64(evbuf, number) evtag_encode_int64((evbuf), (number)) +/**@}*/ + +#endif /* EVENT2_TAG_H_INCLUDED_ */ diff --git a/include/libevent/event2/thread.h b/include/libevent/event2/thread.h new file mode 100644 index 0000000..481a02f --- /dev/null +++ b/include/libevent/event2/thread.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2008-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_THREAD_H_INCLUDED_ +#define EVENT2_THREAD_H_INCLUDED_ + +/** @file event2/thread.h + + @brief Functions for multi-threaded applications using Libevent. + + When using a multi-threaded application in which multiple threads + add and delete events from a single event base, Libevent needs to + lock its data structures. + + Like the memory-management function hooks, all of the threading functions + _must_ be set up before an event_base is created if you want the base to + use them. + + Most programs will either be using Windows threads or Posix threads. You + can configure Libevent to use one of these evthread_use_windows_threads() or + evthread_use_pthreads() respectively. If you're using another threading + library, you'll need to configure threading functions manually using + evthread_set_lock_callbacks() and evthread_set_condition_callbacks(). + + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + @name Flags passed to lock functions + + @{ +*/ +/** A flag passed to a locking callback when the lock was allocated as a + * read-write lock, and we want to acquire or release the lock for writing. */ +#define EVTHREAD_WRITE 0x04 +/** A flag passed to a locking callback when the lock was allocated as a + * read-write lock, and we want to acquire or release the lock for reading. */ +#define EVTHREAD_READ 0x08 +/** A flag passed to a locking callback when we don't want to block waiting + * for the lock; if we can't get the lock immediately, we will instead + * return nonzero from the locking callback. */ +#define EVTHREAD_TRY 0x10 +/**@}*/ + +#if !defined(EVENT__DISABLE_THREAD_SUPPORT) || defined(EVENT_IN_DOXYGEN_) + +#define EVTHREAD_LOCK_API_VERSION 1 + +/** + @name Types of locks + + @{*/ +/** A recursive lock is one that can be acquired multiple times at once by the + * same thread. No other process can allocate the lock until the thread that + * has been holding it has unlocked it as many times as it locked it. */ +#define EVTHREAD_LOCKTYPE_RECURSIVE 1 +/* A read-write lock is one that allows multiple simultaneous readers, but + * where any one writer excludes all other writers and readers. */ +#define EVTHREAD_LOCKTYPE_READWRITE 2 +/**@}*/ + +/** This structure describes the interface a threading library uses for + * locking. It's used to tell evthread_set_lock_callbacks() how to use + * locking on this platform. + */ +struct evthread_lock_callbacks { + /** The current version of the locking API. Set this to + * EVTHREAD_LOCK_API_VERSION */ + int lock_api_version; + /** Which kinds of locks does this version of the locking API + * support? A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and + * EVTHREAD_LOCKTYPE_READWRITE. + * + * (Note that RECURSIVE locks are currently mandatory, and + * READWRITE locks are not currently used.) + **/ + unsigned supported_locktypes; + /** Function to allocate and initialize new lock of type 'locktype'. + * Returns NULL on failure. */ + void *(*alloc)(unsigned locktype); + /** Funtion to release all storage held in 'lock', which was created + * with type 'locktype'. */ + void (*free)(void *lock, unsigned locktype); + /** Acquire an already-allocated lock at 'lock' with mode 'mode'. + * Returns 0 on success, and nonzero on failure. */ + int (*lock)(unsigned mode, void *lock); + /** Release a lock at 'lock' using mode 'mode'. Returns 0 on success, + * and nonzero on failure. */ + int (*unlock)(unsigned mode, void *lock); +}; + +/** Sets a group of functions that Libevent should use for locking. + * For full information on the required callback API, see the + * documentation for the individual members of evthread_lock_callbacks. + * + * Note that if you're using Windows or the Pthreads threading library, you + * probably shouldn't call this function; instead, use + * evthread_use_windows_threads() or evthread_use_posix_threads() if you can. + */ +EVENT2_EXPORT_SYMBOL +int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *); + +#define EVTHREAD_CONDITION_API_VERSION 1 + +struct timeval; + +/** This structure describes the interface a threading library uses for + * condition variables. It's used to tell evthread_set_condition_callbacks + * how to use locking on this platform. + */ +struct evthread_condition_callbacks { + /** The current version of the conditions API. Set this to + * EVTHREAD_CONDITION_API_VERSION */ + int condition_api_version; + /** Function to allocate and initialize a new condition variable. + * Returns the condition variable on success, and NULL on failure. + * The 'condtype' argument will be 0 with this API version. + */ + void *(*alloc_condition)(unsigned condtype); + /** Function to free a condition variable. */ + void (*free_condition)(void *cond); + /** Function to signal a condition variable. If 'broadcast' is 1, all + * threads waiting on 'cond' should be woken; otherwise, only on one + * thread is worken. Should return 0 on success, -1 on failure. + * This function will only be called while holding the associated + * lock for the condition. + */ + int (*signal_condition)(void *cond, int broadcast); + /** Function to wait for a condition variable. The lock 'lock' + * will be held when this function is called; should be released + * while waiting for the condition to be come signalled, and + * should be held again when this function returns. + * If timeout is provided, it is interval of seconds to wait for + * the event to become signalled; if it is NULL, the function + * should wait indefinitely. + * + * The function should return -1 on error; 0 if the condition + * was signalled, or 1 on a timeout. */ + int (*wait_condition)(void *cond, void *lock, + const struct timeval *timeout); +}; + +/** Sets a group of functions that Libevent should use for condition variables. + * For full information on the required callback API, see the + * documentation for the individual members of evthread_condition_callbacks. + * + * Note that if you're using Windows or the Pthreads threading library, you + * probably shouldn't call this function; instead, use + * evthread_use_windows_threads() or evthread_use_pthreads() if you can. + */ +EVENT2_EXPORT_SYMBOL +int evthread_set_condition_callbacks( + const struct evthread_condition_callbacks *); + +/** + Sets the function for determining the thread id. + + @param id_fn the identify function Libevent should invoke to + determine the identity of a thread. +*/ +EVENT2_EXPORT_SYMBOL +void evthread_set_id_callback( + unsigned long (*id_fn)(void)); + +#if (defined(_WIN32) && !defined(EVENT__DISABLE_THREAD_SUPPORT)) || defined(EVENT_IN_DOXYGEN_) +/** Sets up Libevent for use with Windows builtin locking and thread ID + functions. Unavailable if Libevent is not built for Windows. + + @return 0 on success, -1 on failure. */ +EVENT2_EXPORT_SYMBOL +int evthread_use_windows_threads(void); +/** + Defined if Libevent was built with support for evthread_use_windows_threads() +*/ +#define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 1 +#endif + +#if defined(EVENT__HAVE_PTHREADS) || defined(EVENT_IN_DOXYGEN_) +/** Sets up Libevent for use with Pthreads locking and thread ID functions. + Unavailable if Libevent is not build for use with pthreads. Requires + libraries to link against Libevent_pthreads as well as Libevent. + + @return 0 on success, -1 on failure. */ +EVENT2_EXPORT_SYMBOL +int evthread_use_pthreads(void); + +/* Enables posix mutex priority inheritance + * (if pthread_mutexattr_setprotocol() is supported). */ +#define EVTHREAD_PTHREAD_PRIO_INHERIT 0x01 + +/** + * Sets up Libevent for use with Pthreads locking and thread ID functions. + * Use evthread_use_pthreads_with_flags() to use Pthreads locking, taking the + * specified flags under consideration. + * + * @param flags the flags to apply when setting up Pthreads locking. @see EVTHREAD_PTHREAD_* + * @return 0 on success, -1 on failure. + **/ +EVENT2_EXPORT_SYMBOL +int evthread_use_pthreads_with_flags(int flags); + +/** Defined if Libevent was built with support for evthread_use_pthreads() */ +#define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1 + +#endif + +/** Enable debugging wrappers around the current lock callbacks. If Libevent + * makes one of several common locking errors, exit with an assertion failure. + * + * If you're going to call this function, you must do so before any locks are + * allocated. + **/ +EVENT2_EXPORT_SYMBOL +void evthread_enable_lock_debugging(void); + +/* Old (misspelled) version: This is deprecated; use + * evthread_enable_log_debugging instead. */ +EVENT2_EXPORT_SYMBOL +void evthread_enable_lock_debuging(void); + +#endif /* EVENT__DISABLE_THREAD_SUPPORT */ + +struct event_base; +/** Make sure it's safe to tell an event base to wake up from another thread + or a signal handler. + + You shouldn't need to call this by hand; configuring the base with thread + support should be necessary and sufficient. + + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evthread_make_base_notifiable(struct event_base *base); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_THREAD_H_INCLUDED_ */ diff --git a/include/libevent/event2/util.h b/include/libevent/event2/util.h new file mode 100644 index 0000000..60ff4cc --- /dev/null +++ b/include/libevent/event2/util.h @@ -0,0 +1,888 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_UTIL_H_INCLUDED_ +#define EVENT2_UTIL_H_INCLUDED_ + +/** @file event2/util.h + + @brief Common convenience functions for cross-platform portability and + related socket manipulations. + + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_STDINT_H +#include +#elif defined(EVENT__HAVE_INTTYPES_H) +#include +#endif +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_STDDEF_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#ifdef EVENT__HAVE_NETDB_H +#include +#endif + +#ifdef _WIN32 +#include +#ifdef EVENT__HAVE_GETADDRINFO +/* for EAI_* definitions. */ +#include +#endif +#else +#ifdef EVENT__HAVE_ERRNO_H +#include +#endif +#include +#endif + +#include + +/* Some openbsd autoconf versions get the name of this macro wrong. */ +#if defined(EVENT__SIZEOF_VOID__) && !defined(EVENT__SIZEOF_VOID_P) +#define EVENT__SIZEOF_VOID_P EVENT__SIZEOF_VOID__ +#endif + +/** + * @name Standard integer types. + * + * Integer type definitions for types that are supposed to be defined in the + * C99-specified stdint.h. Shamefully, some platforms do not include + * stdint.h, so we need to replace it. (If you are on a platform like this, + * your C headers are now over 10 years out of date. You should bug them to + * do something about this.) + * + * We define: + * + *
+ *
ev_uint64_t, ev_uint32_t, ev_uint16_t, ev_uint8_t
+ *
unsigned integer types of exactly 64, 32, 16, and 8 bits + * respectively.
+ *
ev_int64_t, ev_int32_t, ev_int16_t, ev_int8_t
+ *
signed integer types of exactly 64, 32, 16, and 8 bits + * respectively.
+ *
ev_uintptr_t, ev_intptr_t
+ *
unsigned/signed integers large enough + * to hold a pointer without loss of bits.
+ *
ev_ssize_t
+ *
A signed type of the same size as size_t
+ *
ev_off_t
+ *
A signed type typically used to represent offsets within a + * (potentially large) file
+ * + * @{ + */ +#ifdef EVENT__HAVE_UINT64_T +#define ev_uint64_t uint64_t +#define ev_int64_t int64_t +#elif defined(_WIN32) +#define ev_uint64_t unsigned __int64 +#define ev_int64_t signed __int64 +#elif EVENT__SIZEOF_LONG_LONG == 8 +#define ev_uint64_t unsigned long long +#define ev_int64_t long long +#elif EVENT__SIZEOF_LONG == 8 +#define ev_uint64_t unsigned long +#define ev_int64_t long +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint64_t ... +#define ev_int64_t ... +#else +#error "No way to define ev_uint64_t" +#endif + +#ifdef EVENT__HAVE_UINT32_T +#define ev_uint32_t uint32_t +#define ev_int32_t int32_t +#elif defined(_WIN32) +#define ev_uint32_t unsigned int +#define ev_int32_t signed int +#elif EVENT__SIZEOF_LONG == 4 +#define ev_uint32_t unsigned long +#define ev_int32_t signed long +#elif EVENT__SIZEOF_INT == 4 +#define ev_uint32_t unsigned int +#define ev_int32_t signed int +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint32_t ... +#define ev_int32_t ... +#else +#error "No way to define ev_uint32_t" +#endif + +#ifdef EVENT__HAVE_UINT16_T +#define ev_uint16_t uint16_t +#define ev_int16_t int16_t +#elif defined(_WIN32) +#define ev_uint16_t unsigned short +#define ev_int16_t signed short +#elif EVENT__SIZEOF_INT == 2 +#define ev_uint16_t unsigned int +#define ev_int16_t signed int +#elif EVENT__SIZEOF_SHORT == 2 +#define ev_uint16_t unsigned short +#define ev_int16_t signed short +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint16_t ... +#define ev_int16_t ... +#else +#error "No way to define ev_uint16_t" +#endif + +#ifdef EVENT__HAVE_UINT8_T +#define ev_uint8_t uint8_t +#define ev_int8_t int8_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint8_t ... +#define ev_int8_t ... +#else +#define ev_uint8_t unsigned char +#define ev_int8_t signed char +#endif + +#ifdef EVENT__HAVE_UINTPTR_T +#define ev_uintptr_t uintptr_t +#define ev_intptr_t intptr_t +#elif EVENT__SIZEOF_VOID_P <= 4 +#define ev_uintptr_t ev_uint32_t +#define ev_intptr_t ev_int32_t +#elif EVENT__SIZEOF_VOID_P <= 8 +#define ev_uintptr_t ev_uint64_t +#define ev_intptr_t ev_int64_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uintptr_t ... +#define ev_intptr_t ... +#else +#error "No way to define ev_uintptr_t" +#endif + +#ifdef EVENT__ssize_t +#define ev_ssize_t EVENT__ssize_t +#else +#define ev_ssize_t ssize_t +#endif + +/* Note that we define ev_off_t based on the compile-time size of off_t that + * we used to build Libevent, and not based on the current size of off_t. + * (For example, we don't define ev_off_t to off_t.). We do this because + * some systems let you build your software with different off_t sizes + * at runtime, and so putting in any dependency on off_t would risk API + * mismatch. + */ +#ifdef _WIN32 +#define ev_off_t ev_int64_t +#elif EVENT__SIZEOF_OFF_T == 8 +#define ev_off_t ev_int64_t +#elif EVENT__SIZEOF_OFF_T == 4 +#define ev_off_t ev_int32_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_off_t ... +#else +#define ev_off_t off_t +#endif +/**@}*/ + +/* Limits for integer types. + + We're making two assumptions here: + - The compiler does constant folding properly. + - The platform does signed arithmetic in two's complement. +*/ + +/** + @name Limits for integer types + + These macros hold the largest or smallest values possible for the + ev_[u]int*_t types. + + @{ +*/ +#ifndef EVENT__HAVE_STDINT_H +#define EV_UINT64_MAX ((((ev_uint64_t)0xffffffffUL) << 32) | 0xffffffffUL) +#define EV_INT64_MAX ((((ev_int64_t) 0x7fffffffL) << 32) | 0xffffffffL) +#define EV_INT64_MIN ((-EV_INT64_MAX) - 1) +#define EV_UINT32_MAX ((ev_uint32_t)0xffffffffUL) +#define EV_INT32_MAX ((ev_int32_t) 0x7fffffffL) +#define EV_INT32_MIN ((-EV_INT32_MAX) - 1) +#define EV_UINT16_MAX ((ev_uint16_t)0xffffUL) +#define EV_INT16_MAX ((ev_int16_t) 0x7fffL) +#define EV_INT16_MIN ((-EV_INT16_MAX) - 1) +#define EV_UINT8_MAX 255 +#define EV_INT8_MAX 127 +#define EV_INT8_MIN ((-EV_INT8_MAX) - 1) +#else +#define EV_UINT64_MAX UINT64_MAX +#define EV_INT64_MAX INT64_MAX +#define EV_INT64_MIN INT64_MIN +#define EV_UINT32_MAX UINT32_MAX +#define EV_INT32_MAX INT32_MAX +#define EV_INT32_MIN INT32_MIN +#define EV_UINT16_MAX UINT16_MAX +#define EV_INT16_MIN INT16_MIN +#define EV_INT16_MAX INT16_MAX +#define EV_UINT8_MAX UINT8_MAX +#define EV_INT8_MAX INT8_MAX +#define EV_INT8_MIN INT8_MIN +/** @} */ +#endif + + +/** + @name Limits for SIZE_T and SSIZE_T + + @{ +*/ +#if EVENT__SIZEOF_SIZE_T == 8 +#define EV_SIZE_MAX EV_UINT64_MAX +#define EV_SSIZE_MAX EV_INT64_MAX +#elif EVENT__SIZEOF_SIZE_T == 4 +#define EV_SIZE_MAX EV_UINT32_MAX +#define EV_SSIZE_MAX EV_INT32_MAX +#elif defined(EVENT_IN_DOXYGEN_) +#define EV_SIZE_MAX ... +#define EV_SSIZE_MAX ... +#else +#error "No way to define SIZE_MAX" +#endif + +#define EV_SSIZE_MIN ((-EV_SSIZE_MAX) - 1) +/**@}*/ + +#ifdef _WIN32 +#define ev_socklen_t int +#elif defined(EVENT__socklen_t) +#define ev_socklen_t EVENT__socklen_t +#else +#define ev_socklen_t socklen_t +#endif + +#ifdef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#if !defined(EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) \ + && !defined(ss_family) +#define ss_family __ss_family +#endif +#endif + +/** + * A type wide enough to hold the output of "socket()" or "accept()". On + * Windows, this is an intptr_t; elsewhere, it is an int. */ +#ifdef _WIN32 +#define evutil_socket_t intptr_t +#else +#define evutil_socket_t int +#endif + +/** + * Structure to hold information about a monotonic timer + * + * Use this with evutil_configure_monotonic_time() and + * evutil_gettime_monotonic(). + * + * This is an opaque structure; you can allocate one using + * evutil_monotonic_timer_new(). + * + * @see evutil_monotonic_timer_new(), evutil_monotonic_timer_free(), + * evutil_configure_monotonic_time(), evutil_gettime_monotonic() + */ +struct evutil_monotonic_timer +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +#define EV_MONOT_PRECISE 1 +#define EV_MONOT_FALLBACK 2 + +/** Format a date string using RFC 1123 format (used in HTTP). + * If `tm` is NULL, current system's time will be used. + * The number of characters written will be returned. + * One should check if the return value is smaller than `datelen` to check if + * the result is truncated or not. + */ +EVENT2_EXPORT_SYMBOL int +evutil_date_rfc1123(char *date, const size_t datelen, const struct tm *tm); + +/** Allocate a new struct evutil_monotonic_timer for use with the + * evutil_configure_monotonic_time() and evutil_gettime_monotonic() + * functions. You must configure the timer with + * evutil_configure_monotonic_time() before using it. + */ +EVENT2_EXPORT_SYMBOL +struct evutil_monotonic_timer * evutil_monotonic_timer_new(void); + +/** Free a struct evutil_monotonic_timer that was allocated using + * evutil_monotonic_timer_new(). + */ +EVENT2_EXPORT_SYMBOL +void evutil_monotonic_timer_free(struct evutil_monotonic_timer *timer); + +/** Set up a struct evutil_monotonic_timer; flags can include + * EV_MONOT_PRECISE and EV_MONOT_FALLBACK. + */ +EVENT2_EXPORT_SYMBOL +int evutil_configure_monotonic_time(struct evutil_monotonic_timer *timer, + int flags); + +/** Query the current monotonic time from a struct evutil_monotonic_timer + * previously configured with evutil_configure_monotonic_time(). Monotonic + * time is guaranteed never to run in reverse, but is not necessarily epoch- + * based, or relative to any other definite point. Use it to make reliable + * measurements of elapsed time between events even when the system time + * may be changed. + * + * It is not safe to use this funtion on the same timer from multiple + * threads_. + */ +EVENT2_EXPORT_SYMBOL +int evutil_gettime_monotonic(struct evutil_monotonic_timer *timer, + struct timeval *tp); + +/** Create two new sockets that are connected to each other. + + On Unix, this simply calls socketpair(). On Windows, it uses the + loopback network interface on 127.0.0.1, and only + AF_INET,SOCK_STREAM are supported. + + (This may fail on some Windows hosts where firewall software has cleverly + decided to keep 127.0.0.1 from talking to itself.) + + Parameters and return values are as for socketpair() +*/ +EVENT2_EXPORT_SYMBOL +int evutil_socketpair(int d, int type, int protocol, evutil_socket_t sv[2]); +/** Do platform-specific operations as needed to make a socket nonblocking. + + @param sock The socket to make nonblocking + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_socket_nonblocking(evutil_socket_t sock); + +/** Do platform-specific operations to make a listener socket reusable. + + Specifically, we want to make sure that another program will be able + to bind this address right after we've closed the listener. + + This differs from Windows's interpretation of "reusable", which + allows multiple listeners to bind the same address at the same time. + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable(evutil_socket_t sock); + +/** Do platform-specific operations to make a listener port reusable. + + Specifically, we want to make sure that multiple programs which also + set the same socket option will be able to bind, listen at the same time. + + This is a feature available only to Linux 3.9+ + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable_port(evutil_socket_t sock); + +/** Set ipv6 only bind socket option to make listener work only in ipv6 sockets. + + According to RFC3493 and most Linux distributions, default value for the + sockets is to work in IPv4-mapped mode. In IPv4-mapped mode, it is not possible + to bind same port from different IPv4 and IPv6 handlers. + + @param sock The socket to make in ipv6only working mode + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_ipv6only(evutil_socket_t sock); + +/** Do platform-specific operations as needed to close a socket upon a + successful execution of one of the exec*() functions. + + @param sock The socket to be closed + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_socket_closeonexec(evutil_socket_t sock); + +/** Do the platform-specific call needed to close a socket returned from + socket() or accept(). + + @param sock The socket to be closed + @return 0 on success (whether the operation is supported or not), + -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_closesocket(evutil_socket_t sock); +#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s) + +/** Do platform-specific operations, if possible, to make a tcp listener + * socket defer accept()s until there is data to read. + * + * Not all platforms support this. You don't want to do this for every + * listener socket: only the ones that implement a protocol where the + * client transmits before the server needs to respond. + * + * @param sock The listening socket to to make deferred + * @return 0 on success (whether the operation is supported or not), + * -1 on failure +*/ +EVENT2_EXPORT_SYMBOL +int evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock); + +#ifdef _WIN32 +/** Return the most recent socket error. Not idempotent on all platforms. */ +#define EVUTIL_SOCKET_ERROR() WSAGetLastError() +/** Replace the most recent socket error with errcode */ +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { WSASetLastError(errcode); } while (0) +/** Return the most recent socket error to occur on sock. */ +EVENT2_EXPORT_SYMBOL +int evutil_socket_geterror(evutil_socket_t sock); +/** Convert a socket error to a string. */ +EVENT2_EXPORT_SYMBOL +const char *evutil_socket_error_to_string(int errcode); +#define EVUTIL_INVALID_SOCKET INVALID_SOCKET +#elif defined(EVENT_IN_DOXYGEN_) +/** + @name Socket error functions + + These functions are needed for making programs compatible between + Windows and Unix-like platforms. + + You see, Winsock handles socket errors differently from the rest of + the world. Elsewhere, a socket error is like any other error and is + stored in errno. But winsock functions require you to retrieve the + error with a special function, and don't let you use strerror for + the error codes. And handling EWOULDBLOCK is ... different. + + @{ +*/ +/** Return the most recent socket error. Not idempotent on all platforms. */ +#define EVUTIL_SOCKET_ERROR() ... +/** Replace the most recent socket error with errcode */ +#define EVUTIL_SET_SOCKET_ERROR(errcode) ... +/** Return the most recent socket error to occur on sock. */ +#define evutil_socket_geterror(sock) ... +/** Convert a socket error to a string. */ +#define evutil_socket_error_to_string(errcode) ... +#define EVUTIL_INVALID_SOCKET -1 +/**@}*/ +#else /** !EVENT_IN_DOXYGEN_ && !_WIN32 */ +#define EVUTIL_SOCKET_ERROR() (errno) +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { errno = (errcode); } while (0) +#define evutil_socket_geterror(sock) (errno) +#define evutil_socket_error_to_string(errcode) (strerror(errcode)) +#define EVUTIL_INVALID_SOCKET -1 +#endif /** !_WIN32 */ + + +/** + * @name Manipulation macros for struct timeval. + * + * We define replacements + * for timeradd, timersub, timerclear, timercmp, and timerisset. + * + * @{ + */ +#ifdef EVENT__HAVE_TIMERADD +#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) +#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) +#else +#define evutil_timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define evutil_timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* !EVENT__HAVE_TIMERADD */ + +#ifdef EVENT__HAVE_TIMERCLEAR +#define evutil_timerclear(tvp) timerclear(tvp) +#else +#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +/**@}*/ + +/** Return true iff the tvp is related to uvp according to the relational + * operator cmp. Recognized values for cmp are ==, <=, <, >=, and >. */ +#define evutil_timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) + +#ifdef EVENT__HAVE_TIMERISSET +#define evutil_timerisset(tvp) timerisset(tvp) +#else +#define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif + +/** Replacement for offsetof on platforms that don't define it. */ +#ifdef offsetof +#define evutil_offsetof(type, field) offsetof(type, field) +#else +#define evutil_offsetof(type, field) ((off_t)(&((type *)0)->field)) +#endif + +/* big-int related functions */ +/** Parse a 64-bit value from a string. Arguments are as for strtol. */ +EVENT2_EXPORT_SYMBOL +ev_int64_t evutil_strtoll(const char *s, char **endptr, int base); + +/** Replacement for gettimeofday on platforms that lack it. */ +#ifdef EVENT__HAVE_GETTIMEOFDAY +#define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#else +struct timezone; +EVENT2_EXPORT_SYMBOL +int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + +/** Replacement for snprintf to get consistent behavior on platforms for + which the return value of snprintf does not conform to C99. + */ +EVENT2_EXPORT_SYMBOL +int evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif +; +/** Replacement for vsnprintf to get consistent behavior on platforms for + which the return value of snprintf does not conform to C99. + */ +EVENT2_EXPORT_SYMBOL +int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 0))) +#endif +; + +/** Replacement for inet_ntop for platforms which lack it. */ +EVENT2_EXPORT_SYMBOL +const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); +/** Variation of inet_pton that also parses IPv6 scopes. Public for + unit tests. No reason to call this directly. + */ +EVENT2_EXPORT_SYMBOL +int evutil_inet_pton_scope(int af, const char *src, void *dst, + unsigned *indexp); +/** Replacement for inet_pton for platforms which lack it. */ +EVENT2_EXPORT_SYMBOL +int evutil_inet_pton(int af, const char *src, void *dst); +struct sockaddr; + +/** Parse an IPv4 or IPv6 address, with optional port, from a string. + + Recognized formats are: + - [IPv6Address]:port + - [IPv6Address] + - IPv6Address + - IPv4Address:port + - IPv4Address + + If no port is specified, the port in the output is set to 0. + + @param str The string to parse. + @param out A struct sockaddr to hold the result. This should probably be + a struct sockaddr_storage. + @param outlen A pointer to the number of bytes that that 'out' can safely + hold. Set to the number of bytes used in 'out' on success. + @return -1 if the address is not well-formed, if the port is out of range, + or if out is not large enough to hold the result. Otherwise returns + 0 on success. +*/ +EVENT2_EXPORT_SYMBOL +int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen); + +/** Compare two sockaddrs; return 0 if they are equal, or less than 0 if sa1 + * preceeds sa2, or greater than 0 if sa1 follows sa2. If include_port is + * true, consider the port as well as the address. Only implemented for + * AF_INET and AF_INET6 addresses. The ordering is not guaranteed to remain + * the same between Libevent versions. */ +EVENT2_EXPORT_SYMBOL +int evutil_sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2, + int include_port); + +/** As strcasecmp, but always compares the characters in locale-independent + ASCII. That's useful if you're handling data in ASCII-based protocols. + */ +EVENT2_EXPORT_SYMBOL +int evutil_ascii_strcasecmp(const char *str1, const char *str2); +/** As strncasecmp, but always compares the characters in locale-independent + ASCII. That's useful if you're handling data in ASCII-based protocols. + */ +EVENT2_EXPORT_SYMBOL +int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n); + +/* Here we define evutil_addrinfo to the native addrinfo type, or redefine it + * if this system has no getaddrinfo(). */ +#ifdef EVENT__HAVE_STRUCT_ADDRINFO +#define evutil_addrinfo addrinfo +#else +/** A definition of struct addrinfo for systems that lack it. + + (This is just an alias for struct addrinfo if the system defines + struct addrinfo.) +*/ +struct evutil_addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for nodename */ + struct sockaddr *ai_addr; /* binary address */ + struct evutil_addrinfo *ai_next; /* next structure in linked list */ +}; +#endif +/** @name evutil_getaddrinfo() error codes + + These values are possible error codes for evutil_getaddrinfo() and + related functions. + + @{ +*/ +#if defined(EAI_ADDRFAMILY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_ADDRFAMILY EAI_ADDRFAMILY +#else +#define EVUTIL_EAI_ADDRFAMILY -901 +#endif +#if defined(EAI_AGAIN) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_AGAIN EAI_AGAIN +#else +#define EVUTIL_EAI_AGAIN -902 +#endif +#if defined(EAI_BADFLAGS) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_BADFLAGS EAI_BADFLAGS +#else +#define EVUTIL_EAI_BADFLAGS -903 +#endif +#if defined(EAI_FAIL) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_FAIL EAI_FAIL +#else +#define EVUTIL_EAI_FAIL -904 +#endif +#if defined(EAI_FAMILY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_FAMILY EAI_FAMILY +#else +#define EVUTIL_EAI_FAMILY -905 +#endif +#if defined(EAI_MEMORY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_MEMORY EAI_MEMORY +#else +#define EVUTIL_EAI_MEMORY -906 +#endif +/* This test is a bit complicated, since some MS SDKs decide to + * remove NODATA or redefine it to be the same as NONAME, in a + * fun interpretation of RFC 2553 and RFC 3493. */ +#if defined(EAI_NODATA) && defined(EVENT__HAVE_GETADDRINFO) && (!defined(EAI_NONAME) || EAI_NODATA != EAI_NONAME) +#define EVUTIL_EAI_NODATA EAI_NODATA +#else +#define EVUTIL_EAI_NODATA -907 +#endif +#if defined(EAI_NONAME) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_NONAME EAI_NONAME +#else +#define EVUTIL_EAI_NONAME -908 +#endif +#if defined(EAI_SERVICE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SERVICE EAI_SERVICE +#else +#define EVUTIL_EAI_SERVICE -909 +#endif +#if defined(EAI_SOCKTYPE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SOCKTYPE EAI_SOCKTYPE +#else +#define EVUTIL_EAI_SOCKTYPE -910 +#endif +#if defined(EAI_SYSTEM) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SYSTEM EAI_SYSTEM +#else +#define EVUTIL_EAI_SYSTEM -911 +#endif + +#define EVUTIL_EAI_CANCEL -90001 + +#if defined(AI_PASSIVE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_PASSIVE AI_PASSIVE +#else +#define EVUTIL_AI_PASSIVE 0x1000 +#endif +#if defined(AI_CANONNAME) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_CANONNAME AI_CANONNAME +#else +#define EVUTIL_AI_CANONNAME 0x2000 +#endif +#if defined(AI_NUMERICHOST) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_NUMERICHOST AI_NUMERICHOST +#else +#define EVUTIL_AI_NUMERICHOST 0x4000 +#endif +#if defined(AI_NUMERICSERV) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_NUMERICSERV AI_NUMERICSERV +#else +#define EVUTIL_AI_NUMERICSERV 0x8000 +#endif +#if defined(AI_V4MAPPED) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_V4MAPPED AI_V4MAPPED +#else +#define EVUTIL_AI_V4MAPPED 0x10000 +#endif +#if defined(AI_ALL) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_ALL AI_ALL +#else +#define EVUTIL_AI_ALL 0x20000 +#endif +#if defined(AI_ADDRCONFIG) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_ADDRCONFIG AI_ADDRCONFIG +#else +#define EVUTIL_AI_ADDRCONFIG 0x40000 +#endif +/**@}*/ + +struct evutil_addrinfo; +/** + * This function clones getaddrinfo for systems that don't have it. For full + * details, see RFC 3493, section 6.1. + * + * Limitations: + * - When the system has no getaddrinfo, we fall back to gethostbyname_r or + * gethostbyname, with their attendant issues. + * - The AI_V4MAPPED and AI_ALL flags are not currently implemented. + * + * For a nonblocking variant, see evdns_getaddrinfo. + */ +EVENT2_EXPORT_SYMBOL +int evutil_getaddrinfo(const char *nodename, const char *servname, + const struct evutil_addrinfo *hints_in, struct evutil_addrinfo **res); + +/** Release storage allocated by evutil_getaddrinfo or evdns_getaddrinfo. */ +EVENT2_EXPORT_SYMBOL +void evutil_freeaddrinfo(struct evutil_addrinfo *ai); + +EVENT2_EXPORT_SYMBOL +const char *evutil_gai_strerror(int err); + +/** Generate n bytes of secure pseudorandom data, and store them in buf. + * + * Current versions of Libevent use an ARC4-based random number generator, + * seeded using the platform's entropy source (/dev/urandom on Unix-like + * systems; BCryptGenRandom on Windows). This is not actually as secure as it + * should be: ARC4 is a pretty lousy cipher, and the current implementation + * provides only rudimentary prediction- and backtracking-resistance. Don't + * use this for serious cryptographic applications. + */ +EVENT2_EXPORT_SYMBOL +void evutil_secure_rng_get_bytes(void *buf, size_t n); + +/** + * Seed the secure random number generator if needed, and return 0 on + * success or -1 on failure. + * + * It is okay to call this function more than once; it will still return + * 0 if the RNG has been successfully seeded and -1 if it can't be + * seeded. + * + * Ordinarily you don't need to call this function from your own code; + * Libevent will seed the RNG itself the first time it needs good random + * numbers. You only need to call it if (a) you want to double-check + * that one of the seeding methods did succeed, or (b) you plan to drop + * the capability to seed (by chrooting, or dropping capabilities, or + * whatever), and you want to make sure that seeding happens before your + * program loses the ability to do it. + */ +EVENT2_EXPORT_SYMBOL +int evutil_secure_rng_init(void); + +/** + * Set a filename to use in place of /dev/urandom for seeding the secure + * PRNG. Return 0 on success, -1 on failure. + * + * Call this function BEFORE calling any other initialization or RNG + * functions. + * + * (This string will _NOT_ be copied internally. Do not free it while any + * user of the secure RNG might be running. Don't pass anything other than a + * real /dev/...random device file here, or you might lose security.) + * + * This API is unstable, and might change in a future libevent version. + */ +EVENT2_EXPORT_SYMBOL +int evutil_secure_rng_set_urandom_device_file(char *fname); + +#if !defined(EVENT__HAVE_ARC4RANDOM) || defined(EVENT__HAVE_ARC4RANDOM_ADDRANDOM) +/** Seed the random number generator with extra random bytes. + + You should almost never need to call this function; it should be + sufficient to invoke evutil_secure_rng_init(), or let Libevent take + care of calling evutil_secure_rng_init() on its own. + + If you call this function as a _replacement_ for the regular + entropy sources, then you need to be sure that your input + contains a fairly large amount of strong entropy. Doing so is + notoriously hard: most people who try get it wrong. Watch out! + + @param dat a buffer full of a strong source of random numbers + @param datlen the number of bytes to read from datlen + */ +EVENT2_EXPORT_SYMBOL +void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT1_EVUTIL_H_INCLUDED_ */ diff --git a/include/libevent/event2/visibility.h b/include/libevent/event2/visibility.h new file mode 100644 index 0000000..2b07994 --- /dev/null +++ b/include/libevent/event2/visibility.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_VISIBILITY_H_INCLUDED_ +#define EVENT2_VISIBILITY_H_INCLUDED_ + +#include + +#if defined(event_shared_EXPORTS) || \ + defined(event_extra_shared_EXPORTS) || \ + defined(event_core_shared_EXPORTS) || \ + defined(event_pthreads_shared_EXPORTS) || \ + defined(event_openssl_shared_EXPORTS) || \ + defined(event_mbedtls_shared_EXPORTS) + +# if defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define EVENT2_EXPORT_SYMBOL __global +# elif defined __GNUC__ +# define EVENT2_EXPORT_SYMBOL __attribute__ ((visibility("default"))) +# elif defined(_MSC_VER) +# define EVENT2_EXPORT_SYMBOL __declspec(dllexport) +# else +# define EVENT2_EXPORT_SYMBOL /* unknown compiler */ +# endif + +#else /* event_*_EXPORTS */ + +# define EVENT2_EXPORT_SYMBOL + +#endif /* event_*_EXPORTS */ + +/** We need to dllimport event_debug_logging_mask_ into event_extra */ +#if defined(_MSC_VER) +# if defined(event_core_shared_EXPORTS) /** from core export */ +# define EVENT2_CORE_EXPORT_SYMBOL __declspec(dllexport) +# elif defined(event_extra_shared_EXPORTS) || /** from extra import */ \ + defined(EVENT_VISIBILITY_WANT_DLLIMPORT) +# define EVENT2_CORE_EXPORT_SYMBOL __declspec(dllimport) +# endif +#endif /* _MSC_VER */ +#if !defined(EVENT2_CORE_EXPORT_SYMBOL) +# define EVENT2_CORE_EXPORT_SYMBOL EVENT2_EXPORT_SYMBOL +#endif + +#endif /* EVENT2_VISIBILITY_H_INCLUDED_ */ diff --git a/include/libevent/event2/watch.h b/include/libevent/event2/watch.h new file mode 100644 index 0000000..90007aa --- /dev/null +++ b/include/libevent/event2/watch.h @@ -0,0 +1,136 @@ +/* + * 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. The name of the author may not 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. + */ +#ifndef EVENT2_WATCH_H_INCLUDED_ +#define EVENT2_WATCH_H_INCLUDED_ + +/** @file event2/watch.h + + @brief "Prepare" and "check" watchers. + + "Prepare" and "check" watchers. A "prepare" watcher is a callback that fires + immediately before polling for I/O. A "check" watcher is a callback that + fires immediately after polling and before processing any active events. This + may be useful for embedding other libraries' event loops (e.g. UI toolkits) + into libevent's. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct event_base; +struct evwatch; +struct evwatch_prepare_cb_info; +struct evwatch_check_cb_info; +struct timeval; + +/** + Prepare callback, invoked by event_base_loop immediately before polling for + I/O. + + @param watcher the prepare watcher that invoked this callback. + @param info contextual information passed from event_base_loop. + @param arg additional user-defined argument, set in `evwatch_prepare_new`. + */ +typedef void (*evwatch_prepare_cb)(struct evwatch *, const struct evwatch_prepare_cb_info *, void *); + +/** + Check callback, invoked by event_base_loop immediately after polling for I/O + and before processing any active events. + + @param watcher the check watcher that invoked this callback. + @param info contextual information passed from event_base_loop. + @param arg additional user-defined argument, set in `evwatch_check_new`. + */ +typedef void (*evwatch_check_cb)(struct evwatch *, const struct evwatch_check_cb_info *, void *); + +/** + Register a new "prepare" watcher, to be called in the event loop prior to + polling for events. Watchers will be called in the order they were + registered. + + @param base the event_base to operate on. + @param callback the callback function to invoke. + @param arg additional user-defined argument provided to the callback. + @return a pointer to the newly allocated event watcher. + */ +EVENT2_EXPORT_SYMBOL +struct evwatch *evwatch_prepare_new(struct event_base *base, evwatch_prepare_cb callback, void *arg); + +/** + Register a new "check" watcher, to be called in the event loop after polling + for events and before handling them. Watchers will be called in the order + they were registered. + + @param base the event_base to operate on. + @param callback the callback function to invoke. + @param arg additional user-defined argument provided to the callback. + @return a pointer to the newly allocated event watcher. + */ +EVENT2_EXPORT_SYMBOL +struct evwatch *evwatch_check_new(struct event_base *base, evwatch_check_cb callback, void *arg); + +/** + Get the event_base that a given evwatch is registered with. + + @param watcher the watcher to get the event_base for. + @return the event_base for the given watcher. + */ +EVENT2_EXPORT_SYMBOL +struct event_base *evwatch_base(struct evwatch *watcher); + +/** + Deregister and deallocate a watcher. Any watchers not freed using + evwatch_free will eventually be deallocated in event_base_free + (calling evwatch_free on a watcher after event_base_free has been + called on its corresponding event_base is an error). + + @param watcher the watcher to deregister and deallocate. + */ +EVENT2_EXPORT_SYMBOL +void evwatch_free(struct evwatch *watcher); + +/** + Get the timeout (the expected polling duration) passed to the underlying + implementation's `dispatch`. This value will only be set if there are pending + EV_TIMEOUT events and if the event_base isn't in EVLOOP_NONBLOCK mode. It may + be a useful performance statistic to compare the expected polling duration + against the actual polling duration (that is, the time difference measured + between this prepare callback and the following check callback). + + @param info the "prepare" callback info. + @param timeout address of a timeval to write the polling duration to. + @return 1 if a value was written to *timeout, or 0 if not. + */ +EVENT2_EXPORT_SYMBOL +int evwatch_prepare_get_timeout(const struct evwatch_prepare_cb_info *info, struct timeval *timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_WATCH_H_INCLUDED_ */ diff --git a/include/libevent/event2/ws.h b/include/libevent/event2/ws.h new file mode 100644 index 0000000..11fc2e5 --- /dev/null +++ b/include/libevent/event2/ws.h @@ -0,0 +1,58 @@ +#ifndef EVENT2_WS_H_INCLUDED_ +#define EVENT2_WS_H_INCLUDED_ + +struct evws_connection; + +#define WS_CR_NONE 0 +#define WS_CR_NORMAL 1000 +#define WS_CR_PROTO_ERR 1002 +#define WS_CR_DATA_TOO_BIG 1009 + +#define WS_TEXT_FRAME 0x1 +#define WS_BINARY_FRAME 0x2 + +typedef void (*ws_on_msg_cb)( + struct evws_connection *, int type, const unsigned char *, size_t, void *); +typedef void (*ws_on_close_cb)(struct evws_connection *, void *); + +/** Opens new WebSocket session from HTTP request. + @param req a request object + @param cb the callback function that gets invoked on receiving message + with len bytes length. In case of receiving text messages user is responsible + to make a string with terminating \0 (with copying-out data) or use text data + other way in which \0 is not required + @param arg an additional context argument for the callback + @return a pointer to a newly initialized WebSocket connection or NULL + on error + @see evws_close() + */ +EVENT2_EXPORT_SYMBOL +struct evws_connection *evws_new_session( + struct evhttp_request *req, ws_on_msg_cb, void *arg, int options); + +/** Sends data over WebSocket connection */ +EVENT2_EXPORT_SYMBOL +void evws_send( + struct evws_connection *evws, const char *packet_str, size_t str_len); + +/** Closes a WebSocket connection with reason code */ +EVENT2_EXPORT_SYMBOL +void evws_close(struct evws_connection *evws, uint16_t reason); + +/** Sets a callback for connection close. */ +EVENT2_EXPORT_SYMBOL +void evws_connection_set_closecb( + struct evws_connection *evws, ws_on_close_cb, void *); + +/** Frees a WebSocket connection */ +EVENT2_EXPORT_SYMBOL +void evws_connection_free(struct evws_connection *evws); + +/** + * Return the bufferevent that an evws_connection is using. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *evws_connection_get_bufferevent( + struct evws_connection *evws); + +#endif diff --git a/include/libevent/evhttp.h b/include/libevent/evhttp.h new file mode 100644 index 0000000..549bc9b --- /dev/null +++ b/include/libevent/evhttp.h @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2007 Niels Provos + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT1_EVHTTP_H_INCLUDED_ +#define EVENT1_EVHTTP_H_INCLUDED_ + +/** @file evhttp.h + + An http implementation subsystem for Libevent. + + The header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other + headers. + */ + +#include +#include +#include +#include + +#endif /* EVENT1_EVHTTP_H_INCLUDED_ */ diff --git a/include/libevent/evrpc.h b/include/libevent/evrpc.h new file mode 100644 index 0000000..7e986f7 --- /dev/null +++ b/include/libevent/evrpc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT1_EVRPC_H_INCLUDED_ +#define EVENT1_EVRPC_H_INCLUDED_ + +/** @file evrpc.h + + An RPC system for Libevent. + + The header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other + headers. + */ + +#include +#include +#include +#include + +#endif /* EVENT1_EVRPC_H_INCLUDED_ */ diff --git a/include/libevent/evutil.h b/include/libevent/evutil.h new file mode 100644 index 0000000..12c137d --- /dev/null +++ b/include/libevent/evutil.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not 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. + */ +#ifndef EVENT1_EVUTIL_H_INCLUDED_ +#define EVENT1_EVUTIL_H_INCLUDED_ + +/** @file evutil.h + + Utility and compatibility functions for Libevent. + + The header is deprecated in Libevent 2.0 and later; please + use instead. +*/ + +#include + +#endif /* EVENT1_EVUTIL_H_INCLUDED_ */ diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h new file mode 100644 index 0000000..35650af --- /dev/null +++ b/include/rapidjson/allocators.h @@ -0,0 +1,693 @@ +// 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. +// +// 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 RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" +#include "internal/meta.h" + +#include +#include + +#if RAPIDJSON_HAS_CXX11 +#include +#endif + +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 +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// 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 RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); } + + bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return true; + } + bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return false; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// 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 +class MemoryPoolAllocator { + //! 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. + }; + + struct SharedData { + ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + BaseAllocator* ownBaseAllocator; //!< base allocator created by this object. + size_t refcount; + bool ownBuffer; + }; + + static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData)); + static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader)); + + static inline ChunkHeader *GetChunkHead(SharedData *shared) + { + return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA); + } + static inline uint8_t *GetChunkBuffer(SharedData *shared) + { + return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER; + } + + static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + explicit + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()), + shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0)) + { + RAPIDJSON_ASSERT(baseAllocator_ != 0); + RAPIDJSON_ASSERT(shared_ != 0); + if (baseAllocator) { + shared_->ownBaseAllocator = 0; + } + else { + shared_->ownBaseAllocator = baseAllocator_; + } + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = 0; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBuffer = true; + shared_->refcount = 1; + } + + //! 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) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator), + shared_(static_cast(AlignBuffer(buffer, size))) + { + RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER); + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBaseAllocator = 0; + shared_->ownBuffer = false; + shared_->refcount = 1; + } + + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + ++shared_->refcount; + } + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + ++rhs.shared_->refcount; + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + rhs.shared_ = 0; + } + MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + rhs.shared_ = 0; + return *this; + } +#endif + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT { + if (!shared_) { + // do nothing if moved + return; + } + if (shared_->refcount > 1) { + --shared_->refcount; + return; + } + Clear(); + BaseAllocator *a = shared_->ownBaseAllocator; + if (shared_->ownBuffer) { + baseAllocator_->Free(shared_); + } + RAPIDJSON_DELETE(a); + } + + //! Deallocates all memory chunks, excluding the first/user one. + void Clear() RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + for (;;) { + ChunkHeader* c = shared_->chunkHead; + if (!c->next) { + break; + } + shared_->chunkHead = c->next; + baseAllocator_->Free(c); + } + shared_->chunkHead->size = 0; + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t capacity = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t size = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Whether the allocator is shared. + /*! \return true or false. + */ + bool Shared() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + return shared_->refcount > 1; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity)) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size; + shared_->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); + + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = 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 == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) { + shared_->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) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing + + //! Compare (equality) with another MemoryPoolAllocator + bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + return shared_ == rhs.shared_; + } + //! Compare (inequality) with another MemoryPoolAllocator + bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + return !operator==(rhs); + } + +private: + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = shared_->chunkHead; + shared_->chunkHead = chunk; + return true; + } + else + return false; + } + + static inline void* AlignBuffer(void* buf, size_t &size) + { + RAPIDJSON_NOEXCEPT_ASSERT(buf != 0); + const uintptr_t mask = sizeof(void*) - 1; + const uintptr_t ubuf = reinterpret_cast(buf); + if (RAPIDJSON_UNLIKELY(ubuf & mask)) { + const uintptr_t abuf = (ubuf + mask) & ~mask; + RAPIDJSON_ASSERT(size >= abuf - ubuf); + buf = reinterpret_cast(abuf); + size -= abuf - ubuf; + } + return buf; + } + + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + SharedData *shared_; //!< The shared data of the allocator +}; + +namespace internal { + template + struct IsRefCounted : + public FalseType + { }; + template + struct IsRefCounted::Type> : + public TrueType + { }; +} + +template +inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n) +{ + RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T)); + return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T))); +} + +template +inline T *Malloc(A& a, size_t n = 1) +{ + return Realloc(a, NULL, 0, n); +} + +template +inline void Free(A& a, T *p, size_t n = 1) +{ + static_cast(Realloc(a, p, n, 0)); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited +#endif + +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; +#if RAPIDJSON_HAS_CXX11 + typedef std::allocator_traits traits_type; +#else + typedef allocator_type traits_type; +#endif + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(std::move(rhs)), + baseAllocator_(std::move(rhs.baseAllocator_)) + { } +#endif +#if RAPIDJSON_HAS_CXX11 + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; +#endif + + /* implicit */ + StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(allocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename traits_type::size_type size_type; + typedef typename traits_type::difference_type difference_type; + + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::const_pointer const_pointer; + +#if RAPIDJSON_HAS_CXX11 + + typedef typename std::add_lvalue_reference::type &reference; + typedef typename std::add_lvalue_reference::type>::type &const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return traits_type::max_size(*this); + } + + template + void construct(pointer p, Args&&... args) + { + traits_type::construct(*this, p, std::forward(args)...); + } + void destroy(pointer p) + { + traits_type::destroy(*this, p); + } + +#else // !RAPIDJSON_HAS_CXX11 + + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return allocator_type::max_size(); + } + + void construct(pointer p, const_reference r) + { + allocator_type::construct(p, r); + } + void destroy(pointer p) + { + allocator_type::destroy(p); + } + +#endif // !RAPIDJSON_HAS_CXX11 + + template + U* allocate(size_type n = 1, const void* = 0) + { + return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n); + } + template + void deallocate(U* p, size_type n = 1) + { + RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n); + } + + pointer allocate(size_type n = 1, const void* = 0) + { + return allocate(n); + } + void deallocate(pointer p, size_type n = 1) + { + deallocate(p, n); + } + +#if RAPIDJSON_HAS_CXX11 + using is_always_equal = std::is_empty; +#endif + + template + bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return baseAllocator_ == rhs.baseAllocator_; + } + template + bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return !operator==(rhs); + } + + //! rapidjson Allocator concept + static const bool kNeedFree = BaseAllocator::kNeedFree; + static const bool kRefCounted = internal::IsRefCounted::Value; + void* Malloc(size_t size) + { + return baseAllocator_.Malloc(size); + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) + { + return baseAllocator_.Realloc(originalPtr, originalSize, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT + { + BaseAllocator::Free(ptr); + } + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; + +#if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17 +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename allocator_type::value_type value_type; + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/cursorstreamwrapper.h b/include/rapidjson/cursorstreamwrapper.h new file mode 100644 index 0000000..fd6513d --- /dev/null +++ b/include/rapidjson/cursorstreamwrapper.h @@ -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. +// +// 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 RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h new file mode 100644 index 0000000..2cd9a70 --- /dev/null +++ b/include/rapidjson/document.h @@ -0,0 +1,3043 @@ +// 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. +// +// 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 RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifdef GetObject +// see https://github.com/Tencent/rapidjson/issues/1448 +// a former included windows.h might have defined a macro called GetObject, which affects +// GetObject defined here. This ensures the macro does not get applied +#pragma push_macro("GetObject") +#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#undef GetObject +#endif + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_USE_MEMBERSMAP +#include // std::multimap +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(NotNullStrLen(str)) {} + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: + DoCopyMembers(rhs, allocator, copyConstStrings); + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release + // their Allocator if it's refcounted (e.g. MemoryPoolAllocator). + if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 && + internal::IsRefCounted::Value)) { + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(e); + } + } + break; + + case kObjectFlag: + DoFreeMembers(); + break; + + case kCopyStringFlag: + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(const_cast(GetStringPointer())); + } + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + // Can't destroy "this" before assigning "rhs", otherwise "rhs" + // could be used after free if it's an sub-Value of "this", + // hence the temporary danse. + GenericValue temp; + temp.RawAssign(rhs); + this->~GenericValue(); + RawAssign(temp); + } + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + +#ifndef __cpp_impl_three_way_comparison + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} +#endif + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + +#if RAPIDJSON_HAS_CXX11 + // Use thread-local storage to prevent races between threads. + // Use static buffer and placement-new to prevent destruction, with + // alignas() to ensure proper alignment. + alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(_MSC_VER) && _MSC_VER < 1900 + // There's no way to solve both thread locality and proper alignment + // simultaneously. + __declspec(thread) static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(__GNUC__) || defined(__clang__) + // This will generate -Wexit-time-destructors in clang, but that's + // better than having under-alignment. + __thread static GenericValue buffer; + return buffer; +#else + // Don't know what compiler this is, so don't know how to ensure + // thread-locality. + static GenericValue buffer; + return buffer; +#endif + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + DoReserveMembers(newCapacity, allocator); + return *this; + } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + return DoFindMember(name); + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + DoAddMember(name, value, allocator); + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + DoClearMembers(); + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + return DoRemoveMember(m); + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + return DoEraseMembers(first, last); + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (ConstValueIterator v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str); + } + static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length; + } + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + +#if RAPIDJSON_USE_MEMBERSMAP + + struct MapTraits { + struct Less { + bool operator()(const Data& s1, const Data& s2) const { + SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2); + int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2)); + return cmp < 0 || (cmp == 0 && n1 < n2); + } + }; + typedef std::pair Pair; + typedef std::multimap > Map; + typedef typename Map::iterator Iterator; + }; + typedef typename MapTraits::Map Map; + typedef typename MapTraits::Less MapLess; + typedef typename MapTraits::Pair MapPair; + typedef typename MapTraits::Iterator MapIterator; + + // + // Layout of the members' map/array, re(al)located according to the needed capacity: + // + // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]} + // + // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed) + // + + static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) { + return RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(capacity * sizeof(Member)) + + capacity * sizeof(MapIterator); + } + + static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) { + return *reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType))); + } + + static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member))); + } + + static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) { + RAPIDJSON_ASSERT(members != 0); + return *reinterpret_cast(reinterpret_cast(members) - + RAPIDJSON_ALIGN(sizeof(SizeType)) - + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting.. + RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) { +#if RAPIDJSON_HAS_CXX11 + MapIterator ret = std::move(rhs); +#else + MapIterator ret = rhs; +#endif + rhs.~MapIterator(); + return ret; + } + + Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) { + Map **newMap = static_cast(allocator.Malloc(GetMapLayoutSize(newCapacity))); + GetMapCapacity(*newMap) = newCapacity; + if (!oldMap) { + *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator); + } + else { + *newMap = *oldMap; + size_t count = (*oldMap)->size(); + std::memcpy(static_cast(GetMapMembers(*newMap)), + static_cast(GetMapMembers(*oldMap)), + count * sizeof(Member)); + MapIterator *oldIt = GetMapIterators(*oldMap), + *newIt = GetMapIterators(*newMap); + while (count--) { + new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count])); + } + Allocator::Free(oldMap); + } + return *newMap; + } + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return GetMapMembers(DoReallocMap(0, capacity, allocator)); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* oldMembers = GetMembersPointer(); + Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0, + *&newMap = DoReallocMap(oldMap, newCapacity, allocator); + RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap)); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator mit = map->find(reinterpret_cast(name.data_)); + if (mit != map->end()) { + return MemberIterator(&members[mit->second]); + } + } + return MemberEnd(); + } + + void DoClearMembers() { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < data_.o.size; i++) { + map->erase(DropMapIterator(mit[i])); + members[i].~Member(); + } + data_.o.size = 0; + } + } + + void DoFreeMembers() { + if (Member* members = GetMembersPointer()) { + GetMap(members)->~Map(); + for (SizeType i = 0; i < data_.o.size; i++) { + members[i].~Member(); + } + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Map** map = &GetMap(members); + Allocator::Free(*map); + Allocator::Free(map); + } + } + } + +#else // !RAPIDJSON_USE_MEMBERSMAP + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return Malloc(allocator, capacity); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* newMembers = Realloc(allocator, GetMembersPointer(), o.capacity, newCapacity); + RAPIDJSON_SETPOINTER(Member, o.members, newMembers); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + + void DoClearMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + void DoFreeMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + } + +#endif // !RAPIDJSON_USE_MEMBERSMAP + + void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + ObjectData& o = data_.o; + if (o.size >= o.capacity) + DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator); + Member* members = GetMembersPointer(); + Member* m = members + o.size; + m->name.RawAssign(name); + m->value.RawAssign(value); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size))); +#endif + ++o.size; + } + + MemberIterator DoRemoveMember(MemberIterator m) { + ObjectData& o = data_.o; + Member* members = GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + SizeType mpos = static_cast(&*m - members); + map->erase(DropMapIterator(mit[mpos])); +#endif + MemberIterator last(members + (o.size - 1)); + if (o.size > 1 && m != last) { +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members])); + mit[mpos]->second = mpos; +#endif + *m = *last; // Move the last one to this place + } + else { + m->~Member(); // Only one left, just destroy + } + --o.size; + return m; + } + + MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) { + ObjectData& o = data_.o; + MemberIterator beg = MemberBegin(), + pos = beg + (first - beg), + end = MemberEnd(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(GetMembersPointer()); + MapIterator* mit = GetMapIterators(map); +#endif + for (MemberIterator itr = pos; itr != last; ++itr) { +#if RAPIDJSON_USE_MEMBERSMAP + map->erase(DropMapIterator(mit[itr - beg])); +#endif + itr->~Member(); + } +#if RAPIDJSON_USE_MEMBERSMAP + if (first != last) { + // Move remaining members/iterators + MemberIterator next = pos + (last - first); + for (MemberIterator itr = pos; next != end; ++itr, ++next) { + std::memcpy(static_cast(&*itr), &*next, sizeof(Member)); + SizeType mpos = static_cast(itr - beg); + new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg])); + mit[mpos]->second = mpos; + } + } +#else + std::memmove(static_cast(&*pos), &*last, + static_cast(end - last) * sizeof(Member)); +#endif + o.size -= static_cast(last - first); + return pos; + } + + template + void DoCopyMembers(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings) { + RAPIDJSON_ASSERT(rhs.GetType() == kObjectType); + + data_.f.flags = kObjectFlag; + SizeType count = rhs.data_.o.size; + Member* lm = DoAllocMembers(count, allocator); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(lm); + MapIterator* mit = GetMapIterators(map); +#endif + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i))); +#endif + } + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = DoAllocMembers(count, allocator); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(m); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < count; i++) { + new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i))); + } +#endif + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType() + // runs last and may access its elements or members which would be freed + // with an allocator like MemoryPoolAllocator (CrtAllocator does not + // free its data when destroyed, but MemoryPoolAllocator does). + if (ownAllocator_) { + ValueType::SetNull(); + } + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + operator ValueType&() const { return value_; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + operator ValueType&() const { return value_; } + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#pragma pop_macro("GetObject") +#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h new file mode 100644 index 0000000..cf046b8 --- /dev/null +++ b/include/rapidjson/encodedstream.h @@ -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. +// +// 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 RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +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 +class EncodedInputStream { + 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) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(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 +class EncodedOutputStream { + 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 { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::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 +class AutoUTFInputStream { + 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) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { 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) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { 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(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(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_) { + int 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) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) 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 +class AutoUTFOutputStream { + 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) { + 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) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { 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 { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h new file mode 100644 index 0000000..50ad18b --- /dev/null +++ b/include/rapidjson/encodings.h @@ -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. +// +// 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 RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +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 + 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 + 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 + 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 + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + 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 +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c; + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_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 + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(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 +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(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 +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(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 +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(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 +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(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 +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..c87b04e --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,176 @@ +// 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. +// +// 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 RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup 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 RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \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 RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); + case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of schema document compilation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param schemaErrorCode Error code obtained from compiling the schema document. + \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 RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document."); + case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer."); + case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string."); + case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'."); + case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document."); + case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical."); + case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); + case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); + case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); + case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); + case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); + case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); + case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } + } + +//! Maps error code of pointer parse into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param pointerParseErrorCode Error code obtained from pointer parse. + \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 RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) { + switch (pointerParseErrorCode) { + case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'."); + case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape."); + case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment."); + case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..cae345d --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,285 @@ +// 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. +// +// 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 RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup 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 RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup 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 RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup 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 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 { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +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_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! 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_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return 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 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 RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values. + kValidateErrorType, //!< Property has a type that is not allowed by the schema. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. + + kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing + kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// SchemaErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum SchemaErrorCode { + kSchemaErrorNone = 0, //!< No error. + + kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document + kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer + kSchemaErrorRefInvalid, //!< $ref must not be an empty string + kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset + kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document + kSchemaErrorRefCyclical, //!< $ref is cyclical + kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider + kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema + kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized + kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported + kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document + kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' +}; + +//! Function pointer type of GetSchemaError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetSchemaError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// PointerParseErrorCode + +//! Error code of JSON pointer parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +//! Function pointer type of GetPointerParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetPointerParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode); + + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h new file mode 100644 index 0000000..f8bb43c --- /dev/null +++ b/include/rapidjson/filereadstream.h @@ -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. +// +// 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 RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +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) { + RAPIDJSON_ASSERT(fp_ != 0); + 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(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::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_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h new file mode 100644 index 0000000..5d89588 --- /dev/null +++ b/include/rapidjson/filewritestream.h @@ -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. +// +// 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 RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for output using fwrite(). +/*! + \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_) { + 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(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { 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); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/fwd.h b/include/rapidjson/fwd.h new file mode 100644 index 0000000..d62f77f --- /dev/null +++ b/include/rapidjson/fwd.h @@ -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. +// +// 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 RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h new file mode 100644 index 0000000..af48738 --- /dev/null +++ b/include/rapidjson/internal/biginteger.h @@ -0,0 +1,297 @@ +// 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. +// +// 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 RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) +#include // for _umul128 +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +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; + } + + template + BigInteger(const Ch* decimals, size_t length) : count_(1) { + 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; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(digits_ + offset, digits_, 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 *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(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); + 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 { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + template + void AppendDecimal64(const Ch* begin, const Ch* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + template + static uint64_t ParseUint64(const Ch* begin, const Ch* end) { + uint64_t r = 0; + for (const Ch* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); + r = r * 10u + static_cast(*p - Ch('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(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(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(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 +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/include/rapidjson/internal/clzll.h b/include/rapidjson/internal/clzll.h new file mode 100644 index 0000000..8fc5118 --- /dev/null +++ b/include/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// 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. +// +// 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 RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..f7d4653 --- /dev/null +++ b/include/rapidjson/internal/diyfp.h @@ -0,0 +1,261 @@ +// 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. +// +// 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 RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +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((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(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(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 { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + 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; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(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 = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = 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[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + 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 + }; + RAPIDJSON_ASSERT(index < 87); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(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(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..cd45672 --- /dev/null +++ b/include/rapidjson/internal/dtoa.h @@ -0,0 +1,249 @@ +// 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. +// +// 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 RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +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 int 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 uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, + 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, + 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int 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('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 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('0' + static_cast(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('0' + static_cast(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(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(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(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) { + 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__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..68c9e96 --- /dev/null +++ b/include/rapidjson/internal/ieee754.h @@ -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. +// +// 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 RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +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 { + 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(((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 int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h new file mode 100644 index 0000000..9fe8c93 --- /dev/null +++ b/include/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// 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. +// +// 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 RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +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) { + RAPIDJSON_ASSERT(buffer != 0); + + 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('0' + static_cast(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) { + RAPIDJSON_ASSERT(buffer != 0); + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + 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(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(value / kTen8); + const uint32_t v1 = static_cast(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]; + + *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(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(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('0' + static_cast(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(value / kTen8); + const uint32_t v1 = static_cast(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) { + RAPIDJSON_ASSERT(buffer != 0); + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h new file mode 100644 index 0000000..27092dc --- /dev/null +++ b/include/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// 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. +// +// 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 RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + 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 struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h new file mode 100644 index 0000000..eae1a43 --- /dev/null +++ b/include/rapidjson/internal/pow10.h @@ -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. +// +// 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 RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +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 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h new file mode 100644 index 0000000..6446c40 --- /dev/null +++ b/include/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// 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. +// +// 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 RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +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_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! 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 +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +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; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 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() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(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(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if 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 + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + 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& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = 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(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = 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(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + 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& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(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(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& 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 + bool ParseRange(DecodedStream& 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('-'); + 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 + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& 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 + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.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(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = 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 = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h new file mode 100644 index 0000000..73abd70 --- /dev/null +++ b/include/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// 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. +// +// 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 RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" +#include + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +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 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 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) 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_); // NOLINT (+clang-analyzer-unix.Malloc) + 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 + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + 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_ = 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(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + 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 +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..b698a8f --- /dev/null +++ b/include/rapidjson/internal/strfunc.h @@ -0,0 +1,83 @@ +// 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. +// +// 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 RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" +#include + +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 +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Custom strcmpn() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s1 Null-terminated input string. + \param s2 Null-terminated input string. + \return 0 if equal +*/ +template +inline int StrCmp(const Ch* s1, const Ch* s2) { + RAPIDJSON_ASSERT(s1 != 0); + RAPIDJSON_ASSERT(s2 != 0); + while(*s1 && (*s1 == *s2)) { s1++; s2++; } + return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream 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 +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h new file mode 100644 index 0000000..55f0e38 --- /dev/null +++ b/include/rapidjson/internal/strtod.h @@ -0,0 +1,293 @@ +// 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. +// +// 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 RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" +#include +#include + +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 +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(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(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 +template +inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) + break; + significand = significand * 10u + static_cast(decimals[i] - Ch('0')); + } + + if (i < dLen && decimals[i] >= Ch('5')) // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // 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 int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + 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(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(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +template +inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + 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(); +} + +template +inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/include/rapidjson/internal/swap.h b/include/rapidjson/internal/swap.h new file mode 100644 index 0000000..2cf92f9 --- /dev/null +++ b/include/rapidjson/internal/swap.h @@ -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. +// +// 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 RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ 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 +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/include/rapidjson/istreamwrapper.h b/include/rapidjson/istreamwrapper.h new file mode 100644 index 0000000..01437ec --- /dev/null +++ b/include/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// 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. +// +// 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 RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +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 +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + 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(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000..ffbc41e --- /dev/null +++ b/include/rapidjson/memorybuffer.h @@ -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. +// +// 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 RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +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 +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack 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(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h new file mode 100644 index 0000000..77af6c9 --- /dev/null +++ b/include/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// 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. +// +// 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 RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +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 RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { 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. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..1811128 --- /dev/null +++ b/include/rapidjson/msinttypes/inttypes.h @@ -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 +#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_ ] diff --git a/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..3d4477b --- /dev/null +++ b/include/rapidjson/msinttypes/stdint.h @@ -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 + +#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 . +// 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 + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap 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 +#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 +#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 . +// 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_ ] diff --git a/include/rapidjson/ostreamwrapper.h b/include/rapidjson/ostreamwrapper.h new file mode 100644 index 0000000..11ed4d3 --- /dev/null +++ b/include/rapidjson/ostreamwrapper.h @@ -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. +// +// 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 RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +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 +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 { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h new file mode 100644 index 0000000..05b1704 --- /dev/null +++ b/include/rapidjson/pointer.h @@ -0,0 +1,1470 @@ +// 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. +// +// 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 RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "uri.h" +#include "internal/itoa.h" +#include "error/error.h" // PointerParseErrorCode + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + typedef GenericUri UriType; + + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, internal::StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = static_cast(buffer[i]); + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Compute URI + //@{ + + //! Compute the in-scope URI for a subtree. + // For use with JSON pointers into JSON schema documents. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param rootUri Root URI + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \param allocator Allocator for Uris + \return Uri if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a URI cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + static const Ch kIdString[] = { 'i', 'd', '\0' }; + static const ValueType kIdValue(kIdString, 2); + UriType base = UriType(rootUri, allocator); + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + // See if we have an id, and if so resolve with the current base + typename ValueType::MemberIterator m = v->FindMember(kIdValue); + if (m != v->MemberEnd() && (m->value).IsString()) { + UriType here = UriType(m->value, allocator).Resolve(base, allocator); + base = here; + } + m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return UriType(allocator); + } + return base; + } + + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); + } + + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h new file mode 100644 index 0000000..fe45df1 --- /dev/null +++ b/include/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// 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. +// +// 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 RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +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 output 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 TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer 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), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! 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) { + 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::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(Base::WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::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) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(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(); + + 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) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + 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(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h new file mode 100644 index 0000000..5ea6947 --- /dev/null +++ b/include/rapidjson/rapidjson.h @@ -0,0 +1,741 @@ +// 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. +// +// 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 RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup 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 overridden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref 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 // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup 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 RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// __cplusplus macro + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#if defined(_MSC_VER) +#define RAPIDJSON_CPLUSPLUS _MSVC_LANG +#else +#define RAPIDJSON_CPLUSPLUS __cplusplus +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup 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(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_USE_MEMBERSMAP + +/*! \def RAPIDJSON_USE_MEMBERSMAP + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for object members handling in a \c std::multimap + + By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object + members are stored in a \c std::multimap for faster lookup and deletion times, a + trade off with a slightly slower insertion time and a small object allocat(or)ed + memory overhead. + + \hideinitializer +*/ +#ifndef RAPIDJSON_USE_MEMBERSMAP +#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup 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 RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond 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 +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref 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 RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN 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 RAPIDJSON_ENDIAN 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 RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// 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 RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup 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 RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup 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 RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +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 RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L) +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#elif defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#elif defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#ifndef RAPIDJSON_NOEXCEPT +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT throw() +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#endif + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#ifndef RAPIDJSON_HAS_CXX17 +#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L) +#endif + +#if RAPIDJSON_HAS_CXX17 +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]] +# elif __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough)) +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +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 +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h new file mode 100644 index 0000000..5554660 --- /dev/null +++ b/include/rapidjson/reader.h @@ -0,0 +1,2246 @@ +// 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. +// +// 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 RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const StackCharacter* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const StackCharacter* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; + + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + GenericStringStream > srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h new file mode 100644 index 0000000..439133f --- /dev/null +++ b/include/rapidjson/schema.h @@ -0,0 +1,3262 @@ +// 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 RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include "uri.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeywordData(const char* keyword) { + printf(" Fail keyword: '%s'\n", keyword); +} + +inline void PrintInvalidKeywordData(const wchar_t* keyword) { + wprintf(L" Fail keyword: '%ls'\n", keyword); +} + +inline void PrintInvalidDocumentData(const char* document) { + printf(" Fail document: '%s'\n", document); +} + +inline void PrintInvalidDocumentData(const wchar_t* document) { + wprintf(L" Fail document: '%ls'\n", document); +} + +inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { + printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { + wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { + printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); +} + +inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { + wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); +} + +inline void PrintMethodData(const char* method) { + printf("%s\n", method); +} + +inline void PrintMethodData(const char* method, bool b) { + printf("%s, Data: '%s'\n", method, b ? "true" : "false"); +} + +inline void PrintMethodData(const char* method, int64_t i) { + printf("%s, Data: '%" PRId64 "'\n", method, i); +} + +inline void PrintMethodData(const char* method, uint64_t u) { + printf("%s, Data: '%" PRIu64 "'\n", method, u); +} + +inline void PrintMethodData(const char* method, double d) { + printf("%s, Data: '%lf'\n", method, d); +} + +inline void PrintMethodData(const char* method, const char* s) { + printf("%s, Data: '%s'\n", method, s); +} + +inline void PrintMethodData(const char* method, const wchar_t* s) { + wprintf(L"%hs, Data: '%ls'\n", method, s); +} + +inline void PrintMethodData(const char* method, const char* s1, const char* s2) { + printf("%s, Data: '%s', '%s'\n", method, s1, s2); +} + +inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { + wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +#ifndef RAPIDJSON_SCHEMA_PRINT +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) +#else +#define RAPIDJSON_SCHEMA_PRINT(name, ...) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +/*! \see + */ +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateReadFlag = 2, //!< Validation is for a read semantic. + kValidateWriteFlag = 4, //!< Validation is for a write semantic. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Specification +enum SchemaDraft { + kDraftUnknown = -1, + kDraftNone = 0, + kDraft03 = 3, + kDraftMin = 4, //!< Current minimum supported draft + kDraft04 = 4, + kDraft05 = 5, + kDraftMax = 5, //!< Current maximum supported draft + kDraft06 = 6, + kDraft07 = 7, + kDraft2019_09 = 8, + kDraft2020_12 = 9 +}; + +enum OpenApiVersion { + kVersionUnknown = -1, + kVersionNone = 0, + kVersionMin = 2, //!< Current minimum supported version + kVersion20 = 2, + kVersion30 = 3, + kVersionMax = 3, //!< Current maximum supported version + kVersion31 = 4, +}; + +struct Specification { + Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} + Specification(OpenApiVersion o) : oapi(o) { + if (oapi == kVersion20) draft = kDraft04; + else if (oapi == kVersion30) draft = kDraft05; + else if (oapi == kVersion31) draft = kDraft2020_12; + else draft = kDraft04; + } + ~Specification() {} + bool IsSupported() const { + return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); + } + SchemaDraft draft; + OpenApiVersion oapi; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; + virtual void Disallowed() = 0; + virtual void DisallowedWhenWriting() = 0; + virtual void DisallowedWhenReading() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : + factory(f), + error_handler(eh), + schema(s), + flags(fl), + valueSchema(), + invalidKeyword(), + invalidCode(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) { + if (validators[i]) { + factory.DestroySchemaValidator(validators[i]); + } + } + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) { + if (patternPropertiesValidators[i]) { + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + } + } + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + const SchemaType* schema; + unsigned flags; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + ValidateErrorCode invalidCode; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + typedef GenericUri UriType; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + id_(id, allocator), + spec_(schemaDocument->GetSpecification()), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0), + readOnly_(false), + writeOnly_(false), + nullable_(false) + { + GenericStringBuffer sb; + p.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); + + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + // PR #1393 + // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite + // recursion (with recursive schemas), since schemaDocument->getSchema() is always + // checked before creating a new one. Don't cache typeless_, though. + if (this != typeless_) { + typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; + SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); + new (entry) SchemaEntry(pointer_, this, true, allocator_); + schemaDocument->AddSchemaRefs(this); + } + + if (!value.IsObject()) + return; + + // If we have an id property, resolve it with the in-scope id + // Not supported for open api 2.0 or 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetIdString())) { + if (v->IsString()) { + UriType local(*v, allocator); + id_ = local.Resolve(id_, allocator); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); + } + } + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) { + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + } + + if (schemaDocument) + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + + // AnyOf, OneOf, Not not supported for open api 2.0 + if (schemaDocument && spec_.oapi != kVersion20) { + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_); + } + } + + // PatternProperties not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + PointerType r = q.Append(itr->name, allocator_); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + // AdditionalItems not supported for openapi 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_)); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + // ReadOnly - open api only (until draft 7 supported) + // WriteOnly - open api 3 only (until draft 7 supported) + // Both can't be true + if (spec_.oapi != kVersionNone) + AssignIfExist(readOnly_, value, GetReadOnlyString()); + if (spec_.oapi >= kVersion30) + AssignIfExist(writeOnly_, value, GetWriteOnlyString()); + if (readOnly_ && writeOnly_) + schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); + + // Nullable - open api 3 only + // If true add 'null' as allowable type + if (spec_.oapi >= kVersion30) { + AssignIfExist(nullable_, value, GetNullableString()); + if (nullable_) + AddType(GetNullString()); + } + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const UriType& GetId() const { + return id_; + } + + const Specification& GetSpecification() const { + return spec_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + bool BeginValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); + // Only check pattern properties if we have validators + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + + // For enums only check if we have a hasher + if (enum_ && context.hasher) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); + foundEnum:; + } + + // Only check allOf etc if we have validators + if (context.validatorCount > 0) { + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + SizeType firstMatch = 0; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else { + oneValid = true; + firstMatch = i - oneOf_.begin; + } + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + } + + return true; + } + + bool Null(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool b) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); + if (!CheckBool(context, b)) + return false; + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); + + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + } + + return true; + } + + bool StartArray(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); + context.arrayElementIndex = 0; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); + context.inArray = false; + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } + + return true; + } + + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + case kValidateErrorReadOnly: return GetReadOnlyString(); + case kValidateErrorWriteOnly: return GetWriteOnlyString(); + + default: return GetNullString(); + } + } + + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') + RAPIDJSON_STRING_(Id, 'i', 'd') + RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') + RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') + RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); + if (!r->IsValid()) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error& e) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + AllocatorType::Free(r); + } + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { + return 0; + } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. + // Also creates a hasher for enums and array uniqueness, if required. + // Also a useful place to add type-independent error checks. + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_); + context.validatorCount = validatorCount_; + + // Always return after first failure for these sub-validators + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_, false); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_, false); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_, false); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); + } + } + + // Add any other type-independent checks here + if (readOnly_ && (context.flags & kValidateWriteFlag)) { + context.error_handler.DisallowedWhenWriting(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); + } + if (writeOnly_ && (context.flags & kValidateReadFlag)) { + context.error_handler.DisallowedWhenReading(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckBool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return true; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + SValue uri_; + UriType id_; + Specification spec_; + PointerType pointer_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; + + SizeType defaultValueLength_; + + bool readOnly_; + bool writeOnly_; + bool nullable_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; + virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { + // Default implementation just calls through for compatibility + // Following line suppresses unused parameter warning + (void)spec; + // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); + return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + typedef GenericValue GValue; + typedef GenericUri UriType; + typedef GenericStringRef StringRefType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + \param pointer An optional JSON pointer to the start of the schema document + \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, + const PointerType& pointer = PointerType(), // PR #1393 + const Specification& spec = Specification(kDraft04)) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize), + spec_(spec), + error_(kObjectType), + currentError_() + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + docId_ = UriType(uri_, allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); + + // Establish the schema draft or open api version. + // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. + SetSchemaSpecification(document); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call HandleRefSchema() if there are $ref. + // PR #1393 use input pointer if supplied + root_ = typeless_; + if (pointer.GetTokenCount() == 0) { + CreateSchemaRecursive(&root_, pointer, document, document, docId_); + } + else if (const ValueType* v = pointer.Get(document)) { + CreateSchema(&root_, pointer, *v, document, docId_); + } + else { + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch))); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)), + docId_(std::move(rhs.docId_)), + spec_(rhs.spec_), + error_(std::move(rhs.error_)), + currentError_(std::move(rhs.currentError_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + // these may contain some allocator data so clear before deleting ownAllocator_ + uri_.SetNull(); + error_.SetNull(); + currentError_.SetNull(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + const GValue& GetURI() const { return uri_; } + + const Specification& GetSpecification() const { return spec_; } + bool IsSupportedSpecification() const { return spec_.IsSupported(); } + + //! Static method to get the specification of any schema document + // Returns kDraftNone if document is silent + static const Specification GetSpecification(const ValueType& document) { + SchemaDraft draft = GetSchemaDraft(document); + if (draft != kDraftNone) + return Specification(draft); + else { + OpenApiVersion oapi = GetOpenApiVersion(document); + if (oapi != kVersionNone) + return Specification(oapi); + } + return Specification(kDraftNone); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + + //! Gets the error object. + GValue& GetError() { return error_; } + const GValue& GetError() const { return error_; } + + static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorStartUnknown: return GetStartUnknownString(); + case kSchemaErrorRefPlainName: return GetRefPlainNameString(); + case kSchemaErrorRefInvalid: return GetRefInvalidString(); + case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString(); + case kSchemaErrorRefUnknown: return GetRefUnknownString(); + case kSchemaErrorRefCyclical: return GetRefCyclicalString(); + case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); + case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); + case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); + case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); + case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); + case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); + case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); + default: return GetNullString(); + } + } + + //! Default error method + void SchemaError(const SchemaErrorCode code, const PointerType& location) { + currentError_ = GValue(kObjectType); + AddCurrentError(code, location); + } + + //! Method for error with single string value insert + void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + AddCurrentError(code, location); + } + + //! Method for error with invalid pointer + void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + currentError_.AddMember(GetOffsetString(), static_cast(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_); + AddCurrentError(code, location); + } + + private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + typedef const PointerType* SchemaRefPtr; // PR #1393 + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void AddErrorInstanceLocation(GValue& result, const PointerType& location) { + GenericStringBuffer sb; + location.StringifyUriFragment(sb); + GValue instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), *allocator_); + result.AddMember(GetInstanceRefString(), instanceRef, *allocator_); + } + + void AddError(GValue& keyword, GValue& error) { + typename GValue::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, *allocator_); + else { + if (member->value.IsObject()) { + GValue errors(kArrayType); + errors.PushBack(member->value, *allocator_); + member->value = errors; + } + member->value.PushBack(error, *allocator_); + } + } + + void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); + currentError_.AddMember(GetErrorCodeString(), code, *allocator_); + AddErrorInstanceLocation(currentError_, location); + AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e') + RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') + RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') + RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') + RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') + RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') + RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + +#undef RAPIDJSON_STRING_ + + // Static method to get schema draft of any schema document + static SchemaDraft GetSchemaDraft(const ValueType& document) { + static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + + if (!document.IsObject()) { + return kDraftNone; + } + + // Get the schema draft from the $schema keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kDraftUnknown; + const UriType draftUri(itr->value); + // Check base uri for match + if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; + if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; + if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; + if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; + if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; + if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; + if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; + return kDraftUnknown; + } + // $schema not found + return kDraftNone; + } + + + // Get open api version of any schema document + static OpenApiVersion GetOpenApiVersion(const ValueType& document) { + static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; + static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level + static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level + static SizeType len = internal::StrLen(kVersion30String); + + if (!document.IsObject()) { + return kVersionNone; + } + + // Get the open api version from the swagger / openapi keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); + if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kVersionUnknown; + const ValueType kVersion20Value(kVersion20String); + if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly + const ValueType kVersion30Value(kVersion30String); + if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x + const ValueType kVersion31Value(kVersion31String); + if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x + return kVersionUnknown; + } + // swagger or openapi not found + return kVersionNone; + } + + // Get the draft of the schema or the open api version (which implies the draft). + // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. + void SetSchemaSpecification(const ValueType& document) { + // Look for '$schema', 'swagger' or 'openapi' keyword at document root + SchemaDraft docDraft = GetSchemaDraft(document); + OpenApiVersion docOapi = GetOpenApiVersion(document); + // Error if both in document + if (docDraft != kDraftNone && docOapi != kVersionNone) + SchemaError(kSchemaErrorSpecIllegal, PointerType()); + // Use document draft or open api version if present or use spec from constructor + if (docDraft != kDraftNone) + spec_ = Specification(docDraft); + else if (docOapi != kVersionNone) + spec_ = Specification(docOapi); + // Error if draft or version unknown + if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) + SchemaError(kSchemaErrorSpecUnknown, PointerType()); + else if (!spec_.IsSupported()) + SchemaError(kSchemaErrorSpecUnsupported, PointerType()); + } + + // Changed by PR #1393 + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + if (v.GetType() == kObjectType) { + UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); + } + + // Changed by PR #1393 + const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + RAPIDJSON_ASSERT(pointer.IsValid()); + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); + if (v.IsObject()) { + if (const SchemaType* sc = GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + } + else if (!HandleRefSchema(pointer, schema, v, document, id)) { + // The new schema constructor adds itself and its $ref(s) to schemaMap_ + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); + if (schema) + *schema = s; + return s->GetId(); + } + } + else { + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + } + return id; + } + + // Changed by PR #1393 + // TODO should this return a UriType& ? + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { + typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); + if (itr == v.MemberEnd()) + return false; + + GenericStringBuffer sb; + source.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); + // Resolve the source pointer to the $ref'ed schema (finally) + new (schemaRef_.template Push()) SchemaRefPtr(&source); + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len == 0) + SchemaError(kSchemaErrorRefInvalid, source); + else { + // First resolve $ref against the in-scope id + UriType scopeId = UriType(id, allocator_); + UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); + // See if the resolved $ref minus the fragment matches a resolved id in this document + // Search from the root. Returns the subschema in the document and its absolute JSON pointer. + PointerType basePointer = PointerType(); + const ValueType *base = FindId(document, ref, basePointer, docId_, false); + if (!base) { + // Remote reference - call the remote document provider + if (!remoteProvider_) + SchemaError(kSchemaErrorRefNoRemoteProvider, source); + else { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, absolute in the remote schema + const PointerType pointer(s, len, allocator_); + if (!pointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer); + else { + // Get the subschema + if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + return true; + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else + // Plain name fragment, not allowed in remote schema + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + } else + SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength()); + } + } + else { // Local reference + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, relative to the resolved URI + const PointerType relPointer(s, len, allocator_); + if (!relPointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer); + else { + // Get the subschema + if (const ValueType *pv = relPointer.Get(*base)) { + // Now get the absolute JSON pointer by adding relative to base + PointerType pointer(basePointer, allocator_); + for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) + pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else { + // Plain name fragment, relative to the resolved URI + // Not supported in open api 2.0 and 3.0 + PointerType pointer(allocator_); + if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + // See if the fragment matches an id in this document. + // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. + else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } + } + } + + // Invalid/Unknown $ref + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + return true; + } + + //! Find the first subschema with a resolved 'id' that matches the specified URI. + // If full specified use all URI else ignore fragment. + // If found, return a pointer to the subschema and its JSON pointer. + // TODO cache pointer <-> id mapping + ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { + SizeType i = 0; + ValueType* resval = 0; + UriType tempuri = UriType(finduri, allocator_); + UriType localuri = UriType(baseuri, allocator_); + if (doc.GetType() == kObjectType) { + // Establish the base URI of this object + typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); + if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { + localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); + } + // See if it matches + if (localuri.Match(finduri, full)) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); + resval = const_cast(&doc); + resptr = here; + return resval; + } + // No match, continue looking + for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); + } + if (resval) break; + } + } else if (doc.GetType() == kArrayType) { + // Continue looking + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); + } + if (resval) break; + i++; + } + } + return resval; + } + + // Added by PR #1393 + void AddSchemaRefs(SchemaType* schema) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); + while (!schemaRef_.Empty()) { + SchemaRefPtr *ref = schemaRef_.template Pop(1); + SchemaEntry *entry = schemaMap_.template Push(); + new (entry) SchemaEntry(**ref, schema, false, allocator_); + } + } + + // Added by PR #1393 + bool IsCyclicRef(const PointerType& pointer) const { + for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) + if (pointer == **ref) + return true; + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved + GValue uri_; // Schema document URI + UriType docId_; + Specification spec_; + GValue error_; + GValue currentError_; +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + //! End of Implementation of ISchemaValidator + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". + const Ch* GetInvalidSchemaKeyword() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString(); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; + } + + //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidDocumentPointer() const { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorOneOf, subvalidators, count); + } + void MultipleOneOf(SizeType index1, SizeType index2) { + ValueType matches(kArrayType); + matches.PushBack(index1, GetStateAllocator()); + matches.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator()); + AddCurrentError(kValidateErrorOneOfMatch); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + void DisallowedWhenWriting() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorReadOnly); + } + void DisallowedWhenReading() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorWriteOnly); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ + valid_ = false;\ + return valid_;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + valid_ = !outputHandler_ || outputHandler_->StartObject(); + return valid_; + } + + bool Key(const Ch* str, SizeType len, bool copy) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + return valid_; + } + + bool EndObject(SizeType memberCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + valid_ = !outputHandler_ || outputHandler_->StartArray(); + return valid_; + } + + bool EndArray(SizeType elementCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), + depth_ + 1, + &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag); + return sv; + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + // End of implementation of ISchemaStateFactory + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, + unsigned depth, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(depth) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + + bool BeginValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + std::memset(va, 0, sizeof(ISchemaValidator*) * count); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + bool valid_; + unsigned flags_; + unsigned depth_; +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/include/rapidjson/stream.h b/include/rapidjson/stream.h new file mode 100644 index 0000000..1fd7091 --- /dev/null +++ b/include/rapidjson/stream.h @@ -0,0 +1,223 @@ +// 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. +// +// 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 RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +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 +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. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +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(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +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(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(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 +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h new file mode 100644 index 0000000..82ad3ca --- /dev/null +++ b/include/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// 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. +// +// 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 RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +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 +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if 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() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h new file mode 100644 index 0000000..f93e508 --- /dev/null +++ b/include/rapidjson/uri.h @@ -0,0 +1,481 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// (C) Copyright IBM Corporation 2021 +// +// 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 RAPIDJSON_URI_H_ +#define RAPIDJSON_URI_H_ + +#include "internal/strfunc.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// GenericUri + +template +class GenericUri { +public: + typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING + typedef std::basic_string String; +#endif + + //! Constructors + GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + } + + GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, len); + } + + GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, internal::StrLen(uri)); + } + + // Use with specializations of GenericValue + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(u)); + } + +#if RAPIDJSON_HAS_STDSTRING + GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri.c_str(), internal::StrLen(uri.c_str())); + } +#endif + + //! Copy constructor + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { + *this = rhs; + } + + //! Copy constructor + GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + *this = rhs; + } + + //! Destructor. + ~GenericUri() { + Free(); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator + GenericUri& operator=(const GenericUri& rhs) { + if (this != &rhs) { + // Do not delete ownAllocator + Free(); + Allocate(rhs.GetStringLength()); + auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); + path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); + query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); + frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); + base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); + uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); + CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); + } + return *this; + } + + //! Getters + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } + +#if RAPIDJSON_HAS_STDSTRING + static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } + static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } + static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } + static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } + static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } + static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } + static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } +#endif + + //! Equality operators + bool operator==(const GenericUri& rhs) const { + return Match(rhs, true); + } + + bool operator!=(const GenericUri& rhs) const { + return !Match(rhs, true); + } + + bool Match(const GenericUri& uri, bool full = true) const { + Ch* s1; + Ch* s2; + if (full) { + s1 = uri_; + s2 = uri.uri_; + } else { + s1 = base_; + s2 = uri.base_; + } + if (s1 == s2) return true; + if (s1 == 0 || s2 == 0) return false; + return internal::StrCmp(s1, s2) == 0; + } + + //! Resolve this URI against another (base) URI in accordance with URI resolution rules. + // See https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // Returns a new GenericUri for the resolved URI. + GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { + GenericUri resuri; + resuri.allocator_ = allocator; + // Ensure enough space for combining paths + resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash + + if (!(GetSchemeStringLength() == 0)) { + // Use all of this URI + resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base scheme + resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); + if (!(GetAuthStringLength() == 0)) { + // Use this auth, path, query + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base auth + resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); + if (GetPathStringLength() == 0) { + // Use the base path + resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); + if (GetQueryStringLength() == 0) { + // Use the base query + resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); + } else { + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } else { + if (path_[0] == '/') { + // Absolute path - use all of this path + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } else { + // Relative path - append this path to base path after base path's last slash + size_t pos = 0; + if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { + resuri.path_[pos] = '/'; + pos++; + } + size_t lastslashpos = baseuri.GetPathStringLength(); + while (lastslashpos > 0) { + if (baseuri.path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); + pos += lastslashpos; + resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } + } + // Always use this frag + resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); + + // Re-constitute base_ and uri_ + resuri.SetBase(); + resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; + resuri.SetUri(); + return resuri; + } + + //! Get the allocator of this GenericUri. + Allocator& GetAllocator() { return *allocator_; } + +private: + // Allocate memory for a URI + // Returns total amount allocated + std::size_t Allocate(std::size_t len) { + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. + // Order: scheme, auth, path, query, frag, base, uri + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + size_t total = (3 * len + 7) * sizeof(Ch); + scheme_ = static_cast(allocator_->Malloc(total)); + *scheme_ = '\0'; + auth_ = scheme_; + auth_++; + *auth_ = '\0'; + path_ = auth_; + path_++; + *path_ = '\0'; + query_ = path_; + query_++; + *query_ = '\0'; + frag_ = query_; + frag_++; + *frag_ = '\0'; + base_ = frag_; + base_++; + *base_ = '\0'; + uri_ = base_; + uri_++; + *uri_ = '\0'; + return total; + } + + // Free memory for a URI + void Free() { + if (scheme_) { + Allocator::Free(scheme_); + scheme_ = 0; + } + } + + // Parse a URI into constituent scheme, authority, path, query, & fragment parts + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const Ch* uri, std::size_t len) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + Allocate(len); + + // Look for scheme ([^:/?#]+):)? + if (start < len) { + while (pos1 < len) { + if (uri[pos1] == ':') break; + pos1++; + } + if (pos1 != len) { + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (pos1 < pos2) { + pos1++; + std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); + scheme_[pos1] = '\0'; + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + auth_ = scheme_ + GetSchemeStringLength(); + auth_++; + *auth_ = '\0'; + if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; + start = pos2; + } + // Look for path ([^?#]*) + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + path_ = auth_ + GetAuthStringLength(); + path_++; + *path_ = '\0'; + if (start < len) { + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); + path_[pos2 - start] = '\0'; + if (path_[0] == '/') + RemoveDotSegments(); // absolute path - normalize + start = pos2; + } + } + // Look for query (\?([^#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + query_ = path_ + GetPathStringLength(); + query_++; + *query_ = '\0'; + if (start < len && uri[start] == '?') { + pos2 = start + 1; + while (pos2 < len) { + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); + query_[pos2 - start] = '\0'; + start = pos2; + } + } + // Look for fragment (#(.*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + frag_ = query_ + GetQueryStringLength(); + frag_++; + *frag_ = '\0'; + if (start < len && uri[start] == '#') { + std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); + frag_[len - start] = '\0'; + } + + // Re-constitute base_ and uri_ + base_ = frag_ + GetFragStringLength() + 1; + SetBase(); + uri_ = base_ + GetBaseStringLength() + 1; + SetUri(); + } + + // Reconstitute base + void SetBase() { + Ch* next = base_; + std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); + next+= GetSchemeStringLength(); + std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); + next+= GetAuthStringLength(); + std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); + next+= GetPathStringLength(); + std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); + next+= GetQueryStringLength(); + *next = '\0'; + } + + // Reconstitute uri + void SetUri() { + Ch* next = uri_; + std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); + next+= GetBaseStringLength(); + std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); + next+= GetFragStringLength(); + *next = '\0'; + } + + // Copy a part from one GenericUri to another + // Return the pointer to the next part to be copied to + Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { + RAPIDJSON_ASSERT(to != 0); + RAPIDJSON_ASSERT(from != 0); + std::memcpy(to, from, len * sizeof(Ch)); + to[len] = '\0'; + Ch* next = to + len + 1; + return next; + } + + // Remove . and .. segments from the path_ member. + // https://tools.ietf.org/html/rfc3986 + // This is done in place as we are only removing segments. + void RemoveDotSegments() { + std::size_t pathlen = GetPathStringLength(); + std::size_t pathpos = 0; // Position in path_ + std::size_t newpos = 0; // Position in new path_ + + // Loop through each segment in original path_ + while (pathpos < pathlen) { + // Get next segment, bounded by '/' or end + size_t slashpos = 0; + while ((pathpos + slashpos) < pathlen) { + if (path_[pathpos + slashpos] == '/') break; + slashpos++; + } + // Check for .. and . segments + if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { + // Backup a .. segment in the new path_ + // We expect to find a previously added slash at the end or nothing + RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); + size_t lastslashpos = newpos; + // Make sure we don't go beyond the start segment + if (lastslashpos > 1) { + // Find the next to last slash and back up to it + lastslashpos--; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Set the new path_ position + newpos = lastslashpos; + } + } else if (slashpos == 1 && path_[pathpos] == '.') { + // Discard . segment, leaves new path_ unchanged + } else { + // Move any other kind of segment to the new path_ + RAPIDJSON_ASSERT(newpos <= pathpos); + std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); + newpos += slashpos; + // Add slash if not at end + if ((pathpos + slashpos) < pathlen) { + path_[newpos] = '/'; + newpos++; + } + } + // Move to next segment + pathpos += slashpos + 1; + } + path_[newpos] = '\0'; + } + + Ch* uri_; // Everything + Ch* base_; // Everything except fragment + Ch* scheme_; // Includes the : + Ch* auth_; // Includes the // + Ch* path_; // Absolute if starts with / + Ch* query_; // Includes the ? + Ch* frag_; // Includes the # + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Uri. +}; + +//! GenericUri for Value (UTF-8, default allocator). +typedef GenericUri Uri; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_URI_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h new file mode 100644 index 0000000..8b38921 --- /dev/null +++ b/include/rapidjson/writer.h @@ -0,0 +1,710 @@ +// 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. +// +// 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 RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& 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. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/lib/libevent/libevent.a b/lib/libevent/libevent.a new file mode 100644 index 0000000000000000000000000000000000000000..42463111822c9d1a0d1aa7e7a1d9c1cc8450c2a3 GIT binary patch literal 666308 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

5*YVHSOilj6*soYPqN*~x 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 )*pxKLIWCpbleCkYjufA5$ft#m9M} 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

M5C@<8SENx%DA=uWH&b>}}#M3Y!ZjjAJ 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 t`Od!Z=jU|iA3J~obyxBi&nuOFmRINUkGDPJ + + addfrindwindow + + + + 0 + 0 + 400 + 300 + + + + addfrindwindow + + + + + + + + account/username/group: + + + + + + + + + + + + + + + + + + + + diff --git a/src/Forms/imwindow.ui b/src/Forms/imwindow.ui new file mode 100644 index 0000000..a8736ce --- /dev/null +++ b/src/Forms/imwindow.ui @@ -0,0 +1,156 @@ + + + IMWindow + + + true + + + + 0 + 0 + 883 + 450 + + + + + 0 + 0 + + + + + 800 + 450 + + + + + 0 + 0 + + + + IM + + + + + + + + + + + + + + + + + + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + QLayout::SetDefaultConstraint + + + + + TextLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + home + + + + + + + + + + + + + + + + + + + + + + send + + + + + + + + + + + + diff --git a/src/Forms/mainwindow.ui b/src/Forms/mainwindow.ui new file mode 100644 index 0000000..362b8ae --- /dev/null +++ b/src/Forms/mainwindow.ui @@ -0,0 +1,173 @@ + + + MainWindow + + + + 0 + 0 + 400 + 225 + + + + + 0 + 0 + + + + + 400 + 225 + + + + + 400 + 225 + + + + + 0 + 0 + + + + IM + + + + + 0 + 0 + + + + + + 20 + 10 + 355 + 210 + + + + + 258 + 114 + + + + ArrowCursor + + + + QLayout::SetDefaultConstraint + + + 0 + + + + + + + + + + + + + + 16777215 + 16777215 + + + + get code + + + + + + + + + + 256 + 17 + + + + account/email/phone + + + + + + + + + go to register + + + + + + + + 89 + 25 + + + + login + + + + + + + + + + 256 + 17 + + + + ArrowCursor + + + password + + + + + + + code + + + + + + + + + + + + + accountInput + passwordInput + codeInput + codeButton + loginOrRegisterButton + sendButton + + + + diff --git a/src/Header/addfrindwindow.h b/src/Header/addfrindwindow.h new file mode 100644 index 0000000..9794e14 --- /dev/null +++ b/src/Header/addfrindwindow.h @@ -0,0 +1,30 @@ +// +// Created by dongl on 23-6-1. +// + +#ifndef IM_ADDFRINDWINDOW_H +#define IM_ADDFRINDWINDOW_H + +#include + + +QT_BEGIN_NAMESPACE +namespace Ui { class addfrindwindow; } +QT_END_NAMESPACE + +class addfrindwindow : public QWidget { +Q_OBJECT + +public: + explicit addfrindwindow(QWidget *parent = nullptr); + ~addfrindwindow() override; + +private slots: + void get_search_input(); + +private: + Ui::addfrindwindow *ui; +}; + + +#endif //IM_ADDFRINDWINDOW_H diff --git a/src/Header/imwindow.h b/src/Header/imwindow.h new file mode 100644 index 0000000..6daee75 --- /dev/null +++ b/src/Header/imwindow.h @@ -0,0 +1,36 @@ +#ifndef IMWINDOW_H +#define IMWINDOW_H + +#include +#include +#include "user/User.h" +#include "user/UserOperation.h" + +namespace Ui { +class IMWindow; +} + +class IMWindow : public QDialog +{ + Q_OBJECT + +public: + explicit IMWindow(QWidget *parent = 0); + ~IMWindow(); + +public: + void setUser(uint64_t account, const std::string& username); + void initFriendList(); + +private slots: + void on_addfriends_clicked(); + void on_friendListView_clicked(const QModelIndex& index); + +private: + Ui::IMWindow *ui; + User user; + QList friendList; + UserOperation* userOperation; +}; + +#endif // IMWINDOW_H diff --git a/src/Header/mainwindow.h b/src/Header/mainwindow.h new file mode 100644 index 0000000..a5a5945 --- /dev/null +++ b/src/Header/mainwindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "imwindow.h" +#include "user/UserOperation.h" +#include "core/Client.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + // 切换 登陆/注册 + void on_loginOrRegisterButton_clicked(); + // 登陆/注册 + void on_sendButton_clicked(); + // 获取验证码 + void on_codeButton_clicked(); + +private: + IMWindow* imui; + Ui::MainWindow *ui; + bool loginOrRegisterButtonState; + + UserOperation* userOperation; +}; + +#endif // MAINWINDOW_H diff --git a/src/Source/addfrindwindow.cpp b/src/Source/addfrindwindow.cpp new file mode 100644 index 0000000..db6ead0 --- /dev/null +++ b/src/Source/addfrindwindow.cpp @@ -0,0 +1,41 @@ +// +// Created by dongl on 23-6-1. +// + +// You may need to build the project (run Qt uic code generator) to get "ui_addfrindwindow.h" resolved + +#include +#include "ui_addfrindwindow.h" +#include "addfrindwindow.h" + +addfrindwindow::addfrindwindow(QWidget *parent) : + QWidget(parent), ui(new Ui::addfrindwindow) { + ui->setupUi(this); + + connect(ui->search, SIGNAL(returnPressed()), this, SLOT(get_search_input())); +} + +addfrindwindow::~addfrindwindow() { + delete ui; +} + +// add 搜索框 +void addfrindwindow::get_search_input() { + // 取搜索框值 + QString search_value = ui->search->text(); + // 开始搜索 暂时只显示 + + QStringList list; + list.append(search_value); + + // 使用数据列表创建数据显示模型 + QStringListModel* model = new QStringListModel(list); + // 加载到 view + ui->listView->setModel(model); + //设置模型到listview上 不可编辑 + ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers); +} + + + + diff --git a/src/Source/imwindow.cpp b/src/Source/imwindow.cpp new file mode 100644 index 0000000..85a79f6 --- /dev/null +++ b/src/Source/imwindow.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include "imwindow.h" +#include "ui_imwindow.h" +#include "ClientExample.h" +#include "document.h" +#include "addfrindwindow.h" + +IMWindow::IMWindow(QWidget *parent) : + QDialog(parent), + userOperation(new UserOperation(ClientExample::run())), + ui(new Ui::IMWindow) +{ + ui->setupUi(this); +} + +IMWindow::~IMWindow() +{ + delete ui; +} + +void IMWindow::setUser(uint64_t account, const std::string &username) { + user.account = account; + user.username = username; + std::string temp = "在线:" + username; + ui->userInfo->setText(temp.c_str()); + + initFriendList(); +} + +void IMWindow::initFriendList() { + // user.account; + // 创建数据显示列表 + auto friends = userOperation->FetchFriends(user.account).value().m_sri.data(); + rapidjson::Document document; + document.Parse(friends.c_str()); + + QStringList username_list; + for (auto mem = document.MemberBegin(); mem != document.MemberEnd(); ++mem) { + auto temp_user = new User(); + temp_user->account = strtol(mem->name.GetString(), nullptr, 0); + temp_user->username = mem->value.GetObject().FindMember("username")->value.GetString(); + friendList.append(temp_user); + username_list.append(temp_user->username.c_str()); + } + + // 使用数据列表创建数据显示模型 + QStringListModel* model = new QStringListModel(username_list); + // 加载到 view + ui->friendListView->setModel(model); + //设置模型到listview上 不可编辑 + ui->friendListView->setEditTriggers(QAbstractItemView::NoEditTriggers); + // //设置双击 +// ui->friendListView->setEditTriggers(QAbstractItemView::DoubleClicked); + + // 设置信号 + //这里的意思是双击listView的某一项后 +// connect(ui->friendListView,SIGNAL(doubleClicked(const QModelIndex &)),this, +// SLOT(on_friendListView_clicked(const QModelIndex &)) ); +} + +// 好友列表按钮信号 +void IMWindow::on_friendListView_clicked(const QModelIndex& model) { + User* temp_user = friendList.at(model.row()); + ui->chatBoxInfo->clear(); + ui->chatBoxInfo->append(("account:" + std::to_string(temp_user->account)).c_str()); + ui->chatBoxInfo->append(("username:" + temp_user->username).c_str()); +} + +void IMWindow::on_addfriends_clicked() { + auto add_win = new addfrindwindow(); + add_win->show(); +} diff --git a/src/Source/mainwindow.cpp b/src/Source/mainwindow.cpp new file mode 100644 index 0000000..624174c --- /dev/null +++ b/src/Source/mainwindow.cpp @@ -0,0 +1,109 @@ +#include +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "ClientExample.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + userOperation(new UserOperation(ClientExample::run())), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + ui->codeLable->hide(); + ui->codeInput->hide(); + ui->codeButton->hide(); + loginOrRegisterButtonState = true; +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +// ui show hide +void MainWindow::on_loginOrRegisterButton_clicked() +{ + static std::string loginAccount; + static std::string loginPassword; + static std::string registerAccount; + static std::string registerPassword; + + if(loginOrRegisterButtonState) { + ui->codeLable->show(); + ui->codeInput->show(); + ui->codeButton->show(); + ui->loginOrRegisterButton->setText("go to login"); + ui->sendButton->setText("register"); + ui->accountLable->setText("email/phone"); + loginAccount = ui->accountInput->text().toStdString(); + loginPassword = ui->passwordInput->text().toStdString(); + ui->accountInput->setText(registerAccount.c_str()); + ui->passwordInput->setText(registerPassword.c_str()); + loginOrRegisterButtonState = false; + } else { + ui->codeLable->hide(); + ui->codeInput->hide(); + ui->codeButton->hide(); + ui->loginOrRegisterButton->setText("go to register"); + ui->sendButton->setText("login"); + ui->accountLable->setText("account/email/phone"); + registerAccount = ui->accountInput->text().toStdString(); + registerPassword = ui->passwordInput->text().toStdString(); + ui->accountInput->setText(loginAccount.c_str()); + ui->passwordInput->setText(loginPassword.c_str()); + loginOrRegisterButtonState = true; + } +} + + +void MainWindow::on_sendButton_clicked() +{ + QString account = ui->accountInput->text(); + QString password = ui->passwordInput->text(); + QString code = ui->codeInput->text(); + // login + if (ui->sendButton->text() == "login") { + auto ret = userOperation->login(account.toStdString(), password.toStdString()); + if (ret.has_value()) { + // login success + if (ret.value().m_sri.subcommand() == mp::MP_LOGIN_SUCCESS) { + imui = new IMWindow; + imui->setWindowFlags(Qt::Window); + imui->setAttribute(Qt::WA_DeleteOnClose); //关闭窗口时释放内存防止内存泄漏 + imui->setUser(ret.value().m_sri.account(), ret.value().m_sri.username()); + printf("account: %lu\n", ret.value().m_sri.account()); + imui->show(); + + this->close(); + } + // login fail + else { + QMessageBox::information(this, "", ret.value().m_sri.msg().c_str()); + } + } else { + QMessageBox::information(this, "", "输入不合法"); + } + } + // register + else { + auto ret = userOperation->register_(account.toStdString(), password.toStdString(), code.toStdString()); + if (ret.has_value()) { + QMessageBox::information(this, "", ret.value().m_sri.msg().c_str()); + } else { + QMessageBox::information(this, "", "输入不合法"); + } + } +} + +void MainWindow::on_codeButton_clicked() { + QString account = ui->accountInput->text(); + + // account 的校验 在get_code中完成 + auto ret = userOperation->get_code(account.toStdString()); + if (ret.has_value()) { + QMessageBox::information(this, "", ret.value().m_sri.msg().c_str()); + } else { + QMessageBox::information(this, "", "输入不合法"); + } +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a2fe004 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,14 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + + // cancel max min + w.setWindowFlags(w.windowFlags()& ~Qt::WindowMaximizeButtonHint& ~Qt::WindowMinimizeButtonHint); + w.show(); + + return a.exec(); +}