Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
agxbuf.h
Go to the documentation of this file.
1
5/*************************************************************************
6 * Copyright (c) 2011 AT&T Intellectual Property
7 * All rights reserved. This program and the accompanying materials
8 * are made available under the terms of the Eclipse Public License v1.0
9 * which accompanies this distribution, and is available at
10 * https://www.eclipse.org/legal/epl-v10.html
11 *
12 * Contributors: Details at https://graphviz.org
13 *************************************************************************/
14
15#pragma once
16
17#include <assert.h>
18#include <limits.h>
19#include <stdarg.h>
20#include <stdbool.h>
21#include <stddef.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
25#include <util/alloc.h>
26#include <util/unused.h>
27
34
54typedef struct {
55 union {
56 struct {
57 char *buf;
58 size_t size;
59 size_t capacity;
60 char padding[sizeof(size_t) - 1];
61 unsigned char
63 } s;
64 char store[sizeof(char *) + sizeof(size_t) * 3 -
65 1];
67 } u;
68} agxbuf;
69
70static inline bool agxbuf_is_inline(const agxbuf *xb) {
71 assert((xb->u.s.located == AGXBUF_ON_HEAP ||
72 xb->u.s.located <= sizeof(xb->u.store)) &&
73 "corrupted agxbuf type");
74 return xb->u.s.located < AGXBUF_ON_HEAP;
75}
76
78static inline void agxbfree(agxbuf *xb) {
79 if (xb->u.s.located == AGXBUF_ON_HEAP)
80 free(xb->u.s.buf);
81}
82
84static inline char *agxbstart(agxbuf *xb) {
85 return agxbuf_is_inline(xb) ? xb->u.store : xb->u.s.buf;
86}
87
89static inline size_t agxblen(const agxbuf *xb) {
90 if (agxbuf_is_inline(xb)) {
91 return xb->u.s.located - AGXBUF_INLINE_SIZE_0;
92 }
93 return xb->u.s.size;
94}
95
103static inline size_t agxbsizeof(const agxbuf *xb) {
104 if (agxbuf_is_inline(xb)) {
105 return sizeof(xb->u.store);
106 }
107 return xb->u.s.capacity;
108}
109
111static inline int agxbpop(agxbuf *xb) {
112
113 size_t len = agxblen(xb);
114 if (len == 0) {
115 return -1;
116 }
117
118 if (agxbuf_is_inline(xb)) {
119 assert(xb->u.s.located > AGXBUF_INLINE_SIZE_0);
120 int c = xb->u.store[len - 1];
121 --xb->u.s.located;
122 return c;
123 }
124
125 int c = xb->u.s.buf[xb->u.s.size - 1];
126 --xb->u.s.size;
127 return c;
128}
129
131static inline void agxbmore(agxbuf *xb, size_t ssz) {
132 size_t cnt = 0; // current no. of characters in buffer
133 size_t size = 0; // current buffer size
134 size_t nsize = 0; // new buffer size
135 char *nbuf; // new buffer
136
137 size = agxbsizeof(xb);
138 nsize = size == 0 ? BUFSIZ : (2 * size);
139 if (size + ssz > nsize)
140 nsize = size + ssz;
141 cnt = agxblen(xb);
142
143 if (xb->u.s.located == AGXBUF_ON_HEAP) {
144 nbuf = (char *)gv_recalloc(xb->u.s.buf, size, nsize, sizeof(char));
145 } else {
146 nbuf = (char *)gv_calloc(nsize, sizeof(char));
147 memcpy(nbuf, xb->u.store, cnt);
148 xb->u.s.size = cnt;
149 }
150 xb->u.s.buf = nbuf;
151 xb->u.s.capacity = nsize;
153}
154
156static inline char *agxbnext(agxbuf *xb) {
157 size_t len = agxblen(xb);
158 return agxbuf_is_inline(xb) ? &xb->u.store[len] : &xb->u.s.buf[len];
159}
160
162static inline int vagxbprint(agxbuf *xb, const char *fmt, va_list ap) {
163 size_t size;
164 int result;
165
166 // determine how many bytes we need to print
167 {
168 va_list ap2;
169 int rc;
170 va_copy(ap2, ap);
171 rc = vsnprintf(NULL, 0, fmt, ap2);
172 va_end(ap2);
173 if (rc < 0) {
174 va_end(ap);
175 return rc;
176 }
177 size = (size_t)rc + 1; // account for NUL terminator
178 }
179
180 // should we use double buffering?
181 bool use_stage = false;
182
183 // do we need to expand the buffer?
184 {
185 size_t unused_space = agxbsizeof(xb) - agxblen(xb);
186 if (unused_space < size) {
187 size_t extra = size - unused_space;
188 if (agxbuf_is_inline(xb) && extra == 1) {
189 // The content is currently stored inline, but this print will push it
190 // over into being heap-allocated by a single byte. This last byte is a
191 // '\0' that `vsnprintf` unavoidably writes but we do not need. So lets
192 // avoid this by printing to an intermediate, larger buffer, and then
193 // copying the content minus the trailing '\0' to the final destination.
194 use_stage = true;
195 } else {
196 agxbmore(xb, extra);
197 }
198 }
199 }
200
201 // a buffer one byte larger than inline storage to fit the trailing '\0'
202 char stage[sizeof(xb->u.store) + 1] = {0};
203 assert(!use_stage || size <= sizeof(stage));
204
205 // we can now safely print into the buffer
206 char *dst = use_stage ? stage : agxbnext(xb);
207 result = vsnprintf(dst, size, fmt, ap);
208 assert(result == (int)(size - 1) || result < 0);
209 if (result > 0) {
210 if (agxbuf_is_inline(xb)) {
211 assert(result <= (int)UCHAR_MAX);
212 if (use_stage) {
213 memcpy(agxbnext(xb), stage, (size_t)result);
214 }
215 xb->u.s.located += (unsigned char)result;
216 assert(agxblen(xb) <= sizeof(xb->u.store) && "agxbuf corruption");
217 } else {
218 assert(!use_stage);
219 xb->u.s.size += (size_t)result;
220 }
221 }
222
223 return result;
224}
225
226/* support for extra API misuse warnings if available */
227#ifdef __GNUC__
228#define PRINTF_LIKE(index, first) __attribute__((format(printf, index, first)))
229#else
230#define PRINTF_LIKE(index, first) /* nothing */
231#endif
232
234static inline PRINTF_LIKE(2, 3) int agxbprint(agxbuf *xb, const char *fmt,
235 ...) {
236 va_list ap;
237 int result;
238
239 va_start(ap, fmt);
240
241 result = vagxbprint(xb, fmt, ap);
242
243 va_end(ap);
244 return result;
245}
246
247#undef PRINTF_LIKE
248
250static inline size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz) {
251 if (ssz == 0) {
252 return 0;
253 }
254 if (ssz > agxbsizeof(xb) - agxblen(xb))
255 agxbmore(xb, ssz);
256 size_t len = agxblen(xb);
257 if (agxbuf_is_inline(xb)) {
258 memcpy(&xb->u.store[len], s, ssz);
259 assert(ssz <= UCHAR_MAX);
260 xb->u.s.located += (unsigned char)ssz;
261 assert(agxblen(xb) <= sizeof(xb->u.store) && "agxbuf corruption");
262 } else {
263 memcpy(&xb->u.s.buf[len], s, ssz);
264 xb->u.s.size += ssz;
265 }
266 return ssz;
267}
268
270static inline size_t agxbput(agxbuf *xb, const char *s) {
271 size_t ssz = strlen(s);
272
273 return agxbput_n(xb, s, ssz);
274}
275
277static inline int agxbputc(agxbuf *xb, char c) {
278 if (agxblen(xb) >= agxbsizeof(xb)) {
279 agxbmore(xb, 1);
280 }
281 size_t len = agxblen(xb);
282 if (agxbuf_is_inline(xb)) {
283 xb->u.store[len] = c;
284 ++xb->u.s.located;
285 assert(agxblen(xb) <= sizeof(xb->u.store) && "agxbuf corruption");
286 } else {
287 xb->u.s.buf[len] = c;
288 ++xb->u.s.size;
289 }
290 return 0;
291}
292
294static inline void agxbclear(agxbuf *xb) {
295 if (agxbuf_is_inline(xb)) {
297 } else {
298 xb->u.s.size = 0;
299 }
300}
301
302/* Null-terminates buffer; resets and returns pointer to data. The buffer is
303 * still associated with the agxbuf and will be overwritten on the next, e.g.,
304 * agxbput. If you want to retrieve and disassociate the buffer, use agxbdisown
305 * instead.
306 */
307static inline WUR char *agxbuse(agxbuf *xb) {
308 if (!agxbuf_is_inline(xb) || agxblen(xb) != sizeof(xb->u.store)) {
309 (void)agxbputc(xb, '\0');
310 } else {
311 // we can skip explicitly null-terminating the buffer because `agxbclear`
312 // resets the `xb->located` byte such that it naturally forms a terminator
313 assert(AGXBUF_INLINE_SIZE_0 == '\0');
314 }
315
316 agxbclear(xb);
317 return agxbstart(xb);
318}
319
320/* Disassociate the backing buffer from this agxbuf and return it. The buffer is
321 * NUL terminated before being returned. If the agxbuf is using stack memory,
322 * this will first copy the data to a new heap buffer to then return. If you
323 * want to temporarily access the string in the buffer, but have it overwritten
324 * and reused the next time, e.g., agxbput is called, use agxbuse instead of
325 * agxbdisown.
326 */
327static inline char *agxbdisown(agxbuf *xb) {
328 char *buf;
329
330 if (agxbuf_is_inline(xb)) {
331 // the string lives in `store`, so we need to copy its contents to heap
332 // memory
333 buf = gv_strndup(xb->u.store, agxblen(xb));
334 } else {
335 // the buffer is already dynamically allocated, so terminate it and then
336 // take it as-is
337 agxbputc(xb, '\0');
338 buf = xb->u.s.buf;
339 }
340
341 // reset xb to a state where it is usable
342 memset(xb, 0, sizeof(*xb));
343
344 return buf;
345}
346
364static inline void agxbuf_trim_zeros(agxbuf *xb) {
365
366 // find last period
367 char *start = agxbstart(xb);
368 size_t period;
369 for (period = agxblen(xb) - 1;; --period) {
370 if (period == SIZE_MAX) {
371 // we searched the entire string and did not find a period
372 return;
373 }
374 if (start[period] == '.') {
375 break;
376 }
377 }
378
379 // truncate any “0”s that provide no information
380 for (size_t follower = agxblen(xb) - 1;; --follower) {
381 if (follower == period || start[follower] == '0') {
382 // truncate this character
383 if (agxbuf_is_inline(xb)) {
384 assert(xb->u.s.located > AGXBUF_INLINE_SIZE_0);
385 --xb->u.s.located;
386 } else {
387 --xb->u.s.size;
388 }
389 if (follower == period) {
390 break;
391 }
392 } else {
393 return;
394 }
395 }
396
397 // is the remainder we have left not “-0”?
398 const size_t len = agxblen(xb);
399 if (len < 2 || start[len - 2] != '-' || start[len - 1] != '0') {
400 return;
401 }
402
403 // turn “-0” into “0”
404 start[len - 2] = '0';
405 if (agxbuf_is_inline(xb)) {
406 assert(xb->u.s.located > AGXBUF_INLINE_SIZE_0);
407 --xb->u.s.located;
408 } else {
409 --xb->u.s.size;
410 }
411}
static int agxbpop(agxbuf *xb)
removes last character added, if any
Definition agxbuf.h:111
static size_t agxbsizeof(const agxbuf *xb)
Definition agxbuf.h:103
static void agxbuf_trim_zeros(agxbuf *xb)
Definition agxbuf.h:364
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
Definition agxbuf.h:270
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
Definition agxbuf.h:250
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static char * agxbnext(agxbuf *xb)
next position for writing
Definition agxbuf.h:156
static void agxbclear(agxbuf *xb)
resets pointer to data
Definition agxbuf.h:294
static int vagxbprint(agxbuf *xb, const char *fmt, va_list ap)
vprintf-style output to an agxbuf
Definition agxbuf.h:162
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
static void agxbmore(agxbuf *xb, size_t ssz)
expand buffer to hold at least ssz more bytes
Definition agxbuf.h:131
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
Definition agxbuf.h:89
static char * agxbstart(agxbuf *xb)
return pointer to beginning of buffer
Definition agxbuf.h:84
static bool agxbuf_is_inline(const agxbuf *xb)
Definition agxbuf.h:70
#define PRINTF_LIKE(index, first)
Definition agxbuf.h:230
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:277
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:327
agxbuf_loc_t
a description of where a buffer is located
Definition agxbuf.h:29
@ AGXBUF_ON_HEAP
Definition agxbuf.h:31
@ AGXBUF_INLINE_SIZE_0
Definition agxbuf.h:30
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static char * gv_strndup(const char *original, size_t length)
Definition alloc.h:114
static double len(glCompPoint p)
Definition glutils.c:150
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:163
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:210
static int store(segment_t *seg, int first, pointf *pts)
Definition partition.c:115
char store[sizeof(char *)+sizeof(size_t) *3 - 1]
Definition agxbuf.h:65
char * buf
start of buffer
Definition agxbuf.h:57
size_t size
number of characters in the buffer
Definition agxbuf.h:58
struct agxbuf::@121::@122 s
unsigned char located
where does the backing memory for this buffer live?
Definition agxbuf.h:62
size_t capacity
available bytes in the buffer
Definition agxbuf.h:59
union agxbuf::@121 u
Definition grammar.c:93
abstraction for squashing compiler warnings for unused symbols
#define WUR
Definition unused.h:43