include few experimental modules.
authorMarko Kreen <markokr@gmail.com>
Wed, 2 Sep 2009 15:10:21 +0000 (18:10 +0300)
committerMarko Kreen <markokr@gmail.com>
Wed, 2 Sep 2009 16:47:22 +0000 (19:47 +0300)
crc32        - as the name says
sha1         - as the name says
endian       - standardise endianess macros
hashtab-impl - inline hash table
shlist       - list in shared mem
utf8         - low-level utf8 support
misc         - random unclassified stuff
pgsocket     - async postgres connection

They are "experimental" in the sense that their APIs,
layout and usefulness is uncertain.

15 files changed:
Makefile
m4/usual.m4
test/compile.c
usual/crc32.c [new file with mode: 0644]
usual/crc32.h [new file with mode: 0644]
usual/endian.h [new file with mode: 0644]
usual/hashtab-impl.h [new file with mode: 0644]
usual/misc.h [new file with mode: 0644]
usual/pgsocket.c [new file with mode: 0644]
usual/pgsocket.h [new file with mode: 0644]
usual/sha1.c [new file with mode: 0644]
usual/sha1.h [new file with mode: 0644]
usual/shlist.h [new file with mode: 0644]
usual/utf8.c [new file with mode: 0644]
usual/utf8.h [new file with mode: 0644]

index a7de354d507f5315d8af17e55a7a8172164fe83c..efc0f958d1d22b6cf9e645c5dbddbea0eeb5c4c0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ CPPFLAGS = $(USUAL_CPPFLAGS)
 # 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
index 406f55745536073e18499fa1515fff832aadadb9..424b7b32a88e5c74543e1196c0e53054604c84af 100644 (file)
@@ -103,7 +103,7 @@ AC_CHECK_HEADERS([sys/socket.h poll.h sys/poll.h sys/un.h])
 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
index 5b93196d502926ca1bf4e30869811accff3cef20..7c24e6b2946ed2348890211268fcedbdcfb87bdf 100644 (file)
@@ -2,21 +2,35 @@
 #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)
 {
diff --git a/usual/crc32.c b/usual/crc32.c
new file mode 100644 (file)
index 0000000..f4f5441
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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);
+}
+
diff --git a/usual/crc32.h b/usual/crc32.h
new file mode 100644 (file)
index 0000000..741d1cf
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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
+
diff --git a/usual/endian.h b/usual/endian.h
new file mode 100644 (file)
index 0000000..6ccab9c
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * 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_ */
diff --git a/usual/hashtab-impl.h b/usual/hashtab-impl.h
new file mode 100644 (file)
index 0000000..3a17e8b
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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);
+}
+
diff --git a/usual/misc.h b/usual/misc.h
new file mode 100644 (file)
index 0000000..8a3de22
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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
+
diff --git a/usual/pgsocket.c b/usual/pgsocket.c
new file mode 100644 (file)
index 0000000..4b19c26
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * 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;
+}
+
diff --git a/usual/pgsocket.h b/usual/pgsocket.h
new file mode 100644 (file)
index 0000000..0b10ff3
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+
diff --git a/usual/sha1.c b/usual/sha1.c
new file mode 100644 (file)
index 0000000..c4e03d5
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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);
+}
+
diff --git a/usual/sha1.h b/usual/sha1.h
new file mode 100644 (file)
index 0000000..13d15ce
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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
+
diff --git a/usual/shlist.h b/usual/shlist.h
new file mode 100644 (file)
index 0000000..1b9060a
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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
+
diff --git a/usual/utf8.c b/usual/utf8.c
new file mode 100644 (file)
index 0000000..ee53414
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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;
+}
+
diff --git a/usual/utf8.h b/usual/utf8.h
new file mode 100644 (file)
index 0000000..06bcef3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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
+