str.h revision 1.2 1 /* $NetBSD: str.h,v 1.2 2021/04/11 18:44:57 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
74 MAKE_INLINE FStr
75 FStr_Init(const char *str, void *freeIt)
76 {
77 FStr fstr;
78 fstr.str = str;
79 fstr.freeIt = freeIt;
80 return fstr;
81 }
82
83 /* Return a string that is the sole owner of str. */
84 MAKE_INLINE FStr
85 FStr_InitOwn(char *str)
86 {
87 return FStr_Init(str, str);
88 }
89
90 /* Return a string that refers to the shared str. */
91 MAKE_INLINE FStr
92 FStr_InitRefer(const char *str)
93 {
94 return FStr_Init(str, NULL);
95 }
96
97 MAKE_INLINE void
98 FStr_Done(FStr *fstr)
99 {
100 free(fstr->freeIt);
101 #ifdef CLEANUP
102 fstr->str = NULL;
103 fstr->freeIt = NULL;
104 #endif
105 }
106
107
108 MAKE_INLINE MFStr
109 MFStr_Init(char *str, void *freeIt)
110 {
111 MFStr mfstr;
112 mfstr.str = str;
113 mfstr.freeIt = freeIt;
114 return mfstr;
115 }
116
117 /* Return a string that is the sole owner of str. */
118 MAKE_INLINE MFStr
119 MFStr_InitOwn(char *str)
120 {
121 return MFStr_Init(str, str);
122 }
123
124 /* Return a string that refers to the shared str. */
125 MAKE_INLINE MFStr
126 MFStr_InitRefer(char *str)
127 {
128 return MFStr_Init(str, NULL);
129 }
130
131 MAKE_INLINE void
132 MFStr_Done(MFStr *mfstr)
133 {
134 free(mfstr->freeIt);
135 #ifdef CLEANUP
136 mfstr->str = NULL;
137 mfstr->freeIt = NULL;
138 #endif
139 }
140
141
142 MAKE_INLINE Substring
143 Substring_Init(const char *start, const char *end)
144 {
145 Substring sub;
146
147 sub.start = start;
148 sub.end = end;
149 return sub;
150 }
151
152 MAKE_INLINE Substring
153 Substring_InitStr(const char *str)
154 {
155 return Substring_Init(str, str + strlen(str));
156 }
157
158 MAKE_INLINE size_t
159 Substring_Length(Substring sub)
160 {
161 return (size_t)(sub.end - sub.start);
162 }
163
164 MAKE_INLINE bool
165 Substring_IsEmpty(Substring sub)
166 {
167 return sub.start == sub.end;
168 }
169
170 MAKE_INLINE bool
171 Substring_Equals(Substring sub, const char *str)
172 {
173 size_t len = strlen(str);
174 return Substring_Length(sub) == len &&
175 memcmp(sub.start, str, len) == 0;
176 }
177
178 MAKE_INLINE Substring
179 Substring_Sub(Substring sub, size_t start, size_t end)
180 {
181 assert(start <= Substring_Length(sub));
182 assert(end <= Substring_Length(sub));
183 return Substring_Init(sub.start + start, sub.start + end);
184 }
185
186 MAKE_INLINE FStr
187 Substring_Str(Substring sub)
188 {
189 return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
190 }
191
192 MAKE_INLINE const char *
193 Substring_LastIndex(Substring sub, char ch)
194 {
195 const char *p;
196
197 for (p = sub.end; p != sub.start; p--)
198 if (p[-1] == ch)
199 return p - 1;
200 return NULL;
201 }
202
203 MAKE_INLINE Substring
204 Substring_Dirname(Substring pathname)
205 {
206 const char *p;
207
208 for (p = pathname.end; p != pathname.start; p--)
209 if (p[-1] == '/')
210 return Substring_Init(pathname.start, p - 1);
211 return Substring_InitStr(".");
212 }
213
214 MAKE_INLINE Substring
215 Substring_Basename(Substring pathname)
216 {
217 const char *p;
218
219 for (p = pathname.end; p != pathname.start; p--)
220 if (p[-1] == '/')
221 return Substring_Init(p, pathname.end);
222 return pathname;
223 }
224
225
226 MAKE_INLINE void
227 LazyBuf_Init(LazyBuf *buf, Substring expected)
228 {
229 buf->data = NULL;
230 buf->len = 0;
231 buf->cap = 0;
232 buf->expected = expected;
233 buf->freeIt = NULL;
234 }
235
236 MAKE_INLINE void
237 LazyBuf_Done(LazyBuf *buf)
238 {
239 free(buf->freeIt);
240 }
241
242 MAKE_INLINE void
243 LazyBuf_Add(LazyBuf *buf, char ch)
244 {
245
246 if (buf->data != NULL) {
247 if (buf->len == buf->cap) {
248 buf->cap *= 2;
249 buf->data = bmake_realloc(buf->data, buf->cap);
250 }
251 buf->data[buf->len++] = ch;
252
253 } else if (buf->len < Substring_Length(buf->expected) &&
254 ch == buf->expected.start[buf->len]) {
255 buf->len++;
256 return;
257
258 } else {
259 buf->cap = buf->len + 16;
260 buf->data = bmake_malloc(buf->cap);
261 memcpy(buf->data, buf->expected.start, buf->len);
262 buf->data[buf->len++] = ch;
263 }
264 }
265
266 MAKE_INLINE void
267 LazyBuf_AddStr(LazyBuf *buf, const char *str)
268 {
269 const char *p;
270
271 for (p = str; *p != '\0'; p++)
272 LazyBuf_Add(buf, *p);
273 }
274
275 MAKE_INLINE Substring
276 LazyBuf_Get(const LazyBuf *buf)
277 {
278 const char *start = buf->data != NULL
279 ? buf->data : buf->expected.start;
280 return Substring_Init(start, start + buf->len);
281 }
282
283 MAKE_INLINE FStr
284 LazyBuf_DoneGet(LazyBuf *buf)
285 {
286 if (buf->data != NULL) {
287 LazyBuf_Add(buf, '\0');
288 return FStr_InitOwn(buf->data);
289 }
290 return Substring_Str(LazyBuf_Get(buf));
291 }
292
293
294 Words Str_Words(const char *, bool);
295
296 MAKE_INLINE void
297 Words_Free(Words w)
298 {
299 free(w.words);
300 free(w.freeIt);
301 }
302
303
304 char *str_concat2(const char *, const char *);
305 char *str_concat3(const char *, const char *, const char *);
306 char *str_concat4(const char *, const char *, const char *, const char *);
307
308 bool Str_Match(const char *, const char *);
309