Browse Source
Previously, the memory for each frame was allocated separately. Changes it to allocate a single buffer for all the frames. This has the following advantages: 1. Less bookkeeping overhead in the allocator. 2. Less alignment overhead (allocator results are max-aligned by default). We can follow this up with a similar treatment for other multi-file animations.pull/4017/head
8 changed files with 192 additions and 28 deletions
@ -0,0 +1,61 @@
|
||||
#pragma once |
||||
|
||||
#include <cstring> |
||||
#include <initializer_list> |
||||
|
||||
#include <fmt/format.h> |
||||
|
||||
#include "utils/stdcompat/string_view.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* @brief Generates file names from prefixes, a suffix, and an index. |
||||
* |
||||
* @example FileNameGenerator f({"a/", "b"}, ".txt", 1); |
||||
* f() // "a/b.txt"
|
||||
* f(0) // "a/b1.txt"
|
||||
* f(1) // "a/b2.txt"
|
||||
*/ |
||||
class FileNameGenerator { |
||||
public: |
||||
FileNameGenerator(std::initializer_list<string_view> prefixes, string_view suffix, unsigned min = 1) |
||||
: suffix_(suffix) |
||||
, min_(min) |
||||
, prefixEnd_(Append(buf_, prefixes)) |
||||
{ |
||||
} |
||||
|
||||
const char *operator()() const |
||||
{ |
||||
*Append(prefixEnd_, suffix_) = '\0'; |
||||
return buf_; |
||||
} |
||||
|
||||
const char *operator()(size_t i) const |
||||
{ |
||||
*Append(fmt::format_to(prefixEnd_, "{}", static_cast<unsigned>(min_ + i)), suffix_) = '\0'; |
||||
return buf_; |
||||
} |
||||
|
||||
private: |
||||
static char *Append(char *buf, std::initializer_list<string_view> strings) |
||||
{ |
||||
for (string_view str : strings) |
||||
buf = Append(buf, str); |
||||
return buf; |
||||
} |
||||
|
||||
static char *Append(char *buf, string_view str) |
||||
{ |
||||
memcpy(buf, str.data(), str.size()); |
||||
return buf + str.size(); |
||||
} |
||||
|
||||
string_view suffix_; |
||||
unsigned min_; |
||||
char *prefixEnd_; |
||||
char buf_[256]; |
||||
}; |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,60 @@
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <memory> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "appfat.h" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* @brief A stack-allocated vector with a fixed capacity. |
||||
* |
||||
* @tparam T element type. |
||||
* @tparam N capacity. |
||||
*/ |
||||
template <class T, size_t N> |
||||
class StaticVector { |
||||
public: |
||||
template <typename... Args> |
||||
T &emplace_back(Args &&...args) // NOLINT(readability-identifier-naming)
|
||||
{ |
||||
assert(size_ < N); |
||||
::new (&data_[size_]) T(std::forward<Args>(args)...); |
||||
#if __cplusplus >= 201703L |
||||
T &result = *std::launder(reinterpret_cast<T *>(&data_[size_])); |
||||
#else |
||||
T &result = *reinterpret_cast<T *>(&data_[size_]); |
||||
#endif |
||||
++size_; |
||||
return result; |
||||
} |
||||
|
||||
const T &operator[](std::size_t pos) const |
||||
{ |
||||
#if __cplusplus >= 201703L |
||||
return *std::launder(reinterpret_cast<const T *>(&data_[pos])); |
||||
#else |
||||
return *reinterpret_cast<const T *>(&data_[pos]); |
||||
#endif |
||||
} |
||||
|
||||
~StaticVector() |
||||
{ |
||||
for (std::size_t pos = 0; pos < size_; ++pos) { |
||||
#if __cplusplus >= 201703L |
||||
std::destroy_at(std::launder(reinterpret_cast<T *>(&data_[pos]))); |
||||
#else |
||||
reinterpret_cast<T *>(&data_[pos])->~T(); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
private: |
||||
std::aligned_storage_t<sizeof(T), alignof(T)> data_[N]; |
||||
std::size_t size_ = 0; |
||||
}; |
||||
|
||||
} // namespace devilution
|
||||
Loading…
Reference in new issue