initial upload
This commit is contained in:
9
ckittylib.code-workspace
Normal file
9
ckittylib.code-workspace
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
}
|
||||
}
|
||||
161
src/ckitty/crypto/UUID.hpp
Normal file
161
src/ckitty/crypto/UUID.hpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
48
src/ckitty/crypto/base64.hpp
Normal file
48
src/ckitty/crypto/base64.hpp
Normal 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() {}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
49
src/ckitty/crypto/hash.hpp
Normal file
49
src/ckitty/crypto/hash.hpp
Normal 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() {}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
39
src/ckitty/crypto/openssl.hpp
Normal file
39
src/ckitty/crypto/openssl.hpp
Normal 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
108
src/ckitty/memory/array.hpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
58
src/ckitty/memory/bits.hpp
Normal file
58
src/ckitty/memory/bits.hpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
221
src/ckitty/memory/buffers.hpp
Normal file
221
src/ckitty/memory/buffers.hpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
55
src/ckitty/memory/data_view.hpp
Normal file
55
src/ckitty/memory/data_view.hpp
Normal 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
33
src/ckitty/memory/iterators.hpp
Normal file
33
src/ckitty/memory/iterators.hpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
40
src/ckitty/memory/primitives.hpp
Normal file
40
src/ckitty/memory/primitives.hpp
Normal 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>;
|
||||
|
||||
}
|
||||
42
src/ckitty/system/chrono.hpp
Normal file
42
src/ckitty/system/chrono.hpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
68
src/ckitty/system/environment.hpp
Normal file
68
src/ckitty/system/environment.hpp
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
62
src/ckitty/system/error.hpp
Normal file
62
src/ckitty/system/error.hpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
104
src/ckitty/system/strings.hpp
Normal file
104
src/ckitty/system/strings.hpp
Normal 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;
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
86
src/ckitty/system/unicode.hpp
Normal file
86
src/ckitty/system/unicode.hpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user