Squash commits for public release

This commit is contained in:
2025-02-12 09:54:05 -05:00
commit 7118adc514
1108 changed files with 80873 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
#pragma once
#include <cstddef>
namespace LFoundation {
class ByteOrder {
public:
template <typename T>
[[gnu::always_inline]] static inline T from_network(T value)
{
if constexpr (sizeof(T) == 8) {
return __builtin_bswap64(value);
}
if constexpr (sizeof(T) == 4) {
return __builtin_bswap32(value);
}
if constexpr (sizeof(T) == 2) {
return __builtin_bswap16(value);
}
if constexpr (sizeof(T) == 1) {
return value;
}
}
};
} // namespace LFoundation

View File

@@ -0,0 +1,38 @@
#pragma once
namespace LFoundation {
class Event {
public:
enum Type {
Invalid = 0,
FdWaiterRead,
FdWaiterWrite,
DeferredInvoke,
Other,
};
explicit Event(int type)
: m_type(type)
{
}
bool operator==(const Event& other)
{
return m_type == other.m_type;
}
bool operator!=(const Event& other)
{
return m_type != other.m_type;
}
~Event() = default;
int type() const { return m_type; }
private:
int m_type;
};
} // namespace LFoundation

View File

@@ -0,0 +1,85 @@
#pragma once
#include <functional>
#include <libfoundation/Event.h>
#include <libfoundation/EventReceiver.h>
#include <libfoundation/Receivers.h>
#include <list>
#include <memory>
#include <vector>
namespace LFoundation {
class QueuedEvent {
public:
friend class EventLoop;
QueuedEvent(EventReceiver& rec, Event* ptr)
: event(ptr)
, receiver(rec)
{
}
QueuedEvent(QueuedEvent&& qe)
: event(std::move(qe.event))
, receiver(qe.receiver)
{
}
QueuedEvent& operator=(QueuedEvent&& qe)
{
event = std::move(qe.event);
receiver = qe.receiver;
return *this;
}
~QueuedEvent() = default;
EventReceiver& receiver;
std::unique_ptr<Event> event { nullptr };
};
class EventLoop {
public:
inline static EventLoop& the()
{
extern EventLoop* s_LFoundation_EventLoop_the;
return *s_LFoundation_EventLoop_the;
}
EventLoop();
inline void add(int fd, std::function<void(void)> on_read, std::function<void(void)> on_write)
{
m_waiting_fds.push_back(FDWaiter(fd, on_read, on_write));
}
inline void add(const Timer& timer)
{
m_timers.push_back(timer);
}
inline void add(Timer&& timer)
{
m_timers.push_back(std::move(timer));
}
inline void add(EventReceiver& rec, Event* ptr)
{
m_event_queue.push_back(QueuedEvent(rec, ptr));
}
inline void stop(int exit_code) { m_exit_code = exit_code, m_stop_flag = true; }
int run();
private:
void pump();
void cleanup_timers();
void check_fds();
void check_timers();
bool m_stop_flag { false };
int m_exit_code { 0 };
std::vector<FDWaiter> m_waiting_fds;
std::list<Timer> m_timers;
std::vector<QueuedEvent> m_event_queue;
};
} // namespace LFoundation

View File

@@ -0,0 +1,17 @@
#pragma once
#include <libfoundation/Event.h>
#include <memory>
namespace LFoundation {
class EventReceiver {
public:
EventReceiver() = default;
~EventReceiver() = default;
virtual void receive_event(std::unique_ptr<Event> event) { }
private:
};
} // namespace LFoundation

View File

