flatten 20260225
This commit is contained in:
63
tjp/core/containers/Alias.hpp
Normal file
63
tjp/core/containers/Alias.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct Alias : T {
|
||||
using Super = T;
|
||||
using Self = Alias;
|
||||
|
||||
template<typename ... Args>
|
||||
Alias(Args&& ... args) :
|
||||
Super(std::forward<Args>(args)...)
|
||||
{}
|
||||
} ;
|
||||
|
||||
#define ALIAS_OF_HEADER(U, ...) \
|
||||
struct U;
|
||||
|
||||
// https://stackoverflow.com/questions/8942912/how-to-pass-multi-argument-templates-to-macros
|
||||
#define ALIAS_OF(U, ...) \
|
||||
struct U : __VA_ARGS__ { \
|
||||
using Super = __VA_ARGS__; \
|
||||
using Self = U; \
|
||||
template<typename ... Args> \
|
||||
U(Args&& ... args) : \
|
||||
Super(std::forward<Args>(args)...) \
|
||||
{} \
|
||||
} \
|
||||
|
||||
#define ALIAS_OF_WITH_SIMPLE_ASSIGNMENT(U, ...) \
|
||||
struct U : __VA_ARGS__ { \
|
||||
using Super = __VA_ARGS__; \
|
||||
using Self = U; \
|
||||
template<typename ... Args> \
|
||||
U(Args&& ... args) : \
|
||||
Super(std::forward<Args>(args)...) \
|
||||
{} \
|
||||
\
|
||||
template<typename V, typename = std::enable_if_t<!std::is_same_v<std::decay_t<V>, U> && !std::is_same_v<std::decay_t<V>, Super>>> \
|
||||
U &operator =(V &&u) = delete; \
|
||||
\
|
||||
U &operator =(const Super &u) { Super::operator=(u); return *this; } \
|
||||
U &operator =(Super &&u) { Super::operator=(std::move(u)); return *this; } \
|
||||
} \
|
||||
|
||||
// U &operator =(const U &u) { Super::operator=(u); return *this; } \
|
||||
|
||||
#define ALIAS_OF_WITH_TEMPLATE_ASSIGNMENT(U, ...) \
|
||||
struct U : __VA_ARGS__ { \
|
||||
using Super = __VA_ARGS__; \
|
||||
using Self = U; \
|
||||
template<typename ... Args> \
|
||||
U(Args&& ... args) : \
|
||||
Super(std::forward<Args>(args)...) \
|
||||
{} \
|
||||
\
|
||||
template<typename V> \
|
||||
auto &operator =(V &&t) { Super::operator=(std::forward<V>(t)); return *this; } \
|
||||
} \
|
||||
|
||||
} // namespace
|
||||
88
tjp/core/containers/Changed.hpp
Normal file
88
tjp/core/containers/Changed.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct Changed
|
||||
{
|
||||
T value;
|
||||
bool changed_ = true;
|
||||
|
||||
using Self = Changed;
|
||||
|
||||
Changed(T value_) :
|
||||
value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
Changed():
|
||||
changed_(false)
|
||||
{
|
||||
}
|
||||
|
||||
operator T &()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const T &rhs)
|
||||
{
|
||||
if (rhs == value)
|
||||
return;
|
||||
|
||||
value = rhs;
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
Self &operator =(const T &rhs)
|
||||
{
|
||||
set(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
changed_ = false;
|
||||
}
|
||||
|
||||
void mark()
|
||||
{
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
bool use()
|
||||
{
|
||||
bool result = changed_;
|
||||
changed_ = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool changed() const
|
||||
{
|
||||
return changed_;
|
||||
}
|
||||
|
||||
auto *operator ->() const
|
||||
{
|
||||
return &value;
|
||||
}
|
||||
|
||||
auto *operator ->()
|
||||
{
|
||||
return &value;
|
||||
}
|
||||
|
||||
auto &operator *() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
auto &operator *()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
198
tjp/core/containers/CircularBuffer.hpp
Normal file
198
tjp/core/containers/CircularBuffer.hpp
Normal file
@@ -0,0 +1,198 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/assert/debug_assert.h>
|
||||
|
||||
#include <tjp/core/containers/Vector.h>
|
||||
#include <tjp/core/containers/Array.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct CircularBuffer {
|
||||
Vector<T> buffer;
|
||||
size_t begin_ = 0, end_ = 0;
|
||||
size_t size_ = 0;
|
||||
|
||||
size_t begin() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
size_t end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
size_ = end_ = begin_ = 0;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
size_t available() const
|
||||
{
|
||||
return buffer.size() - size();
|
||||
}
|
||||
|
||||
struct Segment {
|
||||
size_t to, from, size;
|
||||
} ;
|
||||
|
||||
const T *data () const
|
||||
{
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
T *data ()
|
||||
{
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
Array<Segment, 2> segments(size_t from_, size_t dataSize) const
|
||||
{
|
||||
auto bufferSize = buffer.size();
|
||||
if (bufferSize == 0)
|
||||
{
|
||||
return {
|
||||
Segment { 0, 0, 0 },
|
||||
Segment { 0, 0, 0 }
|
||||
} ;
|
||||
}
|
||||
|
||||
auto from = from_ % bufferSize;
|
||||
auto to_0 = from + dataSize;
|
||||
auto to_1 = (size_t)0;
|
||||
|
||||
if (to_0 > bufferSize)
|
||||
{
|
||||
to_1 = to_0 % bufferSize;
|
||||
to_0 = to_0 - to_1;
|
||||
}
|
||||
|
||||
auto seg_0 = (to_0 - from);
|
||||
auto seg_1 = (size_t)to_1;
|
||||
|
||||
return {
|
||||
Segment { from, 0, seg_0 },
|
||||
Segment { 0, seg_0, seg_1 }
|
||||
} ;
|
||||
}
|
||||
|
||||
void grow(size_t dataSize)
|
||||
{
|
||||
if (dataSize == 0)
|
||||
return;
|
||||
|
||||
if (available() <= dataSize)
|
||||
{
|
||||
auto previousBufferSize = buffer.size();
|
||||
buffer.resize(previousBufferSize + dataSize);
|
||||
|
||||
// if there is data at the beginning of the buffer, we need to
|
||||
// put some of it at the end
|
||||
if (end_ <= begin_)
|
||||
{
|
||||
auto previousEnd = end_;
|
||||
|
||||
end_ = previousBufferSize;
|
||||
|
||||
if (previousEnd > 0)
|
||||
{
|
||||
size_ -= previousEnd;
|
||||
write(buffer.data(), previousEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_ += dataSize;
|
||||
}
|
||||
|
||||
size_t write(const T *data, size_t dataSize)
|
||||
{
|
||||
auto size = size_;
|
||||
grow(dataSize);
|
||||
|
||||
auto segments_ = segments(begin_ + size, dataSize);
|
||||
|
||||
for (auto &segment: segments_)
|
||||
{
|
||||
if (segment.size)
|
||||
{
|
||||
debug_assert(segment.to + segment.size <= buffer.size());
|
||||
|
||||
if (data)
|
||||
std::memmove(buffer.data() + segment.to, data + segment.from, segment.size * sizeof(T));
|
||||
else
|
||||
std::memset(buffer.data() + segment.to, 0, segment.size * sizeof(T));
|
||||
|
||||
end_ = segment.to + segment.size;
|
||||
end_ = end_ % buffer.size();
|
||||
}
|
||||
}
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
size_t peek(T *data, size_t dataSize) const
|
||||
{
|
||||
if (dataSize == 0)
|
||||
return dataSize;
|
||||
|
||||
if (size_ < dataSize)
|
||||
dataSize = size_;
|
||||
|
||||
auto segments_ = segments(begin_, dataSize);
|
||||
for (auto &segment: segments_)
|
||||
{
|
||||
if (segment.size)
|
||||
{
|
||||
std::memcpy(data + segment.from, buffer.data() + segment.to, segment.size * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
size_t read(T *data, size_t dataSize)
|
||||
{
|
||||
if (dataSize == 0)
|
||||
return dataSize;
|
||||
|
||||
if (size_ < dataSize)
|
||||
dataSize = size_;
|
||||
|
||||
peek(data, dataSize);
|
||||
skip(dataSize);
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
size_t skip(size_t dataSize)
|
||||
{
|
||||
if (dataSize == 0)
|
||||
return dataSize;
|
||||
|
||||
debug_assert(size_ >= dataSize);
|
||||
|
||||
begin_ += dataSize;
|
||||
begin_ = begin_ % buffer.size();
|
||||
size_ -= dataSize;
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
42
tjp/core/containers/InvalidatingCache.hpp
Normal file
42
tjp/core/containers/InvalidatingCache.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/time/Time.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct InvalidatingCache
|
||||
{
|
||||
Optional<T> value;
|
||||
|
||||
bool hasValue(time::Time now)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
T &cache(U &&u)
|
||||
{
|
||||
value.emplace(std::forward<U>(u));
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T &cacheIf(time::Time now, F &&f)
|
||||
{
|
||||
if (!hasValue(now))
|
||||
return cache(f());
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
value.reset();
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
52
tjp/core/containers/InvalidatingTimedCache.hpp
Normal file
52
tjp/core/containers/InvalidatingTimedCache.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/time/Time.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct InvalidatingTimedCache
|
||||
{
|
||||
time::Duration invalidAfter = time::Duration::Zero;
|
||||
|
||||
Optional<T> value;
|
||||
Optional<time::Time> invalidatedAt;
|
||||
|
||||
bool hasValue(time::Time now)
|
||||
{
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
if (invalidatedAt)
|
||||
return now < (*invalidatedAt + invalidAfter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
T &cache(U &&u)
|
||||
{
|
||||
value.emplace(std::forward<U>(u));
|
||||
invalidatedAt.reset();
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T &cacheIf(time::Time now, F &&f)
|
||||
{
|
||||
if (!hasValue(now))
|
||||
return cache(f());
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
void invalidate(time::Time now)
|
||||
{
|
||||
invalidatedAt.emplace(now);
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
101
tjp/core/containers/SizedVector.hpp
Normal file
101
tjp/core/containers/SizedVector.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/assert/debug_assert.h>
|
||||
#include <tjp/core/containers/Vector.h>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct SizedVector
|
||||
{
|
||||
using V = Vector<T>;
|
||||
using iterator = typename V::iterator;
|
||||
using const_iterator = typename V::const_iterator;
|
||||
using value_type = typename V::value_type;
|
||||
using reference = typename V::reference;
|
||||
|
||||
V v;
|
||||
size_t size_ = 0;
|
||||
|
||||
auto reserved() const { return v.size(); }
|
||||
|
||||
void reserve(size_t size)
|
||||
{
|
||||
if (size > v.size())
|
||||
v.resize(size);
|
||||
}
|
||||
|
||||
auto capacity () const {
|
||||
return v.size();
|
||||
}
|
||||
|
||||
auto resize (size_t size)
|
||||
{
|
||||
reserve(size);
|
||||
return size_ = size;
|
||||
}
|
||||
|
||||
auto &front () { return v.front(); }
|
||||
auto &front () const { return v.front(); }
|
||||
|
||||
auto &back () { return at(size_-1); }
|
||||
auto &back () const { return at(size_-1); }
|
||||
|
||||
auto begin () { return v.begin(); }
|
||||
auto end() { return v.begin() + size(); };
|
||||
auto begin () const { return v.begin(); }
|
||||
auto end() const { return v.begin() + size(); };
|
||||
|
||||
auto &at(size_t i)
|
||||
{
|
||||
debug_assert(i < size_);
|
||||
return v[i];
|
||||
}
|
||||
|
||||
auto &at(size_t i) const
|
||||
{
|
||||
debug_assert(i < size_);
|
||||
return v[i];
|
||||
}
|
||||
|
||||
auto &operator[](size_t i) { return at(i); }
|
||||
auto &operator[](size_t i) const { return at(i); }
|
||||
|
||||
auto *data() { return v.data(); }
|
||||
auto *data() const { return v.data(); }
|
||||
|
||||
auto empty() const { return size_ == 0; }
|
||||
auto size() const { return size_; }
|
||||
|
||||
auto push_back(const T &t)
|
||||
{
|
||||
debug_assert(size_ < v.size());
|
||||
|
||||
if (size_ < v.size())
|
||||
{
|
||||
size_++;
|
||||
back() = t;
|
||||
}
|
||||
}
|
||||
|
||||
auto pop_back(const T &t)
|
||||
{
|
||||
debug_assert(size_ > 0);
|
||||
if (size_ > 0)
|
||||
size_--;
|
||||
}
|
||||
|
||||
// can only be used to remove a portion at the end (as of now)
|
||||
template<typename A, typename B>
|
||||
auto erase(A &&a, B &&b)
|
||||
{
|
||||
debug_assert(b == end());
|
||||
auto distance = std::distance(a, b);
|
||||
size_ -= distance;
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
39
tjp/core/containers/TimedCache.hpp
Normal file
39
tjp/core/containers/TimedCache.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/time/Time.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct TimedCache
|
||||
{
|
||||
time::Duration expiresAfter;
|
||||
|
||||
Optional<T> value;
|
||||
time::Time cachedAt = time::Time::Zero;
|
||||
|
||||
bool hasValue(time::Time now)
|
||||
{
|
||||
return value && now < (cachtedAt + expiresAfter);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
T &cache(U &&u)
|
||||
{
|
||||
value.emplace(std::forward<U>(u));
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T &cacheIf(time::Time now, F &&f)
|
||||
{
|
||||
if (!hasValue(now))
|
||||
return cache(f());
|
||||
|
||||
return *value;
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
71
tjp/core/containers/_tests/CircularBuffer.cpp
Normal file
71
tjp/core/containers/_tests/CircularBuffer.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
#include <tjp/core/containers/CircularBuffer.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
namespace {
|
||||
|
||||
SCENARIO("CircularBuffer")
|
||||
{
|
||||
GIVEN("a circular buffer")
|
||||
{
|
||||
typedef int T;
|
||||
CircularBuffer<T> c;
|
||||
|
||||
WHEN("adding in blocks")
|
||||
{
|
||||
constexpr int blockSize = 1024;
|
||||
T removes[blockSize];
|
||||
T adds[blockSize];
|
||||
|
||||
for (auto i=0; i<std::size(adds); ++i)
|
||||
adds[i] = i;
|
||||
|
||||
for (auto i=0; i<64; ++i)
|
||||
{
|
||||
c.write(adds, blockSize);
|
||||
c.read(removes, blockSize);
|
||||
|
||||
bool isSame = memcmp(adds, removes, blockSize * sizeof(T)) == 0;
|
||||
REQUIRE(isSame);
|
||||
}
|
||||
|
||||
for (auto i=0; i<64; ++i)
|
||||
c.write(adds, blockSize);
|
||||
|
||||
for (auto i=0; i<32; ++i)
|
||||
{
|
||||
c.read(removes, blockSize);
|
||||
|
||||
bool isSame = memcmp(adds, removes, blockSize * sizeof(T)) == 0;
|
||||
REQUIRE(isSame);
|
||||
}
|
||||
|
||||
for (auto i=0; i<1024; ++i)
|
||||
{
|
||||
c.read(removes, blockSize);
|
||||
c.write(adds, blockSize);
|
||||
|
||||
bool isSame = memcmp(adds, removes, blockSize * sizeof(T)) == 0;
|
||||
REQUIRE(isSame);
|
||||
}
|
||||
|
||||
for (auto i=0; i<32; ++i)
|
||||
{
|
||||
c.write(adds, blockSize);
|
||||
}
|
||||
|
||||
for (auto i=0; i<64; ++i)
|
||||
{
|
||||
c.read(removes, blockSize);
|
||||
|
||||
bool isSame = memcmp(adds, removes, blockSize * sizeof(T)) == 0;
|
||||
REQUIRE(isSame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user