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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
/// Fixed-size strings with value semantics.
#pragma once
#include <assert.h>
#include <bsd/string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/// A fixed-size string.
/// The string is null-terminated so that it can be used with the usual C APIs.
//
// TODO: The asserts on length should be hard asserts, not just asserts in debug
// builds.
#define DEF_STRING(STRING, SIZE) \
typedef struct STRING { \
size_t length; \
char str[SIZE]; \
} STRING; \
\
static const size_t STRING##_size = SIZE; \
\
static inline const char* STRING##_cstr(const STRING* str) { \
return str->str; \
} \
\
static inline size_t STRING##_length(STRING str) { return str.length; } \
\
static inline STRING STRING##_make(const char* cstr) { \
if (!cstr) { \
return (STRING){0}; \
} else { \
STRING str = (STRING){0}; \
str.length = strlcpy(str.str, cstr, SIZE); \
return str; \
} \
} \
\
static inline STRING STRING##_dirname(STRING path) { \
STRING str = path; \
for (int i = str.length - 1; i >= 0; --i) { \
if (str.str[i] == '/' || str.str[i] == '\\') { \
str.str[i] = 0; \
str.length = i; \
return str; \
} else { \
str.str[i] = 0; \
} \
} \
str = (STRING){0}; \
str.str[0] = '.'; \
str.length = 1; \
return str; \
} \
\
static inline void STRING##_append_cstr(STRING* a, const char* b) { \
size_t b_length = strlen(b); \
assert(a->length + b_length + 1 < SIZE); \
strlcpy(a->str + a->length, b, SIZE); \
a->length = a->length + b_length; \
} \
\
static inline void STRING##_append(STRING* a, STRING b) { \
assert(a->length + b.length + 1 < SIZE); \
strlcpy(a->str + a->length, b.str, SIZE); \
a->length = a->length + b.length; \
} \
\
static inline STRING STRING##_concat(STRING a, STRING b) { \
assert(a.length + b.length + 1 < SIZE); \
STRING str = {0}; \
strlcpy(str.str, a.str, SIZE); \
strlcpy(str.str + a.length, b.str, SIZE); \
str.length = a.length + b.length; \
return str; \
} \
\
static inline STRING STRING##_concat_cstr(STRING a, const char* b) { \
return STRING##_concat(a, STRING##_make(b)); \
} \
\
static inline STRING STRING##_concat_path(STRING a, STRING b) { \
return STRING##_concat(STRING##_concat(a, STRING##_make("/")), b); \
} \
\
static inline bool STRING##_eq(STRING a, STRING b) { \
if (a.length != b.length) { \
return false; \
} \
return strncmp(a.str, b.str, a.length) == 0; \
} \
\
static inline bool STRING##_eq_cstr(STRING a, const char* b) { \
return (a.length == strlen(b)) && strncmp(a.str, b, a.length) == 0; \
} \
\
static inline bool STRING##_empty(STRING a) { return a.length == 0; } \
\
static inline STRING STRING##_itoa(int n) { \
STRING str = (STRING){0}; \
const int written = snprintf(str.str, SIZE, "%d", n); \
assert(written >= 0); \
str.length = (size_t)written; \
return str; \
} \
\
static inline uint64_t STRING##_hash(STRING str) { \
return cstring_hash(str.str); \
}
/// Return a hash of the given string.
static inline uint64_t cstring_hash(const char* str) {
uint64_t hash = 0;
for (size_t i = 0; i < strlen(str); ++i) {
hash = (uint64_t)str[i] + (hash << 6) + (hash << 16) - hash;
}
return hash;
}
DEF_STRING(sstring, 32) // Small.
DEF_STRING(mstring, 256) // Medium.
DEF_STRING(lstring, 1024) // Large.
DEF_STRING(xlstring, 4096) // Extra large.
|