@@ -0,0 +1,56 @@
#pragma once
#include <dirent.h>
#include <string>
#include <unistd.h>
#include <vector>
namespace LFoundation {
class FileManager {
public:
FileManager() = default;
~FileManager() = default;
template <typename Callback>
int foreach_object(const std::string& path, Callback callback) const
{
const size_t temporal_buffer_size = 1024;
char* temporal_buffer = new char[temporal_buffer_size];
struct linux_dirent {
uint32_t inode;
uint16_t rec_len;
uint8_t name_len;
uint8_t file_type;
char* name;
}* d;
int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY);
if (fd < 0) {
return -1;
}
for (;;) {
int nread = getdents(fd, temporal_buffer, temporal_buffer_size);
if (nread == 0) {
break;
}
for (int bpos = 0; bpos < nread;) {
d = (struct linux_dirent*)(temporal_buffer + bpos);
if (((char*)&d->name)[0] != '.') {
callback((char*)&d->name);
}
bpos += d->rec_len;
}
}
close(fd);
delete[] temporal_buffer;
return 0;
}
private:
};
} // namespace LFoundation

View File

@@ -0,0 +1,171 @@
#pragma once
namespace LFoundation {
enum Keycode {
// Alphanumeric keys ////////////////
KEY_CTRLC = '\003',
KEY_SPACE = ' ',
KEY_0 = '0',
KEY_1 = '1',
KEY_2 = '2',
KEY_3 = '3',
KEY_4 = '4',
KEY_5 = '5',
KEY_6 = '6',
KEY_7 = '7',
KEY_8 = '8',
KEY_9 = '9',
KEY_A = 'a',
KEY_B = 'b',
KEY_C = 'c',
KEY_D = 'd',
KEY_E = 'e',
KEY_F = 'f',
KEY_G = 'g',
KEY_H = 'h',
KEY_I = 'i',
KEY_J = 'j',
KEY_K = 'k',
KEY_L = 'l',
KEY_M = 'm',
KEY_N = 'n',
KEY_O = 'o',
KEY_P = 'p',
KEY_Q = 'q',
KEY_R = 'r',
KEY_S = 's',
KEY_T = 't',
KEY_U = 'u',
KEY_V = 'v',
KEY_W = 'w',
KEY_X = 'x',
KEY_Y = 'y',
KEY_Z = 'z',
KEY_RETURN = '\r',
KEY_ESCAPE = 0x1001,
KEY_BACKSPACE = '\b',
// Arrow keys ////////////////////////
KEY_UP = 0x1100,
KEY_DOWN = 0x1101,
KEY_LEFT = 0x1102,
KEY_RIGHT = 0x1103,
// Function keys /////////////////////
KEY_F1 = 0x1201,
KEY_F2 = 0x1202,
KEY_F3 = 0x1203,
KEY_F4 = 0x1204,
KEY_F5 = 0x1205,
KEY_F6 = 0x1206,
KEY_F7 = 0x1207,
KEY_F8 = 0x1208,
KEY_F9 = 0x1209,
KEY_F10 = 0x120a,
KEY_F11 = 0x120b,
KEY_F12 = 0x120b,
KEY_F13 = 0x120c,
KEY_F14 = 0x120d,
KEY_F15 = 0x120e,
KEY_DOT = '.',
KEY_COMMA = ',',
KEY_COLON = ':',
KEY_SEMICOLON = ';',
KEY_SLASH = '/',
KEY_BACKSLASH = '\\',
KEY_PLUS = '+',
KEY_MINUS = '-',
KEY_ASTERISK = '*',
KEY_EXCLAMATION = '!',
KEY_QUESTION = '?',
KEY_QUOTEDOUBLE = '\"',
KEY_QUOTE = '\'',
KEY_EQUAL = '=',
KEY_HASH = '#',
KEY_PERCENT = '%',
KEY_AMPERSAND = '&',
KEY_UNDERSCORE = '_',
KEY_LEFTPARENTHESIS = '(',
KEY_RIGHTPARENTHESIS = ')',
KEY_LEFTBRACKET = '[',
KEY_RIGHTBRACKET = ']',
KEY_LEFTCURL = '{',
KEY_RIGHTCURL = '}',
KEY_DOLLAR = '$',
KEY_POUND = 0,
KEY_EURO = '$',
KEY_LESS = '<',
KEY_GREATER = '>',
KEY_BAR = '|',
KEY_GRAVE = '`',
KEY_TILDE = '~',
KEY_AT = '@',
KEY_CARRET = '^',
// Numeric keypad //////////////////////
KEY_KP_0 = '0',
KEY_KP_1 = '1',
KEY_KP_2 = '2',
KEY_KP_3 = '3',
KEY_KP_4 = '4',
KEY_KP_5 = '5',
KEY_KP_6 = '6',
KEY_KP_7 = '7',
KEY_KP_8 = '8',
KEY_KP_9 = '9',
KEY_KP_PLUS = '+',
KEY_KP_MINUS = '-',
KEY_KP_DECIMAL = '.',
KEY_KP_DIVIDE = '/',
KEY_KP_ASTERISK = '*',
KEY_KP_NUMLOCK = 0x300f,
KEY_KP_ENTER = 0x3010,
KEY_TAB = 0x4000,
KEY_CAPSLOCK = 0x4001,
// Modify keys ////////////////////////////
KEY_LSHIFT = 0x4002,
KEY_LCTRL = 0x4003,
KEY_LALT = 0x4004,
KEY_LWIN = 0x4005,
KEY_RSHIFT = 0x4006,
KEY_RCTRL = 0x4007,
KEY_RALT = 0x4008,
KEY_RWIN = 0x4009,
KEY_INSERT = 0x400a,
KEY_DELETE = 0x400b,
KEY_HOME = 0x400c,
KEY_END = 0x400d,
KEY_PAGEUP = 0x400e,
KEY_PAGEDOWN = 0x400f,
KEY_SCROLLLOCK = 0x4010,
KEY_PAUSE = 0x4011,
// Multimedia keys ////////////////////////
KEY_PREV_TRACK = 0x5001,
KEY_NEXT_TRACK = 0x5002,
KEY_MUTE = 0x5003,
KEY_CALC = 0x5004,
KEY_PLAY = 0x5005,
KEY_STOP = 0x5006,
KEY_VOL_DOWN = 0x5007,
KEY_VOL_UP = 0x5008,
KEY_WWW_HOME = 0x500a,
KEY_UNKNOWN,
KEY_NUMKEYCODES
};
} // namespace LFoundation

