str.h revision 1.3 1 /* $NetBSD: str.h,v 1.3 2021/04/11 19:05:06 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 {
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 Substring 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_INLINE 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_INLINE size_t
166 Substring_Length(Substring sub)
167 {
168 return (size_t)(sub.end - sub.start);
169 }
170
171 MAKE_INLINE 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 Substring
186 Substring_Sub(Substring sub, size_t start, size_t end)
187 {
188 assert(start <= Substring_Length(sub));
189 assert(end <= Substring_Length(sub));
190 return Substring_Init(sub.start + start, sub.start + end);
191 }
192
193 MAKE_INLINE FStr
194 Substring_Str(Substring sub)
195 {
196 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
197 }
198
199 MAKE_INLINE const char *
200 Substring_LastIndex(Substring sub, char ch)
201 {
202 const char *p;
203
204 for (p = sub.end; p != sub.start; p--)
205 if (p[-1] == ch)
206 return p - 1;
207 return NULL;
208 }
209
210 MAKE_INLINE Substring
211 Substring_Dirname(Substring pathname)
212 {
213 const char *p;
214
215 for (p = pathname.end; p != pathname.start; p--)
216 if (p[-1] == '/')
217 return Substring_Init(pathname.start, p - 1);
218 return Substring_InitStr(".");
219 }
220
221 MAKE_INLINE Substring
222 Substring_Basename(Substring pathname)
223 {
224 const char *p;
225
226 for (p = pathname.end; p != pathname.start; p--)
227 if (p[-1] == '/')
228 return Substring_Init(p, pathname.end);
229 return pathname;
230 }
231
232
233 MAKE_INLINE void
234 LazyBuf_Init(LazyBuf *buf, Substring expected)
235 {
236 buf->data = NULL;
237 buf->len = 0;
238 buf->cap = 0;
239 buf->expected = expected;
240 buf->freeIt = NULL;
241 }
242
243 MAKE_INLINE void
244 LazyBuf_Done(LazyBuf *buf)
245 {
246 free(buf->freeIt);
247 }
248
249 MAKE_INLINE void
250 LazyBuf_Add(LazyBuf *buf, char ch)
251 {
252
253 if (buf->data != NULL) {
254 if (buf->len == buf->cap) {
255 buf->cap *= 2;
256 buf->data = bmake_realloc(buf->data, buf->cap);
257 }
258 buf->data[buf->len++] = ch;
259
260 } else if (buf->len < Substring_Length(buf->expected) &&
261 ch == buf->expected.start[buf->len]) {
262 buf->len++;
263 return;
264
265 } else {
266 buf->cap = buf->len + 16;
267 buf->data = bmake_malloc(buf->cap);
268 memcpy(buf->data, buf->expected.start, buf->len);
269 buf->data[buf->len++] = ch;
270 }
271 }
272
273 MAKE_INLINE void
274 LazyBuf_AddStr(LazyBuf *buf, const char *str)
275 {
276 const char *p;
277
278 for (p = str; *p != '\0'; p++)
279 LazyBuf_Add(buf, *p);
280 }
281
282 MAKE_INLINE Substring
283 LazyBuf_Get(const LazyBuf *buf)
284 {
285 const char *start = buf->data != NULL
286 ? buf->data : buf->expected.start;
287 return Substring_Init(start, start + buf->len);
288 }
289
290 MAKE_INLINE FStr
291 LazyBuf_DoneGet(LazyBuf *buf)
292 {
293 if (buf->data != NULL) {
294 LazyBuf_Add(buf, '\0');
295 return FStr_InitOwn(buf->data);
296 }
297 return Substring_Str(LazyBuf_Get(buf));
298 }
299
300
301 Words Str_Words(const char *, bool);
302
303 MAKE_INLINE void
304 Words_Free(Words w)
305 {
306 free(w.words);
307 free(w.freeIt);
308 }
309
310
311 SubstringWords Substring_Words(const char *, bool);
312
313 MAKE_INLINE void
314 SubstringWords_Free(SubstringWords w)
315 {
316 free(w.words);
317 free(w.freeIt);
318 }
319
320
321 char *str_concat2(const char *, const char *);
322 char *str_concat3(const char *, const char *, const char *);
323 char *str_concat4(const char *, const char *, const char *, const char *);
324
325 bool Str_Match(const char *, const char *);
326