1
0
Fork 0
mirror of https://github.com/albfan/miraclecast.git synced 2025-03-09 23:38:56 +00:00

Move shared helpers into ./src/shared/

Move all shared helpers into a separate subdirectory and rename to
libmiracle-shared.la.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
This commit is contained in:
David Herrmann 2014-02-18 13:56:12 +01:00
parent fc30d8b920
commit 47539226cd
9 changed files with 19 additions and 20 deletions

138
src/shared/shl_dlist.h Normal file
View file

@ -0,0 +1,138 @@
/*
* SHL - Double Linked List
*
* Copyright (c) 2010-2013 David Herrmann <dh.herrmann@gmail.com>
* Dedicated to the Public Domain
*/
/*
* A simple double linked list implementation
* This list API does not provide type-safety! It is a simple circular
* double-linked list. Objects need to embed "struct shl_dlist". This is used to
* link and iterate a list. You can get the object back from a shl_dlist pointer
* via shl_dlist_entry(). This breaks any type-safety, though. You need to make
* sure you call this on the right objects.
*/
#ifndef SHL_DLIST_H
#define SHL_DLIST_H
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
/* miscellaneous */
#define shl_dlist_offsetof(pointer, type, member) ({ \
const typeof(((type*)0)->member) *__ptr = (pointer); \
(type*)(((char*)__ptr) - offsetof(type, member)); \
})
/* double linked list */
struct shl_dlist {
struct shl_dlist *next;
struct shl_dlist *prev;
};
#define SHL_DLIST_INIT(head) { &(head), &(head) }
static inline void shl_dlist_init(struct shl_dlist *list)
{
list->next = list;
list->prev = list;
}
static inline void shl_dlist__link(struct shl_dlist *prev,
struct shl_dlist *next,
struct shl_dlist *n)
{
next->prev = n;
n->next = next;
n->prev = prev;
prev->next = n;
}
static inline void shl_dlist_link(struct shl_dlist *head,
struct shl_dlist *n)
{
return shl_dlist__link(head, head->next, n);
}
static inline void shl_dlist_link_tail(struct shl_dlist *head,
struct shl_dlist *n)
{
return shl_dlist__link(head->prev, head, n);
}
static inline void shl_dlist__unlink(struct shl_dlist *prev,
struct shl_dlist *next)
{
next->prev = prev;
prev->next = next;
}
static inline void shl_dlist_unlink(struct shl_dlist *e)
{
shl_dlist__unlink(e->prev, e->next);
e->prev = NULL;
e->next = NULL;
}
static inline bool shl_dlist_empty(struct shl_dlist *head)
{
return head->next == head;
}
static inline struct shl_dlist *shl_dlist_first(struct shl_dlist *head)
{
return head->next;
}
static inline struct shl_dlist *shl_dlist_last(struct shl_dlist *head)
{
return head->prev;
}
#define shl_dlist_entry(ptr, type, member) \
shl_dlist_offsetof((ptr), type, member)
#define shl_dlist_first_entry(head, type, member) \
shl_dlist_entry(shl_dlist_first(head), type, member)
#define shl_dlist_last_entry(head, type, member) \
shl_dlist_entry(shl_dlist_last(head), type, member)
#define shl_dlist_for_each(iter, head) \
for (iter = (head)->next; iter != (head); iter = iter->next)
#define shl_dlist_for_each_but_one(iter, start, head) \
for (iter = ((start)->next == (head)) ? \
(start)->next->next : \
(start)->next; \
iter != (start); \
iter = (iter->next == (head) && (start) != (head)) ? \
iter->next->next : \
iter->next)
#define shl_dlist_for_each_safe(iter, tmp, head) \
for (iter = (head)->next, tmp = iter->next; iter != (head); \
iter = tmp, tmp = iter->next)
#define shl_dlist_for_each_reverse(iter, head) \
for (iter = (head)->prev; iter != (head); iter = iter->prev)
#define shl_dlist_for_each_reverse_but_one(iter, start, head) \
for (iter = ((start)->prev == (head)) ? \
(start)->prev->prev : \
(start)->prev; \
iter != (start); \
iter = (iter->prev == (head) && (start) != (head)) ? \
iter->prev->prev : \
iter->prev)
#define shl_dlist_for_each_reverse_safe(iter, tmp, head) \
for (iter = (head)->prev, tmp = iter->prev; iter != (head); \
iter = tmp, tmp = iter->prev)
#endif /* SHL_DLIST_H */

464
src/shared/shl_htable.c Normal file
View file