View File

@@ -0,0 +1,11 @@
#include <ostream>
namespace LFoundation::Logger {
extern std::ostream debug;
} // namespace LFoundation::Logger
namespace Logger {
using LFoundation::Logger::debug;
} // namespace Logger

View File

@@ -0,0 +1,22 @@
#pragma once
#include <cstddef>
namespace LFoundation {
[[gnu::always_inline]] inline float fast_inv_sqrt(float x)
{
float xhalf = 0.5f * x;
int i = *(int*)&x;
i = 0x5f3759df - (i >> 1);
x = *(float*)&i;
x = x * (1.5f - xhalf * x * x);
return x;
}
[[gnu::always_inline]] inline float fast_sqrt(float x)
{
return 1.0 / fast_inv_sqrt(x);
}
} // namespace LFoundation

View File

@@ -0,0 +1,89 @@
#pragma once
#include <cstddef>
namespace LFoundation {
[[gnu::always_inline]] inline void fast_copy(uint32_t* dest, const uint32_t* src, std::size_t count)
{
#if defined(__i386__) || defined(__x86_64__)
asm volatile(
"rep movsl\n"
: "=S"(src), "=D"(dest), "=c"(count)
: "S"(src), "D"(dest), "c"(count)
: "memory");
#elif __arm__
while (count--) {
asm("pld [%0, #128]" ::"r"(src));
*dest++ = *src++;
}
#elif __aarch64__
while (count--) {
*dest++ = *src++;
}
#elif defined(__riscv) && (__riscv_xlen == 64)
while (count--) {
*dest++ = *src++;
}
#endif
}
[[gnu::always_inline]] inline void fast_set(uint32_t* dest, uint32_t val, std::size_t count)
{
#if defined(__i386__) || defined(__x86_64__)
asm volatile(
"rep stosl\n"
: "=D"(dest), "=c"(count)
: "D"(dest), "c"(count), "a"(val)
: "memory");
#elif __arm__
asm volatile(
"cmp %[count], #0\n"
"beq fast_set_exit%=\n"
"tst %[ptr], #15\n"
"beq fast_set_16bytes_aligned_entry%=\n"
"fast_set_4bytes_aligned_loop%=:\n"
"subs %[count], %[count], #1\n"
"str %[value], [%[ptr]], #4\n"
"beq fast_set_exit%=\n"
"tst %[ptr], #15\n"
"bne fast_set_4bytes_aligned_loop%=\n"
"fast_set_16bytes_aligned_entry%=:\n"
"cmp %[count], #4\n"
"blt fast_set_16bytes_aligned_exit%=\n"
"fast_set_16bytes_aligned_preloop%=:\n"
"mov r4, %[value]\n"
"mov r5, %[value]\n"
"mov r6, %[value]\n"
"mov r7, %[value]\n"
"fast_set_16bytes_aligned_loop%=:\n"
"subs %[count], %[count], #4\n"
"stmia %[ptr]!, {r4,r5,r6,r7}\n"
"cmp %[count], #4\n"
"bge fast_set_16bytes_aligned_loop%=\n"
"fast_set_16bytes_aligned_exit%=:\n"
"cmp %[count], #0\n"
"beq fast_set_exit%=\n"
"fast_set_4bytes_aligned_loop_2_%=:\n"
"subs %[count], %[count], #1\n"
"str %[value], [%[ptr]], #4\n"
"bne fast_set_4bytes_aligned_loop_2_%=\n"
"fast_set_exit%=:"
: [value] "=r"(val),
[ptr] "=r"(dest),
[count] "=r"(count)
: "[value]"(val),
"[ptr]"(dest),
"[count]"(count)
: "r4", "r5", "r6", "r7", "memory", "cc");
#elif __aarch64__
while (count--) {
*dest++ = val;
}
#elif defined(__riscv) && (__riscv_xlen == 64)
while (count--) {
*dest++ = val;
}
#endif
}
} // namespace LFoundation

