initial upload

This commit is contained in:
2026-02-26 12:09:40 -06:00
parent 102beb1f6f
commit 8f208f436c
16 changed files with 1183 additions and 0 deletions

9
ckittylib.code-workspace Normal file
View File

@@ -0,0 +1,9 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
}
}

161
src/ckitty/crypto/UUID.hpp Normal file
View File

@@ -0,0 +1,161 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
#include <ckitty/memory/iterators.hpp>
#include <ckitty/system/strings.hpp>
#include <ckitty/crypto/openssl.hpp>
#include <algorithm>
#include <array>
namespace ckitty {
/**
* @brief An UUID v4.
*/
class UUID {
private:
u8 bytes[16];
public:
UUID() {
std::fill(bytes, bytes + sizeof(bytes), 0);
}
UUID(const UUID& uuid) {
for (u32 i = 0; i < 16; i++) {
bytes[i] = uuid.bytes[i];
}
}
i32 compare(const UUID& uuid) const {
for (u32 i = 0; i < 16; i++) {
u32 j = 15 - i;
i32 r = i32(bytes[j]) - i32(uuid.bytes[j]);
if (r < 0) return -1;
if (r > 0) return 1;
}
return 0;
}
bool operator==(const UUID& uuid) const {
return compare(uuid) == 0;
}
bool operator!=(const UUID& uuid) const {
return compare(uuid) != 0;
}
bool operator<(const UUID& uuid) const {
return compare(uuid) < 0;
}
bool operator<=(const UUID& uuid) const {
return compare(uuid) <= 0;
}
bool operator>(const UUID& uuid) const {
return compare(uuid) > 0;
}
bool operator>=(const UUID& uuid) const {
return compare(uuid) >= 0;
}
public:
std::string toString() const {
// %s%s-%s-%s-%s-%s%s%s
// convert to UUID format
char chars[] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'
};
std::string out = "00000000-0000-0000-0000-000000000000";
// 8 starting chars
u32 i = 0, j = 0;
while (j < 16) {
// parse that byte into 2 hex chars
for (u32 k = 0; k < 2; k++) {
out[i++] = chars[(bytes[j] >> 4) & 0xF];
out[i++] = chars[(bytes[j] >> 0) & 0xF];
j++;
}
// move by one?
if (i < out.size() && out[i] == '-') i++;
}
// send string
return out;
}
public:
static UUID create() {
// get random bytes
UUID uuid;
openssl::randomBytes(uuid.bytes, sizeof(uuid.bytes));
// Set version to 0100
uuid.bytes[6] = (uuid.bytes[6] & 0x0F) | 0x40;
// Set bits 6-7 to 10
uuid.bytes[8] = (uuid.bytes[8] & 0x3F) | 0x80;
return uuid;
}
/**
* @brief Creates an empty UUID.
*/
static UUID empty() {
UUID id = {};
for(int i = 0; i < 16; i++) id.bytes[i] = 0;
return id;
}
/**
* @brief Attempts to parse an UUID.
*/
static UUID fromString(const std::string& str) {
// must be 36 characters long
if(str.length() != 36) throw std::runtime_error("");
// 00000000-0000-0000-0000-000000000000
std::array<size, 4> hyphens = {8, 13, 18, 23};
UUID id;
size j = 0;
// for loop the chars
for(size i = 0; i < 36; i += 2) {
// check hypen
if(str[i] == '-') {
if(!it::contains(hyphens, i))
throw std::runtime_error("Error: Hyphen at unexpected position " + std::to_string(i) + ".");
i++;
}
// parse...
u8 write = 0;
for(size n = 0; n < 2; n++) {
int hex = strings::hexChar(str[i + n]) << ((1 - n) * 4);
if (hex == -1) throw std::runtime_error("Error: Unexpected character \"" + strings::display(str[i + n]) + "\" position " + std::to_string(i + n) + ".");
write |= hex & 0xFF;
}
// write a byte
id.bytes[j++] = write;
}
// return the id now!
return id;
}
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <ckitty/crypto/openssl.hpp>
#include <ckitty/memory/Buffers.hpp>
#include <exception>
namespace ckitty {
namespace base64 {
inline std::string encode(const u8* data, const size length) {
u8 buffer[length * 4 / 3 + 2];
size strlen = EVP_EncodeBlock(buffer, data, length);
return std::string((const char*) buffer, strlen);
}
inline buffer decode(const std::string& str) {
u8 buff[str.length() * 3 / 4 + 3];
int n = EVP_DecodeBlock(buff, (const u8*) str.c_str(), str.length());
if(n == -1) throw std::runtime_error("Invalid base 64 string.");
return buffer(buff, buff + n);
}
// TODO: dedicated encoder & decoder have limited application right now.
/**
* @brief Object to encode base64 data in chunks.
*/
class Encoder {
public:
Encoder() {}
};
/**
* @brief Object to decode base64 data in chunks.
*/
class Decoder {
public:
Decoder() {}
};
}
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <ckitty/crypto/openssl.hpp>
#include <ckitty/memory/data_view.hpp>
#include <ckitty/memory/buffers.hpp>
namespace ckitty {
namespace hash {
/**
* @brief SHA-1 implementation by OpenSSL
*/
class sha1 {
public:
static buffer digestBuffer(data_view view) {
buffer buff(SHA_DIGEST_LENGTH);
SHA1(view.begin(), view.length(), buff.begin());
return buff;
}
public:
sha1() {}
};
/**
* @brief SHA-256 implementation by OpenSSL
*/
class sha256 {
public:
static buffer digestBuffer(data_view view) {
buffer buff(SHA_DIGEST_LENGTH);
SHA256(view.begin(), view.length(), buff.begin());
return buff;
}
public:
sha256() {}
};
}
}

View File

@@ -0,0 +1,39 @@
#pragma once
#define CKITTY_OPENSSL
#define OPENSSL_NO_DEPRECATED
#include <ckitty/memory/primitives.hpp>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/err.h>
namespace ckitty {
namespace openssl {
// NOTE: THIS SECTION REQUIRES AN ALREADY INITIALIZED OPENSSL LIBRARY!
inline std::string getError() {
char str[2048];
auto err = ERR_get_error();
ERR_error_string_n(err, str, sizeof(str));
return std::string(str);
}
/**
* Generates random bytes.
* Use public (private = false) if the bytes are general purpose.
* Use private if the bytes are meant to be secure, i.e. UUID.
*/
inline void randomBytes(u8* bytes, size length, bool usePrivate = false) {
int r;
if (usePrivate) r = RAND_priv_bytes(bytes, length);
else r = RAND_bytes(bytes, length);
if (r != 1) throw std::runtime_error("Error: Could not create UUID: " + getError());
}
}
}

108
src/ckitty/memory/array.hpp Normal file
View File

@@ -0,0 +1,108 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
namespace ckitty {
/**
* @brief Represents a dynamically created,
* fixed size container. Useful to move data
* around, when the data itself has a fixed size.
*
* C++ does not have a standard library equivalent.
* Closest to it are vector and std::array. However,
* vector does not have a constant length and array
* is not dynamically created.
*/
template<typename T>
class array {
protected:
ptr<T[]> _data;
size _length;
public:
/**
* Inherits a pointer, and takes ownership of it.
*/
static array<T> inherit(T* data, size length) {
array<T> arr = array<T>();
arr._data = ptr<T[]>(data);
arr._length = length;
return arr;
}
/**
* @brief Borrows a modifiable part of a buffer to
* this array. The array does not delete it.
*/
static array<T> borrow(T* data, size length) {
array<T> arr = array<T>();
arr._data = ptr<T[]>(data, []([[maybe_unused]] T* _ptr) {});
arr._length = length;
return arr;
}
public:
array(const size length = 0)
: _data(new T[length]), _length(length) {}
array(const T* begin, const T* end)
: _data(new T[end - begin]), _length(end - begin) {
std::copy(begin, end, this->begin());
}
array(const array& copy) : _data(copy._data), _length(copy._length) {}
array(array&& move) : _data(std::move(move._data)), _length(move._length) {
// move._data = nullptr
move._length = 0;
}
array& operator=(const array& copy) {
this->_data = copy._data;
this->_length = copy._length;
return *this;
}
array& operator=(array&& move) {
this->_data = std::move(move._data);
this->_length = move._length;
// move._data = nullptr
move._length = 0;
return *this;
}
T& operator[](const size i) {
return *(_data.get() + i);
}
const T& operator[](const size i) const {
return *(_data.get() + i);
}
size length() const {
return _length;
}
T* begin() {
return _data.get();
}
T* end() {
return _data.get() + _length;
}
const T* begin() const {
return _data.get();
}
const T* end() const {
return _data.get() + _length;
}
};
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
#include <ckitty/memory/array.hpp>
#include <type_traits>
namespace ckitty {
namespace bits {
/**
* Unpacks a value into bytes.
*/
template<typename T>
inline ckitty::array<u8> unpack(const T value, bool endianness) {
const size bytelen = sizeof(T);
ckitty::array<u8> bytes(bytelen);
for(size i = 0; i < bytelen; i++) {
size j = endianness ? bytelen - i - 1 : i;
bytes[i] = (value >> (j * 8)) & 0xFF;
}
return bytes;
}
/**
* Packs bytes into a value.
*/
template<typename T>
inline T pack(const u8* bytes, bool endianness) {
const size bytelen = sizeof(T);
T type(0);
for(size i = 0; i < bytelen; i++) {
size j = endianness ? bytelen - i - 1 : i;
type |= T(bytes[i] << (j * 8)) & 0xFF;
}
return type;
}
template<typename T>
inline bool read(const T value, size bitno) {
return (value >> bitno) & 1;
}
template<typename T>
inline T write(const T value, size bitno, bool bitval) {
size mask = T(1) << bitno;
return bitval ? (value | mask) : (value & ~mask);
}
template<typename T>
inline void write_r(T& value, size bitno, bool bitval) {
value = write(value, bitno, bitval);
}
}
}

View File

@@ -0,0 +1,221 @@
#pragma once
#include <ckitty/memory/array.hpp>
#include <ckitty/memory/data_view.hpp>
#include <iomanip>
#include <cmath>
#include <iostream>
namespace ckitty {
/**
* Define a buffer as an array of u8.
*/
using buffer = array<u8>;
namespace buffers {
/**
* @brief The sole purpose of this function
* is to stop me from confusing the signs.
*/
inline bool fits(size index, size length) {
return index < length;
}
/**
* @brief Returns true if some data fits in the
* provided space.
*/
inline bool fits(size offset, size length, size test) {
return (offset < length) && ((length - offset) >= test);
}
inline data_view view(const buffer& buff) {
return data_view(buff.begin(), buff.end());
}
/**
* @brief Simple match agains data view.
*/
inline bool matches(data_view d1, data_view d2) {
if(d1.length() != d2.length()) return false;
for(size i = 0; i < d1.length(); i++) {
if(d1[i] != d2[i]) return false;
}
return true;
}
/**
* @brief Matches if the data starts with a prefix
*/
inline bool startsWith(data_view d1, data_view pre) {
if(d1.length() < pre.length()) return false;
for(size i = 0; i < pre.length(); i++) {
if(d1[i] != pre[i]) return false;
}
return true;
}
inline bool find(data_view data, data_view test, size& index) {
// loop until we find match
for(size off = 0; off < data.length(); off++) {
// fits?
if(!fits(off, data.length(), test.length())) return false;
// test for equality
if(matches(data_view(data.begin() + off, test.length()), test)) {
index = off;
return true;
}
}
return false;
}
/**
* @brief Checks if an index is in between
* min and max, inclusively.
*/
inline bool in_range(size min, size index, size max) {
return min <= index && index <= max;
}
/**
* @brief Moves the buffer to a new
* one, and restores the size in the
* previous one.
*/
inline buffer moveRealocate(buffer& buff) {
size len = buff.length();
buffer out = std::move(buff);
buff = buffer(len);
return out;
}
inline data_view view(std::string& str) {
return data_view((const u8*) str.c_str(), str.length());
}
inline void copy(buffer& buff, size& index, const std::string& str) {
std::copy(str.begin(), str.end(), buff.begin() + index);
index += str.size();
}
inline void copy(buffer& buff, size& index, const buffer& data) {
std::copy(data.begin(), data.end(), buff.begin() + index);
index += data.length();
}
inline void hex(data_view view, std::ostream& os) {
char chars[] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'
};
for(u8 c : view) {
os << chars[ (c >> 4) & 0xF ];
os << chars[ c & 0xF ];
}
}
inline void hexdump(data_view view, std::ostream& os, std::string prefix = "") {
// format:
// FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
// -
char chars[] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'
};
// how many rows chars to use?
double logv = std::log(std::max<size>(1, view.length()));
double spaces = std::floor((long double) (logv / std::log(16) + 1));
size rowchars = std::max<size>(2, spaces) - 1;
// print header
os << prefix << std::string(rowchars, ' ') << " X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF | -------- --------\n";
// check zero data
if(view.length() == 0) {
os << prefix << std::string(rowchars, ' ') << " (NO DATA)\n";
os.flush();
return;
}
// add prefix
os << prefix << "\n";
// loop through the bytes
size index = 0, row = 0;
while(index < view.length()) {
// ascii buffer
std::string ascii;
// add header
os << prefix;
os << std::hex << std::setfill('0') << std::setw(rowchars) << row << std::dec << "X ";
// print a line of the characters
for(int g = 0; g < 2; g++) {
// print half a row
for(int i = 0; i < 8; i++) {
// end of file?
u8 value, high, low;
// compute vars
if(index < view.length()) {
// get the byte value
value = view[index++];
high = (value >> 4) & 0xF;
low = (value >> 0) & 0xF;
// log value
os << chars[high] << chars[low];
} else {
os << " ";
value = ' ';
}
// add to ascii?
char ch = value;
if(!std::isgraph(ch)) ch = ' ';
ascii += ch;
// space condition
if(i < 7) {
os << ' ';
continue;
}
if(g == 0) {
os << " ";
ascii += " ";
continue;
}
}
// <-
}
// add the "ASCII" version
os << " | " << ascii;
row++;
// end of line
os << '\n';
os.flush();
}
// undo manips
os << std::setfill(' ');
}
inline buffer from(const std::string& str) {
return buffer((const u8*) str.begin().base(), (const u8*) str.end().base());
}
}
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
namespace ckitty {
/**
* @brief Represents a piece of a shared buffer that
* is not owned by this object.
*/
class data_view {
private:
const u8* _begin;
const u8* _end;
public:
data_view(const u8* begin, const u8* end)
: _begin(begin), _end(end) {}
data_view(const u8* data, const size length)
: _begin(data), _end(data + length) {}
data_view(const std::string& str)
: _begin((const u8*) str.begin().base()),
_end((const u8*) str.end().base()) {}
const u8* begin() const {
return _begin;
}
const u8* end() const {
return _end;
}
u8 operator[](size i) const {
return *(_begin + i);
}
size length() const {
return _end - _begin;
}
const data_view sub_off(size offset, size length) const {
return data_view(_begin + offset, _begin + offset + length);
}
const data_view sub_abs(size begin, size end) const {
return data_view(_begin + begin, _begin + end);
}
};
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
#include <algorithm>
namespace ckitty {
namespace it {
template<typename T, typename V>
bool contains(const T& cont, const V& value) {
return std::find(cont.begin(), cont.end(), value) != cont.end();
}
template<typename T, typename V>
bool erase(T& cont, const V& value) {
auto it = std::find(cont.begin(), cont.end(), value);
if (it == cont.end()) return false;
cont.erase(it);
return true;
}
template<typename T, typename V>
bool map_erase(T& map, const V& value) {
auto it = map.find(value);
if (it == map.end()) return false;
map.erase(it);
return true;
}
}
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
#include <vector>
#include <map>
#include <deque>
#include <memory>
#include <optional>
#include <string>
#include <initializer_list>
namespace ckitty {
typedef std::uint8_t u8;
typedef std::uint16_t u16;
typedef std::uint32_t u32;
typedef std::uint64_t u64;
typedef std::int8_t i8;
typedef std::int16_t i16;
typedef std::int32_t i32;
typedef std::int64_t i64;
typedef std::size_t size;
typedef float f32;
typedef double f64;
using std::vector;
using std::map;
using std::deque;
using std::optional;
template<typename T> using ptr = std::shared_ptr<T>;
template<typename T> using uptr = std::unique_ptr<T>;
template<typename T> using wptr = std::weak_ptr<T>;
template<typename T> using ilist = std::initializer_list<T>;
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
#include <chrono>
#include <string>
#include <iostream>
#include <iomanip>
#include <sstream>
namespace ckitty {
namespace chrono {
using namespace std::chrono;
inline u64 millis() {
auto now = high_resolution_clock::now().time_since_epoch();
return duration_cast<milliseconds>(now).count();
}
inline u64 micros() {
auto now = high_resolution_clock::now().time_since_epoch();
return duration_cast<microseconds>(now).count();
}
// Date Time String // Could be done better but this is neat too
inline std::string dateTimeString(const std::string& format, const std::tm time) {
std::ostringstream oss;
oss << std::put_time(&time, format.c_str());
return oss.str();
}
inline std::string dateTimeString(const std::string& format = "%d-%m-%Y %H-%M-%S") {
const std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
return dateTimeString(format, tm);
}
}
}

View File

@@ -0,0 +1,68 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
namespace ckitty {
namespace environment {
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
//define something for Windows (32-bit and 64-bit, this part is common)
// Do not bother to differentiate 32 bits from 64 bits.
inline const std::string OS_NAME = "Windows";
inline const std::string DEVICE = "Desktop";
inline const std::string CODE = "desktop/windows";
#define CKITTY_OS_WINDOWS
#define CKITTY_DEVICE_DESKTOP
#elif __APPLE__
#include <TargetConditionals.h>
// iOS, tvOS, or watchOS Simulator
// #elif TARGET_OS_MACCATALYST
// Mac's Catalyst (ports iOS API into Mac, like UIKit).
#if defined(TARGET_IPHONE_SIMULATOR) || defined(TARGET_OS_IPHONE)
// iOS, tvOS, or watchOS device
inline const std::string OS_NAME = "iOS";
inline const std::string DEVICE = "Mobile";
inline const std::string CODE = "mobile/ios";
#define CKITTY_OS_IOS
#define CKITTY_DEVICE_MOBILE
#elif TARGET_OS_MAC
// Other kinds of Apple platforms
inline const std::string OS_NAME = "MacOS";
inline const std::string DEVICE = "Desktop";
inline const std::string CODE = "desktop/macos";
#define CKITTY_OS_MACOS
#define CKITTY_DEVICE_DESKTOP
#else
# error "Unknown Apple platform"
#endif
#elif __ANDROID__
// Below __linux__ check should be enough to handle Android,
// but something may be unique to Android.
// all unices not caught above
inline const std::string OS_NAME = "Android";
inline const std::string DEVICE = "Mobile";
inline const std::string CODE = "mobile/android";
#define CKITTY_OS_ANDROID
#define CKITTY_DEVICE_MOBILE
#elif defined(__linux__) || defined(__unix__)
// linux / unix
inline const std::string OS_NAME = "Linux";
inline const std::string DEVICE = "Desktop";
inline const std::string CODE = "desktop/linux";
#define CKITTY_OS_LINUX
#define CKITTY_DEVICE_DESKTOP
#elif defined(__EMSCRIPTEN__) || defined(__wasm__) || defined(__wasm)
// WASM
inline const std::string OS_NAME = "WASM";
inline const std::string DEVICE = "Browser";
inline const std::string CODE = "browser/wasm";
#define CKITTY_OS_WASM
#define CKITTY_DEVICE_BROWSER
#else
# error "Unknown compiler/system."
#endif
}
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
#include <sstream>
#include <iomanip>
namespace ckitty {
struct error {
i32 code;
std::string message;
error(i32 code = 0, const std::string& msg = "")
: code(code), message(msg) {
}
operator bool() const {
return code;
}
operator std::string() const {
std::ostringstream oss;
oss << "[" << std::hex << std::showbase << std::uppercase << std::setw(2) << std::setfill('0') << code << "] ";
oss << (message.empty() ? "(no info)" : message);
return oss.str();
}
error& operator+=(const error& err) {
auto err1 = (*this) + err;
code = err1.code;
message = err1.message;
return *this;
}
error operator+(const error& err) const {
// If this error has no code (no error), return the other error
if (code == 0) {
return err;
}
// If the other error has no code, return this error
if (err.code == 0) {
return *this;
}
// Both errors have codes, combine them
error result = *this;
// Combine messages
if (!result.message.empty()) {
result.message += "\n ";
}
// add next error to the message
result.message += err.operator std::string();
return result;
}
};
}

View File

@@ -0,0 +1,104 @@
#pragma once
#include <ckitty/memory/primitives.hpp>
#include <string>
#include <algorithm>
#include <cctype>
#include <locale>
namespace ckitty {
namespace strings {
// trim spaces
inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](unsigned char ch) {
return !std::isspace(ch);
}));
}
inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
[](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
inline void trim(std::string& str) {
ltrim(str);
rtrim(str);
}
inline std::string trimc(const std::string& str) {
std::string copy = str;
trim(copy);
return copy;
}
inline std::string ltrimc(const std::string& str) {
std::string copy = str;
ltrim(copy);
return copy;
}
inline std::string rtrimc(const std::string& str) {
std::string copy = str;
rtrim(copy);
return copy;
}
/**
* Returns a displayable representation of the character.
*/
inline std::string display(char c) {
if(std::isprint(c)) return std::string(1, c);
char chars[] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'
};
std::string str = "0x00";
str[2] = chars[ (c >> 4) & 0xF ];
str[3] = chars[ c & 0xF ];
return str;
}
inline int hexChar(char c) {
if('0' <= c && c <= '9') return c - '0';
if('a' <= c && c <= 'f') return c - 'a' + 10;
if('A' <= c && c <= 'F') return c - 'A' + 10;
return -1;
}
// ascii upper & lower
inline void upper(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}
inline void lower(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
}
inline std::string copy_upper(const std::string& str) {
std::string copy = str;
upper(copy);
return copy;
}
inline std::string copy_lower(const std::string& str) {
std::string copy = str;
lower(copy);
return copy;
}
// ...
}
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include <ckitty/memory/array.hpp>
#include <ckitty/system/strings.hpp>
/*
* NOTICE: This is the standalone unicode library for ckittylib.
* If using ICU, use the /unicode folder's headers.
*/
namespace ckitty {
namespace unicode {
using sequences = std::string;
using string = std::u32string;
/**
* Returns the amount of bytes needed to
* represent an UTF-8 character given
* the first byte of the sequence.
*
* Returns 0 for invalid sequences.
*/
inline u32 sequenceLength(const u8 head) {
return (head - 160 >> 20 - head / 16) + 2;
}
/**
* Encodes a UTF-32 character into a UTF-8 sequence.
*/
inline sequences encodeChar(const u32 cha) {}
/**
* Decodes a UTF-8 sequence (the first one) into a UTF-32 character.
* Invalid sequences return 0 and raise a flag.
*/
inline u32 decodeChar(const sequences& cha, bool* error = nullptr) {}
inline sequences encodeChar(const u32 cha) {}
struct decode_result {
// Was the result valid?
bool valid = false;
// First character error.
u64 errCharIndex = 0;
// Byte index of character.
u64 errByteIndex = 0;
// UTF32 result
string output;
};
/**
* Decodes all UTF-8 sequences into 32 bit chars.
* Invalid ones are read as one byte.
*/
inline decode_result decodeUTF8(const sequences& str) {
decode_result r;
return r;
}
/**
* Encodes UTF32 into UTF-8 byte sequences.
*/
inline sequences encodeUTF8(const string& str) {}
struct validate_result {
// Was the result valid?
bool valid = false;
// First character error.
u64 errCharIndex = 0;
// Byte index of character.
u64 errByteIndex = 0;
};
/**
* Validates that a std::string is made out of valid UTF8 sequences.
*/
inline validate_result validateUTF8(const sequences& str) {
validate_result r;
return r;
}
}
}