Compare commits

..

72 Commits

Author SHA1 Message Date
82a6293f21 added macro try_handle() 2026-01-13 18:20:01 +05:00
c4fd22542d fixed IGNORE_RESULT macro bug 2026-01-09 09:47:41 +05:00
4cfed24ec3 fixed cpp guard 2026-01-09 05:52:38 +05:00
de88e9ff16 added List_T_destroyWithElements 2025-12-21 20:22:35 +05:00
26de01c3e7 added path_getUserDir, dir_createParent 2025-12-21 20:00:10 +05:00
7dc80c2fdf insertion sort fix 2025-12-19 08:15:28 +05:00
bdbe959e23 DateTime_parse 2025-12-15 23:21:59 +05:00
94b051e852 defer simplified 2025-12-15 13:38:03 +05:00
08d45faa83 fixed some errors 2025-12-13 03:24:46 +05:00
af0931c727 changed Error_create msg argument type to str 2025-12-13 02:30:45 +05:00
0d422cd7e5 HashMapKeyValue.value -> value_ptr 2025-12-02 20:06:02 +05:00
17d2d1c38d added inline insertion sort and binary search 2025-12-02 19:24:46 +05:00
a57f05cfeb zeroStruct 2025-12-02 17:17:39 +05:00
ea6c20f430 str_expand fix 2025-11-27 01:44:49 +05:00
77e4f38416 updated README.md 2025-11-27 01:38:50 +05:00
f3e4f8f061 added str_expand 2025-11-27 01:23:41 +05:00
83091ac707 added file_readWholeText 2025-11-26 23:52:18 +05:00
Timerix
6a7f0a8715 added List<T>_expand 2025-11-26 20:56:09 +05:00
8a40caaf10 str_destroy and str_free 2025-11-26 17:03:54 +05:00
4c97787c27 moved term.h to tlibc 2025-11-25 18:22:14 +05:00
82bd234d08 rewritten Array and List, changed str.size -> .len 2025-11-25 16:31:04 +05:00
b557881168 LList fix 2025-11-24 23:48:38 +05:00
f2ce18b16d implemented doubly linked list 2025-11-24 23:41:58 +05:00
3a7f09bb49 fixed file_close bug when file is NULL 2025-11-21 21:21:02 +05:00
0b4574b53e replaced strerror() with thread-safe strerror_malloc() 2025-11-21 13:19:14 +05:00
225a48a8d9 List_index fix 2025-11-18 22:37:27 +05:00
ec164dc4e9 moved magic.h to tlibc 2025-11-18 16:29:34 +05:00
c68e4e87b3 fixed DateTime_get bugs and added FMT_DateTime 2025-11-18 16:00:50 +05:00
bc41577248 file_readWhole 2025-11-18 14:18:09 +05:00
89aab2b5bf Array_free 2025-11-18 12:16:46 +05:00
425794361b fix README 2025-11-15 15:23:13 +05:00
fc8541ad2d fixed portable_localtime_s on posix systems 2025-11-15 14:41:58 +05:00
ae0fa95d6a added macros for printf format codes 2025-11-15 12:12:06 +05:00
5ef223372b renamed char_* functions 2025-11-15 11:51:21 +05:00
adaf5cc311 cstr_copy 2025-11-13 06:16:57 +05:00
af36bab444 implemented DateTime struct 2025-11-13 05:11:35 +05:00
1775b27980 Array_null and List_null 2025-11-13 02:29:55 +05:00
77589878c1 added check for zero size in Array_alloc 2025-11-13 00:37:59 +05:00
3034e4d186 readme 2025-11-10 12:40:53 +05:00
d9daae8c5e README 2025-11-10 11:44:33 +05:00
972f244ae5 implemented ErrorCodePage 2025-11-10 11:42:25 +05:00
6a1067a612 created HashMap iterator 2025-11-10 10:30:22 +05:00
2de044711f implemented List_increaseCapacity_size 2025-11-10 10:29:50 +05:00
958cb269b2 renamed first argument of all methods to 'self' 2025-11-10 07:06:56 +05:00
0a1e87817d added HashMap_pushOrUpdate 2025-11-10 06:55:47 +05:00
57c5942fcc new StringBuilder functions 2025-11-10 06:55:26 +05:00
1406189511 implemented buffer_size calculation in vsprintf_malloc 2025-11-10 01:54:47 +05:00
e2f1f6c09b added ATTRIBUTE_THREAD_LOCAL 2025-11-09 23:56:42 +05:00
bb3b096262 updated gprof task 2025-11-09 23:45:48 +05:00
02fb2767a8 updated to cbuild 2.3.0 2025-11-09 23:27:21 +05:00
4cc226b57a enabled more warnings 2025-11-09 18:37:18 +05:00
cf5fda7a1e added macro List_castTo_Array 2025-11-09 18:05:44 +05:00
2c8e6fc601 added null check to destructors 2025-11-09 03:37:17 +05:00
d6436d0833 added sec_t 2025-11-08 23:57:22 +05:00
ee2b88a412 added warning on unused Result return value and a way to supress this warning 2025-11-08 22:18:33 +05:00
dbe8924466 compiler attributes were hidden behind ATTRIBUTE_* macros 2025-11-08 21:24:00 +05:00
30c141f587 renamed Array_slice macros 2025-11-08 18:13:06 +05:00
00a1a29d34 changed Array_memset(), added file_close() 2025-11-06 22:25:19 +05:00
5fb2db2380 implemented str_slice 2025-11-01 19:49:31 +05:00
c4cb696721 fixed file_getSize 2025-11-01 19:49:17 +05:00
b801719712 implemented Array_slice* methods 2025-11-01 17:20:20 +05:00
75c94e88d9 fixed str_trim bugs 2025-11-01 00:15:41 +05:00
a0affaa6d0 added braces to try_assert 2025-10-26 14:17:25 +05:00
f0992c0217 try_assert 2025-10-25 19:07:20 +05:00
0184d2e8c9 struct_castTo_Array 2025-10-25 16:42:45 +05:00
14ffede476 smal fixes 2025-09-29 10:22:50 +05:00
b3f67a38de base64 2025-09-29 08:19:13 +05:00
7e7bd195a9 added Array_copy() 2025-09-12 20:57:34 +05:00
c415e2ca8f str_trim 2025-08-12 19:51:07 +03:00
bf56984482 try_stderrcode 2025-08-10 21:33:16 +03:00
8eeaff4245 made str_equals faster 2025-08-10 17:21:55 +03:00
a097d5aff9 header fix for linux 2025-08-10 03:22:10 +03:00
70 changed files with 1904 additions and 426 deletions

36
README.md Normal file
View File

@@ -0,0 +1,36 @@
# tlibc
C library with collections, strings, exceptions, io...
## Build
1. Clone this repository.
```
git clone https://timerix.ddns.net/git/Timerix/tlibc.git
```
2. Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild/releases).
Select latest version compatible with the one in `project.config`.
Example: For `2.3.0` download latest `2.3.x`.
3. Build static library
```
cd tlibc
cbuild build_static_lib_dbg
```
6. If you use tlibc as static library, add `LINKER_LIBS` from tlibc `project.config` to your project.
## Usage
```c
#include "tlibc/tlibc.h"
int main(){
Deferral(32); // reserve memory for 32 defers
try_fatal_void(tlibc_init());
Defer(tlibc_deinit());
Return 0; // call defers
}
```

View File

@@ -0,0 +1,32 @@
#pragma once
#include "std.h"
/// USAGE insertionSort(list.data, list.len, .id)
#define insertionSort_inline(arr, n, field) \
for(i32 i = 1, j; i < (i32)n; i++) { \
j = i; \
typeof(arr[i]) t = arr[i];\
while(j > 0 && arr[j - 1]field > t##field){\
arr[j] = arr[j - 1]; \
j--; \
} \
arr[j] = t; \
} \
#define binarySearch_inline(arr, n, key, field, out_index) {\
i32 low = 0; \
i32 high = n - 1; \
while (low <= high) { \
i32 mid = low + (high - low) / 2; \
if (arr[mid]field == key) { \
out_index = mid; \
break; \
} \
/* choose left or right half */ \
if (arr[mid]field < key) \
low = mid + 1; \
else high = mid - 1; \
} \
out_index = -1; \
}

23
include/tlibc/base64.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include "tlibc/std.h"
/// @param src_size size of data to encode
/// @return number of encoded characters. Is a multiple of 4.
u32 base64_encodedSize(u32 src_size);
/// @param src data to encode
/// @param src_size size of data to encode
/// @param dst buffer of size base64_encodedSize(src_size)
/// @return number of encoded characters. Is a multiple of 4.
u32 base64_encode(const u8* src, u32 src_size, char* dst);
/// @param src data to decode
/// @param src_size size of data to decode. Must be a multiple of 4 for valid base64 data.
/// @return number of decoded characters or 0 on error
u32 base64_decodedSize(const char* src, u32 src_size);
/// @param src data to decode
/// @param src_size size of data to decode. Must be a multiple of 4 for valid base64 data.
/// @param dst buffer of size base64_decodedSize(src, src_size)
/// @return number of decoded characters or 0 on error
u32 base64_decode(const char* src, u32 src_size, u8* dst);

View File

@@ -1,34 +1,62 @@
#pragma once #pragma once
#include "../std.h" #include "../std.h"
#define Array(T) Array_ /*
Pointer and length.
Can be initialized with {0}.
*/
typedef struct Array_ { #define Array(T) Array_##T
void* data;
u32 size;
} Array_;
/// creates Array_ from a const array /// create Array(T) from array initializer list ({ a, b, c })
#define ARRAY(T, A...) Array_construct_size(((T[])A), sizeof((T[])A)) #define ARRAY(T, A...) ((Array(T)){ .data = ((T[])A), .len = sizeof((T[])A) / sizeof(T) })
#define Array_construct(DATA, T, COUNT) Array_construct_size(DATA, (COUNT) * sizeof(T)) #define Array_declare(T) \
#define Array_construct_size(DATA, LEN) ((Array_){ .data = (DATA), .size = (LEN) }) typedef struct Array(T) { \
T* data; \
u32 len; \
} Array(T); \
\
static inline Array(T) Array_##T##_construct(T* data, u32 len) { \
return (Array(T)){ .data = data, .len = len }; \
} \
\
static inline Array(T) Array_##T##_alloc(u32 len){ \
if(len == 0) \
return Array_##T##_construct(NULL, 0); \
return Array_##T##_construct(malloc(len * sizeof(T)), len); \
} \
\
static inline void Array_##T##_realloc(Array(T)* self, u32 new_len){ \
self->data = (T*)realloc(self->data, new_len * sizeof(T)); \
self->len = new_len; \
} \
\
static inline Array(T) Array_##T##_copy(const Array(T) src){ \
Array(T) copy = Array_##T##_alloc(src.len); \
if(copy.data != NULL) \
memcpy(copy.data, src.data, src.len * sizeof(T)); \
return copy; \
} \
\
static inline void Array_##T##_destroy(Array(T)* self){ \
if(!self) \
return; \
free(self->data); \
} \
\
static inline void Array_##T##_memset(Array(T)* self, u32 value){ \
memset(self->data, value, self->len * sizeof(T)); \
} \
\
/* @return self[0..i-1] */ \
static inline Array(T) Array_##T##_sliceTo(const Array(T) src, u32 i){ \
return Array_##T##_construct(src.data, i); \
} \
\
/* @return self[i...] */ \
static inline Array(T) Array_##T##_sliceFrom(const Array(T) src, u32 i){ \
return Array_##T##_construct(src.data + i, src.len - i); \
} \
#define Array_alloc(T, COUNT) Array_alloc_size((COUNT) * sizeof(T)) #define struct_castTo_Array_u8(STRUCT_PTR) Array_u8_construct((void*)(STRUCT_PTR), sizeof(*STRUCT_PTR))
static inline Array_ Array_alloc_size(u32 size){
return Array_construct_size(malloc(size), size);
}
#define Array_realloc(AR, T, COUNT) Array_realloc_size(AR, (COUNT) * sizeof(T))
static inline void Array_realloc_size(Array_* ar, u32 new_size){
ar->data = realloc(ar->data, new_size);
ar->size = new_size;
}
#define Array_len(AR, T) ((AR)->size / sizeof(T))
#define Array_memset(A, VAL) memset((A)->data, VAL, (A)->size)
#define str_castTo_Array(S) Array_construct_size((S).data, (S).size)
#define Array_castTo_str(S, IS_ZERO_TERMINATED) str_construct((S).data, (S).size, IS_ZERO_TERMINATED)

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(char);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(f32);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(f64);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(i16);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(i32);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(i64);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(i8);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(u16);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(u32);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(u64);

View File

@@ -0,0 +1,2 @@
#pragma once
Array_declare(u8);

View File

@@ -1,34 +1,55 @@
#pragma once #pragma once
#include "../std.h" #include "../std.h"
#include "../string/str.h" #include "../string/str.h"
#include "Array.h"
#include "List.h" #include "List.h"
typedef void (*FreeFunction)(void*); typedef struct HashMapKeyHash {
typedef struct KeyHash {
str key; str key;
u32 hash; u32 hash;
} KeyHash; } HashMapKeyHash;
List_declare(HashMapKeyHash);
typedef struct HashMapBucket { typedef struct HashMapBucket {
List(KeyHash) key_hash_list; List(HashMapKeyHash) key_hash_list;
List(T) value_list; List_ value_list;
} HashMapBucket; } HashMapBucket;
#define HashMap(T) HashMap_ #define HashMap(T) HashMap_
typedef struct HashMap_ { typedef struct HashMap_ {
HashMapBucket* table; NULLABLE(HashMapBucket*) table;
FreeFunction NULLABLE(value_destructor); NULLABLE(Destructor_t) value_destructor;
u32 value_t_size; u32 value_t_size;
u32 height; u32 height;
u16 height_n; u16 height_n;
} HashMap_; } HashMap_;
#define HashMap_construct(PTR, T, VALUE_DESTRUCTOR) HashMap_construct_size(PTR, sizeof(T), VALUE_DESTRUCTOR) #define HashMap_construct(SELF, T, VALUE_DESTRUCTOR) HashMap_construct_size(SELF, sizeof(T), VALUE_DESTRUCTOR)
void HashMap_construct_size(HashMap_* ptr, u32 value_t_size, FreeFunction NULLABLE(value_destructor)); void HashMap_construct_size(HashMap_* self, u32 value_t_size, Destructor_t NULLABLE(value_destructor));
void HashMap_destroy(HashMap_* ptr); void HashMap_destroy(HashMap_* self);
void* NULLABLE(HashMap_tryGetPtr)(HashMap_* ptr, str key);
bool HashMap_tryPush(HashMap_* ptr, str key, void* value_ptr); bool HashMap_tryPush(HashMap_* self, const str key, void* value_ptr);
bool HashMap_tryDelete(HashMap_* ptr, str key); void HashMap_pushOrUpdate(HashMap_* self, const str key, void* value_ptr);
NULLABLE(void*) HashMap_tryGetPtr(const HashMap_* self, const str key);
bool HashMap_tryDelete(HashMap_* self, const str key);
typedef struct HashMapIter {
const HashMap_* map;
i32 bucket_n;
i32 elem_n;
} HashMapIter;
typedef struct HashMapKeyValue {
str key;
void* value_ptr;
} HashMapKeyValue;
static inline HashMapIter HashMapIter_create(const HashMap_* table){
return (HashMapIter){ .map = table, .bucket_n = -1, .elem_n = -1 };
}
bool HashMapIter_moveNext(HashMapIter* self);
/// don't forget to call HashMapIter_moveNext() first
bool HashMapIter_getCurrent(HashMapIter* self, HashMapKeyValue* kv);

View File