@ -0,0 +1,464 @@
/*
* SHL - Dynamic hash-table
*
* Written-by: Rusty Russell <rusty@rustcorp.com.au>
* Adjusted-by: David Herrmann <dh.herrmann@gmail.com>
* Licensed under LGPLv2+ - see LICENSE_htable file for details
*/
/*
* Please see ccan/htable/_info at:
* https://github.com/rustyrussell/ccan/tree/master/ccan/htable
* for information on the hashtable algorithm. This file copies the code inline
* and is released under the same conditions.
*
* At the end of the file you can find some helpers to use this htable to store
* objects with "unsigned long" or "char*" keys.
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "shl_htable.h"
#define COLD __attribute__((cold))
struct htable {
/* KEEP IN SYNC WITH "struct shl_htable_int" */
size_t (*rehash)(const void *elem, void *priv);
void *priv;
unsigned int bits;
size_t elems, deleted, max, max_with_deleted;
/* These are the bits which are the same in all pointers. */
uintptr_t common_mask, common_bits;
uintptr_t perfect_bit;
uintptr_t *table;
};
#define HTABLE_INITIALIZER(name, rehash, priv) \
{ rehash, priv, 0, 0, 0, 0, 0, -1, 0, 0, &name.perfect_bit }
struct htable_iter {
size_t off;
};
/*
* INLINE COPY OF ccan/htable.c
*/
/* We use 0x1 as deleted marker. */
#define HTABLE_DELETED (0x1)
/* We clear out the bits which are always the same, and put metadata there. */
static inline uintptr_t get_extra_ptr_bits(const struct htable *ht,
uintptr_t e)
{
return e & ht->common_mask;
}
static inline void *get_raw_ptr(const struct htable *ht, uintptr_t e)
{
return (void *)((e & ~ht->common_mask) | ht->common_bits);
}
static inline uintptr_t make_hval(const struct htable *ht,
const void *p, uintptr_t bits)
{
return ((uintptr_t)p & ~ht->common_mask) | bits;
}
static inline bool entry_is_valid(uintptr_t e)
{
return e > HTABLE_DELETED;
}
static inline uintptr_t get_hash_ptr_bits(const struct htable *ht,
size_t hash)
{
/* Shuffling the extra bits (as specified in mask) down the
* end is quite expensive. But the lower bits are redundant, so
* we fold the value first. */
return (hash ^ (hash >> ht->bits))
& ht->common_mask & ~ht->perfect_bit;
}
static void htable_init(struct htable *ht,
size_t (*rehash)(const void *elem, void *priv),
void *priv)
{
struct htable empty = HTABLE_INITIALIZER(empty, NULL, NULL);
*ht = empty;
ht->rehash = rehash;
ht->priv = priv;
ht->table = &ht->perfect_bit;
}
static void htable_clear(struct htable *ht,
void (*free_cb) (void *entry, void *ctx),
void *ctx)
{
size_t i;
if (ht->table != &ht->perfect_bit) {
if (free_cb) {
for (i = 0; i < (size_t)1 << ht->bits; ++i) {
if (entry_is_valid(ht->table[i]))
free_cb(get_raw_ptr(ht, ht->table[i]),
ctx);
}
}
free((void *)ht->table);
}
htable_init(ht, ht->rehash, ht->priv);
}
size_t shl_htable_this_or_next(struct shl_htable *htable, size_t i)
{
struct htable *ht = (void*)&htable->htable;
if (ht->table != &ht->perfect_bit)
for ( ; i < (size_t)1 << ht->bits; ++i)
if (entry_is_valid(ht->table[i]))
return i;
return SIZE_MAX;
}
void *shl_htable_get_entry(struct shl_htable *htable, size_t i)
{
struct htable *ht = (void*)&htable->htable;
if (i < (size_t)1 << ht->bits)
if (entry_is_valid(ht->table[i]))
return get_raw_ptr(ht, ht->table[i]);
return NULL;
}
static void htable_visit(struct htable *ht,
void (*visit_cb) (void *elem, void *ctx),
void *ctx)
{
size_t i;
if (visit_cb && ht->table != &ht->perfect_bit) {
for (i = 0; i < (size_t)1 << ht->bits; ++i) {
if (entry_is_valid(ht->table[i]))
visit_cb(get_raw_ptr(ht, ht->table[i]), ctx);
}
}
}
static size_t hash_bucket(const struct htable *ht, size_t h)
{
return h & ((1 << ht->bits)-1);
}
static void *htable_val(const struct htable *ht,
struct htable_iter *i, size_t hash, uintptr_t perfect)
{
uintptr_t h2 = get_hash_ptr_bits(ht, hash) | perfect;
while (ht->table[i->off]) {
if (ht->table[i->off] != HTABLE_DELETED) {
if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2)
return get_raw_ptr(ht, ht->table[i->off]);
}
i->off = (i->off + 1) & ((1 << ht->bits)-1);
h2 &= ~perfect;
}
return NULL;
}
static void *htable_firstval(const struct htable *ht,
struct htable_iter *i, size_t hash)
{
i->off = hash_bucket(ht, hash);
return htable_val(ht, i, hash, ht->perfect_bit);
}
static void *htable_nextval(const struct htable *ht,
struct htable_iter *i, size_t hash)
{
i->off = (i->off + 1) & ((1 << ht->bits)-1);
return htable_val(ht, i, hash, 0);
}
/* This does not expand the hash table, that's up to caller. */
static void ht_add(struct htable *ht, const void *new, size_t h)
{
size_t i;
uintptr_t perfect = ht->perfect_bit;
i = hash_bucket(ht, h);
while (entry_is_valid(ht->table[i])) {
perfect = 0;
i = (i + 1) & ((1 << ht->bits)-1);
}
ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
}
static COLD bool double_table(struct htable *ht)
{
unsigned int i;
size_t oldnum = (size_t)1 << ht->bits;
uintptr_t *oldtable, e;
oldtable = ht->table;
ht->table = calloc(1 << (ht->bits+1), sizeof(size_t));
if (!ht->table) {
ht->table = oldtable;
return false;
}
ht->bits++;
ht->max = ((size_t)3 << ht->bits) / 4;
ht->max_with_deleted = ((size_t)9 << ht->bits) / 10;
/* If we lost our "perfect bit", get it back now. */
if (!ht->perfect_bit && ht->common_mask) {
for (i = 0; i < sizeof(ht->common_mask) * CHAR_BIT; i++) {
if (ht->common_mask & ((size_t)1 << i)) {
ht->perfect_bit = (size_t)1 << i;
break;
}
}
}
if (oldtable != &ht->perfect_bit) {
for (i = 0; i < oldnum; i++) {
if (entry_is_valid(e = oldtable[i])) {
void *p = get_raw_ptr(ht, e);
ht_add(ht, p, ht->rehash(p, ht->priv));
}
}
free(oldtable);
}
ht->deleted = 0;
return true;
}
static COLD void rehash_table(struct htable *ht)
{
size_t start, i;
uintptr_t e;
/* Beware wrap cases: we need to start from first empty bucket. */
for (start = 0; ht->table[start]; start++);
for (i = 0; i < (size_t)1 << ht->bits; i++) {
size_t h = (i + start) & ((1 << ht->bits)-1);
e = ht->table[h];
if (!e)
continue;
if (e == HTABLE_DELETED)
ht->table[h] = 0;
else if (!(e & ht->perfect_bit)) {
void *p = get_raw_ptr(ht, e);
ht->table[h] = 0;
ht_add(ht, p, ht->rehash(p, ht->priv));
}
}
ht->deleted = 0;
}
/* We stole some bits, now we need to put them back... */
static COLD void update_common(struct htable *ht, const void *p)
{
unsigned int i;
uintptr_t maskdiff, bitsdiff;
if (ht->elems == 0) {
/* Always reveal one bit of the pointer in the bucket,
* so it's not zero or HTABLE_DELETED (1), even if
* hash happens to be 0. Assumes (void *)1 is not a
* valid pointer. */
for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) {
if ((uintptr_t)p & ((uintptr_t)1 << i))
break;
}
ht->common_mask = ~((uintptr_t)1 << i);
ht->common_bits = ((uintptr_t)p & ht->common_mask);
ht->perfect_bit = 1;
return;
}
/* Find bits which are unequal to old common set. */
maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask);
/* These are the bits which go there in existing entries. */
bitsdiff = ht->common_bits & maskdiff;
for (i = 0; i < (size_t)1 << ht->bits; i++) {
if (!entry_is_valid(ht->table[i]))
continue;
/* Clear the bits no longer in the mask, set them as
* expected. */
ht->table[i] &= ~maskdiff;
ht->table[i] |= bitsdiff;
}
/* Take away those bits from our mask, bits and perfect bit. */
ht->common_mask &= ~maskdiff;
ht->common_bits &= ~maskdiff;
ht->perfect_bit &= ~maskdiff;
}
static bool htable_add(struct htable *ht, size_t hash, const void *p)
{
if (ht->elems+1 > ht->max && !double_table(ht))
return false;
if (ht->elems+1 + ht->deleted > ht->max_with_deleted)
rehash_table(ht);
assert(p);
if (((uintptr_t)p & ht->common_mask) != ht->common_bits)
update_common(ht, p);
ht_add(ht, p, hash);
ht->elems++;
return true;
}
static void htable_delval(struct htable *ht, struct htable_iter *i)
{
assert(i->off < (size_t)1 << ht->bits);
assert(entry_is_valid(ht->table[i->off]));
ht->elems--;
ht->table[i->off] = HTABLE_DELETED;
ht->deleted++;
}
/*
* Wrapper code to make it easier to use this hash-table as map.
*/
void shl_htable_init(struct shl_htable *htable,
bool (*compare) (const void *a, const void *b),
size_t (*rehash)(const void *elem, void *priv),
void *priv)
{
struct htable *ht = (void*)&htable->htable;
htable->compare = compare;
htable_init(ht, rehash, priv);
}
void shl_htable_clear(struct shl_htable *htable,
void (*free_cb) (void *elem, void *ctx),
void *ctx)
{
struct htable *ht = (void*)&htable->htable;
htable_clear(ht, free_cb, ctx);
}
void shl_htable_visit(struct shl_htable *htable,
void (*visit_cb) (void *elem, void *ctx),
void *ctx)
{
struct htable *ht = (void*)&htable->htable;
htable_visit(ht, visit_cb, ctx);
}
bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash,
void **out)
{
struct htable *ht = (void*)&htable->htable;
struct htable_iter i;
void *c;
for (c = htable_firstval(ht, &i, hash);
c;
c = htable_nextval(ht, &i, hash)) {
if (htable->compare(obj, c)) {
if (out)
*out = c;
return true;
}
}
return false;
}
int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash)
{
struct htable *ht = (void*)&htable->htable;
bool b;
b = htable_add(ht, hash, (void*)obj);
return b ? 0 : -ENOMEM;
}
bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash,
void **out)
{
struct htable *ht = (void*)&htable->htable;
struct htable_iter i;
void *c;
for (c = htable_firstval(ht, &i, hash);
c;
c = htable_nextval(ht, &i, hash)) {
if (htable->compare(obj, c)) {
if (out)
*out = c;
htable_delval(ht, &i);
return true;
}
}
return false;
}
/*
* Helpers
*/
bool shl_htable_compare_uint(const void *a, const void *b)
{
return *(const unsigned int*)a == *(const unsigned int*)b;
}
size_t shl_htable_rehash_uint(const void *elem, void *priv)
{
return (size_t)*(const unsigned int*)elem;
}
bool shl_htable_compare_ulong(const void *a, const void *b)
{
return *(const unsigned long*)a == *(const unsigned long*)b;
}
size_t shl_htable_rehash_ulong(const void *elem, void *priv)
{
return (size_t)*(const unsigned long*)elem;
}
bool shl_htable_compare_str(const void *a, const void *b)
{
if (!*(char**)a || !*(char**)b)
return *(char**)a == *(char**)b;
else
return !strcmp(*(char**)a, *(char**)b);
}
/* DJB's hash function */
size_t shl_htable_rehash_str(const void *elem, void *priv)
{
const char *str = *(char**)elem;
size_t hash = 5381;
for ( ; str && *str; ++str)
hash = (hash << 5) + hash + (size_t)*str;
return hash;
}

