-
Notifications
You must be signed in to change notification settings - Fork 317
/
str.c
123 lines (106 loc) · 3.79 KB
/
str.c
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
/**
* SPDX-FileCopyrightText: Copyright 2022-present Greg Hurrell and contributors.
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "str.h"
#include <assert.h> /* for assert() */
#include <stdlib.h> /* for free() */
#include <string.h> /* for memcpy() */
#include "xmalloc.h"
// When allocating memory, reserve a little more than was asked for,
// which can help to avoid subsequent allocations.
#define STR_OVERALLOC 256
// Special `capacity` value to flag a `str_t` as having been "slab" allocated.
#define SLAB_ALLOCATION -1
#define NULL_PADDING 1
str_t *str_new_copy(const char *source, size_t length) {
assert(length < SSIZE_MAX);
str_t *str = xmalloc(sizeof(str_t));
str->contents = xmalloc(length + NULL_PADDING);
str->length = length;
str->capacity = length + NULL_PADDING;
memcpy((void *)str->contents, source, length);
char *end = (char *)str->contents + length;
end[0] = '\0';
return str;
}
str_t *str_new_size(size_t length) {
assert(length < SSIZE_MAX);
str_t *str = xmalloc(sizeof(str_t));
str->contents = xmalloc(length + NULL_PADDING);
str->length = 0;
str->capacity = length + NULL_PADDING;
((char *)str->contents)[0] = '\0';
return str;
}
void str_init(str_t *str, const char *source, size_t length) {
assert(length < SSIZE_MAX);
str->contents = source;
str->length = length;
str->capacity = SLAB_ALLOCATION;
}
void str_init_copy(str_t *str, const char *source, size_t length) {
assert(length < SSIZE_MAX);
str->contents = xmalloc(length + NULL_PADDING);
str->length = length;
str->capacity = length + NULL_PADDING;
memcpy((void *)str->contents, source, length);
char *end = (char *)str->contents + length;
end[0] = '\0';
}
// Internal only, so doesn't need to be fast/cheap. This is currently only used
// by `scanner_dump()` (a debugging function).
str_t *str_new(void) {
str_t *str = xmalloc(sizeof(str_t));
str->contents = xcalloc(STR_OVERALLOC, 1);
str->length = 0;
str->capacity = STR_OVERALLOC;
return str;
}
void str_append(str_t *str, const char *source, size_t length) {
assert(str->capacity != SLAB_ALLOCATION);
size_t new_length = str->length + length;
assert(new_length + NULL_PADDING < SSIZE_MAX);
if (str->capacity < (ssize_t)(new_length + NULL_PADDING)) {
str->contents =
xrealloc((void *)str->contents, new_length + STR_OVERALLOC);
str->capacity = new_length + STR_OVERALLOC;
}
memcpy((void *)str->contents + str->length, source, length + NULL_PADDING);
str->length = new_length;
}
void str_append_char(str_t *str, char c) {
assert(str->capacity != SLAB_ALLOCATION);
size_t new_length = str->length + 1;
assert(new_length + NULL_PADDING < SSIZE_MAX);
if (str->capacity < (ssize_t)(new_length + NULL_PADDING)) {
str->contents =
xrealloc((void *)str->contents, new_length + STR_OVERALLOC);
str->capacity = new_length + STR_OVERALLOC;
}
((char *)str->contents)[str->length] = c;
((char *)str->contents)[str->length + 1] = '\0';
str->length = new_length;
}
void str_append_str(str_t *str, str_t *other) {
str_append(str, other->contents, other->length);
}
void str_truncate(str_t *str, size_t length) {
assert(str->length > length);
str->length = length;
((char *)str->contents)[length] = '\0';
}
void str_free(str_t *str) {
// If we were part of a "slab" allocation, do nothing. We should get freed
// automatically when our slab gets freed.
if (str->capacity != SLAB_ALLOCATION) {
free((void *)str->contents);
free(str);
}
}
const char *str_c_string(str_t *str) {
char *c_string = xmalloc(str->length + NULL_PADDING);
memcpy(c_string, str->contents, str->length);
(c_string + str->length)[0] = '\0';
return c_string;
}