[project] Lose the battle between tabs & spaces

I'm a tabs guy. I like tabs, it's an elegant way to represent
indentation instead of brute-forcing it. But I have to admit that the
world seems to be going towards spaces, and tooling tends not to play
nice with tabs. So here we go, changing the whole repo to spaces since
I'm getting tired of all the inconsistent formatting.
This commit is contained in:
F in Chat for Tabs
2021-08-01 17:46:16 -07:00
committed by Justin C. Miller
parent d36b2d8057
commit 8f529046a9
161 changed files with 7958 additions and 7958 deletions

View File

@@ -5,71 +5,71 @@ namespace cpu {
inline static void
__cpuid(
uint32_t leaf,
uint32_t subleaf,
uint32_t *eax,
uint32_t *ebx = nullptr,
uint32_t *ecx = nullptr,
uint32_t *edx = nullptr)
uint32_t leaf,
uint32_t subleaf,
uint32_t *eax,
uint32_t *ebx = nullptr,
uint32_t *ecx = nullptr,
uint32_t *edx = nullptr)
{
uint32_t a, b, c, d;
__asm__ __volatile__ ( "cpuid"
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
: "a"(leaf), "c"(subleaf)
);
if (eax) *eax = a;
if (ebx) *ebx = b;
if (ecx) *ecx = c;
if (edx) *edx = d;
uint32_t a, b, c, d;
__asm__ __volatile__ ( "cpuid"
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
: "a"(leaf), "c"(subleaf)
);
if (eax) *eax = a;
if (ebx) *ebx = b;
if (ecx) *ecx = c;
if (edx) *edx = d;
}
cpu_id::cpu_id() :
m_features {0},
m_missing {0}
m_features {0},
m_missing {0}
{
__cpuid(0, 0,
&m_high_basic,
reinterpret_cast<uint32_t *>(&m_vendor_id[0]),
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
__cpuid(0, 0,
&m_high_basic,
reinterpret_cast<uint32_t *>(&m_vendor_id[0]),
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
__cpuid(cpuid_extended, 0, &m_high_ext);
__cpuid(cpuid_extended, 0, &m_high_ext);
if (m_high_ext >= cpuid_extended + 4) {
__cpuid(cpuid_extended + 2, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[0]),
reinterpret_cast<uint32_t *>(&m_brand_name[4]),
reinterpret_cast<uint32_t *>(&m_brand_name[8]),
reinterpret_cast<uint32_t *>(&m_brand_name[12]));
__cpuid(cpuid_extended + 3, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[16]),
reinterpret_cast<uint32_t *>(&m_brand_name[20]),
reinterpret_cast<uint32_t *>(&m_brand_name[24]),
reinterpret_cast<uint32_t *>(&m_brand_name[28]));
__cpuid(cpuid_extended + 4, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[32]),
reinterpret_cast<uint32_t *>(&m_brand_name[36]),
reinterpret_cast<uint32_t *>(&m_brand_name[40]),
reinterpret_cast<uint32_t *>(&m_brand_name[44]));
} else {
m_brand_name[0] = 0;
}
if (m_high_ext >= cpuid_extended + 4) {
__cpuid(cpuid_extended + 2, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[0]),
reinterpret_cast<uint32_t *>(&m_brand_name[4]),
reinterpret_cast<uint32_t *>(&m_brand_name[8]),
reinterpret_cast<uint32_t *>(&m_brand_name[12]));
__cpuid(cpuid_extended + 3, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[16]),
reinterpret_cast<uint32_t *>(&m_brand_name[20]),
reinterpret_cast<uint32_t *>(&m_brand_name[24]),
reinterpret_cast<uint32_t *>(&m_brand_name[28]));
__cpuid(cpuid_extended + 4, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[32]),
reinterpret_cast<uint32_t *>(&m_brand_name[36]),
reinterpret_cast<uint32_t *>(&m_brand_name[40]),
reinterpret_cast<uint32_t *>(&m_brand_name[44]));
} else {
m_brand_name[0] = 0;
}
uint32_t leaf = -1u;
uint32_t sub = -1u;
regs r;
uint32_t leaf = -1u;
uint32_t sub = -1u;
regs r;
#define CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit) \
if (leaf != feat_leaf || sub != feat_sub) { \
leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \
} \
if (r.regname & (1ull << bit)) \
m_features |= (1ull << static_cast<uint64_t>(feature::name)); \
if (leaf != feat_leaf || sub != feat_sub) { \
leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \
} \
if (r.regname & (1ull << bit)) \
m_features |= (1ull << static_cast<uint64_t>(feature::name)); \
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
if ((r.regname & (1ull << bit)) == 0) { \
m_missing |= (1ull << static_cast<uint64_t>(feature::name)); \
}
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
if ((r.regname & (1ull << bit)) == 0) { \
m_missing |= (1ull << static_cast<uint64_t>(feature::name)); \
}
#include "cpu/features.inc"
#undef CPU_FEATURE_OPT
@@ -79,28 +79,28 @@ cpu_id::cpu_id() :
cpu_id::regs
cpu_id::get(uint32_t leaf, uint32_t sub) const
{
regs ret {0, 0, 0, 0};
regs ret {0, 0, 0, 0};
if ((leaf & cpuid_extended) == 0 && leaf > m_high_basic) return ret;
if ((leaf & cpuid_extended) != 0 && leaf > m_high_ext) return ret;
if ((leaf & cpuid_extended) == 0 && leaf > m_high_basic) return ret;
if ((leaf & cpuid_extended) != 0 && leaf > m_high_ext) return ret;
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
return ret;
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
return ret;
}
bool
cpu_id::has_feature(feature feat)
{
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
}
uint8_t
cpu_id::local_apic_id() const
{
uint32_t eax_unused;
uint32_t ebx;
__cpuid(1, 0, &eax_unused, &ebx);
return static_cast<uint8_t>(ebx >> 24);
uint32_t eax_unused;
uint32_t ebx;
__cpuid(1, 0, &eax_unused, &ebx);
return static_cast<uint8_t>(ebx >> 24);
}
}

View File

@@ -12,73 +12,73 @@ enum class feature {
#include "cpu/features.inc"
#undef CPU_FEATURE_OPT
#undef CPU_FEATURE_REQ
max
max
};
class cpu_id
{
public:
static constexpr uint32_t cpuid_extended = 0x80000000;
static constexpr uint32_t cpuid_extended = 0x80000000;
/// CPUID result register values
struct regs {
union {
uint32_t reg[4];
uint32_t eax, ebx, ecx, edx;
};
/// CPUID result register values
struct regs {
union {
uint32_t reg[4];
uint32_t eax, ebx, ecx, edx;
};
/// Return true if bit |bit| of EAX is set
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
/// Return true if bit |bit| of EAX is set
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
/// Return true if bit |bit| of EBX is set
bool ebx_bit(unsigned bit) { return (ebx >> bit) & 0x1; }
/// Return true if bit |bit| of EBX is set
bool ebx_bit(unsigned bit) { return (ebx >> bit) & 0x1; }
/// Return true if bit |bit| of ECX is set
bool ecx_bit(unsigned bit) { return (ecx >> bit) & 0x1; }
/// Return true if bit |bit| of ECX is set
bool ecx_bit(unsigned bit) { return (ecx >> bit) & 0x1; }
/// Return true if bit |bit| of EDX is set
bool edx_bit(unsigned bit) { return (edx >> bit) & 0x1; }
};
/// Return true if bit |bit| of EDX is set
bool edx_bit(unsigned bit) { return (edx >> bit) & 0x1; }
};
cpu_id();
cpu_id();
/// The the result of a given CPUID leaf/subleaf
/// \arg leaf The leaf selector (initial EAX)
/// \arg subleaf The subleaf selector (initial ECX)
/// \returns A |regs| struct of the values retuned
regs get(uint32_t leaf, uint32_t sub = 0) const;
/// The the result of a given CPUID leaf/subleaf
/// \arg leaf The leaf selector (initial EAX)
/// \arg subleaf The subleaf selector (initial ECX)
/// \returns A |regs| struct of the values retuned
regs get(uint32_t leaf, uint32_t sub = 0) const;
/// Get the local APIC ID of the current CPU
uint8_t local_apic_id() const;
/// Get the local APIC ID of the current CPU
uint8_t local_apic_id() const;
/// Get the name of the cpu vendor (eg, "GenuineIntel")
inline const char * vendor_id() const { return m_vendor_id; }
/// Get the name of the cpu vendor (eg, "GenuineIntel")
inline const char * vendor_id() const { return m_vendor_id; }
/// Get the brand name of this processor model
inline const char * brand_name() const { return m_brand_name; }
/// Get the brand name of this processor model
inline const char * brand_name() const { return m_brand_name; }
/// Get the highest basic CPUID leaf supported
inline uint32_t highest_basic() const { return m_high_basic; }
/// Get the highest basic CPUID leaf supported
inline uint32_t highest_basic() const { return m_high_basic; }
/// Get the highest extended CPUID leaf supported
inline uint32_t highest_ext() const { return m_high_ext; }
/// Get the highest extended CPUID leaf supported
inline uint32_t highest_ext() const { return m_high_ext; }
/// Get which required options are missing as flags
inline uint64_t missing() const { return m_missing; }
/// Get which required options are missing as flags
inline uint64_t missing() const { return m_missing; }
/// Validate the CPU supports the necessary options for jsix
inline bool supported() const { return m_missing; }
/// Validate the CPU supports the necessary options for jsix
inline bool supported() const { return m_missing; }
/// Return true if the CPU claims to support the given feature
bool has_feature(feature feat);
/// Return true if the CPU claims to support the given feature
bool has_feature(feature feat);
private:
uint32_t m_high_basic;
uint32_t m_high_ext;
uint64_t m_features;
uint64_t m_missing;
char m_vendor_id[13];
char m_brand_name[48];
uint32_t m_high_basic;
uint32_t m_high_ext;
uint64_t m_features;
uint64_t m_missing;
char m_vendor_id[13];
char m_brand_name[48];
};
}