View File

@@ -0,0 +1,12 @@
#pragma once
#include <libfoundation/EventReceiver.h>
namespace LFoundation {
class Object : public LFoundation::EventReceiver {
public:
Object() = default;
~Object() = default;
};
} // namespace LFoundation

View File

@@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <unistd.h>
#include <vector>
namespace LFoundation {
class ProcessInfo {
public:
inline static ProcessInfo& the()
{
extern ProcessInfo* s_LFoundation_ProcessInfo_the;
return *s_LFoundation_ProcessInfo_the;
}
ProcessInfo(int argc, char** argv);
~ProcessInfo() = default;
std::vector<std::string>& arguments() { return m_args; }
const std::vector<std::string>& arguments() const { return m_args; }
const std::string& process_name() const { return m_process_name; }
const std::string& bundle_id() const { return m_bundle_id; }
int processor_count();
bool mobile_app_on_desktop() { return false; }
private:
// TODO: Maybe move out info file parsing to a seperate file?
void parse_info_file();
std::vector<std::string> m_args;
std::string m_process_name;
std::string m_bundle_id;
int m_processor_count { -1 };
};
} // namespace LFoundation

View File

@@ -0,0 +1,200 @@
#pragma once
#include <ctime>
#include <libfoundation/Event.h>
#include <libfoundation/EventReceiver.h>
#include <libfoundation/Logger.h>
#include <memory>
#include <vector>
namespace LFoundation {
class FDWaiterReadEvent final : public Event {
public:
FDWaiterReadEvent()
: Event(Event::Type::FdWaiterRead)
{
}
~FDWaiterReadEvent() = default;
};
class FDWaiterWriteEvent final : public Event {
public:
FDWaiterWriteEvent()
: Event(Event::Type::FdWaiterWrite)
{
}
~FDWaiterWriteEvent() = default;
};
class FDWaiter : public EventReceiver {
public:
friend class EventLoop;
FDWaiter(int fd, std::function<void(void)> on_read, std::function<void(void)> on_write)
: EventReceiver()
, m_fd(fd)
, m_on_read(on_read)
, m_on_write(on_write)
{
}
FDWaiter(FDWaiter&& fdw)
: EventReceiver()
, m_fd(fdw.m_fd)
, m_on_read(fdw.m_on_read)
, m_on_write(fdw.m_on_write)
{
}
FDWaiter& operator=(const FDWaiter& fdw)
{
m_fd = fdw.m_fd;
m_on_read = fdw.m_on_read;
m_on_write = fdw.m_on_write;
return *this;
}
FDWaiter& operator=(FDWaiter&& fdw)
{
m_fd = fdw.m_fd;
m_on_read = fdw.m_on_read;
m_on_write = fdw.m_on_write;
return *this;
}
void receive_event(std::unique_ptr<Event> event) override
{
if (event->type() == Event::Type::FdWaiterRead) {
m_on_read();
} else if (event->type() == Event::Type::FdWaiterWrite) {
m_on_write();
}
}
inline int fd() const { return m_fd; }
private:
int m_fd;
std::function<void(void)> m_on_read;
std::function<void(void)> m_on_write;
};
class TimerEvent final : public Event {
public:
TimerEvent()
: Event(Event::Type::DeferredInvoke)
{
}
~TimerEvent() = default;
};
class Timer : public EventReceiver {
public:
static const bool Once = false;
static const bool Repeat = true;
friend class EventLoop;
explicit Timer(std::function<void(void)> callback, std::time_t time_interval, bool repeat = false)
: EventReceiver()
, m_callback(callback)
, m_time_interval(time_interval)
, m_repeat(repeat)
, m_valid(true)
{
clock_gettime(CLOCK_MONOTONIC, &m_expire_time);
reload(m_expire_time);
}
Timer(const Timer& fdw)
: EventReceiver()
, m_callback(fdw.m_callback)
, m_time_interval(fdw.m_time_interval)
, m_repeat(fdw.m_repeat)
, m_expire_time(fdw.m_expire_time)
, m_valid(fdw.m_valid)
{
}
Timer(Timer&& fdw)
: EventReceiver()
, m_callback(fdw.m_callback)
, m_time_interval(fdw.m_time_interval)
, m_repeat(fdw.m_repeat)
, m_expire_time(fdw.m_expire_time)
, m_valid(fdw.m_valid)
{
}
Timer& operator=(const Timer& fdw)
{
m_callback = fdw.m_callback;
return *this;
}
Timer& operator=(Timer&& fdw)
{
m_callback = fdw.m_callback;
return *this;
}
inline bool valid() const { return m_valid; }
inline bool repeated() const { return m_repeat; }
inline bool expired(const std::timespec& now) const
{
return now.tv_sec > m_expire_time.tv_sec || (now.tv_sec == m_expire_time.tv_sec && now.tv_nsec >= m_expire_time.tv_nsec);
}
void reload(const std::timespec& now)
{
std::time_t secs = now.tv_nsec + (m_time_interval % 1000) * 1000000;
m_expire_time.tv_nsec = (secs % 1000000000);
m_expire_time.tv_sec = now.tv_sec + m_time_interval / 1000 + (secs / 1000000000);
}
void receive_event(std::unique_ptr<Event> event) override
{
m_callback();
}
private:
inline void mark_invalid() { m_valid = false; }
std::function<void(void)> m_callback;
std::timespec m_expire_time;
std::time_t m_time_interval;
bool m_valid { true };
bool m_repeat { false };
};
class CallEvent final : public Event {
public:
CallEvent(void (*callback)())
: Event(Event::Type::DeferredInvoke)
, m_callback(callback)
{
}
~CallEvent() = default;
private:
void (*m_callback)();
};
// class Caller : public EventReceiver {
// public:
// friend class EventLoop;
// Caller()
// : EventReceiver()
// {
// }
// void receive_event(std::unique_ptr<Event> event) override
// {
// if (event->type() == Event::Type::DeferredInvoke) {
// }
// }
// };
} // namespace LFoundation

