Skip to content

Commit

Permalink
Merge pull request #154 from adya/dev
Browse files Browse the repository at this point in the history
BSTArray: Added insert/emplace to any position
  • Loading branch information
powerof3 authored Jan 1, 2025
2 parents b4660c3 + 06f80d1 commit 4a19842
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 3 deletions.
82 changes: 79 additions & 3 deletions include/RE/B/BSTArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,23 @@ namespace RE
set_size(newSize);
}

inline BSTArray(const std::initializer_list<T> a_list)
{
if (a_list.size() == 0) {
return;
}

const auto newSize = a_list.size();
const auto newData = allocate(newSize);
size_type i = 0;
for (const auto& elem : a_list) {
std::construct_at(newData + i++, elem);
}

set_allocator_traits(newData, newSize);
set_size(newSize);
}

BSTArray(BSTArray&&) = default;

explicit inline BSTArray(size_type a_count)
Expand Down Expand Up @@ -555,6 +572,53 @@ namespace RE
return elem;
}

inline void insert(const_iterator position, const value_type& a_value) { emplace(position, a_value); }
inline void insert(const_iterator position, value_type&& a_value) { emplace(position, std::move(a_value)); }

inline void push_front(const value_type& a_value) { emplace(cbegin(), a_value); }
inline void push_front(value_type&& a_value) { emplace(cbegin(), std::move(a_value)); }

template <class... Args>
inline reference emplace(const_iterator position, Args&&... a_args)
{
assert(position >= cbegin() && position <= cend());

if (position == cend()) {
return emplace_back(std::forward<Args>(a_args)...);
}

pointer oldData;
pointer newData;
size_type newCapacity;

if (size() == capacity()) {
newCapacity = next_capacity();
newData = allocate(newCapacity); // manually grow capacity to avoid unnecessary memcpy from change_capacity
oldData = data();
} else {
newData = data();
oldData = nullptr;
newCapacity = capacity();
}

const auto headPartToCopy = position - cbegin();
const auto tailPartToCopy = cend() - position;
const auto tailBytesToCopy = tailPartToCopy * sizeof(T);
std::memcpy(newData + headPartToCopy + 1, cend() - tailPartToCopy, tailBytesToCopy);
std::construct_at(newData + headPartToCopy, std::forward<Args>(a_args)...);
const auto headBytesToCopy = headPartToCopy * sizeof(T);
std::memcpy(newData, cbegin(), headBytesToCopy);

if (oldData) {
deallocate(oldData);
set_allocator_traits(newData, newCapacity);
}

set_size(size() + 1);

return *(newData + headPartToCopy);
}

inline void pop_back()
{
assert(!empty());
Expand Down Expand Up @@ -630,13 +694,25 @@ namespace RE
set_size(a_newSize);
}

inline void grow_capacity() { grow_capacity(capacity()); }
/// Calculates the next value for the array capacity.
/// Capacity grows exponentially: hint * 2^level, where level is the number of times the capacity has grown.
[[nodiscard]]
inline size_type next_capacity() const { return next_capacity(capacity()); }

inline void grow_capacity(size_type a_hint)
/// Calculates the next value for the array capacity.
/// Capacity grows exponentially: hint * 2^level, where level is the number of times the capacity has grown.
inline size_type next_capacity(size_type a_hint) const
{
auto cap = a_hint;
cap = cap > 0 ? static_cast<size_type>(std::ceil(static_cast<float>(cap) * GROWTH_FACTOR)) : DF_CAP;
change_capacity(cap);
return cap;
}

inline void grow_capacity() { grow_capacity(capacity()); }

inline void grow_capacity(size_type a_hint)
{
change_capacity(next_capacity(a_hint));
}

inline void release()
Expand Down
42 changes: 42 additions & 0 deletions include/RE/B/BSTEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,48 @@ namespace RE
AddEventSink(a_sink);
}

/// Adds an event sink to the front of sinks list.
///
/// When there is an ongoing notification, sinks are prepended to a pending list.
/// Consider the following case: Add(A), Add(B), notifying=true, Add(C), Prepend(D), Prepend(E), notifying = false
/// Sinks: A, B
/// Pending: E, D, C
/// Result: A, B, E, D, C
///
/// However without notifying the same chain of calls will look like this:
/// Sinks: E, D, A, B, C
///
/// The relative order of C, D, and E is guaranteed in both cases, but, previous sinks may appear both before and after the new sinks.
void PrependEventSink(Sink* a_eventSink)
{
if (!a_eventSink) {
return;
}

BSSpinLockGuard locker(lock);

if (notifying) {
if (std::find(pendingRegisters.begin(), pendingRegisters.end(), a_eventSink) == pendingRegisters.end()) {
pendingRegisters.push_front(a_eventSink);
}
} else {
if (std::find(sinks.begin(), sinks.end(), a_eventSink) == sinks.end()) {
sinks.push_front(a_eventSink);
}
}

auto it = std::find(pendingUnregisters.begin(), pendingUnregisters.end(), a_eventSink);
if (it != pendingUnregisters.end()) {
pendingUnregisters.erase(it);
}
}

template <class SinkEvent>
inline void PrependEventSink(BSTEventSink<SinkEvent>* a_sink)
{
PrependEventSink(a_sink);
}

void RemoveEventSink(Sink* a_eventSink)
{
if (!a_eventSink) {
Expand Down
6 changes: 6 additions & 0 deletions include/RE/S/ScriptEventSourceHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ namespace RE
GetEventSource<T>()->AddEventSink(a_sink);
}

template <class T>
inline void PrependEventSink(BSTEventSink<T>* a_sink)
{
GetEventSource<T>()->PrependEventSink(a_sink);
}

template <class T>
inline void RemoveEventSink(BSTEventSink<T>* a_sink)
{
Expand Down

0 comments on commit 4a19842

Please sign in to comment.