@@ -0,0 +1,92 @@
#pragma once
#include "../std.h"
/*
Doubly linked list.
Can be initialized with {0}.
*/
#define LLNode(T) LLNode_##T
#define LList(T) LList_##T
#define LList_declare(T) \
typedef struct LLNode(T) LLNode(T);\
typedef struct LLNode(T) { \
LLNode(T)* prev; \
LLNode(T)* next; \
T value; \
} LLNode(T); \
\
static inline LLNode(T)* LLNode_##T##_createZero(){ \
LLNode(T)* n = (LLNode(T)*)malloc(sizeof(LLNode(T))); \
n->prev = NULL; \
n->next = NULL; \
zeroStruct(&n->value); \
return n; \
} \
\
typedef struct LList(T) { \
LLNode(T)* first; \
LLNode(T)* last; \
u32 count; \
NULLABLE(void (*value_destructor)(T*)); \
} LList(T); \
\
/* Peek node from list. Detatched nodes can be inserted in different place. */ \
static inline void LList_##T##_detatch(LList(T)* l, LLNode(T)* n) \
{ _LList_detatch((void*)l, (void*)n); } \
\
/* @param detatched must have null .next and .prev */ \
/* @param target can be null only if it is l->first or l->last */ \
static inline void LList_##T##_insertAfter( \
LList(T)* l, LLNode(T)* target, LLNode(T)* detatched) \
{ _LList_insertAfter((void*)l, (void*)target, (void*)detatched); } \
\
/* @param detatched must have null .next and .prev */ \
/* @param target can be null only if it is l->first or l->last */ \
static inline void LList_##T##_insertBefore( \
LList(T)* l, LLNode(T)* target, LLNode(T)* detatched) \
{ _LList_insertBefore((void*)l, (void*)target, (void*)detatched); } \
\
static inline void LList_##T##_destroy(LList(T)* l){ \
if(!l) \
return; \
LLNode(T)* n = l->first; \
while(n){ \
if(l->value_destructor) \
l->value_destructor(&n->value); \
LLNode(T)* next = n->next; \
free(n); \
n = next; \
} \
} \
#define LList_construct(T, VALUE_DESTRUCTOR) ((LList(T)){ \
.first = NULL, .last = NULL, .count = 0, .value_destructor = VALUE_DESTRUCTOR })
typedef struct LLNode_ LLNode_;
typedef struct LLNode_ {
LLNode_* prev;
LLNode_* next;
/* value of unknown type */
} LLNode_;
typedef struct LList_ {
LLNode_* first;
LLNode_* last;
u32 count;
NULLABLE(Destructor_t) value_destructor;
} LList_;
/* Peek node from list. Detatched nodes can be inserted in different place. */
void _LList_detatch(LList_* l, LLNode_* n);
/* @param detatched must have null .next and .prev */
/* @param target can be null only if it is l->first or l->last */
void _LList_insertAfter(LList_* l, NULLABLE(LLNode_*) target, LLNode_* detatched);
/* @param detatched must have null .next and .prev */
/* @param target can be null only if it is l->first or l->last */
void _LList_insertBefore(LList_* l, NULLABLE(LLNode_*) target, LLNode_* detatched);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(char);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(f32);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(f64);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(i16);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(i32);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(i64);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(i8);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(u16);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(u32);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(u64);

View File

@@ -0,0 +1,2 @@
#pragma once
LList_declare(u8);

View File

@@ -1,30 +1,103 @@
#pragma once #pragma once
#include "../std.h" #include "../std.h"
#define List(T) List_ /*
Dynamic array.
CAN NOT be initialized with {0}.
*/
#define List(T) List_##T
#define List_declare(T) \
typedef struct List(T) { \
T* data; \
u32 len; \
u32 capacity; \
u32 elem_t_size; \
} List(T); \
\
static inline List(T) List_##T##_construct(T* data_ptr, u32 occupied_len, u32 capacity) { \
return (List(T)){ \
.data = data_ptr, \
.len = occupied_len, \
.capacity = capacity, \
.elem_t_size = sizeof(T) \
}; \
} \
\
static inline List(T) List_##T##_alloc(u32 initial_capacity) { \
List_ l = _List_alloc(initial_capacity, sizeof(T)); \
void* l_ptr = &l; \
return *(List(T)*)l_ptr; \
} \
\
static inline List(T) List_##T##_copy(const List(T)* src) { \
List_ l = _List_copy((void*)src); \
void* l_ptr = &l; \
return *(List(T)*)l_ptr; \
} \
\
static inline void List_##T##_destroy(List(T)* self) { _List_destroy((void*)self); } \
\
static inline void List_##T##_destroyWithElements(List(T)* self, void (*elem_destructor)(T*)) { \
for(u32 i = 0; i < self->len; i++){ \
elem_destructor(self->data + i);\
} \
_List_destroy((void*)self); \
} \
\
/* alloc bigger buffer if size + len_to_add won't fit in current */ \
static inline void List_##T##_increaseCapacity(List(T)* self, u32 len_to_add){ \
_List_increaseCapacity((void*)self, len_to_add); \
} \
\
/* call increaseCapacity and return pointer to next empty cell */ \
static inline T* List_##T##_expand(List(T)* self, u32 len_to_add){ \
return (T*)_List_expand((void*)self, len_to_add); \
} \
\
static inline void List_##T##_push(List(T)* self, T value) { \
T* empty_cell = List_##T##_expand(self, 1); \
*empty_cell = value; \
} \
\
static inline void List_##T##_pushMany(List(T)* self, T* values_ptr, u32 len) { \
_List_push((void*)self, values_ptr, len); \
} \
\
static inline bool List_##T##_tryRemoveAt(List(T)* self, u32 i, u32 remove_len) ATTRIBUTE_WARN_UNUSED_RESULT; \
static inline bool List_##T##_tryRemoveAt(List(T)* self, u32 i, u32 remove_len) { \
return _List_tryRemoveAt((void*)self, i, remove_len); \
} \
typedef struct List_ { typedef struct List_ {
void* data; void* data;
u32 size; u32 len;
u32 allocated_size; u32 capacity;
u32 elem_t_size;
} List_; } List_;
#define List_construct(T, DATA_PTR, OCCUPIED_COUNT, ALLOCATED_COUNT) \ static inline List_ _List_construct(void* data_ptr, u32 len, u32 capacity, u32 elem_t_size) {
List_construct_size(DATA_PTR, (OCCUPIED_COUNT) * sizeof(T), (ALLOCATED_COUNT) * sizeof(T)) return (List_){
.data = data_ptr,
static inline List_ List_construct_size(void* data_ptr, u32 occupied_size, u32 allocated_size) { .len = len,
return (List_){ .data = data_ptr, .size = occupied_size, .allocated_size = allocated_size }; .capacity = capacity,
.elem_t_size = elem_t_size
};
} }
#define List_alloc(T, INITIAL_COUNT) List_alloc_size((INITIAL_COUNT) * sizeof(T)) static inline void _List_destroy(List_* self){
List_ List_alloc_size(u32 initial_size); if(!self)
return;
free(self->data);
}
void* List_expand_size(List_* ptr, u32 expansion_size); List_ _List_alloc(u32 initial_capacity, u32 elem_t_size);
#define List_push(L, T, VALUE) *(T*)(List_expand_size(L, sizeof(T))) = VALUE List_ _List_copy(const List_* src);
#define List_pushMany(L, T, VALUES_PTR, COUNT) List_push_size(L, VALUES_PTR, (COUNT) * sizeof(T)) /* alloc bigger buffer if size + len_to_add won't fit in current */
void List_push_size(List_* ptr, void* values, u32 size); void _List_increaseCapacity(List_* self, u32 len_to_add);
void* _List_expand(List_* self, u32 len_to_add);
#define List_removeAt(L, T, I, COUNT) List_removeAt_size(L, (I)*sizeof(T), (COUNT) * sizeof(T)) void _List_push(List_* self, void* values, u32 len);
bool List_removeAt_size(List_* ptr, u32 i, u32 remove_size); bool _List_tryRemoveAt(List_* self, u32 i, u32 remove_len) ATTRIBUTE_WARN_UNUSED_RESULT;
#define List_len(L, T) ((L)->size / sizeof(T))

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(char);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(f32);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(f64);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(i16);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(i32);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(i64);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(i8);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(u16);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(u32);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(u64);

View File

@@ -0,0 +1,2 @@
#pragma once
List_declare(u8);

View File