304
src/shared/shl_htable.h Normal file
View file

@ -0,0 +1,304 @@
/*
* SHL - Dynamic hash-table
*
* Copyright (c) 2010-2013 David Herrmann <dh.herrmann@gmail.com>
* Licensed under LGPLv2+ - see LICENSE_htable file for details
*/
/*
* Dynamic hash-table
* Implementation of a self-resizing hashtable to store arbitrary objects.
* Entries are not allocated by the table itself but are user-allocated. A
* single entry can be stored multiple times in the hashtable. No
* maintenance-members need to be embedded in user-allocated objects. However,
* the key (and optionally the hash) must be stored in the objects.
*
* Uses internally the htable from CCAN. See LICENSE_htable.
*/
#ifndef SHL_HTABLE_H
#define SHL_HTABLE_H
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/* miscellaneous */
#define shl_htable_offsetof(pointer, type, member) ({ \
const typeof(((type*)0)->member) *__ptr = (pointer); \
(type*)(((char*)__ptr) - offsetof(type, member)); \
})
/* htable */
struct shl_htable_int {
size_t (*rehash)(const void *elem, void *priv);
void *priv;
unsigned int bits;
size_t elems, deleted, max, max_with_deleted;
/* These are the bits which are the same in all pointers. */
uintptr_t common_mask, common_bits;
uintptr_t perfect_bit;
uintptr_t *table;
};
struct shl_htable {
bool (*compare) (const void *a, const void *b);
struct shl_htable_int htable;
};
#define SHL_HTABLE_INIT(_obj, _compare, _rehash, _priv) \
{ \
.compare = (_compare), \
.htable = { \
.rehash = (_rehash), \
.priv = (_priv), \
.bits = 0, \
.elems = 0, \
.deleted = 0, \
.max = 0, \
.max_with_deleted = 0, \
.common_mask = -1, \
.common_bits = 0, \
.perfect_bit = 0, \
.table = &(_obj).htable.perfect_bit \
} \
}
void shl_htable_init(struct shl_htable *htable,
bool (*compare) (const void *a, const void *b),
size_t (*rehash)(const void *elem, void *priv),
void *priv);
void shl_htable_clear(struct shl_htable *htable,
void (*free_cb) (void *elem, void *ctx),
void *ctx);
void shl_htable_visit(struct shl_htable *htable,
void (*visit_cb) (void *elem, void *ctx),
void *ctx);
bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash,
void **out);
int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash);
bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash,
void **out);
size_t shl_htable_this_or_next(struct shl_htable *htable, size_t i);
void *shl_htable_get_entry(struct shl_htable *htable, size_t i);
#define SHL_HTABLE_FOREACH(_iter, _ht) for ( \
size_t htable__i = shl_htable_this_or_next((_ht), 0); \
(_iter = shl_htable_get_entry((_ht), htable__i)); \
htable__i = shl_htable_this_or_next((_ht), htable__i + 1) \
)
#define SHL_HTABLE_FOREACH_MACRO(_iter, _ht, _accessor) for ( \
size_t htable__i = shl_htable_this_or_next((_ht), 0); \
(_iter = shl_htable_get_entry((_ht), htable__i), \
_iter = _iter ? _accessor((void*)_iter) : NULL); \
htable__i = shl_htable_this_or_next((_ht), htable__i + 1) \
)
#define SHL_HTABLE_FIRST(_ht) \
shl_htable_get_entry((_ht), shl_htable_this_or_next((_ht), 0))
#define SHL_HTABLE_FIRST_MACRO(_ht, _accessor) ({ \
void *htable__i = shl_htable_get_entry((_ht), \
shl_htable_this_or_next((_ht), 0)); \
htable__i ? _accessor(htable__i) : NULL; })
/* uint htables */
#if SIZE_MAX < UINT_MAX
# error "'size_t' is smaller than 'unsigned int'"
#endif
bool shl_htable_compare_uint(const void *a, const void *b);
size_t shl_htable_rehash_uint(const void *elem, void *priv);
#define SHL_HTABLE_INIT_UINT(_obj) \
SHL_HTABLE_INIT((_obj), shl_htable_compare_uint, \
shl_htable_rehash_uint, \
NULL)
static inline void shl_htable_init_uint(struct shl_htable *htable)
{
shl_htable_init(htable, shl_htable_compare_uint,
shl_htable_rehash_uint, NULL);
}
static inline void shl_htable_clear_uint(struct shl_htable *htable,
void (*cb) (unsigned int *elem,
void *ctx),
void *ctx)
{
shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx);
}
static inline void shl_htable_visit_uint(struct shl_htable *htable,
void (*cb) (unsigned int *elem,
void *ctx),
void *ctx)
{
shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx);
}
static inline bool shl_htable_lookup_uint(struct shl_htable *htable,
unsigned int key,
unsigned int **out)
{
return shl_htable_lookup(htable, (const void*)&key, (size_t)key,
(void**)out);
}
static inline int shl_htable_insert_uint(struct shl_htable *htable,
const unsigned int *key)
{
return shl_htable_insert(htable, (const void*)key, (size_t)*key);
}
static inline bool shl_htable_remove_uint(struct shl_htable *htable,
unsigned int key,
unsigned int **out)
{
return shl_htable_remove(htable, (const void*)&key, (size_t)key,
(void**)out);
}
/* ulong htables */
#if SIZE_MAX < ULONG_MAX
# error "'size_t' is smaller than 'unsigned long'"
#endif
bool shl_htable_compare_ulong(const void *a, const void *b);
size_t shl_htable_rehash_ulong(const void *elem, void *priv);
#define SHL_HTABLE_INIT_ULONG(_obj) \
SHL_HTABLE_INIT((_obj), shl_htable_compare_ulong, \
shl_htable_rehash_ulong, \
NULL)
static inline void shl_htable_init_ulong(struct shl_htable *htable)
{
shl_htable_init(htable, shl_htable_compare_ulong,
shl_htable_rehash_ulong, NULL);
}
static inline void shl_htable_clear_ulong(struct shl_htable *htable,
void (*cb) (unsigned long *elem,
void *ctx),
void *ctx)
{
shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx);
}
static inline void shl_htable_visit_ulong(struct shl_htable *htable,
void (*cb) (unsigned long *elem,
void *ctx),
void *ctx)
{
shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx);
}
static inline bool shl_htable_lookup_ulong(struct shl_htable *htable,
unsigned long key,
unsigned long **out)
{
return shl_htable_lookup(htable, (const void*)&key, (size_t)key,
(void**)out);
}
static inline int shl_htable_insert_ulong(struct shl_htable *htable,
const unsigned long *key)
{
return shl_htable_insert(htable, (const void*)key, (size_t)*key);
}
static inline bool shl_htable_remove_ulong(struct shl_htable *htable,
unsigned long key,
unsigned long **out)
{
return shl_htable_remove(htable, (const void*)&key, (size_t)key,
(void**)out);
}
/* string htables */
bool shl_htable_compare_str(const void *a, const void *b);
size_t shl_htable_rehash_str(const void *elem, void *priv);
#define SHL_HTABLE_INIT_STR(_obj) \
SHL_HTABLE_INIT((_obj), shl_htable_compare_str, \
shl_htable_rehash_str, \
NULL)
static inline void shl_htable_init_str(struct shl_htable *htable)
{
shl_htable_init(htable, shl_htable_compare_str,
shl_htable_rehash_str, NULL);
}
static inline void shl_htable_clear_str(struct shl_htable *htable,
void (*cb) (char **elem,
void *ctx),
void *ctx)
{
shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx);
}
static inline void shl_htable_visit_str(struct shl_htable *htable,
void (*cb) (char **elem,
void *ctx),
void *ctx)
{
shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx);
}
static inline size_t shl_htable_hash_str(struct shl_htable *htable,
const char *str, size_t *hash)
{
size_t h;
if (hash && *hash) {
h = *hash;
} else {
h = htable->htable.rehash((const void*)&str, NULL);
if (hash)
*hash = h;
}
return h;
}
static inline bool shl_htable_lookup_str(struct shl_htable *htable,
const char *str, size_t *hash,
char ***out)
{
size_t h;
h = shl_htable_hash_str(htable, str, hash);
return shl_htable_lookup(htable, (const void*)&str, h, (void**)out);
}
static inline int shl_htable_insert_str(struct shl_htable *htable,
char **str, size_t *hash)
{
size_t h;
h = shl_htable_hash_str(htable, *str, hash);
return shl_htable_insert(htable, (const void*)str, h);
}
static inline bool shl_htable_remove_str(struct shl_htable *htable,
const char *str, size_t *hash,
char ***out)
{
size_t h;
h = shl_htable_hash_str(htable, str, hash);
return shl_htable_remove(htable, (const void*)&str, h, (void **)out);
}
#endif /* SHL_HTABLE_H */

