285 lines
No EOL
8.7 KiB
C
285 lines
No EOL
8.7 KiB
C
|
|
/*
|
|
Usage:
|
|
|
|
// We need to use macro const_string to convert literal to string
|
|
string fmt = const_string("Pointer address: 0x%x");
|
|
print(fmt, cast(u64)&a); // Print to stdout
|
|
|
|
// Format a string and allocate with context.allocator
|
|
string a = sprint("Hello, %cs!\n", "balls"); // %cs for char*
|
|
|
|
string balls = const_string("balls");
|
|
// tprint for temporary allocation
|
|
string b = tprint(const_string("Hello, %s!\n"), balls); // %s for string
|
|
|
|
// Allocate a new string of length 12 (with context allocator)
|
|
string c = alloc_string(12);
|
|
dealloc_string(c);
|
|
|
|
// We can use raw char* for format with printf/sprintf/tprintf
|
|
printf("Hello, %!\n", balls);
|
|
|
|
// concatenation
|
|
string concatenated = string_concat(a, b);
|
|
|
|
// Use temporary memory to make a null-terminated copy of fixed-length string
|
|
char* cstring = temp_convert_to_null_terminated_string(balls);
|
|
|
|
// To convert a cstring to string (using same memory)
|
|
string s;
|
|
s.data = (u8*)cstring;
|
|
s.count = strlen(cstring);
|
|
|
|
// String matching
|
|
bool match = strings_match(a, b);
|
|
|
|
// View into "Hello, balls!\n" from index 7 with a count of 5; "balls"
|
|
string balls2 = string_view(a, 7, 5);
|
|
|
|
|
|
*/
|
|
|
|
void * memcpy (void *,const void *,size_t);
|
|
void* talloc(u64);
|
|
|
|
typedef struct string {
|
|
u64 count;
|
|
u8 *data;
|
|
} string;
|
|
|
|
void push_temp_allocator();
|
|
|
|
#define cstr const_string
|
|
#define const_string(s) ((string){ length_of_null_terminated_string(s), (u8*)s })
|
|
|
|
inline u64 length_of_null_terminated_string(const char* cstring) {
|
|
u64 len = 0;
|
|
while (*cstring != 0) {
|
|
len += 1;
|
|
cstring += 1;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
string alloc_string(u64 count) {
|
|
string s;
|
|
s.count = count;
|
|
s.data = cast(u8*)alloc(count);
|
|
return s;
|
|
}
|
|
void dealloc_string(string s) {
|
|
dealloc(s.data);
|
|
}
|
|
|
|
// context.allocator !
|
|
string string_concat(const string left, const string right) {
|
|
string result;
|
|
result.count = left.count + right.count;
|
|
result.data = cast(u8*)alloc(result.count);
|
|
memcpy(result.data, left.data, left.count);
|
|
memcpy(result.data+left.count, right.data, right.count);
|
|
return result;
|
|
}
|
|
// context.allocator !
|
|
char *convert_to_null_terminated_string(const string s) {
|
|
char *cstring = cast(char*)alloc(s.count+1);
|
|
memcpy(cstring, s.data, s.count);
|
|
cstring[s.count] = 0;
|
|
return cstring;
|
|
}
|
|
|
|
char *temp_convert_to_null_terminated_string(const string s) {
|
|
push_temp_allocator();
|
|
char *c = convert_to_null_terminated_string(s);
|
|
pop_allocator();
|
|
return c;
|
|
}
|
|
inline int crt_sprintf(char *str, const char *format, ...);
|
|
int vsnprintf(char* buffer, size_t n, const char* fmt, va_list args);
|
|
|
|
bool is_pointer_valid(void *p);
|
|
u64 format_string_to_buffer(char* buffer, u64 count, const char* fmt, va_list args) {
|
|
if (!buffer) count = UINT64_MAX;
|
|
const char* p = fmt;
|
|
char* bufp = buffer;
|
|
while (*p != '\0' && (bufp - buffer) < count - 1) {
|
|
if (*p == '%') {
|
|
p += 1;
|
|
if (*p == 's') {
|
|
// We replace %s formatting with our fixed length string
|
|
p += 1;
|
|
string s = va_arg(args, string);
|
|
assert(s.count < (1024ULL*1024ULL*1024ULL*256ULL), "Ypu passed something else than a fixed-length 'string' to %%s. Maybe you passed a char* and should do %%cs instead?");
|
|
for (u64 i = 0; i < s.count && (bufp - buffer) < count - 1; i++) {
|
|
if (buffer) *bufp = s.data[i];
|
|
bufp += 1;
|
|
}
|
|
} else if (*p == 'c' && *(p+1) == 's') {
|
|
// We extend the standard formatting and add %cs so we can format c strings if we need to
|
|
p += 2;
|
|
char* s = va_arg(args, char*);
|
|
assert(is_pointer_valid(s), "You passed an invalid pointer to %%cs");
|
|
u64 len = 0;
|
|
while (*s != '\0' && (bufp - buffer) < count - 1) {
|
|
assert(is_pointer_valid(s) && len < (1024ULL*1024ULL*1024ULL*1ULL), "The argument passed to %%cs is either way too big, missing null-termination or simply not a char*.");
|
|
if (buffer) {
|
|
*bufp = *s;
|
|
}
|
|
s += 1;
|
|
bufp += 1;
|
|
len += 1;
|
|
assert(is_pointer_valid(s) && len < (1024ULL*1024ULL*1024ULL*1ULL), "The argument passed to %%cs is either way too big, missing null-termination or simply not a char*.");
|
|
}
|
|
} else {
|
|
// Fallback to standard vsnprintf
|
|
char temp_buffer[512];
|
|
char format_specifier[64];
|
|
int specifier_len = 0;
|
|
format_specifier[specifier_len++] = '%';
|
|
|
|
while (*p != '\0' && strchr("diuoxXfFeEgGaAcCpn%", *p) == NULL) {
|
|
format_specifier[specifier_len++] = *p++;
|
|
}
|
|
if (*p != '\0') {
|
|
format_specifier[specifier_len++] = *p++;
|
|
}
|
|
format_specifier[specifier_len] = '\0';
|
|
|
|
int temp_len = vsnprintf(temp_buffer, sizeof(temp_buffer), format_specifier, args);
|
|
switch (format_specifier[specifier_len - 1]) {
|
|
case 'd': case 'i': va_arg(args, int); break;
|
|
case 'u': case 'x': case 'X': case 'o': va_arg(args, unsigned int); break;
|
|
case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': va_arg(args, double); break;
|
|
case 'c': va_arg(args, int); break;
|
|
case 's': va_arg(args, char*); break;
|
|
case 'p': va_arg(args, void*); break;
|
|
case 'n': va_arg(args, int*); break;
|
|
default: break;
|
|
}
|
|
|
|
if (temp_len < 0) {
|
|
return -1; // Error in formatting
|
|
}
|
|
|
|
for (int i = 0; i < temp_len && (bufp - buffer) < count - 1; i++) {
|
|
if (buffer) *bufp = temp_buffer[i];
|
|
bufp += 1;
|
|
}
|
|
}
|
|
} else {
|
|
if (buffer) {
|
|
*bufp = *p;
|
|
}
|
|
bufp += 1;
|
|
p += 1;
|
|
}
|
|
}
|
|
if (buffer) *bufp = '\0';
|
|
|
|
return bufp - buffer;
|
|
}
|
|
string sprint_null_terminated_string_va_list_to_buffer(const char *fmt, va_list args, void* buffer, u64 count) {
|
|
u64 formatted_length = format_string_to_buffer((char*)buffer, count, fmt, args);
|
|
|
|
string result;
|
|
result.data = (u8*)buffer;
|
|
|
|
if (formatted_length >= 0 && formatted_length < count) {
|
|
result.count = formatted_length;
|
|
} else {
|
|
result.count = count - 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
string sprint_va_list_to_buffer(const string fmt, va_list args, void* buffer, u64 count) {
|
|
|
|
char* fmt_cstring = temp_convert_to_null_terminated_string(fmt);
|
|
return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count);
|
|
}
|
|
// context.allocator
|
|
string sprint_va_list(const string fmt, va_list args) {
|
|
|
|
char* fmt_cstring = temp_convert_to_null_terminated_string(fmt);
|
|
u64 count = format_string_to_buffer(NULL, 0, fmt_cstring, args) + 1;
|
|
|
|
char* buffer = NULL;
|
|
|
|
buffer = (char*)alloc(count);
|
|
|
|
return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count);
|
|
}
|
|
|
|
// context.allocator
|
|
string sprint(const string fmt, ...) {
|
|
va_list args = 0;
|
|
va_start(args, fmt);
|
|
string s = sprint_va_list(fmt, args);
|
|
va_end(args);
|
|
return s;
|
|
}
|
|
|
|
// temp allocator
|
|
string tprint(const string fmt, ...) {
|
|
va_list args = 0;
|
|
va_start(args, fmt);
|
|
push_temp_allocator();
|
|
string s = sprint_va_list(fmt, args);
|
|
pop_allocator();
|
|
va_end(args);
|
|
return s;
|
|
}
|
|
|
|
// context.allocator
|
|
string sprintf(const char *fmt, ...) {
|
|
string sfmt;
|
|
sfmt.data = cast(u8*)fmt;
|
|
sfmt.count = strlen(fmt);
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
string s = sprint_va_list(sfmt, args);
|
|
va_end(args);
|
|
|
|
return s;
|
|
}
|
|
// temp allocator
|
|
string tprintf(const char *fmt, ...) {
|
|
string sfmt;
|
|
sfmt.data = cast(u8*)fmt;
|
|
sfmt.count = strlen(fmt);
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
push_temp_allocator();
|
|
string s = sprint_va_list(sfmt, args);
|
|
pop_allocator();
|
|
va_end(args);
|
|
|
|
return s;
|
|
}
|
|
|
|
bool strings_match(string a, string b) {
|
|
if (a.count != b.count) return false;
|
|
|
|
// Count match, pointer match: they are the same
|
|
if (a.data == b.data) return true;
|
|
|
|
return memcmp(a.data, b.data, a.count) == 0;
|
|
}
|
|
|
|
string string_view(string s, u64 start_index, u64 count) {
|
|
assert(start_index < s.count, "array_view start_index % out of range for string count %", start_index, s.count);
|
|
assert(count > 0, "array_view count must be more than 0");
|
|
assert(start_index + count <= s.count, "array_view start_index + count is out of range");
|
|
|
|
string result;
|
|
result.data = s.data+start_index;
|
|
result.count = count;
|
|
|
|
return result;
|
|
}
|
|
|
|
// Defined in os interface
|
|
void print(string s, ...); |