View File

@@ -0,0 +1,74 @@
#pragma once
#include <x/shared_buffer.h>
namespace LFoundation {
template <typename T>
class SharedBuffer {
public:
SharedBuffer() = default;
SharedBuffer(size_t size)
: m_size(size)
{
m_id = shared_buffer_create((uint8_t**)&m_data, m_size * sizeof(T));
}
SharedBuffer(int id)
: m_id(id)
{
if (shared_buffer_get(m_id, (uint8_t**)&m_data) != 0) {
m_id = -1;
}
}
~SharedBuffer()
{
}
inline void create(size_t size)
{
m_size = size;
m_id = shared_buffer_create((uint8_t**)&m_data, m_size * sizeof(T));
}
inline void open(int id)
{
m_id = id;
if (shared_buffer_get(m_id, (uint8_t**)&m_data) != 0) {
m_id = -1;
}
}
void free()
{
if (alive()) {
shared_buffer_free(id());
m_id = -1;
}
}
inline void resize(size_t new_size)
{
free();
create(new_size);
}
inline bool alive() const { return m_id >= 0; }
inline const T& at(size_t i) const { return data()[i]; }
inline T& at(size_t i) { return data()[i]; }
inline size_t size() const { return m_size; }
inline const T& operator[](size_t i) const { return at(i); }
inline T& operator[](size_t i) { return at(i); }
inline int id() const { return m_id; }
inline T* data() { return m_data; }
private:
int m_id { -1 };
size_t m_size { 0 };
T* m_data { nullptr };
};
} // namespace LFoundation

