From d403ce66efb9b1150d103bc3b15eeaad312553a8 Mon Sep 17 00:00:00 2001
From: Mitya Selivanov <automainint@guattari.tech>
Date: Tue, 12 Sep 2023 16:21:29 +0200
Subject: Update TODO

---
 TODO                             |  17 ++++--
 source/kit/array_ref.h           |   8 +--
 source/kit/async_function.h      |  22 +++----
 source/kit/atomic.h              |  14 ++---
 source/kit/bigint.h              |  12 ++--
 source/kit/dynamic_array.h       |   8 +--
 source/kit/file.h                |  46 +++++++-------
 source/kit/input_buffer.h        |  12 ++--
 source/kit/input_stream.h        |   8 +--
 source/kit/lower_bound.h         |   8 +--
 source/kit/mersenne_twister_64.h |   8 +--
 source/kit/move_back.h           |   8 +--
 source/kit/secure_random.h       |   8 +--
 source/kit/status.h              |  11 +---
 source/kit/string_ref.h          |   4 +-
 source/kit/time.h                |  11 ++++
 source/kit/unival.c              |  51 ++++++++++++++++
 source/kit/unival.h              |  79 ++++++++++++++++++++++++
 source/kit/xml.c                 | 128 +++++++++++++++++++++++++++++++++++++++
 source/kit/xml.h                 |  50 +++++++++++++++
 source/tests/input_buffer.test.c |   2 +-
 21 files changed, 417 insertions(+), 98 deletions(-)
 create mode 100644 source/kit/unival.c
 create mode 100644 source/kit/unival.h
 create mode 100644 source/kit/xml.c
 create mode 100644 source/kit/xml.h

diff --git a/TODO b/TODO
index 3011831..a9017da 100644
--- a/TODO
+++ b/TODO
@@ -7,17 +7,24 @@ To-Do
   - Memory pool
   - Memory stack
   - Bump
+- Data formats
+  - UTF-8.
+  - XML
+  - JSON
+  - unival
 - System
+  - Better atomics support.
+  - System call
   - Terminal
   - File and memory mapping
   - Inter-process transfer
+  - Sockets tests.
   - Graphics boilerplate
     - X11 (Linux)
     - Win32 (Windows)
     - Cocoa (macOS)
     - Emscripten (WebAssembly)
-- String builder.
-- UTF-8.
-- Sockets tests.
-- HTTP 1 client.
-- Better atomics support.
+    - Parsing OpenGL interface
+- Utils
+  - String builder.
+  - HTTP 1 client.
diff --git a/source/kit/array_ref.h b/source/kit/array_ref.h
index 20851f9..3ca6918 100644
--- a/source/kit/array_ref.h
+++ b/source/kit/array_ref.h
@@ -42,6 +42,10 @@ i8 kit_ar_compare(i64 left_element_size, i64 left_size,
                  (right_).size, (right_).values,             \
                  (kit_ar_compare_fn) (compare_))
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define ar_compare_fn kit_ar_compare_fn
 #  define ar_equal_bytes kit_ar_equal_bytes
