Compare commits

..

No commits in common. "bwi/v2.3.3" and "main" have entirely different histories.

13 changed files with 142 additions and 378 deletions

View File

@ -7,7 +7,7 @@ include(GNUInstallDirs)
set(PROJECT_VERSION_MAJOR 2)
set(PROJECT_VERSION_MINOR 3)
set(PROJECT_VERSION_PATCH 3)
set(PROJECT_VERSION_PATCH 2)
set(RUBLON_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(CMAKE_CXX_STANDARD 17)
@ -15,11 +15,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_POSITION_INDEPENDENT_CODE YES)
include(CheckIPOSupported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
add_compile_options(-Wall -Wextra -Wno-format-security -fvisibility=hidden -fno-exceptions)
add_compile_options(-Wall -Wextra -Wno-format-security)
add_compile_options(-fpermissive)
# add_compile_options(-g -fsanitize=address,undefined,float-divide-by-zero,float-cast-overflow,null -fsanitize-address-use-after-scope -fno-sanitize-recover=all -fno-sanitize=alignment -fno-omit-frame-pointer)
# add_link_options(-g -fsanitize=address,undefined,float-divide-by-zero,float-cast-overflow,null -fsanitize-address-use-after-scope -fno-sanitize-recover=all -fno-sanitize=alignment -fno-omit-frame-pointer)

View File

@ -8,7 +8,6 @@ set(INC
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/core_handler.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/core_handler_interface.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/curl.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/ws_http.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/error_handler.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/error.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/finish.hpp
@ -69,7 +68,6 @@ set(LWS_WITH_SHARED OFF)
set(LWS_ROLE_RAW_FILE OFF)
set(LWS_ROLE_DBUS OFF)
set(LWS_WITHOUT_DAEMONIZE ON)
set(LWS_WITHOUT_SERVER ON)
set(LWS_WITHOUT_TESTAPPS ON)
@ -79,16 +77,12 @@ set(LWS_WITHOUT_TEST_SERVER ON)
set(LWS_WITHOUT_TEST_SERVER_EXTPOLL ON)
set(LWS_UNIX_SOCK OFF)
set(LWS_WITH_DIR OFF)
set(LWS_WITH_FILE_OPS OFF)
set(LWS_FOR_GITOHASHI OFF)
set(LWS_WITH_HTTP2 OFF)
set(LWS_WITH_HTTP_BASIC_AUTH OFF)
set(LWS_WITH_HTTP_DIGEST_AUTH OFF)
set(LWS_WITH_HTTP_UNCOMMON_HEADERS OFF)
set(LWS_WITH_SYS_STATE OFF)
set(LWS_WITH_SYS_SMD OFF)
set(LWS_WITH_JPEG OFF)
set(LWS_WITH_JSONRPC OFF)
set(LWS_WITH_LEJP OFF)
@ -105,14 +99,11 @@ set(LWS_WITH_UDP OFF)
set(LWS_WITH_HTTP_STREAM_COMPRESSION OFF)
set(LWS_WITH_HTTP_BROTLI OFF)
set(LWS_WITH_ZLIB OFF)
set(LWS_WITH_SEQUENCER OFF)
set(LWS_WITH_LWSAC OFF)
set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_CXX17 ON CACHE BOOL "" FORCE)
set(RAPIDJSON_HAS_STDSTRING OFF)
set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_CXX17 ON CACHE BOOL "" FORCE)
cmake_policy(SET CMP0077 NEW)
@ -122,15 +113,23 @@ endif()
FetchContent_Declare(
libwebsockets
URL https://github.com/warmcat/libwebsockets/archive/refs/tags/v4.3.6.zip
URL_HASH MD5=1b57fde90182c8fea21a9f96df0b53eb
URL https://github.com/warmcat/libwebsockets/archive/refs/tags/v4.3.3.zip
URL_HASH MD5=96ad80a3283825a9af6a2d9d1acbd132
)
FetchContent_MakeAvailable(libwebsockets)
set(RAPIDJSON_BUILD_DOC OFF)
set(RAPIDJSON_BUILD_EXAMPLES OFF)
set(RAPIDJSON_BUILD_TESTS OFF)
set(RAPIDJSON_HAS_STDSTRING OFF)
FetchContent_Declare(
RapidJSON
URL https://github.com/jcelerier/rapidjson/archive/refs/tags/v1.2.1.zip
URL_HASH MD5=54d53169cbd2c290076677627779636d
URL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.zip
URL_HASH MD5=ceb1cf16e693a3170c173dc040a9d2bd
PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_LIST_DIR}/patches/rapidjson.patch
)
if(NOT RapidJSON_POPULATED)