View File

@@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <unistd.h>
#include <vector>
namespace LFoundation {
class URL {
public:
enum Scheme {
Http,
Https,
File,
};
explicit URL(const std::string& url)
: m_url(url)
{
m_scheme = parse_scheme(url);
}
~URL() = default;
bool is_file() { return m_scheme == Scheme::File; }
const std::string& url() const { return m_url; }
const Scheme& scheme() const { return m_scheme; }
private:
Scheme parse_scheme(const std::string& path);
std::string m_url;
Scheme m_scheme;
};
} // namespace LFoundation

View File

@@ -0,0 +1,47 @@
#pragma once
/* puff.h
Copyright (C) 2002-2013 Mark Adler, all rights reserved
version 2.3, 21 Jan 2013
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Mark Adler madler@alumni.caltech.edu
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <sys/types.h>
/*
* See puff.c for purpose and usage.
*/
#ifndef NIL
#define NIL ((unsigned char*)0) /* for no output option */
#endif
int puff(unsigned char* dest, /* pointer to destination pointer */
size_t* destlen, /* amount of output space */
const unsigned char* source, /* pointer to source data pointer */
size_t* sourcelen); /* amount of input available */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,15 @@
#include <__std_streambuffer>
namespace LFoundation::Logger {
class StreamBuf : public std::__stdoutbuf<char> {
public:
StreamBuf(FILE* file)
: __stdoutbuf<char>(file)
{
}
~StreamBuf() = default;
};
} // namespace LFoundation::Logger

View File

@@ -0,0 +1,68 @@
#pragma once
#include <cctype>
#include <string>
#include <unistd.h>
namespace LFoundation::Json {
class Lexer {
public:
Lexer() = default;
Lexer(const std::string& filepath)
: m_text_data()
, m_pointer(0)
{
set_file(filepath);
}
~Lexer() = default;
int set_file(const std::string& filepath);
bool is_eof() const { return m_pointer >= m_text_data.size(); }
int lookup_char() const
{
if (is_eof()) {
return EOF;
}
return m_text_data[m_pointer];
}
int next_char()
{
if (is_eof()) {
return EOF;
}
return m_text_data[m_pointer++];
}
void skip_spaces()
{
while (std::isspace(lookup_char())) {
next_char();
}
}
bool eat_string(std::string& word)
{
// TODO: Fix stop at "
while (std::isprint(lookup_char()) && lookup_char() != '\"') {
word.push_back(next_char());
};
return true;
}
bool eat_token(char what)
{
skip_spaces();
return next_char() == what;
}
private:
int m_pointer;
std::string m_text_data;
};
} // namespace LFoundation

