499 lines
8.1 KiB
C
499 lines
8.1 KiB
C
/**
|
|
* @file pl.c Pointer-length functions
|
|
*
|
|
* Copyright (C) 2010 Creytiv.com
|
|
*/
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_STRINGS_H
|
|
#define __EXTENSIONS__ 1
|
|
#include <strings.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <re_types.h>
|
|
#include <re_mem.h>
|
|
#include <re_mbuf.h>
|
|
#include <re_fmt.h>
|
|
|
|
|
|
/** 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 (; i<len; i+=4) {
|
|
if (LOWER(*p1++) != LOWER(*p2++))
|
|
return EINVAL;
|
|
}
|
|
|
|
next:
|
|
/* Compare byte-wise */
|
|
for (; i<pl->l; 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;
|
|
}
|