str.h revision 1.11 1 /* $NetBSD: str.h,v 1.11 2021/12/05 12:17:49 rillig Exp $ */
2
3 /*
4 Copyright (c) 2021 Roland Illig <rillig (at) NetBSD.org>
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
21 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 */
29
30
31 /*
32 * Memory-efficient string handling.
33 */
34
35
36 /* A read-only string that may need to be freed after use. */
37 typedef struct FStr {
38 const char *str;
39 void *freeIt;
40 } FStr;
41
42 /* A modifiable string that may need to be freed after use. */
43 typedef struct MFStr {
44 char *str;
45 void *freeIt;
46 } MFStr;
47
48 /* A read-only range of a character array, NOT null-terminated. */
49 typedef struct Substring {
50 const char *start;
51 const char *end;
52 } Substring;
53
54 /*
55 * Builds a string, only allocating memory if the string is different from the
56 * expected string.
57 */
58 typedef struct LazyBuf {
59 char *data;
60 size_t len;
61 size_t cap;
62 const char *expected;
63 void *freeIt;
64 } LazyBuf;
65
66 /* The result of splitting a string into words. */
67 typedef struct Words {
68 char **words;
69 size_t len;
70 void *freeIt;
71 } Words;
72
73 /* The result of splitting a string into words. */
74 typedef struct SubstringWords {
75 Substring *words;
76 size_t len;
77 void *freeIt;
78 } SubstringWords;
79
80
81 MAKE_INLINE FStr
82 FStr_Init(const char *str, void *freeIt)
83 {
84 FStr fstr;
85 fstr.str = str;
86 fstr.freeIt = freeIt;
87 return fstr;
88 }
89
90 /* Return a string that is the sole owner of str. */
91 MAKE_INLINE FStr
92 FStr_InitOwn(char *str)
93 {
94 return FStr_Init(str, str);
95 }
96
97 /* Return a string that refers to the shared str. */
98 MAKE_INLINE FStr
99 FStr_InitRefer(const char *str)
100 {
101 return FStr_Init(str, NULL);
102 }
103
104 MAKE_INLINE void
105 FStr_Done(FStr *fstr)
106 {
107 free(fstr->freeIt);
108 #ifdef CLEANUP
109 fstr->str = NULL;
110 fstr->freeIt = NULL;
111 #endif
112 }
113
114
115 MAKE_INLINE MFStr
116 MFStr_Init(char *str, void *freeIt)
117 {
118 MFStr mfstr;
119 mfstr.str = str;
120 mfstr.freeIt = freeIt;
121 return mfstr;
122 }
123
124 /* Return a string that is the sole owner of str. */
125 MAKE_INLINE MFStr
126 MFStr_InitOwn(char *str)
127 {
128 return MFStr_Init(str, str);
129 }
130
131 /* Return a string that refers to the shared str. */
132 MAKE_INLINE MFStr
133 MFStr_InitRefer(char *str)
134 {
135 return MFStr_Init(str, NULL);
136 }
137
138 MAKE_INLINE void
139 MFStr_Done(MFStr *mfstr)
140 {
141 free(mfstr->freeIt);
142 #ifdef CLEANUP
143 mfstr->str = NULL;
144 mfstr->freeIt = NULL;
145 #endif
146 }
147
148
149 MAKE_STATIC Substring
150 Substring_Init(const char *start, const char *end)
151 {
152 Substring sub;
153
154 sub.start = start;
155 sub.end = end;
156 return sub;
157 }
158
159 MAKE_INLINE Substring
160 Substring_InitStr(const char *str)
161 {
162 return Substring_Init(str, str + strlen(str));
163 }
164
165 MAKE_STATIC size_t
166 Substring_Length(Substring sub)
167 {
168 return (size_t)(sub.end - sub.start);
169 }
170
171 MAKE_STATIC bool
172 Substring_IsEmpty(Substring sub)
173 {
174 return sub.start == sub.end;
175 }
176
177 MAKE_INLINE bool
178 Substring_Equals(Substring sub, const char *str)
179 {
180 size_t len = strlen(str);
181 return Substring_Length(sub) == len &&
182 memcmp(sub.start, str, len) == 0;
183 }
184
185 MAKE_INLINE bool
186 Substring_Eq(Substring sub, Substring str)
187 {
188 size_t len = Substring_Length(sub);
189 return len == Substring_Length(str) &&
190 memcmp(sub.start, str.start, len) == 0;
191 }
192
193 MAKE_STATIC Substring
194 Substring_Sub(Substring sub, size_t start, size_t end)
195 {
196 assert(start <= Substring_Length(sub));
197 assert(end <= Substring_Length(sub));
198 return Substring_Init(sub.start + start, sub.start + end);
199 }
200
201 MAKE_STATIC bool
202 Substring_HasPrefix(Substring sub, Substring prefix)
203 {
204 return Substring_Length(sub) >= Substring_Length(prefix) &&
205 memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
206 }
207
208 MAKE_STATIC bool
209 Substring_HasSuffix(Substring sub, Substring suffix)
210 {
211 size_t suffixLen = Substring_Length(suffix);
212 return Substring_Length(sub) >= suffixLen &&
213 memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
214 }
215
216 /* Returns an independent, null-terminated copy of the substring. */
217 MAKE_STATIC FStr
218 Substring_Str(Substring sub)
219 {
220 if (Substring_IsEmpty(sub))
221 return FStr_InitRefer("");
222 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
223 }
224
225 MAKE_STATIC const char *
226 Substring_SkipFirst(Substring sub, char ch)
227 {
228 const char *p;
229
230 for (p = sub.start; p != sub.end; p++)
231 if (*p == ch)
232 return p + 1;
233 return sub.start;
234 }
235
236 MAKE_STATIC const char *
237 Substring_LastIndex(Substring sub, char ch)
238 {
239 const char *p;
240
241 for (p = sub.end; p != sub.start; p--)
242 if (p[-1] == ch)
243 return p - 1;
244 return NULL;
245 }
246
247 MAKE_STATIC Substring
248 Substring_Dirname(Substring pathname)
249 {
250 const char *p;
251
252 for (p = pathname.end; p != pathname.start; p--)
253 if (p[-1] == '/')
254 return Substring_Init(pathname.start, p - 1);
255 return Substring_InitStr(".");
256 }
257
258 MAKE_STATIC Substring
259 Substring_Basename(Substring pathname)
260 {
261 const char *p;
262
263 for (p = pathname.end; p != pathname.start; p--)
264 if (p[-1] == '/')
265 return Substring_Init(p, pathname.end);
266 return pathname;
267 }
268
269
270 MAKE_STATIC void
271 LazyBuf_Init(LazyBuf *buf, const char *expected)
272 {
273 buf->data = NULL;
274 buf->len = 0;
275 buf->cap = 0;
276 buf->expected = expected;
277 buf->freeIt = NULL;
278 }
279
280 MAKE_INLINE void
281 LazyBuf_Done(LazyBuf *buf)
282 {
283 free(buf->freeIt);
284 }
285
286 MAKE_STATIC void
287 LazyBuf_Add(LazyBuf *buf, char ch)
288 {
289
290 if (buf->data != NULL) {
291 if (buf->len == buf->cap) {
292 buf->cap *= 2;
293 buf->data = bmake_realloc(buf->data, buf->cap);
294 }
295 buf->data[buf->len++] = ch;
296
297 } else if (ch == buf->expected[buf->len]) {
298 buf->len++;
299 return;
300
301 } else {
302 buf->cap = buf->len + 16;
303 buf->data = bmake_malloc(buf->cap);
304 memcpy(buf->data, buf->expected, buf->len);
305 buf->data[buf->len++] = ch;
306 }
307 }
308
309 MAKE_STATIC void
310 LazyBuf_AddStr(LazyBuf *buf, const char *str)
311 {
312 const char *p;
313
314 for (p = str; *p != '\0'; p++)
315 LazyBuf_Add(buf, *p);
316 }
317
318 MAKE_STATIC void
319 LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end)
320 {
321 const char *p;
322
323 for (p = start; p != end; p++)
324 LazyBuf_Add(buf, *p);
325 }
326
327 MAKE_INLINE void
328 LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
329 {
330 LazyBuf_AddBytesBetween(buf, sub.start, sub.end);
331 }
332
333 MAKE_STATIC Substring
334 LazyBuf_Get(const LazyBuf *buf)
335 {
336 const char *start = buf->data != NULL ? buf->data : buf->expected;
337 return Substring_Init(start, start + buf->len);
338 }
339
340 MAKE_STATIC FStr
341 LazyBuf_DoneGet(LazyBuf *buf)
342 {
343 if (buf->data != NULL) {
344 LazyBuf_Add(buf, '\0');
345 return FStr_InitOwn(buf->data);
346 }
347 return Substring_Str(LazyBuf_Get(buf));
348 }
349
350
351 Words Str_Words(const char *, bool);
352
353 MAKE_INLINE void
354 Words_Free(Words w)
355 {
356 free(w.words);
357 free(w.freeIt);
358 }
359
360
361 SubstringWords Substring_Words(const char *, bool);
362
363 MAKE_INLINE void
364 SubstringWords_Init(SubstringWords *w)
365 {
366 w->words = NULL;
367 w->len = 0;
368 w->freeIt = NULL;
369 }
370
371 MAKE_INLINE void
372 SubstringWords_Free(SubstringWords w)
373 {
374 free(w.words);
375 free(w.freeIt);
376 }
377
378
379 char *str_concat2(const char *, const char *);
380 char *str_concat3(const char *, const char *, const char *);
381
382 bool Str_Match(const char *, const char *);
383