Graphviz 12.0.1~dev.20240715.2254
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 <cgraph/alloc.h>
19#include <limits.h>
20#include <stdarg.h>
21#include <stdbool.h>
22#include <stddef.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <string.h>
26
33
53typedef struct {
54 union {
55 struct {
56 char *buf;
57 size_t size;
58 size_t capacity;
59 char padding[sizeof(size_t) - 1];
60 unsigned char
62 } s;
63 char store[sizeof(char *) + sizeof(size_t) * 3 -
64 1];
66 } u;
67} agxbuf;
68
69static inline bool agxbuf_is_inline(const agxbuf *xb) {
70 assert((xb->u.s.located == AGXBUF_ON_HEAP ||
71 xb->u.s.located <= sizeof(xb->u.store)) &&
72 "corrupted agxbuf type");
73 return xb->u.s.located < AGXBUF_ON_HEAP;
74}
75
77static inline void agxbfree(agxbuf *xb) {
78 if (xb->u.s.located == AGXBUF_ON_HEAP)
79 free(xb->u.s.buf);
80}
81
83static inline char *agxbstart(agxbuf *xb) {
84 return agxbuf_is_inline(xb) ? xb->u.store : xb->u.s.buf;
85}
86
88static inline size_t agxblen(const agxbuf *xb) {
89 if (agxbuf_is_inline(xb)) {
90 return xb->u.s.located - AGXBUF_INLINE_SIZE_0;
91 }
92 return xb->u.s.size;
93}
94
102static inline size_t agxbsizeof(const agxbuf *xb) {
103 if (agxbuf_is_inline(xb)) {
104 return sizeof(xb->u.store);
105 }
106 return xb->u.s.capacity;
107}
108
110static inline int agxbpop(agxbuf *xb) {
111
112 size_t len = agxblen(xb);
113 if (len == 0) {
114 return -1;
115 }
116
117 if (agxbuf_is_inline(xb)) {
118 assert(xb->u.s.located > AGXBUF_INLINE_SIZE_0);
119 int c = xb->u.store[len - 1];
120 --xb->u.s.located;
121 return c;
122 }
123
124 int c = xb->u.s.buf[xb->u.s.size - 1];
125 --xb->u.s.size;
126 return c;
127}
128
130static inline void agxbmore(agxbuf *xb, size_t ssz) {
131 size_t cnt = 0; // current no. of characters in buffer
132 size_t size = 0; // current buffer size
133 size_t nsize = 0; // new buffer size
134 char *nbuf; // new buffer
135
136 size = agxbsizeof(xb);
137 nsize = size == 0 ? BUFSIZ : (2 * size);
138 if (size + ssz > nsize)
139 nsize = size + ssz;
140 cnt = agxblen(xb);
141
142 if (xb->u.s.located == AGXBUF_ON_HEAP) {
143 nbuf = (char *)gv_recalloc(xb->u.s.buf, size, nsize, sizeof(char));
144 } else {
145 nbuf = (char *)gv_calloc(nsize, sizeof(char));
146 memcpy(nbuf, xb->u.store, cnt);
147 xb->u.s.size = cnt;
148 }
149 xb->u.s.buf = nbuf;
150 xb->u.s.capacity = nsize;
152}
153
155static inline char *agxbnext(agxbuf *xb) {
156 size_t len = agxblen(xb);
157 return agxbuf_is_inline(xb) ? &xb->u.store[len] : &xb->u.s.buf[len];
158}
159
161static inline int vagxbprint(agxbuf *xb, const char *fmt, va_list ap) {
162 size_t size;
163 int result;
164
165 // determine how many bytes we need to print
166 {
167 va_list ap2;
168 int rc;
169 va_copy(ap2, ap);
170 rc = vsnprintf(NULL, 0, fmt, ap2);
171 va_end(ap2);
172 if (rc < 0) {
173 va_end(ap);
174 return rc;
175 }
176 size = (size_t)rc + 1; // account for NUL terminator
177 }
178
179 // do we need to expand the buffer?
180 {
181 size_t unused_space = agxbsizeof(xb) - agxblen(xb);
182 if (unused_space < size) {
183 size_t extra = size - unused_space;
184 agxbmore(xb, extra);
185 }
186 }
187
188 // we can now safely print into the buffer
189 char *dst = agxbnext(xb);
190 result = vsnprintf(dst, size, fmt, ap);
191 assert(result == (int)(size - 1) || result < 0);
192 if (result > 0) {
193 if (agxbuf_is_inline(xb)) {
194 assert(result <= (int)UCHAR_MAX);
195 xb->u.s.located += (unsigned char)result;
196 assert(agxblen(xb) <= sizeof(xb->u.store) && "agxbuf corruption");
197 } else {
198 xb->u.s.size += (size_t)result;
199 }
200 }
201
202 return result;
203}
204
205/* support for extra API misuse warnings if available */
206#ifdef __GNUC__
207#define PRINTF_LIKE(index, first) __attribute__((format(printf, index, first)))
208#else
209#define PRINTF_LIKE(index, first) /* nothing */
210#endif
211
213static inline PRINTF_LIKE(2, 3) int agxbprint(agxbuf *xb, const char *fmt,
214 ...) {
215 va_list ap;
216 int result;
217
218 va_start(ap, fmt);
219
220 result = vagxbprint(xb, fmt, ap);
221
222 va_end(ap);
223 return result;
224}
225
226#undef PRINTF_LIKE
227
229static inline size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz) {
230 if (ssz == 0) {
231 return 0;
232 }
233 if (ssz > agxbsizeof(xb) - agxblen(xb))
234 agxbmore(xb, ssz);
235 size_t len = agxblen(xb);
236 if (agxbuf_is_inline(xb)) {
237 memcpy(&xb->u.store[len], s, ssz);
238 assert(ssz <= UCHAR_MAX);
239 xb->u.s.located += (unsigned char)ssz;
240 assert(agxblen(xb) <= sizeof(xb->u.store) && "agxbuf corruption");
241 } else {
242 memcpy(&xb->u.s.buf[len], s, ssz);
243 xb->u.s.size += ssz;
244 }
245 return ssz;
246}
247
249static inline size_t agxbput(agxbuf *xb, const char *s) {
250 size_t ssz = strlen(s);
251
252 return agxbput_n(xb, s, ssz);
253}
254
256static inline int agxbputc(agxbuf *xb, char c) {
257 if (agxblen(xb) >= agxbsizeof(xb)) {
258 agxbmore(xb, 1);
259 }
260 size_t len = agxblen(xb);
261 if (agxbuf_is_inline(xb)) {
262 xb->u.store[len] = c;
263 ++xb->u.s.located;
264 assert(agxblen(xb) <= sizeof(xb->u.store) && "agxbuf corruption");
265 } else {
266 xb->u.s.buf[len] = c;
267 ++xb->u.s.size;
268 }
269 return 0;
270}
271
273static inline void agxbclear(agxbuf *xb) {
274 if (agxbuf_is_inline(xb)) {
276 } else {
277 xb->u.s.size = 0;
278 }
279}
280
281/* Null-terminates buffer; resets and returns pointer to data. The buffer is
282 * still associated with the agxbuf and will be overwritten on the next, e.g.,
283 * agxbput. If you want to retrieve and disassociate the buffer, use agxbdisown
284 * instead.
285 */
286static inline char *agxbuse(agxbuf *xb) {
287 (void)agxbputc(xb, '\0');
288 agxbclear(xb);
289 return agxbstart(xb);
290}
291
292/* Disassociate the backing buffer from this agxbuf and return it. The buffer is
293 * NUL terminated before being returned. If the agxbuf is using stack memory,
294 * this will first copy the data to a new heap buffer to then return. If you
295 * want to temporarily access the string in the buffer, but have it overwritten
296 * and reused the next time, e.g., agxbput is called, use agxbuse instead of
297 * agxbdisown.
298 */
299static inline char *agxbdisown(agxbuf *xb) {
300 char *buf;
301
302 if (agxbuf_is_inline(xb)) {
303 // the string lives in `store`, so we need to copy its contents to heap
304 // memory
305 buf = gv_strndup(xb->u.store, agxblen(xb));
306 } else {
307 // the buffer is already dynamically allocated, so terminate it and then
308 // take it as-is
309 agxbputc(xb, '\0');
310 buf = xb->u.s.buf;
311 }
312
313 // reset xb to a state where it is usable
314 memset(xb, 0, sizeof(*xb));
315
316 return buf;
317}
318
336static inline void agxbuf_trim_zeros(agxbuf *xb) {
337
338 // find last period
339 char *start = agxbstart(xb);
340 size_t period;
341 for (period = agxblen(xb) - 1;; --period) {
342 if (period == SIZE_MAX) {
343 // we searched the entire string and did not find a period
344 return;
345 }
346 if (start[period] == '.') {
347 break;
348 }
349 }
350
351 // truncate any “0”s that provide no information
352 for (size_t follower = agxblen(xb) - 1;; --follower) {
353 if (follower == period || start[follower] == '0') {
354 // truncate this character
355 if (agxbuf_is_inline(xb)) {
356 assert(xb->u.s.located > AGXBUF_INLINE_SIZE_0);
357 --xb->u.s.located;
358 } else {
359 --xb->u.s.size;
360 }
361 if (follower == period) {
362 break;
363 }
364 } else {
365 return;
366 }
367 }
368
369 // is the remainder we have left not “-0”?
370 const size_t len = agxblen(xb);
371 if (len < 2 || start[len - 2] != '-' || start[len - 1] != '0') {
372 return;
373 }
374
375 // turn “-0” into “0”
376 start[len - 2] = '0';
377 if (agxbuf_is_inline(xb)) {
378 assert(xb->u.s.located > AGXBUF_INLINE_SIZE_0);
379 --xb->u.s.located;
380 } else {
381 --xb->u.s.size;
382 }
383}
static int agxbpop(agxbuf *xb)
removes last character added, if any
Definition agxbuf.h:110
static size_t agxbsizeof(const agxbuf *xb)
Definition agxbuf.h:102
static void agxbuf_trim_zeros(agxbuf *xb)
Definition agxbuf.h:336
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
Definition agxbuf.h:249
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
Definition agxbuf.h:229
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static char * agxbnext(agxbuf *xb)
next position for writing
Definition agxbuf.h:155
static void agxbclear(agxbuf *xb)
resets pointer to data
Definition agxbuf.h:273
static int vagxbprint(agxbuf *xb, const char *fmt, va_list ap)
vprintf-style output to an agxbuf
Definition agxbuf.h:161
static void agxbmore(agxbuf *xb, size_t ssz)
expand buffer to hold at least ssz more bytes
Definition agxbuf.h:130
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
Definition agxbuf.h:88
static char * agxbstart(agxbuf *xb)
return pointer to beginning of buffer
Definition agxbuf.h:83
static bool agxbuf_is_inline(const agxbuf *xb)
Definition agxbuf.h:69
#define PRINTF_LIKE(index, first)
Definition agxbuf.h:209
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:256
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:299
agxbuf_loc_t
a description of where a buffer is located
Definition agxbuf.h:28
@ AGXBUF_ON_HEAP
Definition agxbuf.h:30
@ AGXBUF_INLINE_SIZE_0
Definition agxbuf.h:29
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:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
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:64
char * buf
start of buffer
Definition agxbuf.h:56
size_t size
number of characters in the buffer
Definition agxbuf.h:57
struct agxbuf::@59::@60 s
union agxbuf::@59 u
unsigned char located
where does the backing memory for this buffer live?
Definition agxbuf.h:61
size_t capacity
available bytes in the buffer
Definition agxbuf.h:58
Definition grammar.c:93