View File

@@ -0,0 +1,162 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <map>
#include <string>
#include <vector>
namespace LFoundation::Json {
class Object {
public:
enum Type {
Array,
String,
Number,
Dict,
List,
Null,
Bool,
Invalid,
};
constexpr static Object::Type ObjType = Type::Invalid;
Object() = default;
Object(Type type)
: m_type(type)
{
}
virtual ~Object() = default;
Type type() const { return m_type; }
bool invalid() const { return type() == Type::Invalid; }
template <class ObjectT>
Object* assert_object()
{
assert(ObjectT::ObjType == m_type);
return this;
}
template <class ObjectT>
ObjectT* cast_to_no_assert() { return (ObjectT*)this; }
template <class ObjectT>
ObjectT* cast_to() { return (ObjectT*)assert_object<ObjectT>(); }
private:
Type m_type { Invalid };
};
class StringObject : public Object {
public:
constexpr static Object::Type ObjType = Type::String;
StringObject()
: Object(ObjType)
{
}
~StringObject() = default;
std::string& data() { return m_data; }
const std::string& data() const { return m_data; }
private:
std::string m_data;
};
class DictObject : public Object {
public:
constexpr static Object::Type ObjType = Type::Dict;
DictObject()
: Object(ObjType)
{
}
~DictObject()
{
// TODO: Add iterators to map.
// for (auto it : m_data) {
// delete it.second;
// }
}
std::map<std::string, Object*>& data() { return m_data; }
const std::map<std::string, Object*>& data() const { return m_data; }
private:
std::map<std::string, Object*> m_data;
};
class ListObject : public Object {
public:
constexpr static Object::Type ObjType = Type::List;
ListObject()
: Object(ObjType)
{
}
~ListObject()
{
for (auto& i : m_data) {
delete i;
}
}
std::vector<Object*>& data() { return m_data; }
const std::vector<Object*>& data() const { return m_data; }
private:
std::vector<Object*> m_data;
};
class NullObject : public Object {
public:
constexpr static Object::Type ObjType = Type::Null;
NullObject()
: Object(ObjType)
{
}
~NullObject() = default;
private:
};
class BoolObject : public Object {
public:
constexpr static Object::Type ObjType = Type::Bool;
BoolObject()
: Object(ObjType)
{
}
~BoolObject() = default;
bool& data() { return m_data; }
const bool& data() const { return m_data; }
private:
bool m_data;
};
class InvalidObject : public Object {
public:
constexpr static Object::Type ObjType = Type::Invalid;
InvalidObject()
: Object(ObjType)
{
}
~InvalidObject() = default;
};
} // namespace LFoundation

View File

@@ -0,0 +1,41 @@
#pragma once
#include <libfoundation/json/Lexer.h>
#include <libfoundation/json/Object.h>
#include <string>
namespace LFoundation::Json {
class Parser {
public:
Parser(const std::string& filepath)
{
int err = m_lexer.set_file(filepath);
if (err) {
m_root_object = new InvalidObject();
}
}
~Parser() = default;
Object* object()
{
if (!m_root_object) {
m_root_object = parse_object();
}
return m_root_object;
}
private:
StringObject* parse_string();
DictObject* parse_dict();
ListObject* parse_list();
BoolObject* parse_bool();
NullObject* parse_null();
Object* parse_object();
Object* m_root_object { nullptr };
Lexer m_lexer;
};
} // namespace LFoundation