flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:39:24 -05:00
commit fa54be052a
315 changed files with 49791 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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