View File

@ -6,6 +6,7 @@ target_link_libraries(rublon_application
PUBLIC
rublon-ssh-ifc
websockets
-lcurl
-lssl
-lcrypto
)

View File

@ -70,14 +70,14 @@ int main(int argc, const char ** argv) {
return printAuthMessageAndExit(rublon::ErrorHandler{pam, session.config()}.printErrorDetails(error));
};
// {
// CheckApplication ca{session};
// auto ret = ca.call(CH, session.config().systemToken).or_else(mapError);
// if(not ret.has_value()) {
// log(LogLevel::Error, "Check Application step failed, check configration");
// return PAM_MAXTRIES;
// }
// }
{
CheckApplication ca{session};
auto ret = ca.call(CH, session.config().systemToken).or_else(mapError);
if(not ret.has_value()) {
log(LogLevel::Error, "Check Application step failed, check configration");
return PAM_MAXTRIES;
}
}
auto ret = Init{session}
.handle(CH, pam) //

View File

@ -1,9 +1,7 @@
#pragma once
#include <rublon/core_handler.hpp>
#include <rublon/curl.hpp>
#include <rublon/ws_http.hpp>
#ifndef RUBLON_USE_STDOUT
#include <rublon/pam.hpp>
@ -20,5 +18,5 @@ using Pam_t = PamStub;
#endif
namespace rublon {
using CoreHandler_t = CoreHandler< LWS >;
using CoreHandler_t = CoreHandler< CURL >;
} // namespace rublon

View File

@ -13,7 +13,6 @@
namespace rublon {
template < typename HttpHandler = CURL >
class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
std::reference_wrapper< const Configuration > _config;
@ -164,9 +163,9 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
request.headers["Accept"] = "application/json";
stringifyTo(body, request.body);
signRequest(request);
std::pmr::string uri{&memoryResource};
uri.reserve(conservative_estimate(config().apiServer, path.size()));
uri += config().apiServer;
uri += path.data();

View File

@ -18,6 +18,38 @@ namespace {
}
} // namespace
struct Request {
std::pmr::memory_resource * _mr;
std::pmr::map< std::pmr::string, std::pmr::string > headers;
std::pmr::string body;
public:
Request(std::pmr::memory_resource * mr) : _mr{mr}, headers{_mr}, body{_mr} {};
Request(const Request & res) = delete;
Request & operator=(const Request & res) = delete;
Request(Request && res) = delete;
Request & operator=(Request &&) = delete;
};
struct Response {
std::pmr::memory_resource * _mr;
std::pmr::map< std::pmr::string, std::pmr::string, ci_less > headers;
std::pmr::string body;
public:
Response(std::pmr::memory_resource * mr) : _mr{mr}, headers{_mr}, body{_mr} {};
Response(const Response & res) = delete;
Response & operator=(const Response & res) = delete;
Response(Response && res) noexcept = default;
Response & operator=(Response && res) noexcept = default;
};
class CURL {
std::unique_ptr< ::CURL, void (*)(::CURL *) > curl;
std::reference_wrapper< const Configuration > _config;

View File

@ -77,17 +77,17 @@ class StaticString : public details::StaticStringBase {
return N - 1;
}
// StaticString< N > & operator+=(const char * rhs) {
// auto remaining = capacity() - _size;
// auto rhs_len = std::strlen(rhs);
// auto copy_len = std::min(rhs_len, remaining);
StaticString< N > & operator+=(const char * rhs) {
auto remaining = capacity() - _size;
auto rhs_len = std::strlen(rhs);
auto copy_len = std::min(rhs_len, remaining);
// std::strncpy(data() + _size, rhs, copy_len);
// _size += copy_len;
// data()[_size] = '\0'; // null
std::strncpy(data() + _size, rhs, copy_len);
_size += copy_len;
data()[_size] = '\0'; // null
// return *this;
// }
return *this;
}
template < std::size_t M >
friend class StaticString;

View File

@ -21,7 +21,6 @@
#include <security/pam_modules.h>
namespace rublon {
inline bool fileGood(const std::filesystem::path & path) {
std::ifstream file(path);
return file.good();
@ -291,39 +290,6 @@ struct ci_less {
}
};
struct Request {
std::pmr::memory_resource * _mr;
std::pmr::map< std::pmr::string, std::pmr::string > headers;
std::pmr::string body;
public:
Request(std::pmr::memory_resource * mr) : _mr{mr}, headers{_mr}, body{_mr} {};
Request(const Request & res) = delete;
Request & operator=(const Request & res) = delete;
Request(Request && res) = delete;
Request & operator=(Request &&) = delete;
};
struct Response {
std::pmr::memory_resource * _mr;
std::pmr::map< std::pmr::string, std::pmr::string, ci_less > headers;
std::pmr::string body;
public:
Response(std::pmr::memory_resource * mr) : _mr{mr}, headers{_mr}, body{_mr} {};
Response(const Response & res) = delete;
Response & operator=(const Response & res) = delete;
Response(Response && res) noexcept = default;
Response & operator=(Response && res) noexcept = default;
};
template < typename Out, typename... Types >
constexpr std::array< Out, sizeof...(Types) > make_array(Types... names) {
return {std::forward< Types >(names)...};

View File

@ -24,7 +24,7 @@ enum TransactionConfirmationStatus { transactionConfirmed, transactionDenied };
struct RublonEventData {
TransactionConfirmationStatus status;
StaticString< 32 > transactionID; /// TODO: tid verification?
StaticString< 32 > transactionID; /// TODO tid verification?
std::optional< StaticString< 60 > > accessToken;
};
@ -127,9 +127,7 @@ class WebSocket {
}
bool attachToTransactionConfirmationChannel(std::string_view transaction_id) {
memory::MonotonicStackResource<128> mr;
std::pmr::string subscribe_message{&mr};
subscribe_message.reserve(128);
StaticString< 128 > subscribe_message{};
unsigned char buf[128 + LWS_PRE] = {};
subscribe_message += R"msg(42["subscribe",{"channel":"transactionConfirmation.)msg";
@ -302,6 +300,5 @@ class WebSocket {
const struct lws_protocols WebSocket::protocols[] = { //
{"wss", WebSocket::callback_ws, 1024, 1024, 0, NULL, 0},
{nullptr, nullptr, 0, 0, 0, nullptr, 0}
};
{nullptr, nullptr, 0, 0, 0, nullptr, 0}};
} // namespace rublon

View File

@ -1,285 +0,0 @@
#pragma once
#include <cstring>
#include <rublon/configuration.hpp>
#include <rublon/error.hpp>
#include <rublon/utils.hpp>
#include <tl/expected.hpp>
#include <libwebsockets.h>
#include <string>
namespace rublon {
// Struktura do obsługi połączenia WebSocket
class LWS {
std::reference_wrapper< const Configuration > _config;
const Configuration & conf() const noexcept {
return _config.get();
}
struct session_data {
const Request & request;
Response & response;
int status{};
bool done{false};
};
static int callback(struct lws * wsi, enum lws_callback_reasons reason, void * user, void * in, size_t len) {
auto * data = reinterpret_cast< session_data * >(user);
int n;
switch(reason) {
/* because we are protocols[0] ... */
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
log(LogLevel::Error, "CLIENT_CONNECTION_ERROR: %s\n", in ? ( char * ) in : "(null)");
if(data)
data->done = true;
lws_cancel_service(lws_get_context(wsi));
return 0;
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
if(data)
data->done = true;
lws_cancel_service(lws_get_context(wsi));
return 0;
/* ...callbacks related to receiving the result... */
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
data->status = lws_http_client_http_response(wsi);
log(LogLevel::Info, "Connected with server response: %d\n", data->status);
char tmp[512];
auto copy_header = [&](const auto name) {
if (lws_hdr_custom_length(wsi, name.data(), name.size()) > 0 &&
lws_hdr_custom_copy(wsi, tmp, sizeof(tmp), name.data(), name.size()) > 0) {
data->response.headers[{name.data(), name.size()-1}] = tmp;
}
};
using namespace std::string_view_literals;
copy_header("x-ratelimit-limit:"sv);
copy_header("x-ratelimit-remaining:"sv);
copy_header("x-rublon-signature:"sv);
break;
}
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
log(LogLevel::Info, "RECEIVE_CLIENT_HTTP_READ: read %d\n", ( int ) len);
if(data && in && len) {
data->response.body.append(static_cast< const char * >(in), len);
}
return 0; /* don't passthru */
}
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: {
// Drain the socket; lws will subsequently trigger READ callbacks with 'in/len'
char buf[LWS_PRE + 2048];
char * p = buf + LWS_PRE;
int n = ( int ) sizeof(buf) - LWS_PRE;
if(lws_http_client_read(wsi, &p, &n) < 0)
return -1;
return 0; /* don't passthru */
}
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
log(LogLevel::Info, "LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
if(data)
data->done = true;
lws_cancel_service(lws_get_context(wsi));
return 0;
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: {
if(!lws_http_is_redirected_to_get(wsi)) {
unsigned char ** pp = ( unsigned char ** ) in;
unsigned char * end = (*pp) + len;
// Add user headers (ensure names include ':' e.g. "Content-Type:")
for(const auto & [name, value] : data->request.headers) {
if(lws_add_http_header_by_name(wsi,
( const unsigned char * ) name.c_str(),
( const unsigned char * ) value.c_str(),
( int ) value.size(),
pp,
end)) {
log(LogLevel::Error, "Header add error");
return -1;
}
}
// Ensure Content-Length if sending a body
if(!data->request.body.empty()) {
std::string cl = std::to_string(data->request.body.size());
if(lws_add_http_header_by_name(wsi,
( const unsigned char * ) "Content-Length:",
( const unsigned char * ) cl.c_str(),
( int ) cl.size(),
pp,
end)) {
log(LogLevel::Error, "Header Content-Length error");
return -1;
}
}
// Tell lws the body will follow
if(!data->request.body.empty())
lws_client_http_body_pending(wsi, 1);
lws_callback_on_writable(wsi);
}
return 0;
}
case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
// according to doc, there is no need for LWS_PRE when using HTTP_WRITE
n = lws_write(wsi, ( unsigned char * ) data->request.body.c_str(), data->request.body.size(), LWS_WRITE_HTTP_FINAL);
if(n < 0)
return -1;
/* we only had one thing to send, so inform lws we are donefi
* if we had more to send, call lws_callback_on_writable(wsi);
* and just return 0 from callback. On having sent the last
* part, call the below api instead.*/
lws_client_http_body_pending(wsi, 0);
return 0;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
};
static inline lws_protocols protocols[] = {{
"http-client", // const char *name
LWS::callback, // lws_callback_function *
0, // size_t per_session_data_size
0, // size_t rx_buffer_size
0, // unsigned int id
0, // void *user
0 // size_t tx_packet_size
},
{nullptr, nullptr, 0, 0, 0, nullptr, 0}};
public:
lws_context_creation_info info{};
lws_context * _context{nullptr};
LWS(const Configuration & config) : _config{config} {
std::memset(&info, 0, sizeof(info));
info.port = CONTEXT_PORT_NO_LISTEN;
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.protocols = protocols;
_context = lws_create_context(&info);
assert(_context != nullptr);
auto lws_log_emit = [](int level, const char * line) {
LogLevel rlevel{LLL_DEBUG};
if(level == LLL_ERR)
rlevel = LogLevel::Error;
if(level == LLL_WARN)
rlevel = LogLevel::Warning;
if(level == LLL_INFO)
rlevel = LogLevel::Info;
if(level == LLL_NOTICE)
rlevel = LogLevel::Debug;
if(level == LLL_DEBUG)
rlevel = LogLevel::Debug;
log(rlevel, "libwesockets: %s", line);
};
// if(_config.get().logging) {
lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | LLL_USER | LLL_HEADER | LLL_CLIENT, lws_log_emit);
// } else {
// lws_set_log_level(LLL_ERR | LLL_WARN, lws_log_emit);
// }
}
LWS(const LWS &) = delete;
LWS(LWS &&) = delete;
LWS & operator=(const LWS &) = delete;
LWS & operator=(LWS &&) = delete;
tl::expected< std::reference_wrapper< Response >, ConnectionError >
request(std::string_view uri, const Request & request, Response & response) const {
using namespace memory::literals;
memory::Monotonic_8k_Resource memoryResource;
// --- Parse URL ---
std::pmr::string protocol = "https";
std::pmr::string host{&memoryResource};
std::pmr::string path{"/", &memoryResource};
std::string_view sv_uri(uri);
if(auto pos = sv_uri.find("://"); pos != std::string_view::npos) {
protocol = std::string(sv_uri.substr(0, pos));
sv_uri.remove_prefix(pos + 3);
}
auto slash_pos = sv_uri.find('/');
if(slash_pos != std::string_view::npos) {
host = std::pmr::string(sv_uri.substr(0, slash_pos), &memoryResource);
path = std::pmr::string(sv_uri.substr(slash_pos), &memoryResource);
} else {
host = std::pmr::string(sv_uri, &memoryResource);
}
const int use_ssl = (protocol == "https") ? LCCSCF_USE_SSL : 0;
// --- Callback funkcji obsługującej klienta ---
session_data client_data{request, response};
lws_client_connect_info ccinfo = {};
ccinfo.alpn = "http/1.1";
ccinfo.context = _context;
ccinfo.address = host.c_str();
ccinfo.port = (protocol == "https") ? 443 : 80;
ccinfo.path = path.c_str();
ccinfo.ssl_connection = use_ssl;
ccinfo.host = host.c_str();
ccinfo.origin = host.c_str();
ccinfo.method = "POST";
ccinfo.protocol = protocols[0].name;
ccinfo.userdata = &client_data;
ccinfo.ietf_version_or_minus_one = -1;
ccinfo.pwsi = nullptr;
struct lws * wsi = lws_client_connect_via_info(&ccinfo);
if(!wsi) {
lws_context_destroy(_context);
return tl::unexpected{ConnectionError{ConnectionError::Timeout, 0}};
}
// --- Pętla serwisu ---
while(lws_service(_context, 1000) >= 0) {
if(!response.body.empty())
break; // odebrano dane
}
const auto http_code = client_data.status;
if(http_code >= 500) {
log(LogLevel::Error, "%s response with code %d ", "CURL", http_code);
return tl::unexpected{ConnectionError{ConnectionError::HttpError, http_code}};
}
for(const auto & [name, value] : response.headers) {
log(LogLevel::Debug, "Header %s:%s", name.c_str(), value.c_str());
}
log(LogLevel::Debug, "Body %s", response.body.c_str());
return response;
}
~LWS() {
if(_context) {
lws_context_destroy(_context);
_context = nullptr;
}
}
};
} // namespace rublon

View File

@ -26,6 +26,7 @@ target_link_libraries(rublon-ssh
PUBLIC
rublon-ssh-ifc
websockets
-lcurl
-lssl
-lcrypto
)
@ -49,13 +50,8 @@ elseif(${outOS} MATCHES "opensuse-leap" )
message(INFO " detected suse base system")
set(_destination /lib64/security/)
elseif(${outOS} MATCHES "debian|ubuntu")
if(TARGET_ARCH STREQUAL "ARM64")
message(INFO " detected debian arm64 based system")
set(_destination /usr/lib/security/)
else()
message(INFO " detected debian based system")
set(_destination /usr/lib/x86_64-linux-gnu/security/)
endif()
message(INFO " detected debian based system")
set(_destination /usr/lib/x86_64-linux-gnu/security/)
endif()
install(

View File

@ -0,0 +1,64 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ceda71b..0128f99 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,7 @@ endif()
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules)
+cmake_policy(SET CMP0048 NEW)
PROJECT(RapidJSON CXX)
set(LIB_MAJOR_VERSION "1")
diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h
index e3e20dfb..592c5678 100644
--- a/include/rapidjson/document.h
+++ b/include/rapidjson/document.h
@@ -97,17 +97,20 @@ struct GenericMember {
\see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
*/
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
template <bool Const, typename Encoding, typename Allocator>
class GenericMemberIterator
: public std::iterator<std::random_access_iterator_tag
, typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
-
+
friend class GenericValue<Encoding,Allocator>;
template <bool, typename, typename> friend class GenericMemberIterator;
typedef GenericMember<Encoding,Allocator> PlainType;
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
+#pragma GCC diagnostic pop
public:
//! Iterator type itself
@@ -1936,7 +1939,10 @@ private:
if (count) {
GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
SetElementsPointer(e);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wclass-memaccess"
std::memcpy(e, values, count * sizeof(GenericValue));
+#pragma GCC diagnostic pop
}
else
SetElementsPointer(0);
@@ -1949,7 +1955,10 @@ private:
if (count) {
Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
SetMembersPointer(m);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wclass-memaccess"
std::memcpy(m, members, count * sizeof(Member));
+#pragma GCC diagnostic pop
}
else
SetMembersPointer(0);
diff --git a/package.json b/package.json
index 843463d7..cc6087a5 100644
Binary files a/package.json and b/package.json differ