View File

@@ -9,36 +9,36 @@ namespace elf {
inline const file_header * fh(const void *data) { return reinterpret_cast<const file_header*>(data); }
file::file(const void *data, size_t size) :
m_programs(offset_ptr<program_header>(data, fh(data)->ph_offset), fh(data)->ph_entsize, fh(data)->ph_num),
m_sections(offset_ptr<section_header>(data, fh(data)->sh_offset), fh(data)->sh_entsize, fh(data)->sh_num),
m_data(data),
m_size(size)
m_programs(offset_ptr<program_header>(data, fh(data)->ph_offset), fh(data)->ph_entsize, fh(data)->ph_num),
m_sections(offset_ptr<section_header>(data, fh(data)->sh_offset), fh(data)->sh_entsize, fh(data)->sh_num),
m_data(data),
m_size(size)
{
}
bool
file::valid() const
{
if (m_size < sizeof(file_header))
return false;
if (m_size < sizeof(file_header))
return false;
const file_header *fheader = header();
const file_header *fheader = header();
return
fheader->magic == expected_magic &&
fheader->word_size == wordsize::bits64 &&
fheader->endianness == encoding::lsb &&
fheader->os_abi == osabi::sysV &&
fheader->file_type == filetype::executable &&
fheader->machine_type == machine::x64 &&
fheader->ident_version == 1 &&
fheader->version == 1;
return
fheader->magic == expected_magic &&
fheader->word_size == wordsize::bits64 &&
fheader->endianness == encoding::lsb &&
fheader->os_abi == osabi::sysV &&
fheader->file_type == filetype::executable &&
fheader->machine_type == machine::x64 &&
fheader->ident_version == 1 &&
fheader->version == 1;
}
uintptr_t
file::entrypoint() const
{
return static_cast<uintptr_t>(header()->entrypoint);
return static_cast<uintptr_t>(header()->entrypoint);
}

View File

@@ -14,62 +14,62 @@ template <typename T>
class subheaders
{
public:
using iterator = const_offset_iterator<T>;
using iterator = const_offset_iterator<T>;
subheaders(const T *start, size_t size, unsigned count) :
m_start(start), m_size(size), m_count(count) {}
subheaders(const T *start, size_t size, unsigned count) :
m_start(start), m_size(size), m_count(count) {}
inline size_t size() const { return m_size; }
inline unsigned count() const { return m_count; }
inline size_t size() const { return m_size; }
inline unsigned count() const { return m_count; }
inline const T & operator [] (int i) const { return *offset_ptr<T>(m_start, m_size*i); }
inline const iterator begin() const { return iterator(m_start, m_size); }
inline const iterator end() const { return offset_ptr<T>(m_start, m_size*m_count); }
inline const T & operator [] (int i) const { return *offset_ptr<T>(m_start, m_size*i); }
inline const iterator begin() const { return iterator(m_start, m_size); }
inline const iterator end() const { return offset_ptr<T>(m_start, m_size*m_count); }
private:
const T *m_start;
size_t m_size;
unsigned m_count;
const T *m_start;
size_t m_size;
unsigned m_count;
};
/// Represents a full ELF file's data
class file
{
public:
/// Constructor: Create an elf object out of ELF data in memory
/// \arg data The ELF data to read
/// \arg size Size of the ELF data, in bytes
file(const void *data, size_t size);
/// Constructor: Create an elf object out of ELF data in memory
/// \arg data The ELF data to read
/// \arg size Size of the ELF data, in bytes
file(const void *data, size_t size);
/// Check the validity of the ELF data
/// \returns true for valid ELF data
bool valid() const;
/// Check the validity of the ELF data
/// \returns true for valid ELF data
bool valid() const;
/// Get the entrypoint address of the program image
/// \returns A pointer to the entrypoint of the program
uintptr_t entrypoint() const;
/// Get the entrypoint address of the program image
/// \returns A pointer to the entrypoint of the program
uintptr_t entrypoint() const;
/// Get the base address of the program in memory
inline uintptr_t base() const {
return reinterpret_cast<uintptr_t>(m_data);
}
/// Get the base address of the program in memory
inline uintptr_t base() const {
return reinterpret_cast<uintptr_t>(m_data);
}
/// Get the ELF program headers
inline const subheaders<program_header> & programs() const { return m_programs; }
/// Get the ELF program headers
inline const subheaders<program_header> & programs() const { return m_programs; }
/// Get the ELF section headers
inline const subheaders<section_header> & sections() const { return m_sections; }
/// Get the ELF section headers
inline const subheaders<section_header> & sections() const { return m_sections; }
inline const file_header * header() const {
return reinterpret_cast<const file_header *>(m_data);
}
inline const file_header * header() const {
return reinterpret_cast<const file_header *>(m_data);
}
private:
subheaders<program_header> m_programs;
subheaders<section_header> m_sections;
subheaders<program_header> m_programs;
subheaders<section_header> m_sections;
const void *m_data;
size_t m_size;
const void *m_data;
size_t m_size;
};
}

View File

@@ -11,45 +11,45 @@ enum class machine : uint16_t { none, x64 = 0x3e };
enum class filetype : uint16_t
{
none,
relocatable,
executable,
shared,
core
none,
relocatable,
executable,
shared,
core
};
struct file_header
{
uint32_t magic;
uint32_t magic;
wordsize word_size;
encoding endianness;
uint8_t ident_version;
osabi os_abi;
wordsize word_size;
encoding endianness;
uint8_t ident_version;
osabi os_abi;
uint64_t reserved;
uint64_t reserved;
filetype file_type;
machine machine_type;
filetype file_type;
machine machine_type;
uint32_t version;
uint32_t version;
uint64_t entrypoint;
uint64_t ph_offset;
uint64_t sh_offset;
uint64_t entrypoint;
uint64_t ph_offset;
uint64_t sh_offset;
uint32_t flags;
uint32_t flags;
uint16_t eh_size;
uint16_t eh_size;
uint16_t ph_entsize;
uint16_t ph_num;
uint16_t ph_entsize;
uint16_t ph_num;
uint16_t sh_entsize;
uint16_t sh_num;
uint16_t sh_entsize;
uint16_t sh_num;
uint16_t sh_str_idx;
uint16_t sh_str_idx;
} __attribute__ ((packed));
@@ -57,40 +57,40 @@ enum class segment_type : uint32_t { null, load, dynamic, interpreter, note };
struct program_header
{
segment_type type;
uint32_t flags;
uint64_t offset;
segment_type type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t vaddr;
uint64_t paddr;
uint64_t file_size;
uint64_t mem_size;
uint64_t file_size;
uint64_t mem_size;
uint64_t align;
uint64_t align;
} __attribute__ ((packed));
enum class section_type : uint32_t { null, progbits };
enum class section_flags : uint64_t
{
write = 0x01,
alloc = 0x02,
exec = 0x04,
write = 0x01,
alloc = 0x02,
exec = 0x04,
};
struct section_header
{
uint32_t name_offset;
section_type type;
section_flags flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t align;
uint64_t entry_size;
uint32_t name_offset;
section_type type;
section_flags flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t align;
uint64_t entry_size;
} __attribute__ ((packed));
} // namespace elf

View File

@@ -11,31 +11,31 @@ j6_handle_t __handle_self = j6_handle_invalid;
extern "C" void
_get_init(size_t *initc, struct j6_init_value **initv)
{
if (!initc)
return;
if (!initc)
return;
*initc = __initc;
if (initv)
*initv = __initv;
*initc = __initc;
if (initv)
*initv = __initv;
}
extern "C" void
_init_libj6(uint64_t *rsp)
{
uint64_t argc = *rsp++;
rsp += argc;
uint64_t argc = *rsp++;
rsp += argc;
__initc = *rsp++;
__initv = (struct j6_init_value *)rsp;
__initc = *rsp++;
__initv = (struct j6_init_value *)rsp;
for (unsigned i = 0; i < __initc; ++i) {
if (__initv[i].type == j6_init_handle_other &&
__initv[i].handle.type == j6_object_type_system) {
__handle_sys = __initv[i].handle.handle;
}
else if (__initv[i].type == j6_init_handle_self &&
__initv[i].handle.type == j6_object_type_process) {
__handle_self = __initv[i].handle.handle;
}
}
for (unsigned i = 0; i < __initc; ++i) {
if (__initv[i].type == j6_init_handle_other &&
__initv[i].handle.type == j6_object_type_system) {
__handle_sys = __initv[i].handle.handle;
}
else if (__initv[i].type == j6_init_handle_self &&
__initv[i].handle.type == j6_object_type_process) {
__handle_self = __initv[i].handle.handle;
}
}
}

