Home | History | Annotate | Line # | Download | only in make
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