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:
parent
fc30d8b920
commit
47539226cd
9 changed files with 19 additions and 20 deletions
138
src/shared/shl_dlist.h
Normal file
138
src/shared/shl_dlist.h
Normal 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
464
src/shared/shl_htable.c
Normal 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
304
src/shared/shl_htable.h
Normal 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
239
src/shared/shl_log.c
Normal 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
204
src/shared/shl_log.h
Normal 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
234
src/shared/shl_macro.h
Normal 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
532
src/shared/shl_util.c
Normal 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
115
src/shared/shl_util.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue