From 3af14c8943b9fe6af0f7a6b6b90b3d5d0fed7034 Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Tue, 31 Jan 2023 16:07:08 +0000 Subject: Implement aligned_alloc --- vespamalloc/src/tests/test1/new_test.cpp | 40 ++++++++++++++++++++++ vespamalloc/src/vespamalloc/malloc/malloc.h | 2 +- vespamalloc/src/vespamalloc/malloc/memblock.h | 7 ++-- .../src/vespamalloc/malloc/memblockboundscheck.h | 7 ++-- vespamalloc/src/vespamalloc/malloc/overload.h | 24 ++++++++----- 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/vespamalloc/src/tests/test1/new_test.cpp b/vespamalloc/src/tests/test1/new_test.cpp index dfa67f2aa7c..9ea4418aad5 100644 --- a/vespamalloc/src/tests/test1/new_test.cpp +++ b/vespamalloc/src/tests/test1/new_test.cpp @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include +#include #include #include #include @@ -250,4 +251,43 @@ TEST("test realloc large buffers") { EXPECT_EQUAL(1, mallopt(M_MMAP_THRESHOLD, 1_Gi)); } +void verify_alignment(void * ptr, size_t align, size_t min_sz) { + EXPECT_NOT_EQUAL(ptr, nullptr); + EXPECT_EQUAL(0u, size_t(ptr) & (align-1)); + assert(0ul == (size_t(ptr) & (align-1))); + EXPECT_GREATER_EQUAL(malloc_usable_size(ptr), min_sz); + free(ptr); +} + +TEST("test memalign") { + verify_alignment(memalign(0, 0), 1, 1); + verify_alignment(memalign(0, 1), 1, 1); + verify_alignment(memalign(1, 0), 1, 1); + + for (size_t align : {3,7,19}) { + // According to man pages these should fail, but it seems it rounds up and does best effort + verify_alignment(memalign(align, 73), 1ul << vespalib::Optimized::msbIdx(align), 73); + } + for (size_t align : {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536}) { + verify_alignment(memalign(align, 1), align, 1); + } +} + +TEST("test aligned_alloc") { + verify_alignment(aligned_alloc(0, 0), 1, 1); + verify_alignment(aligned_alloc(0, 1), 1, 1); + verify_alignment(aligned_alloc(1, 0), 1, 1); + for (size_t align : {3,7,19}) { + // According to man pages these should fail, but it seems it rounds up and does best effort + verify_alignment(aligned_alloc(align, align*7), 1ul << vespalib::Optimized::msbIdx(align), align*7); + } + for (size_t align : {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536}) { + verify_alignment(aligned_alloc(align, align*7), align, align*7); + } + for (size_t sz : {31,33,63}) { + // According to man pages these should fail, but it seems it rounds up and does best effort + verify_alignment(aligned_alloc(32, sz), 32, sz); + } +} + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespamalloc/src/vespamalloc/malloc/malloc.h b/vespamalloc/src/vespamalloc/malloc/malloc.h index 0ecdb8c58e1..5867852db31 100644 --- a/vespamalloc/src/vespamalloc/malloc/malloc.h +++ b/vespamalloc/src/vespamalloc/malloc/malloc.h @@ -73,7 +73,7 @@ public: size_t getMinSizeForAlignment(size_t align, size_t sz) const { return MemBlockPtrT::getMinSizeForAlignment(align, sz); } size_t sizeClass(const void *ptr) const { return _segment.sizeClass(ptr); } size_t usable_size(void *ptr) const { - return MemBlockPtrT::usable_size(ptr, _segment.getMaxSize(ptr)); + return MemBlockPtrT::usable_size(ptr, _segment); } void * calloc(size_t nelm, size_t esz) { diff --git a/vespamalloc/src/vespamalloc/malloc/memblock.h b/vespamalloc/src/vespamalloc/malloc/memblock.h index f7b5923ecff..ee090de6f3d 100644 --- a/vespamalloc/src/vespamalloc/malloc/memblock.h +++ b/vespamalloc/src/vespamalloc/malloc/memblock.h @@ -18,7 +18,7 @@ public: SizeClassSpan = (MaxSizeClassMultiAllocC-MinSizeClassC) }; MemBlockT() : _ptr(nullptr) { } - MemBlockT(void * p) : _ptr(p) { } + explicit MemBlockT(void * p) : _ptr(p) { } MemBlockT(void * p, size_t /*sz*/) : _ptr(p) { } MemBlockT(void * p, size_t, bool) : _ptr(p) { } template @@ -34,8 +34,9 @@ public: void setThreadId(uint32_t ) { } void free() { } size_t size() const { return 0; } - static size_t usable_size(void *, size_t classSize) { - return classSize; + template + static size_t usable_size(void * ptr, const T & segment) { + return segment.template getMaxSize(ptr); } bool allocated() const { return false; } uint32_t threadId() const { return 0; } diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h index 9d46dbce378..67b98701d8e 100644 --- a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h +++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h @@ -103,8 +103,11 @@ public: && ((p[3] == ALLOC_MAGIC) || (p[3] == FREE_MAGIC)) && *(reinterpret_cast ((const char*)_ptr + size() + alignment() + StackTraceLen*sizeof(void *))) == TAIL_MAGIC; } - static size_t usable_size(void *ptr, size_t ) { - return MemBlockBoundsCheckBaseT(ptr).size(); + template + static size_t usable_size(void *ptr, const T & segment) { + MemBlockBoundsCheckBaseT mem(ptr); + mem.readjustAlignment(segment); + return mem.size(); } bool validAlloc1() const { unsigned *p((unsigned*)_ptr); diff --git a/vespamalloc/src/vespamalloc/malloc/overload.h b/vespamalloc/src/vespamalloc/malloc/overload.h index 6650f107ca9..e209d1b6df5 100644 --- a/vespamalloc/src/vespamalloc/malloc/overload.h +++ b/vespamalloc/src/vespamalloc/malloc/overload.h @@ -1,8 +1,9 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once +#include "common.h" #include -#include +#include #include #include #include @@ -174,15 +175,22 @@ void * reallocarray(void * ptr, size_t nemb, size_t elemSize) __THROW } void* memalign(size_t align, size_t sz) __THROW __attribute__((visibility ("default"))); -void* memalign(size_t align, size_t sz) __THROW +void* memalign(size_t align_in, size_t sz) __THROW { - void *ptr(nullptr); + size_t align = (align_in == 0) ? 1 : 1ul << vespamalloc::msbIdx(align_in*2 - 1); size_t align_1(align - 1); - if ((align & (align_1)) == 0) { - ptr = vespamalloc::_GmemP->malloc(vespamalloc::_GmemP->getMinSizeForAlignment(align, sz)); - ptr = (void *) ((size_t(ptr) + align_1) & ~align_1); - } - return ptr; + void * ptr = vespamalloc::_GmemP->malloc(vespamalloc::_GmemP->getMinSizeForAlignment(align, sz)); + return (void *) ((size_t(ptr) + align_1) & ~align_1); +} + +void *aligned_alloc (size_t align, size_t sz) __THROW __attribute__((visibility ("default"))); +void *aligned_alloc (size_t align_in, size_t sz_in) __THROW +{ + size_t align = (align_in == 0) ? 1 : 1ul << vespamalloc::msbIdx(align_in*2 - 1); + size_t align_1(align - 1); + size_t sz = ((sz_in - 1) + align) & ~align_1; + void * ptr = vespamalloc::_GmemP->malloc(vespamalloc::_GmemP->getMinSizeForAlignment(align, sz)); + return (void *) ((size_t(ptr) + align_1) & ~align_1); } int posix_memalign(void** ptr, size_t align, size_t sz) __THROW __nonnull((1)) __attribute__((visibility ("default"))); -- cgit v1.2.3 From 0fb36d107e58f52a130826948d7d44d2ed215e0b Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Tue, 31 Jan 2023 16:08:27 +0000 Subject: Always optimize avx implementations. If not compiler is unhappy. --- vespalib/src/vespa/vespalib/hwaccelrated/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vespalib/src/vespa/vespalib/hwaccelrated/CMakeLists.txt b/vespalib/src/vespa/vespalib/hwaccelrated/CMakeLists.txt index d5a3ab429a9..84cd60887b7 100644 --- a/vespalib/src/vespa/vespalib/hwaccelrated/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/hwaccelrated/CMakeLists.txt @@ -13,5 +13,5 @@ vespa_add_library(vespalib_vespalib_hwaccelrated OBJECT ${ACCEL_FILES} DEPENDS ) -set_source_files_properties(avx2.cpp PROPERTIES COMPILE_FLAGS -march=haswell) -set_source_files_properties(avx512.cpp PROPERTIES COMPILE_FLAGS -march=skylake-avx512) +set_source_files_properties(avx2.cpp PROPERTIES COMPILE_FLAGS "-O3 -march=haswell") +set_source_files_properties(avx512.cpp PROPERTIES COMPILE_FLAGS "-O3 -march=skylake-avx512") -- cgit v1.2.3 From f24ab39a9b700e67a7e3b7d36e53604a505eb728 Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Tue, 31 Jan 2023 16:09:15 +0000 Subject: Deinline destructors and copy constructors/operators. --- .../vespa/vespalib/coro/async_crypto_socket.cpp | 16 ++++++++-- .../vespa/vespalib/net/http/json_handler_repo.cpp | 8 +++-- .../vespa/vespalib/net/http/json_handler_repo.h | 15 +++++---- .../src/vespa/vespalib/testkit/test_master.cpp | 36 +++++++++++++--------- vespalib/src/vespa/vespalib/testkit/test_master.h | 8 +++-- vespalib/src/vespa/vespalib/trace/tracenode.cpp | 1 + vespalib/src/vespa/vespalib/trace/tracenode.h | 2 +- vespalib/src/vespa/vespalib/util/jsonstream.cpp | 13 ++++++++ vespalib/src/vespa/vespalib/util/jsonstream.h | 12 ++++---- 9 files changed, 74 insertions(+), 37 deletions(-) diff --git a/vespalib/src/vespa/vespalib/coro/async_crypto_socket.cpp b/vespalib/src/vespa/vespalib/coro/async_crypto_socket.cpp index 4f862b48690..81b53102b4e 100644 --- a/vespalib/src/vespa/vespalib/coro/async_crypto_socket.cpp +++ b/vespalib/src/vespa/vespalib/coro/async_crypto_socket.cpp @@ -24,7 +24,9 @@ struct RawSocket : AsyncCryptoSocket { AsyncIo::SP async; SocketHandle handle; RawSocket(AsyncIo &async_in, SocketHandle handle_in) - : async(async_in.shared_from_this()), handle(std::move(handle_in)) {} + : async(async_in.shared_from_this()), handle(std::move(handle_in)) {} + RawSocket(RawSocket &&) noexcept; + ~RawSocket() override; Lazy read(char *buf, size_t len) override { return async->read(handle, buf, len); } @@ -33,12 +35,16 @@ struct RawSocket : AsyncCryptoSocket { } }; +RawSocket::~RawSocket() = default; + struct SnoopedRawSocket : AsyncCryptoSocket { AsyncIo::SP async; SocketHandle handle; SmartBuffer data; SnoopedRawSocket(AsyncIo &async_in, SocketHandle handle_in) - : async(async_in.shared_from_this()), handle(std::move(handle_in)), data(0) {} + : async(async_in.shared_from_this()), handle(std::move(handle_in)), data(0) {} + SnoopedRawSocket(SnoopedRawSocket &&) noexcept; + ~SnoopedRawSocket() override; void inject_data(const char *buf, size_t len) { if (len > 0) { auto dst = data.reserve(len); @@ -68,6 +74,8 @@ struct SnoopedRawSocket : AsyncCryptoSocket { } }; +SnoopedRawSocket::~SnoopedRawSocket() = default; + struct TlsSocket : AsyncCryptoSocket { AsyncIo::SP async; SocketHandle handle; @@ -78,6 +86,8 @@ struct TlsSocket : AsyncCryptoSocket { TlsSocket(AsyncIo &async_in, SocketHandle handle_in, std::unique_ptr codec_in) : async(async_in.shared_from_this()), handle(std::move(handle_in)), codec(std::move(codec_in)), app_input(0), enc_input(0), enc_output(0) {} + TlsSocket(TlsSocket &&) noexcept; + ~TlsSocket() override; void inject_enc_input(const char *buf, size_t len) { if (len > 0) { auto dst = enc_input.reserve(len); @@ -175,6 +185,8 @@ struct TlsSocket : AsyncCryptoSocket { } }; +TlsSocket::~TlsSocket() = default; + Lazy try_handshake(std::unique_ptr tls_socket) { bool hs_ok = co_await tls_socket->handshake(); if (hs_ok) { diff --git a/vespalib/src/vespa/vespalib/net/http/json_handler_repo.cpp b/vespalib/src/vespa/vespalib/net/http/json_handler_repo.cpp index 07b9306b5dc..3f92cffb4af 100644 --- a/vespalib/src/vespa/vespalib/net/http/json_handler_repo.cpp +++ b/vespalib/src/vespa/vespalib/net/http/json_handler_repo.cpp @@ -46,17 +46,21 @@ JsonHandlerRepo::State::unbind(size_t my_seq) { //----------------------------------------------------------------------------- +JsonHandlerRepo::JsonHandlerRepo() + : _state(std::make_shared()) +{} +JsonHandlerRepo::~JsonHandlerRepo() = default; JsonHandlerRepo::Token::UP JsonHandlerRepo::bind(vespalib::stringref path_prefix, const JsonGetHandler &get_handler) { - return Token::UP(new Unbinder(_state, _state->bind(path_prefix, get_handler))); + return std::make_unique(_state, _state->bind(path_prefix, get_handler)); } JsonHandlerRepo::Token::UP JsonHandlerRepo::add_root_resource(vespalib::stringref path) { - return Token::UP(new Unbinder(_state, _state->add_root_resource(path))); + return std::make_unique(_state, _state->add_root_resource(path)); } std::vector diff --git a/vespalib/src/vespa/vespalib/net/http/json_handler_repo.h b/vespalib/src/vespa/vespalib/net/http/json_handler_repo.h index 4ca3e061c71..46ef3bd762a 100644 --- a/vespalib/src/vespa/vespalib/net/http/json_handler_repo.h +++ b/vespalib/src/vespa/vespalib/net/http/json_handler_repo.h @@ -25,7 +25,7 @@ class JsonHandlerRepo : public JsonGetHandler public: struct Token { using UP = std::unique_ptr; - virtual ~Token() {} + virtual ~Token() = default; }; private: @@ -78,14 +78,13 @@ private: std::shared_ptr _state; public: - JsonHandlerRepo() : _state(std::make_shared()) {} - Token::UP bind(vespalib::stringref path_prefix, - const JsonGetHandler &get_handler); + JsonHandlerRepo(); + ~JsonHandlerRepo(); + Token::UP bind(vespalib::stringref path_prefix, const JsonGetHandler &get_handler); Token::UP add_root_resource(vespalib::stringref path); - std::vector get_root_resources() const; - vespalib::string get(const vespalib::string &host, - const vespalib::string &path, - const std::map ¶ms) const override; + [[nodiscard]] std::vector get_root_resources() const; + [[nodiscard]] vespalib::string get(const vespalib::string &host, const vespalib::string &path, + const std::map ¶ms) const override; }; } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/testkit/test_master.cpp b/vespalib/src/vespa/vespalib/testkit/test_master.cpp index 434d08d6c7a..bacb1051cf3 100644 --- a/vespalib/src/vespa/vespalib/testkit/test_master.cpp +++ b/vespalib/src/vespa/vespalib/testkit/test_master.cpp @@ -31,16 +31,22 @@ TestMaster TestMaster::master; //----------------------------------------------------------------------------- -__thread TestMaster::ThreadState *TestMaster::_threadState = 0; +__thread TestMaster::ThreadState *TestMaster::_threadState = nullptr; //----------------------------------------------------------------------------- - +TestMaster::TraceItem::TraceItem(const std::string &file_in, uint32_t line_in, const std::string &msg_in) + : file(file_in), line(line_in), msg(msg_in) +{} +TestMaster::TraceItem::TraceItem(TraceItem &&) noexcept = default; +TestMaster::TraceItem & TestMaster::TraceItem::operator=(TraceItem &&) noexcept = default; +TestMaster::TraceItem::TraceItem(const TraceItem &) = default; +TestMaster::TraceItem & TestMaster::TraceItem::operator=(const TraceItem &) = default; TestMaster::TraceItem::~TraceItem() = default; TestMaster::ThreadState & TestMaster::threadState(const lock_guard &) { - if (_threadState == 0) { + if (_threadState == nullptr) { std::ostringstream threadName; threadName << "thread-" << _threadStorage.size(); _threadStorage.push_back(std::make_unique(threadName.str())); @@ -52,7 +58,7 @@ TestMaster::threadState(const lock_guard &) TestMaster::ThreadState & TestMaster::threadState() { - if (_threadState != 0) { + if (_threadState != nullptr) { return *_threadState; } { @@ -87,7 +93,7 @@ TestMaster::printDiff(const lock_guard &guard, const std::string &lhs, const std::string &rhs) { ThreadState &thread = threadState(guard); - if (_state.lhsFile == NULL || _state.rhsFile == NULL) { + if (_state.lhsFile == nullptr || _state.rhsFile == nullptr) { fprintf(stderr, "lhs: %s\n" "rhs: %s\n", @@ -123,13 +129,13 @@ TestMaster::handleFailure(const lock_guard &guard, bool fatal) void TestMaster::closeDebugFiles(const lock_guard &) { - if (_state.lhsFile != NULL) { + if (_state.lhsFile != nullptr) { fclose(_state.lhsFile); - _state.lhsFile = NULL; + _state.lhsFile = nullptr; } - if (_state.rhsFile != NULL) { + if (_state.rhsFile != nullptr) { fclose(_state.rhsFile); - _state.rhsFile = NULL; + _state.rhsFile = nullptr; } } @@ -137,8 +143,8 @@ void TestMaster::importThreads(const lock_guard &) { size_t importCnt = 0; - for (size_t i = 0; i < _threadStorage.size(); ++i) { - ThreadState &thread = *_threadStorage[i]; + for (const auto & i : _threadStorage) { + ThreadState &thread = *i; _state.passCnt += thread.passCnt; importCnt += thread.passCnt; thread.passCnt = 0; @@ -245,7 +251,7 @@ void TestMaster::awaitThreadBarrier(const char *file, uint32_t line) { ThreadState &thread = threadState(); - if (thread.barrier == 0) { + if (thread.barrier == nullptr) { return; } if (!thread.barrier->await()) { @@ -275,7 +281,7 @@ TestMaster::Progress TestMaster::getProgress() { lock_guard guard(_lock); - return Progress(_state.passCnt, _state.failCnt); + return {_state.passCnt, _state.failCnt}; } void @@ -286,7 +292,7 @@ TestMaster::openDebugFiles(const std::string &lhsFile, closeDebugFiles(guard); _state.lhsFile = fopen(lhsFile.c_str(), "w"); _state.rhsFile = fopen(rhsFile.c_str(), "w"); - if (_state.lhsFile == NULL || _state.rhsFile == NULL) { + if (_state.lhsFile == nullptr || _state.rhsFile == nullptr) { closeDebugFiles(guard); fprintf(stderr, "%s: Warn: could not open debug files (%s, %s)\n", _name.c_str(), lhsFile.c_str(), rhsFile.c_str()); @@ -299,7 +305,7 @@ TestMaster::openDebugFiles(const std::string &lhsFile, void TestMaster::pushState(const char *file, uint32_t line, const char *msg) { - threadState().traceStack.push_back(TraceItem(skip_path(file), line, msg)); + threadState().traceStack.emplace_back(skip_path(file), line, msg); } void diff --git a/vespalib/src/vespa/vespalib/testkit/test_master.h b/vespalib/src/vespa/vespalib/testkit/test_master.h index 01ea94c074d..b841197f2f9 100644 --- a/vespalib/src/vespa/vespalib/testkit/test_master.h +++ b/vespalib/src/vespa/vespalib/testkit/test_master.h @@ -36,9 +36,11 @@ public: std::string file; uint32_t line; std::string msg; - TraceItem(const std::string &file_in, uint32_t line_in, - const std::string &msg_in) - : file(file_in), line(line_in), msg(msg_in) {} + TraceItem(const std::string &file_in, uint32_t line_in, const std::string &msg_in); + TraceItem(TraceItem &&) noexcept; + TraceItem & operator=(TraceItem &&) noexcept; + TraceItem(const TraceItem &); + TraceItem & operator=(const TraceItem &); ~TraceItem(); }; diff --git a/vespalib/src/vespa/vespalib/trace/tracenode.cpp b/vespalib/src/vespa/vespalib/trace/tracenode.cpp index 881b9cc3300..f05953e62df 100644 --- a/vespalib/src/vespa/vespalib/trace/tracenode.cpp +++ b/vespalib/src/vespa/vespalib/trace/tracenode.cpp @@ -67,6 +67,7 @@ TraceNode::TraceNode(const TraceNode &rhs) } TraceNode::TraceNode(TraceNode &&) noexcept = default; +TraceNode & TraceNode::operator =(TraceNode &&) noexcept = default; TraceNode & TraceNode::operator =(const TraceNode &) = default; TraceNode::~TraceNode() = default; diff --git a/vespalib/src/vespa/vespalib/trace/tracenode.h b/vespalib/src/vespa/vespalib/trace/tracenode.h index 4062dc78d3f..c38c5236e35 100644 --- a/vespalib/src/vespa/vespalib/trace/tracenode.h +++ b/vespalib/src/vespa/vespalib/trace/tracenode.h @@ -51,7 +51,7 @@ public: TraceNode & operator =(const TraceNode &); TraceNode(TraceNode &&) noexcept; - TraceNode & operator =(TraceNode &&) noexcept = default; + TraceNode & operator =(TraceNode &&) noexcept; ~TraceNode(); /** diff --git a/vespalib/src/vespa/vespalib/util/jsonstream.cpp b/vespalib/src/vespa/vespalib/util/jsonstream.cpp index 135611e975f..e8814ee95f6 100644 --- a/vespalib/src/vespa/vespalib/util/jsonstream.cpp +++ b/vespalib/src/vespa/vespalib/util/jsonstream.cpp @@ -8,6 +8,19 @@ namespace vespalib { +JsonStream::StateEntry::StateEntry() noexcept + : state(State::ROOT), object_key(""), array_index(size_t(0)) +{} +JsonStream::StateEntry::StateEntry(State s) noexcept + : state(s), object_key(""), array_index(size_t(0)) +{} +JsonStream::StateEntry::StateEntry(State s, stringref key) noexcept + : state(s), object_key(key), array_index(size_t(0)) +{} +JsonStream::StateEntry::StateEntry(const StateEntry &) noexcept = default; +JsonStream::StateEntry & JsonStream::StateEntry::operator =(const StateEntry &) noexcept = default; +JsonStream::StateEntry::~StateEntry() = default; + const char* JsonStream::getStateName(const State& s) { switch (s) { diff --git a/vespalib/src/vespa/vespalib/util/jsonstream.h b/vespalib/src/vespa/vespalib/util/jsonstream.h index e41c4238b09..b85ad7f5486 100644 --- a/vespalib/src/vespa/vespalib/util/jsonstream.h +++ b/vespalib/src/vespa/vespalib/util/jsonstream.h @@ -44,12 +44,12 @@ class JsonStream : public JsonStreamTypes { string object_key; size_t array_index; - StateEntry() noexcept - : state(State::ROOT), object_key(""), array_index(size_t(0)) {} - StateEntry(State s) - : state(s), object_key(""), array_index(size_t(0)) {} - StateEntry(State s, stringref key) - : state(s), object_key(key), array_index(size_t(0)) {} + StateEntry() noexcept; + StateEntry(State s) noexcept; + StateEntry(State s, stringref key) noexcept; + StateEntry(const StateEntry &) noexcept; + StateEntry & operator =(const StateEntry &) noexcept; + ~StateEntry(); }; std::vector _state; -- cgit v1.2.3