@@ -17,18 +17,21 @@ void foo(){
#if defined(__GNUC__) || defined(__TINYC__) #if defined(__GNUC__) || defined(__TINYC__)
#define Deferral(MAX_DEFER_STATEMENTS) \ #define Deferral(MAX_DEFER_STATEMENTS) \
unsigned char _num_deferrals = 0; \ int _num_deferrals = 0; \
void *_defer_return_loc = 0, *_deferrals[MAX_DEFER_STATEMENTS] = {0}; ATTRIBUTE_UNUSED void *_defer_return_loc = 0;\
ATTRIBUTE_UNUSED void *_deferrals[MAX_DEFER_STATEMENTS] = {0};
# define Defer(block) _Defer(block, __LINE__) # define Defer(block) _Defer(block, __LINE__)
# define Return _Return(__LINE__) # define Return _Return(__LINE__)
#define _defer_token_cat(a, b) a ## b #ifndef CAT2
#define CAT2(a, b) a ## b
#endif
#define _Defer(block, n) do { \ #define _Defer(block, n) do { \
_deferrals[_num_deferrals++] = && _defer_token_cat(_defer_ini, n); \ _deferrals[_num_deferrals++] = && CAT2(_defer_ini, n); \
if (0) { \ if (0) { \
_defer_token_cat(_defer_ini, n): \ CAT2(_defer_ini, n): \
block; \ block; \
if (_num_deferrals) { \ if (_num_deferrals) { \
goto *_deferrals[--_num_deferrals]; \ goto *_deferrals[--_num_deferrals]; \
@@ -39,13 +42,11 @@ void foo(){
} while (0) } while (0)
#define _Return(n) \ #define _Return(n) \
if (_num_deferrals \ if (_num_deferrals) \
/* this nonsense disables warning Wunused-but-set-variable */ \
&& _defer_return_loc == _defer_return_loc ) \
{ \ { \
_defer_return_loc = && _defer_token_cat(_defer_fini_, n); \ _defer_return_loc = && CAT2(_defer_fini_, n); \
goto *_deferrals[--_num_deferrals]; \ goto *_deferrals[--_num_deferrals]; \
} else _defer_token_cat(_defer_fini_, n): \ } else CAT2(_defer_fini_, n): \
return return
#else /* !__GNUC__ && !__TINYCC__ */ #else /* !__GNUC__ && !__TINYCC__ */
@@ -59,7 +60,7 @@ void foo(){
#endif #endif
#define Deferral(MAX_DEFER_STATEMENTS) \ #define Deferral(MAX_DEFER_STATEMENTS) \
volatile unsigned char _num_deferrals = 0; \ volatile int _num_deferrals = 0; \
jmp_buf _defer_return_loc = {0}, _deferrals[MAX_DEFER_STATEMENTS] = {0}; jmp_buf _defer_return_loc = {0}, _deferrals[MAX_DEFER_STATEMENTS] = {0};
#define Defer(block) do { \ #define Defer(block) do { \

View File

@@ -9,23 +9,53 @@ typedef struct ErrorCallPos {
cstr file; cstr file;
cstr func; cstr func;
} ErrorCallPos; } ErrorCallPos;
#define ErrorCallPos_here() (ErrorCallPos){\ #define ErrorCallPos_here() (ErrorCallPos){\
.line = __LINE__,\ .line = __LINE__,\
.file = __FILE__,\ .file = __FILE__,\
.func = __func__\ .func = __func__\
} }
List_declare(ErrorCallPos);
typedef struct Error { typedef struct Error {
str msg; str msg;
bool is_msg_on_heap; bool is_msg_on_heap;
u16 error_code_page;
u32 error_code;
List(ErrorCallPos) call_stack; List(ErrorCallPos) call_stack;
} Error; } Error;
Error* Error_create(const char* msg, bool is_msg_on_heap, ErrorCallPos p); Error* Error_create(str msg, bool is_msg_on_heap, ErrorCallPos p,
void Error_destroy(Error* e); u16 error_code_page, u32 error_code);
void Error_free(Error* e);
void Error_addCallPos(Error* e, ErrorCallPos p); void Error_addCallPos(Error* e, ErrorCallPos p);
str Error_toStr(Error* e); str Error_toStr(Error* e);
void Error_printAndExit(Error* e) __attribute__ ((__noreturn__)); void Error_printAndExit(Error* e) ATTRIBUTE_NORETURN;
/*
Define in header, declare somewhere in source.
Create a function like
```
void init_your_lib() {
ErrorCodePage_register(YOUR_PAGE);
}
```
and call it in main().
*/
#define ErrorCodePage_name(name) ErrorCodePage_##name
#define ErrorCodePage_declare(name) extern u16 ErrorCodePage_name(name);
#define ErrorCodePage_define(name) u16 ErrorCodePage_name(name) = 0;
void _ErrorCodePage_register(u16* error_code_page_ptr);
#define ErrorCodePage_register(name) _ErrorCodePage_register(&ErrorCodePage_name(name));
typedef enum TlibcError {
TLIBC_ERROR_UNKNOWN
} TlibcError;
#define ErrorCodePage_NONE 0
ErrorCodePage_declare(TLIBC);
ErrorCodePage_declare(LIBC_ERRNO);
typedef struct Result_ { typedef struct Result_ {
Error* error; Error* error;
@@ -38,50 +68,100 @@ typedef struct Result_ {
}; };
} Result_; } Result_;
#define Result(T) Result_ ///Use this macro only to specify function return type.
/// To declare variable, use ResultVar().
/// Warning can be suppressed by IGNORE_RESULT
#define Result(T) Result_ ATTRIBUTE_WARN_UNUSED_RESULT
#define ResultVar(T) Result_
// for some stupid reason gcc requires more than 3 levels of macros to concat token with line number
#define _ignore_var_name(N) CAT2(__ignored_, N)
///USAGE: IGNORE_RESULT trySomething();
#define IGNORE_RESULT Result_ _ignore_var_name(__LINE__) ATTRIBUTE_UNUSED =
#define RESULT_ERROR(MSG, IS_MSG_ON_HEAP) (Result_){ .error = Error_create(MSG, IS_MSG_ON_HEAP, ErrorCallPos_here()) } #define RESULT_ERROR_CODE(CODE_PAGE, CODE, MSG, IS_MSG_ON_HEAP) (Result_){ \
#define RESULT_ERROR_FMT(FORMAT, ARGS...) RESULT_ERROR(sprintf_malloc(4096, FORMAT ,##ARGS), true) .error = Error_create(MSG, IS_MSG_ON_HEAP, ErrorCallPos_here(), \
#define RESULT_ERROR_ERRNO() RESULT_ERROR(strerror(errno), false) ErrorCodePage_name(CODE_PAGE), CODE), \
.u = 0 \
}
#define RESULT_ERROR_CODE_FMT(CODE_PAGE, CODE, FORMAT, ARGS...) \
RESULT_ERROR_CODE(CODE_PAGE, CODE, str_from_cstr(sprintf_malloc(FORMAT ,##ARGS)), true)
#define RESULT_ERROR(MSG, IS_MSG_ON_HEAP) \
RESULT_ERROR_CODE(NONE, 0, MSG, IS_MSG_ON_HEAP);
#define RESULT_ERROR_LITERAL(MSG) \
RESULT_ERROR(STR((MSG)), false)
#define RESULT_ERROR_FMT(FORMAT, ARGS...) \
RESULT_ERROR_CODE_FMT(NONE, 0, FORMAT ,##ARGS)
#define RESULT_ERROR_ERRNO() \
RESULT_ERROR_CODE(LIBC_ERRNO, errno, str_from_cstr(strerror_malloc(errno)), true)
#define RESULT_VOID (Result_){ .error = NULL, .u = 0 } #define RESULT_VOID (Result_){ .error = NULL, .u = 0 }
#define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V } #define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V }
#define try(VAR, RESULT_FIELD, RSLT_CALL) \
_try(VAR, RESULT_FIELD, RSLT_CALL, __LINE__)
#define try_void(RSLT_CALL) \
_try_void(RSLT_CALL, __LINE__)
#define try_fatal(VAR, RESULT_FIELD, RSLT_CALL) \
_try_fatal(VAR, RESULT_FIELD, RSLT_CALL, __LINE__)
#define try_fatal_void(RSLT_CALL) \
_try_fatal_void(RSLT_CALL, __LINE__)
#define try_handle(VAR, RESULT_FIELD, RSLT_CALL, HANDLER) \
_try_handle(VAR, RESULT_FIELD, RSLT_CALL, HANDLER, __LINE__)
#define try_handle_void(RSLT_CALL, HANDLER) \
_try_handle_void(RSLT_CALL, HANDLER, __LINE__)
#define try_stderrcode(RSLT_CALL) \
_try_stderrcode(RSLT_CALL, __LINE__)
#define try_fatal_stderrcode(RSLT_CALL) \
_try_fatal_stderrcode(RSLT_CALL, __LINE__)
#define _rname(N) __r_##N #define _rname(N) __r_##N
#define try(VAR, RESULT_FIELD, RSLT_CALL) _try(VAR, RESULT_FIELD, RSLT_CALL, __LINE__) #define _try_handle(VAR, RESULT_FIELD, RSLT_CALL, HANDLER, N) \
#define try_void(RSLT_CALL) _try_void(RSLT_CALL, __LINE__) Result_ _rname(N) = RSLT_CALL;\
#define try_fatal(VAR, RESULT_FIELD, RSLT_CALL) _try_fatal(VAR, RESULT_FIELD, RSLT_CALL, __LINE__) if(_rname(N).error){\
#define try_fatal_void(RSLT_CALL) _try_fatal_void(RSLT_CALL, __LINE__) Error_addCallPos(_rname(N).error, ErrorCallPos_here());\
HANDLER(_rname(N));\
}\
VAR = _rname(N).RESULT_FIELD;
#define _try_handle_void(RSLT_CALL, HANDLER, N) \
Result_ _rname(N) = RSLT_CALL;\
if(_rname(N).error){\
Error_addCallPos(_rname(N).error, ErrorCallPos_here());\
HANDLER(_rname(N));\
}\
#define _try__handler(R) Return R
#define _try(VAR, RESULT_FIELD, RSLT_CALL, N) \ #define _try(VAR, RESULT_FIELD, RSLT_CALL, N) \
Result_ _rname(N) = RSLT_CALL;\ _try_handle(VAR, RESULT_FIELD, RSLT_CALL, _try__handler, N)
if(_rname(N).error){\ #define _try_void(RSLT_CALL, N) \
Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ _try_handle_void(RSLT_CALL, _try__handler, N)
Return _rname(N);\
};\
VAR = _rname(N).RESULT_FIELD;
#define _try_void(RSLT_CALL, N) do {\
Result_ _rname(N) = RSLT_CALL;\
if(_rname(N).error){\
Error_addCallPos(_rname(N).error, ErrorCallPos_here());\
Return _rname(N);\
};\
} while(0)
#define _try_fatal__handler(R) Error_printAndExit(R.error)
#define _try_fatal(VAR, RESULT_FIELD, RSLT_CALL, N) \ #define _try_fatal(VAR, RESULT_FIELD, RSLT_CALL, N) \
Result_ _rname(N) = RSLT_CALL;\ _try_handle(VAR, RESULT_FIELD, RSLT_CALL, _try_fatal__handler, N)
if(_rname(N).error){\ #define _try_fatal_void(RSLT_CALL, N) \
Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ _try_handle_void(RSLT_CALL, _try_fatal__handler, N)
Error_printAndExit(_rname(N).error);\
};\
VAR = _rname(N).RESULT_FIELD;
#define _try_fatal_void(RSLT_CALL, N) do {\ #define _try_stderrcode(CALL, N) \
Result_ _rname(N) = RSLT_CALL;\ int _rname(N) = CALL;\
if(_rname(N).error){\ if(_rname(N) != 0){\
Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ Return RESULT_ERROR_CODE(LIBC_ERRNO, _rname(N), str_from_cstr(strerror_malloc(_rname(N))), true);\
Error_printAndExit(_rname(N).error);\ }\
};\
} while(0) #define _try_fatal_stderrcode(CALL, N) \
int _rname(N) = CALL;\
if(_rname(N) != 0){\
Error_printAndExit(Error_create(\
str_from_cstr(strerror_malloc(_rname(N))), true, \
ErrorCallPos_here(), \
ErrorCodePage_name(LIBC_ERRNO), _rname(N)\
));\
}\
#define try_assert(EXPR) if(!(EXPR)) { Return RESULT_ERROR_LITERAL("assertion must be true: " #EXPR); }

View File

@@ -4,6 +4,7 @@
#include "errors.h" #include "errors.h"
#include "string/str.h" #include "string/str.h"
#include "collections/Array.h" #include "collections/Array.h"
#include "collections/Array_impl/Array_u8.h"
#if !defined(TLIBC_FS_USE_WINDOWS_H) #if !defined(TLIBC_FS_USE_WINDOWS_H)
#if defined(_WIN64) || defined(_WIN32) #if defined(_WIN64) || defined(_WIN32)
@@ -38,6 +39,8 @@ str path_dirname(str path);
/// @return pointer to a segment of path.data or path itself if no path_sep has been found /// @return pointer to a segment of path.data or path itself if no path_sep has been found
str path_basename(str path, bool remove_ext); str path_basename(str path, bool remove_ext);
/// @return heap-allocated string
Result(char*) path_getUserDir();
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// FILE // // FILE //
@@ -58,10 +61,20 @@ str path_basename(str path, bool remove_ext);
Result(FILE*) file_open(cstr file_name, cstr fopen_mode); Result(FILE*) file_open(cstr file_name, cstr fopen_mode);
static inline void file_close(FILE* f){
if(f == NULL)
return;
fclose(f);
}
/// if file exists, opens it with "rb+", else creates it with "wb+" /// if file exists, opens it with "rb+", else creates it with "wb+"
Result(FILE*) file_openOrCreateReadWrite(cstr file_name); Result(FILE*) file_openOrCreateReadWrite(cstr file_name);
bool file_exists(cstr path); bool file_exists(cstr path);
///@return current position in file
Result(i64) file_tellPos(FILE* f);
///@return total size of file
Result(i64) file_getSize(FILE* f); Result(i64) file_getSize(FILE* f);
@@ -71,6 +84,7 @@ typedef enum SeekOrigin {
SeekOrigin_End = SEEK_END, SeekOrigin_End = SEEK_END,
} SeekOrigin; } SeekOrigin;
/// @brief changes current position in file
Result(void) file_seek(FILE* f, i64 offset, SeekOrigin origin); Result(void) file_seek(FILE* f, i64 offset, SeekOrigin origin);
@@ -82,7 +96,7 @@ static inline Result(void) file_writeBytes(FILE* f, const void* src, u64 size){
} }
static inline Result(void) file_writeBytesArray(FILE* f, Array(u8) src){ static inline Result(void) file_writeBytesArray(FILE* f, Array(u8) src){
return file_writeStructs(f, src.data, src.size, 1); return file_writeStructs(f, src.data, src.len, 1);
} }
@@ -101,10 +115,10 @@ static inline Result(u64) file_readBytes(FILE* f, void* dst, u64 max_count){
return file_readStructs(f, dst, 1, max_count); return file_readStructs(f, dst, 1, max_count);
} }
/// @param dst array where .size is the maximum number of bytes to read /// @param dst array where .len is the maximum number of bytes to read
/// @return number of bytes that were read (<=max_count) /// @return number of bytes that were read (<=max_count)
static inline Result(u64) file_readBytesArray(FILE* f, Array(u8) dst){ static inline Result(u64) file_readBytesArray(FILE* f, Array(u8) dst){
return file_readStructs(f, dst.data, 1, dst.size); return file_readStructs(f, dst.data, 1, dst.len);
} }
@@ -116,17 +130,32 @@ static inline Result(void) file_readBytesExactly(FILE* f, void* dst, u64 exact_c
return file_readStructsExactly(f, dst, 1, exact_count); return file_readStructsExactly(f, dst, 1, exact_count);
} }
/// @param dst array where .size is the exact number of bytes to read /// @param dst array where .len is the exact number of bytes to read
static inline Result(void) file_readBytesArrayExactly(FILE* f, Array(u8) dst){ static inline Result(void) file_readBytesArrayExactly(FILE* f, Array(u8) dst){
return file_readStructsExactly(f, dst.data, 1, dst.size); return file_readStructsExactly(f, dst.data, 1, dst.len);
} }
/// @brief allocates buffer of size `file_getSize(f)` and reads whole file
/// @param out_buf output array allocated on heap
Result(void) file_readWhole(FILE* f, Array(u8)* out_buf);
/// @brief allocates buffer of size `file_getSize(f) + 1` and reads whole file
/// @param out_str output str allocated on heap, null-terminated
Result(void) file_readWholeText(FILE* f, str* out_str);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// DIRECTORY // // DIRECTORY //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @return true if directory exists or `path` is null or empty or '.' or './'
bool dir_exists(cstr path); bool dir_exists(cstr path);
/// @brief creates directories specified in path recursively /// @brief creates directories specified in path recursively
/// EXAMPLE: dir_createParent("a/b/c") -> creates "a", "a/b", "a/b/c"
/// @return false if directory was present already, true if it has been created /// @return false if directory was present already, true if it has been created
Result(bool) dir_create(cstr path); Result(bool) dir_create(cstr path);
/// @brief creates directories except the last part of path
/// EXAMPLE: dir_createParent("a/b/c") -> creates "a", "a/b"
/// @return false if directory was present already, true if it has been created
Result(bool) dir_createParent(cstr path);

12
include/tlibc/magic.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include "tlibc/std.h"
typedef union Magic32 {
u32 n;
u8 bytes[4];
} Magic32;
typedef union Magic64 {
u64 n;
u8 bytes[8];
} Magic64;

View File