View File

@@ -4,94 +4,94 @@
namespace kutil {
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)
{}
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)
{}
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)
{
if (m_size_r) {
*area = nullptr;
return 0;
}
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;
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;
}
}
// 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;
}
if (!remaining) {
*area = nullptr;
return 0;
}
m_size_r = (remaining < size) ? remaining : size;
*area = &m_buffer[m_start_r];
return m_size_r;
m_size_r = (remaining < size) ? remaining : size;
*area = &m_buffer[m_start_r];
return m_size_r;
}
void bip_buffer::commit(size_t size)
{
kassert(size <= m_size_r, "Tried to commit more than reserved");
kassert(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
kassert(m_start_r == m_start_b + m_size_b, "Bad m_start_r!");
m_size_b += size;
}
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
kassert(m_start_r == m_start_b + m_size_b, "Bad m_start_r!");
m_size_b += size;
}
m_start_r = m_size_r = 0;
m_start_r = m_size_r = 0;
}
size_t bip_buffer::get_block(void **area) const
{
*area = m_size_a ? &m_buffer[m_start_a] : nullptr;
return m_size_a;
*area = m_size_a ? &m_buffer[m_start_a] : nullptr;
return m_size_a;
}
void bip_buffer::consume(size_t size)
{
kassert(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;
}
kassert(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 kutil

View File

@@ -8,172 +8,172 @@ namespace kutil {
struct heap_allocator::mem_header
{
mem_header(mem_header *prev, mem_header *next, uint8_t order) :
m_prev(prev), m_next(next)
{
set_order(order);
}
mem_header(mem_header *prev, mem_header *next, uint8_t order) :
m_prev(prev), m_next(next)
{
set_order(order);
}
inline void set_order(uint8_t order) {
m_prev = reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(prev()) | (order & 0x3f));
}
inline void set_order(uint8_t order) {
m_prev = reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(prev()) | (order & 0x3f));
}
inline void set_used(bool used) {
m_next = reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(next()) | (used ? 1 : 0));
}
inline void set_used(bool used) {
m_next = reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(next()) | (used ? 1 : 0));
}
inline void set_next(mem_header *next) {
bool u = used();
m_next = next;
set_used(u);
}
inline void set_next(mem_header *next) {
bool u = used();
m_next = next;
set_used(u);
}
inline void set_prev(mem_header *prev) {
uint8_t s = order();
m_prev = prev;
set_order(s);
}
inline void set_prev(mem_header *prev) {
uint8_t s = order();
m_prev = prev;
set_order(s);
}
void remove() {
if (next()) next()->set_prev(prev());
if (prev()) prev()->set_next(next());
set_prev(nullptr);
set_next(nullptr);
}
void remove() {
if (next()) next()->set_prev(prev());
if (prev()) prev()->set_next(next());
set_prev(nullptr);
set_next(nullptr);
}
inline mem_header * next() { return kutil::mask_pointer(m_next, 0x3f); }
inline mem_header * prev() { return kutil::mask_pointer(m_prev, 0x3f); }
inline mem_header * next() { return kutil::mask_pointer(m_next, 0x3f); }
inline mem_header * prev() { return kutil::mask_pointer(m_prev, 0x3f); }
inline mem_header * buddy() const {
return reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(this) ^ (1 << order()));
}
inline mem_header * buddy() const {
return reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(this) ^ (1 << order()));
}
inline bool eldest() const { return this < buddy(); }
inline bool eldest() const { return this < buddy(); }
inline uint8_t order() const { return reinterpret_cast<uintptr_t>(m_prev) & 0x3f; }
inline bool used() const { return reinterpret_cast<uintptr_t>(m_next) & 0x1; }
inline uint8_t order() const { return reinterpret_cast<uintptr_t>(m_prev) & 0x3f; }
inline bool used() const { return reinterpret_cast<uintptr_t>(m_next) & 0x1; }
private:
mem_header *m_prev;
mem_header *m_next;
mem_header *m_prev;
mem_header *m_next;
};
heap_allocator::heap_allocator() : m_start {0}, m_end {0} {}
heap_allocator::heap_allocator(uintptr_t start, size_t size) :
m_start {start},
m_end {start+size},
m_blocks {0},
m_allocated_size {0}
m_start {start},
m_end {start+size},
m_blocks {0},
m_allocated_size {0}
{
kutil::memset(m_free, 0, sizeof(m_free));
kutil::memset(m_free, 0, sizeof(m_free));
}
void *
heap_allocator::allocate(size_t length)
{
size_t total = length + sizeof(mem_header);
size_t total = length + sizeof(mem_header);
if (length == 0)
return nullptr;
if (length == 0)
return nullptr;
unsigned order = log2(total);
if (order < min_order)
order = min_order;
unsigned order = log2(total);
if (order < min_order)
order = min_order;
kassert(order <= max_order, "Tried to allocate a block bigger than max_order");
if (order > max_order)
return nullptr;
kassert(order <= max_order, "Tried to allocate a block bigger than max_order");
if (order > max_order)
return nullptr;
scoped_lock lock {m_lock};
scoped_lock lock {m_lock};
mem_header *header = pop_free(order);
header->set_used(true);
m_allocated_size += (1 << order);
return header + 1;
mem_header *header = pop_free(order);
header->set_used(true);
m_allocated_size += (1 << order);
return header + 1;
}
void
heap_allocator::free(void *p)
{
if (!p) return;
if (!p) return;
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
kassert(addr >= m_start && addr < m_end,
"Attempt to free non-heap pointer");
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
kassert(addr >= m_start && addr < m_end,
"Attempt to free non-heap pointer");
scoped_lock lock {m_lock};
scoped_lock lock {m_lock};
mem_header *header = reinterpret_cast<mem_header *>(p);
header -= 1; // p points after the header
header->set_used(false);
m_allocated_size -= (1 << header->order());
mem_header *header = reinterpret_cast<mem_header *>(p);
header -= 1; // p points after the header
header->set_used(false);
m_allocated_size -= (1 << header->order());
while (header->order() != max_order) {
auto order = header->order();
while (header->order() != max_order) {
auto order = header->order();
mem_header *buddy = header->buddy();
if (buddy->used() || buddy->order() != order)
break;
mem_header *buddy = header->buddy();
if (buddy->used() || buddy->order() != order)
break;
if (get_free(order) == buddy)
get_free(order) = buddy->next();
if (get_free(order) == buddy)
get_free(order) = buddy->next();
buddy->remove();
buddy->remove();
header = header->eldest() ? header : buddy;
header->set_order(order + 1);
}
header = header->eldest() ? header : buddy;
header->set_order(order + 1);
}
uint8_t order = header->order();
header->set_next(get_free(order));
get_free(order) = header;
if (header->next())
header->next()->set_prev(header);
uint8_t order = header->order();
header->set_next(get_free(order));
get_free(order) = header;
if (header->next())
header->next()->set_prev(header);
}
void
heap_allocator::ensure_block(unsigned order)
{
if (get_free(order) != nullptr)
return;
if (get_free(order) != nullptr)
return;
if (order == max_order) {
size_t bytes = (1 << max_order);
uintptr_t next = m_start + m_blocks * bytes;
if (next + bytes <= m_end) {
mem_header *nextp = reinterpret_cast<mem_header *>(next);
new (nextp) mem_header(nullptr, nullptr, order);
get_free(order) = nextp;
++m_blocks;
}
} else {
mem_header *orig = pop_free(order + 1);
if (orig) {
mem_header *next = kutil::offset_pointer(orig, 1 << order);
new (next) mem_header(orig, nullptr, order);
if (order == max_order) {
size_t bytes = (1 << max_order);
uintptr_t next = m_start + m_blocks * bytes;
if (next + bytes <= m_end) {
mem_header *nextp = reinterpret_cast<mem_header *>(next);
new (nextp) mem_header(nullptr, nullptr, order);
get_free(order) = nextp;
++m_blocks;
}
} else {
mem_header *orig = pop_free(order + 1);
if (orig) {
mem_header *next = kutil::offset_pointer(orig, 1 << order);
new (next) mem_header(orig, nullptr, order);
orig->set_next(next);
orig->set_order(order);
get_free(order) = orig;
}
}
orig->set_next(next);
orig->set_order(order);
get_free(order) = orig;
}
}
}
heap_allocator::mem_header *
heap_allocator::pop_free(unsigned order)
{
ensure_block(order);
mem_header *block = get_free(order);
if (block) {
get_free(order) = block->next();
block->remove();
}
return block;
ensure_block(order);
mem_header *block = get_free(order);
if (block) {
get_free(order) = block->next();
block->remove();
}
return block;
}
} // namespace kutil

View File

