Derive-C
Loading...
Searching...
No Matches
template.h
Go to the documentation of this file.
1
3
4#if !defined _GNU_SOURCE
5 // Enable gnu specific features for fopencookie
6 // - Must be set before stdio include
7 // - see: https://www.man7.org/linux/man-pages/man7/feature_test_macros.7.html
8 #define _GNU_SOURCE
9#endif
10
12
13#include <errno.h>
14#include <stdio.h>
15
18#include <string.h>
19
20typedef struct {
21 FILE* stream;
22 char* buf;
24 size_t capacity;
25 ALLOC* alloc;
26} SELF;
27
28// Additional constant when extending the string allocation.
29static size_t const NS(SELF, additional_alloc_size) = 32;
30
31#define INVARIANT_CHECK(self) \
32 ASSUME(self); \
33 ASSUME((self)->alloc); \
34 ASSUME(WHEN((self)->buf, (self)->stream && (self)->capacity > 0)); \
35 ASSUME(WHEN((self)->capacity == 0, !(self)->buf)); \
36 ASSUME(WHEN((self)->buf, (self)->size_without_null + 1 <= (self)->capacity));
37
38static ssize_t PRIV(NS(SELF, read))(void* capture,
39 char* buf /*NOLINT(readability-non-const-parameter)*/,
40 size_t size) {
41 SELF* self = (SELF*)capture;
42 INVARIANT_CHECK(self);
43
44 (void)buf;
45 (void)size;
46
47 PANIC("cookie set in write-only mode, but read was called.");
48}
49
50static ssize_t PRIV(NS(SELF, write))(void* capture, const char* data, size_t size) {
51 SELF* self = (SELF*)capture;
52 INVARIANT_CHECK(self);
53
54 if (self->size_without_null + size + 1 > self->capacity) {
55 size_t new_capacity;
56 char* new_buf;
57 if (self->buf != NULL) {
58 size_t const growth_factor = 2;
59 new_capacity =
60 (self->capacity * growth_factor) + size + NS(SELF, additional_alloc_size);
61 new_buf = (char*)NS(ALLOC, realloc)(self->alloc, self->buf, new_capacity);
62 } else {
63 ASSUME(self->capacity == 0);
64 new_capacity = size + 1 + NS(SELF, additional_alloc_size);
65 new_buf = (char*)NS(ALLOC, malloc)(self->alloc, new_capacity);
66 }
67
68 if (!new_buf) {
69 errno = ENOMEM; // NOLINT(misc-include-cleaner)
70 return -1;
71 }
72
73 self->capacity = new_capacity;
74 self->buf = new_buf;
75 }
76
77 memcpy(self->buf + self->size_without_null, data, size);
78 self->size_without_null += size;
79 self->buf[self->size_without_null] = '\0';
80 return (ssize_t)size;
81}
82
83static int PRIV(NS(SELF, seek))(void* capture,
84 off_t* offset /*NOLINT(readability-non-const-parameter)*/,
85 int whence) {
86 SELF* self = (SELF*)capture;
87 INVARIANT_CHECK(self);
88 (void)offset;
89 (void)whence;
90
91 errno = EPERM; // NOLINT(misc-include-cleaner)
92 return -1;
93}
94
95static int PRIV(NS(SELF, close))(void* capture) {
96 SELF* self = (SELF*)capture;
97 INVARIANT_CHECK(self);
98 return 0;
99}
100
101static SELF NS(SELF, new)(ALLOC* alloc) {
102 return (SELF){
103 .stream = NULL,
104 .buf = NULL,
105 .size_without_null = 0,
106 .capacity = 0,
107 .alloc = alloc,
108 };
109}
110
112static FILE* NS(SELF, stream)(SELF* self) {
113 INVARIANT_CHECK(self);
114
115 if (self->stream == NULL) {
116 cookie_io_functions_t /* NOLINT(misc-include-cleaner) */ const io = {
117 .read = PRIV(NS(SELF, read)),
118 .write = PRIV(NS(SELF, write)),
119 .seek = PRIV(NS(SELF, seek)),
120 .close = PRIV(NS(SELF, close)),
121 };
122 self->stream = fopencookie(self, "w", io);
123 ASSERT(self->stream != NULL, "Failed to open stream for string builder, with error: %s",
124 strerror(errno));
125
126 // JUSTIFY: No buffering
127 // - No performance advantage to buffering writes - this already writes to an in memory
128 // buffer
129 // - No need to fflush
130 setvbuf(self->stream, NULL, _IONBF, 0);
131 }
132
133 return self->stream;
134}
135
137static void NS(SELF, reset)(SELF* self) {
138 INVARIANT_CHECK(self);
139 self->size_without_null = 0;
140}
141
143static char const* NS(SELF, string)(SELF const* self) {
144 INVARIANT_CHECK(self);
145 if (self->size_without_null == 0) {
146 return "";
147 }
148 return self->buf;
149}
150
152static char* NS(SELF, release_string)(SELF* self) {
153 INVARIANT_CHECK(self);
154 char* buf = self->buf;
155 self->size_without_null = 0;
156 self->buf = NULL;
157 self->capacity = 0;
158
159 return buf;
160}
161
162static size_t NS(SELF, string_size)(SELF* self) {
163 INVARIANT_CHECK(self);
164 return self->size_without_null;
165}
166
167static void NS(SELF, delete)(SELF* self) {
168 INVARIANT_CHECK(self);
169 if (self->stream) {
170 fclose(self->stream);
171 }
172 if (self->buf) {
173 NS(ALLOC, free)(self->alloc, self->buf);
174 }
175}
176
177#undef INVARIANT_CHECK
178
static void free(SELF *self, void *ptr)
Definition template.h:58
static void * realloc(SELF *self, void *ptr, size_t size)
Definition template.h:47
static void * malloc(SELF *self, size_t size)
Definition template.h:25
#define ALLOC
Definition template.h:59
static INDEX_TYPE size(SELF const *self)
Definition template.h:275
#define INVARIANT_CHECK(self)
Definition template.h:142
static VALUE const * read(SELF const *self, INDEX index)
Definition template.h:240
static VALUE * write(SELF *self, INDEX index)
Definition template.h:222
static ITEM * data(SELF *self)
Definition template.h:259
#define ASSERT(expr,...)
Definition macros.h:42
#define ASSUME(expr,...)
Definition macros.h:62
#define PANIC(...)
Definition macros.h:34
#define NS(pre, post)
Definition namespace.h:4
#define PRIV(name)
Definition namespace.h:6
#define SELF
Definition def.h:52
size_t capacity
Definition template.h:124
char * buf
Definition template.h:22
FILE * stream
Definition template.h:21
ALLOC * alloc
Definition template.h:66
size_t size_without_null
Definition template.h:23
static char * release_string(SELF *self)
Disowns the current string, free/management with chosen allocator determined by user.
Definition template.h:152
static size_t const additional_alloc_size
Definition template.h:29
static int PRIV close(void *capture)
Definition template.h:95
static size_t string_size(SELF *self)
Definition template.h:162
static FILE * stream(SELF *self)
Opens a file for.
Definition template.h:112
static int PRIV seek(void *capture, off_t *offset, int whence)
Definition template.h:83
static void reset(SELF *self)
Resets the string, but keps the same stream pointer alive.
Definition template.h:137