@@ -1,28 +1,29 @@
#pragma once #pragma once
#if __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <errno.h> #include <errno.h>
typedef int8_t i8; typedef int8_t i8;
typedef uint8_t u8; typedef int16_t i16;
typedef int16_t i16; typedef int32_t i32;
typedef uint16_t u16; typedef int64_t i64;
typedef int32_t i32; typedef uint8_t u8;
typedef uint32_t u32; typedef uint16_t u16;
typedef int64_t i64; typedef uint32_t u32;
typedef uint64_t u64; typedef uint64_t u64;
typedef float f32; typedef float f32;
typedef double f64; typedef double f64;
typedef void* ptr; typedef const char* cstr;
#if !__cplusplus && !defined(bool) #if !__cplusplus && !defined(bool)
typedef u8 bool; typedef u8 bool;
@@ -30,11 +31,36 @@ typedef u8 bool;
#define false 0 #define false 0
#endif #endif
typedef const char* cstr; typedef void (*Destructor_t)(void* self);
#define FMT_i8 "%"PRIi8
#define FMT_i16 "%"PRIi16
#define FMT_i32 "%"PRIi32
#define FMT_i64 "%"PRIi64
#define FMT_u8 "%"PRIu8
#define FMT_u16 "%"PRIu16
#define FMT_u32 "%"PRIu32
#define FMT_u64 "%"PRIu64
#define FMT_x8 "%"PRIx8
#define FMT_x16 "%"PRIx16
#define FMT_x32 "%"PRIx32
#define FMT_x64 "%"PRIx64
#define FMT_X8 "%"PRIX8
#define FMT_X16 "%"PRIX16
#define FMT_X32 "%"PRIX32
#define FMT_X64 "%"PRIX64
#define FMT_o8 "%"PRIo8
#define FMT_o16 "%"PRIo16
#define FMT_o32 "%"PRIo32
#define FMT_o64 "%"PRIo64
#define FMT_f32 "%f"
#define FMT_f64 "%lf"
#define dbg(N) printf("\e[95m%d\n",N) #define dbg(N) printf("\e[95m%d\n",N)
#define nameof(V) #V #define nameof(V) #V
#define CAT2(a, b) a ## b
#define CAT3(a, b, c) a ## b ## c
#define ARRAY_LEN(A) (sizeof(A)/sizeof(A[0])) #define ARRAY_LEN(A) (sizeof(A)/sizeof(A[0]))
#define ALIGN_TO(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) #define ALIGN_TO(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))
@@ -52,7 +78,7 @@ typedef const char* cstr;
a48,a49,a50,a51,a52,a53,a54,a55, a56,a57,a58,a59,a60,a61,a62,a63, \ a48,a49,a50,a51,a52,a53,a54,a55, a56,a57,a58,a59,a60,a61,a62,a63, \
a64,...) a64 a64,...) a64
// Macro for counting variadic arguments (max 64) // Macro for counting variadic arguments (max 64)
// (see usage in kprint.h) // (see usage in cptr.h)
#define count_args(ARGS...) __count_args(ARGS, \ #define count_args(ARGS...) __count_args(ARGS, \
64,63,62,61,60,59,58,57, 56,55,54,53,52,51,50,49, \ 64,63,62,61,60,59,58,57, 56,55,54,53,52,51,50,49, \
48,47,46,45,44,43,42,41, 40,39,38,37,36,35,34,33, \ 48,47,46,45,44,43,42,41, 40,39,38,37,36,35,34,33, \
@@ -61,10 +87,61 @@ typedef const char* cstr;
#define printfe(FORMAT, ...) fprintf(stderr, FORMAT ,##__VA_ARGS__) #define printfe(FORMAT, ...) fprintf(stderr, FORMAT ,##__VA_ARGS__)
#define zeroStruct(STRUCT_PTR) memset((STRUCT_PTR), 0, sizeof(*STRUCT_PTR));
/// @warning pointer can be null /// @warning pointer can be null
#define NULLABLE(NAME) NAME #define NULLABLE(NAME) NAME
#define ATTRIBUTE_PACKED \
__attribute__((__packed__))
#if __cplusplus #define ATTRIBUTE_ALIGNED(BYTE_N) \
__attribute__((aligned(BYTE_N)))
#define ATTRIBUTE_NORETURN \
__attribute__ ((__noreturn__))
#define ATTRIBUTE_WARN_UNUSED_RESULT \
__attribute__((warn_unused_result))
#define ATTRIBUTE_UNUSED \
__attribute__ ((unused))
///@brief https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/Common-Function-Attributes.html#index-format-function-attribute
///@param FMT_ARG_INDEX Index of format literal argument. First argument is 1.
///@param VARIADIC_ARGS_INDEX Index of variadic arguments.
#define ATTRIBUTE_CHECK_FORMAT_PRINTF(FMT_ARG_INDEX, VARIADIC_ARGS_INDEX) \
__attribute__((__format__(__printf__, FMT_ARG_INDEX, VARIADIC_ARGS_INDEX)))
// taken from libtoml
#if defined(__cplusplus) && __cplusplus >= 201103L
#define ATTRIBUTE_THREAD_LOCAL thread_local
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define ATTRIBUTE_THREAD_LOCAL _Thread_local
#elif defined(_MSC_VER)
#define ATTRIBUTE_THREAD_LOCAL __declspec(thread)
#else
#define ATTRIBUTE_THREAD_LOCAL __thread
#endif
#ifdef _MSC_VER
#define PRAGMA_WARNING_PUSH __pragma(warning( push ))
#define PRAGMA_WARNING_POP __pragma(warning( pop ))
#define PRAGMA_WARNING_DISABLE(wNumber) __pragma(warning( disable : wNumber ))
#define W_RETURN_TYPE
#else
#define _PRAGMA(P) _Pragma(#P)
#define PRAGMA_WARNING_PUSH _PRAGMA(GCC diagnostic push)
#define PRAGMA_WARNING_POP _PRAGMA(GCC diagnostic pop)
#define PRAGMA_WARNING_DISABLE(wName) _PRAGMA(GCC diagnostic ignored wName)
#define W_RETURN_TYPE "-Wreturn-type"
#endif
#define WARNING_DISABLE(WARNING, CODE...) \
PRAGMA_WARNING_PUSH \
PRAGMA_WARNING_DISABLE(WARNING) \
CODE; \
PRAGMA_WARNING_POP
#ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "../collections/List.h" #include "../collections/List.h"
#include "../collections/Array.h" #include "../collections/List_impl/List_char.h"
#include "str.h" #include "str.h"
typedef struct StringBuilder { typedef struct StringBuilder {
@@ -9,10 +9,20 @@ typedef struct StringBuilder {
} StringBuilder; } StringBuilder;
static inline StringBuilder StringBuilder_alloc(u32 initial_size) { static inline StringBuilder StringBuilder_alloc(u32 initial_size) {
return (StringBuilder){ .buffer = List_alloc_size(initial_size) }; return (StringBuilder){ .buffer = List_char_alloc(initial_size) };
} }
void StringBuilder_destroy(StringBuilder* b); void StringBuilder_destroy(StringBuilder* b);
static inline StringBuilder StringBuilder_copy(const StringBuilder* b){
return (StringBuilder) { .buffer = List_char_copy(&b->buffer) };
}
// alloc bigger buffer if size + len_to_add won't fit in current
static inline void StringBuilder_increaseCapacity(StringBuilder* b, u32 len_to_add){
List_char_increaseCapacity(&b->buffer, len_to_add);
}
/// @param count set to -1 to clear StringBuilder /// @param count set to -1 to clear StringBuilder
void StringBuilder_removeFromEnd(StringBuilder* b, u32 count); void StringBuilder_removeFromEnd(StringBuilder* b, u32 count);
void StringBuilder_append_char(StringBuilder* b, char c); void StringBuilder_append_char(StringBuilder* b, char c);
@@ -20,8 +30,11 @@ void StringBuilder_append_cstr(StringBuilder* b, cstr s);
void StringBuilder_append_str(StringBuilder* b, str s); void StringBuilder_append_str(StringBuilder* b, str s);
void StringBuilder_append_i64(StringBuilder* b, i64 a); void StringBuilder_append_i64(StringBuilder* b, i64 a);
void StringBuilder_append_u64(StringBuilder* b, u64 a); void StringBuilder_append_u64(StringBuilder* b, u64 a);
void StringBuilder_append_f32(StringBuilder* b, f32 n);
void StringBuilder_append_f64(StringBuilder* b, f64 a); void StringBuilder_append_f64(StringBuilder* b, f64 a);
void StringBuilder_append_memory(StringBuilder* b, Array(u8) mem, bool uppercase); void StringBuilder_append_memory(StringBuilder* b, Array(u8) mem, bool uppercase);
// adds '\0' to the buffer and returns pointer to buffer content // adds '\0' to the buffer and returns pointer to buffer content
str StringBuilder_getStr(StringBuilder* b); str StringBuilder_getStr(StringBuilder* b);
bool StringBuilder_equals(const StringBuilder* a, const StringBuilder* b);

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "../std.h" #include "../std.h"
static inline bool isAlphabeticalLower(char c) { return 'a' <= c && c <= 'z'; } static inline bool char_isLatinLower(char c) { return 'a' <= c && c <= 'z'; }
static inline bool isAlphabeticalUpper(char c) { return 'A' <= c && c <= 'Z'; } static inline bool char_isLatinUpper(char c) { return 'A' <= c && c <= 'Z'; }
static inline bool isDigit(char c) { return '0' <= c && c <= '9'; } static inline bool char_isDigit(char c) { return '0' <= c && c <= '9'; }

View File

@@ -1,9 +1,14 @@
#pragma once #pragma once
#include "../std.h" #include "../std.h"
#define strcat_malloc(STR0, ...) _strcat_malloc(count_args(__VA_ARGS__), STR0, __VA_ARGS__) char* cstr_copy(cstr self);
char* _strcat_malloc(size_t n, cstr str0, ...);
char* _vstrcat_malloc(size_t n, cstr str0, va_list argv);
char* NULLABLE(sprintf_malloc)(size_t buffer_size, cstr format, ...) __attribute__((__format__(__printf__, 2, 3))); #define strcat_malloc(STR0, ...) _strcat_malloc(count_args(__VA_ARGS__), STR0, __VA_ARGS__)
char* NULLABLE(vsprintf_malloc)(size_t buffer_size, cstr format, va_list argv); char* _strcat_malloc(u64 n, cstr str0, ...);
char* _vstrcat_malloc(u64 n, cstr str0, va_list args);
char* sprintf_malloc(cstr format, ...) ATTRIBUTE_CHECK_FORMAT_PRINTF(1, 2);
char* vsprintf_malloc(cstr format, va_list args);
/// thread-safe version of strerror
char* strerror_malloc(int errcode);

View File

@@ -4,47 +4,98 @@
#include "char.h" #include "char.h"
#include "cstr.h" #include "cstr.h"
#include "../collections/Array.h" #include "../collections/Array.h"
#include "../collections/Array_impl/Array_char.h"
#include "../collections/Array_impl/Array_u8.h"
typedef struct str { typedef struct str {
char* data; char* data;
u32 size; // size of data in bytes without \0 u32 len; // size of data in bytes without \0
bool isZeroTerminated; bool isZeroTerminated;
} str; } str;
/*
USAGE:
str s = STR("something");
printf(FMT_str"\n", str_unwrap(s));
*/
#define FMT_str "%.*s"
#define str_unwrap(S) (S).len, (S).data
/// creates str from a string literal /// creates str from a string literal
#define STR(LITERAL) str_construct(LITERAL, ARRAY_LEN(LITERAL) - 1, true) #define STR(LITERAL) str_construct(LITERAL, ARRAY_LEN(LITERAL) - 1, true)
#define str_construct(DATA, LEN, ZERO_TERMINATED) ((str){ .data = DATA, .size = LEN, .isZeroTerminated = ZERO_TERMINATED }) #define str_construct(DATA, LEN, ZERO_TERMINATED) ((str){ .data = DATA, .len = LEN, .isZeroTerminated = ZERO_TERMINATED })
static inline str str_from_cstr(cstr s_ptr){ static inline str str_from_cstr(cstr s_ptr){
return str_construct((void*)s_ptr, strlen(s_ptr), true); return str_construct((void*)s_ptr, strlen(s_ptr), true);
} }
/// destroy str with .data allocated on heap
static inline void str_destroy(str self){
free(self.data);
}
/// destroy str allocated on heap with .data allocated on heap
static inline void str_free(str* self){
if(!self)
return;
free(self->data);
free(self);
}
static inline Array(char) str_castTo_Array_char(str s) {
return Array_char_construct(s.data, s.len);
}
static inline Array(u8) str_castTo_Array_u8(str s) {
return Array_u8_construct((void*)s.data, s.len);
}
static inline str Array_char_castTo_str(Array(char) a, bool isZeroTerminated) {
return str_construct(a.data, a.len, isZeroTerminated);
}
static inline str Array_u8_castTo_str(Array(u8) a, bool isZeroTerminated) {
return str_construct((void*)a.data, a.len, isZeroTerminated);
}
static const str str_null = str_construct(NULL, 0, 0); static const str str_null = str_construct(NULL, 0, 0);
/// copies src content to new string and adds \0 at the end /// copy str data to new str and add \0 at the end
str str_copy(str src); str str_copy(const str self);
/// compares two strings, NullPtr-friendly /// compares two strings, NullPtr-friendly
bool str_equals(str str0, str str1); bool str_equals(const str self, const str other);
/// allocates new string which is reversed variant of <s> /// allocates new string which is reversed variant of <s>
str str_reverse(str s); str str_reverse(str s);
i32 str_seek(str src, str fragment, u32 startIndex); i32 str_seek(const str src, const str fragment, u32 startIndex);
i32 str_seekReverse(str src, str fragment, u32 startIndex); i32 str_seekReverse(const str src, const str fragment, u32 startIndex);
i32 str_seekChar(str src, char c, u32 startIndex); i32 str_seekChar(const str src, char c, u32 startIndex);
i32 str_seekCharReverse(str src, char c, u32 startIndex); i32 str_seekCharReverse(const str src, char c, u32 startIndex);
bool str_startsWith(str src, str fragment); bool str_startsWith(const str src, const str fragment);
bool str_endsWith(str src, str fragment); bool str_endsWith(const str src, const str fragment);
/// @brief calculates string hash using sdbm32 algorythm (something like lightweight crc32) /// @brief calculates string hash using sdbm32 algorythm (something like lightweight crc32)
/// @return non-cryptografic hash of the string /// @return non-cryptografic hash of the string
u32 str_hash32(str s); u32 str_hash32(const str s);
str str_toUpper(str src); str str_toUpper(const str src);
str str_toLower(str src); str str_toLower(const str src);
str hex_to_str(Array(u8) buf, bool uppercase); str hex_to_str(Array(u8) buf, bool uppercase);
/// @brief removes blank characters from start and end of the line
void str_trim(str* line, bool set_zero_at_end);
///@return s[0..n]
static inline str str_sliceBefore(str s, u32 n){
return str_construct(s.data, n, false);
}
///@return s[n...]
static inline str str_sliceAfter(str s, u32 n){
return str_construct(s.data + n, s.len - n, s.isZeroTerminated);
}

47
include/tlibc/term.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include "tlibc/errors.h"
typedef struct TerminalSize {
i16 cols;
i16 rows;
} TerminalSize;
typedef enum Color16 {
Color16_Black = 30,
Color16_DarkRed = 31,
Color16_DarkGreen = 32,
Color16_DarkYellow = 33,
Color16_DarkBlue = 34,
Color16_DarkMagenta = 35,
Color16_DarkCyan = 36,
Color16_Gray = 37,
Color16_DarkGray = 90,
Color16_Red = 91,
Color16_Green = 92,
Color16_Yellow = 93,
Color16_Blue = 94,
Color16_Magenta = 95,
Color16_Cyan = 96,
Color16_White = 97
} Color16;
Result(void) term_init();
Result(void) term_getSize(TerminalSize* out);
Result(void) term_readLine(char* buf, u32 bufsize);
Result(void) term_readLineHidden(char *buf, u32 bufsize);
void term_setFgColor16(Color16 c);
void term_setBgColor16(Color16 c);
void term_bold();
void term_italic();
void term_underline();
void term_strikethrough();
void term_resetColors();
void term_clear();
void term_eraseRow();
void term_resetCursor();
void term_cursorMove(u16 row, u16 column);
void term_cursorHide();
void term_cursorShow();

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
#if __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include "std.h" #include "errors.h"
/// nanoseconds /// nanoseconds
typedef u64 nsec_t; typedef u64 nsec_t;
@@ -12,6 +12,8 @@ typedef u64 nsec_t;
typedef u64 usec_t; typedef u64 usec_t;
/// miliseconds /// miliseconds
typedef u64 msec_t; typedef u64 msec_t;
/// seconds
typedef u64 sec_t;
/// system time now in nanoseconds /// system time now in nanoseconds
///@return u64 will overflow in 13 years ///@return u64 will overflow in 13 years
@@ -25,6 +27,49 @@ void sleepNsec(nsec_t time);
void sleepUsec(usec_t time); void sleepUsec(usec_t time);
void sleepMsec(msec_t time); void sleepMsec(msec_t time);
#if __cplusplus
typedef struct Time {
i32 nsec; /* 0..999_999_999 */
i8 sec; /* 0..60 leap second??? */
i8 min; /* 0..59 */
i8 hour; /* 0..23 */
} Time;
typedef struct Date {
i8 month_day; /* 1..31 */
i8 month; /* 1..12 */
i16 year; /* 1900-xxxx */
i8 week_day; /* 1..7 */
i16 year_day; /* 1..366 */
} Date;
typedef struct DateTime {
Date d;
Time t;
} DateTime;
void DateTime_get(DateTime* dt, bool utc_time);
static inline void DateTime_getLocal(DateTime* dt) { DateTime_get(dt, false); }
static inline void DateTime_getUTC(DateTime* dt) { DateTime_get(dt, true); }
/// yyyy.MM.dd HH:mm:ss_float
Result(void) DateTime_parse(cstr src, DateTime* dt);
/// yyyy.MM.dd_HH-mm-ss
#define FMT_DateTime_fileName "%04i.%02i.%02i_%02i-%02i-%02i"
/// yyyy.MM.dd HH:mm:ss
/// yyyy.MM.dd HH:mm:ss_float
#define FMT_DateTime_text "%04i.%02i.%02i-%02i:%02i:%02i"
#define FMT_DateTime_text_subsec FMT_DateTime_text".%09i"
/*
USAGE:
DateTime dt;
DateTime_getLocal(&dt);
printf(FMT_DateTime_text, DT_expand(dt));
*/
#define DT_expand(dt) dt.d.year, dt.d.month, dt.d.month_day, dt.t.hour, dt.t.min, dt.t.sec
#define DT_expand_subsec(dt) dt.d.year, dt.d.month, dt.d.month_day, dt.t.hour, dt.t.min, dt.t.sec, dt.t.nsec
#ifdef __cplusplus
} }
#endif #endif

5
include/tlibc/tlibc.h Normal file
View File

@@ -0,0 +1,5 @@
#include "tlibc/errors.h"
/// call once at the start of main()
Result(void) tlibc_init();
void tlibc_deinit();

View File