@@ -14,238 +14,238 @@ template <typename T> class avl_tree;
/// A node in a `avl_tree<T>`
template <typename T>
class avl_node :
public T,
public slab_allocated<avl_node<T>>
public T,
public slab_allocated<avl_node<T>>
{
public:
using item_type = T;
using node_type = avl_node<T>;
using item_type = T;
using node_type = avl_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 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; }
/// 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 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; }
/// 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; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to const T&.
inline const item_type& item() const { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to const T&.
inline const item_type& item() const { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to T&.
inline item_type& item() { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to T&.
inline item_type& item() { return *this; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline node_type * left() { return m_left; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline node_type * left() { return m_left; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline const node_type * left() const { return m_left; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline const node_type * left() const { return m_left; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline node_type * right() { return m_right; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline node_type * right() { return m_right; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline const node_type * right() const { return m_right; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline const node_type * right() const { return m_right; }
private:
friend class avl_tree<T>;
friend class avl_tree<T>;
inline static int height(node_type *l) { return (l ? l->m_height : 0); }
inline static int height(node_type *l) { return (l ? l->m_height : 0); }
// Update this node's height and return its new balance factor
inline int update_height()
{
int left = height(m_left);
int right = height(m_right);
m_height = left > right ? left : right;
return left - right;
}
// Update this node's height and return its new balance factor
inline int update_height()
{
int left = height(m_left);
int right = height(m_right);
m_height = left > right ? left : right;
return left - right;
}
int bias(node_type *addend)
{
const item_type &this_item = *this;
const item_type &that_item = *addend;
if (that_item < this_item)
return -1;
else if (that_item > this_item)
return 1;
int bias(node_type *addend)
{
const item_type &this_item = *this;
const item_type &that_item = *addend;
if (that_item < this_item)
return -1;
else if (that_item > this_item)
return 1;
kassert(false, "Equal items not allowed in AVL tree");
return 0;
}
kassert(false, "Equal items not allowed in AVL tree");
return 0;
}
static node_type * rotate_right(node_type *existing)
{
node_type *root = existing->m_left;
node_type *left = root->m_right;
static node_type * rotate_right(node_type *existing)
{
node_type *root = existing->m_left;
node_type *left = root->m_right;
root->m_right = existing;
existing->m_left = left;
root->m_right = existing;
existing->m_left = left;
existing->update_height();
root->update_height();
existing->update_height();
root->update_height();
return root;
}
return root;
}
static node_type * rotate_left(node_type *existing)
{
node_type *root = existing->m_right;
node_type *right = root->m_left;
static node_type * rotate_left(node_type *existing)
{
node_type *root = existing->m_right;
node_type *right = root->m_left;
root->m_left = existing;
existing->m_right = right;
root->m_left = existing;
existing->m_right = right;
existing->update_height();
root->update_height();
existing->update_height();
root->update_height();
return root;
}
return root;
}
static node_type * insert(node_type *existing, node_type *addend)
{
if (existing == nullptr)
return addend;
static node_type * insert(node_type *existing, node_type *addend)
{
if (existing == nullptr)
return addend;
if (existing->compare(addend) < 0)
existing->m_left = insert(existing->m_left, addend);
else
existing->m_right = insert(existing->m_right, addend);
if (existing->compare(addend) < 0)
existing->m_left = insert(existing->m_left, addend);
else
existing->m_right = insert(existing->m_right, addend);
int balance = existing->update_height();
if (balance > 1) {
// Left-heavy
if (existing->m_left->compare(addend) < 0) {
// Left Left
return rotate_right(existing);
} else {
// Left Right
existing->m_left = rotate_left(existing->m_left);
return rotate_right(existing);
}
} else if (balance < -1) {
// Right-heavy
if (existing->m_right->compare(addend) > 0) {
// Right Right
return rotate_left(existing);
} else {
// Right Left
existing->m_right = rotate_right(existing->m_right);
return rotate_left(existing);
}
}
int balance = existing->update_height();
if (balance > 1) {
// Left-heavy
if (existing->m_left->compare(addend) < 0) {
// Left Left
return rotate_right(existing);
} else {
// Left Right
existing->m_left = rotate_left(existing->m_left);
return rotate_right(existing);
}
} else if (balance < -1) {
// Right-heavy
if (existing->m_right->compare(addend) > 0) {
// Right Right
return rotate_left(existing);
} else {
// Right Left
existing->m_right = rotate_right(existing->m_right);
return rotate_left(existing);
}
}
return existing;
}
return existing;
}
static node_type * remove(node_type *existing, node_type *subtrahend)
{
if (existing == nullptr)
return existing;
static node_type * remove(node_type *existing, node_type *subtrahend)
{
if (existing == nullptr)
return existing;
if (existing == subtrahend) {
if (!existing->m_left || !existing->m_right) {
// At least one child is null
node_type *temp = existing->m_left ?
existing->m_left : existing->m_right;
if (existing == subtrahend) {
if (!existing->m_left || !existing->m_right) {
// At least one child is null
node_type *temp = existing->m_left ?
existing->m_left : existing->m_right;
if (temp == nullptr) {
// Both were null
temp = existing;
existing = nullptr;
} else {
*existing = *temp;
}
if (temp == nullptr) {
// Both were null
temp = existing;
existing = nullptr;
} else {
*existing = *temp;
}
delete temp;
} else {
// Both children exist, find next node
node_type *temp = existing->m_right;
while (temp->m_left)
temp = temp->m_left;
delete temp;
} else {
// Both children exist, find next node
node_type *temp = existing->m_right;
while (temp->m_left)
temp = temp->m_left;
*existing = *temp;
existing->m_right = remove(existing->m_right, temp);
}
} else if (existing->compare(subtrahend) < 0) {
existing->m_left = remove(existing->m_left, subtrahend);
} else {
existing->m_right = remove(existing->m_right, subtrahend);
}
*existing = *temp;
existing->m_right = remove(existing->m_right, temp);
}
} else if (existing->compare(subtrahend) < 0) {
existing->m_left = remove(existing->m_left, subtrahend);
} else {
existing->m_right = remove(existing->m_right, subtrahend);
}
if (!existing)
return nullptr;
if (!existing)
return nullptr;
int balance = existing->update_height();
if (balance > 1) {
int left_balance = existing->m_left->update_height();
int balance = existing->update_height();
if (balance > 1) {
int left_balance = existing->m_left->update_height();
if (left_balance < 0)
existing->m_left = rotate_left(existing->m_left);
if (left_balance < 0)
existing->m_left = rotate_left(existing->m_left);
return rotate_right(existing);
} else if (balance < -1) {
int right_balance = existing->m_right->update_height();
return rotate_right(existing);
} else if (balance < -1) {
int right_balance = existing->m_right->update_height();
if (right_balance > 0)
existing->m_right = rotate_right(existing->m_right);
if (right_balance > 0)
existing->m_right = rotate_right(existing->m_right);
return rotate_left(existing);
}
return rotate_left(existing);
}
return existing;
}
return existing;
}
int m_height;
node_type *m_left;
node_type *m_right;
int m_height;
node_type *m_left;
node_type *m_right;
};
template <typename T>
class avl_tree
{
public:
using item_type = T;
using node_type = avl_node<T>;
using item_type = T;
using node_type = avl_node<T>;
avl_tree() = default;
avl_tree(avl_tree &&other) :
m_count(other.m_count), m_root(other.m_root)
{
other.m_root = nullptr;
other.m_count = 0;
}
avl_tree() = default;
avl_tree(avl_tree &&other) :
m_count(other.m_count), m_root(other.m_root)
{
other.m_root = nullptr;
other.m_count = 0;
}
inline node_type * root() { return m_root; }
inline unsigned count() const { return m_count; }
inline node_type * root() { return m_root; }
inline unsigned count() const { return m_count; }
inline void remove(node_type *subtrahend) {
m_root = node_type::remove(m_root, subtrahend);
m_count--;
}
inline void remove(node_type *subtrahend) {
m_root = node_type::remove(m_root, subtrahend);
m_count--;
}
inline void insert(node_type *addend) {
m_root = node_type::insert(m_root, addend);
m_count++;
}
inline void insert(node_type *addend) {
m_root = node_type::insert(m_root, addend);
m_count++;
}
private:
unsigned m_count {0};
node_type *m_root {nullptr};
unsigned m_count {0};
node_type *m_root {nullptr};
};
} // namespace kutil

View File

@@ -11,49 +11,49 @@ namespace kutil {
class bip_buffer
{
public:
/// Default constructor. Creates a zero-size buffer.
bip_buffer();
/// Default constructor. Creates a zero-size buffer.
bip_buffer();
/// Constructor.
bip_buffer(uint8_t *buffer, size_t size);
/// 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);
/// 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);
/// 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;
/// 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);
/// 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 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(); }
/// 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;
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;
const size_t m_buffer_size;
uint8_t * const m_buffer;
const size_t m_buffer_size;
uint8_t * const m_buffer;
};
} // namespace kutil

View File

@@ -8,35 +8,35 @@
namespace kutil {
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
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;
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];
return !s[off] ? 5381 : (djb_hash_32(s, off+1)*33) ^ s[off];
}
} // namespace kutil
constexpr inline uint8_t operator "" _h (const char *s, size_t len) {
return kutil::pearson_hash_8(s, static_cast<uint8_t>(len & 0xff));
return kutil::pearson_hash_8(s, static_cast<uint8_t>(len & 0xff));
}

View File

@@ -5,10 +5,10 @@ namespace kutil {
template <typename T>
struct coord
{
T x, y;
coord() : x(T{}), y(T{}) {}
coord(T x, T y) : x(x), y(y) {}
T size() const { return x * y; }
T x, y;
coord() : x(T{}), y(T{}) {}
coord(T x, T y) : x(x), y(y) {}
T size() const { return x * y; }
};
} // namespace kutil

View File

@@ -10,7 +10,7 @@ namespace kutil {
/// A GUID
struct guid
{
uint64_t a, b;
uint64_t a, b;
};
/// Make a GUID by writing it naturally-ordered in code:
@@ -19,17 +19,17 @@ struct guid
/// \returns The guid object
inline constexpr guid make_guid(uint32_t a, uint16_t b, uint16_t c, uint16_t d, uint64_t e)
{
const uint64_t h =
static_cast<uint64_t>(c) << 48 |
static_cast<uint64_t>(b) << 32 |
a;
const uint64_t h =
static_cast<uint64_t>(c) << 48 |
static_cast<uint64_t>(b) << 32 |
a;
const uint64_t l =
static_cast<uint64_t>(byteswap(e & 0xffffffff)) << 32 |
(byteswap(e >> 32) & 0xffff0000) |
((d << 8) & 0xff00) | ((d >> 8) & 0xff);
const uint64_t l =
static_cast<uint64_t>(byteswap(e & 0xffffffff)) << 32 |
(byteswap(e >> 32) & 0xffff0000) |
((d << 8) & 0xff00) | ((d >> 8) & 0xff);
return {h, l};
return {h, l};
}
} // namespace kutil

View File

@@ -12,29 +12,29 @@ 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;
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;
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));
return hash_buffer(reinterpret_cast<const void*>(&v), sizeof(T));
}
template <> inline uint64_t hash<uint64_t>(const uint64_t &i) { return i; }

View File

@@ -12,55 +12,55 @@ namespace kutil {
class heap_allocator
{
public:
/// Default constructor creates a valid but empty heap.
heap_allocator();
/// Default constructor creates a valid but empty heap.
heap_allocator();
/// Constructor. The given memory area must already have been reserved.
/// \arg start Starting address of the heap
/// \arg size Size of the heap in bytes
heap_allocator(uintptr_t start, size_t size);
/// Constructor. The given memory area must already have been reserved.
/// \arg start Starting address of the heap
/// \arg size Size of the heap in bytes
heap_allocator(uintptr_t start, size_t size);
/// Allocate memory from the area managed.
/// \arg length The amount of memory to allocate, in bytes
/// \returns A pointer to the allocated memory, or nullptr if
/// allocation failed.
void * allocate(size_t length);
/// Allocate memory from the area managed.
/// \arg length The amount of memory to allocate, in bytes
/// \returns A pointer to the allocated memory, or nullptr if
/// allocation failed.
void * allocate(size_t length);
/// Free a previous allocation.
/// \arg p A pointer previously retuned by allocate()
void free(void *p);
/// Free a previous allocation.
/// \arg p A pointer previously retuned by allocate()
void free(void *p);
/// Minimum block size is (2^min_order). Must be at least 6.
static const unsigned min_order = 6;
/// Minimum block size is (2^min_order). Must be at least 6.
static const unsigned min_order = 6;
/// Maximum block size is (2^max_order). Must be less than 64.
static const unsigned max_order = 22;
/// Maximum block size is (2^max_order). Must be less than 64.
static const unsigned max_order = 22;
protected:
class mem_header;
class mem_header;
/// Ensure there is a block of a given order, recursively splitting
/// \arg order Order (2^N) of the block we want
void ensure_block(unsigned order);
/// Ensure there is a block of a given order, recursively splitting
/// \arg order Order (2^N) of the block we want
void ensure_block(unsigned order);
/// Helper accessor for the list of blocks of a given order
/// \arg order Order (2^N) of the block we want
/// \returns A mutable reference to the head of the list
mem_header *& get_free(unsigned order) { return m_free[order - min_order]; }
/// Helper accessor for the list of blocks of a given order
/// \arg order Order (2^N) of the block we want
/// \returns A mutable reference to the head of the list
mem_header *& get_free(unsigned order) { return m_free[order - min_order]; }
/// Helper to get a block of the given order, growing if necessary
/// \arg order Order (2^N) of the block we want
/// \returns A detached block of the given order
mem_header * pop_free(unsigned order);
/// Helper to get a block of the given order, growing if necessary
/// \arg order Order (2^N) of the block we want
/// \returns A detached block of the given order
mem_header * pop_free(unsigned order);
uintptr_t m_start, m_end;
size_t m_blocks;
mem_header *m_free[max_order - min_order + 1];
size_t m_allocated_size;
uintptr_t m_start, m_end;
size_t m_blocks;
mem_header *m_free[max_order - min_order + 1];
size_t m_allocated_size;
spinlock m_lock;
spinlock m_lock;
heap_allocator(const heap_allocator &) = delete;
heap_allocator(const heap_allocator &) = delete;
};
} // namespace kutil

View File

@@ -11,77 +11,77 @@ 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 T
{
public:
using item_type = T;
using node_type = list_node<T>;
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 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; }
/// 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 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; }
/// 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 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 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 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; }
/// 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>;
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 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;
}
/// 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;
}
/// 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;
node_type *m_next;
node_type *m_prev;
};
@@ -90,18 +90,18 @@ template <typename T>
class list_iterator
{
public:
using item_type = list_node<T>;
using item_type = list_node<T>;
list_iterator(item_type *item) : m_item(item) {}
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; }
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;
item_type *m_item;
};
@@ -110,239 +110,239 @@ template <typename T>
class linked_list
{
public:
using item_type = list_node<T>;
using iterator = list_iterator<T>;
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)
{}
/// 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;
}
/// 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;
}
/// 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; }
/// 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; }
/// 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;
}
/// 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 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; }
/// 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;
/// 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;
}
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;
}
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;
/// 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;
}
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;
}
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 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;
}
/// 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;
/// 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;
}
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;
}
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;
/// 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;
}
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;
}
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;
}
/// 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;
/// 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;
}
}
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;
/// 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;
}
}
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;
/// 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;
item_type *cur = m_head;
while (cur && item->compare(*cur) > 0)
cur = cur->m_next;
insert_before(cur, item);
}
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 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 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); }
/// 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;
item_type *m_head;
item_type *m_tail;
size_t m_count;
};

