Compare commits
81 Commits
0e80a56892
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 82a6293f21 | |||
| c4fd22542d | |||
| 4cfed24ec3 | |||
| de88e9ff16 | |||
| 26de01c3e7 | |||
| 7dc80c2fdf | |||
| bdbe959e23 | |||
| 94b051e852 | |||
| 08d45faa83 | |||
| af0931c727 | |||
| 0d422cd7e5 | |||
| 17d2d1c38d | |||
| a57f05cfeb | |||
| ea6c20f430 | |||
| 77e4f38416 | |||
| f3e4f8f061 | |||
| 83091ac707 | |||
|
|
6a7f0a8715 | ||
| 8a40caaf10 | |||
| 4c97787c27 | |||
| 82bd234d08 | |||
| b557881168 | |||
| f2ce18b16d | |||
| 3a7f09bb49 | |||
| 0b4574b53e | |||
| 225a48a8d9 | |||
| ec164dc4e9 | |||
| c68e4e87b3 | |||
| bc41577248 | |||
| 89aab2b5bf | |||
| 425794361b | |||
| fc8541ad2d | |||
| ae0fa95d6a | |||
| 5ef223372b | |||
| adaf5cc311 | |||
| af36bab444 | |||
| 1775b27980 | |||
| 77589878c1 | |||
| 3034e4d186 | |||
| d9daae8c5e | |||
| 972f244ae5 | |||
| 6a1067a612 | |||
| 2de044711f | |||
| 958cb269b2 | |||
| 0a1e87817d | |||
| 57c5942fcc | |||
| 1406189511 | |||
| e2f1f6c09b | |||
| bb3b096262 | |||
| 02fb2767a8 | |||
| 4cc226b57a | |||
| cf5fda7a1e | |||
| 2c8e6fc601 | |||
| d6436d0833 | |||
| ee2b88a412 | |||
| dbe8924466 | |||
| 30c141f587 | |||
| 00a1a29d34 | |||
| 5fb2db2380 | |||
| c4cb696721 | |||
| b801719712 | |||
| 75c94e88d9 | |||
| a0affaa6d0 | |||
| f0992c0217 | |||
| 0184d2e8c9 | |||
| 14ffede476 | |||
| b3f67a38de | |||
| 7e7bd195a9 | |||
| c415e2ca8f | |||
| bf56984482 | |||
| 8eeaff4245 | |||
| a097d5aff9 | |||
| d04aac567f | |||
| fe9e44a660 | |||
| 223406d4e4 | |||
| e1dc972b24 | |||
| ab55f85160 | |||
| e0f1941c82 | |||
| 6d959fe8f5 | |||
| 961d00fdb0 | |||
| 4c3933f789 |
36
README.md
Normal file
36
README.md
Normal 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
|
||||||
|
}
|
||||||
|
```
|
||||||
32
include/tlibc/algorithms.h
Normal file
32
include/tlibc/algorithms.h
Normal 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
23
include/tlibc/base64.h
Normal 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);
|
||||||
@@ -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)
|
|
||||||
|
|||||||
2
include/tlibc/collections/Array_impl/Array_char.h
Normal file
2
include/tlibc/collections/Array_impl/Array_char.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(char);
|
||||||
2
include/tlibc/collections/Array_impl/Array_f32.h
Normal file
2
include/tlibc/collections/Array_impl/Array_f32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(f32);
|
||||||
2
include/tlibc/collections/Array_impl/Array_f64.h
Normal file
2
include/tlibc/collections/Array_impl/Array_f64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(f64);
|
||||||
2
include/tlibc/collections/Array_impl/Array_i16.h
Normal file
2
include/tlibc/collections/Array_impl/Array_i16.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(i16);
|
||||||
2
include/tlibc/collections/Array_impl/Array_i32.h
Normal file
2
include/tlibc/collections/Array_impl/Array_i32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(i32);
|
||||||
2
include/tlibc/collections/Array_impl/Array_i64.h
Normal file
2
include/tlibc/collections/Array_impl/Array_i64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(i64);
|
||||||
2
include/tlibc/collections/Array_impl/Array_i8.h
Normal file
2
include/tlibc/collections/Array_impl/Array_i8.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(i8);
|
||||||
2
include/tlibc/collections/Array_impl/Array_u16.h
Normal file
2
include/tlibc/collections/Array_impl/Array_u16.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(u16);
|
||||||
2
include/tlibc/collections/Array_impl/Array_u32.h
Normal file
2
include/tlibc/collections/Array_impl/Array_u32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(u32);
|
||||||
2
include/tlibc/collections/Array_impl/Array_u64.h
Normal file
2
include/tlibc/collections/Array_impl/Array_u64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(u64);
|
||||||
2
include/tlibc/collections/Array_impl/Array_u8.h
Normal file
2
include/tlibc/collections/Array_impl/Array_u8.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
Array_declare(u8);
|
||||||
@@ -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);
|
||||||
|
|||||||
92
include/tlibc/collections/LList.h
Normal file
92
include/tlibc/collections/LList.h
Normal 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);
|
||||||
2
include/tlibc/collections/LList_impl/LList_char.h
Normal file
2
include/tlibc/collections/LList_impl/LList_char.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(char);
|
||||||
2
include/tlibc/collections/LList_impl/LList_f32.h
Normal file
2
include/tlibc/collections/LList_impl/LList_f32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(f32);
|
||||||
2
include/tlibc/collections/LList_impl/LList_f64.h
Normal file
2
include/tlibc/collections/LList_impl/LList_f64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(f64);
|
||||||
2
include/tlibc/collections/LList_impl/LList_i16.h
Normal file
2
include/tlibc/collections/LList_impl/LList_i16.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(i16);
|
||||||
2
include/tlibc/collections/LList_impl/LList_i32.h
Normal file
2
include/tlibc/collections/LList_impl/LList_i32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(i32);
|
||||||
2
include/tlibc/collections/LList_impl/LList_i64.h
Normal file
2
include/tlibc/collections/LList_impl/LList_i64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(i64);
|
||||||
2
include/tlibc/collections/LList_impl/LList_i8.h
Normal file
2
include/tlibc/collections/LList_impl/LList_i8.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(i8);
|
||||||
2
include/tlibc/collections/LList_impl/LList_u16.h
Normal file
2
include/tlibc/collections/LList_impl/LList_u16.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(u16);
|
||||||
2
include/tlibc/collections/LList_impl/LList_u32.h
Normal file
2
include/tlibc/collections/LList_impl/LList_u32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(u32);
|
||||||
2
include/tlibc/collections/LList_impl/LList_u64.h
Normal file
2
include/tlibc/collections/LList_impl/LList_u64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(u64);
|
||||||
2
include/tlibc/collections/LList_impl/LList_u8.h
Normal file
2
include/tlibc/collections/LList_impl/LList_u8.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
LList_declare(u8);
|
||||||
@@ -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))
|
|
||||||
|
|||||||
2
include/tlibc/collections/List_impl/List_char.h
Normal file
2
include/tlibc/collections/List_impl/List_char.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(char);
|
||||||
2
include/tlibc/collections/List_impl/List_f32.h
Normal file
2
include/tlibc/collections/List_impl/List_f32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(f32);
|
||||||
2
include/tlibc/collections/List_impl/List_f64.h
Normal file
2
include/tlibc/collections/List_impl/List_f64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(f64);
|
||||||
2
include/tlibc/collections/List_impl/List_i16.h
Normal file
2
include/tlibc/collections/List_impl/List_i16.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(i16);
|
||||||
2
include/tlibc/collections/List_impl/List_i32.h
Normal file
2
include/tlibc/collections/List_impl/List_i32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(i32);
|
||||||
2
include/tlibc/collections/List_impl/List_i64.h
Normal file
2
include/tlibc/collections/List_impl/List_i64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(i64);
|
||||||
2
include/tlibc/collections/List_impl/List_i8.h
Normal file
2
include/tlibc/collections/List_impl/List_i8.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(i8);
|
||||||
2
include/tlibc/collections/List_impl/List_u16.h
Normal file
2
include/tlibc/collections/List_impl/List_u16.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(u16);
|
||||||
2
include/tlibc/collections/List_impl/List_u32.h
Normal file
2
include/tlibc/collections/List_impl/List_u32.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(u32);
|
||||||
2
include/tlibc/collections/List_impl/List_u64.h
Normal file
2
include/tlibc/collections/List_impl/List_u64.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(u64);
|
||||||
2
include/tlibc/collections/List_impl/List_u8.h
Normal file
2
include/tlibc/collections/List_impl/List_u8.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
List_declare(u8);
|
||||||
82
include/tlibc/defer.h
Normal file
82
include/tlibc/defer.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
Based on https://github.com/moon-chilled/Defer
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
void foo(){
|
||||||
|
Deferral(16);
|
||||||
|
void* p = malloc(8);
|
||||||
|
Defer(free(p));
|
||||||
|
Return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__TINYC__)
|
||||||
|
|
||||||
|
#define Deferral(MAX_DEFER_STATEMENTS) \
|
||||||
|
int _num_deferrals = 0; \
|
||||||
|
ATTRIBUTE_UNUSED void *_defer_return_loc = 0;\
|
||||||
|
ATTRIBUTE_UNUSED void *_deferrals[MAX_DEFER_STATEMENTS] = {0};
|
||||||
|
|
||||||
|
# define Defer(block) _Defer(block, __LINE__)
|
||||||
|
# define Return _Return(__LINE__)
|
||||||
|
|
||||||
|
#ifndef CAT2
|
||||||
|
#define CAT2(a, b) a ## b
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _Defer(block, n) do { \
|
||||||
|
_deferrals[_num_deferrals++] = && CAT2(_defer_ini, n); \
|
||||||
|
if (0) { \
|
||||||
|
CAT2(_defer_ini, n): \
|
||||||
|
block; \
|
||||||
|
if (_num_deferrals) { \
|
||||||
|
goto *_deferrals[--_num_deferrals]; \
|
||||||
|
} else { \
|
||||||
|
goto *_defer_return_loc; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define _Return(n) \
|
||||||
|
if (_num_deferrals) \
|
||||||
|
{ \
|
||||||
|
_defer_return_loc = && CAT2(_defer_fini_, n); \
|
||||||
|
goto *_deferrals[--_num_deferrals]; \
|
||||||
|
} else CAT2(_defer_fini_, n): \
|
||||||
|
return
|
||||||
|
|
||||||
|
#else /* !__GNUC__ && !__TINYCC__ */
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma message("You are using the unsafe longjmp()-based defer implementation. Expect bugs if you don't know what you're doing.")
|
||||||
|
#else
|
||||||
|
# warning You are using the unsafe longjmp()-based defer implementation. Expect bugs if you don't know what you're doing.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Deferral(MAX_DEFER_STATEMENTS) \
|
||||||
|
volatile int _num_deferrals = 0; \
|
||||||
|
jmp_buf _defer_return_loc = {0}, _deferrals[MAX_DEFER_STATEMENTS] = {0};
|
||||||
|
|
||||||
|
#define Defer(block) do { \
|
||||||
|
if (setjmp(_deferrals[_num_deferrals++])) { \
|
||||||
|
block; \
|
||||||
|
if (_num_deferrals) { \
|
||||||
|
longjmp(_deferrals[--_num_deferrals], 1); \
|
||||||
|
} else { \
|
||||||
|
longjmp(_defer_return_loc, 1); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define Return \
|
||||||
|
if (!setjmp(_defer_return_loc)) { \
|
||||||
|
if (_num_deferrals) longjmp(_deferrals[--_num_deferrals], 1); \
|
||||||
|
} else return
|
||||||
|
|
||||||
|
#endif /* __GNUC__ */
|
||||||
@@ -2,29 +2,60 @@
|
|||||||
#include "std.h"
|
#include "std.h"
|
||||||
#include "string/str.h"
|
#include "string/str.h"
|
||||||
#include "collections/List.h"
|
#include "collections/List.h"
|
||||||
|
#include "defer.h"
|
||||||
|
|
||||||
typedef struct ErrorCallPos {
|
typedef struct ErrorCallPos {
|
||||||
i32 line;
|
i32 line;
|
||||||
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;
|
||||||
@@ -33,29 +64,104 @@ typedef struct Result_ {
|
|||||||
i64 i;
|
i64 i;
|
||||||
f32 f;
|
f32 f;
|
||||||
f64 d;
|
f64 d;
|
||||||
void* v_ptr;
|
void* p;
|
||||||
};
|
};
|
||||||
} 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_VOID (Result_){ .error = NULL }
|
.error = Error_create(MSG, IS_MSG_ON_HEAP, ErrorCallPos_here(), \
|
||||||
|
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_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V }
|
#define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V }
|
||||||
|
|
||||||
#define try(VAR, RSLT_CALL, DEFER_CODE) \
|
#define try(VAR, RESULT_FIELD, RSLT_CALL) \
|
||||||
Result_ VAR = RSLT_CALL;\
|
_try(VAR, RESULT_FIELD, RSLT_CALL, __LINE__)
|
||||||
if(VAR.error){\
|
#define try_void(RSLT_CALL) \
|
||||||
Error_addCallPos(VAR.error, ErrorCallPos_here());\
|
_try_void(RSLT_CALL, __LINE__)
|
||||||
DEFER_CODE;\
|
#define try_fatal(VAR, RESULT_FIELD, RSLT_CALL) \
|
||||||
return VAR;\
|
_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 try_fatal(VAR, RSLT_CALL, DEFER_CODE) \
|
|
||||||
Result_ VAR = RSLT_CALL;\
|
#define _rname(N) __r_##N
|
||||||
if(VAR.error){\
|
|
||||||
Error_addCallPos(VAR.error, ErrorCallPos_here());\
|
#define _try_handle(VAR, RESULT_FIELD, RSLT_CALL, HANDLER, N) \
|
||||||
DEFER_CODE;\
|
Result_ _rname(N) = RSLT_CALL;\
|
||||||
Error_printAndExit(VAR.error);\
|
if(_rname(N).error){\
|
||||||
};
|
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) \
|
||||||
|
_try_handle(VAR, RESULT_FIELD, RSLT_CALL, _try__handler, N)
|
||||||
|
#define _try_void(RSLT_CALL, N) \
|
||||||
|
_try_handle_void(RSLT_CALL, _try__handler, N)
|
||||||
|
|
||||||
|
#define _try_fatal__handler(R) Error_printAndExit(R.error)
|
||||||
|
#define _try_fatal(VAR, RESULT_FIELD, RSLT_CALL, N) \
|
||||||
|
_try_handle(VAR, RESULT_FIELD, RSLT_CALL, _try_fatal__handler, N)
|
||||||
|
#define _try_fatal_void(RSLT_CALL, N) \
|
||||||
|
_try_handle_void(RSLT_CALL, _try_fatal__handler, N)
|
||||||
|
|
||||||
|
#define _try_stderrcode(CALL, N) \
|
||||||
|
int _rname(N) = CALL;\
|
||||||
|
if(_rname(N) != 0){\
|
||||||
|
Return RESULT_ERROR_CODE(LIBC_ERRNO, _rname(N), str_from_cstr(strerror_malloc(_rname(N))), true);\
|
||||||
|
}\
|
||||||
|
|
||||||
|
#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); }
|
||||||
|
|||||||
161
include/tlibc/filesystem.h
Normal file
161
include/tlibc/filesystem.h
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "std.h"
|
||||||
|
#include "errors.h"
|
||||||
|
#include "string/str.h"
|
||||||
|
#include "collections/Array.h"
|
||||||
|
#include "collections/Array_impl/Array_u8.h"
|
||||||
|
|
||||||
|
#if !defined(TLIBC_FS_USE_WINDOWS_H)
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#define TLIBC_FS_USE_WINDOWS_H 1
|
||||||
|
#else
|
||||||
|
#define TLIBC_FS_USE_WINDOWS_H 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PATH //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#define path_sep '\\'
|
||||||
|
#define path_seps "\\"
|
||||||
|
#define path_notsep '/'
|
||||||
|
#define path_notseps "/"
|
||||||
|
#else
|
||||||
|
#define path_sep '/'
|
||||||
|
#define path_seps "/"
|
||||||
|
#define path_notsep '\\'
|
||||||
|
#define path_notseps "\\"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief removes part of path after path_sep (including path_sep)
|
||||||
|
/// @return pointer to a segment of path.data or "." if no path_sep has been found
|
||||||
|
str path_dirname(str path);
|
||||||
|
|
||||||
|
/// @brief removes part of path before path_sep (including path_sep)
|
||||||
|
/// @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);
|
||||||
|
|
||||||
|
/// @return heap-allocated string
|
||||||
|
Result(char*) path_getUserDir();
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// FILE //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// open file for reading
|
||||||
|
#define FO_ReadExisting "rb"
|
||||||
|
/// (re)create file for writing
|
||||||
|
#define FO_WriteNew "wb"
|
||||||
|
/// open or create file for writing data to the end
|
||||||
|
#define FO_AppendOrCreate "ab"
|
||||||
|
/// open file for reading and writing
|
||||||
|
#define FO_ReadWriteExisting "rb+"
|
||||||
|
/// (re)create file for reading/writing
|
||||||
|
#define FO_ReadWriteNew "wb+"
|
||||||
|
/// open or create file for readng and writing data to the end
|
||||||
|
#define FO_ReadAppend "ab+"
|
||||||
|
|
||||||
|
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+"
|
||||||
|
Result(FILE*) file_openOrCreateReadWrite(cstr file_name);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum SeekOrigin {
|
||||||
|
SeekOrigin_Start = SEEK_SET,
|
||||||
|
SeekOrigin_Current = SEEK_CUR,
|
||||||
|
SeekOrigin_End = SEEK_END,
|
||||||
|
} SeekOrigin;
|
||||||
|
|
||||||
|
/// @brief changes current position in file
|
||||||
|
Result(void) file_seek(FILE* f, i64 offset, SeekOrigin origin);
|
||||||
|
|
||||||
|
|
||||||
|
Result(void) file_writeByte(FILE* f, u8 b);
|
||||||
|
Result(void) file_writeStructs(FILE* f, const void* src, u64 struct_size, u64 count);
|
||||||
|
|
||||||
|
static inline Result(void) file_writeBytes(FILE* f, const void* src, u64 size){
|
||||||
|
return file_writeStructs(f, src, size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Result(void) file_writeBytesArray(FILE* f, Array(u8) src){
|
||||||
|
return file_writeStructs(f, src.data, src.len, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @param out_byte is not set on error or end of file
|
||||||
|
/// @return true if byte was read, false if end of file was reached
|
||||||
|
Result(bool) file_readByte(FILE* f, u8* out_byte);
|
||||||
|
|
||||||
|
|
||||||
|
/// @param max_count maximum number of structs to read
|
||||||
|
/// @return number of structs that were read (<=max_count)
|
||||||
|
Result(u64) file_readStructs(FILE* f, void* dst, u64 struct_size, u64 max_count);
|
||||||
|
|
||||||
|
/// @param max_count maximum number of bytes to read
|
||||||
|
/// @return number of bytes that were read (<=max_count)
|
||||||
|
static inline Result(u64) file_readBytes(FILE* f, void* dst, u64 max_count){
|
||||||
|
return file_readStructs(f, dst, 1, max_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param dst array where .len is the maximum number of bytes to read
|
||||||
|
/// @return number of bytes that were read (<=max_count)
|
||||||
|
static inline Result(u64) file_readBytesArray(FILE* f, Array(u8) dst){
|
||||||
|
return file_readStructs(f, dst.data, 1, dst.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @param max_count exact number of structs to read
|
||||||
|
Result(void) file_readStructsExactly(FILE* f, void* dst, u64 struct_size, u64 exact_count);
|
||||||
|
|
||||||
|
/// @param exact_count exact number of bytes to read
|
||||||
|
static inline Result(void) file_readBytesExactly(FILE* f, void* dst, u64 exact_count){
|
||||||
|
return file_readStructsExactly(f, dst, 1, exact_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param dst array where .len is the exact number of bytes to read
|
||||||
|
static inline Result(void) file_readBytesArrayExactly(FILE* f, Array(u8) dst){
|
||||||
|
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 //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// @return true if directory exists or `path` is null or empty or '.' or './'
|
||||||
|
bool dir_exists(cstr path);
|
||||||
|
|
||||||
|
/// @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
|
||||||
|
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
12
include/tlibc/magic.h
Normal 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;
|
||||||
@@ -1,27 +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>
|
||||||
|
|
||||||
typedef int8_t i8;
|
typedef int8_t i8;
|
||||||
typedef uint8_t u8;
|
|
||||||
typedef int16_t i16;
|
typedef int16_t i16;
|
||||||
typedef uint16_t u16;
|
|
||||||
typedef int32_t i32;
|
typedef int32_t i32;
|
||||||
typedef uint32_t u32;
|
|
||||||
typedef int64_t i64;
|
typedef int64_t i64;
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
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;
|
||||||
@@ -29,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))
|
||||||
@@ -51,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, \
|
||||||
@@ -60,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
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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'; }
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -4,43 +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){
|
||||||
|
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
47
include/tlibc/term.h
Normal 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();
|
||||||
@@ -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
5
include/tlibc/tlibc.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include "tlibc/errors.h"
|
||||||
|
|
||||||
|
/// call once at the start of main()
|
||||||
|
Result(void) tlibc_init();
|
||||||
|
void tlibc_deinit();
|
||||||
@@ -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"
|
WARN_C="-Wall -Wextra
|
||||||
WARN_CPP="-Wall -Wextra"
|
-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)
|
||||||
|
|||||||
11
project.config.user.default
Normal file
11
project.config.user.default
Normal 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
86
src/base64.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -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
58
src/collections/LList.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/errors.c
37
src/errors.c
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
66
src/filesystem/dir.c
Normal file
66
src/filesystem/dir.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
bool dir_exists(cstr path){
|
||||||
|
if(path == NULL || path[0] == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(path[0]=='.'){
|
||||||
|
if(path[1]==0 || (path[1]==path_sep && path[1]==0))
|
||||||
|
return true; // dir . or ./ always exists
|
||||||
|
// else if(path[1]=='.' && path[2]==path_sep)
|
||||||
|
//TODO path_resolve because windows doesnt recognize .\ pattern
|
||||||
|
}
|
||||||
|
#if TLIBC_FS_USE_WINDOWS_H
|
||||||
|
DWORD dwAttrib = GetFileAttributes(path);
|
||||||
|
return (bool)(
|
||||||
|
(dwAttrib != INVALID_FILE_ATTRIBUTES) && // file exists
|
||||||
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); // file is a directory
|
||||||
|
#else
|
||||||
|
struct stat stats;
|
||||||
|
i32 rez=stat(path, &stats);
|
||||||
|
return (bool)(
|
||||||
|
(rez!=-1) && // file exists
|
||||||
|
(S_ISDIR(stats.st_mode))); // file is a directory
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(bool) dir_create(cstr path){
|
||||||
|
Deferral(4);
|
||||||
|
|
||||||
|
if (path == NULL || path[0] == 0 || dir_exists(path)){
|
||||||
|
Return RESULT_VALUE(i, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try_void(dir_createParent(path));
|
||||||
|
|
||||||
|
#if TLIBC_FS_USE_WINDOWS_H
|
||||||
|
if(!CreateDirectory(path, NULL))
|
||||||
|
#else
|
||||||
|
if(mkdir(path, 0777) == -1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
169
src/filesystem/file.c
Normal file
169
src/filesystem/file.c
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
bool file_exists(cstr path){
|
||||||
|
if(path[0]=='.'){
|
||||||
|
if(path[1]==0 || (path[1]==path_sep && path[2]==0))
|
||||||
|
return false; // . or ./ is not a file
|
||||||
|
// else if(path[1]=='.' && path[2]==path_sep)
|
||||||
|
//TODO path_resolve because windows doesnt recognize .\ pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TLIBC_FS_USE_WINDOWS_H
|
||||||
|
DWORD dwAttrib = GetFileAttributes(path);
|
||||||
|
return (bool)(
|
||||||
|
(dwAttrib != INVALID_FILE_ATTRIBUTES) && // file exists
|
||||||
|
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); // file is not directory
|
||||||
|
#else
|
||||||
|
struct stat stats;
|
||||||
|
i32 rez=stat(path, &stats);
|
||||||
|
return (bool)(
|
||||||
|
(rez!=-1) && // file exists
|
||||||
|
!(S_ISDIR(stats.st_mode))); // file is not directory
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(FILE*) file_open(cstr file_name, cstr fopen_mode){
|
||||||
|
FILE* f = fopen(file_name, fopen_mode);
|
||||||
|
if(f == NULL){
|
||||||
|
char* errno_s = strerror_malloc(errno);
|
||||||
|
ResultVar(void) err = RESULT_ERROR_FMT(
|
||||||
|
"Can't open (%s) file '%s': %s",
|
||||||
|
fopen_mode, file_name, errno_s);
|
||||||
|
free(errno_s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return RESULT_VALUE(p, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(FILE*) file_openOrCreateReadWrite(cstr file_name){
|
||||||
|
cstr fopen_mode = "rb+";
|
||||||
|
FILE* f = fopen(file_name, fopen_mode);
|
||||||
|
if(f == NULL){
|
||||||
|
fopen_mode = "wb+";
|
||||||
|
f = fopen(file_name, fopen_mode);
|
||||||
|
if(f == NULL){
|
||||||
|
char* errno_s = strerror_malloc(errno);
|
||||||
|
ResultVar(void) err = RESULT_ERROR_FMT(
|
||||||
|
"Can't open (%s) file '%s': %s",
|
||||||
|
fopen_mode, file_name, errno_s);
|
||||||
|
free(errno_s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RESULT_VALUE(p, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Result(i64) file_tellPos(FILE* f){
|
||||||
|
i64 r = IFWIN(_ftelli64, ftello64)(f);
|
||||||
|
if(r < 0){
|
||||||
|
return RESULT_ERROR_ERRNO();
|
||||||
|
}
|
||||||
|
return RESULT_VALUE(i, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(void) file_seek(FILE* f, i64 offset, SeekOrigin origin){
|
||||||
|
if(IFWIN(_fseeki64, fseeko64)(f, offset, (int)origin) != 0){
|
||||||
|
char* errno_s = strerror_malloc(errno);
|
||||||
|
ResultVar(void) err = RESULT_ERROR_FMT(
|
||||||
|
"Can't seek (offset: "FMT_i64", origin: %i) in file: %s",
|
||||||
|
offset, origin, errno_s);
|
||||||
|
free(errno_s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
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){
|
||||||
|
u64 r = fwrite(src, struct_size, count, f);
|
||||||
|
if(r != count){
|
||||||
|
return RESULT_ERROR_ERRNO();
|
||||||
|
}
|
||||||
|
return RESULT_VOID;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(void) file_writeByte(FILE* f, u8 b){
|
||||||
|
if(fputc(b, f) != 0){
|
||||||
|
return RESULT_ERROR_ERRNO();
|
||||||
|
}
|
||||||
|
return RESULT_VOID;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(bool) file_readByte(FILE* f, u8* out_byte){
|
||||||
|
int r = fgetc(f);
|
||||||
|
if(ferror(f)){
|
||||||
|
return RESULT_ERROR_ERRNO();
|
||||||
|
}
|
||||||
|
if(feof(f)){
|
||||||
|
return RESULT_VALUE(u, false);
|
||||||
|
}
|
||||||
|
*out_byte = r;
|
||||||
|
return RESULT_VALUE(u, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(u64) file_readStructs(FILE* f, void* dst, u64 struct_size, u64 max_count){
|
||||||
|
u64 r = fread(dst, struct_size, max_count, f);
|
||||||
|
if(ferror(f)){
|
||||||
|
return RESULT_ERROR_ERRNO();
|
||||||
|
}
|
||||||
|
return RESULT_VALUE(u, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(void) file_readStructsExactly(FILE* f, void* dst, u64 struct_size, u64 exact_count){
|
||||||
|
u64 r = fread(dst, struct_size, exact_count, f);
|
||||||
|
if(ferror(f)){
|
||||||
|
return RESULT_ERROR_ERRNO();
|
||||||
|
}
|
||||||
|
if(r != exact_count){
|
||||||
|
return RESULT_ERROR_FMT(
|
||||||
|
"read "FMT_u64" structures out of "FMT_u64,
|
||||||
|
r, exact_count);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
11
src/filesystem/internal.h
Normal file
11
src/filesystem/internal.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define _LARGEFILE64_SOURCE 1
|
||||||
|
#include "tlibc/filesystem.h"
|
||||||
|
#if TLIBC_FS_USE_WINDOWS_H
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
71
src/filesystem/path.c
Normal file
71
src/filesystem/path.c
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
#if TLIBC_FS_USE_WINDOWS_H
|
||||||
|
#include <knownfolders.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
|
||||||
|
str path_dirname(str path){
|
||||||
|
if(path.len == 0)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
// remove trailing slash (name/)
|
||||||
|
if(path.data[path.len - 1] == path_sep){
|
||||||
|
path.len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 sepIndex = str_seekCharReverse(path, path_sep, -1);
|
||||||
|
if(sepIndex < 0)
|
||||||
|
return STR(".");
|
||||||
|
|
||||||
|
path.len = sepIndex;
|
||||||
|
path.isZeroTerminated = false;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
str path_basename(str path, bool remove_ext){
|
||||||
|
if(path.len == 0)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
// remove trailing slash (name/)
|
||||||
|
if(path.data[path.len - 1] == path_sep){
|
||||||
|
path.len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 nameIndex = str_seekCharReverse(path, path_sep, -1) + 1;
|
||||||
|
if((u32)nameIndex != 0){
|
||||||
|
path.data += nameIndex;
|
||||||
|
path.len -= nameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!remove_ext)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
i32 extIndex = str_seekCharReverse(path, '.', -1);
|
||||||
|
if(extIndex > 0){
|
||||||
|
path.len = extIndex;
|
||||||
|
path.isZeroTerminated = false;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
151
src/string/str.c
151
src/string/str.c
@@ -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++)
|
|
||||||
if(s0.data[i] != s1.data[i])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
/*
|
||||||
|
BENCHMARK:
|
||||||
|
str_equals64: 2.967s
|
||||||
|
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
189
src/term.c
Normal 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");
|
||||||
|
}
|
||||||
51
src/time.c
51
src/time.c
@@ -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
14
src/tlibc.c
Normal 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(){
|
||||||
|
|
||||||
|
}
|
||||||
14
tlibc.config
14
tlibc.config
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user