@@ -53,8 +57,4 @@ i8 kit_ar_compare(i64 left_element_size, i64 left_size,
 #  define AR_COMPARE KIT_AR_COMPARE
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/async_function.h b/source/kit/async_function.h
index 2c126ab..4e725e7 100644
--- a/source/kit/async_function.h
+++ b/source/kit/async_function.h
@@ -9,12 +9,6 @@
 extern "C" {
 #endif
 
-#ifdef __GNUC__
-#  pragma GCC diagnostic push
-#  pragma GCC diagnostic ignored "-Wunused-function"
-#  pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#endif
-
 typedef struct {
   i32 _;
 } kit_af_void;
@@ -32,6 +26,12 @@ typedef struct {
   KIT_AF_STATE_DATA;
 } kit_af_type_void;
 
+#if defined(__GNUC__) || defined(__clang__)
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wunused-function"
+#  pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#endif
+
 #define KIT_AF_INTERNAL(coro_) (*((kit_af_type_void *) (coro_)))
 
 #ifdef KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH
@@ -49,7 +49,7 @@ static void kit_async_function_dispatch(void *promise) {
 }
 #endif
 
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__clang__)
 #  pragma GCC diagnostic pop
 #endif
 
@@ -196,6 +196,10 @@ static void kit_async_function_dispatch(void *promise) {
   KIT_AF_FINISHED_N((return_), (promises_),     \
                     sizeof(promises_) / sizeof((promises_)[0]))
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define af_void kit_af_void
 #  define af_state_machine kit_af_state_machine
@@ -229,8 +233,4 @@ static void kit_async_function_dispatch(void *promise) {
 #  define AF_FINISHED_ALL KIT_AF_FINISHED_ALL
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/atomic.h b/source/kit/atomic.h
index f10402b..9d7f284 100644
--- a/source/kit/atomic.h
+++ b/source/kit/atomic.h
@@ -10,13 +10,13 @@
 #else
 #  include <assert.h>
 
-#  define KIT_ATOMIC(type_) type_ volatile
-#  define _Atomic volatile
-
 #  ifdef __cplusplus
 extern "C" {
 #  endif
 
+#  define KIT_ATOMIC(type_) type_ volatile
+#  define _Atomic volatile
+
 enum {
   memory_order_relaxed,
   memory_order_consume,
@@ -211,14 +211,14 @@ u64 kit_atomic_fetch_add_explicit_64(u64 volatile *var, u64 value,
 
 #  define atomic_fetch_add(var_, value_) \
     atomic_fetch_add(var_, value_, memory_order_seq_cst)
+
+#  ifdef __cplusplus
+}
+#  endif
 #endif
 
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define ATOMIC KIT_ATOMIC
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/bigint.h b/source/kit/bigint.h
index be831b7..28addb0 100644
--- a/source/kit/bigint.h
+++ b/source/kit/bigint.h
@@ -18,7 +18,7 @@ typedef struct {
   u32 v[KIT_BIGINT_SIZE / 4];
 } kit_bigint_t;
 
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__clang__)
 #  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wunused-function"
 #  pragma GCC diagnostic ignored "-Wunknown-pragmas"
@@ -551,7 +551,7 @@ static kit_bigint_t kit_bi_from_base58(kit_str_t base58) {
   return z;
 }
 
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__clang__)
 #  pragma GCC            pop_options
 #  pragma GCC diagnostic pop
 #endif
@@ -571,6 +571,10 @@ static kit_bigint_t kit_bi_from_base58(kit_str_t base58) {
 #define KIT_BASE58(static_str_) \
   kit_bi_from_base58(kit_str(sizeof(static_str_) - 1, (static_str_)))
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define bigint_t kit_bigint_t
 #  define bi_u32 kit_bi_u32
@@ -598,8 +602,4 @@ static kit_bigint_t kit_bi_from_base58(kit_str_t base58) {
 #  define BASE58 KIT_BASE58
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/dynamic_array.h b/source/kit/dynamic_array.h
index dbb18d9..df7b933 100644
--- a/source/kit/dynamic_array.h
+++ b/source/kit/dynamic_array.h
@@ -101,6 +101,10 @@ void kit_da_resize_exact(kit_da_void_t *array, i64 element_size,
     KIT_DA_RESIZE((array_), (array_).size - 1);       \
   } while (0)
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define da_void_t kit_da_void_t
 #  define da_init kit_da_init
@@ -117,8 +121,4 @@ void kit_da_resize_exact(kit_da_void_t *array, i64 element_size,
 #  define DA_ERASE KIT_DA_ERASE
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/file.h b/source/kit/file.h
index c3cb0b0..c0a8f23 100644
--- a/source/kit/file.h
+++ b/source/kit/file.h
@@ -9,6 +9,25 @@
 extern "C" {
 #endif
 
+typedef enum {
+  KIT_PATH_NONE,
+  KIT_PATH_FILE,
+  KIT_PATH_FOLDER
+} kit_path_type_t;
+
+typedef struct {
+  kit_status_t status;
+
+  i64 time_modified_sec;
+  i32 time_modified_nsec;
+  i64 size;
+} kit_file_info_t;
+
+typedef struct {
+  kit_status_t status;
+  KIT_DA(kit_str_builder_t) files;
+} kit_path_list_t;
+
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #  define KIT_PATH_DELIM_C '\\'
 #  define KIT_PATH_DELIM "\\"
@@ -44,34 +63,19 @@ kit_status_t kit_file_remove_folder(kit_str_t path);
 kit_status_t kit_file_remove_recursive(kit_str_t        path,
                                        kit_allocator_t *alloc);
 
-typedef enum {
-  KIT_PATH_NONE,
-  KIT_PATH_FILE,
-  KIT_PATH_FOLDER
-} kit_path_type_t;
-
 kit_path_type_t kit_path_type(kit_str_t path);
 
-typedef struct {
-  kit_status_t status;
-
-  i64 time_modified_sec;
-  i32 time_modified_nsec;
-  i64 size;
-} kit_file_info_t;
-
 kit_file_info_t kit_file_info(kit_str_t path);
 
-typedef struct {
-  kit_status_t status;
-  KIT_DA(kit_str_builder_t) files;
-} kit_path_list_t;
-
 kit_path_list_t kit_file_enum_folder(kit_str_t        path,
                                      kit_allocator_t *alloc);
 
 void kit_path_list_destroy(kit_path_list_t list);
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define path_norm kit_path_norm
 #  define path_join kit_path_join
@@ -102,8 +106,4 @@ void kit_path_list_destroy(kit_path_list_t list);
 #  define PATH_FOLDER KIT_PATH_FOLDER
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/input_buffer.h b/source/kit/input_buffer.h
index 51fccbb..5de85c9 100644
--- a/source/kit/input_buffer.h
+++ b/source/kit/input_buffer.h
@@ -16,13 +16,13 @@ typedef struct {
   kit_str_builder_t data;
 } kit_ib_handle_t;
 
+typedef i8 (*kit_ib_read_condition_fn)(kit_str_t data);
+
 kit_ib_handle_t kit_ib_wrap(kit_is_handle_t  upstream,
                             kit_allocator_t *alloc);
 
 kit_ib_handle_t kit_ib_read(kit_ib_handle_t buf, i64 size);
 
-typedef int (*kit_ib_read_condition_fn)(kit_str_t data);
-
 kit_ib_handle_t kit_ib_read_while(kit_ib_handle_t          buf,
                                   kit_ib_read_condition_fn condition);
 
@@ -30,6 +30,10 @@ void kit_ib_destroy(kit_ib_handle_t buf);
 
 #define KIT_IB_WRAP(upstream) kit_ib_wrap(upstream, NULL)
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define ib_handle_t kit_ib_handle_t
 #  define ib_read_condition_fn kit_ib_read_condition_fn
@@ -41,8 +45,4 @@ void kit_ib_destroy(kit_ib_handle_t buf);
 #  define IB_WRAP KIT_IB_WRAP
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/input_stream.h b/source/kit/input_stream.h
index d355983..c60c9e3 100644
--- a/source/kit/input_stream.h
+++ b/source/kit/input_stream.h
@@ -31,6 +31,10 @@ void kit_is_destroy(kit_is_handle_t in);
 #define KIT_IS_READ(in, destination) \
   (in).read((in).state, (destination))
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define is_read_fn kit_is_read_fn
 #  define is_handle_t kit_is_handle_t
@@ -43,8 +47,4 @@ void kit_is_destroy(kit_is_handle_t in);
 #  define IS_READ KIT_IS_READ
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/lower_bound.h b/source/kit/lower_bound.h
index 1ad670b..2ee04b9 100644
--- a/source/kit/lower_bound.h
+++ b/source/kit/lower_bound.h
@@ -31,14 +31,14 @@ extern "C" {
   KIT_LOWER_BOUND_INL(return_val, (array).size,           \
                       (op) ((array).values + index_, (value)))
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define LOWER_BOUND_INL KIT_LOWER_BOUND_INL
 #  define LOWER_BOUND KIT_LOWER_BOUND
 #  define LOWER_BOUND_REF KIT_LOWER_BOUND_REF
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/mersenne_twister_64.h b/source/kit/mersenne_twister_64.h
index c0c04a9..ab496a9 100644
--- a/source/kit/mersenne_twister_64.h
+++ b/source/kit/mersenne_twister_64.h
@@ -22,6 +22,10 @@ void kit_mt64_init(kit_mt64_state_t *state, u64 seed);
 void kit_mt64_rotate(kit_mt64_state_t *state);
 u64  kit_mt64_generate(kit_mt64_state_t *state);
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define mt64_state_t kit_mt64_state_t
 #  define mt64_init_array kit_mt64_init_array
@@ -30,8 +34,4 @@ u64  kit_mt64_generate(kit_mt64_state_t *state);
 #  define mt64_generate kit_mt64_generate
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/move_back.h b/source/kit/move_back.h
index 7313a1e..b6eb979 100644
--- a/source/kit/move_back.h
+++ b/source/kit/move_back.h
@@ -38,14 +38,14 @@ extern "C" {
   KIT_MOVE_BACK_INL(new_size, array,                    \
                     (cond) ((array).values + index_, (value)))
 
+#ifdef __cplusplus
+}
+#endif
+
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define MOVE_BACK_INL KIT_MOVE_BACK_INL
 #  define MOVE_BACK KIT_MOVE_BACK
 #  define MOVE_BACK_REF KIT_MOVE_BACK_REF
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/secure_random.h b/source/kit/secure_random.h
index 36eca0d..c894bd1 100644
--- a/source/kit/secure_random.h
+++ b/source/kit/secure_random.h
@@ -9,12 +9,12 @@ extern "C" {
 
 void kit_secure_random(i64 size, void *data);
 
-#ifndef KIT_DISABLE_SHORT_NAMES
-#  define secure_random kit_secure_random
-#endif
-
 #ifdef __cplusplus
 }
 #endif
 
+#ifndef KIT_DISABLE_SHORT_NAMES
+#  define secure_random kit_secure_random
+#endif
+
 #endif
diff --git a/source/kit/status.h b/source/kit/status.h
index 7b98229..a0f209e 100644
--- a/source/kit/status.h
+++ b/source/kit/status.h
@@ -3,12 +3,9 @@
 
 #include "types.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 enum {
   KIT_OK,
+  KIT_ERROR_NOT_IMPLEMENTED,
   KIT_ERROR_BAD_ALLOC,
   KIT_ERROR_MKDIR_FAILED,
   KIT_ERROR_RMDIR_FAILED,
@@ -17,13 +14,9 @@ enum {
   KIT_ERROR_FILE_DO_NOT_EXIST,
   KIT_ERROR_PATH_TOO_LONG,
   KIT_ERROR_SOCKETS_STARTUP_FAILED,
-  KIT_ERROR_SOCKET_CONTROL_FAILED
+  KIT_ERROR_SOCKET_CONTROL_FAILED,
 };
 
 typedef i32 kit_status_t;
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/source/kit/string_ref.h b/source/kit/string_ref.h
index 29ef8ab..2ed6f46 100644
--- a/source/kit/string_ref.h
+++ b/source/kit/string_ref.h
@@ -11,7 +11,7 @@ extern "C" {
 
 typedef KIT_AR(char) kit_str_t;
 
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__clang__)
 #  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wunused-function"
 #  pragma GCC diagnostic ignored "-Wunknown-pragmas"
@@ -41,7 +41,7 @@ static char *kit_make_bs(kit_str_t s) {
   return result;
 }
 
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__clang__)
 #  pragma GCC            pop_options
 #  pragma GCC diagnostic pop
 #endif
diff --git a/source/kit/time.h b/source/kit/time.h
index 1b57e08..b1816b0 100644
--- a/source/kit/time.h
+++ b/source/kit/time.h
@@ -7,6 +7,10 @@
 
 #include <time.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef TIME_UTC
 #  define TIME_UTC 1
 #endif
@@ -15,6 +19,9 @@
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN 1
 #  endif
+#  ifndef NOMINMAX
+#    define NOMINMAX 1
+#  endif
 #  include <windows.h>
 
 #  define KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull
@@ -40,4 +47,8 @@ static int timespec_get(struct timespec *ts, int base) {
 }
 #endif
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/source/kit/unival.c b/source/kit/unival.c
new file mode 100644
index 0000000..4fed179
--- /dev/null
+++ b/source/kit/unival.c
@@ -0,0 +1,51 @@
+#include "unival.h"
+
+kit_uv_decode_result_t kit_uv_decode(kit_is_handle_t  is,
+                                     kit_allocator_t *alloc) {
+  kit_uv_decode_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+kit_uv_encode_result_t kit_uv_encode(kit_unival_t    *value,
+                                     kit_allocator_t *alloc,
+                                     i32              flags) {
+  kit_uv_encode_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+kit_uv_decode_result_t kit_uv_parse(kit_is_handle_t  is,
+                                    kit_allocator_t *alloc) {
+  kit_uv_decode_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+kit_uv_decode_result_t kit_uv_parse_json(kit_is_handle_t  is,
+                                         kit_allocator_t *alloc) {
+  kit_uv_decode_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+kit_uv_print_result_t kit_uv_print(kit_unival_t    *value,
+                                   kit_allocator_t *alloc,
+                                   i32              flags) {
+  kit_uv_print_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+void kit_uv_destroy(kit_unival_t *value) { }
+
diff --git a/source/kit/unival.h b/source/kit/unival.h
new file mode 100644
index 0000000..3ff1cb2
--- /dev/null
+++ b/source/kit/unival.h
@@ -0,0 +1,79 @@
+#ifndef KIT_UNIVAL_H
+#define KIT_UNIVAL_H
+
+#include "status.h"
+#include "string_builder.h"
+#include "input_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+  KIT_UNIVAL_NONE,
+  KIT_UNIVAL_BOOLEAN,
+  KIT_UNIVAL_INTEGER,
+  KIT_UNIVAL_FLOAT,
+  KIT_UNIVAL_STRING,
+  KIT_UNIVAL_BYTES,
+  KIT_UNIVAL_ARRAY,
+  KIT_UNIVAL_COMPOSITE,
+};
+
+enum { KIT_UNIVAL_ENCODE_BZIP = 1 };
+enum { KIT_UNIVAL_PRINT_PRETTY = 1, KIT_UNIVAL_PRINT_JSON = 2 };
+
+typedef struct kit_uv_data kit_unival_t;
+
+typedef KIT_DA(kit_unival_t) kit_da_unival_t;
+typedef KIT_DA(u8) kit_uv_bytes_t;
+
+struct kit_uv_data {
+  i8 type;
+  union {
+    i8                boolean;
+    i64               integer;
+    f64               float_;
+    kit_str_builder_t string;
+    kit_uv_bytes_t    bytes;
+    kit_da_unival_t   array;
+    struct {
+      kit_da_unival_t keys;
+      kit_da_unival_t values;
+    } composite;
+  };
+};
+
+typedef struct {
+  kit_status_t  status;
+  kit_unival_t *value;
+} kit_uv_decode_result_t;
+
+typedef struct {
+  kit_status_t   status;
+  kit_uv_bytes_t value;
+} kit_uv_encode_result_t;
+
+typedef struct {
+  kit_status_t      status;
+  kit_str_builder_t value;
+} kit_uv_print_result_t;
+
+kit_uv_decode_result_t kit_uv_decode(kit_is_handle_t  is,
+                                     kit_allocator_t *alloc);
+kit_uv_encode_result_t kit_uv_encode(kit_unival_t    *value,
+                                     kit_allocator_t *alloc,
+                                     i32              flags);
+kit_uv_decode_result_t kit_uv_parse(kit_is_handle_t  is,
+                                    kit_allocator_t *alloc);
+kit_uv_decode_result_t kit_uv_parse_json(kit_is_handle_t  is,
+                                         kit_allocator_t *alloc);
+kit_uv_print_result_t  kit_uv_print(kit_unival_t    *value,
+                                    kit_allocator_t *alloc, i32 flags);
+void                   kit_uv_destroy(kit_unival_t *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/xml.c b/source/kit/xml.c
new file mode 100644
index 0000000..22a850a
--- /dev/null
+++ b/source/kit/xml.c
@@ -0,0 +1,128 @@
+#include "xml.h"
+
+#include <assert.h>
+
+static i8 kit_xml_is_spaces(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  if (data.size <= 0)
+    return 1;
+  char c = data.values[data.size - 1];
+  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static i8 kit_xml_is_idword(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  if (data.size <= 0)
+    return 1;
+  if (data.values[0] >= '0' || data.values[0] <= '9')
+    return 0;
+  char c = data.values[data.size - 1];
+  return c == '_' || (c >= '0' && c <= '9') ||
+         (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+static i8 kit_xml_not_tag_open(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size <= 0 || data.values[data.size - 1] != '<';
+}
+
+static i8 kit_xml_not_tag_close(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size <= 0 || data.values[data.size - 1] != '>';
+}
+
+static i8 kit_xml_is_tag_open(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size == 1 && data.values[0] == '<';
+}
+
+static i8 kit_xml_is_tag_close(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size == 1 && data.values[0] == '>';
+}
+
+static i8 kit_xml_is_tag_final_open(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size == 2 && data.values[0] == '<' &&
+         data.values[1] == '/';
+}
+
+static i8 kit_xml_is_tag_final_close(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size == 2 && data.values[0] == '/' &&
+         data.values[0] == '>';
+}
+
+static i8 kit_xml_is_quotes(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  return data.size == 1 && data.values[0] == '"';
+}
+
+static i8 kit_xml_is_inside_quotes(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  if (data.size <= 0)
+    return 1;
+  for (i64 i = 0; i < data.size; i++)
+    if (data.values[i] == '"')
+      return 0;
+    else if (data.values[i] == '\\')
+      i++;
+  return 1;
+}
+
+static i8 kit_xml_is_number(str_t data) {
+  assert(data.size >= 0);
+  assert(data.values != NULL);
+
+  if (data.size <= 0)
+    return 1;
+  char c = data.values[data.size - 1];
+  return c >= '0' && c <= '9';
+}
+
+kit_xml_parse_result_t kit_xml_parse(kit_is_handle_t  is,
+                                     kit_allocator_t *alloc) {
+  kit_xml_parse_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+kit_xml_print_result_t kit_xml_print(kit_xml_t       *xml,
+                                     kit_allocator_t *alloc) {
+  assert(xml != NULL);
+
+  kit_xml_print_result_t result;
+  memset(&result, 0, sizeof result);
+
+  result.status = KIT_ERROR_NOT_IMPLEMENTED;
+  return result;
+}
+
+void kit_xml_destroy(kit_xml_t *xml) {
+  assert(xml != NULL);
+
+  if (xml == NULL)
+    return;
+}
diff --git a/source/kit/xml.h b/source/kit/xml.h
new file mode 100644
index 0000000..409c835
--- /dev/null
+++ b/source/kit/xml.h
@@ -0,0 +1,50 @@
+#ifndef KIT_XML_H
+#define KIT_XML_H
+
+#include "status.h"
+#include "string_builder.h"
+#include "input_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct kit_xml_ kit_xml_t;
+
+typedef struct {
+  kit_str_builder_t name;
+  kit_str_builder_t value;
+} kit_xml_property_t;
+
+typedef KIT_DA(kit_xml_property_t) kit_da_xml_property_t;
+typedef KIT_DA(kit_xml_t) kit_da_xml_t;
+
+struct kit_xml_ {
+  kit_str_builder_t     tag;
+  kit_str_builder_t     text;
+  kit_str_builder_t     tail;
+  kit_da_xml_property_t properties;
+  kit_da_xml_t          children;
+};
+
+typedef struct {
+  kit_status_t status;
+  kit_xml_t   *xml;
+} kit_xml_parse_result_t;
+
+typedef struct {
+  kit_status_t      status;
+  kit_str_builder_t xml;
+} kit_xml_print_result_t;
+
+kit_xml_parse_result_t kit_xml_parse(kit_is_handle_t  is,
+                                     kit_allocator_t *alloc);
+kit_xml_print_result_t kit_xml_print(kit_xml_t       *xml,
+                                     kit_allocator_t *alloc);
+void                   kit_xml_destroy(kit_xml_t *xml);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/tests/input_buffer.test.c b/source/tests/input_buffer.test.c
index d37ebb6..4712da9 100644
--- a/source/tests/input_buffer.test.c
+++ b/source/tests/input_buffer.test.c
@@ -56,7 +56,7 @@ TEST("input buffer read twice") {
   is_destroy(in);
 }
 
-static int is_integer_(str_t const data) {
+static i8 is_integer_(str_t const data) {
   for (ptrdiff_t i = 0; i < data.size; i++)
     if (data.values[i] < '0' || data.values[i] > '9')
       return 0;
-- 
cgit v1.2.3