View File

@@ -13,109 +13,109 @@ namespace log {
using area_t = uint8_t;
enum class level : uint8_t {
none, debug, info, warn, error, fatal, max
none, debug, info, warn, error, fatal, max
};
class logger
{
public:
/// Callback type for immediate-mode logging
typedef void (*immediate_cb)(area_t, level, const char *);
/// Callback type for immediate-mode logging
typedef void (*immediate_cb)(area_t, level, const char *);
/// Callback type for log flushing
typedef void (*flush_cb)();
/// Callback type for log flushing
typedef void (*flush_cb)();
/// Default constructor. Creates a logger without a backing store.
/// \arg output Immediate-mode logging output function
logger(immediate_cb output = nullptr);
/// Default constructor. Creates a logger without a backing store.
/// \arg output Immediate-mode logging output function
logger(immediate_cb output = nullptr);
/// Constructor. Logs are written to the given buffer.
/// \arg buffer Buffer to which logs are written
/// \arg size Size of `buffer`, in bytes
/// \arg output Immediate-mode logging output function
logger(uint8_t *buffer, size_t size, immediate_cb output = nullptr);
/// Constructor. Logs are written to the given buffer.
/// \arg buffer Buffer to which logs are written
/// \arg size Size of `buffer`, in bytes
/// \arg output Immediate-mode logging output function
logger(uint8_t *buffer, size_t size, immediate_cb output = nullptr);
/// Register a log area for future use.
/// \arg area The key for the new area
/// \arg name The area name
/// \arg verbosity What level of logs to print for this area
void register_area(area_t area, const char *name, level verbosity);
/// Register a log area for future use.
/// \arg area The key for the new area
/// \arg name The area name
/// \arg verbosity What level of logs to print for this area
void register_area(area_t area, const char *name, level verbosity);
/// Register an immediate-mode log callback
inline void set_immediate(immediate_cb cb) { m_immediate = cb; }
/// Register an immediate-mode log callback
inline void set_immediate(immediate_cb cb) { m_immediate = cb; }
/// Register a flush callback
inline void set_flush(flush_cb cb) { m_flush = cb; }
/// Register a flush callback
inline void set_flush(flush_cb cb) { m_flush = cb; }
/// Get the default logger.
inline logger & get() { return *s_log; }
/// Get the default logger.
inline logger & get() { return *s_log; }
/// Get the registered name for a given area
inline const char * area_name(area_t area) const { return m_names[area]; }
/// Get the registered name for a given area
inline const char * area_name(area_t area) const { return m_names[area]; }
/// Get the name of a level
inline const char * level_name(level l) const { return s_level_names[static_cast<unsigned>(l)]; }
/// Get the name of a level
inline const char * level_name(level l) const { return s_level_names[static_cast<unsigned>(l)]; }
/// Write to the log
/// \arg severity The severity of the message
/// \arg area The log area to write to
/// \arg fmt A printf-like format string
inline void log(level severity, area_t area, const char *fmt, ...)
{
level limit = get_level(area);
if (limit == level::none || severity < limit)
return;
/// Write to the log
/// \arg severity The severity of the message
/// \arg area The log area to write to
/// \arg fmt A printf-like format string
inline void log(level severity, area_t area, const char *fmt, ...)
{
level limit = get_level(area);
if (limit == level::none || severity < limit)
return;
va_list args;
va_start(args, fmt);
output(severity, area, fmt, args);
va_end(args);
}
va_list args;
va_start(args, fmt);
output(severity, area, fmt, args);
va_end(args);
}
struct entry
{
uint8_t bytes;
area_t area;
level severity;
uint8_t sequence;
char message[0];
};
struct entry
{
uint8_t bytes;
area_t area;
level severity;
uint8_t sequence;
char message[0];
};
/// Get the next log entry from the buffer
/// \arg buffer The buffer to copy the log message into
/// \arg size Size of the passed-in buffer, in bytes
/// \returns The size of the log entry (if larger than the
/// buffer, then no data was copied)
size_t get_entry(void *buffer, size_t size);
/// Get the next log entry from the buffer
/// \arg buffer The buffer to copy the log message into
/// \arg size Size of the passed-in buffer, in bytes
/// \returns The size of the log entry (if larger than the
/// buffer, then no data was copied)
size_t get_entry(void *buffer, size_t size);
/// Get whether there is currently data in the log buffer
inline bool has_log() const { return m_buffer.size(); }
/// Get whether there is currently data in the log buffer
inline bool has_log() const { return m_buffer.size(); }
private:
friend void debug(area_t area, const char *fmt, ...);
friend void info (area_t area, const char *fmt, ...);
friend void warn (area_t area, const char *fmt, ...);
friend void error(area_t area, const char *fmt, ...);
friend void fatal(area_t area, const char *fmt, ...);
friend void debug(area_t area, const char *fmt, ...);
friend void info (area_t area, const char *fmt, ...);
friend void warn (area_t area, const char *fmt, ...);
friend void error(area_t area, const char *fmt, ...);
friend void fatal(area_t area, const char *fmt, ...);
void output(level severity, area_t area, const char *fmt, va_list args);
void output(level severity, area_t area, const char *fmt, va_list args);
void set_level(area_t area, level l);
level get_level(area_t area);
void set_level(area_t area, level l);
level get_level(area_t area);
static const unsigned num_areas = 1 << (sizeof(area_t) * 8);
uint8_t m_levels[num_areas / 2];
const char *m_names[num_areas];
immediate_cb m_immediate;
flush_cb m_flush;
static const unsigned num_areas = 1 << (sizeof(area_t) * 8);
uint8_t m_levels[num_areas / 2];
const char *m_names[num_areas];
immediate_cb m_immediate;
flush_cb m_flush;
uint8_t m_sequence;
uint8_t m_sequence;
kutil::bip_buffer m_buffer;
kutil::spinlock m_lock;
kutil::bip_buffer m_buffer;
kutil::spinlock m_lock;
static logger *s_log;
static const char *s_level_names[static_cast<unsigned>(level::max)];
static logger *s_log;
static const char *s_level_names[static_cast<unsigned>(level::max)];
};
void debug(area_t area, const char *fmt, ...);

View File

@@ -24,25 +24,25 @@ 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
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;
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; }
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; }
inline uint64_t & hash() { return h; }
inline uint64_t hash() const { return h; }
};
/// Base class for hash maps
@@ -50,242 +50,242 @@ template <typename K, typename V>
class base_map
{
protected:
using node = hash_node<K, V>;
using node = hash_node<K, V>;
public:
static constexpr size_t min_capacity = 8;
static constexpr size_t max_load = 90;
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;
};
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));
}
/// 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();
kfree(m_nodes);
}
virtual ~base_map() {
for (size_t i = 0; i < m_capacity; ++i)
m_nodes[i].~node();
kfree(m_nodes);
}
iterator begin() {
if (!m_count) return iterator {0};
iterator it {m_nodes - 1, m_nodes + m_capacity};
return ++it;
}
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 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);
}
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));
}
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;
bool erase(const K &k)
{
node *n = lookup(k);
if (!n) return false;
n->~node();
--m_count;
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);
}
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;
}
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; }
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));
}
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) {
kassert((capacity & (capacity - 1)) == 0,
"Map capacity must be a power of two");
void set_capacity(size_t capacity) {
kassert((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*>(kalloc(size));
kutil::memset(m_nodes, 0, size);
}
m_capacity = capacity;
const size_t size = m_capacity * sizeof(node);
m_nodes = reinterpret_cast<node*>(kalloc(size));
kutil::memset(m_nodes, 0, size);
}
void grow() {
node *old = m_nodes;
size_t count = m_capacity;
void grow() {
node *old = m_nodes;
size_t count = m_capacity;
size_t cap = m_capacity * 2;
if (cap < min_capacity)
cap = min_capacity;
size_t cap = m_capacity * 2;
if (cap < min_capacity)
cap = min_capacity;
set_capacity(cap);
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();
}
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();
}
kfree(old);
}
kfree(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));
}
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;
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));
}
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;
}
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;
}
}
i = mod(++i);
++dist;
}
}
node * lookup(const K &k) {
if (!m_count)
return nullptr;
node * lookup(const K &k) {
if (!m_count)
return nullptr;
uint64_t h = hash(k);
size_t i = mod(h);
size_t dist = 0;
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;
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;
else if (n.hash() == h && equal(n.key, k))
return &n;
i = mod(++i);
++dist;
}
}
i = mod(++i);
++dist;
}
}
const node * lookup(const K &k) const {
if (!m_count)
return nullptr;
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;
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;
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;
else if (n.hash() == h && equal(n.key, k))
return &n;
i = mod(++i);
++dist;
}
}
i = mod(++i);
++dist;
}
}
size_t m_count;
size_t m_capacity;
node *m_nodes;
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>
public base_map<K, V>
{
using base = base_map<K, V>;
using node = typename base::node;
using base = base_map<K, V>;
using node = typename base::node;
public:
map(size_t capacity = 0) :
base(capacity) {}
map(size_t capacity = 0) :
base(capacity) {}
V * find(const K &k) {
node *n = this->lookup(k);
return n ? &n->val : nullptr;
}
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;
}
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*>
public base_map<K, V*>
{
using base = base_map<K, V*>;
using node = typename base::node;
using base = base_map<K, V*>;
using node = typename base::node;
public:
map(size_t capacity = 0) :
base(capacity) {}
map(size_t capacity = 0) :
base(capacity) {}
V * find(const K &k) const {
const node *n = this->lookup(k);
return n ? n->val : nullptr;
}
V * find(const K &k) const {
const node *n = this->lookup(k);
return n ? n->val : nullptr;
}
};
} // namespace kutil