239
src/shared/shl_log.c Normal file
View file

@ -0,0 +1,239 @@
/*
* SHL - Log/Debug Interface
*
* Copyright (c) 2010-2013 David Herrmann <dh.herrmann@gmail.com>
* Dedicated to the Public Domain
*/
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "shl_log.h"
/*
* Locking
* Dummies to implement locking. If we ever want lock-protected logging, these
* need to be provided by the user.
*/
static inline void log_lock()
{
}
static inline void log_unlock()
{
}
/*
* Time Management
* We print seconds and microseconds since application start for each
* log-message in case log_init_time() has been called.
*/
static struct timeval log__ftime;
static bool log__have_time(void)
{
return !(log__ftime.tv_sec == 0 && log__ftime.tv_usec == 0);
}
void log_init_time(void)
{
if (!log__have_time())
gettimeofday(&log__ftime, NULL);
}
static void log__time(long long *sec, long long *usec)
{
struct timeval t;
/* In case this is called in parallel to log_init_time(), we need to
* catch negative time-diffs. Other than that, this can be called
* unlocked. */
gettimeofday(&t, NULL);
*sec = t.tv_sec - log__ftime.tv_sec;
*usec = (long long)t.tv_usec - (long long)log__ftime.tv_usec;
if (*usec < 0) {
*sec -= 1;
if (*sec < 0)
*sec = 0;
*usec = 1000000 + *usec;
}
}
/*
* Default Values
* Several logging-parameters may be omitted by applications. To provide sane
* default values we provide constants here.
*
* LOG_SUBSYSTEM: By default no subsystem is specified
*/
const char *LOG_SUBSYSTEM = NULL;
/*
* Max Severity
* Messages with severities between log_max_sev and LOG_SEV_NUM (exclusive)
* are not logged, but discarded.
*/
unsigned int log_max_sev = LOG_NOTICE;
/*
* Forward declaration so we can use the locked-versions in other functions
* here. Be careful to avoid deadlocks, though.
* Also set default log-subsystem to "log" for all logging inside this API.
*/
static void log__submit(const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
va_list args);
#define LOG_SUBSYSTEM "log"
/*
* Basic logger
* The log__submit function writes the message into the current log-target. It
* must be called with log__mutex locked.
* By default the current time elapsed since the first message was logged is
* prepended to the message. file, line and func information are appended to the
* message if sev == LOG_DEBUG.
* The subsystem, if not NULL, is prepended as "SUBS: " to the message and a
* newline is always appended by default. Multiline-messages are not allowed.
*/
static const char *log__sev2str[LOG_SEV_NUM] = {
[LOG_DEBUG] = "DEBUG",
[LOG_INFO] = "INFO",
[LOG_NOTICE] = "NOTICE",
[LOG_WARNING] = "WARNING",
[LOG_ERROR] = "ERROR",
[LOG_CRITICAL] = "CRITICAL",
[LOG_ALERT] = "ALERT",
[LOG_FATAL] = "FATAL",
};
static void log__submit(const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
va_list args)
{
int saved_errno = errno;
const char *prefix = NULL;
FILE *out;
long long sec, usec;
out = stderr;
log__time(&sec, &usec);
if (sev < LOG_SEV_NUM && sev > log_max_sev)
return;
if (sev < LOG_SEV_NUM)
prefix = log__sev2str[sev];
if (prefix) {
if (subs) {
if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] %s: %s: ",
sec, usec, prefix, subs);
else
fprintf(out, "%s: %s: ", prefix, subs);
} else {
if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] %s: ",
sec, usec, prefix);
else
fprintf(out, "%s: ", prefix);
}
} else {
if (subs) {
if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] %s: ",
sec, usec, subs);
else
fprintf(out, "%s: ", subs);
} else {
if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] ", sec, usec);
}
}
errno = saved_errno;
vfprintf(out, format, args);
if (sev == LOG_DEBUG || sev <= LOG_WARNING) {
if (!func)
func = "<unknown>";
if (!file)
file = "<unknown>";
if (line < 0)
line = 0;
fprintf(out, " (%s() in %s:%d)\n", func, file, line);
} else {
fprintf(out, "\n");
}
}
void log_submit(const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
va_list args)
{
int saved_errno = errno;
log_lock();
errno = saved_errno;
log__submit(file, line, func, subs, sev, format, args);
log_unlock();
errno = saved_errno;
}
void log_format(const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
...)
{
int saved_errno = errno;
va_list list;
va_start(list, format);
log_lock();
errno = saved_errno;
log__submit(file, line, func, subs, sev, format, list);
log_unlock();
va_end(list);
errno = saved_errno;
}
void log_llog(void *data,
const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
va_list args)
{
log_submit(file, line, func, subs, sev, format, args);
}

