# sources
USUAL_DIR = .
USUAL_OBJDIR = obj
-USUAL_MODULES = $(subst .h,, $(notdir $(wildcard usual/*.h)))
+USUAL_MODULES = $(filter-out pgsocket, $(subst .h,, $(notdir $(wildcard usual/*.h))))
include $(USUAL_DIR)/Setup.mk
# extra warning flags
AC_CHECK_HEADERS([arpa/inet.h netinet/in.h netinet/tcp.h])
AC_CHECK_HEADERS([sys/param.h sys/uio.h libgen.h pwd.h grp.h])
AC_CHECK_HEADERS([sys/wait.h sys/mman.h syslog.h netdb.h])
-AC_CHECK_HEADERS([err.h pthread.h])
+AC_CHECK_HEADERS([err.h pthread.h endian.h sys/endian.h byteswap.h])
dnl ucred.h may have prereqs
AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [
#ifdef HAVE_SYS_TYPES_H
#include <usual/base.h>
#include <usual/cbtree.h>
#include <usual/cfparser.h>
+#include <usual/crc32.h>
#include <usual/daemon.h>
+#include <usual/endian.h>
#include <usual/err.h>
#include <usual/event.h>
#include <usual/fileutil.h>
+#include <usual/hashtab-impl.h>
+#include <usual/heap-impl.h>
#include <usual/list.h>
#include <usual/logging.h>
#include <usual/lookup3.h>
+#include <usual/mbuf.h>
#include <usual/md5.h>
+#include <usual/misc.h>
+//#include <usual/pgsocket.h>
#include <usual/safeio.h>
-#include <usual/slab.h>
+#include <usual/shlist.h>
#include <usual/signal.h>
+#include <usual/slab.h>
#include <usual/socket.h>
#include <usual/statlist.h>
#include <usual/string.h>
#include <usual/time.h>
+#include <usual/utf8.h>
+
+static inline bool heap_is_better(const void *a, const void *b)
+{
+ return 1;
+}
int main(void)
{
--- /dev/null
+/*
+ * CRC32.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <usual/crc32.h>
+
+static const uint32_t crc_tab[256] = {
+0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+static inline uint32_t crc32(uint32_t prev, uint8_t c)
+{
+ return crc_tab[(prev ^ c) & 0xFF] ^ (prev >> 8);
+}
+
+uint32_t calc_crc32(const void *data, size_t len, uint32_t init)
+{
+ const uint8_t *p = data;
+ uint32_t crc = init ^ (~0);
+ while (len--)
+ crc = crc32(crc, *p++);
+ return crc ^ (~0);
+}
+
--- /dev/null
+/*
+ * CRC32.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_CRC32_H_
+#define _USUAL_CRC32_H_
+
+#include <usual/base.h>
+
+uint32_t calc_crc32(const void *data, size_t len, uint32_t init);
+
+#endif
+
--- /dev/null
+/*
+ * Endianess comversion macros.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Always swap:
+ * bswap16, bswap32, bswap64
+ *
+ * Host <> LE/BE
+ * htobe16, htobe32, htobe64
+ * htole16, htole32, htole64
+ * be16toh, be32toh, be64toh
+ * le16toh, le32toh, le64toh
+ *
+ * Read LE/BE -> Host:
+ * le16dec, le32dec, le64dec
+ * be16dec, be32dec, be64dec
+ *
+ * Write Host -> LE/BE:
+ * le16enc, le32enc, le64enc
+ * be16enc, be32enc, be64enc
+ */
+
+#ifndef _USUAL_ENDIAN_H_
+#define _USUAL_ENDIAN_H_
+
+#include <usual/base.h>
+
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+#include <string.h>
+
+/*
+ * Always swap.
+ */
+
+#ifndef bswap16
+#ifdef bswap_16
+#define bswap16(x) bswap_16(x)
+#else
+static inline uint16_t _gen_bswap16(uint16_t x)
+{
+ return (x << 8) | (x >> 8);
+}
+#define bswap16(x) _gen_bswap16(x)
+#endif
+#endif
+
+#ifndef bswap32
+#ifdef bswap_32
+#define bswap32(x) bswap_32(x)
+#else
+static inline uint32_t _gen_bswap32(uint32_t x)
+{
+#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)))
+ return __builtin_bswap32(x);
+#else
+ x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF);
+ return (x << 16) | (x >> 16);
+#endif
+}
+#define bswap32(x) _gen_bswap32(x)
+#endif
+#endif
+
+#ifndef bswap64
+#ifdef bswap_64
+#define bswap64(x) bswap_64(x)
+#else
+static inline uint64_t _gen_bswap64(uint64_t x)
+{
+ return ((uint64_t)bswap32(x) << 32) | bswap32(x >> 32);
+}
+#define bswap64(x) _gen_bswap64(x)
+#endif
+#endif
+
+/*
+ * Host <-> LE/BE
+ */
+
+#ifndef le64toh
+
+#ifdef WORDS_BIGENDIAN
+#define htobe16(x) ((uint16_t)(x))
+#define htobe32(x) ((uint32_t)(x))
+#define htobe64(x) ((uint64_t)(x))
+#define htole16(x) bswap16(x)
+#define htole32(x) bswap32(x)
+#define htole64(x) bswap64(x)
+
+#define be16toh(x) ((uint16_t)(x))
+#define be32toh(x) ((uint32_t)(x))
+#define be64toh(x) ((uint64_t)(x))
+#define le16toh(x) bswap16(x)
+#define le32toh(x) bswap32(x)
+#define le64toh(x) bswap64(x)
+
+#else
+
+#define htobe16(x) bswap16(x)
+#define htobe32(x) bswap32(x)
+#define htobe64(x) bswap64(x)
+#define htole16(x) ((uint16_t)(x))
+#define htole32(x) ((uint32_t)(x))
+#define htole64(x) ((uint64_t)(x))
+
+#define be16toh(x) bswap16(x)
+#define be32toh(x) bswap32(x)
+#define be64toh(x) bswap64(x)
+#define le16toh(x) ((uint16_t)(x))
+#define le32toh(x) ((uint32_t)(x))
+#define le64toh(x) ((uint64_t)(x))
+#endif
+
+#endif
+
+/*
+ * Read LE/BE values from memory.
+ */
+
+#define _DEC(name, typ, decode) \
+static inline typ name(const void *p) { \
+ typ tmp; \
+ memcpy(&tmp, p, sizeof(typ)); \
+ return decode(tmp); \
+}
+
+_DEC(be16dec, uint16_t, be16toh)
+_DEC(be32dec, uint32_t, be32toh)
+_DEC(be64dec, uint64_t, be64toh)
+_DEC(le16dec, uint16_t, le16toh)
+_DEC(le32dec, uint32_t, le32toh)
+_DEC(le64dec, uint64_t, le64toh)
+#undef _DEC
+
+/*
+ * Write LE/BE values to memory.
+ */
+
+#define _ENC(name, typ, encode) \
+static inline void name(void *p, typ val) { \
+ typ tmp = encode(val); \
+ memcpy(p, &tmp, sizeof(typ)); \
+}
+_ENC(be16enc, uint16_t, htobe16)
+_ENC(be32enc, uint32_t, htobe32)
+_ENC(be64enc, uint64_t, htobe64)
+_ENC(le16enc, uint16_t, htole16)
+_ENC(le32enc, uint32_t, htole32)
+_ENC(le64enc, uint64_t, htole64)
+#undef _ENC
+
+#endif /* _USUAL_ENDIAN_H_ */
--- /dev/null
+/*
+ * Simple customizable hashtable implementation.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+/*
+ * - Fixed-size hash table, open-addressed
+ * - Extended by linking several together
+ * - Resizable by copying.
+ * - Can be lockless in multi-reader, one-writer situation if
+ * mempory barrier macros are defined. This also requires that
+ * HashItem must not be split across cachelines.
+ */
+
+#include <usual/base.h>
+
+#include <string.h>
+
+#ifndef HTAB_KEY_T
+#define HTAB_KEY_T unsigned long
+#endif
+#ifndef HTAB_VAL_T
+#define HTAB_VAL_T void *
+#endif
+
+#ifndef HTAB_RMB
+#define HTAB_RMB
+#endif
+#ifndef HTAB_WMB
+#define HTAB_WMB
+#endif
+
+typedef HTAB_KEY_T htab_key_t;
+typedef HTAB_VAL_T htab_val_t;
+
+#ifndef HTAB_ITEM
+#define HTAB_ITEM
+struct HashItem {
+ htab_key_t key;
+ htab_val_t value;
+};
+#endif
+
+typedef bool (*hash_cmp_fn)(htab_val_t curval, void *arg);
+
+#define MASK(h) ((h)->size - 1)
+#define CALC_POS(h, key) (key & MASK(h))
+#define NEXT_POS(h, pos) (((pos) * 5 + 1) & MASK(h))
+#define MAX_USED(h) ((h)->size * 75 / 100)
+
+struct HashTab {
+ struct HashTab *next;
+ hash_cmp_fn cmp_fn;
+ unsigned size;
+ unsigned used;
+ struct HashItem tab[];
+};
+
+static struct HashTab *hashtab_create(unsigned size, hash_cmp_fn cmp_fn)
+{
+ struct HashTab *h;
+ unsigned len = size * sizeof(struct HashItem) + offsetof(struct HashTab, tab);
+ h = zmalloc(len);
+ h->size = size;
+ h->cmp_fn = cmp_fn;
+ return h;
+}
+
+static void hashtab_destroy(struct HashTab *h)
+{
+ struct HashTab *tmp;
+ while (h) {
+ tmp = h->next;
+ free(h);
+ h = tmp;
+ }
+}
+
+static htab_val_t *hashtab_lookup(struct HashTab *h, htab_key_t key, bool do_insert, void *arg)
+{
+ unsigned pos;
+ struct HashItem *i;
+loop:
+ /* find key, starting from pos */
+ pos = CALC_POS(h, key);
+ while (h->tab[pos].value) {
+ i = &h->tab[pos];
+ HTAB_RMB;
+ if (i->key == key) {
+ if (arg && h->cmp_fn(i->value, arg))
+ return &i->value;
+ }
+ pos = NEXT_POS(h, pos);
+ }
+
+ /* not found in this one, check chained tables */
+ if (h->next) {
+ h = h->next;
+ goto loop;
+ }
+
+ /* just lookup? */
+ if (!do_insert)
+ return NULL;
+
+ /* insert */
+ if (h->used >= MAX_USED(h)) {
+ struct HashTab *tmp;
+ tmp = hashtab_create(h->size, h->cmp_fn);
+ if (!tmp)
+ return NULL;
+ h->next = tmp;
+ h = tmp;
+ pos = CALC_POS(h, key);
+ }
+ h->used++;
+ h->tab[pos].key = key;
+ HTAB_WMB;
+ return &h->tab[pos].value;
+}
+
+/* if proper pos is between src and dst, cannot move */
+static bool _hashtab_slot_can_move(struct HashTab *h, unsigned dstpos, unsigned srcpos)
+{
+ htab_key_t key = h->tab[srcpos].key;
+ unsigned pos, kpos = CALC_POS(h, key);
+ if (kpos == srcpos)
+ return false;
+ if (kpos == dstpos)
+ return true;
+ for (pos = NEXT_POS(h, dstpos); pos != srcpos; pos = NEXT_POS(h, pos)) {
+ if (pos == kpos)
+ return false;
+ }
+ return true;
+}
+
+static void hashtab_delete(struct HashTab *h, htab_key_t key, void *arg)
+{
+ htab_val_t *vptr;
+ struct HashItem *hd;
+ unsigned pos, dstpos;
+
+ /* find it */
+ vptr = hashtab_lookup(h, key, false, arg);
+ if (!vptr)
+ return;
+
+ /* find right tab */
+ hd = container_of(vptr, struct HashItem, value);
+ while (h && ((hd < h->tab) && (hd >= h->tab + h->size)))
+ h = h->next;
+
+ /* calculate index */
+ dstpos = hd - h->tab;
+
+loop:
+ /* move slot */
+ for (pos = NEXT_POS(h, dstpos); h->tab[pos].value; pos = NEXT_POS(h, pos)) {
+ if (_hashtab_slot_can_move(h, dstpos, pos)) {
+ h->tab[dstpos].key = h->tab[pos].key;
+ h->tab[dstpos].value = h->tab[pos].value;
+ dstpos = pos;
+ goto loop;
+ }
+ }
+ h->tab[dstpos].value = 0;
+ HTAB_WMB;
+ h->tab[dstpos].key = 0;
+ h->used--;
+}
+
+static void hashtab_stats(struct HashTab *h, unsigned *nitem_p, unsigned *ntab_p)
+{
+ unsigned n = 0, l = 0;
+ while (h) {
+ l++;
+ n += h->used;
+ h = h->next;
+ }
+ *nitem_p = n;
+ *ntab_p = l;
+}
+
+static struct HashTab *hashtab_copy(struct HashTab *h_old, unsigned newsize)
+{
+ struct HashTab *h_new;
+ unsigned i;
+
+ h_new = hashtab_create(newsize, h_old->cmp_fn);
+ for (; h_old; h_old = h_old->next) {
+ for (i = 0; i < h_old->size; i++) {
+ struct HashItem *s = &h_old->tab[i];
+ htab_val_t *new_pos;
+ if (s->value) {
+ new_pos = hashtab_lookup(h_new, s->key, true, NULL);
+ if (!new_pos)
+ goto err;
+ *new_pos = s->value;
+ }
+ }
+ }
+ return h_new;
+err:
+ hashtab_destroy(h_new);
+ return NULL;
+}
+
+/* example, and avoid "unused" warnings */
+static inline void _hashtab_example(void)
+{
+ unsigned nitem, nlink;
+ struct HashTab *h, *h2;
+
+ h = hashtab_create(1024, NULL);
+ hashtab_lookup(h, 123, true, NULL);
+ hashtab_stats(h, &nitem, &nlink);
+ h2 = hashtab_copy(h, 2048);
+ hashtab_delete(h, 123, NULL);
+ hashtab_destroy(h);
+ hashtab_destroy(h2);
+}
+
--- /dev/null
+/*
+ * Random stuff that does not fit elsewhere.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_MISC_H_
+#define _USUAL_MISC_H_
+
+#include <usual/base.h>
+
+#ifdef WORDS_BIGENDIAN
+#define FOURCC(a,b,c,d) \
+ ( ((unsigned int)(unsigned char)(a) << 24) \
+ | ((unsigned int)(unsigned char)(b) << 16) \
+ | ((unsigned int)(unsigned char)(c) << 8) \
+ | ((unsigned int)(unsigned char)(d)))
+#else
+#define FOURCC(a,b,c,d) \
+ ( ((unsigned int)(unsigned char)(a)) \
+ | ((unsigned int)(unsigned char)(b) << 8) \
+ | ((unsigned int)(unsigned char)(c) << 16) \
+ | ((unsigned int)(unsigned char)(d) << 24))
+#endif
+
+static inline int is_power_of_2(int n)
+{
+ return (n > 0) && !(n & (n - 1));
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+#define mb() asm volatile("mfence":::"memory")
+#define rmb() asm volatile("lfence":::"memory")
+#define wmb() asm volatile("sfence":::"memory")
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * Async Postgres connection.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <usual/pgsocket.h>
+
+#include <stdarg.h>
+
+#include <usual/event.h>
+#include <usual/logging.h>
+#include <usual/time.h>
+#include <usual/string.h>
+
+/* PgSocket.wait_type */
+#define W_NONE 0
+#define W_SOCK 1
+#define W_TIME 2
+
+typedef void (*libev_cb)(int sock, short flags, void *arg);
+
+struct PgSocket {
+ struct event ev;
+
+ unsigned wait_type:4;
+
+ PGconn *con;
+
+ pgs_handler_f handler_func;
+ void *handler_arg;
+
+ const char *connstr;
+
+ struct event_base *base;
+};
+
+static void send_event(struct PgSocket *db, enum PgEvent ev)
+{
+ db->handler_func(db, db->handler_arg, ev, NULL);
+}
+
+static void wait_event(struct PgSocket *db, short ev, libev_cb fn)
+{
+ Assert(!db->wait_type);
+
+ event_set(&db->ev, PQsocket(db->con), ev, fn, db);
+ if (db->base)
+ event_base_set(db->base, &db->ev);
+ if (event_add(&db->ev, NULL) < 0)
+ fatal_perror("event_add");
+
+ db->wait_type = W_SOCK;
+}
+
+static void timeout_cb(int sock, short flags, void *arg)
+{
+ struct PgSocket *db = arg;
+
+ db->wait_type = 0;
+
+ send_event(db, PGS_TIMEOUT);
+}
+
+/* some error happened */
+static void conn_error(struct PgSocket *db, enum PgEvent ev, const char *desc)
+{
+ log_error("connection error: %s", desc);
+ log_error("libpq: %s", PQerrorMessage(db->con));
+ send_event(db, ev);
+}
+
+/*
+ * Called when select() told that conn is avail for reading/writing.
+ *
+ * It should call postgres handlers and then change state if needed.
+ */
+static void result_cb(int sock, short flags, void *arg)
+{
+ struct PgSocket *db = arg;
+ PGresult *res, *res_saved = NULL;
+
+ db->wait_type = 0;
+
+ if (!PQconsumeInput(db->con)) {
+ conn_error(db, PGS_RESULT_BAD, "PQconsumeInput");
+ return;
+ }
+
+ /* loop until PQgetResult returns NULL */
+ while (1) {
+ /* incomplete result? */
+ if (PQisBusy(db->con)) {
+ wait_event(db, EV_READ, result_cb);
+ return;
+ }
+
+ /* next result */
+ res = PQgetResult(db->con);
+ if (!res)
+ break;
+
+ if (res_saved) {
+ PQclear(res_saved);
+ }
+ res_saved = res;
+ }
+
+ db->handler_func(db, db->handler_arg, PGS_RESULT_OK, res_saved);
+}
+
+static void send_cb(int sock, short flags, void *arg)
+{
+ int res;
+ struct PgSocket *db = arg;
+
+ db->wait_type = 0;
+
+ res = PQflush(db->con);
+ if (res > 0) {
+ wait_event(db, EV_WRITE, send_cb);
+ } else if (res == 0) {
+ wait_event(db, EV_READ, result_cb);
+ } else
+ conn_error(db, PGS_RESULT_BAD, "PQflush");
+}
+
+
+static void connect_cb(int sock, short flags, void *arg)
+{
+ struct PgSocket *db = arg;
+ PostgresPollingStatusType poll_res;
+
+ db->wait_type = 0;
+
+ poll_res = PQconnectPoll(db->con);
+ switch (poll_res) {
+ case PGRES_POLLING_WRITING:
+ wait_event(db, EV_WRITE, connect_cb);
+ break;
+ case PGRES_POLLING_READING:
+ wait_event(db, EV_READ, connect_cb);
+ break;
+ case PGRES_POLLING_OK:
+ send_event(db, PGS_CONNECT_OK);
+ break;
+ default:
+ conn_error(db, PGS_CONNECT_FAILED, "PQconnectPoll");
+ }
+}
+
+/*
+ * Public API
+ */
+
+struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *handler_arg)
+{
+ struct PgSocket *db;
+
+ db = zmalloc(sizeof(*db));
+ if (!db)
+ return NULL;
+
+ db->handler_func = fn;
+ db->handler_arg = handler_arg;
+
+ db->connstr = strdup(connstr);
+ if (!db->connstr) {
+ pgs_free(db);
+ return NULL;
+ }
+ return db;
+}
+
+void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base)
+{
+ pgs->base = base;
+}
+
+void pgs_connect(struct PgSocket *db)
+{
+ db->con = PQconnectStart(db->connstr);
+ if (db->con == NULL) {
+ conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart");
+ return;
+ }
+
+ if (PQstatus(db->con) == CONNECTION_BAD) {
+ conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart");
+ return;
+ }
+
+ wait_event(db, EV_WRITE, connect_cb);
+}
+
+
+void pgs_disconnect(struct PgSocket *db)
+{
+ if (db->con) {
+ PQfinish(db->con);
+ db->con = NULL;
+ }
+}
+
+void pgs_free(struct PgSocket *db)
+{
+ if (db) {
+ pgs_disconnect(db);
+ free(db->connstr);
+ free(db);
+ }
+}
+
+void pgs_sleep(struct PgSocket *db, double timeout)
+{
+ struct timeval tv;
+
+ Assert(!db->wait_type);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = (timeout - tv.tv_sec) * USEC;
+
+ evtimer_set(&db->ev, timeout_cb, db);
+ if (db->base)
+ event_base_set(db->base, &db->ev);
+ //event_assign(&db->ev, db->base, -1, 0, timeout_cb, db);
+ if (evtimer_add(&db->ev, &tv) < 0)
+ fatal_perror("event_add");
+
+ db->wait_type = W_TIME;
+}
+
+void pgs_reconnect(struct PgSocket *db)
+{
+ pgs_disconnect(db);
+ pgs_sleep(db, 60);
+}
+
+void pgs_send_query_simple(struct PgSocket *db, const char *q)
+{
+ int res;
+
+ log_debug("%s", q);
+ res = PQsendQuery(db->con, q);
+ if (!res) {
+ conn_error(db, PGS_RESULT_BAD, "PQsendQuery");
+ return;
+ }
+
+ res = PQflush(db->con);
+ if (res > 0) {
+ wait_event(db, EV_WRITE, send_cb);
+ } else if (res == 0) {
+ wait_event(db, EV_READ, result_cb);
+ } else
+ conn_error(db, PGS_RESULT_BAD, "PQflush");
+}
+
+void pgs_send_query_params(struct PgSocket *db, const char *q, int cnt, ...)
+{
+ int i;
+ va_list ap;
+ const char * args[10];
+
+ if (cnt > 10) cnt = 10;
+
+ va_start(ap, cnt);
+ for (i = 0; i < cnt; i++)
+ args[i] = va_arg(ap, char *);
+ va_end(ap);
+
+ pgs_send_query_params_list(db, q, cnt, args);
+}
+
+void pgs_send_query_params_list(struct PgSocket *db, const char *q, int cnt, const char *args[])
+{
+ int res;
+
+ res = PQsendQueryParams(db->con, q, cnt, NULL, args, NULL, NULL, 0);
+ if (!res) {
+ conn_error(db, PGS_RESULT_BAD, "PQsendQueryParams");
+ return;
+ }
+
+ res = PQflush(db->con);
+ if (res > 0) {
+ wait_event(db, EV_WRITE, send_cb);
+ } else if (res == 0) {
+ wait_event(db, EV_READ, result_cb);
+ } else
+ conn_error(db, PGS_RESULT_BAD, "PQflush");
+}
+
+int pgs_connection_valid(struct PgSocket *db)
+{
+ return (db->con != NULL);
+}
+
+PGconn *pgs_get_connection(struct PgSocket *db)
+{
+ return db->con;
+}
+
--- /dev/null
+/*
+ * Async Postgres connection.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_PGSOCKET_H_
+#define _USUAL_PGSOCKET_H_
+
+#include <usual/base.h>
+
+#include <libpq-fe.h>
+
+enum PgEvent {
+ PGS_CONNECT_OK,
+ PGS_CONNECT_FAILED,
+ PGS_RESULT_OK,
+ PGS_RESULT_BAD,
+ PGS_TIMEOUT,
+};
+
+struct PgSocket;
+struct event_base;
+
+typedef void (*pgs_handler_f)(struct PgSocket *pgs, void *arg, enum PgEvent dbev, PGresult *res);
+
+struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *arg);
+void pgs_free(struct PgSocket *db);
+
+void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base);
+
+void pgs_connect(struct PgSocket *db);
+void pgs_disconnect(struct PgSocket *db);
+void pgs_reconnect(struct PgSocket *db);
+
+void pgs_send_query_simple(struct PgSocket *db, const char *query);
+void pgs_send_query_params(struct PgSocket *db, const char *query, int nargs, ...);
+void pgs_send_query_params_list(struct PgSocket *db, const char *query, int nargs, const char *argv[]);
+
+void pgs_sleep(struct PgSocket *db, double timeout);
+
+int pgs_connection_valid(struct PgSocket *db);
+
+PGconn *pgs_get_connection(struct PgSocket *db);
+
+#endif
+
--- /dev/null
+/*
+ * SHA1 implementation based on RFC3174.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <usual/sha1.h>
+
+#include <usual/endian.h>
+
+/*
+ * Support functions.
+ */
+
+#define bufpos(ctx) ((ctx)->nbytes & (SHA1_BLOCK_SIZE - 1))
+#define rol32(v, s) (((v) << (s)) | ((v) >> (32 - (s))))
+
+/*
+ * SHA1 core.
+ */
+
+#define W(n) (buf[(n) & 15])
+#define setW(n, val) W(n) = val
+
+/* base SHA1 operation */
+#define SHA1OP(_t, fn, K) do { \
+ uint32_t tmp, t = (_t); \
+ if (t >= 16) { \
+ tmp = W(t - 3) ^ W(t - 8) ^ W(t - 14) ^ W(t - 16); \
+ setW(t, rol32(tmp, 1)); \
+ } else { \
+ /* convert endianess on first go */ \
+ setW(t, be32toh(W(t))); \
+ } \
+ tmp = rol32(a, 5) + fn(b, c, d) + e + W(t) + K; \
+ e = d; d = c; c = rol32(b, 30); b = a; a = tmp; \
+} while (0)
+
+/* mix functions */
+#define F0(b, c, d) (d ^ (b & (c ^ d)))
+#define F1(b, c, d) (b ^ c ^ d)
+#define F2(b, c, d) ((b & c) | (b & d) | (c & d))
+#define F3(b, c, d) (b ^ c ^ d)
+
+/* operation details for each round */
+#define SHA1R0(t) SHA1OP(t, F0, 0x5a827999)
+#define SHA1R1(t) SHA1OP(t, F1, 0x6ed9eba1)
+#define SHA1R2(t) SHA1OP(t, F2, 0x8f1bbcdc)
+#define SHA1R3(t) SHA1OP(t, F3, 0xca62c1d6)
+
+/* repeat with increasing offset */
+#define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3)
+#define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12)
+#define R20(R, t) R16(R, t+0); R4(R, t+16)
+
+static void sha1_core(struct sha1_ctx * ctx, uint32_t *buf)
+{
+ uint32_t a, b, c, d, e;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+ e = ctx->e;
+
+ R20(SHA1R0, 0);
+ R20(SHA1R1, 20);
+ R20(SHA1R2, 40);
+ R20(SHA1R3, 60);
+
+ ctx->a += a;
+ ctx->b += b;
+ ctx->c += c;
+ ctx->d += d;
+ ctx->e += e;
+}
+
+/*
+ * Public API.
+ */
+
+void sha1_reset(struct sha1_ctx *ctx)
+{
+ ctx->nbytes = 0;
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+ ctx->e = 0xc3d2e1f0;
+}
+
+void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len)
+{
+ unsigned int n;
+ const uint8_t *src = data;
+ uint8_t *dst = (uint8_t *)ctx->buf;
+
+ while (len > 0) {
+ n = SHA1_BLOCK_SIZE - bufpos(ctx);
+ if (n > len)
+ n = len;
+
+ memcpy(dst + bufpos(ctx), src, n);
+ src += n;
+ len -= n;
+ ctx->nbytes += n;
+
+ if (bufpos(ctx) == 0)
+ sha1_core(ctx, ctx->buf);
+ }
+}
+
+void sha1_final(uint8_t *dst, struct sha1_ctx *ctx)
+{
+ static const uint8_t padding[SHA1_BLOCK_SIZE] = { 0x80 };
+ uint64_t nbits = ctx->nbytes * 8;
+ int pad_len, pos = bufpos(ctx);
+
+ /* add padding */
+ pad_len = SHA1_BLOCK_SIZE - 8 - pos;
+ if (pad_len <= 0)
+ pad_len += SHA1_BLOCK_SIZE;
+ sha1_update(ctx, padding, pad_len);
+
+ /* add length */
+ ctx->buf[14] = htobe32(nbits >> 32);
+ ctx->buf[15] = htobe32(nbits);
+
+ /* final result */
+ sha1_core(ctx, ctx->buf);
+ be32enc(dst + 0*4, ctx->a);
+ be32enc(dst + 1*4, ctx->b);
+ be32enc(dst + 2*4, ctx->c);
+ be32enc(dst + 3*4, ctx->d);
+ be32enc(dst + 4*4, ctx->e);
+}
+
--- /dev/null
+/*
+ * SHA1 implementation based on RFC3174.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_SHA1_H_
+#define _USUAL_SHA1_H_
+
+#include <usual/base.h>
+
+#define SHA1_BLOCK_SIZE 64
+#define SHA1_DIGEST_LENGTH 20
+
+struct sha1_ctx {
+ uint64_t nbytes;
+ uint32_t a, b, c, d, e;
+ uint32_t buf[SHA1_BLOCK_SIZE / 4];
+};
+
+void sha1_reset(struct sha1_ctx *ctx);
+void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len);
+void sha1_final(uint8_t *dst, struct sha1_ctx *ctx);
+
+#ifndef AVOID_SHA1_COMPAT
+typedef struct sha1_ctx SHA1_CTX;
+#define SHA1Init(ctx) sha1_reset(ctx)
+#define SHA1Update(ctx, data, len) sha1_update(ctx, data, len)
+#define SHA1Final(dst, ctx) sha1_final(dst, ctx)
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * Circular list for shared mem.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_SHLIST_H_
+#define _USUAL_SHLIST_H_
+
+#include <usual/base.h>
+
+struct SHList {
+ uintptr_t next, prev;
+};
+
+/*
+ * Calculate offset relative to base.
+ *
+ * Instead of using some third pointer (eg. shmem start) as base,
+ * we use list itself as base. This results in simpler APi
+ * and also means that empty list appears as zero-filled.
+ */
+
+static inline uintptr_t _ptr2sh(const void *base, const void *ptr)
+{
+ return (uintptr_t)((char *)ptr - (char *)base);
+}
+
+static inline void *_sh2ptr(const void *base, uintptr_t sh)
+{
+ return (void *)((char *)base + sh);
+}
+
+/*
+ * List operations.
+ */
+
+static inline void shlist_init(struct SHList *list)
+{
+ list->next = _ptr2sh(list, list);
+ list->prev = _ptr2sh(list, list);
+}
+
+/* insert as last element */
+static inline void shlist_append(struct SHList *list, struct SHList *item)
+{
+ struct SHList *last = _sh2ptr(list, list->prev);
+ item->next = _ptr2sh(list, list);
+ item->prev = _ptr2sh(list, last);
+ list->prev = _ptr2sh(list, item);
+ last->next = _ptr2sh(list, item);
+}
+
+/* insert as first element */
+static inline void shlist_prepend(struct SHList *list, struct SHList *item)
+{
+ struct SHList *first = _sh2ptr(list, list->prev);
+ item->next = _ptr2sh(list, first);
+ item->prev = _ptr2sh(list, list);
+ list->next = _ptr2sh(list, item);
+ first->prev = _ptr2sh(list, item);
+}
+
+/* remove an item */
+static inline void shlist_remove(struct SHList *list, struct SHList *item)
+{
+ struct SHList *next = _sh2ptr(list, item->next);
+ struct SHList *prev = _sh2ptr(list, item->prev);
+ prev->next = item->next;
+ next->prev = item->prev;
+ item->next = item->prev = 0; /* _ptr2sh(list, item) does not make sense here; */
+}
+
+/* no elements? */
+static inline bool shlist_empty(const struct SHList *list)
+{
+ return list->next == list->prev;
+}
+
+/* return first elem */
+static inline struct SHList *shlist_first(const struct SHList *list)
+{
+ if (shlist_empty(list))
+ return NULL;
+ return _sh2ptr(list, list->next);
+}
+
+/* remove first elem */
+static inline struct SHList *shlist_pop(struct SHList *list)
+{
+ struct SHList *item = shlist_first(list);
+ if (item)
+ shlist_remove(list, item);
+ return item;
+}
+
+/* remove specific type of elem */
+#define shlist_pop_type(list, type, field) ( \
+ shlist_empty(list) ? NULL : container_of(shlist_pop(list), type, field))
+
+/* loop over list */
+#define shlist_for_each(item, list) \
+ for ((item) = _sh2ptr((list), (list)->next); \
+ (item) != (list); \
+ (item) = _sh2ptr((list), (item)->next))
+
+/* loop over list and allow removing item */
+#define shlist_for_each_safe(item, list, tmp) \
+ for ((item) = _sh2ptr((list), (list)->next), \
+ (tmp) = _sh2ptr((list), (item)->next); \
+ (item) != (list); \
+ (item) = (tmp), (tmp) = _sh2ptr((list), (tmp)->next))
+
+
+#endif
+
--- /dev/null
+/*
+ * Low-level UTF8 handling.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <usual/utf8.h>
+
+#define u8head(c, mask) (((c) & (mask | (mask >> 1))) == mask)
+#define u8tail(c) u8head(c, 0x80)
+
+/*
+ * conservative utf8 decoder
+ *
+ * if invalid char, advance src pointer by one and return 0.
+ * this can be ignored or replaced.
+ */
+uint32_t utf8_get_char(const uint8_t **src_p, const uint8_t *srcend)
+{
+ uint32_t c;
+ const uint8_t *p = *src_p;
+ /*
+ * 0xxx xxxx -> len=1
+ * 10xx xxxx -> tail byte
+ * 110x xxxx -> len=2
+ * 1110 xxxx -> len=3
+ * 1111 0xxx -> len=4
+ */
+ if (p[0] < 0x80) {
+ c = *p++;
+ } else if (u8head(p[0], 0xC0)) {
+ if (p + 2 > srcend)
+ goto eos;
+ if (!u8tail(p[1]))
+ goto bad_enc;
+
+ c = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F);
+ if (c < 0x80)
+ goto bad_enc;
+ p += 2;
+ } else if (u8head(p[0], 0xE0)) {
+ if (p + 3 > srcend)
+ goto eos;
+ if (!u8tail(p[1]) || !u8tail(p[2]))
+ goto bad_enc;
+
+ c = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F);
+ if ((c < 0x800) || ((c & 0xF800) == 0xD800))
+ goto bad_enc;
+ p += 3;
+ } else if (u8head(p[0], 0xF0)) {
+ if (p + 4 > srcend)
+ goto eos;
+ if (!u8tail(p[1]) || !u8tail(p[2]) || !u8tail(p[3]))
+ goto bad_enc;
+
+ c = ((p[0] & 0x1F) << 18) | ((p[1] & 0x3F) << 12)
+ | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F);
+ if (c < 0x10000 || c > 0x10FFFF)
+ goto bad_enc;
+ p += 4;
+ } else {
+ goto bad_enc;
+ }
+ *src_p = p;
+ return c;
+bad_enc:
+ *src_p = p + 1;
+ return 0;
+eos:
+ *src_p = srcend;
+ return 0;
+}
+
+/* encode one char - skip invalid ones */
+bool utf8_put_char(uint32_t c, uint8_t **dst_p, const uint8_t *dstend)
+{
+ uint8_t *dst = *dst_p;
+ if (c < 0x80) {
+ if (dst + 1 > dstend)
+ goto no_room;
+ *dst++ = c;
+ } else if (c < 0x800) {
+ if (dst + 2 > dstend)
+ goto no_room;
+ *dst++ = 0xC0 | (c >> 6);
+ *dst++ = 0x80 | (c & 0x3F);
+ } else if (c < 0x00010000) {
+ if (dst + 3 > dstend)
+ goto no_room;
+ if (c < 0xD800 || c > 0xDFFF) {
+ *dst++ = 0xE0 | (c >> 12);
+ *dst++ = 0x80 | ((c >> 6) & 0x3F);
+ *dst++ = 0x80 | (c & 0x3F);
+ }
+ } else if (c <= 0x10FFFF) {
+ if (dst + 4 > dstend)
+ goto no_room;
+ *dst++ = 0xF0 | (c >> 18);
+ *dst++ = 0x80 | ((c >> 12) & 0x3F);
+ *dst++ = 0x80 | ((c >> 6) & 0x3F);
+ *dst++ = 0x80 | (c & 0x3F);
+ }
+ *dst_p = dst;
+ return true;
+
+no_room:
+ return false;
+}
+
+unsigned utf8_char_size(uint32_t c)
+{
+ if (c < 0x80) return 1;
+ if (c < 0x800) return 2;
+ if (c < 0x1000) return 3;
+ return 4;
+}
+
+unsigned utf8_seq_size(uint8_t b)
+{
+ if (b < 0x80) return 1;
+ if (b < 0xC0) return 0;
+ if (b < 0xE0) return 2;
+ if (b < 0xF0) return 3;
+ if (b < 0xF8) return 4;
+ return 0;
+}
+
--- /dev/null
+/*
+ * Low-level UTF8 handling.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_UTF8_H_
+#define _USUAL_UTF8_H_
+
+#include <usual/base.h>
+
+uint32_t utf8_get_char(const uint8_t **src_p, const uint8_t *srcend);
+
+bool utf8_put_char(uint32_t c, uint8_t **dst_p, const uint8_t *dstend);
+
+unsigned utf8_char_size(uint32_t c);
+unsigned utf8_seq_size(uint8_t c);
+
+#endif
+