View File

@@ -40,7 +40,7 @@ void * memcpy(void *dest, const void *src, size_t n);
template <typename T>
inline T read_from(const void *p)
{
return *reinterpret_cast<const T *>(p);
return *reinterpret_cast<const T *>(p);
}
/// Get a pointer that's offset from another pointer
@@ -50,7 +50,7 @@ inline T read_from(const void *p)
template <typename T>
inline T * offset_pointer(T *p, ptrdiff_t n)
{
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) + n);
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) + n);
}
/// Return a pointer with the given bits masked out
@@ -60,7 +60,7 @@ inline T * offset_pointer(T *p, ptrdiff_t n)
template <typename T>
inline T* mask_pointer(T *p, uintptr_t mask)
{
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) & ~mask);
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) & ~mask);
}
/// Do a simple byte-wise checksum of an area of memory.

View File

@@ -5,8 +5,8 @@ namespace kutil {
constexpr uint32_t
byteswap(uint32_t x)
{
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00)
| ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00)
| ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
}
}

View File

@@ -8,9 +8,9 @@ namespace kutil {
template <typename T>
union no_construct
{
T value;
no_construct() {}
~no_construct() {}
T value;
no_construct() {}
~no_construct() {}
};
} // namespace kutil

View File

@@ -12,36 +12,36 @@ template <typename T, unsigned N = 1>
class slab_allocated
{
public:
void * operator new(size_t size)
{
kassert(size == sizeof(T), "Slab allocator got wrong size allocation");
if (s_free.count() == 0)
allocate_chunk();
void * operator new(size_t size)
{
kassert(size == sizeof(T), "Slab allocator got wrong size allocation");
if (s_free.count() == 0)
allocate_chunk();
T *item = s_free.pop();
kutil::memset(item, 0, sizeof(T));
return item;
}
T *item = s_free.pop();
kutil::memset(item, 0, sizeof(T));
return item;
}
void operator delete(void *p) { s_free.append(reinterpret_cast<T*>(p)); }
void operator delete(void *p) { s_free.append(reinterpret_cast<T*>(p)); }
private:
static void allocate_chunk()
{
size_t size = N * ::memory::frame_size;
s_free.ensure_capacity(size / sizeof(T));
static void allocate_chunk()
{
size_t size = N * ::memory::frame_size;
s_free.ensure_capacity(size / sizeof(T));
void *memory = kalloc(size);
T *current = reinterpret_cast<T *>(memory);
T *end = offset_pointer(current, size);
while (current < end)
s_free.append(current++);
}
void *memory = kalloc(size);
T *current = reinterpret_cast<T *>(memory);
T *end = offset_pointer(current, size);
while (current < end)
s_free.append(current++);
}
static vector<T*> s_free;
static vector<T*> s_free;
};
#define DEFINE_SLAB_ALLOCATOR(type, N) \
template<> ::kutil::vector<type*> kutil::slab_allocated<type, N>::s_free {};
template<> ::kutil::vector<type*> kutil::slab_allocated<type, N>::s_free {};
} // namespace kutil

