/** * @file pl.c Pointer-length functions * * Copyright (C) 2010 Creytiv.com */ #include #include #ifdef HAVE_STRINGS_H #define __EXTENSIONS__ 1 #include #endif #include #include #include #include #include #include /** Pointer-length NULL initialiser */ const struct pl pl_null = {NULL, 0}; /** * Initialise a pointer-length object from a NULL-terminated string * * @param pl Pointer-length object to be initialised * @param str NULL-terminated string */ void pl_set_str(struct pl *pl, const char *str) { if (!pl || !str) return; pl->p = str; pl->l = strlen(str); } /** * Initialise a pointer-length object from current position and * length of a memory buffer * * @param pl Pointer-length object to be initialised * @param mb Memory buffer */ void pl_set_mbuf(struct pl *pl, const struct mbuf *mb) { if (!pl || !mb) return; pl->p = (char *)mbuf_buf(mb); pl->l = mbuf_get_left(mb); } /** * Convert a pointer-length object to a numeric 32-bit value * * @param pl Pointer-length object * * @return 32-bit value */ uint32_t pl_u32(const struct pl *pl) { uint32_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const uint8_t c = *--p - '0'; if (c > 9) return 0; v += mul * c; mul *= 10; } return v; } /** * Convert a hex pointer-length object to a numeric 32-bit value * * @param pl Pointer-length object * * @return 32-bit value */ uint32_t pl_x32(const struct pl *pl) { uint32_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; uint8_t c; if ('0' <= ch && ch <= '9') c = ch - '0'; else if ('A' <= ch && ch <= 'F') c = ch - 'A' + 10; else if ('a' <= ch && ch <= 'f') c = ch - 'a' + 10; else return 0; v += mul * c; mul *= 16; } return v; } /** * Convert a pointer-length object to a numeric 64-bit value * * @param pl Pointer-length object * * @return 64-bit value */ uint64_t pl_u64(const struct pl *pl) { uint64_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const uint8_t c = *--p - '0'; if (c > 9) return 0; v += mul * c; mul *= 10; } return v; } /** * Convert a hex pointer-length object to a numeric 64-bit value * * @param pl Pointer-length object * * @return 64-bit value */ uint64_t pl_x64(const struct pl *pl) { uint64_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; uint8_t c; if ('0' <= ch && ch <= '9') c = ch - '0'; else if ('A' <= ch && ch <= 'F') c = ch - 'A' + 10; else if ('a' <= ch && ch <= 'f') c = ch - 'a' + 10; else return 0; v += mul * c; mul *= 16; } return v; } /** * Convert a pointer-length object to floating point representation * * @param pl Pointer-length object * * @return Double value */ double pl_float(const struct pl *pl) { double v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; if ('0' <= ch && ch <= '9') { v += mul * (ch - '0'); mul *= 10; } else if (ch == '.') { v /= mul; mul = 1; } else { return 0; } } return v; } /** * Check if pointer-length object is set * * @param pl Pointer-length object * * @return true if set, false if not set */ bool pl_isset(const struct pl *pl) { return pl ? pl->p && pl->l : false; } /** * Copy a pointer-length object to a NULL-terminated string * * @param pl Pointer-length object * @param str Buffer for NULL-terminated string * @param size Size of buffer * * @return 0 if success, otherwise errorcode */ int pl_strcpy(const struct pl *pl, char *str, size_t size) { size_t len; if (!pl || !pl->p || !str || !size) return EINVAL; len = min(pl->l, size-1); memcpy(str, pl->p, len); str[len] = '\0'; return 0; } /** * Duplicate a pointer-length object to a NULL-terminated string * * @param dst Pointer to destination string (set on return) * @param src Source pointer-length object * * @return 0 if success, otherwise errorcode */ int pl_strdup(char **dst, const struct pl *src) { char *p; if (!dst || !src || !src->p) return EINVAL; p = mem_alloc(src->l+1, NULL); if (!p) return ENOMEM; memcpy(p, src->p, src->l); p[src->l] = '\0'; *dst = p; return 0; } /** * Duplicate a pointer-length object to a new pointer-length object * * @param dst Destination pointer-length object (set on return) * @param src Source pointer-length object * * @return 0 if success, otherwise errorcode */ int pl_dup(struct pl *dst, const struct pl *src) { char *p; if (!dst || !src || !src->p) return EINVAL; p = mem_alloc(src->l, NULL); if (!p) return ENOMEM; memcpy(p, src->p, src->l); dst->p = p; dst->l = src->l; return 0; } /** * Compare a pointer-length object with a NULL-terminated string * (case-sensitive) * * @param pl Pointer-length object * @param str NULL-terminated string * * @return 0 if match, otherwise errorcode */ int pl_strcmp(const struct pl *pl, const char *str) { struct pl s; if (!pl || !str) return EINVAL; pl_set_str(&s, str); return pl_cmp(pl, &s); } /** * Compare a pointer-length object with a NULL-terminated string * (case-insensitive) * * @param pl Pointer-length object * @param str NULL-terminated string * * @return 0 if match, otherwise errorcode */ int pl_strcasecmp(const struct pl *pl, const char *str) { struct pl s; if (!pl || !str) return EINVAL; pl_set_str(&s, str); return pl_casecmp(pl, &s); } /** * Compare two pointer-length objects (case-sensitive) * * @param pl1 First pointer-length object * @param pl2 Second pointer-length object * * @return 0 if match, otherwise errorcode */ int pl_cmp(const struct pl *pl1, const struct pl *pl2) { if (!pl1 || !pl2) return EINVAL; /* Different length -> no match */ if (pl1->l != pl2->l) return EINVAL; /* Zero-length strings are always identical */ if (pl1->l == 0) return 0; /* * ~35% speed increase for fmt/pl test */ /* The two pl's are the same */ if (pl1 == pl2) return 0; /* Two different pl's pointing to same string */ if (pl1->p == pl2->p) return 0; return 0 == memcmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL; } #ifndef HAVE_STRINGS_H static int casecmp(const struct pl *pl, const char *str) { size_t i = 0; #define LOWER(d) ((d) | 0x20202020) const uint32_t *p1 = (uint32_t *)pl->p; const uint32_t *p2 = (uint32_t *)str; const size_t len = pl->l & ~0x3; /* Skip any unaligned pointers */ if (((size_t)pl->p) & (sizeof(void *) - 1)) goto next; if (((size_t)str) & (sizeof(void *) - 1)) goto next; /* Compare word-wise */ for (; il; i++) { if (tolower(pl->p[i]) != tolower(str[i])) return EINVAL; } return 0; } #endif /** * Compare two pointer-length objects (case-insensitive) * * @param pl1 First pointer-length object * @param pl2 Second pointer-length object * * @return 0 if match, otherwise errorcode */ int pl_casecmp(const struct pl *pl1, const struct pl *pl2) { if (!pl1 || !pl2) return EINVAL; /* Different length -> no match */ if (pl1->l != pl2->l) return EINVAL; /* Zero-length strings are always identical */ if (pl1->l == 0) return 0; /* * ~35% speed increase for fmt/pl test */ /* The two pl's are the same */ if (pl1 == pl2) return 0; /* Two different pl's pointing to same string */ if (pl1->p == pl2->p) return 0; #ifdef HAVE_STRINGS_H return 0 == strncasecmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL; #else return casecmp(pl1, pl2->p); #endif } /** * Locate character in pointer-length string * * @param pl Pointer-length string * @param c Character to locate * * @return Pointer to first char if found, otherwise NULL */ const char *pl_strchr(const struct pl *pl, char c) { const char *p, *end; if (!pl) return NULL; end = pl->p + pl->l; for (p = pl->p; p < end; p++) { if (*p == c) return p; } return NULL; }