204
src/shared/shl_log.h Normal file
View file

@ -0,0 +1,204 @@
/*
* SHL - Log/Debug Interface
*
* Copyright (c) 2010-2013 David Herrmann <dh.herrmann@gmail.com>
* Dedicated to the Public Domain
*/
/*
* Log/Debug Interface
* This interface provides basic logging to stderr.
*
* Define BUILD_ENABLE_DEBUG before including this header to enable
* debug-messages for this file.
*/
#ifndef SHL_LOG_H
#define SHL_LOG_H
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
enum log_severity {
LOG_FATAL = 0,
LOG_ALERT = 1,
LOG_CRITICAL = 2,
LOG_ERROR = 3,
LOG_WARNING = 4,
LOG_NOTICE = 5,
LOG_INFO = 6,
LOG_DEBUG = 7,
LOG_SEV_NUM,
};
/*
* Max Severity
* Messages with severities between log_max_sev and LOG_SEV_NUM (exclusive)
* are not logged, but discarded.
* Default: LOG_NOTICE
*/
extern unsigned int log_max_sev;
/*
* Timestamping
* Call this to initialize timestamps and cause all log-messages to be prefixed
* with a timestamp. If not called, no timestamps are added.
*/
void log_init_time(void);
/*
* Log-Functions
* These functions pass a log-message to the log-subsystem. Handy helpers are
* provided below. You almost never use these directly.
*
* log_submit:
* Submit the message to the log-subsystem. This is the backend of all other
* loggers.
*
* log_format:
* Same as log_submit but first converts the arguments into a va_list object.
*
* log_llog:
* Same as log_submit but used as connection to llog.
*
* log_dummyf:
* Dummy logger used for gcc var-arg validation.
*/
__attribute__((format(printf, 6, 0)))
void log_submit(const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
va_list args);
__attribute__((format(printf, 6, 7)))
void log_format(const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
...);
__attribute__((format(printf, 7, 0)))
void log_llog(void *data,
const char *file,
int line,
const char *func,
const char *subs,
unsigned int sev,
const char *format,
va_list args);
static inline __attribute__((format(printf, 2, 3)))
void log_dummyf(unsigned int sev, const char *format, ...)
{
}
/*
* Default values
* All helpers automatically pick-up the file, line, func and subsystem
* parameters for a log-message. file, line and func are generated with
* __FILE__, __LINE__ and __func__ and should almost never be replaced.
* The subsystem is by default an empty string. To overwrite this, add this
* line to the top of your source file:
* #define LOG_SUBSYSTEM "mysubsystem"
* Then all following log-messages will use this string as subsystem. You can
* define it before or after including this header.
*
* If you want to change one of these, you need to directly use log_submit and
* log_format. If you want the defaults for file, line and func you can use:
* log_format(LOG_DEFAULT_BASE, subsys, sev, format, ...);
* If you want all default values, use:
* log_format(LOG_DEFAULT, sev, format, ...);
*
* If you want to change a single value, this is the default line that is used
* internally. Adjust it to your needs:
* log_format(__FILE__, __LINE__, __func__, LOG_SUBSYSTEM, LOG_ERROR,
* "your format string: %s %d", "some args", 5, ...);
*
* log_printf is the same as log_format(LOG_DEFAULT, sev, format, ...) and is
* the most basic wrapper that you can use.
*/
#ifndef LOG_SUBSYSTEM
extern const char *LOG_SUBSYSTEM;
#endif
#define LOG_DEFAULT_BASE __FILE__, __LINE__, __func__
#define LOG_DEFAULT LOG_DEFAULT_BASE, LOG_SUBSYSTEM
#define log_printf(sev, format, ...) \
log_format(LOG_DEFAULT, (sev), (format), ##__VA_ARGS__)
/*
* Helpers
* These pick up all the default values and submit the message to the
* log-subsystem. The log_debug() function produces zero-code if
* BUILD_ENABLE_DEBUG is not defined. Therefore, it can be heavily used for
* debugging and will not have any side-effects.
* Even if disabled, parameters are evaluated! So it only produces zero code
* if there are no side-effects and the compiler can optimized it away.
*/
#ifdef BUILD_ENABLE_DEBUG
#define log_debug(format, ...) \
log_printf(LOG_DEBUG, (format), ##__VA_ARGS__)
#else
#define log_debug(format, ...) \
log_dummyf(LOG_DEBUG, (format), ##__VA_ARGS__)
#endif
#define log_info(format, ...) \
log_printf(LOG_INFO, (format), ##__VA_ARGS__)
#define log_notice(format, ...) \
log_printf(LOG_NOTICE, (format), ##__VA_ARGS__)
#define log_warning(format, ...) \
log_printf(LOG_WARNING, (format), ##__VA_ARGS__)
#define log_error(format, ...) \
log_printf(LOG_ERROR, (format), ##__VA_ARGS__)
#define log_critical(format, ...) \
log_printf(LOG_CRITICAL, (format), ##__VA_ARGS__)
#define log_alert(format, ...) \
log_printf(LOG_ALERT, (format), ##__VA_ARGS__)
#define log_fatal(format, ...) \
log_printf(LOG_FATAL, (format), ##__VA_ARGS__)
#define log_EINVAL() \
(log_error("invalid arguments"), -EINVAL)
#define log_vEINVAL() \
((void)log_EINVAL())
#define log_EFAULT() \
(log_error("internal operation failed"), -EFAULT)
#define log_vEFAULT() \
((void)log_EFAULT())
#define log_ENOMEM() \
(log_error("out of memory"), -ENOMEM)
#define log_vENOMEM() \
((void)log_ENOMEM())
#define log_EPIPE() \
(log_error("fd closed unexpectedly"), -EPIPE)
#define log_vEPIPE() \
((void)log_EPIPE())
#define log_ERRNO() \
(log_error("syscall failed (%d): %m", errno), -errno)
#define log_vERRNO() \
((void)log_ERRNO())
#define log_ERR(_r) \
(errno = -(_r), log_error("syscall failed (%d): %m", (_r)), (_r))
#define log_vERR(_r) \
((void)log_ERR(_r))
#endif /* SHL_LOG_H */

234
src/shared/shl_macro.h Normal file
View file

@ -0,0 +1,234 @@
/*
* SHL - Macros
*
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
* Dedicated to the Public Domain
*/
/*
* Macros
*/
#ifndef SHL_MACRO_H
#define SHL_MACRO_H
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
/* sanity checks required for some macros */
#if __SIZEOF_POINTER__ != 4 && __SIZEOF_POINTER__ != 8
#error "Pointer size is neither 4 nor 8 bytes"
#endif
/* gcc attributes; look them up for more information */
#define _shl_printf_(_a, _b) __attribute__((__format__(printf, _a, _b)))
#define _shl_alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#define _shl_sentinel_ __attribute__((__sentinel__))
#define _shl_noreturn_ __attribute__((__noreturn__))
#define _shl_unused_ __attribute__((__unused__))
#define _shl_pure_ __attribute__((__pure__))
#define _shl_const_ __attribute__((__const__))
#define _shl_deprecated_ __attribute__((__deprecated__))
#define _shl_packed_ __attribute__((__packed__))
#define _shl_malloc_ __attribute__((__malloc__))
#define _shl_weak_ __attribute__((__weak__))
#define _shl_likely_(_val) (__builtin_expect(!!(_val), 1))
#define _shl_unlikely_(_val) (__builtin_expect(!!(_val), 0))
#define _shl_public_ __attribute__((__visibility__("default")))
#define _shl_hidden_ __attribute__((__visibility__("hidden")))
#define _shl_weakref_(_val) __attribute__((__weakref__(#_val)))
#define _shl_cleanup_(_val) __attribute__((__cleanup__(_val)))
static inline void shl_freep(void *p)
{
free(*(void**)p);
}
#define _shl_cleanup_free_ _shl_cleanup_(shl_freep)
static inline void shl_set_errno(int *r)
{
errno = *r;
}
#define SHL_PROTECT_ERRNO \
_shl_cleanup_(shl_set_errno) _shl_unused_ int shl__errno = errno
/* 2-level stringify helper */
#define SHL__STRINGIFY(_val) #_val
#define SHL_STRINGIFY(_val) SHL__STRINGIFY(_val)
/* 2-level concatenate helper */
#define SHL__CONCATENATE(_a, _b) _a ## _b
#define SHL_CONCATENATE(_a, _b) SHL__CONCATENATE(_a, _b)
/* unique identifier with prefix */
#define SHL_UNIQUE(_prefix) SHL_CONCATENATE(_prefix, __COUNTER__)
/* array element count */
#define SHL_ARRAY_LENGTH(_array) (sizeof(_array)/sizeof(*(_array)))
/* get parent pointer by container-type, member and member-pointer */
#define shl_container_of(_ptr, _type, _member) \
({ \
const typeof( ((_type *)0)->_member ) *__mptr = (_ptr); \
(_type *)( (char *)__mptr - offsetof(_type, _member) ); \
})
/* return maximum of two values and do strict type checking */
#define shl_max(_a, _b) \
({ \
typeof(_a) __a = (_a); \
typeof(_b) __b = (_b); \
(void) (&__a == &__b); \
__a > __b ? __a : __b; \
})
/* same as shl_max() but perform explicit cast beforehand */
#define shl_max_t(_type, _a, _b) \
({ \
_type __a = (_type)(_a); \
_type __b = (_type)(_b); \
__a > __b ? __a : __b; \
})
/* return minimum of two values and do strict type checking */
#define shl_min(_a, _b) \
({ \
typeof(_a) __a = (_a); \
typeof(_b) __b = (_b); \
(void) (&__a == &__b); \
__a < __b ? __a : __b; \
})
/* same as shl_min() but perform explicit cast beforehand */
#define shl_min_t(_type, _a, _b) \
({ \
_type __a = (_type)(_a); \
_type __b = (_type)(_b); \
__a < __b ? __a : __b; \
})
/* clamp value between low and high barriers */
#define shl_clamp(_val, _low, _high) \
({ \
typeof(_val) __v = (_val); \
typeof(_low) __l = (_low); \
typeof(_high) __h = (_high); \
(void) (&__v == &__l); \
(void) (&__v == &__h); \
((__v > __h) ? __h : ((__v < __l) ? __l : __v)); \
})
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline size_t SHL_ALIGN_POWER2(size_t u)
{
return 1ULL << ((sizeof(u) * 8ULL) - __builtin_clzll(u - 1ULL));
}
/* zero memory or type */
#define shl_memzero(_ptr, _size) (memset((_ptr), 0, (_size)))
#define shl_zero(_ptr) (shl_memzero(&(_ptr), sizeof(_ptr)))
/* ptr <=> uint casts */
#define SHL_PTR_TO_TYPE(_type, _ptr) ((_type)((uintptr_t)(_ptr)))
#define SHL_TYPE_TO_PTR(_type, _int) ((void*)((uintptr_t)(_int)))
#define SHL_PTR_TO_INT(_ptr) SHL_PTR_TO_TYPE(int, (_ptr))
#define SHL_INT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int, (_ptr))
#define SHL_PTR_TO_UINT(_ptr) SHL_PTR_TO_TYPE(unsigned int, (_ptr))
#define SHL_UINT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned int, (_ptr))
#define SHL_PTR_TO_LONG(_ptr) SHL_PTR_TO_TYPE(long, (_ptr))
#define SHL_LONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(long, (_ptr))
#define SHL_PTR_TO_ULONG(_ptr) SHL_PTR_TO_TYPE(unsigned long, (_ptr))
#define SHL_ULONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned long, (_ptr))
#define SHL_PTR_TO_S32(_ptr) SHL_PTR_TO_TYPE(int32_t, (_ptr))
#define SHL_S32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int32_t, (_ptr))
#define SHL_PTR_TO_U32(_ptr) SHL_PTR_TO_TYPE(uint32_t, (_ptr))
#define SHL_U32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint32_t, (_ptr))
#define SHL_PTR_TO_S64(_ptr) SHL_PTR_TO_TYPE(int64_t, (_ptr))
#define SHL_S64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int64_t, (_ptr))
#define SHL_PTR_TO_U64(_ptr) SHL_PTR_TO_TYPE(uint64_t, (_ptr))
#define SHL_U64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint64_t, (_ptr))
/* compile-time assertions */
#define shl_assert_cc(_expr) static_assert(_expr, #_expr)
/*
* Safe Multiplications
* Multiplications are subject to overflows. These helpers guarantee that the
* multiplication can be done safely and return -ERANGE if not.
*
* Note: This is horribly slow for ull/uint64_t as we need a division to test
* for overflows. Take that into account when using these. For smaller integers,
* we can simply use an upcast-multiplication which gcc should be smart enough
* to optimize.
*/
#define SHL__REAL_MULT(_max, _val, _factor) \
({ \
(_factor == 0 || *(_val) <= (_max) / (_factor)) ? \
((*(_val) *= (_factor)), 0) : \
-ERANGE; \
})
#define SHL__UPCAST_MULT(_type, _max, _val, _factor) \
({ \
_type v = *(_val) * (_type)(_factor); \
(v <= (_max)) ? \
((*(_val) = v), 0) : \
-ERANGE; \
})
static inline int shl_mult_ull(unsigned long long *val,
unsigned long long factor)
{
return SHL__REAL_MULT(ULLONG_MAX, val, factor);
}
static inline int shl_mult_ul(unsigned long *val, unsigned long factor)
{
#if ULONG_MAX < ULLONG_MAX
return SHL__UPCAST_MULT(unsigned long long, ULONG_MAX, val, factor);
#else
shl_assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
return shl_mult_ull((unsigned long long*)val, factor);
#endif
}
static inline int shl_mult_u(unsigned int *val, unsigned int factor)
{
#if UINT_MAX < ULONG_MAX
return SHL__UPCAST_MULT(unsigned long, UINT_MAX, val, factor);
#elif UINT_MAX < ULLONG_MAX
return SHL__UPCAST_MULT(unsigned long long, UINT_MAX, val, factor);
#else
shl_assert_cc(sizeof(unsigned int) == sizeof(unsigned long long));
return shl_mult_ull(val, factor);
#endif
}
static inline int shl_mult_u64(uint64_t *val, uint64_t factor)
{
return SHL__REAL_MULT(UINT64_MAX, val, factor);
}
static inline int shl_mult_u32(uint32_t *val, uint32_t factor)
{
return SHL__UPCAST_MULT(uint_fast64_t, UINT32_MAX, val, factor);
}
static inline int shl_mult_u16(uint16_t *val, uint16_t factor)
{
return SHL__UPCAST_MULT(uint_fast32_t, UINT16_MAX, val, factor);
}
static inline int shl_mult_u8(uint8_t *val, uint8_t factor)
{
return SHL__UPCAST_MULT(uint_fast16_t, UINT8_MAX, val, factor);
}
#endif /* SHL_MACRO_H */

532
src/shared/shl_util.c Normal file
View file

@ -0,0 +1,532 @@
/*
* SHL - Utility Helpers
*
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
* Dedicated to the Public Domain
*/
/*
* Utility Helpers
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "shl_macro.h"
#include "shl_util.h"
/*
* Strict atoi()
* These helpers implement a strict version of atoi() (or strtol()). They only
* parse digit/alpha characters. No whitespace or other characters are parsed.
* The unsigned-variants explicitly forbid leading +/- signs. Use the signed
* variants to allow these.
* Base-prefix parsing is only done if base=0 is requested. Otherwise,
* base-prefixes are forbidden.
* The input string must be ASCII compatbile (which includes UTF8).
*
* We also always check for overflows and return errors (but continue parsing!)
* so callers can catch it correctly.
*
* Additionally, we allow "length" parameters so strings do not necessarily have
* to be zero-terminated. We have wrappers which skip this by passing strlen().
*/
int shl_ctoi(char ch, unsigned int base)
{
unsigned int v;
switch (ch) {
case '0'...'9':
v = ch - '0';
break;
case 'a'...'z':
v = ch - 'a' + 10;
break;
case 'A'...'Z':
v = ch - 'A' + 10;
break;
default:
return -EINVAL;
}
if (v >= base)
return -EINVAL;
return v;
}
/* figure out base and skip prefix */
static unsigned int shl__skip_base(const char **str, size_t *len)
{
if (*len > 1) {
if ((*str)[0] == '0') {
if (shl_ctoi((*str)[1], 8) >= 0) {
*str += 1;
*len -= 1;
return 8;
}
}
}
if (*len > 2) {
if ((*str)[0] == '0' && (*str)[1] == 'x') {
if (shl_ctoi((*str)[2], 16) >= 0) {
*str += 2;
*len -= 2;
return 16;
}
}
}
return 10;
}
int shl_atoi_ulln(const char *str,
size_t len,
unsigned int base,
const char **next,
unsigned long long *out)
{
bool huge;
uint32_t val1;
unsigned long long val2;
size_t pos;
int r, c;
/* We use u32 as storage first so we have fast mult-overflow checks. We
* cast up to "unsigned long long" once we exceed UINT32_MAX. Overflow
* checks will get pretty slow for non-power2 bases, though. */
huge = false;
val1 = 0;
val2 = 0;
r = 0;
if (base > 36) {
if (next)
*next = str;
if (out)
*out = 0;
return -EINVAL;
}
if (base == 0)
base = shl__skip_base(&str, &len);
for (pos = 0; pos < len; ++pos) {
c = shl_ctoi(str[pos], base);
if (c < 0)
break;
/* skip calculations on error */
if (r < 0)
continue;
if (!huge) {
val2 = val1;
r = shl_mult_u32(&val1, base);
if (r >= 0 && val1 + c >= val1)
val1 += c;
else
huge = true;
}
if (huge) {
r = shl_mult_ull(&val2, base);
if (r >= 0 && val2 + c >= val2)
val2 += c;
}
}
if (next)
*next = (char*)&str[pos];
if (out) {
if (r < 0)
*out = ULLONG_MAX;
else if (huge)
*out = val2;
else
*out = val1;
}
return r;
}
int shl_atoi_uln(const char *str,
size_t len,
unsigned int base,
const char **next,
unsigned long *out)
{
unsigned long long val;
int r;
r = shl_atoi_ulln(str, len, base, next, &val);
if (r >= 0 && val > ULONG_MAX)
r = -ERANGE;
if (out)
*out = shl_min(val, (unsigned long long)ULONG_MAX);
return r;
}
int shl_atoi_un(const char *str,
size_t len,
unsigned int base,
const char **next,
unsigned int *out)
{
unsigned long long val;
int r;
r = shl_atoi_ulln(str, len, base, next, &val);
if (r >= 0 && val > UINT_MAX)
r = -ERANGE;
if (out)
*out = shl_min(val, (unsigned long long)UINT_MAX);
return r;
}
int shl_atoi_zn(const char *str,
size_t len,
unsigned int base,
const char **next,
size_t *out)
{
unsigned long long val;
int r;
r = shl_atoi_ulln(str, len, base, next, &val);
if (r >= 0 && val > SIZE_MAX)
r = -ERANGE;
if (out)
*out = shl_min(val, (unsigned long long)SIZE_MAX);
return r;
}
/*
* Greedy Realloc
* The greedy-realloc helpers simplify power-of-2 buffer allocations. If you
* have a dynamic array, simply use shl_greedy_realloc() for re-allocations
* and it makes sure your buffer-size is always a multiple of 2 and is big
* enough for your new entries.
* Default size is 64, but you can initialize your buffer to a bigger default
* if you need.
*/
void *shl_greedy_realloc(void **mem, size_t *size, size_t need)
{
size_t nsize;
void *p;
if (*size >= need)
return *mem;
nsize = SHL_ALIGN_POWER2(shl_max_t(size_t, 64U, need));
if (nsize == 0)
return NULL;
p = realloc(*mem, nsize);
if (!p)
return NULL;
*mem = p;
*size = nsize;
return p;
}
void *shl_greedy_realloc0(void **mem, size_t *size, size_t need)
{
size_t prev = *size;
uint8_t *p;
p = shl_greedy_realloc(mem, size, need);
if (!p)
return NULL;
if (*size > prev)
shl_memzero(&p[prev], *size - prev);
return p;
}
/*
* String Helpers
*/
char *shl_strcat(const char *first, const char *second)
{
size_t flen, slen;
char *str;
if (!first)
first = "";
if (!second)
second = "";
flen = strlen(first);
slen = strlen(second);
if (flen + slen + 1 <= flen)
return NULL;
str = malloc(flen + slen + 1);
if (!str)
return NULL;
strcpy(str, first);
strcpy(&str[flen], second);
return str;
}
char *shl_strjoin(const char *first, ...) {
va_list args;
size_t len, l;
const char *arg;
char *str, *p;
va_start(args, first);
for (arg = first, len = 0; arg; arg = va_arg(args, const char*)) {
l = strlen(arg);
if (len + l < len)
return NULL;
len += l;
}
va_end(args);
str = malloc(len + 1);
if (!str)
return NULL;
va_start(args, first);
for (arg = first, p = str; arg; arg = va_arg(args, const char*))
p = stpcpy(p, arg);
va_end(args);
*p = 0;
return str;
}
/*
* strv
*/
void shl_strv_free(char **strv)
{
unsigned int i;
if (!strv)
return;
for (i = 0; strv[i]; ++i)
free(strv[i]);
free(strv);
}
/*
* Quoted Strings
*/
char shl_qstr_unescape_char(char c)
{
switch (c) {
case 'a':
return '\a';
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
case '"':
return '"';
case '\'':
return '\'';
case '\\':
return '\\';
default:
return 0;
}
}
void shl_qstr_decode_n(char *str, size_t length)
{
size_t i;
bool escaped;
char *pos, c, quoted;
quoted = 0;
escaped = false;
pos = str;
for (i = 0; i < length; ++i) {
if (escaped) {
escaped = false;
c = shl_qstr_unescape_char(str[i]);
if (c) {
*pos++ = c;
} else if (!str[i]) {
/* ignore binary 0 */
} else {
*pos++ = '\\';
*pos++ = str[i];
}
} else if (quoted) {
if (str[i] == '\\')
escaped = true;
else if (str[i] == '"' && quoted == '"')
quoted = 0;
else if (str[i] == '\'' && quoted == '\'')
quoted = 0;
else if (!str[i])
/* ignore binary 0 */ ;
else
*pos++ = str[i];
} else {
if (str[i] == '\\')
escaped = true;
else if (str[i] == '"' || str[i] == '\'')
quoted = str[i];
else if (!str[i])
/* ignore binary 0 */ ;
else
*pos++ = str[i];
}
}
if (escaped)
*pos++ = '\\';
*pos = 0;
}
static int shl__qstr_push(char ***strv,
size_t *strv_num,
size_t *strv_size,
const char *str,
size_t len)
{
size_t strv_need;
char *ns;
strv_need = (*strv_num + 2) * sizeof(**strv);
if (!shl_greedy_realloc0((void**)strv, strv_size, strv_need))
return -ENOMEM;
ns = malloc(len + 1);
memcpy(ns, str, len);
ns[len] = 0;
shl_qstr_decode_n(ns, len);
(*strv)[*strv_num] = ns;
*strv_num += 1;
return 0;
}
int shl_qstr_tokenize_n(const char *str, size_t length, char ***out)
{
char **strv, quoted;
size_t i, strv_num, strv_size;
const char *pos;
bool escaped;
int r;
if (!out)
return -EINVAL;
if (!str)
str = "";
strv_num = 0;
strv_size = sizeof(*strv);
strv = malloc(strv_size);
if (!strv)
return -ENOMEM;
quoted = 0;
escaped = false;
pos = str;
for (i = 0; i < length; ++i) {
if (escaped) {
escaped = false;
} else if (str[i] == '\\') {
escaped = true;
} else if (quoted) {
if (str[i] == '"' && quoted == '"')
quoted = 0;
else if (str[i] == '\'' && quoted == '\'')
quoted = 0;
} else if (str[i] == '"' && quoted == '"') {
quoted = '"';
} else if (str[i] == '\'' && quoted == '\'') {
quoted = '\'';
} else if (str[i] == ' ') {
/* ignore multiple separators */
if (pos != &str[i]) {
r = shl__qstr_push(&strv,
&strv_num,
&strv_size,
pos,
&str[i] - pos);
if (r < 0)
goto error;
}
pos = &str[i + 1];
}
}
/* copy trailing token if available */
if (i > 0 && pos != &str[i]) {
r = shl__qstr_push(&strv,
&strv_num,
&strv_size,
pos,
&str[i] - pos);
if (r < 0)
goto error;
}
if ((int)strv_num < (ssize_t)strv_num) {
r = -ENOMEM;
goto error;
}
strv[strv_num] = NULL;
*out = strv;
return strv_num;
error:
for (i = 0; i < strv_num; ++i)
free(strv[i]);
free(strv);
return r;
}
int shl_qstr_tokenize(const char *str, char ***out)
{
return shl_qstr_tokenize_n(str, str ? strlen(str) : 0, out);
}

115
src/shared/shl_util.h Normal file
View file

@ -0,0 +1,115 @@
/*
* SHL - Utility Helpers
*
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
* Dedicated to the Public Domain
*/
/*
* Utility Helpers
*/
#ifndef SHL_UTIL_H
#define SHL_UTIL_H
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "shl_macro.h"
/* strict atoi */
int shl_ctoi(char ch, unsigned int base);
int shl_atoi_ulln(const char *str,
size_t len,
unsigned int base,
const char **next,
unsigned long long *out);
int shl_atoi_uln(const char *str,
size_t len,
unsigned int base,
const char **next,
unsigned long *out);
int shl_atoi_un(const char *str,
size_t len,
unsigned int base,
const char **next,
unsigned int *out);
int shl_atoi_zn(const char *str,
size_t len,
unsigned int base,
const char **next,
size_t *out);
static inline int shl_atoi_ull(const char *str,
unsigned int base,
const char **next,
unsigned long long *out)
{
return shl_atoi_ulln(str, strlen(str), base, next, out);
}
static inline int shl_atoi_ul(const char *str,
unsigned int base,
const char **next,
unsigned long *out)
{
return shl_atoi_uln(str, strlen(str), base, next, out);
}
static inline int shl_atoi_u(const char *str,
unsigned int base,
const char **next,
unsigned int *out)
{
return shl_atoi_un(str, strlen(str), base, next, out);
}
static inline int shl_atoi_z(const char *str,
unsigned int base,
const char **next,
size_t *out)
{
return shl_atoi_zn(str, strlen(str), base, next, out);
}
/* greedy alloc */
void *shl_greedy_realloc(void **mem, size_t *size, size_t need);
void *shl_greedy_realloc0(void **mem, size_t *size, size_t need);
/* string helpers */
char *shl_strcat(const char *first, const char *second);
_shl_sentinel_ char *shl_strjoin(const char *first, ...);
static inline char *shl_startswith(const char *str, const char *prefix)
{
if (!strncmp(str, prefix, strlen(prefix)))
return (char*)str + strlen(prefix);
else
return NULL;
}
/* strv */
void shl_strv_free(char **strv);
static inline void shl_strv_freep(char ***strv)
{
shl_strv_free(*strv);
}
#define _shl_cleanup_strv_ _shl_cleanup_(shl_strv_freep)
/* quoted strings */
char shl_qstr_unescape_char(char c);
void shl_qstr_decode_n(char *str, size_t length);
int shl_qstr_tokenize_n(const char *str, size_t length, char ***out);
int shl_qstr_tokenize(const char *str, char ***out);
#endif /* SHL_UTIL_H */