@@ -1,19 +1,28 @@
#!/usr/bin/env bash #!/usr/bin/env bash
CBUILD_VERSION=2.2.3 CBUILD_VERSION=2.3.0
PROJECT="tlibc" PROJECT="tlibc"
CMP_C="gcc" CMP_C="gcc"
CMP_CPP="g++" CMP_CPP="g++"
STD_C="c99" STD_C="c11"
STD_CPP="c++11" STD_CPP="c++11"
WARN_C="-Wall -Wextra -Werror=return-type -Werror=pointer-arith" WARN_C="-Wall -Wextra
WARN_CPP="-Wall -Wextra -Werror=return-type -Werror=pointer-arith" -Wduplicated-branches
-Wduplicated-cond
-Wformat=2
-Wmissing-include-dirs
-Wshadow
-Werror=return-type
-Werror=pointer-arith
-Werror=init-self
-Werror=incompatible-pointer-types"
WARN_CPP="$WARN_C"
SRC_C="$(find src -name '*.c')" SRC_C="$(find src -name '*.c')"
SRC_CPP="$(find src -name '*.cpp')" SRC_CPP="$(find src -name '*.cpp')"
# Directory with dependency configs. # Directory with dependency configs.
# See cbuild/example_dependency_configs # See cbuild/example_dependency_configs
DEPENDENCY_CONFIGS_DIR='.' DEPENDENCY_CONFIGS_DIR='dependencies'
# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space. # List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space.
ENABLED_DEPENDENCIES='' ENABLED_DEPENDENCIES=''
@@ -34,7 +43,7 @@ case "$OS" in
EXEC_FILE="$PROJECT.exe" EXEC_FILE="$PROJECT.exe"
SHARED_LIB_FILE="$PROJECT.dll" SHARED_LIB_FILE="$PROJECT.dll"
INCLUDE="$INCLUDE " INCLUDE="$INCLUDE "
LINKER_LIBS="" LINKER_LIBS="-luuid"
;; ;;
LINUX) LINUX)
EXEC_FILE="$PROJECT" EXEC_FILE="$PROJECT"
@@ -60,61 +69,61 @@ case "$TASK" in
C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-use -fprofile-prefix-path=$(realpath $OBJDIR)/objects -fdata-sections -ffunction-sections -Wl,--gc-sections" C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-use -fprofile-prefix-path=$(realpath $OBJDIR)/objects -fdata-sections -ffunction-sections -Wl,--gc-sections"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=""
TASK_SCRIPT=cbuild/default_tasks/build_exec.sh TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# creates executable with debug info and no optimizations # creates executable with debug info and no optimizations
build_exec_dbg) build_exec_dbg)
C_ARGS="-O0 -g3" C_ARGS="-O0 -g3"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=""
TASK_SCRIPT=cbuild/default_tasks/build_exec.sh TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# creates shared library # creates shared library
build_shared_lib) build_shared_lib)
C_ARGS="-O2 -fpic -flto -shared" C_ARGS="-O2 -fpic -flto -shared"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=""
TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh TASK_SCRIPT="@cbuild/default_tasks/build_shared_lib.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# creates shared library with debug symbols and no optimizations # creates shared library with debug symbols and no optimizations
build_shared_lib_dbg) build_shared_lib_dbg)
C_ARGS="-O0 -g3 -fpic -shared" C_ARGS="-O0 -g3 -fpic -shared"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=""
TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh TASK_SCRIPT="@cbuild/default_tasks/build_shared_lib.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# creates static library # creates static library
build_static_lib) build_static_lib)
C_ARGS="-O2 -fpic -fdata-sections -ffunction-sections" C_ARGS="-O2 -fpic -fdata-sections -ffunction-sections"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=""
TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh TASK_SCRIPT="@cbuild/default_tasks/build_static_lib.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# creates static library with debug symbols and no optimizations # creates static library with debug symbols and no optimizations
build_static_lib_dbg) build_static_lib_dbg)
C_ARGS="-O0 -g3" C_ARGS="-O0 -g3"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=""
TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh TASK_SCRIPT="@cbuild/default_tasks/build_static_lib.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# executes $EXEC_FILE # executes $EXEC_FILE
exec) exec)
TASK_SCRIPT=cbuild/default_tasks/exec.sh TASK_SCRIPT="@cbuild/default_tasks/exec.sh"
;; ;;
# executes $EXEC_FILE with valgrind memory checker # executes $EXEC_FILE with valgrind memory checker
valgrind) valgrind)
VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all" VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all"
TASK_SCRIPT=cbuild/default_tasks/valgrind.sh TASK_SCRIPT="@cbuild/default_tasks/valgrind.sh"
;; ;;
# generates profiling info # generates profiling info
profile) profile)
@@ -128,22 +137,25 @@ case "$TASK" in
C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-generate -fprofile-prefix-path=$(realpath $OBJDIR)/objects" C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-generate -fprofile-prefix-path=$(realpath $OBJDIR)/objects"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
TASK_SCRIPT=cbuild/default_tasks/profile.sh TASK_SCRIPT="@cbuild/default_tasks/profile.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# compiles program with -pg and runs it with gprof # compiles program with -pg and runs it with gprof
# uses gprof2dot python script to generate function call tree (pip install gprof2dot) # uses gprof2dot python script to generate function call tree (pip install gprof2dot)
# requires graphviz (https://www.graphviz.org/download/source/) # requires graphviz (https://www.graphviz.org/download/source/)
gprof) gprof)
OUTDIR="$OUTDIR/gprof" OUTDIR="$OUTDIR/gprof"
# -pg adds code to executable, that generates file containing function call info (gmon.out) # arguments that emit some call counter code and disable optimizations to see function names
C_ARGS="-O2 -flto=auto -fuse-linker-plugin -pg" # https://github.com/msys2/MINGW-packages/issues/8503#issuecomment-1365475205
C_ARGS="-O0 -g -pg -no-pie -fno-omit-frame-pointer
-fno-inline-functions -fno-inline-functions-called-once
-fno-optimize-sibling-calls -fopenmp"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
TASK_SCRIPT=cbuild/default_tasks/gprof.sh TASK_SCRIPT="@cbuild/default_tasks/gprof.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# compiles program and runs it with callgrind (part of valgrind) # compiles program and runs it with callgrind (part of valgrind)
# uses gprof2dot python script to generate function call tree (pip install gprof2dot) # uses gprof2dot python script to generate function call tree (pip install gprof2dot)
@@ -155,9 +167,9 @@ case "$TASK" in
C_ARGS="-O2 -flto=auto -fuse-linker-plugin" C_ARGS="-O2 -flto=auto -fuse-linker-plugin"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
TASK_SCRIPT=cbuild/default_tasks/callgrind.sh TASK_SCRIPT="@cbuild/default_tasks/callgrind.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# compiles executable with sanitizers and executes it to find errors and warnings # compiles executable with sanitizers and executes it to find errors and warnings
sanitize) sanitize)
@@ -165,19 +177,19 @@ case "$TASK" in
C_ARGS="-O0 -g3 -fsanitize=undefined,address" C_ARGS="-O0 -g3 -fsanitize=undefined,address"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
TASK_SCRIPT=cbuild/default_tasks/exec.sh TASK_SCRIPT="@cbuild/default_tasks/exec.sh"
POST_TASK_SCRIPT= POST_TASK_SCRIPT=""
;; ;;
# rebuilds specified dependencies # rebuilds specified dependencies
# EXAMPLE: `cbuild rebuild_dependencies=libexample1,fonts` # EXAMPLE: `cbuild rebuild_dependencies=libexample1,fonts`
# 'all' can be specified to rebuild all dependencies # 'all' can be specified to rebuild all dependencies
rebuild_dependencies) rebuild_dependencies)
TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh TASK_SCRIPT="@cbuild/default_tasks/rebuild_dependencies.sh"
;; ;;
# deletes generated files # deletes generated files
clean) clean)
TASK_SCRIPT=cbuild/default_tasks/clean.sh TASK_SCRIPT="@cbuild/default_tasks/clean.sh"
;; ;;
# nothing to do # nothing to do
"" | no_task) "" | no_task)

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Project user config is ignored by git.
# Here you can add variables that users might want to change
# on their local machine, without commiting to the repository.
# Directory where you install dependencies.
# Do not confuse with DEPENDENCY_CONFIGS_DIR
# Example:
# libexample source code is at `../libexample`, and dependency config
# that specifies how to build this lib is at `dependencies/libexample.config`
DEPENDENCIES_DIR=".."

86
src/base64.c Normal file
View File

@@ -0,0 +1,86 @@
#include "tlibc/base64.h"
// based on https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c
u32 base64_encodedSize(u32 src_size){
u32 ret = src_size;
if (src_size % 3 != 0)
ret += 3 - (src_size % 3);
ret /= 3;
ret *= 4;
return ret;
}
static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
u32 base64_encode(const u8* src, u32 src_size, char* dst){
u32 i = 0, j = 0, v = 0;
for (; i < src_size; i += 3) {
v = src[i];
v = i + 1 < src_size ? v << 8 | src[i + 1] : v << 8;
v = i + 2 < src_size ? v << 8 | src[i + 2] : v << 8;
dst[j++] = b64chars[(v >> 18) & 0x3F];
dst[j++] = b64chars[(v >> 12) & 0x3F];
dst[j++] = i + 1 < src_size ? b64chars[(v >> 6) & 0x3F] : '=';
dst[j++] = i + 2 < src_size ? b64chars[v & 0x3F] : '=';
}
return j;
}
u32 base64_decodedSize(const char* src, u32 src_size){
// incomplete src
if(src_size % 4 != 0)
return 0;
u32 ret = src_size / 4 * 3;
u32 i = src_size;
while(i > 0 && src[--i] == '=') {
ret--;
}
return ret;
}
/*
void base64_generateDecodeTable(){
int inv[80];
memset(inv, -1, sizeof(inv));
for (u32 i = 0; i < 64; i + +) {
inv[b64chars[i] - 43] = i;
}
}
*/
static int b64inverse[] = {
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51
};
u32 base64_decode(const char* src, u32 src_size, u8* dst){
// incomplete src
if(src_size % 4 != 0)
return 0;
u32 i = 0, j = 0, v = 0;
for (; i < src_size; i += 4) {
v = b64inverse[src[i] - 43];
v = (v << 6) | b64inverse[src[i + 1] - 43];
v = src[i + 2]=='=' ? v << 6 : (v << 6) | b64inverse[src[i + 2] - 43];
v = src[i + 3]=='=' ? v << 6 : (v << 6) | b64inverse[src[i + 3] - 43];
dst[j++] = (v >> 16) & 0xFF;
if (src[i + 2] != '='){
dst[j++] = (v >> 8) & 0xFF;
}
if (src[i + 3] != '='){
dst[j++] = v & 0xFF;
}
}
return j;
}

View File

