blob: a35fe77c451bfbb154c28df9d921d722f0cfde6c [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file pl.c Pointer-length functions
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <ctype.h>
7#include <sys/types.h>
8#ifdef HAVE_STRINGS_H
9#define __EXTENSIONS__ 1
10#include <strings.h>
11#endif
12#include <string.h>
13#include <stdlib.h>
14#include <re_types.h>
15#include <re_mem.h>
16#include <re_mbuf.h>
17#include <re_fmt.h>
18
19
20/** Pointer-length NULL initialiser */
21const struct pl pl_null = {NULL, 0};
22
23
24/**
25 * Initialise a pointer-length object from a NULL-terminated string
26 *
27 * @param pl Pointer-length object to be initialised
28 * @param str NULL-terminated string
29 */
30void pl_set_str(struct pl *pl, const char *str)
31{
32 if (!pl || !str)
33 return;
34
35 pl->p = str;
36 pl->l = strlen(str);
37}
38
39
40/**
41 * Initialise a pointer-length object from current position and
42 * length of a memory buffer
43 *
44 * @param pl Pointer-length object to be initialised
45 * @param mb Memory buffer
46 */
47void pl_set_mbuf(struct pl *pl, const struct mbuf *mb)
48{
49 if (!pl || !mb)
50 return;
51
52 pl->p = (char *)mbuf_buf(mb);
53 pl->l = mbuf_get_left(mb);
54}
55
56
57/**
58 * Convert a pointer-length object to a numeric 32-bit value
59 *
60 * @param pl Pointer-length object
61 *
62 * @return 32-bit value
63 */
64uint32_t pl_u32(const struct pl *pl)
65{
66 uint32_t v=0, mul=1;
67 const char *p;
68
69 if (!pl || !pl->p)
70 return 0;
71
72 p = &pl->p[pl->l];
73 while (p > pl->p) {
74 const uint8_t c = *--p - '0';
75 if (c > 9)
76 return 0;
77 v += mul * c;
78 mul *= 10;
79 }
80
81 return v;
82}
83
84
85/**
86 * Convert a hex pointer-length object to a numeric 32-bit value
87 *
88 * @param pl Pointer-length object
89 *
90 * @return 32-bit value
91 */
92uint32_t pl_x32(const struct pl *pl)
93{
94 uint32_t v=0, mul=1;
95 const char *p;
96
97 if (!pl || !pl->p)
98 return 0;
99
100 p = &pl->p[pl->l];
101 while (p > pl->p) {
102
103 const char ch = *--p;
104 uint8_t c;
105
106 if ('0' <= ch && ch <= '9')
107 c = ch - '0';
108 else if ('A' <= ch && ch <= 'F')
109 c = ch - 'A' + 10;
110 else if ('a' <= ch && ch <= 'f')
111 c = ch - 'a' + 10;
112 else
113 return 0;
114
115 v += mul * c;
116 mul *= 16;
117 }
118
119 return v;
120}
121
122
123/**
124 * Convert a pointer-length object to a numeric 64-bit value
125 *
126 * @param pl Pointer-length object
127 *
128 * @return 64-bit value
129 */
130uint64_t pl_u64(const struct pl *pl)
131{
132 uint64_t v=0, mul=1;
133 const char *p;
134
135 if (!pl || !pl->p)
136 return 0;
137
138 p = &pl->p[pl->l];
139 while (p > pl->p) {
140 const uint8_t c = *--p - '0';
141 if (c > 9)
142 return 0;
143 v += mul * c;
144 mul *= 10;
145 }
146
147 return v;
148}
149
150
151/**
152 * Convert a hex pointer-length object to a numeric 64-bit value
153 *
154 * @param pl Pointer-length object
155 *
156 * @return 64-bit value
157 */
158uint64_t pl_x64(const struct pl *pl)
159{
160 uint64_t v=0, mul=1;
161 const char *p;
162
163 if (!pl || !pl->p)
164 return 0;
165
166 p = &pl->p[pl->l];
167 while (p > pl->p) {
168
169 const char ch = *--p;
170 uint8_t c;
171
172 if ('0' <= ch && ch <= '9')
173 c = ch - '0';
174 else if ('A' <= ch && ch <= 'F')
175 c = ch - 'A' + 10;
176 else if ('a' <= ch && ch <= 'f')
177 c = ch - 'a' + 10;
178 else
179 return 0;
180
181 v += mul * c;
182 mul *= 16;
183 }
184
185 return v;
186}
187
188
189/**
190 * Convert a pointer-length object to floating point representation.
191 * Both positive and negative numbers are supported, a string with a
192 * minus sign ('-') is treated as a negative number.
193 *
194 * @param pl Pointer-length object
195 *
196 * @return Double value
197 */
198double pl_float(const struct pl *pl)
199{
200 double v=0, mul=1;
201 const char *p;
202 bool neg = false;
203
204 if (!pl || !pl->p)
205 return 0;
206
207 p = &pl->p[pl->l];
208
209 while (p > pl->p) {
210
211 const char ch = *--p;
212
213 if ('0' <= ch && ch <= '9') {
214 v += mul * (ch - '0');
215 mul *= 10;
216 }
217 else if (ch == '.') {
218 v /= mul;
219 mul = 1;
220 }
221 else if (ch == '-' && p == pl->p) {
222 neg = true;
223 }
224 else {
225 return 0;
226 }
227 }
228
229 return neg ? -v : v;
230}
231
232
233/**
234 * Check if pointer-length object is set
235 *
236 * @param pl Pointer-length object
237 *
238 * @return true if set, false if not set
239 */
240bool pl_isset(const struct pl *pl)
241{
242 return pl ? pl->p && pl->l : false;
243}
244
245
246/**
247 * Copy a pointer-length object to a NULL-terminated string
248 *
249 * @param pl Pointer-length object
250 * @param str Buffer for NULL-terminated string
251 * @param size Size of buffer
252 *
253 * @return 0 if success, otherwise errorcode
254 */
255int pl_strcpy(const struct pl *pl, char *str, size_t size)
256{
257 size_t len;
258
259 if (!pl || !pl->p || !str || !size)
260 return EINVAL;
261
262 len = min(pl->l, size-1);
263
264 memcpy(str, pl->p, len);
265 str[len] = '\0';
266
267 return 0;
268}
269
270
271/**
272 * Duplicate a pointer-length object to a NULL-terminated string
273 *
274 * @param dst Pointer to destination string (set on return)
275 * @param src Source pointer-length object
276 *
277 * @return 0 if success, otherwise errorcode
278 */
279int pl_strdup(char **dst, const struct pl *src)
280{
281 char *p;
282
283 if (!dst || !src || !src->p)
284 return EINVAL;
285
286 p = mem_alloc(src->l+1, NULL);
287 if (!p)
288 return ENOMEM;
289
290 memcpy(p, src->p, src->l);
291 p[src->l] = '\0';
292
293 *dst = p;
294
295 return 0;
296}
297
298
299/**
300 * Duplicate a pointer-length object to a new pointer-length object
301 *
302 * @param dst Destination pointer-length object (set on return)
303 * @param src Source pointer-length object
304 *
305 * @return 0 if success, otherwise errorcode
306 */
307int pl_dup(struct pl *dst, const struct pl *src)
308{
309 char *p;
310
311 if (!dst || !src || !src->p)
312 return EINVAL;
313
314 p = mem_alloc(src->l, NULL);
315 if (!p)
316 return ENOMEM;
317
318 memcpy(p, src->p, src->l);
319
320 dst->p = p;
321 dst->l = src->l;
322
323 return 0;
324}
325
326
327/**
328 * Compare a pointer-length object with a NULL-terminated string
329 * (case-sensitive)
330 *
331 * @param pl Pointer-length object
332 * @param str NULL-terminated string
333 *
334 * @return 0 if match, otherwise errorcode
335 */
336int pl_strcmp(const struct pl *pl, const char *str)
337{
338 struct pl s;
339
340 if (!pl || !str)
341 return EINVAL;
342
343 pl_set_str(&s, str);
344
345 return pl_cmp(pl, &s);
346}
347
348
349/**
350 * Compare a pointer-length object with a NULL-terminated string
351 * (case-insensitive)
352 *
353 * @param pl Pointer-length object
354 * @param str NULL-terminated string
355 *
356 * @return 0 if match, otherwise errorcode
357 */
358int pl_strcasecmp(const struct pl *pl, const char *str)
359{
360 struct pl s;
361
362 if (!pl || !str)
363 return EINVAL;
364
365 pl_set_str(&s, str);
366
367 return pl_casecmp(pl, &s);
368}
369
370
371/**
372 * Compare two pointer-length objects (case-sensitive)
373 *
374 * @param pl1 First pointer-length object
375 * @param pl2 Second pointer-length object
376 *
377 * @return 0 if match, otherwise errorcode
378 */
379int pl_cmp(const struct pl *pl1, const struct pl *pl2)
380{
381 if (!pl1 || !pl2)
382 return EINVAL;
383
384 /* Different length -> no match */
385 if (pl1->l != pl2->l)
386 return EINVAL;
387
388 /* Zero-length strings are always identical */
389 if (pl1->l == 0)
390 return 0;
391
392 /*
393 * ~35% speed increase for fmt/pl test
394 */
395
396 /* The two pl's are the same */
397 if (pl1 == pl2)
398 return 0;
399
400 /* Two different pl's pointing to same string */
401 if (pl1->p == pl2->p)
402 return 0;
403
404 return 0 == memcmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL;
405}
406
407
408#ifndef HAVE_STRINGS_H
409static int casecmp(const struct pl *pl, const char *str)
410{
411 size_t i = 0;
412
413#define LOWER(d) ((d) | 0x20202020)
414 const uint32_t *p1 = (uint32_t *)pl->p;
415 const uint32_t *p2 = (uint32_t *)str;
416 const size_t len = pl->l & ~0x3;
417
418 /* Skip any unaligned pointers */
419 if (((size_t)pl->p) & (sizeof(void *) - 1))
420 goto next;
421 if (((size_t)str) & (sizeof(void *) - 1))
422 goto next;
423
424 /* Compare word-wise */
425 for (; i<len; i+=4) {
426 if (LOWER(*p1++) != LOWER(*p2++))
427 return EINVAL;
428 }
429
430 next:
431 /* Compare byte-wise */
432 for (; i<pl->l; i++) {
433 if (tolower(pl->p[i]) != tolower(str[i]))
434 return EINVAL;
435 }
436
437 return 0;
438}
439#endif
440
441
442/**
443 * Compare two pointer-length objects (case-insensitive)
444 *
445 * @param pl1 First pointer-length object
446 * @param pl2 Second pointer-length object
447 *
448 * @return 0 if match, otherwise errorcode
449 */
450int pl_casecmp(const struct pl *pl1, const struct pl *pl2)
451{
452 if (!pl1 || !pl2)
453 return EINVAL;
454
455 /* Different length -> no match */
456 if (pl1->l != pl2->l)
457 return EINVAL;
458
459 /* Zero-length strings are always identical */
460 if (pl1->l == 0)
461 return 0;
462
463 /*
464 * ~35% speed increase for fmt/pl test
465 */
466
467 /* The two pl's are the same */
468 if (pl1 == pl2)
469 return 0;
470
471 /* Two different pl's pointing to same string */
472 if (pl1->p == pl2->p)
473 return 0;
474
475#ifdef HAVE_STRINGS_H
476 return 0 == strncasecmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL;
477#else
478 return casecmp(pl1, pl2->p);
479#endif
480}
481
482
483/**
484 * Locate character in pointer-length string
485 *
486 * @param pl Pointer-length string
487 * @param c Character to locate
488 *
489 * @return Pointer to first char if found, otherwise NULL
490 */
491const char *pl_strchr(const struct pl *pl, char c)
492{
493 const char *p, *end;
494
495 if (!pl)
496 return NULL;
497
498 end = pl->p + pl->l;
499 for (p = pl->p; p < end; p++) {
500 if (*p == c)
501 return p;
502 }
503
504 return NULL;
505}