Added custom allocator for subsonic responses
This commit is contained in:
@@ -58,3 +58,6 @@ target_link_libraries(lmssubsonic PUBLIC
|
|||||||
|
|
||||||
install(TARGETS lmssubsonic DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS lmssubsonic DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
|
|
||||||
|
if (BUILD_BENCHMARKS)
|
||||||
|
add_subdirectory(bench)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
add_executable(bench-subsonic
|
||||||
|
SubsonicBench.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(bench-subsonic PRIVATE
|
||||||
|
../impl
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(bench-subsonic PRIVATE
|
||||||
|
lmscore
|
||||||
|
lmsdatabase
|
||||||
|
lmssubsonic
|
||||||
|
benchmark
|
||||||
|
)
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
#include "SubsonicResponse.hpp"
|
||||||
|
|
||||||
|
namespace lms::api::subsonic::benchs
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
Response generateFakeResponse()
|
||||||
|
{
|
||||||
|
Response response{ Response::createOkResponse(defaultServerProtocolVersion) };
|
||||||
|
|
||||||
|
Response::Node& node{ response.createNode("MyNode") };
|
||||||
|
node.setAttribute("Attr1", "value1");
|
||||||
|
node.setAttribute("Attr2", "value2");
|
||||||
|
|
||||||
|
for (std::size_t i{}; i < 100; ++i)
|
||||||
|
{
|
||||||
|
Response::Node& childNode{ node.createArrayChild("MyArrayChild") };
|
||||||
|
childNode.setAttribute("Attr42", i);
|
||||||
|
|
||||||
|
node.addArrayValue("MyArray1", "value1");
|
||||||
|
node.addArrayValue("MyArray1", "value2");
|
||||||
|
for (std::size_t j{}; j < i; ++j)
|
||||||
|
node.addArrayValue("MyArray2", j);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_SubsonicResponse_generate(benchmark::State& state)
|
||||||
|
{
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
Response response{ generateFakeResponse() };
|
||||||
|
benchmark::DoNotOptimize(response);
|
||||||
|
TLSMonotonicMemoryResource::getInstance().reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <ResponseFormat responseFormat>
|
||||||
|
static void BM_SubsonicResponse_serialize(benchmark::State& state)
|
||||||
|
{
|
||||||
|
const Response response{ generateFakeResponse() };
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
std::ostringstream oss; // TODO find something more optimized
|
||||||
|
response.write(oss, responseFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(BM_SubsonicResponse_generate)->Threads(1)->Threads(std::thread::hardware_concurrency());
|
||||||
|
BENCHMARK(BM_SubsonicResponse_serialize<ResponseFormat::json>);
|
||||||
|
BENCHMARK(BM_SubsonicResponse_serialize<ResponseFormat::xml>);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
@@ -159,7 +159,7 @@ namespace lms::api::subsonic
|
|||||||
CheckImplementedFunc checkFunc{};
|
CheckImplementedFunc checkFunc{};
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::unordered_map<core::LiteralString, RequestEntryPointInfo, core::LiteralStringHash, core::LiteralStringEqual> requestEntryPoints
|
const std::unordered_map<core::LiteralString, RequestEntryPointInfo, core::LiteralStringHash, core::LiteralStringEqual> requestEntryPoints
|
||||||
{
|
{
|
||||||
// System
|
// System
|
||||||
{"/ping", {handlePingRequest}},
|
{"/ping", {handlePingRequest}},
|
||||||
@@ -266,13 +266,26 @@ namespace lms::api::subsonic
|
|||||||
};
|
};
|
||||||
|
|
||||||
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>;
|
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>;
|
||||||
static std::unordered_map<core::LiteralString, MediaRetrievalHandlerFunc, core::LiteralStringHash, core::LiteralStringEqual> mediaRetrievalHandlers
|
const std::unordered_map<core::LiteralString, MediaRetrievalHandlerFunc, core::LiteralStringHash, core::LiteralStringEqual> mediaRetrievalHandlers
|
||||||
{
|
{
|
||||||
// Media retrieval
|
// Media retrieval
|
||||||
{"/download", handleDownload},
|
{"/download", handleDownload},
|
||||||
{"/stream", handleStream},
|
{"/stream", handleStream},
|
||||||
{"/getCoverArt", handleGetCoverArt},
|
{"/getCoverArt", handleGetCoverArt},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TLSMonotonicMemoryResourceCleaner
|
||||||
|
{
|
||||||
|
TLSMonotonicMemoryResourceCleaner() = default;
|
||||||
|
~TLSMonotonicMemoryResourceCleaner()
|
||||||
|
{
|
||||||
|
TLSMonotonicMemoryResource::getInstance().reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TLSMonotonicMemoryResourceCleaner(const TLSMonotonicMemoryResourceCleaner&) = delete;
|
||||||
|
TLSMonotonicMemoryResourceCleaner& operator=(const TLSMonotonicMemoryResourceCleaner&) = delete;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
SubsonicResource::SubsonicResource(db::Db& db)
|
SubsonicResource::SubsonicResource(db::Db& db)
|
||||||
@@ -287,7 +300,8 @@ namespace lms::api::subsonic
|
|||||||
{
|
{
|
||||||
static std::atomic<std::size_t> curRequestId{};
|
static std::atomic<std::size_t> curRequestId{};
|
||||||
|
|
||||||
const std::size_t requestId{ curRequestId++ };
|
const std::size_t requestId{ curRequestId++ };
|
||||||
|
TLSMonotonicMemoryResourceCleaner memoryResourceCleaner;
|
||||||
|
|
||||||
LMS_LOG(API_SUBSONIC, DEBUG, "Handling request " << requestId << " '" << request.pathInfo() << "', continuation = " << (request.continuation() ? "true" : "false") << ", params = " << parameterMapToDebugString(request.getParameterMap()));
|
LMS_LOG(API_SUBSONIC, DEBUG, "Handling request " << requestId << " '" << request.pathInfo() << "', continuation = " << (request.continuation() ? "true" : "false") << ", params = " << parameterMapToDebugString(request.getParameterMap()));
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace lms::api::subsonic
|
|||||||
void Response::Node::setValue(std::string_view value)
|
void Response::Node::setValue(std::string_view value)
|
||||||
{
|
{
|
||||||
assert(_children.empty() && _childrenArrays.empty() && _childrenValues.empty());
|
assert(_children.empty() && _childrenArrays.empty() && _childrenValues.empty());
|
||||||
_value = std::string{ value };
|
_value = string{ value };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Node::setValue(long long value)
|
void Response::Node::setValue(long long value)
|
||||||
@@ -55,13 +55,13 @@ namespace lms::api::subsonic
|
|||||||
|
|
||||||
void Response::Node::setAttribute(Key key, std::string_view value)
|
void Response::Node::setAttribute(Key key, std::string_view value)
|
||||||
{
|
{
|
||||||
_attributes[key] = std::string{ value };
|
_attributes[key] = string{ value };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Node::addChild(Key key, Node&& node)
|
void Response::Node::addChild(Key key, Node&& node)
|
||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
assert(_children.find(key) == std::cend(_children));
|
assert(!_children.contains(key));
|
||||||
_children[key] = std::move(node);
|
_children[key] = std::move(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,29 +69,29 @@ namespace lms::api::subsonic
|
|||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
assert(_children.find(key) == std::cend(_children));
|
assert(_children.find(key) == std::cend(_children));
|
||||||
_childrenArrays.emplace(key, std::vector<Node>{});
|
_childrenArrays.emplace(key, vector<Node>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Node::addArrayChild(Key key, Node&& node)
|
void Response::Node::addArrayChild(Key key, Node&& node)
|
||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
assert(_children.find(key) == std::cend(_children));
|
assert(!_children.contains(key));
|
||||||
_childrenArrays[key].emplace_back(std::move(node));
|
_childrenArrays[key].emplace_back(std::move(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Node::createEmptyArrayValue(Key key)
|
void Response::Node::createEmptyArrayValue(Key key)
|
||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
assert(_children.find(key) == std::cend(_children));
|
assert(!_children.contains(key));
|
||||||
_childrenValues.emplace(key, ValuesType{});
|
_childrenValues.emplace(key, ValuesType{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Node::addArrayValue(Key key, std::string_view value)
|
void Response::Node::addArrayValue(Key key, std::string_view value)
|
||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
assert(_children.find(key) == std::cend(_children));
|
assert(!_children.contains(key));
|
||||||
auto& values{ _childrenValues[key] };
|
auto& values{ _childrenValues[key] };
|
||||||
values.push_back(std::string{ value });
|
values.emplace_back(string{ value });
|
||||||
assert(std::all_of(std::cbegin(values) + 1, std::cend(values), [&](const ValueType& value) {return value.index() == values.front().index();}));
|
assert(std::all_of(std::cbegin(values) + 1, std::cend(values), [&](const ValueType& value) {return value.index() == values.front().index();}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ namespace lms::api::subsonic
|
|||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
auto& values{ _childrenValues[key] };
|
auto& values{ _childrenValues[key] };
|
||||||
values.push_back(value);
|
values.emplace_back(value);
|
||||||
assert(std::all_of(std::cbegin(values) + 1, std::cend(values), [&](const ValueType& value) {return value.index() == values.front().index();}));
|
assert(std::all_of(std::cbegin(values) + 1, std::cend(values), [&](const ValueType& value) {return value.index() == values.front().index();}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ namespace lms::api::subsonic
|
|||||||
Response::Node& Response::Node::createArrayChild(Key key)
|
Response::Node& Response::Node::createArrayChild(Key key)
|
||||||
{
|
{
|
||||||
assert(!_value);
|
assert(!_value);
|
||||||
assert(_children.find(key) == std::cend(_children));
|
assert(!_children.contains(key));
|
||||||
_childrenArrays[key].emplace_back();
|
_childrenArrays[key].emplace_back();
|
||||||
return _childrenArrays[key].back();
|
return _childrenArrays[key].back();
|
||||||
}
|
}
|
||||||
@@ -192,8 +192,8 @@ namespace lms::api::subsonic
|
|||||||
|
|
||||||
for (const auto& [key, value] : node._attributes)
|
for (const auto& [key, value] : node._attributes)
|
||||||
{
|
{
|
||||||
if (std::holds_alternative<std::string>(value))
|
if (std::holds_alternative<Node::string>(value))
|
||||||
res.put("<xmlattr>." + std::string{ key.str() }, std::get<std::string>(value));
|
res.put("<xmlattr>." + std::string{ key.str() }, std::get<Node::string>(value));
|
||||||
else if (std::holds_alternative<bool>(value))
|
else if (std::holds_alternative<bool>(value))
|
||||||
res.put("<xmlattr>." + std::string{ key.str() }, std::get<bool>(value));
|
res.put("<xmlattr>." + std::string{ key.str() }, std::get<bool>(value));
|
||||||
else if (std::holds_alternative<float>(value))
|
else if (std::holds_alternative<float>(value))
|
||||||
@@ -240,7 +240,7 @@ namespace lms::api::subsonic
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::property_tree::ptree root{ nodeToPropertyTree(_root) };
|
const boost::property_tree::ptree root{ nodeToPropertyTree(_root) };
|
||||||
boost::property_tree::write_xml(os, root);
|
boost::property_tree::write_xml(os, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,9 +337,9 @@ namespace lms::api::subsonic
|
|||||||
|
|
||||||
void Response::JsonSerializer::serializeValue(std::ostream& os, const Node::ValueType& value)
|
void Response::JsonSerializer::serializeValue(std::ostream& os, const Node::ValueType& value)
|
||||||
{
|
{
|
||||||
if (std::holds_alternative<std::string>(value))
|
if (std::holds_alternative<Node::string>(value))
|
||||||
{
|
{
|
||||||
serializeEscapedString(os, std::get<std::string>(value));
|
serializeEscapedString(os, std::get<Node::string>(value));
|
||||||
}
|
}
|
||||||
else if (std::holds_alternative<bool>(value))
|
else if (std::holds_alternative<bool>(value))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "core/LiteralString.hpp"
|
#include "core/LiteralString.hpp"
|
||||||
#include "RequestContext.hpp"
|
#include "RequestContext.hpp"
|
||||||
|
#include "SubsonicResponseAllocator.hpp"
|
||||||
|
|
||||||
namespace lms::api::subsonic
|
namespace lms::api::subsonic
|
||||||
{
|
{
|
||||||
@@ -240,14 +241,23 @@ namespace lms::api::subsonic
|
|||||||
void setVersionAttribute(ProtocolVersion version);
|
void setVersionAttribute(ProtocolVersion version);
|
||||||
|
|
||||||
friend class Response;
|
friend class Response;
|
||||||
using ValueType = std::variant<std::string, bool, float, long long>;
|
|
||||||
std::map<Key, ValueType> _attributes;
|
|
||||||
std::optional<ValueType> _value;
|
|
||||||
std::map<Key, Node> _children;
|
|
||||||
std::map<Key, std::vector<Node>> _childrenArrays;
|
|
||||||
|
|
||||||
using ValuesType = std::vector<ValueType>;
|
template <typename Key, typename Value>
|
||||||
std::map<Key, ValuesType> _childrenValues;
|
using map = std::map< Key, Value, std::less<Key>, ResponseAllocator<std::pair<const Key, Value>>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using vector = std::vector<T, ResponseAllocator<T>>;
|
||||||
|
|
||||||
|
using string = std::basic_string<char, std::char_traits<char>, ResponseAllocator<char>>;
|
||||||
|
|
||||||
|
using ValueType = std::variant<string, bool, float, long long>;
|
||||||
|
map<Key, ValueType> _attributes;
|
||||||
|
std::optional<ValueType> _value;
|
||||||
|
map<Key, Node> _children;
|
||||||
|
map<Key, vector<Node>> _childrenArrays;
|
||||||
|
|
||||||
|
using ValuesType = vector<ValueType>;
|
||||||
|
map<Key, ValuesType> _childrenValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Response createOkResponse(ProtocolVersion protocolVersion);
|
static Response createOkResponse(ProtocolVersion protocolVersion);
|
||||||
@@ -272,7 +282,7 @@ namespace lms::api::subsonic
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void serializeNode(std::ostream& os, const Node& node);
|
void serializeNode(std::ostream& os, const Node& node);
|
||||||
void serializeValue(std::ostream& os, const Node::ValueType& node);
|
void serializeValue(std::ostream& os, const Node::ValueType& value);
|
||||||
void serializeEscapedString(std::ostream&, std::string_view str);
|
void serializeEscapedString(std::ostream&, std::string_view str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TLSMonotonicMemoryResource.hpp"
|
||||||
|
|
||||||
|
namespace lms::api::subsonic
|
||||||
|
{
|
||||||
|
// Stateless allocator that uses a shared MemoryResource
|
||||||
|
template <typename MemoryResource, typename T>
|
||||||
|
class Allocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using pointer = T*;
|
||||||
|
using const_pointer = const T*;
|
||||||
|
using reference = T&;
|
||||||
|
using const_reference = const T&;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
constexpr Allocator() noexcept = default;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
constexpr Allocator(const Allocator<MemoryResource, U>&) noexcept {}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
struct rebind
|
||||||
|
{
|
||||||
|
using other = Allocator<MemoryResource, V>;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] pointer allocate(size_type n)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<pointer>(MemoryResource::getInstance().allocate(n * sizeof(T), alignof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocate memory pointed to by p
|
||||||
|
void deallocate(pointer p, std::size_t) noexcept
|
||||||
|
{
|
||||||
|
MemoryResource::getInstance().deallocate(reinterpret_cast<std::byte*>(p));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class MemoryResource, class T, class U>
|
||||||
|
bool operator==(const Allocator <MemoryResource, T>&, const Allocator <MemoryResource, U>&)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class MemoryResource, class T, class U>
|
||||||
|
bool operator!=(const Allocator <MemoryResource, T>&, const Allocator <MemoryResource, U>&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using ResponseAllocator = Allocator<TLSMonotonicMemoryResource, T>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace lms::api::subsonic
|
||||||
|
{
|
||||||
|
class TLSMonotonicMemoryResource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static TLSMonotonicMemoryResource& getInstance()
|
||||||
|
{
|
||||||
|
static thread_local TLSMonotonicMemoryResource instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
TLSMonotonicMemoryResource()
|
||||||
|
{
|
||||||
|
allocateNewBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::byte* allocate(std::size_t byteCount, std::size_t alignment)
|
||||||
|
{
|
||||||
|
std::byte* currentAddrAligned{ computeAlignedAddr(_currentAddr, alignment) };
|
||||||
|
|
||||||
|
if (currentAddrAligned + byteCount > &_currentBlock->back() + 1)
|
||||||
|
{
|
||||||
|
allocateNewBlock();
|
||||||
|
currentAddrAligned = computeAlignedAddr(_currentAddr, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requested too many bytes for blockSize!
|
||||||
|
if (currentAddrAligned + byteCount > &_currentBlock->back() + 1)
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
|
||||||
|
assert(currentAddrAligned >= &_currentBlock->front());
|
||||||
|
|
||||||
|
std::byte* res{ currentAddrAligned };
|
||||||
|
_currentAddr = currentAddrAligned + byteCount;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(std::byte*)
|
||||||
|
{
|
||||||
|
// nothing to do!
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
// always keep at least one block
|
||||||
|
if (_blocks.size() > 1)
|
||||||
|
_blocks.erase(std::next(std::cbegin(_blocks), 1), std::cend(_blocks));
|
||||||
|
|
||||||
|
_currentBlock = &_blocks.front();
|
||||||
|
_currentAddr = _currentBlock->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void allocateNewBlock()
|
||||||
|
{
|
||||||
|
_currentBlock = &_blocks.emplace_back();
|
||||||
|
_currentAddr = _currentBlock->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::byte* computeAlignedAddr(std::byte* addr, std::size_t alignment) noexcept
|
||||||
|
{
|
||||||
|
const std::uintptr_t addrValue{ reinterpret_cast<std::uintptr_t>(addr) };
|
||||||
|
|
||||||
|
const std::uintptr_t mask{ alignment - 1 };
|
||||||
|
const std::uintptr_t adjustment{ (alignment - (addrValue & mask)) & mask };
|
||||||
|
return reinterpret_cast<std::byte*>(addr + adjustment);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::size_t blockSize{ static_cast<std::size_t>(1 * 1024 * 1024) };
|
||||||
|
using BlockType = std::array<std::byte, blockSize>;
|
||||||
|
|
||||||
|
std::list<BlockType> _blocks;
|
||||||
|
BlockType* _currentBlock{};
|
||||||
|
std::byte* _currentAddr{};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user