View File

@@ -9,38 +9,38 @@ namespace kutil {
class spinlock
{
public:
spinlock();
~spinlock();
spinlock();
~spinlock();
/// A node in the wait queue.
struct waiter
{
bool blocked;
waiter *next;
};
/// A node in the wait queue.
struct waiter
{
bool blocked;
waiter *next;
};
void acquire(waiter *w);
void release(waiter *w);
void acquire(waiter *w);
void release(waiter *w);
private:
waiter *m_lock;
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(spinlock &lock) : m_lock(lock) {
m_lock.acquire(&m_waiter);
}
inline ~scoped_lock() {
m_lock.release(&m_waiter);
}
inline ~scoped_lock() {
m_lock.release(&m_waiter);
}
private:
spinlock &m_lock;
spinlock::waiter m_waiter;
spinlock &m_lock;
spinlock::waiter m_waiter;
};
} // namespace kutil

View File

@@ -8,9 +8,9 @@ namespace kutil {
// 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;
if (i < 2) return 0;
const unsigned clz = __builtin_clzll(i - 1);
return 64 - clz;
}
}

View File

@@ -13,282 +13,282 @@ namespace kutil {
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;
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)
{}
/// 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);
}
/// 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);
kutil::memcpy(m_elements, other.m_elements, other.m_size * sizeof(T));
m_size = other.m_size;
}
/// 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);
kutil::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;
}
/// 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])
{
}
/// 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();
/// Destructor. Destroys any remaining items in the array.
~vector()
{
while (m_size) remove();
bool was_static = m_capacity & ~cap_mask;
if (!was_static)
kfree(m_elements);
}
bool was_static = m_capacity & ~cap_mask;
if (!was_static)
kfree(m_elements);
}
/// Get the size of the array.
inline count_t count() const { return m_size; }
/// 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; }
/// 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 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]; }
/// 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.
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 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.
T * end() { return m_elements + m_size; }
/// Get a pointer to the end for iteration.
const T * end() const { 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++];
}
/// 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++];
}
/// 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;
}
/// 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;
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;
}
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 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;
}
insert(start, item);
return start;
}
/// Remove an item from the end of the array.
void remove()
{
kassert(m_size, "Called remove() on an empty array");
/// Remove an item from the end of the array.
void remove()
{
kassert(m_size, "Called remove() on an empty array");
m_size -= 1;
m_elements[m_size].~T();
}
m_size -= 1;
m_elements[m_size].~T();
}
/// Remove an item from the front of the array, preserving order.
void remove_front()
{
kassert(m_size, "Called remove_front() on an empty array");
remove_at(0);
}
/// Remove an item from the front of the array, preserving order.
void remove_front()
{
kassert(m_size, "Called remove_front() on an empty array");
remove_at(0);
}
/// Remove an item from the array.
void remove(const T &item)
{
kassert(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 an item from the array.
void remove(const T &item)
{
kassert(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();
}
/// 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;
}
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 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;
/// 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;
}
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()
{
kassert(m_size, "Called pop() on an empty array");
/// Remove an item from the end of the array and return it.
T pop()
{
kassert(m_size, "Called pop() on an empty array");
T temp = m_elements[m_size - 1];
remove();
return temp;
}
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()
{
kassert(m_size, "Called pop_front() on an empty array");
/// Remove an item from the beginning of the array and return it.
T pop_front()
{
kassert(m_size, "Called pop_front() on an empty array");
T temp = m_elements[0];
remove_front();
return temp;
}
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;
}
/// 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);
}
/// 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*>(kalloc(capacity * sizeof(T)));
count_t size = capacity > m_size ? m_size : 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*>(kalloc(capacity * sizeof(T)));
count_t size = capacity > m_size ? m_size : capacity;
kutil::memcpy(new_array, m_elements, size * sizeof(T));
kutil::memcpy(new_array, m_elements, size * sizeof(T));
while (size < m_size) remove();
m_size = size;
m_capacity = capacity;
while (size < m_size) remove();
m_size = size;
m_capacity = capacity;
if (!was_static)
kfree(m_elements);
m_elements = new_array;
}
if (!was_static)
kfree(m_elements);
m_elements = new_array;
}
private:
count_t m_size;
count_t m_capacity;
T *m_elements;
count_t m_size;
count_t m_capacity;
T *m_elements;
};
} // namespace kutil

View File

