[util] Replace kutil with util
Now that kutil has no kernel-specific code in it anymore, it can actually be linked to by anything, so I'm renaming it 'util'. Also, I've tried to unify the way that the system libraries from src/libraries are #included using <> instead of "". Other small change: util::bip_buffer got a spinlock to guard against state corruption.
This commit is contained in:
110
src/libraries/util/bip_buffer.cpp
Normal file
110
src/libraries/util/bip_buffer.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#if __has_include(<assert.h>)
|
||||
#include <assert.h>
|
||||
#else
|
||||
#define assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
#include <util/bip_buffer.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
bip_buffer::bip_buffer() :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(0),
|
||||
m_buffer(nullptr)
|
||||
{}
|
||||
|
||||
bip_buffer::bip_buffer(uint8_t *buffer, size_t size) :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(size),
|
||||
m_buffer(buffer)
|
||||
{}
|
||||
|
||||
size_t bip_buffer::reserve(size_t size, void **area)
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
if (m_size_r) {
|
||||
*area = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t remaining = 0;
|
||||
if (m_size_b) {
|
||||
// If B exists, we're appending there. Get space between
|
||||
// the end of B and start of A.
|
||||
remaining = m_start_a - m_start_b - m_size_b;
|
||||
m_start_r = m_start_b + m_size_b;
|
||||
} else {
|
||||
// B doesn't exist, check the space both before and after A.
|
||||
// If the end of A has enough room for this write, put it there.
|
||||
remaining = m_buffer_size - m_start_a - m_size_a;
|
||||
m_start_r = m_start_a + m_size_a;
|
||||
|
||||
// Otherwise use the bigger of the areas in front of and after A
|
||||
if (remaining < size && m_start_a > remaining) {
|
||||
remaining = m_start_a;
|
||||
m_start_r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!remaining) {
|
||||
*area = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_size_r = (remaining < size) ? remaining : size;
|
||||
*area = &m_buffer[m_start_r];
|
||||
return m_size_r;
|
||||
}
|
||||
|
||||
void bip_buffer::commit(size_t size)
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
assert(size <= m_size_r && "Tried to commit more than reserved");
|
||||
|
||||
if (m_start_r == m_start_a + m_size_a) {
|
||||
// We were adding to A
|
||||
m_size_a += size;
|
||||
} else {
|
||||
// We were adding to B
|
||||
assert(m_start_r == m_start_b + m_size_b && "Bad m_start_r!");
|
||||
m_size_b += size;
|
||||
}
|
||||
|
||||
m_start_r = m_size_r = 0;
|
||||
}
|
||||
|
||||
size_t bip_buffer::get_block(void **area) const
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
*area = m_size_a ? &m_buffer[m_start_a] : nullptr;
|
||||
return m_size_a;
|
||||
}
|
||||
|
||||
void bip_buffer::consume(size_t size)
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
assert(size <= m_size_a && "Consumed more bytes than exist in A");
|
||||
if (size >= m_size_a) {
|
||||
m_size_a = m_size_b;
|
||||
m_start_a = m_start_b;
|
||||
m_size_b = m_start_b = 0;
|
||||
} else {
|
||||
m_size_a -= size;
|
||||
m_start_a += size;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
63
src/libraries/util/include/util/bip_buffer.h
Normal file
63
src/libraries/util/include/util/bip_buffer.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
/// \file bip_buffer.h
|
||||
/// A Bip Buffer (bipartite circular buffer). For more on the Bip Buffer structure, see
|
||||
/// https://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util/spinlock.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
class bip_buffer
|
||||
{
|
||||
public:
|
||||
/// Default constructor. Creates a zero-size buffer.
|
||||
bip_buffer();
|
||||
|
||||
/// Constructor.
|
||||
bip_buffer(uint8_t *buffer, size_t size);
|
||||
|
||||
/// Reserve an area of buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \returns Size of returned area, in bytes, or 0 on failure
|
||||
size_t reserve(size_t size, void **area);
|
||||
|
||||
/// Commit a pending write started by reserve()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
void commit(size_t size);
|
||||
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \arg area [out] Pointer to the retuned area
|
||||
/// \returns Size of the returned area, in bytes
|
||||
size_t get_block(void **area) const;
|
||||
|
||||
/// Mark a number of bytes as consumed, freeing buffer space
|
||||
/// \arg size Number of bytes to consume
|
||||
void consume(size_t size);
|
||||
|
||||
/// Get total amount of data in the buffer.
|
||||
/// \returns Number of bytes committed to the buffer
|
||||
inline size_t size() const { return m_size_a + m_size_b; }
|
||||
|
||||
/// Get total amount of free buffer remaining
|
||||
/// \returns Number of bytes of buffer that are free
|
||||
inline size_t free_space() const { return m_buffer_size - size(); }
|
||||
|
||||
private:
|
||||
size_t m_start_a;
|
||||
size_t m_start_b;
|
||||
size_t m_start_r;
|
||||
size_t m_size_a;
|
||||
size_t m_size_b;
|
||||
size_t m_size_r;
|
||||
|
||||
mutable spinlock m_lock;
|
||||
|
||||
const size_t m_buffer_size;
|
||||
uint8_t * const m_buffer;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
42
src/libraries/util/include/util/constexpr_hash.h
Normal file
42
src/libraries/util/include/util/constexpr_hash.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
/// \file constexpr_hash.h
|
||||
/// A complile-time hashing function
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
constexpr static const uint8_t pearson_hash_table[256] = {
|
||||
0x76,0x07,0xbe,0x47,0xcf,0x41,0x0a,0xe8,0x01,0x5c,0x9f,0xc5,0x24,0x63,0x9a,0x85,
|
||||
0x39,0x2c,0xe2,0x34,0xb9,0xf2,0xae,0x40,0x10,0x90,0x94,0xd1,0x98,0x2d,0x16,0xfd,
|
||||
0xc6,0x48,0x0d,0xce,0x74,0x43,0x28,0xf9,0x61,0x12,0xd0,0xcd,0xd8,0xd7,0xa8,0x78,
|
||||
0x73,0x70,0xcc,0x1e,0x17,0xa7,0x87,0x38,0x68,0x91,0xc1,0x04,0x3f,0xf5,0xde,0xa3,
|
||||
0x8a,0xe5,0x9b,0xec,0x97,0xd5,0x71,0x4a,0x20,0xca,0xc8,0xc4,0x83,0x53,0xe7,0x7b,
|
||||
0x64,0x31,0x06,0xe0,0x7a,0xb6,0x52,0x8c,0xba,0x58,0xcb,0xb5,0x37,0x51,0x59,0xa1,
|
||||
0x11,0xe3,0x5a,0xdb,0xe1,0x6d,0x46,0x62,0xaf,0xbd,0x57,0xb8,0x0e,0xf4,0xdd,0xa6,
|
||||
0x45,0xf8,0x35,0x42,0x56,0xdf,0xad,0x80,0xb2,0x0b,0x5b,0xd4,0x86,0xb3,0xf0,0xc9,
|
||||
0x3c,0xa5,0xc0,0x8e,0x55,0x77,0xeb,0x36,0x79,0xab,0x4c,0x25,0xed,0xa9,0x75,0x8f,
|
||||
0xee,0xc2,0x72,0x8b,0x60,0x2a,0xfa,0x32,0xe9,0xda,0x03,0x1b,0x27,0x69,0x18,0x9e,
|
||||
0x88,0x96,0x54,0x81,0x30,0x22,0x7c,0x4f,0xc7,0xef,0x5d,0xa4,0x67,0x44,0xc3,0x99,
|
||||
0xbb,0xd3,0x8d,0x65,0xb1,0x82,0x09,0x1a,0x13,0xd9,0x9c,0x4d,0xb0,0xfc,0xac,0xbc,
|
||||
0x6a,0x29,0x95,0x19,0x92,0xaa,0x49,0x7d,0x3b,0xfb,0x50,0xb7,0xf3,0x5e,0x3e,0x6b,
|
||||
0x3a,0x14,0x2b,0xb4,0xfe,0xe6,0x93,0x23,0xd6,0x1f,0xd2,0x0c,0x1d,0x9d,0x6c,0x66,
|
||||
0x1c,0x89,0xbf,0xf6,0xff,0x6f,0x84,0x6e,0x2e,0xea,0x21,0xf7,0x7f,0x33,0xf1,0xe4,
|
||||
0x3d,0x0f,0x05,0x08,0x4e,0xa2,0xa0,0x2f,0xdc,0x00,0x5f,0x15,0x7e,0x02,0x4b,0x26
|
||||
};
|
||||
|
||||
constexpr inline uint8_t pearson_hash_8(const char *s, uint8_t inv) {
|
||||
return (*s) ? pearson_hash_8(s + 1, pearson_hash_table[inv ^ *s]) : inv;
|
||||
}
|
||||
|
||||
constexpr inline uint32_t djb_hash_32(const char *s, int off = 0) {
|
||||
return !s[off] ? 5381 : (djb_hash_32(s, off+1)*33) ^ s[off];
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
constexpr inline uint8_t operator "" _h (const char *s, size_t len) {
|
||||
return util::pearson_hash_8(s, static_cast<uint8_t>(len & 0xff));
|
||||
}
|
||||
|
||||
43
src/libraries/util/include/util/hash.h
Normal file
43
src/libraries/util/include/util/hash.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
/// \file hash.h
|
||||
/// Simple templated hashing functions
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
constexpr uint64_t fnv_64_prime = 0x100000001b3ull;
|
||||
constexpr uint64_t fnv1a_64_init = 0xcbf29ce484222325ull;
|
||||
|
||||
/// Return the FNV-1a hash of the given 0-terminated string.
|
||||
inline uint64_t hash_string(char const *s, uint64_t init = 0) {
|
||||
if (!init) init = fnv1a_64_init;
|
||||
while(s && *s) {
|
||||
init ^= static_cast<uint64_t>(*s++);
|
||||
init *= fnv_64_prime;
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
/// Return the FNV-1a hash of the given buffer.
|
||||
inline uint64_t hash_buffer(const void *v, size_t len, uint64_t init = 0) {
|
||||
uint8_t const *p = reinterpret_cast<uint8_t const*>(v);
|
||||
uint8_t const *end = p + len;
|
||||
if (!init) init = fnv1a_64_init;
|
||||
while(p < end) {
|
||||
init ^= static_cast<uint64_t>(*p++);
|
||||
init *= fnv_64_prime;
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline uint64_t hash(const T &v) {
|
||||
return hash_buffer(reinterpret_cast<const void*>(&v), sizeof(T));
|
||||
}
|
||||
|
||||
template <> inline uint64_t hash<uint64_t>(const uint64_t &i) { return i; }
|
||||
template <> inline uint64_t hash<const char *>(const char * const &s) { return hash_string(s); }
|
||||
|
||||
} // namespace util
|
||||
349
src/libraries/util/include/util/linked_list.h
Normal file
349
src/libraries/util/include/util/linked_list.h
Normal file
@@ -0,0 +1,349 @@
|
||||
#pragma once
|
||||
/// \file linked_list.h
|
||||
/// A generic templatized linked list.
|
||||
#include <stddef.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
template <typename T> class linked_list;
|
||||
|
||||
|
||||
/// A list node in a `linked_list<T>` or `sortable_linked_list<T>`.
|
||||
template <typename T>
|
||||
class list_node :
|
||||
public T
|
||||
{
|
||||
public:
|
||||
using item_type = T;
|
||||
using node_type = list_node<T>;
|
||||
|
||||
/// Dereference operator. Helper to cast this node to the contained type.
|
||||
/// \returns A pointer to the node, cast to T*.
|
||||
inline item_type & operator*() { return *this; }
|
||||
|
||||
/// Dereference operator. Helper to cast this node to the contained type.
|
||||
/// \returns A pointer to the node, cast to T*.
|
||||
inline const item_type & operator*() const { return *this; }
|
||||
|
||||
/// Cast operator. Helper to cast this node to the contained type.
|
||||
/// \returns A reference to the node, cast to T&.
|
||||
inline operator item_type& () { return *this; }
|
||||
|
||||
/// Cast operator. Helper to cast this node to the contained type.
|
||||
/// \returns A reference to the node, cast to const T&.
|
||||
inline operator const item_type& () { return *this; }
|
||||
|
||||
/// Accessor for the next pointer.
|
||||
/// \returns The next node in the list
|
||||
inline node_type * next() { return m_next; }
|
||||
|
||||
/// Accessor for the next pointer.
|
||||
/// \returns The next node in the list
|
||||
inline const node_type * next() const { return m_next; }
|
||||
|
||||
/// Accessor for the prev pointer.
|
||||
/// \returns The prev node in the list
|
||||
inline node_type * prev() { return m_prev; }
|
||||
|
||||
/// Accessor for the prev pointer.
|
||||
/// \returns The prev node in the list
|
||||
inline const node_type * prev() const { return m_prev; }
|
||||
|
||||
private:
|
||||
friend class linked_list<T>;
|
||||
|
||||
/// Insert an item after this one in the list.
|
||||
/// \arg item The item to insert
|
||||
void insert_after(node_type *item)
|
||||
{
|
||||
if (m_next) m_next->m_prev = item;
|
||||
item->m_next = m_next;
|
||||
item->m_prev = this;
|
||||
m_next = item;
|
||||
}
|
||||
|
||||
/// Insert an item before this one in the list.
|
||||
/// \arg item The item to insert
|
||||
void insert_before(node_type *item)
|
||||
{
|
||||
if (m_prev) m_prev->m_next = item;
|
||||
item->m_prev = m_prev;
|
||||
item->m_next = this;
|
||||
m_prev = item;
|
||||
}
|
||||
|
||||
/// Remove this item from its list.
|
||||
void remove()
|
||||
{
|
||||
if (m_next) m_next->m_prev = m_prev;
|
||||
if (m_prev) m_prev->m_next = m_next;
|
||||
m_next = m_prev = nullptr;
|
||||
}
|
||||
|
||||
node_type *m_next;
|
||||
node_type *m_prev;
|
||||
};
|
||||
|
||||
|
||||
/// An iterator for linked lists
|
||||
template <typename T>
|
||||
class list_iterator
|
||||
{
|
||||
public:
|
||||
using item_type = list_node<T>;
|
||||
|
||||
list_iterator(item_type *item) : m_item(item) {}
|
||||
|
||||
inline item_type * operator*() { return m_item; }
|
||||
inline const item_type * operator*() const { return m_item; }
|
||||
inline list_iterator & operator++() { m_item = m_item ? m_item->next() : nullptr; return *this; }
|
||||
inline list_iterator operator++(int) { return list_iterator<T>(m_item ? m_item->next() : nullptr); }
|
||||
inline bool operator!=(const list_iterator<T> &other) { return m_item != other.m_item; }
|
||||
|
||||
private:
|
||||
item_type *m_item;
|
||||
};
|
||||
|
||||
|
||||
/// A templatized doubly-linked list container of `list_node<T>` items.
|
||||
template <typename T>
|
||||
class linked_list
|
||||
{
|
||||
public:
|
||||
using item_type = list_node<T>;
|
||||
using iterator = list_iterator<T>;
|
||||
|
||||
/// Constructor. Creates an empty list.
|
||||
linked_list() :
|
||||
m_head(nullptr),
|
||||
m_tail(nullptr),
|
||||
m_count(0)
|
||||
{}
|
||||
|
||||
/// Move constructor. Takes ownership of list elements.
|
||||
linked_list(linked_list<T> &&other) :
|
||||
m_head(other.m_head),
|
||||
m_tail(other.m_tail),
|
||||
m_count(other.m_count)
|
||||
{
|
||||
other.m_head = other.m_tail = nullptr;
|
||||
other.m_count = 0;
|
||||
}
|
||||
|
||||
/// Assignment operator. Takes ownership of list elements.
|
||||
/// Destructive towards current data!
|
||||
linked_list & operator=(linked_list &&other)
|
||||
{
|
||||
m_head = other.m_head;
|
||||
m_tail = other.m_tail;
|
||||
m_count = other.m_count;
|
||||
other.m_head = other.m_tail = nullptr;
|
||||
other.m_count = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Check if the list is empty.
|
||||
/// \returns true if the list is empty
|
||||
bool empty() const { return m_head == nullptr; }
|
||||
|
||||
/// Get the cached length of the list.
|
||||
/// \returns The number of entries in the list.
|
||||
size_t length() const { return m_count; }
|
||||
|
||||
/// Count the items in the list.
|
||||
/// \returns The number of entries in the list.
|
||||
size_t count_length()
|
||||
{
|
||||
size_t len = 0;
|
||||
for (item_type *cur = m_head; cur; cur = cur->m_next) ++len;
|
||||
m_count = len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/// Get the item at the front of the list, without removing it
|
||||
/// \returns The first item in the list
|
||||
inline item_type * front() { return m_head; }
|
||||
|
||||
/// Get the item at the back of the list, without removing it
|
||||
/// \returns The last item in the list
|
||||
inline item_type * back() { return m_tail; }
|
||||
|
||||
/// Prepend an item to the front of this list.
|
||||
/// \arg item The node to insert.
|
||||
void push_front(item_type *item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (!m_head) {
|
||||
m_head = m_tail = item;
|
||||
item->m_next = item->m_prev = nullptr;
|
||||
} else {
|
||||
m_head->m_prev = item;
|
||||
item->m_next = m_head;
|
||||
item->m_prev = nullptr;
|
||||
m_head = item;
|
||||
}
|
||||
|
||||
m_count += 1;
|
||||
}
|
||||
|
||||
/// Append an item to the end of this list.
|
||||
/// \arg item The node to append.
|
||||
void push_back(item_type *item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (!m_tail) {
|
||||
m_head = m_tail = item;
|
||||
item->m_next = item->m_prev = nullptr;
|
||||
} else {
|
||||
m_tail->m_next = item;
|
||||
item->m_prev = m_tail;
|
||||
item->m_next = nullptr;
|
||||
m_tail = item;
|
||||
}
|
||||
|
||||
m_count += 1;
|
||||
}
|
||||
|
||||
/// Remove an item from the front of this list.
|
||||
/// \returns The node that was removed
|
||||
item_type * pop_front()
|
||||
{
|
||||
item_type *item = m_head;
|
||||
remove(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
/// Remove an item from the end of this list.
|
||||
/// \returns The node that was removed
|
||||
item_type * pop_back()
|
||||
{
|
||||
item_type *item = m_tail;
|
||||
remove(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
/// Append the contents of another list to the end of this list. The other
|
||||
/// list is emptied, and this list takes ownership of its items.
|
||||
/// \arg list The other list.
|
||||
void append(linked_list<T> &list)
|
||||
{
|
||||
if (!list.m_head) return;
|
||||
|
||||
if (!m_tail) {
|
||||
m_head = list.m_head;
|
||||
m_tail = list.m_tail;
|
||||
} else {
|
||||
m_tail->m_next = list.m_head;
|
||||
m_tail = list.m_tail;
|
||||
}
|
||||
|
||||
m_count += list.m_count;
|
||||
list.m_count = 0;
|
||||
list.m_head = list.m_tail = nullptr;
|
||||
}
|
||||
|
||||
/// Append the contents of another list to the end of this list. The other
|
||||
/// list is emptied, and this list takes ownership of its items.
|
||||
/// \arg list The other list.
|
||||
void append(linked_list<T> &&list)
|
||||
{
|
||||
if (!list.m_head) return;
|
||||
|
||||
if (!m_tail) {
|
||||
m_head = list.m_head;
|
||||
m_tail = list.m_tail;
|
||||
} else {
|
||||
m_tail->m_next = list.m_head;
|
||||
m_tail = list.m_tail;
|
||||
}
|
||||
|
||||
m_count += list.m_count;
|
||||
list.m_count = 0;
|
||||
list.m_head = list.m_tail = nullptr;
|
||||
}
|
||||
|
||||
/// Remove an item from the list.
|
||||
/// \arg item The item to remove
|
||||
void remove(item_type *item)
|
||||
{
|
||||
if (!item) return;
|
||||
if (item == m_head)
|
||||
m_head = item->m_next;
|
||||
if (item == m_tail)
|
||||
m_tail = item->m_prev;
|
||||
item->remove();
|
||||
m_count -= 1;
|
||||
}
|
||||
|
||||
/// Inserts an item into the list before another given item.
|
||||
/// \arg existing The existing item to insert before
|
||||
/// \arg item The new item to insert
|
||||
void insert_before(item_type *existing, item_type *item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
if (!existing) {
|
||||
push_back(item);
|
||||
} else if (existing == m_head) {
|
||||
push_front(item);
|
||||
} else {
|
||||
existing->insert_before(item);
|
||||
m_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts an item into the list after another given item.
|
||||
/// \arg existing The existing item to insert after
|
||||
/// \arg item The new item to insert
|
||||
void insert_after(item_type *existing, item_type *item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
if (!existing) {
|
||||
push_front(item);
|
||||
} else if (existing == m_tail) {
|
||||
push_back(item);
|
||||
} else {
|
||||
existing->insert_after(item);
|
||||
m_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert an item into the list in a sorted position. Depends on T
|
||||
/// having a method `int compare(const T *other)`.
|
||||
/// \arg item The item to insert
|
||||
void sorted_insert(item_type *item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
item_type *cur = m_head;
|
||||
while (cur && item->compare(*cur) > 0)
|
||||
cur = cur->m_next;
|
||||
|
||||
insert_before(cur, item);
|
||||
}
|
||||
|
||||
/// Range-based for iterator generator.
|
||||
/// \returns An iterator to the beginning of the list
|
||||
inline iterator begin() { return iterator(m_head); }
|
||||
|
||||
/// Range-based for iterator generator.
|
||||
/// \returns A const iterator to the beginning of the list
|
||||
inline const iterator begin() const { return iterator(m_head); }
|
||||
|
||||
/// Range-based for end-iterator generator.
|
||||
/// \returns An iterator to the end of the list
|
||||
inline const iterator end() const { return iterator(nullptr); }
|
||||
|
||||
private:
|
||||
item_type *m_head;
|
||||
item_type *m_tail;
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
|
||||
} // namespace util
|
||||
294
src/libraries/util/include/util/map.h
Normal file
294
src/libraries/util/include/util/map.h
Normal file
@@ -0,0 +1,294 @@
|
||||
#pragma once
|
||||
/// \file map.h
|
||||
/// Definition of a simple associative array collection for use in kernel space.
|
||||
/// Thanks to the following people for inspiration of this implementation:
|
||||
///
|
||||
/// Sebastian Sylvan
|
||||
/// https://www.sebastiansylvan.com/post/robin-hood-hashing-should-be-your-default-hash-table-implementation/
|
||||
///
|
||||
/// Emmanuel Goossaert
|
||||
/// http://codecapsule.com/2013/11/11/robin-hood-hashing/
|
||||
/// http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
|
||||
|
||||
#include <new>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <util/hash.h>
|
||||
#include <util/vector.h>
|
||||
#include <util/util.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
/// Templated equality check to allow overriding
|
||||
template <typename T>
|
||||
inline bool equal(const T &a, const T &b) { return a == b; }
|
||||
|
||||
template <>
|
||||
inline bool equal<const char *>(const char * const &a, const char * const &b) {
|
||||
if (!a || !b) return a == b;
|
||||
const char *a1 = a, *b1 = b;
|
||||
while (*a1 && *b1) if (*a1++ != *b1++) return false;
|
||||
return *a1 == *b1; // Make sure they're both zero
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
struct hash_node
|
||||
{
|
||||
uint64_t h {0};
|
||||
K key;
|
||||
V val;
|
||||
|
||||
hash_node(hash_node &&o) : h(o.h), key(std::move(o.key)), val(std::move(o.val)) {}
|
||||
hash_node(uint64_t h, K &&k, V &&v) : h(h), key(std::move(k)), val(std::move(v)) {}
|
||||
~hash_node() { h = 0; }
|
||||
|
||||
inline uint64_t & hash() { return h; }
|
||||
inline uint64_t hash() const { return h; }
|
||||
};
|
||||
|
||||
/// Base class for hash maps
|
||||
template <typename K, typename V>
|
||||
class base_map
|
||||
{
|
||||
protected:
|
||||
using node = hash_node<K, V>;
|
||||
|
||||
public:
|
||||
static constexpr size_t min_capacity = 8;
|
||||
static constexpr size_t max_load = 90;
|
||||
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
inline node & operator*() { return *m_node; }
|
||||
inline node * operator->() { return m_node; }
|
||||
inline const node & operator*() const { return *m_node; }
|
||||
inline iterator & operator++() { incr(); return *this; }
|
||||
inline iterator operator++(int) { node *old = m_node; incr(); return iterator(old); }
|
||||
inline bool operator!=(const iterator &o) { return m_node != o.m_node; }
|
||||
private:
|
||||
friend class base_map;
|
||||
iterator(node *n) : m_node(n), m_end(n) {}
|
||||
iterator(node *n, node *end) : m_node(n), m_end(end) {}
|
||||
void incr() { while (m_node < m_end) { ++m_node; if (m_node->hash()) break; } }
|
||||
node *m_node;
|
||||
node *m_end;
|
||||
};
|
||||
|
||||
/// Default constructor. Creates an empty map with the given capacity.
|
||||
base_map(size_t capacity = 0) :
|
||||
m_count(0),
|
||||
m_capacity(0),
|
||||
m_nodes(nullptr)
|
||||
{
|
||||
if (capacity)
|
||||
set_capacity(1 << log2(capacity));
|
||||
}
|
||||
|
||||
virtual ~base_map() {
|
||||
for (size_t i = 0; i < m_capacity; ++i)
|
||||
m_nodes[i].~node();
|
||||
delete [] reinterpret_cast<uint8_t*>(m_nodes);
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
if (!m_count) return iterator {0};
|
||||
iterator it {m_nodes - 1, m_nodes + m_capacity};
|
||||
return ++it;
|
||||
}
|
||||
|
||||
const iterator begin() const {
|
||||
if (!m_count) return iterator {0};
|
||||
iterator it {m_nodes - 1, m_nodes + m_capacity};
|
||||
return ++it;
|
||||
}
|
||||
|
||||
const iterator end() const {
|
||||
if (!m_count) return iterator {0};
|
||||
return iterator(m_nodes + m_capacity);
|
||||
}
|
||||
|
||||
void insert(K k, V v) {
|
||||
if (++m_count > threshold()) grow();
|
||||
insert_node(hash(k), std::move(k), std::move(v));
|
||||
}
|
||||
|
||||
bool erase(const K &k)
|
||||
{
|
||||
node *n = lookup(k);
|
||||
if (!n) return false;
|
||||
|
||||
n->~node();
|
||||
--m_count;
|
||||
|
||||
size_t i = n - m_nodes;
|
||||
while (true) {
|
||||
size_t next = mod(i+1);
|
||||
node &m = m_nodes[next];
|
||||
if (!m.hash() || mod(m.hash()) == next) break;
|
||||
construct(i, m.hash(), std::move(m.key), std::move(m.val));
|
||||
m.~node();
|
||||
i = mod(++i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t count() const { return m_count; }
|
||||
inline size_t capacity() const { return m_capacity; }
|
||||
inline size_t threshold() const { return (m_capacity * max_load) / 100; }
|
||||
|
||||
protected:
|
||||
inline size_t mod(uint64_t i) const { return i & (m_capacity - 1); }
|
||||
inline size_t offset(uint64_t h, size_t i) const {
|
||||
return mod(i + m_capacity - mod(h));
|
||||
}
|
||||
|
||||
void set_capacity(size_t capacity) {
|
||||
assert((capacity & (capacity - 1)) == 0 &&
|
||||
"Map capacity must be a power of two");
|
||||
|
||||
m_capacity = capacity;
|
||||
const size_t size = m_capacity * sizeof(node);
|
||||
m_nodes = reinterpret_cast<node*>(new uint8_t [size]);
|
||||
memset(m_nodes, 0, size);
|
||||
}
|
||||
|
||||
void grow() {
|
||||
node *old = m_nodes;
|
||||
size_t count = m_capacity;
|
||||
|
||||
size_t cap = m_capacity * 2;
|
||||
if (cap < min_capacity)
|
||||
cap = min_capacity;
|
||||
|
||||
set_capacity(cap);
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
node &n = old[i];
|
||||
insert_node(n.hash(), std::move(n.key), std::move(n.val));
|
||||
n.~node();
|
||||
}
|
||||
|
||||
delete [] reinterpret_cast<uint8_t*>(old);
|
||||
}
|
||||
|
||||
inline node * construct(size_t i, uint64_t h, K &&k, V &&v) {
|
||||
return new (&m_nodes[i]) node(h, std::move(k), std::move(v));
|
||||
}
|
||||
|
||||
node * insert_node(uint64_t h, K &&k, V &&v) {
|
||||
size_t i = mod(h);
|
||||
size_t dist = 0;
|
||||
|
||||
while (true) {
|
||||
if (!m_nodes[i].hash()) {
|
||||
return construct(i, h, std::move(k), std::move(v));
|
||||
}
|
||||
|
||||
node &elem = m_nodes[i];
|
||||
size_t elem_dist = offset(elem.hash(), i);
|
||||
if (elem_dist < dist) {
|
||||
std::swap(h, elem.hash());
|
||||
std::swap(k, elem.key);
|
||||
std::swap(v, elem.val);
|
||||
dist = elem_dist;
|
||||
}
|
||||
|
||||
i = mod(++i);
|
||||
++dist;
|
||||
}
|
||||
}
|
||||
|
||||
node * lookup(const K &k) {
|
||||
if (!m_count)
|
||||
return nullptr;
|
||||
|
||||
uint64_t h = hash(k);
|
||||
size_t i = mod(h);
|
||||
size_t dist = 0;
|
||||
|
||||
while (true) {
|
||||
node &n = m_nodes[i];
|
||||
if (!n.hash() || dist > offset(n.hash(), i))
|
||||
return nullptr;
|
||||
|
||||
else if (n.hash() == h && equal(n.key, k))
|
||||
return &n;
|
||||
|
||||
i = mod(++i);
|
||||
++dist;
|
||||
}
|
||||
}
|
||||
|
||||
const node * lookup(const K &k) const {
|
||||
if (!m_count)
|
||||
return nullptr;
|
||||
|
||||
uint64_t h = hash(k);
|
||||
size_t i = mod(h);
|
||||
size_t dist = 0;
|
||||
|
||||
while (true) {
|
||||
const node &n = m_nodes[i];
|
||||
if (!n.hash() || dist > offset(n.hash(), i))
|
||||
return nullptr;
|
||||
|
||||
else if (n.hash() == h && equal(n.key, k))
|
||||
return &n;
|
||||
|
||||
i = mod(++i);
|
||||
++dist;
|
||||
}
|
||||
}
|
||||
|
||||
size_t m_count;
|
||||
size_t m_capacity;
|
||||
node *m_nodes;
|
||||
};
|
||||
|
||||
/// An open addressing hash map using robinhood hashing.
|
||||
template <typename K, typename V>
|
||||
class map :
|
||||
public base_map<K, V>
|
||||
{
|
||||
using base = base_map<K, V>;
|
||||
using node = typename base::node;
|
||||
|
||||
public:
|
||||
map(size_t capacity = 0) :
|
||||
base(capacity) {}
|
||||
|
||||
V * find(const K &k) {
|
||||
node *n = this->lookup(k);
|
||||
return n ? &n->val : nullptr;
|
||||
}
|
||||
|
||||
const V * find(const K &k) const {
|
||||
const node *n = this->lookup(k);
|
||||
return n ? &n->val : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// An open addressing hash map using robinhood hashing. Specialization
|
||||
/// for storing pointers: don't return a pointer to a pointer.
|
||||
template <typename K, typename V>
|
||||
class map <K, V*> :
|
||||
public base_map<K, V*>
|
||||
{
|
||||
using base = base_map<K, V*>;
|
||||
using node = typename base::node;
|
||||
|
||||
public:
|
||||
map(size_t capacity = 0) :
|
||||
base(capacity) {}
|
||||
|
||||
V * find(const K &k) const {
|
||||
const node *n = this->lookup(k);
|
||||
return n ? n->val : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
12
src/libraries/util/include/util/misc.h
Normal file
12
src/libraries/util/include/util/misc.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
namespace util {
|
||||
|
||||
constexpr uint32_t
|
||||
byteswap(uint32_t x)
|
||||
{
|
||||
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00)
|
||||
| ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
|
||||
}
|
||||
|
||||
}
|
||||
16
src/libraries/util/include/util/no_construct.h
Normal file
16
src/libraries/util/include/util/no_construct.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
/// \file no_construct.h
|
||||
/// Tools for creating objects witout running constructors
|
||||
|
||||
namespace util {
|
||||
|
||||
/// Helper template for creating objects witout running constructors
|
||||
template <typename T>
|
||||
union no_construct
|
||||
{
|
||||
T value;
|
||||
no_construct() {}
|
||||
~no_construct() {}
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
46
src/libraries/util/include/util/spinlock.h
Normal file
46
src/libraries/util/include/util/spinlock.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/// \file spinlock.h
|
||||
/// Spinlock types and related defintions
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace util {
|
||||
|
||||
/// An MCS based spinlock
|
||||
class spinlock
|
||||
{
|
||||
public:
|
||||
spinlock();
|
||||
~spinlock();
|
||||
|
||||
/// A node in the wait queue.
|
||||
struct waiter
|
||||
{
|
||||
bool blocked;
|
||||
waiter *next;
|
||||
};
|
||||
|
||||
void acquire(waiter *w);
|
||||
void release(waiter *w);
|
||||
|
||||
private:
|
||||
waiter *m_lock;
|
||||
};
|
||||
|
||||
/// Scoped lock that owns a spinlock::waiter
|
||||
class scoped_lock
|
||||
{
|
||||
public:
|
||||
inline scoped_lock(spinlock &lock) : m_lock(lock) {
|
||||
m_lock.acquire(&m_waiter);
|
||||
}
|
||||
|
||||
inline ~scoped_lock() {
|
||||
m_lock.release(&m_waiter);
|
||||
}
|
||||
|
||||
private:
|
||||
spinlock &m_lock;
|
||||
spinlock::waiter m_waiter;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
16
src/libraries/util/include/util/util.h
Normal file
16
src/libraries/util/include/util/util.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
/// \file util.h
|
||||
/// Utility functions used in other util code
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
// Get the base-2 logarithm of i
|
||||
inline unsigned log2(uint64_t i) {
|
||||
if (i < 2) return 0;
|
||||
const unsigned clz = __builtin_clzll(i - 1);
|
||||
return 64 - clz;
|
||||
}
|
||||
|
||||
}
|
||||
296
src/libraries/util/include/util/vector.h
Normal file
296
src/libraries/util/include/util/vector.h
Normal file
@@ -0,0 +1,296 @@
|
||||
#pragma once
|
||||
/// \file vector.h
|
||||
/// Definition of a simple dynamic vector collection for use in kernel space
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <utility>
|
||||
|
||||
#include <util/util.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
/// A dynamic array.
|
||||
template <typename T, typename S = uint32_t>
|
||||
class vector
|
||||
{
|
||||
using count_t = S;
|
||||
static constexpr count_t min_capacity = 4;
|
||||
static constexpr count_t cap_mask = static_cast<S>(-1) >> 1;
|
||||
|
||||
public:
|
||||
/// Default constructor. Creates an empty vector with no capacity.
|
||||
vector() :
|
||||
m_size(0),
|
||||
m_capacity(0),
|
||||
m_elements(nullptr)
|
||||
{}
|
||||
|
||||
/// Constructor. Creates an empty array with capacity.
|
||||
/// \arg capacity Initial capacity to allocate
|
||||
vector(count_t capacity) :
|
||||
m_size(0),
|
||||
m_capacity(0),
|
||||
m_elements(nullptr)
|
||||
{
|
||||
set_capacity(capacity);
|
||||
}
|
||||
|
||||
/// Copy constructor. Allocates a copy of the other's array.
|
||||
vector(const vector& other) :
|
||||
m_size(0),
|
||||
m_capacity(0),
|
||||
m_elements(nullptr)
|
||||
{
|
||||
set_capacity(other.m_capacity);
|
||||
memcpy(m_elements, other.m_elements, other.m_size * sizeof(T));
|
||||
m_size = other.m_size;
|
||||
}
|
||||
|
||||
/// Move constructor. Takes ownership of the other's array.
|
||||
vector(vector &&other) :
|
||||
m_size(other.m_size),
|
||||
m_capacity(other.m_capacity),
|
||||
m_elements(other.m_elements)
|
||||
{
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
other.m_elements = nullptr;
|
||||
}
|
||||
|
||||
/// Static array constructor. Starts the vector off with the given
|
||||
/// static storage.
|
||||
vector(T *data, count_t size, count_t capacity) :
|
||||
m_size(size),
|
||||
m_capacity(capacity | ~cap_mask),
|
||||
m_elements(&data[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor. Destroys any remaining items in the array.
|
||||
~vector()
|
||||
{
|
||||
while (m_size) remove();
|
||||
|
||||
bool was_static = m_capacity & ~cap_mask;
|
||||
if (!was_static)
|
||||
delete [] m_elements;
|
||||
}
|
||||
|
||||
/// Get the size of the array.
|
||||
inline count_t count() const { return m_size; }
|
||||
|
||||
/// Get the capacity of the array. This is the amount of space
|
||||
/// actually allocated.
|
||||
inline count_t capacity() const { return m_capacity & cap_mask; }
|
||||
|
||||
/// Access an element in the array.
|
||||
inline T & operator[] (count_t i) { return m_elements[i]; }
|
||||
|
||||
/// Access an element in the array.
|
||||
inline const T & operator[] (count_t i) const { return m_elements[i]; }
|
||||
|
||||
/// Get a pointer to the beginning for iteration.
|
||||
T * begin() { return m_elements; }
|
||||
|
||||
/// Get a pointer to the beginning for iteration.
|
||||
const T * begin() const { return m_elements; }
|
||||
|
||||
/// Get a pointer to the end for iteration.
|
||||
T * end() { return m_elements + m_size; }
|
||||
|
||||
/// Get a pointer to the end for iteration.
|
||||
const T * end() const { return m_elements + m_size; }
|
||||
|
||||
/// Add an item onto the array by copying it.
|
||||
/// \arg item The item to add
|
||||
/// \returns A reference to the added item
|
||||
T & append(const T& item)
|
||||
{
|
||||
ensure_capacity(m_size + 1);
|
||||
m_elements[m_size] = item;
|
||||
return m_elements[m_size++];
|
||||
}
|
||||
|
||||
/// Construct an item in place onto the end of the array.
|
||||
/// \returns A reference to the added item
|
||||
template <typename... Args>
|
||||
T & emplace(Args&&... args)
|
||||
{
|
||||
ensure_capacity(m_size + 1);
|
||||
new (&m_elements[m_size]) T(std::forward<Args>(args)...);
|
||||
return m_elements[m_size++];
|
||||
}
|
||||
|
||||
/// Insert an item into the array at the given index
|
||||
void insert(count_t i, const T& item)
|
||||
{
|
||||
if (i >= count()) {
|
||||
append(item);
|
||||
return;
|
||||
}
|
||||
|
||||
ensure_capacity(m_size + 1);
|
||||
for (count_t j = m_size; j > i; --j)
|
||||
m_elements[j] = m_elements[j-1];
|
||||
m_size += 1;
|
||||
|
||||
m_elements[i] = item;
|
||||
}
|
||||
|
||||
/// Insert an item into the list in a sorted position. Depends on T
|
||||
/// having a method `int compare(const T &other)`.
|
||||
/// \returns index of the new item
|
||||
count_t sorted_insert(const T& item)
|
||||
{
|
||||
count_t start = 0;
|
||||
count_t end = m_size;
|
||||
while (end > start) {
|
||||
count_t m = start + (end - start) / 2;
|
||||
int c = item.compare(m_elements[m]);
|
||||
if (c < 0) end = m;
|
||||
else start = m + 1;
|
||||
}
|
||||
|
||||
insert(start, item);
|
||||
return start;
|
||||
}
|
||||
|
||||
/// Remove an item from the end of the array.
|
||||
void remove()
|
||||
{
|
||||
assert(m_size && "Called remove() on an empty array");
|
||||
|
||||
m_size -= 1;
|
||||
m_elements[m_size].~T();
|
||||
}
|
||||
|
||||
/// Remove an item from the front of the array, preserving order.
|
||||
void remove_front()
|
||||
{
|
||||
assert(m_size && "Called remove_front() on an empty array");
|
||||
remove_at(0);
|
||||
}
|
||||
|
||||
/// Remove an item from the array.
|
||||
void remove(const T &item)
|
||||
{
|
||||
assert(m_size && "Called remove() on an empty array");
|
||||
for (count_t i = 0; i < m_size; ++i) {
|
||||
if (m_elements[i] == item) {
|
||||
remove_at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove n items starting at the given index from the array,
|
||||
/// order-preserving.
|
||||
void remove_at(count_t i, count_t n = 1)
|
||||
{
|
||||
for (count_t j = i; j < i + n; ++j) {
|
||||
if (j >= m_size) return;
|
||||
m_elements[j].~T();
|
||||
}
|
||||
|
||||
for (; i < m_size - n; ++i)
|
||||
m_elements[i] = m_elements[i+n];
|
||||
m_size -= n;
|
||||
}
|
||||
|
||||
/// Remove the first occurance of an item from the array, not
|
||||
/// order-preserving. Does nothing if the item is not in the array.
|
||||
void remove_swap(const T &item)
|
||||
{
|
||||
for (count_t i = 0; i < m_size; ++i) {
|
||||
if (m_elements[i] == item) {
|
||||
remove_swap_at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the item at the given index from the array, not
|
||||
/// order-preserving.
|
||||
void remove_swap_at(count_t i)
|
||||
{
|
||||
if (i >= count()) return;
|
||||
|
||||
m_elements[i].~T();
|
||||
if (i < m_size - 1)
|
||||
m_elements[i] = m_elements[m_size - 1];
|
||||
m_size -= 1;
|
||||
}
|
||||
|
||||
/// Remove an item from the end of the array and return it.
|
||||
T pop()
|
||||
{
|
||||
assert(m_size && "Called pop() on an empty array");
|
||||
|
||||
T temp = m_elements[m_size - 1];
|
||||
remove();
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// Remove an item from the beginning of the array and return it.
|
||||
T pop_front()
|
||||
{
|
||||
assert(m_size && "Called pop_front() on an empty array");
|
||||
|
||||
T temp = m_elements[0];
|
||||
remove_front();
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// Set the size of the array. Any new items are default constructed.
|
||||
/// Any items past the end are deleted. The array is realloced if needed.
|
||||
/// \arg size The new size
|
||||
void set_size(count_t size)
|
||||
{
|
||||
ensure_capacity(size);
|
||||
for (count_t i = size; i < m_size; ++i)
|
||||
m_elements[i].~T();
|
||||
for (count_t i = m_size; i < size; ++i)
|
||||
new (&m_elements[i]) T;
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
/// Ensure the array will fit an item.
|
||||
/// \arg size Size of the array
|
||||
void ensure_capacity(count_t size)
|
||||
{
|
||||
if (capacity() >= size) return;
|
||||
count_t capacity = (1 << log2(size));
|
||||
if (capacity < min_capacity)
|
||||
capacity = min_capacity;
|
||||
set_capacity(capacity);
|
||||
}
|
||||
|
||||
/// Reallocate the array. Copy over any old elements that will
|
||||
/// fit into the new array. The rest are destroyed.
|
||||
/// \arg capacity Number of elements to allocate
|
||||
void set_capacity(count_t capacity)
|
||||
{
|
||||
bool was_static = m_capacity & ~cap_mask;
|
||||
T *new_array = reinterpret_cast<T*>(new uint8_t [capacity * sizeof(T)]);
|
||||
count_t size = capacity > m_size ? m_size : capacity;
|
||||
|
||||
memcpy(new_array, m_elements, size * sizeof(T));
|
||||
|
||||
while (size < m_size) remove();
|
||||
m_size = size;
|
||||
m_capacity = capacity;
|
||||
|
||||
if (!was_static)
|
||||
delete [] m_elements;
|
||||
|
||||
m_elements = new_array;
|
||||
}
|
||||
|
||||
private:
|
||||
count_t m_size;
|
||||
count_t m_capacity;
|
||||
T *m_elements;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
46
src/libraries/util/spinlock.cpp
Normal file
46
src/libraries/util/spinlock.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <util/spinlock.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
static constexpr int memorder = __ATOMIC_SEQ_CST;
|
||||
|
||||
spinlock::spinlock() : m_lock {nullptr} {}
|
||||
spinlock::~spinlock() {}
|
||||
|
||||
void
|
||||
spinlock::acquire(waiter *w)
|
||||
{
|
||||
w->next = nullptr;
|
||||
w->blocked = true;
|
||||
|
||||
// Point the lock at this waiter
|
||||
waiter *prev = __atomic_exchange_n(&m_lock, w, memorder);
|
||||
if (prev) {
|
||||
// If there was a previous waiter, wait for them to
|
||||
// unblock us
|
||||
prev->next = w;
|
||||
while (w->blocked)
|
||||
asm ("pause");
|
||||
} else {
|
||||
w->blocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
spinlock::release(waiter *w)
|
||||
{
|
||||
// If we're still the last waiter, we're done
|
||||
waiter *expected = w;
|
||||
if(__atomic_compare_exchange_n(&m_lock, &expected, nullptr, false, memorder, memorder))
|
||||
return;
|
||||
|
||||
// Wait for the subseqent waiter to tell us who they are
|
||||
while (!w->next)
|
||||
asm ("pause");
|
||||
|
||||
// Unblock the subseqent waiter
|
||||
w->next->blocked = false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace util
|
||||
9
src/libraries/util/util.module
Normal file
9
src/libraries/util/util.module
Normal file
@@ -0,0 +1,9 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("util",
|
||||
kind = "lib",
|
||||
includes = [ "include" ],
|
||||
sources = [
|
||||
"bip_buffer.cpp",
|
||||
"spinlock.cpp",
|
||||
])
|
||||
Reference in New Issue
Block a user