@@ -1,4 +1,6 @@
#include "tlibc/collections/HashMap.h" #include "tlibc/collections/HashMap.h"
#include "tlibc/collections/Array.h"
#include "tlibc/collections/Array_impl/Array_u32.h"
#include <assert.h> #include <assert.h>
//TODO: sort bucket keys for binary search //TODO: sort bucket keys for binary search
@@ -9,36 +11,44 @@
static const Array(u32) __HashMap_heights = ARRAY(u32, { static const Array(u32) __HashMap_heights = ARRAY(u32, {
0, 17, 31, 61, 127, 257, 521, 1021, 2053, 4099, 8191, 16381, 0, 17, 31, 61, 127, 257, 521, 1021, 2053, 4099, 8191, 16381,
32771, 65521, 131071, 262147, 524287, 1048583, 2097169, 4194319, 32771, 65521, 131071, 262147, 524287, 1048583, 2097169, 4194319,
8388617, 16777213, 33554467, 67108859, 134217757, 268435493 8388617, 16777213, 33554467, 67108859, 134217757, 268435493,
536871001, 1073742037, 2147484061
}); });
void HashMapBucket_construct(HashMapBucket* self, u32 value_t_size){
void HashMap_construct_size(HashMap_* ptr, u32 value_t_size, FreeFunction NULLABLE(value_destructor)){ self->key_hash_list = List_HashMapKeyHash_construct(NULL, 0, 0);
ptr->value_t_size = value_t_size; self->value_list = _List_construct(NULL, 0, 0, value_t_size);
ptr->value_destructor = value_destructor;
ptr->height_n = 0;
ptr->height = 0;
ptr->table = NULL;
} }
void HashMap_destroy(HashMap_* ptr){ void HashMap_construct_size(HashMap_* self, u32 value_t_size, Destructor_t NULLABLE(value_destructor)){
for(u32 i = 0; i < ptr->height; i++){ self->value_t_size = value_t_size;
HashMapBucket* bu = &ptr->table[i]; self->value_destructor = value_destructor;
u32 len = List_len(&bu->key_hash_list, KeyHash); self->height_n = 0;
self->height = 0;
self->table = NULL;
}
void HashMap_destroy(HashMap_* self){
if(!self)
return;
for(u32 i = 0; i < self->height; i++){
HashMapBucket* bu = &self->table[i];
// free key strings // free key strings
u32 len = bu->key_hash_list.len;
for(u32 j = 0; j < len; j++){ for(u32 j = 0; j < len; j++){
KeyHash* kh = (KeyHash*)bu->key_hash_list.data + j; HashMapKeyHash* kh = (HashMapKeyHash*)bu->key_hash_list.data + j;
free(kh->key.data); free(kh->key.data);
} }
// destroy values // destroy values
if(ptr->value_destructor){ if(self->value_destructor){
u8* value_ptr = (u8*)bu->value_list.data; u8* value_ptr = (u8*)bu->value_list.data;
u8* end = value_ptr + bu->value_list.size; u8* end = value_ptr + bu->value_list.len * bu->value_list.elem_t_size;
while(value_ptr < end){ while(value_ptr < end){
ptr->value_destructor(value_ptr); self->value_destructor(value_ptr);
value_ptr += ptr->value_t_size; value_ptr += self->value_t_size;
} }
} }
@@ -46,7 +56,7 @@ void HashMap_destroy(HashMap_* ptr){
free(bu->value_list.data); free(bu->value_list.data);
} }
free(ptr->table); free(self->table);
} }
typedef struct BucketAndIndex { typedef struct BucketAndIndex {
@@ -59,14 +69,15 @@ typedef struct BucketAndIndex {
///@returns `HashMapBucket*` for specified key or NULL, if table hasn't been allocated yet; ///@returns `HashMapBucket*` for specified key or NULL, if table hasn't been allocated yet;
/// Index of existing item with the same key or -1 if no item is present. /// Index of existing item with the same key or -1 if no item is present.
static BucketAndIndex __HashMap_search(HashMap_* ptr, str key, u32 hash){ static BucketAndIndex __HashMap_search(const HashMap_* self, const str key, u32 hash){
if(ptr->height == 0) if(self->height == 0)
return BucketAndIndex_null; return BucketAndIndex_null;
BucketAndIndex r; BucketAndIndex r;
r.bu = &ptr->table[hash % ptr->height]; r.bu = &self->table[hash % self->height];
for(r.i = 0; r.i < (i32)List_len(&r.bu->key_hash_list, KeyHash); r.i++){ i32 len = r.bu->key_hash_list.len;
KeyHash* kh = (KeyHash*)r.bu->key_hash_list.data + r.i; for(r.i = 0; r.i < len; r.i++){
HashMapKeyHash* kh = (HashMapKeyHash*)r.bu->key_hash_list.data + r.i;
if(kh->hash == hash && str_equals(kh->key, key)){ if(kh->hash == hash && str_equals(kh->key, key)){
return r; return r;
} }
@@ -76,75 +87,144 @@ static BucketAndIndex __HashMap_search(HashMap_* ptr, str key, u32 hash){
return r; return r;
} }
void* NULLABLE(HashMap_tryGetPtr)(HashMap_* ptr, str key){ void* HashMap_tryGetPtr(const HashMap_* self, const str key){
u32 hash = __HashMap_HASH_FUNC(key); u32 hash = __HashMap_HASH_FUNC(key);
BucketAndIndex r = __HashMap_search(ptr, key, hash); BucketAndIndex r = __HashMap_search(self, key, hash);
// key not found // key not found
if(r.i == -1) if(r.i == -1)
return NULL; return NULL;
return ((u8*)r.bu->value_list.data) + r.i * ptr->value_t_size; return (u8*)r.bu->value_list.data + r.i * self->value_t_size;
} }
static void __HashMap_expand(HashMap_* ptr){ static void __HashMap_expand(HashMap_* self){
u32 height_expanded_n = ptr->height_n + 1; u32 height_expanded_n = self->height_n + 1;
assert(height_expanded_n < Array_len(&__HashMap_heights, u32) && "HashMap IS FULL! Fix your code."); assert(height_expanded_n < __HashMap_heights.len && "HashMap IS FULL! Fix your code.");
// alloc new HashMapBucket array // alloc new HashMapBucket array
u32 height_expanded = ((u32*)__HashMap_heights.data)[height_expanded_n]; u32 height_expanded = __HashMap_heights.data[height_expanded_n];
u32 table_expanded_size = height_expanded * sizeof(HashMapBucket); u32 table_expanded_size = height_expanded * sizeof(HashMapBucket);
HashMapBucket* table_expanded = (HashMapBucket*)malloc(table_expanded_size); HashMapBucket* table_expanded = (HashMapBucket*)malloc(table_expanded_size);
memset(table_expanded, 0, table_expanded_size); // init buckets
for(u32 i = 0; i < height_expanded; i++){
HashMapBucket_construct(table_expanded + i, self->value_t_size);
}
// copy values from old buckets to new // copy values from old buckets to new
for(u32 i = 0; i < ptr->height; i++){ for(u32 i = 0; i < self->height; i++){
HashMapBucket* old_bucket = &ptr->table[i]; HashMapBucket* old_bucket = &self->table[i];
u32 len = List_len(&old_bucket->key_hash_list, KeyHash);
u32 len = old_bucket->key_hash_list.len;
for(u32 j = 0; j < len; j++){ for(u32 j = 0; j < len; j++){
KeyHash kh = ((KeyHash*)old_bucket->key_hash_list.data)[j]; HashMapKeyHash* kh = old_bucket->key_hash_list.data + j;
HashMapBucket* new_bucket = &table_expanded[kh.hash % height_expanded]; HashMapBucket* new_bucket = &table_expanded[kh->hash % height_expanded];
List_push(&new_bucket->key_hash_list, KeyHash, kh);
void* old_value_ptr = (u8*)old_bucket->value_list.data + j * ptr->value_t_size; List_HashMapKeyHash_pushMany(&new_bucket->key_hash_list, kh, 1);
List_push_size(&new_bucket->value_list, old_value_ptr, ptr->value_t_size); void* old_value_ptr = (u8*)old_bucket->value_list.data + j * self->value_t_size;
_List_push(&new_bucket->value_list, old_value_ptr, 1);
} }
free(old_bucket->key_hash_list.data); List_HashMapKeyHash_destroy(&old_bucket->key_hash_list);
free(old_bucket->value_list.data); _List_destroy(&old_bucket->value_list);
} }
free(ptr->table); free(self->table);
ptr->table = table_expanded; self->table = table_expanded;
ptr->height = height_expanded; self->height = height_expanded;
ptr->height_n = height_expanded_n; self->height_n = height_expanded_n;
} }
bool HashMap_tryPush(HashMap_* ptr, str key, void* value_ptr){ bool HashMap_tryPush(HashMap_* self, const str key, void* value_ptr){
u32 hash = __HashMap_HASH_FUNC(key); u32 hash = __HashMap_HASH_FUNC(key);
BucketAndIndex r = __HashMap_search(ptr, key, hash); BucketAndIndex r = __HashMap_search(self, key, hash);
// found existing item with the same key // found existing item with the same key
if(r.i != -1) if(r.i != -1)
return false; return false;
HashMapBucket* bu = r.bu; HashMapBucket* bu = r.bu;
if(bu == NULL || List_len(&bu->key_hash_list, KeyHash) >= __HashMapBucket_MAX_LEN){ if(bu == NULL || r.bu->key_hash_list.len >= __HashMapBucket_MAX_LEN){
__HashMap_expand(ptr); __HashMap_expand(self);
bu = &ptr->table[hash % ptr->height]; bu = &self->table[hash % self->height];
} }
KeyHash kh = { .key = str_copy(key), .hash = hash }; HashMapKeyHash kh = { .key = str_copy(key), .hash = hash };
List_push(&bu->key_hash_list, KeyHash, kh); List_HashMapKeyHash_push(&bu->key_hash_list, kh);
List_push_size(&bu->value_list, value_ptr, ptr->value_t_size); _List_push(&bu->value_list, value_ptr, 1);
return true; return true;
} }
bool HashMap_tryDelete(HashMap_* ptr, str key){ void HashMap_pushOrUpdate(HashMap_* self, const str key, void* value_ptr){
u32 hash = __HashMap_HASH_FUNC(key); u32 hash = __HashMap_HASH_FUNC(key);
BucketAndIndex r = __HashMap_search(ptr, key, hash); BucketAndIndex r = __HashMap_search(self, key, hash);
// found existing item with the same key
if(r.i != -1){
void* existing_item = (u8*)r.bu->value_list.data + r.i * self->value_t_size;
memcpy(existing_item, value_ptr, self->value_t_size);
return;
}
HashMapBucket* bu = r.bu;
if(bu == NULL || r.bu->key_hash_list.len >= __HashMapBucket_MAX_LEN){
__HashMap_expand(self);
bu = &self->table[hash % self->height];
}
HashMapKeyHash kh = { .key = str_copy(key), .hash = hash };
List_HashMapKeyHash_push(&bu->key_hash_list, kh);
_List_push(&bu->value_list, value_ptr, 1);
}
bool HashMap_tryDelete(HashMap_* self, const str key){
u32 hash = __HashMap_HASH_FUNC(key);
BucketAndIndex r = __HashMap_search(self, key, hash);
// key not found // key not found
if(r.i == -1) if(r.i == -1)
return false; return false;
List_removeAt(&r.bu->key_hash_list, KeyHash, r.i, 1); if(!List_HashMapKeyHash_tryRemoveAt(&r.bu->key_hash_list, r.i, 1))
List_removeAt_size(&r.bu->value_list, r.i, ptr->value_t_size); return false;
if(!_List_tryRemoveAt(&r.bu->value_list, r.i, 1))
return false;
return true;
}
bool HashMapIter_moveNext(HashMapIter* self){
if(self->map == NULL)
return false;
if(self->bucket_n == -1){
self->bucket_n = 0;
}
// move to next element in current bucket
const HashMapBucket* bu = &self->map->table[self->bucket_n];
i32 bu_elem_len = bu->key_hash_list.len;
if(self->elem_n + 1 < bu_elem_len){
self->elem_n++;
return true;
}
else {
// move to next non-empty bucket
while(self->bucket_n + 1 < (i32)self->map->height){
self->bucket_n++;
self->elem_n = 0;
bu = &self->map->table[self->bucket_n];
if(bu->key_hash_list.len != 0)
return true;
}
}
return false;
}
bool HashMapIter_getCurrent(HashMapIter* self, HashMapKeyValue* kv){
if(self->map == NULL || self->bucket_n == -1 || self->elem_n == -1)
return false;
const HashMapBucket* bu = &self->map->table[self->bucket_n];
// table is empty
if(bu->key_hash_list.len == 0)
return false;
kv->key = bu->key_hash_list.data[self->elem_n].key;
kv->value_ptr = (u8*)bu->value_list.data + self->map->value_t_size * self->elem_n;
return true; return true;
} }

58
src/collections/LList.c Normal file
View File

@@ -0,0 +1,58 @@
#include "tlibc/collections/LList.h"
#include <assert.h>
void _LList_detatch(LList_* l, LLNode_* n){
if(n == l->first){
l->first = n->next;
}
else {
assert(n->prev != NULL);
n->prev->next = n->next;
}
if(n == l->last){
l->last = n->prev;
}
else {
assert(n->next != NULL);
n->next->prev = n->prev;
}
l->count--;
n->prev = NULL;
n->next = NULL;
}
void _LList_insertAfter(LList_* l, NULLABLE(LLNode_*) target, LLNode_* detatched)
{
if(target == NULL){
assert(l->first == NULL && l->last == NULL);
l->first = detatched;
l->last = detatched;
return;
}
detatched->prev = target;
detatched->next = target->next;
target->next = detatched;
if(detatched->next){
detatched->next->prev = detatched;
}
}
void _LList_insertBefore(LList_* l, NULLABLE(LLNode_*) target, LLNode_* detatched)
{
if(target == NULL){
assert(l->first == NULL && l->last == NULL);
l->first = detatched;
l->last = detatched;
return;
}
detatched->prev = target->prev;
detatched->next = target;
target->prev = detatched;
if(detatched->prev){
detatched->prev->next = detatched;
}
}

View File

@@ -1,39 +1,73 @@
#include "tlibc/collections/List.h" #include "tlibc/collections/List.h"
#include <assert.h>
List_ List_alloc_size(u32 initial_size){ List_ _List_alloc(u32 initial_capacity, u32 elem_t_size){
if(initial_size == 0) assert(elem_t_size != 0);
return List_construct_size(NULL, 0, 0); if(initial_capacity == 0)
u32 allocated_size = ALIGN_TO(initial_size, sizeof(void*)); return _List_construct(NULL, 0, 0, elem_t_size);
return List_construct_size(malloc(allocated_size), 0, allocated_size); u32 initial_size = initial_capacity * elem_t_size;
u32 aligned_capacity = ALIGN_TO(initial_size, sizeof(void*));
return _List_construct(malloc(aligned_capacity), 0, aligned_capacity, elem_t_size);
} }
void* List_expand_size(List_* ptr, u32 expansion_size){ List_ _List_copy(const List_* src){
u32 occupied_size = ptr->size; assert(src->elem_t_size != 0);
u32 expanded_alloc_size = ptr->allocated_size; List_ copy = _List_alloc(src->capacity, src->elem_t_size);
if(expanded_alloc_size == 0) if(copy.data != NULL){
expanded_alloc_size = 64; memcpy(copy.data, src->data, src->len * src->elem_t_size);
ptr->size += expansion_size;
while(ptr->size > expanded_alloc_size){
expanded_alloc_size *= 2;
} }
// if ptr->data is null, realloc acts like malloc return copy;
ptr->data = realloc(ptr->data, expanded_alloc_size);
ptr->allocated_size = expanded_alloc_size;
return (u8*)(ptr->data) + occupied_size;
} }
void List_push_size(List_* ptr, void* values, u32 size){ void _List_increaseCapacity(List_* self, u32 len_to_add){
void* empty_cell_ptr = List_expand_size(ptr, size); assert(self->elem_t_size != 0);
memcpy(empty_cell_ptr, values, size);
u32 expanded_len = self->len + len_to_add;
if(self->capacity > expanded_len)
return;
u32 expanded_capacity = self->capacity;
if(expanded_capacity == 0)
expanded_capacity = 32;
while(expanded_capacity < expanded_len){
expanded_capacity *= 2;
}
// if self->data is null, realloc acts like malloc
self->data = realloc(self->data, expanded_capacity * self->elem_t_size);
self->capacity = expanded_capacity;
} }
bool List_removeAt_size(List_* ptr, u32 i, u32 remove_size){ void* _List_expand(List_* self, u32 len_to_add){
if(i + remove_size >= ptr->size) assert(self->elem_t_size != 0);
_List_increaseCapacity(self, len_to_add);
u8* empty_cell_ptr = (u8*)self->data + self->len * self->elem_t_size;
self->len += len_to_add;
return empty_cell_ptr;
}
void _List_push(List_* self, void* values, u32 len){
assert(self->elem_t_size != 0);
void* empty_cell_ptr = _List_expand(self, len);
memcpy(empty_cell_ptr, values, len * self->elem_t_size);
}
bool _List_tryRemoveAt(List_* self, u32 i, u32 remove_len){
assert(self->elem_t_size != 0);
if(i >= self->len)
return false; return false;
ptr->size -= remove_size; if(remove_len + i > self->len)
u8* src = (u8*)ptr->data + i + remove_size; remove_len = self->len - i;
u8* dst = (u8*)ptr->data + i;
memmove(dst, src, ptr->size - i - remove_size); u32 move_back_n_elements = self->len - i - remove_len;
if(move_back_n_elements > 0){
u32 move_back_size = move_back_n_elements * self->elem_t_size;
u8* dst = (u8*)self->data + i * self->elem_t_size;
u8* src = (u8*)self->data + (i + remove_len) * self->elem_t_size;
memmove(dst, src, move_back_size);
}
self->len -= remove_len;
return true; return true;
} }

View File

@@ -3,30 +3,35 @@
#define ERRMSG_LENGTH 1024 #define ERRMSG_LENGTH 1024
Error* Error_create(const char* msg, bool is_msg_on_heap, ErrorCallPos p){ Error* Error_create(str msg, bool is_msg_on_heap, ErrorCallPos p,
u16 error_code_page, u32 error_code)
{
Error* e = (Error*)malloc(sizeof(Error)); Error* e = (Error*)malloc(sizeof(Error));
e->msg = str_construct((char*)(void*)msg, strlen(msg), true); e->msg = msg;
e->is_msg_on_heap = is_msg_on_heap; e->is_msg_on_heap = is_msg_on_heap;
e->call_stack = List_alloc(ErrorCallPos, 16); e->error_code_page = error_code_page;
List_push(&e->call_stack, ErrorCallPos, p); e->error_code = error_code;
e->call_stack = List_ErrorCallPos_alloc(16);
Error_addCallPos(e, p);
return e; return e;
} }
void Error_destroy(Error* e){ void Error_free(Error* e){
if(e->is_msg_on_heap) if(e->is_msg_on_heap)
free(e->msg.data); free(e->msg.data);
free(e->call_stack.data); free(e->call_stack.data);
free(e);
} }
void Error_addCallPos(Error* e, ErrorCallPos p){ void Error_addCallPos(Error* e, ErrorCallPos p){
List_push(&e->call_stack, ErrorCallPos, p); List_ErrorCallPos_push(&e->call_stack, p);
} }
str Error_toStr(Error* e){ str Error_toStr(Error* e){
u32 len = List_len(&e->call_stack, ErrorCallPos); u32 len = e->call_stack.len;
StringBuilder b = StringBuilder_alloc(e->msg.size + 80 * len); StringBuilder b = StringBuilder_alloc(e->msg.len + 80 * len);
StringBuilder_append_str(&b, STR("Error: ")); StringBuilder_append_str(&b, STR("Catched Error: "));
StringBuilder_append_str(&b, e->msg); StringBuilder_append_str(&b, e->msg);
for(u32 i = 0; i < len; i++){ for(u32 i = 0; i < len; i++){
ErrorCallPos* ep = (ErrorCallPos*)e->call_stack.data + i; ErrorCallPos* ep = (ErrorCallPos*)e->call_stack.data + i;
@@ -45,9 +50,17 @@ str Error_toStr(Error* e){
void Error_printAndExit(Error* e){ void Error_printAndExit(Error* e){
str e_str = Error_toStr(e); str e_str = Error_toStr(e);
printfe("%s\n", e_str.data); printfe("\n"FMT_str"\n", e_str.len, e_str.data);
free(e_str.data); free(e_str.data);
Error_destroy(e); Error_free(e);
free(e);
exit(111); exit(111);
} }
ErrorCodePage_define(TLIBC);
ErrorCodePage_define(LIBC_ERRNO);
static u16 _error_code_page_last = 0;
void _ErrorCodePage_register(u16* error_code_page_ptr){
*error_code_page_ptr = ++_error_code_page_last;
}

View File

@@ -1,7 +1,9 @@
#include "tlibc/filesystem.h"
#include "internal.h" #include "internal.h"
bool dir_exists(cstr path){ bool dir_exists(cstr path){
if(path == NULL || path[0] == 0)
return true;
if(path[0]=='.'){ if(path[0]=='.'){
if(path[1]==0 || (path[1]==path_sep && path[1]==0)) if(path[1]==0 || (path[1]==path_sep && path[1]==0))
return true; // dir . or ./ always exists return true; // dir . or ./ always exists
@@ -24,13 +26,12 @@ bool dir_exists(cstr path){
Result(bool) dir_create(cstr path){ Result(bool) dir_create(cstr path){
Deferral(4); Deferral(4);
if (dir_exists(path)){
if (path == NULL || path[0] == 0 || dir_exists(path)){
Return RESULT_VALUE(i, false); Return RESULT_VALUE(i, false);
} }
char* parentDir= str_copy(path_dirname(str_from_cstr((void*)path))).data; try_void(dir_createParent(path));
Defer(free(parentDir));
try_void(dir_create(parentDir));
#if TLIBC_FS_USE_WINDOWS_H #if TLIBC_FS_USE_WINDOWS_H
if(!CreateDirectory(path, NULL)) if(!CreateDirectory(path, NULL))
@@ -38,8 +39,28 @@ Result(bool) dir_create(cstr path){
if(mkdir(path, 0777) == -1) if(mkdir(path, 0777) == -1)
#endif #endif
{ {
Return RESULT_ERROR_FMT("Can't create dicectory '%s': %s", path, strerror(errno)); char* errno_s = strerror_malloc(errno);
ResultVar(void) err = RESULT_ERROR_FMT(
"Can't create dicectory '%s': %s",
path, errno_s);
free(errno_s);
Return err;
} }
Return RESULT_VALUE(i, true); Return RESULT_VALUE(i, true);
} }
Result(bool) dir_createParent(cstr path){
Deferral(4);
str parent_dir_str = path_dirname(str_from_cstr((void*)path));
if(parent_dir_str.len == 0){
Return RESULT_VALUE(i, false);
}
char* parent_dir_cstr = str_copy(parent_dir_str).data;
Defer(free(parent_dir_cstr));
try(bool result, i, dir_create(parent_dir_cstr));
Return RESULT_VALUE(i, result);
}

View File

@@ -1,4 +1,3 @@
#include "tlibc/filesystem.h"
#include "internal.h" #include "internal.h"
bool file_exists(cstr path){ bool file_exists(cstr path){
@@ -26,27 +25,37 @@ bool file_exists(cstr path){
Result(FILE*) file_open(cstr file_name, cstr fopen_mode){ Result(FILE*) file_open(cstr file_name, cstr fopen_mode){
FILE* f = fopen(file_name, fopen_mode); FILE* f = fopen(file_name, fopen_mode);
if(f == NULL){ if(f == NULL){
return RESULT_ERROR_FMT( char* errno_s = strerror_malloc(errno);
ResultVar(void) err = RESULT_ERROR_FMT(
"Can't open (%s) file '%s': %s", "Can't open (%s) file '%s': %s",
fopen_mode, file_name, strerror(errno)); fopen_mode, file_name, errno_s);
free(errno_s);
return err;
} }
return RESULT_VALUE(p, f); return RESULT_VALUE(p, f);
} }
Result(FILE*) file_openOrCreateReadWrite(cstr file_name){ Result(FILE*) file_openOrCreateReadWrite(cstr file_name){
FILE* f = fopen(file_name, "rb+"); cstr fopen_mode = "rb+";
FILE* f = fopen(file_name, fopen_mode);
if(f == NULL){ if(f == NULL){
f = fopen(file_name, "wb+"); fopen_mode = "wb+";
f = fopen(file_name, fopen_mode);
if(f == NULL){ if(f == NULL){
return RESULT_ERROR_FMT( char* errno_s = strerror_malloc(errno);
"Can't create (%s) file: %s", ResultVar(void) err = RESULT_ERROR_FMT(
file_name, strerror(errno)); "Can't open (%s) file '%s': %s",
fopen_mode, file_name, errno_s);
free(errno_s);
return err;
} }
} }
return RESULT_VALUE(p, f); return RESULT_VALUE(p, f);
} }
Result(i64) file_getSize(FILE* f){
Result(i64) file_tellPos(FILE* f){
i64 r = IFWIN(_ftelli64, ftello64)(f); i64 r = IFWIN(_ftelli64, ftello64)(f);
if(r < 0){ if(r < 0){
return RESULT_ERROR_ERRNO(); return RESULT_ERROR_ERRNO();
@@ -56,13 +65,28 @@ Result(i64) file_getSize(FILE* f){
Result(void) file_seek(FILE* f, i64 offset, SeekOrigin origin){ Result(void) file_seek(FILE* f, i64 offset, SeekOrigin origin){
if(IFWIN(_fseeki64, fseeko64)(f, offset, (int)origin) != 0){ if(IFWIN(_fseeki64, fseeko64)(f, offset, (int)origin) != 0){
return RESULT_ERROR_FMT( char* errno_s = strerror_malloc(errno);
"Can't seek (offset: " IFWIN("%lli", "%li") ", origin: %i) in file: %s", ResultVar(void) err = RESULT_ERROR_FMT(
offset, origin, strerror(errno)); "Can't seek (offset: "FMT_i64", origin: %i) in file: %s",
offset, origin, errno_s);
free(errno_s);
return err;
} }
return RESULT_VOID; return RESULT_VOID;
} }
Result(i64) file_getSize(FILE* f){
Deferral(4);
// get current position
try(i64 original_pos, i, file_tellPos(f));
// seek to end
try_void(file_seek(f, 0, SeekOrigin_End));
try(i64 size, i, file_tellPos(f));
// restore original position
try_void(file_seek(f, original_pos, SeekOrigin_Start));
Return RESULT_VALUE(i, size);
}
Result(void) file_writeStructs(FILE* f, const void* src, u64 struct_size, u64 count){ Result(void) file_writeStructs(FILE* f, const void* src, u64 struct_size, u64 count){
u64 r = fwrite(src, struct_size, count, f); u64 r = fwrite(src, struct_size, count, f);
@@ -106,8 +130,40 @@ Result(void) file_readStructsExactly(FILE* f, void* dst, u64 struct_size, u64 ex
} }
if(r != exact_count){ if(r != exact_count){
return RESULT_ERROR_FMT( return RESULT_ERROR_FMT(
"read " IFWIN("%llu", "%lu") " structures out of " IFWIN("%llu", "%lu"), "read "FMT_u64" structures out of "FMT_u64,
r, exact_count); r, exact_count);
} }
return RESULT_VOID; return RESULT_VOID;
} }
Result(void) file_readWhole(FILE* f, Array(u8)* out_buf){
Deferral(1);
bool success = false;
try(i64 f_size, i, file_getSize(f));
Array(u8) buf = Array_u8_alloc(f_size);
Defer(if(!success) Array_u8_destroy(&buf));
try_void(file_readBytesArrayExactly(f, buf));
*out_buf = buf;
success = true;
Return RESULT_VOID;
}
Result(void) file_readWholeText(FILE* f, str* out_str){
Deferral(1);
bool success = false;
try(i64 f_size, i, file_getSize(f));
Array(u8) buf = Array_u8_alloc(f_size + 1);
Defer(if(!success) Array_u8_destroy(&buf));
buf.len--;
try_void(file_readBytesArrayExactly(f, buf));
buf.data[buf.len] = '\0';
*out_str = Array_u8_castTo_str(buf, true);
success = true;
Return RESULT_VOID;
}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#define _LARGEFILE64_SOURCE 1
#include "tlibc/filesystem.h" #include "tlibc/filesystem.h"
#if TLIBC_FS_USE_WINDOWS_H #if TLIBC_FS_USE_WINDOWS_H
#include <windows.h> #include <windows.h>

View File

@@ -1,36 +1,42 @@
#include "tlibc/filesystem.h" #include "internal.h"
#if TLIBC_FS_USE_WINDOWS_H
#include <knownfolders.h>
#include <shlobj.h>
#else
#endif
str path_dirname(str path){ str path_dirname(str path){
if(path.size == 0) if(path.len == 0)
return path; return path;
// remove trailing slash (name/) // remove trailing slash (name/)
if(path.data[path.size - 1] == path_sep){ if(path.data[path.len - 1] == path_sep){
path.size -= 1; path.len -= 1;
} }
i32 sepIndex = str_seekCharReverse(path, path_sep, -1); i32 sepIndex = str_seekCharReverse(path, path_sep, -1);
if(sepIndex < 0) if(sepIndex < 0)
return STR("."); return STR(".");
path.size = sepIndex; path.len = sepIndex;
path.isZeroTerminated = false; path.isZeroTerminated = false;
return path; return path;
} }
str path_basename(str path, bool remove_ext){ str path_basename(str path, bool remove_ext){
if(path.size == 0) if(path.len == 0)
return path; return path;
// remove trailing slash (name/) // remove trailing slash (name/)
if(path.data[path.size - 1] == path_sep){ if(path.data[path.len - 1] == path_sep){
path.size -= 1; path.len -= 1;
} }
i32 nameIndex = str_seekCharReverse(path, path_sep, -1) + 1; i32 nameIndex = str_seekCharReverse(path, path_sep, -1) + 1;
if((u32)nameIndex != 0){ if((u32)nameIndex != 0){
path.data += nameIndex; path.data += nameIndex;
path.size -= nameIndex; path.len -= nameIndex;
} }
if(!remove_ext) if(!remove_ext)
@@ -38,8 +44,28 @@ str path_basename(str path, bool remove_ext){
i32 extIndex = str_seekCharReverse(path, '.', -1); i32 extIndex = str_seekCharReverse(path, '.', -1);
if(extIndex > 0){ if(extIndex > 0){
path.size = extIndex; path.len = extIndex;
path.isZeroTerminated = false; path.isZeroTerminated = false;
} }
return path; return path;
} }
Result(char*) path_getUserDir(){
#if TLIBC_FS_USE_WINDOWS_H
PWSTR wpath = NULL;
HRESULT reslut = SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &wpath);
if(!SUCCEEDED(reslut)){
return RESULT_ERROR_LITERAL("can't get user directory by SHGetKnownFolderPath()");
}
size_t char_len = wcslen(wpath) * 4 + 1;
char* path = (char*)malloc(char_len);
wcstombs(path, wpath, char_len);
return RESULT_VALUE(p, path);
#else
const char *home = getenv("HOME");
if(home == NULL){
return RESULT_ERROR_LITERAL("can't get user directory by getenv(\"HOME\")");
}
return RESULT_VALUE(p, cstr_copy(home));
#endif
}

View File

@@ -1,36 +1,47 @@
#include "tlibc/string/StringBuilder.h" #include "tlibc/string/StringBuilder.h"
void StringBuilder_destroy(StringBuilder* b){ void StringBuilder_destroy(StringBuilder* b){
if(!b)
return;
free(b->buffer.data); free(b->buffer.data);
b->buffer = List_construct_size(NULL, 0, 0);
} }
str StringBuilder_getStr(StringBuilder* b){ str StringBuilder_getStr(StringBuilder* b){
List_push(&b->buffer, u8, '\0'); if(b->buffer.len == 0 || b->buffer.data[b->buffer.len - 1] != '\0'){
str result = str_construct((char*)b->buffer.data, b->buffer.size - 1, true); List_char_push(&b->buffer, '\0');
// '\0' at the end doesn't increase buffer.len
b->buffer.len -= 1;
}
str result = str_construct(b->buffer.data, b->buffer.len, true);
return result; return result;
} }
bool StringBuilder_equals(const StringBuilder* a, const StringBuilder* b){
str a_str = str_construct(a->buffer.data, a->buffer.len, false);
str b_str = str_construct(b->buffer.data, b->buffer.len, false);
return str_equals(a_str, b_str);
}
void StringBuilder_removeFromEnd(StringBuilder* b, u32 count){ void StringBuilder_removeFromEnd(StringBuilder* b, u32 count){
if(count < b->buffer.size){ if(count < b->buffer.len){
b->buffer.size -= count; b->buffer.len -= count;
} }
else{ else{
b->buffer.size = 0; b->buffer.len = 0;
} }
} }
void StringBuilder_append_char(StringBuilder* b, char c){ void StringBuilder_append_char(StringBuilder* b, char c){
List_push(&b->buffer, u8, c); List_char_push(&b->buffer, c);
} }
void StringBuilder_append_str(StringBuilder* b, str s){ void StringBuilder_append_str(StringBuilder* b, str s){
if(s.data == NULL) if(s.data == NULL)
return; return;
List_push_size(&b->buffer, s.data, s.size); List_char_pushMany(&b->buffer, s.data, s.len);
} }
void StringBuilder_append_cstr(StringBuilder* b, cstr s){ void StringBuilder_append_cstr(StringBuilder* b, cstr s){
@@ -39,19 +50,25 @@ void StringBuilder_append_cstr(StringBuilder* b, cstr s){
void StringBuilder_append_i64(StringBuilder* b, i64 n){ void StringBuilder_append_i64(StringBuilder* b, i64 n){
char buf[32]; char buf[32];
sprintf(buf, IFWIN("%lli", "%li"), n); sprintf(buf, FMT_i64, n);
StringBuilder_append_cstr(b, buf); StringBuilder_append_cstr(b, buf);
} }
void StringBuilder_append_u64(StringBuilder* b, u64 n){ void StringBuilder_append_u64(StringBuilder* b, u64 n){
char buf[32]; char buf[32];
sprintf(buf, IFWIN("%llu", "%lu"), n); sprintf(buf, FMT_u64, n);
StringBuilder_append_cstr(b, buf);
}
void StringBuilder_append_f32(StringBuilder* b, f32 n){
char buf[32];
sprintf(buf, FMT_f32, n);
StringBuilder_append_cstr(b, buf); StringBuilder_append_cstr(b, buf);
} }
void StringBuilder_append_f64(StringBuilder* b, f64 n){ void StringBuilder_append_f64(StringBuilder* b, f64 n){
char buf[32]; char buf[32];
sprintf(buf, "%lf", n); sprintf(buf, FMT_f64, n);
StringBuilder_append_cstr(b, buf); StringBuilder_append_cstr(b, buf);
} }
@@ -60,7 +77,7 @@ void StringBuilder_append_memory(StringBuilder* b, Array(u8) mem, bool uppercase
return; return;
char buf[8]; char buf[8];
for (u32 i=0; i < mem.size; i++) { for (u32 i=0; i < mem.len; i++) {
sprintf(buf, uppercase ? "%02X" : "%02x", ((u8*)mem.data)[i]); sprintf(buf, uppercase ? "%02X" : "%02x", ((u8*)mem.data)[i]);
StringBuilder_append_str(b, str_construct(buf, 2, true)); StringBuilder_append_str(b, str_construct(buf, 2, true));
} }

View File

@@ -1,21 +1,37 @@
// Enable POSIX 2004 definitions.
// Required to use strerror_r in ISO C.
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#include "tlibc/string/cstr.h" #include "tlibc/string/cstr.h"
char* _strcat_malloc(size_t n, cstr str0, ...){ char* cstr_copy(cstr self){
va_list argv; if(self == NULL)
va_start(argv, str0); return NULL;
char* heap_ptr = _vstrcat_malloc(n, str0, argv);
va_end(argv); u64 len_with_zero = strlen(self) + 1;
char* copy = (char*)malloc(len_with_zero);
memcpy(copy, self, len_with_zero);
return copy;
}
char* _strcat_malloc(u64 n, cstr str0, ...){
va_list args;
va_start(args, str0);
char* heap_ptr = _vstrcat_malloc(n, str0, args);
va_end(args);
return heap_ptr; return heap_ptr;
} }
char* _vstrcat_malloc(size_t n, cstr str0, va_list argv){ char* _vstrcat_malloc(u64 n, cstr str0, va_list args){
size_t str0_len = strlen(str0); u64 str0_len = strlen(str0);
size_t total_len = str0_len; u64 total_len = str0_len;
cstr* const parts = malloc(sizeof(cstr) * n); cstr* const parts = malloc(sizeof(cstr) * n);
size_t* const part_lengths = malloc(sizeof(size_t) * n); u64* const part_lengths = malloc(sizeof(u64) * n);
for(size_t i = 0; i < n; i++){ for(u64 i = 0; i < n; i++){
cstr part = va_arg(argv, cstr); cstr part = va_arg(args, cstr);
size_t length = strlen(part); u64 length = strlen(part);
parts[i] = part; parts[i] = part;
part_lengths[i] = length; part_lengths[i] = length;
total_len += length; total_len += length;
@@ -23,7 +39,7 @@ char* _vstrcat_malloc(size_t n, cstr str0, va_list argv){
char* const buf = malloc(total_len + 1); char* const buf = malloc(total_len + 1);
memcpy(buf, str0, str0_len); memcpy(buf, str0, str0_len);
char* walking_ptr = buf + str0_len; char* walking_ptr = buf + str0_len;
for(size_t i = 0; i < n; i++){ for(u64 i = 0; i < n; i++){
memcpy(walking_ptr, parts[i], part_lengths[i]); memcpy(walking_ptr, parts[i], part_lengths[i]);
walking_ptr += part_lengths[i]; walking_ptr += part_lengths[i];
} }
@@ -33,20 +49,42 @@ char* _vstrcat_malloc(size_t n, cstr str0, va_list argv){
return buf; return buf;
} }
char* NULLABLE(sprintf_malloc)(size_t buffer_size, cstr format, ...){ char* NULLABLE(sprintf_malloc)(cstr format, ...){
va_list argv; va_list args;
va_start(argv, format); va_start(args, format);
char* NULLABLE(heap_ptr) = vsprintf_malloc(buffer_size, format, argv); char* heap_ptr = vsprintf_malloc(format, args);
va_end(argv); va_end(args);
return heap_ptr; return heap_ptr;
} }
char* NULLABLE(vsprintf_malloc)(size_t buffer_size, cstr format, va_list argv){ char* vsprintf_malloc(cstr format, va_list args){
char* buf = malloc(buffer_size); // thanks to libtoml developer
int r = vsprintf(buf, format, argv); va_list args_copy;
va_copy(args_copy, args);
i32 buf_size = vsnprintf(NULL, 0, format, args_copy);
va_end(args_copy);
if(buf_size < 63)
buf_size = 63;
buf_size += 1; // for '\0'
char* buf = malloc(buf_size);
int r = vsnprintf(buf, buf_size, format, args);
if(r < 0){ if(r < 0){
free(buf); char err_msg[] = "<ERROR vsprintf_malloc (tlibc/src/cstr.c:55)>";
return NULL; u32 err_msg_size = sizeof(err_msg);
memcpy(buf, err_msg, err_msg_size);
} }
return buf; return buf;
} }
#if defined(_WIN64) || defined(_WIN32)
#define portable_strerror_s(BUF, BUFSIZE, ERRCODE) strerror_s(BUF, BUFSIZE, ERRCODE)
#else
#define portable_strerror_s(BUF, BUFSIZE, ERRCODE) strerror_r(ERRCODE, BUF, BUFSIZE)
#endif
char* strerror_malloc(int errcode){
const int buf_size = 256;
char* buf = malloc(buf_size);
portable_strerror_s(buf, buf_size, errcode);
return buf;
}

View File

@@ -1,44 +1,47 @@
#include "tlibc/string/str.h" #include "tlibc/string/str.h"
#include "tlibc/string/StringBuilder.h" #include "tlibc/string/StringBuilder.h"
str str_copy(str src){ str str_copy(const str self){
if(src.data == NULL || src.size == 0) str copy = str_construct((char*)malloc(self.len + 1), self.len, true);
return src; if(self.len != 0){
memcpy(copy.data, self.data, self.len);
str nstr = str_construct((char*)malloc(src.size + 1), src.size, true); }
memcpy(nstr.data, src.data, src.size); copy.data[copy.len] = '\0';
nstr.data[nstr.size] = '\0'; return copy;
return nstr;
} }
bool str_equals(str s0, str s1){ bool str_equals(const str self, const str other){
if(s0.size != s1.size) if(self.len != other.len)
return false; return false;
if(self.data == other.data)
for(u32 i = 0; i < s0.size; i++) return true;
if(s0.data[i] != s1.data[i]) /*
return false; BENCHMARK:
str_equals64: 2.967s
return true; strcmp: 4.143s
strncmp: 1.611s
memcmp: 0.710s
*/
return memcmp(self.data, other.data, self.len) == 0;
} }
str str_reverse(str s){ str str_reverse(str s){
if(s.data == NULL || s.size == 0) if(s.data == NULL || s.len == 0)
return s; return s;
str r = str_construct(malloc(s.size), s.size, s.isZeroTerminated); str r = str_construct(malloc(s.len), s.len, s.isZeroTerminated);
for(u32 i = 0; i < s.size; i++ ) for(u32 i = 0; i < s.len; i++ )
r.data[i] = s.data[s.size - i - 1]; r.data[i] = s.data[s.len - i - 1];
return r; return r;
} }
i32 str_seek(str src, str fragment, u32 startIndex){ i32 str_seek(const str src, const str fragment, u32 startIndex){
if(src.size == 0 || fragment.size == 0) if(src.len == 0 || fragment.len == 0)
return -1; return -1;
for(u32 i = startIndex; i < src.size - fragment.size + 1; i++){ for(u32 i = startIndex; i < src.len - fragment.len + 1; i++){
for(u32 j = 0;; j++){ for(u32 j = 0;; j++){
if(j == fragment.size) if(j == fragment.len)
return i; return i;
if(src.data[i + j] != fragment.data[j]) if(src.data[i + j] != fragment.data[j])
break; break;
@@ -47,34 +50,34 @@ i32 str_seek(str src, str fragment, u32 startIndex){
return -1; return -1;
} }
i32 str_seekReverse(str src, str fragment, u32 startIndex){ i32 str_seekReverse(const str src, const str fragment, u32 startIndex){
if(src.size == 0 || fragment.size == 0) if(src.len == 0 || fragment.len == 0)
return -1; return -1;
if(startIndex > src.size - 1) if(startIndex > src.len - 1)
startIndex = src.size - 1; startIndex = src.len - 1;
for(u32 i = startIndex; i >= fragment.size - 1; i--){ for(u32 i = startIndex; i >= fragment.len - 1; i--){
for(u32 j = 0;; j++){ for(u32 j = 0;; j++){
if(j == fragment.size) if(j == fragment.len)
return i - j + 1; return i - j + 1;
if(src.data[i - j] != fragment.data[fragment.size - 1 - j]) if(src.data[i - j] != fragment.data[fragment.len - 1 - j])
break; break;
} }
} }
return -1; return -1;
} }
i32 str_seekChar(str src, char c, u32 startIndex){ i32 str_seekChar(const str src, char c, u32 startIndex){
for(u32 i = startIndex; i < src.size; i++){ for(u32 i = startIndex; i < src.len; i++){
if(src.data[i] == c) if(src.data[i] == c)
return i; return i;
} }
return -1; return -1;
} }
i32 str_seekCharReverse(str src, char c, u32 startIndex){ i32 str_seekCharReverse(const str src, char c, u32 startIndex){
if(startIndex > src.size - 1) if(startIndex > src.len - 1)
startIndex = src.size - 1; startIndex = src.len - 1;
for(u32 i = startIndex; i != (u32)-1; i--){ for(u32 i = startIndex; i != (u32)-1; i--){
if(src.data[i] == c) if(src.data[i] == c)
return i; return i;
@@ -82,51 +85,93 @@ i32 str_seekCharReverse(str src, char c, u32 startIndex){
return -1; return -1;
} }
bool str_startsWith(str src, str fragment){ bool str_startsWith(const str src, const str fragment){
if(src.size < fragment.size) if(src.len < fragment.len)
return false; return false;
src.size = fragment.size; str src_fragment = str_null;
return str_equals(src, fragment); src_fragment.data = src.data;
src_fragment.len = fragment.len;
return str_equals(src_fragment, fragment);
} }
bool str_endsWith(str src, str fragment){ bool str_endsWith(const str src, const str fragment){
if(src.size < fragment.size) if(src.len < fragment.len)
return false; return false;
src.data = (char*)(src.data + src.size - fragment.size); str src_fragment = str_null;
src.size = fragment.size; src_fragment.data = (char*)(src.data + src.len - fragment.len);
return str_equals(src, fragment); src_fragment.len = fragment.len;
return str_equals(src_fragment, fragment);
} }
u32 str_hash32(str s){ u32 str_hash32(const str s){
u8* ubuf = (u8*)s.data; u8* ubuf = (u8*)s.data;
u32 hash=0; u32 hash=0;
for (u32 i = 0; i < s.size; i++) for (u32 i = 0; i < s.len; i++)
hash = (hash<<6) + (hash<<16) - hash + ubuf[i]; hash = (hash<<6) + (hash<<16) - hash + ubuf[i];
return hash; return hash;
} }
str str_toUpper(str src){ str str_toUpper(const str src){
str r = str_copy(src); str r = str_copy(src);
for (u32 i = 0; i < r.size; i++){ for (u32 i = 0; i < r.len; i++){
if(isAlphabeticalLower(r.data[i])) if(char_isLatinLower(r.data[i]))
r.data[i] = r.data[i] - 'a' + 'A'; r.data[i] = r.data[i] - 'a' + 'A';
} }
return r; return r;
} }
str str_toLower(str src){ str str_toLower(const str src){
str r = str_copy(src); str r = str_copy(src);
for (u32 i = 0; i < r.size; i++){ for (u32 i = 0; i < r.len; i++){
if(isAlphabeticalUpper(r.data[i])) if(char_isLatinUpper(r.data[i]))
r.data[i] = r.data[i] - 'A' + 'a'; r.data[i] = r.data[i] - 'A' + 'a';
} }
return r; return r;
} }
str hex_to_str(Array(u8) buf, bool uppercase){ str hex_to_str(Array(u8) buf, bool uppercase){
StringBuilder sb = StringBuilder_alloc(buf.size * 2 + 1); StringBuilder sb = StringBuilder_alloc(buf.len * 2 + 1);
StringBuilder_append_memory(&sb, buf, uppercase); StringBuilder_append_memory(&sb, buf, uppercase);
return StringBuilder_getStr(&sb); return StringBuilder_getStr(&sb);
} }
void str_trim(str* line, bool set_zero_at_end){
// loop forward
bool stop = false;
while(line->len > 0 && !stop){
char first_char = line->data[0];
switch(first_char){
case '\0': case '\r': case '\n':
case '\t': case ' ':
line->data++;
line->len--;
break;
default:
stop = true;
break;
}
}
// loop backward
stop = false;
while(line->len > 0 && !stop)
{
char last_char = line->data[line->len - 1];
switch(last_char){
case '\0': case '\r': case '\n':
case '\t': case ' ':
line->len--;
break;
default:
stop = true;
break;
}
}
if(set_zero_at_end){
line->data[line->len] = '\0';
line->isZeroTerminated = true;
}
}

189
src/term.c Normal file
View File

@@ -0,0 +1,189 @@
#include "tlibc/term.h"
#if defined(_WIN64) || defined(_WIN32)
#include <windows.h>
#define try_win_bool(EXPR) if(!(EXPR)) { Return RESULT_ERROR_FMT(#EXPR " failed with WindowsError %lu", GetLastError()); }
#else
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#define try_zero_errno(EXPR) if((EXPR) != 0) { \
char* errno_s = strerror_malloc(errno); \
ResultVar(void) err = RESULT_ERROR_FMT(#EXPR " failed with errno: %s", strerror(errno)); \
free(errno_s); \
Return err; \
}
#endif
Result(void) term_init(){
Deferral(8);
#if defined(_WIN64) || defined(_WIN32)
DWORD mode = 0;
HANDLE console_handle;
// configure stdout
console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
try_assert(console_handle != INVALID_HANDLE_VALUE);
GetConsoleMode(console_handle, &mode);
// https://learn.microsoft.com/en-us/windows/console/setconsolemode
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
mode |= ENABLE_PROCESSED_OUTPUT;
mode |= DISABLE_NEWLINE_AUTO_RETURN;
SetConsoleMode(console_handle, mode);
// configure stderr
console_handle = GetStdHandle(STD_ERROR_HANDLE);
try_assert(console_handle != INVALID_HANDLE_VALUE);
GetConsoleMode(console_handle, &mode);
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
mode |= ENABLE_PROCESSED_OUTPUT;
mode |= DISABLE_NEWLINE_AUTO_RETURN;
SetConsoleMode(console_handle, mode);
// configure stdin
console_handle = GetStdHandle(STD_INPUT_HANDLE);
try_assert(console_handle != INVALID_HANDLE_VALUE);
GetConsoleMode(console_handle, &mode);
mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode(console_handle, mode);
#endif
Return RESULT_VOID;
}
i64 getenv_int(const char* var_name){
char* s = getenv(var_name);
if(s == NULL)
return -1;
return strtoll(s, NULL, 0);
}
Result(void) term_getSize(TerminalSize* out) {
Deferral(4);
#if defined(_WIN64) || defined(_WIN32)
// helps when STD_OUT is redirected to a file
HANDLE console_handle_stderr = GetStdHandle(STD_ERROR_HANDLE);
try_assert(console_handle_stderr != INVALID_HANDLE_VALUE)
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
try_win_bool(GetConsoleScreenBufferInfo(console_handle_stderr, &consoleInfo));
out->cols = consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1;
out->rows = consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1;
#else
struct winsize ws = {0};
// try to get terminal size from stdin, stdout, stderr
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 ||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 ||
ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == 0 ){
out->cols = ws.ws_col;
out->rows = ws.ws_row;
}
// try to get size from environtent variables
else {
out->cols = getenv_int("COLUMNS");
out->rows = getenv_int("LINES");
}
#endif
try_assert(out->cols > 0);
try_assert(out->rows > 0);
Return RESULT_VOID;
}
Result(void) term_readLine(char* buf, u32 bufsize) {
Deferral(1);
if(fgets(buf, bufsize, stdin) == NULL){
try_stderrcode(ferror(stdin));
}
Return RESULT_VOID;
}
Result(void) term_readLineHidden(char *buf, u32 bufsize) {
Deferral(4);
#if defined(_WIN64) || defined(_WIN32)
HANDLE console_handle_stdin = GetStdHandle(STD_INPUT_HANDLE);
try_assert(console_handle_stdin != INVALID_HANDLE_VALUE);
DWORD old_mode;
GetConsoleMode(console_handle_stdin, &old_mode);
// turn off echo
DWORD new_mode = old_mode & ~(ENABLE_ECHO_INPUT);
SetConsoleMode(console_handle_stdin, new_mode);
// restore echo
Defer(SetConsoleMode(console_handle_stdin, old_mode));
#else
struct termios old_mode, new_mode;
try_zero_errno(tcgetattr(STDIN_FILENO, &old_mode));
new_mode = old_mode;
// turn off echo
new_mode.c_lflag &= ~(ECHO);
try_zero_errno(tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_mode));
// restore echo
Defer(tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_mode));
#endif
// read line
try_void(term_readLine(buf, bufsize));
fputc('\n', stdout);
Return RESULT_VOID;
}
/*
Most of escape sequences can be found there
https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
*/
#define ESC "\x1b"
#define CSI ESC"["
void term_setFgColor16(Color16 c){
printf(CSI"%um", c);
}
void term_setBgColor16(Color16 c){
printf(CSI"%um", c + 10);
}
void term_bold(){
printf(CSI"1m");
}
void term_italic(){
printf(CSI"3m");
}
void term_underline(){
printf(CSI"4m");
}
void term_strikethrough(){
printf(CSI"9m");
}
void term_resetCursor() {
printf(CSI"H");
}
void term_resetColors() {
printf(CSI"0m");
}
void term_clear() {
printf(CSI"0m" CSI"H" CSI"2J");
}
void term_eraseRow(){
printf(CSI"2K\r");
}
void term_cursorMove(u16 row, u16 column) {
printf(CSI"%u;%uH", row, column);
}
void term_cursorHide() {
printf(CSI"?25l");
}
void term_cursorShow() {
printf(CSI"?25h");
}

View File

@@ -1,10 +1,7 @@
// posix version definition to use clock_gettime // Enable POSIX 2004 definitions.
// Required to use clock_gettime, localtime_r, gmtime_r in ISO C.
#ifndef _XOPEN_SOURCE #ifndef _XOPEN_SOURCE
#if __STDC_VERSION__ >= 199901L #define _XOPEN_SOURCE 600
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#endif #endif
#include "tlibc/time.h" #include "tlibc/time.h"
@@ -44,3 +41,47 @@ void sleepUsec(usec_t time){
void sleepMsec(msec_t time){ void sleepMsec(msec_t time){
sleepNsec(time * M); sleepNsec(time * M);
} }
#if defined(_WIN64) || defined(_WIN32)
#define portable_localtime_s(time_t_ptr, tm_ptr) localtime_s(tm_ptr, time_t_ptr)
#define portable_gmtime_s(time_t_ptr, tm_ptr) gmtime_s(tm_ptr, time_t_ptr)
#else
#define portable_localtime_s(time_t_ptr, tm_ptr) localtime_r(time_t_ptr, tm_ptr)
#define portable_gmtime_s(time_t_ptr, tm_ptr) gmtime_r(time_t_ptr, tm_ptr)
#endif
void DateTime_get(DateTime* dt, bool utc_time){
time_t c_time = time(NULL);
struct tm c_tm;
struct timespec c_timespec;
if(utc_time){
portable_gmtime_s(&c_time, &c_tm);
}
else {
portable_localtime_s(&c_time, &c_tm);
}
clock_gettime(CLOCK_REALTIME, &c_timespec);
dt->t.nsec = c_timespec.tv_nsec;
dt->t.sec = c_tm.tm_sec;
dt->t.min = c_tm.tm_min;
dt->t.hour = c_tm.tm_hour;
dt->d.month_day = c_tm.tm_mday;
dt->d.month = c_tm.tm_mon + 1;
dt->d.year = c_tm.tm_year + 1900;
dt->d.week_day = c_tm.tm_wday + 1;
dt->d.year_day = c_tm.tm_yday + 1;
}
Result(void) DateTime_parse(cstr src, DateTime* dt){
zeroStruct(dt);
f64 sec_f = 0;
i32 r = sscanf(src, "%"SCNi16".%"SCNi8".%"SCNi8"-%"SCNi8":%"SCNi8":%lf",
&dt->d.year, &dt->d.month, &dt->d.month_day,
&dt->t.hour, &dt->t.min, &sec_f);
if(r != 6){
return RESULT_ERROR_FMT("attepmted to parse DateTime, got %i fields out of 6", r);
}
dt->t.sec = (i32)sec_f;
dt->t.nsec = (sec_f - (i32)sec_f) * 1e9;
return RESULT_VOID;
}

14
src/tlibc.c Normal file
View File

@@ -0,0 +1,14 @@
#include "tlibc/tlibc.h"
Result(void) tlibc_init(){
Deferral(8);
ErrorCodePage_register(TLIBC);
ErrorCodePage_register(LIBC_ERRNO);
Return RESULT_VOID;
}
void tlibc_deinit(){
}

View File

@@ -1,17 +1,17 @@
#!/usr/bin/env bash #!/usr/bin/env bash
###########################################################
# Copy this file to your cbuild DEPENDENCY_CONFIGS_DIR # # This is a dependency config.
# and enable it (ENABLED_DEPENDENCIES=tlibc). # # You can copy it to another project to add tlibc dependency.
###########################################################
DEP_WORKING_DIR="dependencies/tlibc" DEP_WORKING_DIR="$DEPENDENCIES_DIR/tlibc"
DEP_PRE_BUILD_COMMAND=""
DEP_POST_BUILD_COMMAND=""
if [[ "$TASK" = *_dbg ]]; then if [[ "$TASK" = *_dbg ]]; then
dep_build_target="build_static_lib_dbg" dep_build_target="build_static_lib_dbg"
else else
dep_build_target="build_static_lib" dep_build_target="build_static_lib"
fi fi
DEP_PRE_BUILD_COMMAND=""
DEP_BUILD_COMMAND="cbuild $dep_build_target" DEP_BUILD_COMMAND="cbuild $dep_build_target"
DEP_POST_BUILD_COMMAND=""
DEP_CLEAN_COMMAND="cbuild clean" DEP_CLEAN_COMMAND="cbuild clean"
DEP_DYNAMIC_OUT_FILES="" DEP_DYNAMIC_OUT_FILES=""
DEP_STATIC_OUT_FILES="bin/tlibc.a" DEP_STATIC_OUT_FILES="bin/tlibc.a"