@@ -7,8 +7,8 @@
namespace kutil {
namespace logs {
#define LOG(name, lvl) \
const log::area_t name = #name ## _h; \
const char * name ## _name = #name;
const log::area_t name = #name ## _h; \
const char * name ## _name = #name;
#include "j6/tables/log_areas.inc"
#undef LOG
}
@@ -22,28 +22,28 @@ logger *logger::s_log = nullptr;
const char *logger::s_level_names[] = {"", "debug", "info", "warn", "error", "fatal"};
logger::logger(logger::immediate_cb output) :
m_buffer(nullptr, 0),
m_immediate(output),
m_flush(nullptr),
m_sequence(0)
m_buffer(nullptr, 0),
m_immediate(output),
m_flush(nullptr),
m_sequence(0)
{
memset(&m_levels, 0, sizeof(m_levels));
memset(&m_names, 0, sizeof(m_names));
s_log = this;
memset(&m_levels, 0, sizeof(m_levels));
memset(&m_names, 0, sizeof(m_names));
s_log = this;
}
logger::logger(uint8_t *buffer, size_t size, logger::immediate_cb output) :
m_buffer(buffer, size),
m_immediate(output),
m_flush(nullptr),
m_sequence(0)
m_buffer(buffer, size),
m_immediate(output),
m_flush(nullptr),
m_sequence(0)
{
memset(&m_levels, 0, sizeof(m_levels));
memset(&m_names, 0, sizeof(m_names));
s_log = this;
memset(&m_levels, 0, sizeof(m_levels));
memset(&m_names, 0, sizeof(m_names));
s_log = this;
#define LOG(name, lvl) \
register_area(logs::name, logs::name ## _name, log::level::lvl);
register_area(logs::name, logs::name ## _name, log::level::lvl);
#include "j6/tables/log_areas.inc"
#undef LOG
}
@@ -51,105 +51,105 @@ logger::logger(uint8_t *buffer, size_t size, logger::immediate_cb output) :
void
logger::set_level(area_t area, level l)
{
unsigned uarea = static_cast<unsigned>(area);
uint8_t ulevel = static_cast<uint8_t>(l) & 0x0f;
uint8_t &flags = m_levels[uarea / 2];
if (uarea & 1)
flags = (flags & 0x0f) | (ulevel << 4);
else
flags = (flags & 0xf0) | ulevel;
unsigned uarea = static_cast<unsigned>(area);
uint8_t ulevel = static_cast<uint8_t>(l) & 0x0f;
uint8_t &flags = m_levels[uarea / 2];
if (uarea & 1)
flags = (flags & 0x0f) | (ulevel << 4);
else
flags = (flags & 0xf0) | ulevel;
}
level
logger::get_level(area_t area)
{
unsigned uarea = static_cast<unsigned>(area);
uint8_t &flags = m_levels[uarea / 2];
if (uarea & 1)
return static_cast<level>((flags & 0xf0) >> 4);
else
return static_cast<level>(flags & 0x0f);
unsigned uarea = static_cast<unsigned>(area);
uint8_t &flags = m_levels[uarea / 2];
if (uarea & 1)
return static_cast<level>((flags & 0xf0) >> 4);
else
return static_cast<level>(flags & 0x0f);
}
void
logger::register_area(area_t area, const char *name, level verbosity)
{
m_names[area] = name;
set_level(area, verbosity);
m_names[area] = name;
set_level(area, verbosity);
}
void
logger::output(level severity, area_t area, const char *fmt, va_list args)
{
uint8_t buffer[256];
entry *header = reinterpret_cast<entry *>(buffer);
header->bytes = sizeof(entry);
header->area = area;
header->severity = severity;
header->sequence = m_sequence++;
uint8_t buffer[256];
entry *header = reinterpret_cast<entry *>(buffer);
header->bytes = sizeof(entry);
header->area = area;
header->severity = severity;
header->sequence = m_sequence++;
header->bytes +=
vsnprintf(header->message, sizeof(buffer) - sizeof(entry), fmt, args);
header->bytes +=
vsnprintf(header->message, sizeof(buffer) - sizeof(entry), fmt, args);
kutil::scoped_lock lock {m_lock};
kutil::scoped_lock lock {m_lock};
if (m_immediate) {
buffer[header->bytes] = 0;
m_immediate(area, severity, header->message);
return;
}
if (m_immediate) {
buffer[header->bytes] = 0;
m_immediate(area, severity, header->message);
return;
}
uint8_t *out;
size_t n = m_buffer.reserve(header->bytes, reinterpret_cast<void**>(&out));
if (n < sizeof(entry)) {
m_buffer.commit(0); // Cannot even write the header, abort
return;
}
uint8_t *out;
size_t n = m_buffer.reserve(header->bytes, reinterpret_cast<void**>(&out));
if (n < sizeof(entry)) {
m_buffer.commit(0); // Cannot even write the header, abort
return;
}
if (n < header->bytes)
header->bytes = n;
if (n < header->bytes)
header->bytes = n;
memcpy(out, buffer, n);
m_buffer.commit(n);
memcpy(out, buffer, n);
m_buffer.commit(n);
if (m_flush)
m_flush();
if (m_flush)
m_flush();
}
size_t
logger::get_entry(void *buffer, size_t size)
{
kutil::scoped_lock lock {m_lock};
kutil::scoped_lock lock {m_lock};
void *out;
size_t out_size = m_buffer.get_block(&out);
if (out_size == 0 || out == 0)
return 0;
void *out;
size_t out_size = m_buffer.get_block(&out);
if (out_size == 0 || out == 0)
return 0;
kassert(out_size >= sizeof(entry), "Couldn't read a full entry");
if (out_size < sizeof(entry))
return 0;
kassert(out_size >= sizeof(entry), "Couldn't read a full entry");
if (out_size < sizeof(entry))
return 0;
entry *ent = reinterpret_cast<entry *>(out);
if (size >= ent->bytes) {
memcpy(buffer, out, ent->bytes);
m_buffer.consume(ent->bytes);
}
entry *ent = reinterpret_cast<entry *>(out);
if (size >= ent->bytes) {
memcpy(buffer, out, ent->bytes);
m_buffer.consume(ent->bytes);
}
return ent->bytes;
return ent->bytes;
}
#define LOG_LEVEL_FUNCTION(name) \
void name (area_t area, const char *fmt, ...) { \
logger *l = logger::s_log; \
if (!l) return; \
level limit = l->get_level(area); \
if (limit == level::none || level::name < limit) return; \
va_list args; \
va_start(args, fmt); \
l->output(level::name, area, fmt, args); \
va_end(args); \
}
void name (area_t area, const char *fmt, ...) { \
logger *l = logger::s_log; \
if (!l) return; \
level limit = l->get_level(area); \
if (limit == level::none || level::name < limit) return; \
va_list args; \
va_start(args, fmt); \
l->output(level::name, area, fmt, args); \
va_end(args); \
}
LOG_LEVEL_FUNCTION(debug);
LOG_LEVEL_FUNCTION(info);
@@ -158,15 +158,15 @@ LOG_LEVEL_FUNCTION(error);
void fatal(area_t area, const char *fmt, ...)
{
logger *l = logger::s_log;
if (!l) return;
logger *l = logger::s_log;
if (!l) return;
va_list args;
va_start(args, fmt);
l->output(level::fatal, area, fmt, args);
va_end(args);
va_list args;
va_start(args, fmt);
l->output(level::fatal, area, fmt, args);
va_end(args);
kassert(false, "log::fatal");
kassert(false, "log::fatal");
}
} // namespace log

View File

@@ -1,7 +1,7 @@
#include "kutil/memory.h"
namespace std {
enum class __attribute__ ((__type_visibility("default"))) align_val_t : size_t { };
enum class __attribute__ ((__type_visibility("default"))) align_val_t : size_t { };
}
namespace kutil {
@@ -9,27 +9,27 @@ namespace kutil {
void *
memset(void *s, uint8_t v, size_t n)
{
uint8_t *p = reinterpret_cast<uint8_t *>(s);
for (size_t i = 0; i < n; ++i) p[i] = v;
return s;
uint8_t *p = reinterpret_cast<uint8_t *>(s);
for (size_t i = 0; i < n; ++i) p[i] = v;
return s;
}
void *
memcpy(void *dest, const void *src, size_t n)
{
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
uint8_t *d = reinterpret_cast<uint8_t *>(dest);
for (size_t i = 0; i < n; ++i) d[i] = s[i];
return d;
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
uint8_t *d = reinterpret_cast<uint8_t *>(dest);
for (size_t i = 0; i < n; ++i) d[i] = s[i];
return d;
}
uint8_t
checksum(const void *p, size_t len, size_t off)
{
uint8_t sum = 0;
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
for (int i = off; i < len; ++i) sum += c[i];
return sum;
uint8_t sum = 0;
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
for (int i = off; i < len; ++i) sum += c[i];
return sum;
}
} // namespace kutil

View File

@@ -10,36 +10,36 @@ spinlock::~spinlock() {}
void
spinlock::acquire(waiter *w)
{
w->next = nullptr;
w->blocked = true;
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;
}
// 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;
// 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");
// Wait for the subseqent waiter to tell us who they are
while (!w->next)
asm ("pause");
// Unblock the subseqent waiter
w->next->blocked = false;
// Unblock the subseqent waiter
w->next->blocked = false;
}

View File

@@ -51,51 +51,51 @@ pid_t waitpid(pid_t, int *, int);
void _exit(int);
#define START_SUITE(name) \
int run_suite_ ##name (void) { \
int TEST_RESULTS = 0;
int run_suite_ ##name (void) { \
int TEST_RESULTS = 0;
#define END_SUITE \
return TEST_RESULTS; \
}
return TEST_RESULTS; \
}
#define DECLARE_SUITE(name) extern int run_suite_ ##name (void)
#define RUN_TEST(name) TEST_RESULTS += test__ ##name();
#define START_TEST(name) \
int test__ ##name (void) { \
int TEST_RESULTS = 0;
int test__ ##name (void) { \
int TEST_RESULTS = 0;
#define END_TEST \
return TEST_RESULTS; \
}
return TEST_RESULTS; \
}
/* TESTCASE() - generic test */
#define TESTCASE( x ) \
do { \
pid_t pid = fork(); \
if ( !pid ) _exit((x) ? 0 : 0xFF); \
do { \
pid_t pid = fork(); \
if ( !pid ) _exit((x) ? 0 : 0xFF); \
else { \
int __rc = 0; \
waitpid(pid, &__rc, 0); \
if ( __rc & 0xff00 ) { \
TEST_RESULTS += 1; \
fprintf( stderr, "FAILED: " __FILE__ ":%s, line %d - %s\n", __func__, __LINE__, #x ); \
} \
} \
} while(0)
int __rc = 0; \
waitpid(pid, &__rc, 0); \
if ( __rc & 0xff00 ) { \
TEST_RESULTS += 1; \
fprintf( stderr, "FAILED: " __FILE__ ":%s, line %d - %s\n", __func__, __LINE__, #x ); \
} \
} \
} while(0)
/* TESTCASE_REQUIRE() - must-pass test; return early otherwise */
#define TESTCASE_REQUIRE( x ) \
do { \
pid_t pid = fork(); \
if ( !pid ) _exit((x) ? 0 : 0xFF); \
do { \
pid_t pid = fork(); \
if ( !pid ) _exit((x) ? 0 : 0xFF); \
else { \
int __rc = 0; \
waitpid(pid, &__rc, 0); \
if ( __rc & 0xff00 ) { \
TEST_RESULTS += 1; \
fprintf( stderr, "FAILED: " __FILE__ ":%s, line %d - %s\n", __func__, __LINE__, #x ); \
return TEST_RESULTS; \
} \
} \
} while(0)
int __rc = 0; \
waitpid(pid, &__rc, 0); \
if ( __rc & 0xff00 ) { \
TEST_RESULTS += 1; \
fprintf( stderr, "FAILED: " __FILE__ ":%s, line %d - %s\n", __func__, __LINE__, #x ); \
return TEST_RESULTS; \
} \
} \
} while(0)

View File

@@ -1,6 +1,6 @@
#define PRINTF_TEST( expected_rc, expected_string, ... ) do { \
TEST_RESULTS += DO_TESTPRINTF(IMPLFILE, __FILE__, __LINE__, expected_rc, expected_string, __VA_ARGS__); \
} while (0);
TEST_RESULTS += DO_TESTPRINTF(IMPLFILE, __FILE__, __LINE__, expected_rc, expected_string, __VA_ARGS__); \
} while (0);
{

View File

@@ -1,6 +1,6 @@
#define SCANF_TEST( expected_rc, input_string, ... ) do { \
TEST_RESULTS += DO_TESTSCANF(IMPLFILE, __FILE__, __LINE__, expected_rc, input_string, __VA_ARGS__); \
} while (0);
TEST_RESULTS += DO_TESTSCANF(IMPLFILE, __FILE__, __LINE__, expected_rc, input_string, __VA_ARGS__); \
} while (0);
{
char buffer[100];