LuKaicheng vor 6 Jahren
Ursprung
Commit
772f159473

+ 145 - 0
libcs/inc/ae.h

@@ -0,0 +1,145 @@
+/* A simple event-driven programming library. Originally I wrote this code
+ * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
+ * it in form of a library for easy reuse.
+ *
+ * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __AE_H__
+#define __AE_H__
+
+#include <time.h>
+
+#define AE_OK                0
+#define AE_ERR              -1
+
+#define AE_NONE              0
+#define AE_READABLE          1
+#define AE_WRITABLE          2
+
+#define AE_FILE_EVENTS       1
+#define AE_TIME_EVENTS       2
+#define AE_ALL_EVENTS        (AE_FILE_EVENTS | AE_TIME_EVENTS)
+#define AE_DONT_WAIT         4
+#define AE_CALL_AFTER_SLEEP  8
+
+#define AE_NOMORE           -1
+#define AE_DELETED_EVENT_ID -1
+
+/* Macros */
+#define AE_NOTUSED(V) ((void) V)
+
+#define zmalloc malloc
+#define zfree free
+#define zrealloc realloc
+
+struct aeEventLoop;
+
+/* Types and data structures */
+typedef void aeFileProc(
+  struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
+typedef int aeTimeProc(
+  struct aeEventLoop *eventLoop, long long id, void *clientData);
+typedef void aeEventFinalizerProc(
+  struct aeEventLoop *eventLoop, void *clientData);
+typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
+
+/* File event structure */
+typedef struct aeFileEvent {
+  int mask; /* one of AE_(READABLE|WRITABLE) */
+  aeFileProc *rfileProc;
+  aeFileProc *wfileProc;
+  void *clientData;
+} aeFileEvent;
+
+/* Time event structure */
+typedef struct aeTimeEvent {
+  long long id; /* time event identifier. */
+  long when_sec; /* seconds */
+  long when_ms; /* milliseconds */
+  aeTimeProc *timeProc;
+  aeEventFinalizerProc *finalizerProc;
+  void *clientData;
+  struct aeTimeEvent *next;
+} aeTimeEvent;
+
+/* A fired event */
+typedef struct aeFiredEvent {
+  int fd;
+  int mask;
+} aeFiredEvent;
+
+/* State of an event based program */
+typedef struct aeEventLoop {
+  int maxfd;   /* highest file descriptor currently registered */
+  int setsize; /* max number of file descriptors tracked */
+  long long timeEventNextId;
+  time_t lastTime;     /* Used to detect system clock skew */
+  aeFileEvent *events; /* Registered events */
+  aeFiredEvent *fired; /* Fired events */
+  aeTimeEvent *timeEventHead;
+  int stop;
+  void *apidata; /* This is used for polling API specific data */
+  aeBeforeSleepProc *beforesleep;
+  aeBeforeSleepProc *aftersleep;
+} aeEventLoop;
+
+/* Prototypes */
+CS_API aeEventLoop *aeCreateEventLoop(int setsize);
+CS_API void         aeDeleteEventLoop(aeEventLoop *eventLoop);
+CS_API void         aeStop(aeEventLoop *eventLoop);
+CS_API int          aeCreateFileEvent(
+  aeEventLoop *eventLoop, 
+  int fd, 
+  int mask, 
+  aeFileProc *proc, 
+  void *clientData);
+CS_API void         aeDeleteFileEvent(
+  aeEventLoop *eventLoop, int fd, int mask);
+CS_API int          aeGetFileEvents(aeEventLoop *eventLoop, int fd);
+CS_API long long    aeCreateTimeEvent(
+  aeEventLoop *eventLoop, 
+  long long milliseconds, 
+  aeTimeProc *proc, 
+  void *clientData, 
+  aeEventFinalizerProc *finalizerProc);
+CS_API int          aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);
+CS_API int          aeProcessEvents(aeEventLoop *eventLoop, int flags);
+CS_API int          aeWait(int fd, int mask, long long milliseconds);
+CS_API void         aeMain(aeEventLoop *eventLoop);
+CS_API char        *aeGetApiName(void);
+CS_API void         aeSetBeforeSleepProc(
+  aeEventLoop *eventLoop, 
+  aeBeforeSleepProc *beforesleep);
+CS_API void         aeSetAfterSleepProc(
+  aeEventLoop *eventLoop, 
+  aeBeforeSleepProc *aftersleep);
+CS_API int          aeGetSetSize(aeEventLoop *eventLoop);
+CS_API int          aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
+
+#endif

+ 145 - 0
libcs/inc/cs.h

@@ -0,0 +1,145 @@
+/**
+ * csacred.com crt
+ * Copyright (c) 2007-2018 ls
+ **/
+#ifndef _CS_CRT_H_INCLUDED_
+#define _CS_CRT_H_INCLUDED_
+
+#define cs_version 062
+#define CS_VERSION "0.6.2"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>	
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <sys/timeb.h>
+#include <errno.h>
+
+#define CS_OK          0
+#define CS_ERR        -1
+#define CS_AGAIN      -2
+#define CS_BUSY       -3
+#define CS_DONE       -4
+#define CS_DECLINED   -5
+#define CS_ABORT      -6
+
+/*
+ * Platform defined
+ * 
+ * __linux__    // Linux
+ * __APPLE__    // osx
+ * __MVS__      // os390
+ * _PASE        // posix
+ * _AIX         // aix
+ * __sun        // sun os
+ * __FreeBSD__  // freebsd
+ * __OpenBSD__  // openbsd
+ * __NetBSD__   // netbsd
+ **/
+
+/* Platform */
+#ifdef _WIN32 /* Windows */
+#include <winsock2.h>
+//#include <mswsock.h>
+#include <wininet.h>
+#include <winreg.h>
+	
+#include <process.h>
+#include <tchar.h>
+#include <io.h>
+#include <ws2tcpip.h>
+
+#ifndef LIBCS_STATIC
+#ifdef  LIBCS_EXPORTS
+#define CS_API _declspec(dllexport)
+#else
+#define CS_API _declspec(dllimport)
+#endif
+#else
+#define CS_API
+#endif
+#define cs_inline     __inline
+
+#define cs_int64_t    __int64
+#define cs_uint64_t   unsigned __int64
+#define cs_sock_t     SOCKET
+
+#define cs_s64        "%I64d"
+#define cs_us64       "%I64u"
+
+#if _MSC_VER > 1200 /* VC6+ */
+typedef intptr_t      cs_int_t;
+typedef uintptr_t     cs_uint_t;
+#else /* VC6 */
+#define _WIN32_WINNT  0x0500
+typedef int           cs_int_t;
+typedef unsigned int  cs_uint_t;
+#endif
+
+#define cs_errno      GetLastError()
+//#define cs_sock_errno GetLastError() // WSAGetLastError()
+#else /* Linux/BSD/... */
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>  /* TCP_NODELAY, TCP_CORK */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <poll.h>
+#include <sys/malloc.h>
+#include <sys/time.h>
+
+#define CS_API
+#define cs_inline     inline
+
+typedef intptr_t      cs_int_t;
+typedef uintptr_t     cs_uint_t;
+#define cs_int64_t    int64_t
+#define cs_uint64_t   uint64_t
+#define cs_sock_t     int
+
+#define cs_errno      errno
+//#define cs_sock_errno errno
+
+#define cs_s64        "%lld"
+#define cs_us64       "%llu"
+#endif
+
+#ifndef u_char
+#define u_char unsigned char
+#endif
+
+#define CS_ALIGNMENT              sizeof(unsigned long) /* platform word */
+
+#define cs_align(d, a)            (((d) + (a - 1)) & ~(a - 1))
+#define cs_align_ptr(p, a)        (u_char *) (((uintptr_t) (p) +  \
+  ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
+
+#define cs_offsetof(type, member) ((size_t)&((type *)0)->member)
+#ifdef _WIN32
+#define cs_container_of(ptr, type, member) (type *)((char *)ptr - \
+  cs_offsetof(type, member))
+#else
+#define cs_container_of(ptr, type, member) ({                     \
+  const typeof(((type *)0)->member) *__mptr = (ptr);              \
+  (type *)((char *)__mptr - cs_offsetof(type, member));})
+#endif
+
+CS_API const u_char *cs_libversion();
+
+#ifdef __cplusplus
+}
+#endif /* C++ support */
+#endif

+ 42 - 0
libcs/inc/cs_list.h

@@ -0,0 +1,42 @@
+/**
+ * cs_list.h
+ * Copyright (c) 2007-2018 ls
+ **/
+#ifndef _CS_LIST_H_INCLUDED_
+#define _CS_LIST_H_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cs_list_node_s {
+	void *data;
+	struct cs_list_node_s *prev, *next;
+} cs_list_node_t;
+ 
+typedef struct cs_list_s {
+	cs_list_node_t *head, *tail;
+} cs_list_t;
+
+#define cs_list_head(list)                    (list)->head
+#define cs_list_for_each(list, pos)           for (\
+  pos = (list)->head; (pos) != NULL; pos = (pos)->next)
+#define cs_list_for_each_safe(list, pos, tmp)	for ( \
+  pos = (list)->head, tmp = pos; (pos) != NULL; pos = (tmp)->next, tmp = pos)
+
+CS_API void cs_list_init(cs_list_t *list); 
+CS_API void cs_list_add_head(cs_list_t *list, cs_list_node_t *node);
+CS_API void cs_list_add_tail(cs_list_t *list, cs_list_node_t *node);
+CS_API void cs_list_add_before(
+  cs_list_t *list, cs_list_node_t *old, cs_list_node_t *node);
+CS_API void cs_list_add_after(
+  cs_list_t *list, cs_list_node_t *old, cs_list_node_t *node);
+CS_API void cs_list_del(cs_list_t *list, cs_list_node_t *node);
+
+CS_API cs_list_node_t *cs_list_find_u32(cs_list_t *list, cs_uint_t key);
+CS_API cs_list_node_t *cs_list_find_u64(cs_list_t *list, cs_uint64_t key);
+
+#ifdef __cplusplus
+}
+#endif /* C++ support */
+#endif

+ 30 - 0
libcs/inc/cs_os.h

@@ -0,0 +1,30 @@
+/**
+ * cs_os.h
+ * Copyright (c) 2007-2018 ls
+ **/
+#ifndef _CS_OS_H_INCLUDED_
+#define _CS_OS_H_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+* cs_os_cpu_count
+*	Return the number of CPUs.
+*/
+CS_API cs_uint_t cs_os_cpu_count();
+CS_API cs_int_t  cs_os_pagesize();
+
+#ifdef _WIN32
+/*
+* cs_os_is_winnt
+* Return 1 if Windows/NT, otherwise 0.
+*/
+CS_API cs_int_t  cs_os_is_winnt();
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* C++ support */
+#endif

+ 56 - 0
libcs/inc/cs_rbtree.h

@@ -0,0 +1,56 @@
+/**
+ * cs_rbtree.h
+ * Copyright (c) 2007-2018 ls
+ **/
+#ifndef _CS_RBTREE_H_INCLUDED_
+#define _CS_RBTREE_H_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cs_rbtree_node_s {
+	void *key;
+	void *data;
+
+	unsigned char color;	/* if color=0 then the node is black */
+
+	struct cs_rbtree_node_s *left, *right, *parent;
+} cs_rbtree_node_t;
+
+typedef struct cs_rbtree_s {
+	cs_rbtree_node_t *root, *nil;
+
+	int (*cmp)(void *d, void *s);
+} cs_rbtree_t;
+
+#define CS_TREE_RB_RED(n)        ((n)->color = 1)
+#define CS_TREE_RB_BLACK(n)      ((n)->color = 0)
+#define CS_TREE_RB_ISRED(n)      ((n)->color)
+#define CS_TREE_RB_ISBLACK(n)    (!CS_TREE_RB_ISRED(n))
+#define CS_TREE_RB_CPYCOLOR(d,s) ((d)->color = (s)->color)
+
+#define CS_TREE_RB_ISNIL(n)      (!CS_TREE_RB_ISRED(n))
+
+#define CS_TREE_RB_LT(t, a, b)   ((t)->cmp((a), (b)) < 0)  // ((a) < (b))
+#define CS_TREE_RB_GT(t, a, b)   ((t)->cmp((a), (b)) > 0)  // ((a) > (b))
+#define CS_TREE_RB_EQ(t, a, b)   ((t)->cmp((a), (b)) == 0) // ((a) == (b))
+
+CS_API void cs_rbtree_init(
+  cs_rbtree_t *t, cs_rbtree_node_t *nil, int (*cmp)(void *, void *));
+
+CS_API cs_rbtree_node_t *cs_rbtree_add(cs_rbtree_t *t, cs_rbtree_node_t *n);
+CS_API void cs_rbtree_del(cs_rbtree_t *t, cs_rbtree_node_t *n);
+
+CS_API cs_rbtree_node_t *cs_rbtree_query(cs_rbtree_t *t, void *k);
+CS_API cs_rbtree_node_t *cs_rbtree_min(cs_rbtree_t *t);
+CS_API cs_rbtree_node_t *cs_rbtree_max(cs_rbtree_t *t);
+
+CS_API void cs_rbtree_order(
+  cs_rbtree_t *t, 
+  void (*cs_rbtree_callback)(cs_rbtree_node_t *));
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+#endif

+ 118 - 0
libcs/inc/cs_str.h

@@ -0,0 +1,118 @@
+/**
+ * cs_str.h
+ * Copyright (c) 2007-2018 ls
+ **/
+#ifndef _CS_STR_H_INCLUDED_
+#define _CS_STR_H_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 
+* converts hex char (0-9, A-Z, a-z) to decimal.
+* returns 0xFF on invalid input.
+	*/
+#define X2C(x, c)  {                  \
+	c = (u_char)(x - '0'); if (c > 9) { \
+  c = (u_char)((c + '0' - 1) | 0x20); \
+	c = (u_char)(c - 'a' + 11);}}
+
+#define CS_KB  1 << 10
+#define CS_MB  1 << 20
+#define CS_GB  1 << 30
+
+typedef struct cs_buf_s{
+	u_char *buf;
+	size_t used;
+	size_t size;
+} cs_buf_t;
+
+#define cs_buf_str(b)     (b)->buf
+#define cs_buf_used(b)    (b)->used
+#define cs_buf_size(b)    (b)->size
+#define cs_buf_strend(b) *((b)->buf + (b)->used) = '\0'
+#define cs_buf_isempty(b) ((b)->size == 0)
+
+#define cs_malloc         malloc
+#define cs_free           free
+
+#define CS_MALLOC(T, n)   (T *)cs_malloc(sizeof(T) * (n))
+
+#define cs_bcpy(d, s, n)  (u_char *)memcpy(d, s, n)
+#define cs_bcmp(d, s, n)  memcmp(d, s, n)
+#define cs_bchr(s, c, n)  (u_char *)memchr(s, c, n)
+#define cs_bset(s, v, n)  memset(s, v, n)
+#define cs_bzero(s, n)    memset(s, 0, n)
+
+CS_API size_t      cs_strerror(u_char *err, const char *fmt, ...);
+
+CS_API size_t      cs_strlen(const u_char *s);
+
+CS_API u_char     *cs_tolower(u_char *s);
+CS_API u_char     *cs_toupper(u_char *s);
+
+CS_API u_char     *cs_strcpy(u_char *d, const u_char *s);
+CS_API u_char     *cs_strncpy(u_char *d, const u_char *s, size_t n);
+
+CS_API u_char     *cs_strcat(u_char *d, const u_char *s);
+CS_API u_char     *cs_strncat(u_char *d, const u_char *s, size_t n);
+
+CS_API int         cs_strcmp(const u_char *s1, const u_char *s2);
+CS_API int         cs_strncmp(const u_char *s1, const u_char *s2, size_t n);
+
+CS_API int         cs_strcasecmp(u_char *s1, u_char *s2);
+CS_API int         cs_strncasecmp(u_char *s1, u_char *s2, size_t n);
+
+CS_API u_char     *cs_strchr(const u_char *s, u_char c);
+CS_API u_char     *cs_strstr(const u_char *s, u_char *d);
+CS_API u_char     *cs_strrchr(const u_char *s, u_char c);
+CS_API size_t      cs_strchrcpy(u_char *d, const u_char *s, u_char c);
+
+CS_API u_char     *cs_strdup(u_char *s, size_t n);
+
+CS_API cs_buf_t   *cs_buf_init(u_char *p, size_t n);
+CS_API cs_buf_t   *cs_buf_new(size_t n);
+CS_API void        cs_buf_free(cs_buf_t *buf);
+CS_API size_t      cs_buf_cat(cs_buf_t *d, cs_buf_t *s);
+CS_API u_char     *cs_buf_bcpy(cs_buf_t *buf, u_char *s, size_t n);
+CS_API u_char     *cs_buf_bcat(cs_buf_t *buf, u_char *s, size_t n);
+CS_API int         cs_buf_cmp(cs_buf_t *d, cs_buf_t *s);
+
+CS_API cs_int_t    cs_atoi(u_char *s, size_t n);
+CS_API cs_uint_t   cs_atou(u_char *s, size_t n);
+
+CS_API cs_int64_t  cs_atoi64(u_char *s, size_t n);
+
+CS_API cs_int_t    cs_htoi(u_char *s, size_t n);
+CS_API cs_uint_t   cs_htou(u_char *s, size_t n);
+
+CS_API cs_int_t    cs_btoi(u_char *s, size_t n);
+CS_API cs_int_t    cs_btou(u_char *s, size_t n);
+
+CS_API size_t      cs_uri_escape(u_char *d, const u_char *s);
+CS_API size_t      cs_uri_unescape(u_char *d, const u_char *s);
+
+CS_API size_t      cs_json_escape(u_char *d, u_char *s, size_t n);
+
+CS_API size_t      cs_hex_dump(u_char *d, u_char *s, size_t n);
+CS_API size_t      cs_hex_undump(u_char *d, const u_char *s, size_t n);
+
+CS_API void        cs_base64_encode(cs_buf_t *d, cs_buf_t *s);
+CS_API cs_int_t    cs_base64_decode(cs_buf_t *d, cs_buf_t *s);
+
+CS_API cs_uint_t   cs_utf8_decode(u_char **s, size_t n);
+CS_API size_t      cs_utf8_length(u_char *s, size_t n);
+
+CS_API cs_uint_t   cs_hash_djb2(const u_char *s);
+CS_API cs_uint64_t cs_hash_djb2_64(const u_char *s);
+
+CS_API u_char     *cs_md5(u_char *d, const u_char *s, size_t n);
+CS_API u_char     *cs_sha1(u_char *d, const u_char *s, size_t n);
+
+CS_API cs_uint_t   cs_crc32c(const u_char *s, size_t n);
+
+#ifdef __cplusplus
+}
+#endif /* C++ support */
+#endif

+ 37 - 0
libcs/inc/cs_tcp.h

@@ -0,0 +1,37 @@
+/**
+ * cs_tcp.h
+ * Copyright (c) 2007-2018 ls
+ **/
+#ifndef _CS_TCP_H_INCLUDED_
+#define _CS_TCP_H_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CS_TCP_CONNECT_NONE     0x0
+#define CS_TCP_CONNECT_NONBLOCK 0x01
+
+#ifdef _WIN32
+CS_API int       cs_tcp_init();
+CS_API void      cs_tcp_cleanup();
+#endif
+
+CS_API cs_sock_t cs_tcp_v4_server(int port, char *bindaddr);
+CS_API cs_sock_t cs_tcp_v6_server(int port, char *bindaddr);
+CS_API cs_sock_t cs_tcp_accept(cs_sock_t s, u_char *ip, size_t ip_len, int *port);
+
+CS_API int       cs_tcp_enable_nodelay(cs_sock_t fd);
+CS_API int       cs_tcp_disable_nodelay(cs_sock_t fd);
+CS_API int       cs_tcp_keepalive(cs_sock_t fd);
+CS_API int       cs_tcp_sndtimeo(cs_sock_t fd, long long ms);
+CS_API int       cs_tcp_rcvtimeo(cs_sock_t fd, long long ms);
+CS_API int       cs_tcp_nonblocking(cs_sock_t fd);
+CS_API int       cs_tcp_blocking(cs_sock_t fd);
+
+CS_API int       cs_tcp_close(cs_sock_t fd);
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+#endif

+ 27 - 0
libcs/inc/ngx_alloc.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+#ifndef _NGX_ALLOC_H_INCLUDED_
+#define _NGX_ALLOC_H_INCLUDED_
+
+#include <stdlib.h>
+
+void *ngx_alloc(size_t size);
+void *ngx_calloc(size_t size);
+
+#define ngx_free free
+
+/*
+ * Linux has memalign() or posix_memalign()
+ * Solaris has memalign()
+ * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
+ * aligns allocations bigger than page size at the page boundary
+ */
+#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)
+void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);
+#else
+#define ngx_memalign(alignment, size)  ngx_alloc(size)
+#endif
+
+#endif /* _NGX_ALLOC_H_INCLUDED_ */

+ 71 - 0
libcs/inc/ngx_palloc.h

@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+		   
+#ifndef _NGX_PALLOC_H_INCLUDED_
+#define _NGX_PALLOC_H_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#define ngx_pagesize cs_os_pagesize()
+#else
+#define ngx_pagesize getpagesize()
+#endif
+/*
+ * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
+ * On Windows NT it decreases a number of locked pages in a kernel.
+ */
+#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)
+
+#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)
+
+#define NGX_POOL_ALIGNMENT       16
+
+typedef struct ngx_pool_s        ngx_pool_t;
+typedef struct ngx_pool_large_s  ngx_pool_large_t;
+
+#define NGX_MIN_POOL_SIZE        \
+cs_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \
+NGX_POOL_ALIGNMENT)
+
+struct ngx_pool_large_s {
+  ngx_pool_large_t     *next;
+  void                 *alloc;
+};
+
+typedef struct {
+  u_char               *last;
+  u_char               *end;
+  ngx_pool_t           *next;
+  cs_uint_t            failed;
+} ngx_pool_data_t;
+
+
+struct ngx_pool_s {
+  ngx_pool_data_t       d;
+  size_t                max;
+  ngx_pool_t           *current;
+  ngx_pool_large_t     *large;
+};
+
+void       *ngx_alloc(size_t size);
+void       *ngx_calloc(size_t size);
+
+CS_API ngx_pool_t *ngx_create_pool(size_t size);
+CS_API void        ngx_destroy_pool(ngx_pool_t *pool);
+CS_API void        ngx_reset_pool(ngx_pool_t *pool);
+
+CS_API void       *ngx_palloc(ngx_pool_t *pool, size_t size);
+CS_API void       *ngx_pnalloc(ngx_pool_t *pool, size_t size);
+CS_API void       *ngx_pcalloc(ngx_pool_t *pool, size_t size);
+CS_API void       *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
+CS_API cs_int_t    ngx_pfree(ngx_pool_t *pool, void *p);
+
+#ifdef __cplusplus
+}
+#endif /* C++ support */
+#endif /* _NGX_PALLOC_H_INCLUDED_ */

+ 535 - 0
libcs/inc/queue.h

@@ -0,0 +1,535 @@
+/*      $OpenBSD: queue.h,v 1.44 2016/09/09 20:31:46 millert Exp $      */
+/*      $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $       */
+
+/*
+ * Copyright (c) 1991, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      @(#)queue.h     8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+//#include <sys/_null.h>
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues and XOR simple queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * An XOR simple queue is used in the same way as a regular simple queue.
+ * The difference is that the head structure also includes a "cookie" that
+ * is XOR'd with the queue pointer (first, last or next) to generate the
+ * real pointer value.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)                                          \
+struct name {                                                           \
+        struct type *slh_first; /* first element */                     \
+}
+
+#define SLIST_HEAD_INITIALIZER(head)                                    \
+        { NULL }
+
+#define SLIST_ENTRY(type)                                               \
+struct {                                                                \
+        struct type *sle_next;  /* next element */                      \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head)       ((head)->slh_first)
+#define SLIST_END(head)         NULL
+#define SLIST_EMPTY(head)       (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field)                                 \
+        for((var) = SLIST_FIRST(head);                                  \
+            (var) != SLIST_END(head);                                   \
+            (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
+        for ((var) = SLIST_FIRST(head);                         \
+            (var) && ((tvar) = SLIST_NEXT(var, field), 1);              \
+            (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) {                                              \
+        SLIST_FIRST(head) = SLIST_END(head);                            \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+        (elm)->field.sle_next = (slistelm)->field.sle_next;             \
+        (slistelm)->field.sle_next = (elm);                             \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do {                        \
+        (elm)->field.sle_next = (head)->slh_first;                      \
+        (head)->slh_first = (elm);                                      \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do {                             \
+        (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;  \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do {                             \
+        (head)->slh_first = (head)->slh_first->field.sle_next;          \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {                       \
+        if ((head)->slh_first == (elm)) {                               \
+                SLIST_REMOVE_HEAD((head), field);                       \
+        } else {                                                        \
+                struct type *curelm = (head)->slh_first;                \
+                                                                        \
+                while (curelm->field.sle_next != (elm))                 \
+                        curelm = curelm->field.sle_next;                \
+                curelm->field.sle_next =                                \
+                    curelm->field.sle_next->field.sle_next;             \
+        }                                                               \
+        _Q_INVALIDATE((elm)->field.sle_next);                           \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)                                           \
+struct name {                                                           \
+        struct type *lh_first;  /* first element */                     \
+}
+
+#define LIST_HEAD_INITIALIZER(head)                                     \
+        { NULL }
+
+#define LIST_ENTRY(type)                                                \
+struct {                                                                \
+        struct type *le_next;   /* next element */                      \
+        struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List access methods.
+ */
+#define LIST_FIRST(head)                ((head)->lh_first)
+#define LIST_END(head)                  NULL
+#define LIST_EMPTY(head)                (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field)           ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)                                  \
+        for((var) = LIST_FIRST(head);                                   \
+            (var)!= LIST_END(head);                                     \
+            (var) = LIST_NEXT(var, field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar)                       \
+        for ((var) = LIST_FIRST(head);                          \
+            (var) && ((tvar) = LIST_NEXT(var, field), 1);               \
+            (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do {                                            \
+        LIST_FIRST(head) = LIST_END(head);                              \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+        if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \
+                (listelm)->field.le_next->field.le_prev =               \
+                    &(elm)->field.le_next;                              \
+        (listelm)->field.le_next = (elm);                               \
+        (elm)->field.le_prev = &(listelm)->field.le_next;               \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+        (elm)->field.le_prev = (listelm)->field.le_prev;                \
+        (elm)->field.le_next = (listelm);                               \
+        *(listelm)->field.le_prev = (elm);                              \
+        (listelm)->field.le_prev = &(elm)->field.le_next;               \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {                         \
+        if (((elm)->field.le_next = (head)->lh_first) != NULL)          \
+                (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+        (head)->lh_first = (elm);                                       \
+        (elm)->field.le_prev = &(head)->lh_first;                       \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {                                    \
+        if ((elm)->field.le_next != NULL)                               \
+                (elm)->field.le_next->field.le_prev =                   \
+                    (elm)->field.le_prev;                               \
+        *(elm)->field.le_prev = (elm)->field.le_next;                   \
+        _Q_INVALIDATE((elm)->field.le_prev);                            \
+        _Q_INVALIDATE((elm)->field.le_next);                            \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {                             \
+        if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)     \
+                (elm2)->field.le_next->field.le_prev =                  \
+                    &(elm2)->field.le_next;                             \
+        (elm2)->field.le_prev = (elm)->field.le_prev;                   \
+        *(elm2)->field.le_prev = (elm2);                                \
+        _Q_INVALIDATE((elm)->field.le_prev);                            \
+        _Q_INVALIDATE((elm)->field.le_next);                            \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)                                        \
+struct name {                                                           \
+        struct type *sqh_first; /* first element */                     \
+        struct type **sqh_last; /* addr of last next element */         \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)                                  \
+        { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)                                             \
+struct {                                                                \
+        struct type *sqe_next;  /* next element */                      \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head)         ((head)->sqh_first)
+#define SIMPLEQ_END(head)           NULL
+#define SIMPLEQ_EMPTY(head)         (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)                               \
+        for((var) = SIMPLEQ_FIRST(head);                                \
+            (var) != SIMPLEQ_END(head);                                 \
+            (var) = SIMPLEQ_NEXT(var, field))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar)                    \
+        for ((var) = SIMPLEQ_FIRST(head);                               \
+            (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1);            \
+            (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do {                                         \
+        (head)->sqh_first = NULL;                                       \
+        (head)->sqh_last = &(head)->sqh_first;                          \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {                      \
+        if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)        \
+                (head)->sqh_last = &(elm)->field.sqe_next;              \
+        (head)->sqh_first = (elm);                                      \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {                      \
+        (elm)->field.sqe_next = NULL;                                   \
+        *(head)->sqh_last = (elm);                                      \
+        (head)->sqh_last = &(elm)->field.sqe_next;                      \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {            \
+        if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+                (head)->sqh_last = &(elm)->field.sqe_next;              \
+        (listelm)->field.sqe_next = (elm);                              \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do {                   \
+        if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+                (head)->sqh_last = &(head)->sqh_first;                  \
+} while (0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do {                     \
+        if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+            == NULL)                                                    \
+                (head)->sqh_last = &(elm)->field.sqe_next;              \
+} while (0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do {                               \
+        if (!SIMPLEQ_EMPTY((head2))) {                                  \
+                *(head1)->sqh_last = (head2)->sqh_first;                \
+                (head1)->sqh_last = (head2)->sqh_last;                  \
+                SIMPLEQ_INIT((head2));                                  \
+        }                                                               \
+} while (0)
+
+/*
+ * XOR Simple queue definitions.
+ */
+#define XSIMPLEQ_HEAD(name, type)                                       \
+struct name {                                                           \
+        struct type *sqx_first; /* first element */                     \
+        struct type **sqx_last; /* addr of last next element */         \
+        unsigned long sqx_cookie;                                       \
+}
+
+#define XSIMPLEQ_ENTRY(type)                                            \
+struct {                                                                \
+        struct type *sqx_next;  /* next element */                      \
+}
+
+/*
+ * XOR Simple queue access methods.
+ */
+#define XSIMPLEQ_XOR(head, ptr)     ((__typeof(ptr))((head)->sqx_cookie ^ \
+                                        (unsigned long)(ptr)))
+#define XSIMPLEQ_FIRST(head)        XSIMPLEQ_XOR(head, ((head)->sqx_first))
+#define XSIMPLEQ_END(head)          NULL
+#define XSIMPLEQ_EMPTY(head)        (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
+#define XSIMPLEQ_NEXT(head, elm, field)    XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
+
+
+#define XSIMPLEQ_FOREACH(var, head, field)                              \
+        for ((var) = XSIMPLEQ_FIRST(head);                              \
+            (var) != XSIMPLEQ_END(head);                                \
+            (var) = XSIMPLEQ_NEXT(head, var, field))
+
+#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar)                   \
+        for ((var) = XSIMPLEQ_FIRST(head);                              \
+            (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1);     \
+            (var) = (tvar))
+
+/*
+ * XOR Simple queue functions.
+ */
+#define XSIMPLEQ_INIT(head) do {                                        \
+        arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
+        (head)->sqx_first = XSIMPLEQ_XOR(head, NULL);                   \
+        (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first);      \
+} while (0)
+
+#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do {                     \
+        if (((elm)->field.sqx_next = (head)->sqx_first) ==              \
+            XSIMPLEQ_XOR(head, NULL))                                   \
+                (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+        (head)->sqx_first = XSIMPLEQ_XOR(head, (elm));                  \
+} while (0)
+
+#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do {                     \
+        (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL);               \
+        *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
+        (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);  \
+} while (0)
+
+#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {           \
+        if (((elm)->field.sqx_next = (listelm)->field.sqx_next) ==      \
+            XSIMPLEQ_XOR(head, NULL))                                   \
+                (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+        (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm));          \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_HEAD(head, field) do {                          \
+        if (((head)->sqx_first = XSIMPLEQ_XOR(head,                     \
+            (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
+                (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do {                    \
+        if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head,                 \
+            (elm)->field.sqx_next)->field.sqx_next)                     \
+            == XSIMPLEQ_XOR(head, NULL))                                \
+                (head)->sqx_last =                                      \
+                    XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);         \
+} while (0)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)                                          \
+struct name {                                                           \
+        struct type *tqh_first; /* first element */                     \
+        struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)                                    \
+        { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)                                               \
+struct {                                                                \
+        struct type *tqe_next;  /* next element */                      \
+        struct type **tqe_prev; /* address of previous next element */  \
+}
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_FIRST(head)               ((head)->tqh_first)
+#define TAILQ_END(head)                 NULL
+#define TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)                                      \
+        (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)                                \
+        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head)                                               \
+        (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)                                 \
+        for((var) = TAILQ_FIRST(head);                                  \
+            (var) != TAILQ_END(head);                                   \
+            (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
+        for ((var) = TAILQ_FIRST(head);                                 \
+            (var) != TAILQ_END(head) &&                                 \
+            ((tvar) = TAILQ_NEXT(var, field), 1);                       \
+            (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+        for((var) = TAILQ_LAST(head, headname);                         \
+            (var) != TAILQ_END(head);                                   \
+            (var) = TAILQ_PREV(var, headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
+        for ((var) = TAILQ_LAST(head, headname);                        \
+            (var) != TAILQ_END(head) &&                                 \
+            ((tvar) = TAILQ_PREV(var, headname, field), 1);             \
+            (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do {                                           \
+        (head)->tqh_first = NULL;                                       \
+        (head)->tqh_last = &(head)->tqh_first;                          \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+        if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)        \
+                (head)->tqh_first->field.tqe_prev =                     \
+                    &(elm)->field.tqe_next;                             \
+        else                                                            \
+                (head)->tqh_last = &(elm)->field.tqe_next;              \
+        (head)->tqh_first = (elm);                                      \
+        (elm)->field.tqe_prev = &(head)->tqh_first;                     \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+        (elm)->field.tqe_next = NULL;                                   \
+        (elm)->field.tqe_prev = (head)->tqh_last;                       \
+        *(head)->tqh_last = (elm);                                      \
+        (head)->tqh_last = &(elm)->field.tqe_next;                      \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+        if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+                (elm)->field.tqe_next->field.tqe_prev =                 \
+                    &(elm)->field.tqe_next;                             \
+        else                                                            \
+                (head)->tqh_last = &(elm)->field.tqe_next;              \
+        (listelm)->field.tqe_next = (elm);                              \
+        (elm)->field.tqe_prev = &(listelm)->field.tqe_next;             \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+        (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+        (elm)->field.tqe_next = (listelm);                              \
+        *(listelm)->field.tqe_prev = (elm);                             \
+        (listelm)->field.tqe_prev = &(elm)->field.tqe_next;             \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {                             \
+        if (((elm)->field.tqe_next) != NULL)                            \
+                (elm)->field.tqe_next->field.tqe_prev =                 \
+                    (elm)->field.tqe_prev;                              \
+        else                                                            \
+                (head)->tqh_last = (elm)->field.tqe_prev;               \
+        *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
+        _Q_INVALIDATE((elm)->field.tqe_prev);                           \
+        _Q_INVALIDATE((elm)->field.tqe_next);                           \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {                      \
+        if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   \
+                (elm2)->field.tqe_next->field.tqe_prev =                \
+                    &(elm2)->field.tqe_next;                            \
+        else                                                            \
+                (head)->tqh_last = &(elm2)->field.tqe_next;             \
+        (elm2)->field.tqe_prev = (elm)->field.tqe_prev;                 \
+        *(elm2)->field.tqe_prev = (elm2);                               \
+        _Q_INVALIDATE((elm)->field.tqe_prev);                           \
+        _Q_INVALIDATE((elm)->field.tqe_next);                           \
+} while (0)
+
+#define TAILQ_CONCAT(head1, head2, field) do {                          \
+        if (!TAILQ_EMPTY(head2)) {                                      \
+                *(head1)->tqh_last = (head2)->tqh_first;                \
+                (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+                (head1)->tqh_last = (head2)->tqh_last;                  \
+                TAILQ_INIT((head2));                                    \
+        }                                                               \
+} while (0)
+
+#endif  /* !_SYS_QUEUE_H_ */

+ 501 - 0
libcs/src/ae.c

@@ -0,0 +1,501 @@
+/* A simple event-driven programming library. Originally I wrote this code
+ * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
+ * it in form of a library for easy reuse.
+ *
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "../inc/cs.h"
+#include "../inc/ae.h"
+
+/* Include the best multiplexing layer supported by this system.
+ * The following should be ordered by performances, descending. */
+
+#ifdef HAVE_EVPORT
+  #include "ev/ae_evport.c"
+#else
+  #ifdef HAVE_EPOLL
+    #include "ev/ae_epoll.c"
+  #else
+    #ifdef HAVE_KQUEUE
+      #include "ev/ae_kqueue.c"
+    #else
+      #include "ev/ae_select.c"
+    #endif
+  #endif
+#endif
+
+CS_API aeEventLoop *aeCreateEventLoop(int setsize) {
+    aeEventLoop *eventLoop;
+    int i;
+
+    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
+    eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
+    eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
+    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
+    eventLoop->setsize = setsize;
+    eventLoop->lastTime = time(NULL);
+    eventLoop->timeEventHead = NULL;
+    eventLoop->timeEventNextId = 0;
+    eventLoop->stop = 0;
+    eventLoop->maxfd = -1;
+    eventLoop->beforesleep = NULL;
+    eventLoop->aftersleep = NULL;
+    if (aeApiCreate(eventLoop) == -1) goto err;
+    /* Events with mask == AE_NONE are not set. So let's initialize the
+     * vector with it. */
+    for (i = 0; i < setsize; i++)
+        eventLoop->events[i].mask = AE_NONE;
+    return eventLoop;
+
+err:
+    if (eventLoop) {
+        zfree(eventLoop->events);
+        zfree(eventLoop->fired);
+        zfree(eventLoop);
+    }
+    return NULL;
+}
+
+/* Return the current set size. */
+CS_API int aeGetSetSize(aeEventLoop *eventLoop) {
+    return eventLoop->setsize;
+}
+
+/* Resize the maximum set size of the event loop.
+ * If the requested set size is smaller than the current set size, but
+ * there is already a file descriptor in use that is >= the requested
+ * set size minus one, AE_ERR is returned and the operation is not
+ * performed at all.
+ *
+ * Otherwise AE_OK is returned and the operation is successful. */
+CS_API int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
+    int i;
+
+    if (setsize == eventLoop->setsize) return AE_OK;
+    if (eventLoop->maxfd >= setsize) return AE_ERR;
+    if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
+
+    eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
+    eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
+    eventLoop->setsize = setsize;
+
+    /* Make sure that if we created new slots, they are initialized with
+     * an AE_NONE mask. */
+    for (i = eventLoop->maxfd+1; i < setsize; i++)
+        eventLoop->events[i].mask = AE_NONE;
+    return AE_OK;
+}
+
+CS_API void aeDeleteEventLoop(aeEventLoop *eventLoop) {
+    aeApiFree(eventLoop);
+    zfree(eventLoop->events);
+    zfree(eventLoop->fired);
+    zfree(eventLoop);
+}
+
+CS_API void aeStop(aeEventLoop *eventLoop) {
+    eventLoop->stop = 1;
+}
+
+CS_API int aeCreateFileEvent(
+    aeEventLoop *eventLoop, 
+    int fd, 
+    int mask, 
+    aeFileProc *proc, 
+    void *clientData) {
+    if (fd >= eventLoop->setsize) {
+        errno = ERANGE;
+        return AE_ERR;
+    }
+    aeFileEvent *fe = &eventLoop->events[fd];
+
+    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
+        return AE_ERR;
+    fe->mask |= mask;
+    if (mask & AE_READABLE) fe->rfileProc = proc;
+    if (mask & AE_WRITABLE) fe->wfileProc = proc;
+    fe->clientData = clientData;
+    if (fd > eventLoop->maxfd)
+        eventLoop->maxfd = fd;
+    return AE_OK;
+}
+
+CS_API void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    if (fd >= eventLoop->setsize) return;
+    aeFileEvent *fe = &eventLoop->events[fd];
+    if (fe->mask == AE_NONE) return;
+
+    aeApiDelEvent(eventLoop, fd, mask);
+    fe->mask = fe->mask & (~mask);
+    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
+        /* Update the max fd */
+        int j;
+
+        for (j = eventLoop->maxfd-1; j >= 0; j--)
+            if (eventLoop->events[j].mask != AE_NONE) break;
+        eventLoop->maxfd = j;
+    }
+}
+
+CS_API int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
+    if (fd >= eventLoop->setsize) return 0;
+    aeFileEvent *fe = &eventLoop->events[fd];
+
+    return fe->mask;
+}
+
+static void aeGetTime(long *seconds, long *milliseconds) {
+#ifdef _WIN32
+    struct _timeb tb;
+
+    memset(&tb, 0, sizeof(struct _timeb));
+    _ftime_s(&tb);
+    (*seconds) = tb.time;
+    (*milliseconds) = tb.millitm;
+#else
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    *seconds = tv.tv_sec;
+    *milliseconds = tv.tv_usec/1000;
+#endif
+}
+
+static void aeAddMillisecondsToNow(
+    long long milliseconds, long *sec, long *ms) {
+    long cur_sec, cur_ms, when_sec, when_ms;
+
+    aeGetTime(&cur_sec, &cur_ms);
+    when_sec = cur_sec + milliseconds/1000;
+    when_ms = cur_ms + milliseconds%1000;
+    if (when_ms >= 1000) {
+        when_sec ++;
+        when_ms -= 1000;
+    }
+    *sec = when_sec;
+    *ms = when_ms;
+}
+
+CS_API long long aeCreateTimeEvent(
+    aeEventLoop *eventLoop, 
+    long long milliseconds,
+    aeTimeProc *proc, 
+    void *clientData,
+    aeEventFinalizerProc *finalizerProc) {
+    long long id = eventLoop->timeEventNextId++;
+    aeTimeEvent *te;
+
+    te = zmalloc(sizeof(*te));
+    if (te == NULL) return AE_ERR;
+    te->id = id;
+    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
+    te->timeProc = proc;
+    te->finalizerProc = finalizerProc;
+    te->clientData = clientData;
+    te->next = eventLoop->timeEventHead;
+    eventLoop->timeEventHead = te;
+    return id;
+}
+
+CS_API int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) {
+    aeTimeEvent *te = eventLoop->timeEventHead;
+    while(te) {
+        if (te->id == id) {
+            te->id = AE_DELETED_EVENT_ID;
+            return AE_OK;
+        }
+        te = te->next;
+    }
+    return AE_ERR; /* NO event with the specified ID found */
+}
+
+/* Search the first timer to fire.
+ * This operation is useful to know how many time the select can be
+ * put in sleep without to delay any event.
+ * If there are no timers NULL is returned.
+ *
+ * Note that's O(N) since time events are unsorted.
+ * Possible optimizations (not needed by Redis so far, but...):
+ * 1) Insert the event in order, so that the nearest is just the head.
+ *    Much better but still insertion or deletion of timers is O(N).
+ * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
+ */
+static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop) {
+    aeTimeEvent *te = eventLoop->timeEventHead;
+    aeTimeEvent *nearest = NULL;
+
+    while(te) {
+        if (!nearest || te->when_sec < nearest->when_sec ||
+                (te->when_sec == nearest->when_sec &&
+                 te->when_ms < nearest->when_ms))
+            nearest = te;
+        te = te->next;
+    }
+    return nearest;
+}
+
+/* Process time events */
+static int processTimeEvents(aeEventLoop *eventLoop) {
+    int processed = 0;
+    aeTimeEvent *te, *prev;
+    long long maxId;
+    time_t now = time(NULL);
+
+    /* If the system clock is moved to the future, and then set back to the
+     * right value, time events may be delayed in a random way. Often this
+     * means that scheduled operations will not be performed soon enough.
+     *
+     * Here we try to detect system clock skews, and force all the time
+     * events to be processed ASAP when this happens: the idea is that
+     * processing events earlier is less dangerous than delaying them
+     * indefinitely, and practice suggests it is. */
+    if (now < eventLoop->lastTime) {
+        te = eventLoop->timeEventHead;
+        while(te) {
+            te->when_sec = 0;
+            te = te->next;
+        }
+    }
+    eventLoop->lastTime = now;
+
+    prev = NULL;
+    te = eventLoop->timeEventHead;
+    maxId = eventLoop->timeEventNextId-1;
+    while(te) {
+        long now_sec, now_ms;
+        long long id;
+
+        /* Remove events scheduled for deletion. */
+        if (te->id == AE_DELETED_EVENT_ID) {
+            aeTimeEvent *next = te->next;
+            if (prev == NULL)
+                eventLoop->timeEventHead = te->next;
+            else
+                prev->next = te->next;
+            if (te->finalizerProc)
+                te->finalizerProc(eventLoop, te->clientData);
+            zfree(te);
+            te = next;
+            continue;
+        }
+
+        /* Make sure we don't process time events created by time events in
+         * this iteration. Note that this check is currently useless: we always
+         * add new timers on the head, however if we change the implementation
+         * detail, this check may be useful again: we keep it here for future
+         * defense. */
+        if (te->id > maxId) {
+            te = te->next;
+            continue;
+        }
+        aeGetTime(&now_sec, &now_ms);
+        if (now_sec > te->when_sec ||
+            (now_sec == te->when_sec && now_ms >= te->when_ms))
+        {
+            int retval;
+
+            id = te->id;
+            retval = te->timeProc(eventLoop, id, te->clientData);
+            processed++;
+            if (retval != AE_NOMORE) {
+                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
+            } else {
+                te->id = AE_DELETED_EVENT_ID;
+            }
+        }
+        prev = te;
+        te = te->next;
+    }
+    return processed;
+}
+
+/* Process every pending time event, then every pending file event
+ * (that may be registered by time event callbacks just processed).
+ * Without special flags the function sleeps until some file event
+ * fires, or when the next time event occurs (if any).
+ *
+ * If flags is 0, the function does nothing and returns.
+ * if flags has AE_ALL_EVENTS set, all the kind of events are processed.
+ * if flags has AE_FILE_EVENTS set, file events are processed.
+ * if flags has AE_TIME_EVENTS set, time events are processed.
+ * if flags has AE_DONT_WAIT set the function returns ASAP until all
+ * if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called.
+ * the events that's possible to process without to wait are processed.
+ *
+ * The function returns the number of events processed. */
+CS_API int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
+    int processed = 0, numevents;
+
+    /* Nothing to do? return ASAP */
+    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
+
+    /* Note that we want call select() even if there are no
+     * file events to process as long as we want to process time
+     * events, in order to sleep until the next time event is ready
+     * to fire. */
+    if (eventLoop->maxfd != -1 ||
+        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
+        int j;
+        aeTimeEvent *shortest = NULL;
+        struct timeval tv, *tvp;
+
+        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
+            shortest = aeSearchNearestTimer(eventLoop);
+        if (shortest) {
+            long now_sec, now_ms;
+
+            aeGetTime(&now_sec, &now_ms);
+            tvp = &tv;
+
+            /* How many milliseconds we need to wait for the next
+             * time event to fire? */
+            long long ms =
+                (shortest->when_sec - now_sec)*1000 +
+                shortest->when_ms - now_ms;
+
+            if (ms > 0) {
+                tvp->tv_sec = ms/1000;
+                tvp->tv_usec = (ms % 1000)*1000;
+            } else {
+                tvp->tv_sec = 0;
+                tvp->tv_usec = 0;
+            }
+        } else {
+            /* If we have to check for events but need to return
+             * ASAP because of AE_DONT_WAIT we need to set the timeout
+             * to zero */
+            if (flags & AE_DONT_WAIT) {
+                tv.tv_sec = tv.tv_usec = 0;
+                tvp = &tv;
+            } else {
+                /* Otherwise we can block */
+                tvp = NULL; /* wait forever */
+            }
+        }
+
+        /* Call the multiplexing API, will return only on timeout or when
+         * some event fires. */
+        numevents = aeApiPoll(eventLoop, tvp);
+
+        /* After sleep callback. */
+        if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
+            eventLoop->aftersleep(eventLoop);
+
+        for (j = 0; j < numevents; j++) {
+            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
+            int mask = eventLoop->fired[j].mask;
+            int fd = eventLoop->fired[j].fd;
+            int rfired = 0;
+
+	    /* note the fe->mask & mask & ... code: maybe an already processed
+             * event removed an element that fired and we still didn't
+             * processed, so we check if the event is still valid. */
+            if (fe->mask & mask & AE_READABLE) {
+                rfired = 1;
+                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
+            }
+            if (fe->mask & mask & AE_WRITABLE) {
+                if (!rfired || fe->wfileProc != fe->rfileProc)
+                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
+            }
+            processed++;
+        }
+    }
+    /* Check time events */
+    if (flags & AE_TIME_EVENTS)
+        processed += processTimeEvents(eventLoop);
+
+    return processed; /* return the number of processed file/time events */
+}
+
+/* Wait for milliseconds until the given file descriptor becomes
+ * writable/readable/exception */
+CS_API int aeWait(int fd, int mask, long long milliseconds) {
+#ifdef _WIN32
+	struct timeval tv;
+	fd_set rfds, wfds, efds;
+	int retmask = 0, retval;
+
+	tv.tv_sec = (long)(milliseconds/1000);
+	tv.tv_usec = (milliseconds%1000)*1000;
+	FD_ZERO(&rfds);
+	FD_ZERO(&wfds);
+	FD_ZERO(&efds);
+
+	if (mask & AE_READABLE) FD_SET(fd,&rfds);
+	if (mask & AE_WRITABLE) FD_SET(fd,&wfds);
+	if ((retval = select(fd+1, &rfds, &wfds, &efds, &tv)) > 0) {
+		if (FD_ISSET(fd,&rfds)) retmask |= AE_READABLE;
+		if (FD_ISSET(fd,&wfds)) retmask |= AE_WRITABLE;
+		return retmask;
+	} else {
+		return retval;
+	}
+#else
+    struct pollfd pfd;
+    int retmask = 0, retval;
+
+    memset(&pfd, 0, sizeof(pfd));
+    pfd.fd = fd;
+    if (mask & AE_READABLE) pfd.events |= POLLIN;
+    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;
+
+    if ((retval = poll(&pfd, 1, milliseconds))== 1) {
+        if (pfd.revents & POLLIN) retmask |= AE_READABLE;
+        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
+	if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
+        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
+        return retmask;
+    } else {
+        return retval;
+    }
+#endif
+}
+
+CS_API void aeMain(aeEventLoop *eventLoop) {
+    eventLoop->stop = 0;
+    while (!eventLoop->stop) {
+        if (eventLoop->beforesleep != NULL)
+            eventLoop->beforesleep(eventLoop);
+        aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
+    }
+}
+
+CS_API char *aeGetApiName(void) {
+    return aeApiName();
+}
+
+CS_API void aeSetBeforeSleepProc(
+    aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
+    eventLoop->beforesleep = beforesleep;
+}
+
+CS_API void aeSetAfterSleepProc(
+    aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) {
+    eventLoop->aftersleep = aftersleep;
+}

+ 85 - 0
libcs/src/cs_crc32.c

@@ -0,0 +1,85 @@
+/**
+ * cs_crc32.c
+ * Copyright (c) 2007-2018 ls
+ **/
+#include "../inc/cs.h"
+#include "../inc/cs_str.h"
+
+#define CS_CRC32C(c, d) (c = (c >> 8) ^ _crc_table[(c ^ (d)) & 0xff])
+
+static const unsigned int _crc_table[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,
+};
+
+CS_API cs_uint_t cs_crc32c(const u_char *s, size_t n) {
+  size_t i;
+  cs_uint_t crc32 = ~0L;
+
+  for (i = 0; i < n; i++){
+    CS_CRC32C(crc32, s[i]);
+  }
+  return (~crc32);
+}

+ 108 - 0
libcs/src/cs_list.c

@@ -0,0 +1,108 @@
+/**
+ * cs_str.c
+ * Copyright (c) 2007-2018 ls
+ **/
+#include "../inc/cs.h"
+#include "../inc/cs_list.h"
+
+CS_API void cs_list_init(cs_list_t *list) {
+	list->head = list->tail = NULL;
+}
+
+CS_API void cs_list_add_head(cs_list_t *list, cs_list_node_t *node) {
+	if (list->head == NULL) {
+		list->head = list->tail = node;
+		node->prev = node->next = NULL;
+	} else {
+		node->prev = NULL;
+		node->next = list->head;
+		list->head->prev = node;
+		list->head = node;
+	}
+}
+
+CS_API void cs_list_add_tail(cs_list_t *list, cs_list_node_t *node) {
+	if (list->head == NULL) {
+		list->head = list->tail = node;
+		node->prev = node->next = NULL;
+	} else {
+		node->prev = list->tail;
+		node->next = NULL;
+		list->tail->next = node;
+		list->tail = node;
+	}
+}
+
+CS_API void cs_list_add_before(
+  cs_list_t *list, cs_list_node_t *old, cs_list_node_t *node) {
+	node->next = old;
+	node->prev = old->prev;
+	if (list->head == old) {
+		list->head = node;
+	}
+	if (node->prev != NULL) {
+		node->prev->next = node;
+	}
+	if (node->next != NULL) {
+		node->next->prev = node;
+	}
+}
+
+CS_API void cs_list_add_after(
+  cs_list_t *list, cs_list_node_t *old, cs_list_node_t *node) {
+	node->prev = old;
+	node->next = old->next;
+	if (list->tail == old) {
+		list->tail = node;
+	}
+
+	if (node->prev != NULL) {
+		node->prev->next = node;
+	}
+
+	if (node->next != NULL) {
+		node->next->prev = node;
+	}	
+}
+
+CS_API void cs_list_del(cs_list_t *list, cs_list_node_t *node) {
+	if (node->prev == NULL) {
+		list->head = node->next;
+	} else {
+		node->prev->next = node->next;
+	}
+
+	if (node->next == NULL) {
+		list->tail = node->prev;
+	} else {
+		node->next->prev = node->prev;
+	}
+}
+
+CS_API cs_list_node_t *cs_list_find_u32(cs_list_t *list, cs_uint_t key) {
+	cs_list_node_t *node = NULL;
+	cs_uint_t *u;
+
+	cs_list_for_each(list, node) {
+		u = (cs_uint_t *)node->data;
+		if (*u == key) {
+			return (node);
+		}
+	}
+
+	return (NULL);
+}
+
+CS_API cs_list_node_t *cs_list_find_u64(cs_list_t *list, cs_uint64_t key) {
+	cs_list_node_t *node = NULL;
+	cs_uint64_t *u;
+
+	cs_list_for_each(list, node) {
+		u = (cs_uint64_t *)node->data;
+		if (*u == key) {
+			return (node);
+		}
+	}
+	
+	return (NULL);
+}

+ 55 - 0
libcs/src/cs_os.c

@@ -0,0 +1,55 @@
+/**
+ * cs_str.c
+ * Copyright (c) 2007-2018 ls
+ **/
+#include "../inc/cs.h"
+#include "../inc/cs_os.h"
+
+CS_API cs_int_t cs_os_pagesize() {
+	static cs_int_t __os_pagesize = -1;
+	
+	if (__os_pagesize == -1) {
+#ifdef _WIN32
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+		__os_pagesize = (cs_int_t)si.dwPageSize;
+#else
+		__os_pagesize = getpagesize();
+#endif
+	}
+
+	return (__os_pagesize);
+}
+
+CS_API cs_uint_t cs_os_cpu_count() {
+	static cs_uint_t __os_cpu_num = 0;
+
+	if (__os_cpu_num == 0) {
+#ifdef _WIN32
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+		__os_cpu_num = (cs_uint_t)si.dwNumberOfProcessors;
+#endif
+	}
+
+	return(__os_cpu_num);
+}
+
+#ifdef _WIN32
+CS_API cs_int_t cs_os_is_winnt() {
+	static cs_int_t __os_type = -1;
+	/*
+	* The value of __os_type is computed only once, and cached to
+	* avoid the overhead of repeated calls to GetVersion().
+	*/
+	if (__os_type == -1) {
+		if ((GetVersion() & 0x80000000) == 0) {
+			__os_type = 1;
+		} else {
+			__os_type = 0;
+		}
+	}
+
+	return (__os_type);
+}
+#endif

+ 319 - 0
libcs/src/cs_rbtree.c

@@ -0,0 +1,319 @@
+/**
+ * cs_rbtree.c
+ * Copyright (c) 2007-2018 ls
+ **/
+#include "../inc/cs.h"
+#include "../inc/cs_rbtree.h"
+
+CS_API void cs_rbtree_init(
+  cs_rbtree_t *t, cs_rbtree_node_t *nil, int (*cmp)(void *, void *)) {
+	CS_TREE_RB_BLACK(nil);
+	nil->key = 0;
+	nil->parent = nil->left = nil->right = nil;
+	
+	t->nil = nil;
+	t->root = nil;
+
+	t->cmp = cmp;
+}
+
+static cs_inline void cs_rbtree_left_rotate(
+  cs_rbtree_t *t, cs_rbtree_node_t *x) {
+	cs_rbtree_node_t *y;
+	cs_rbtree_node_t *nil = t->nil;
+
+	y = x->right;
+	x->right = y->left;
+
+	if (y->left != nil) {
+		y->left->parent = x; 
+	}
+  
+	y->parent = x->parent;   
+
+	if (x == x->parent->left) {
+		x->parent->left = y;
+	} else {
+		x->parent->right = y;
+	}
+	y->left = x;
+	x->parent = y;
+}
+
+static cs_inline void cs_rbtree_right_rotate(
+  cs_rbtree_t *t, cs_rbtree_node_t *x) {
+	cs_rbtree_node_t *y;
+	cs_rbtree_node_t *nil = t->nil;
+	
+	y = x->left;
+	x->left = y->right;
+	
+	if (nil != y->right) {
+		y->right->parent = x; 
+	}
+	y->parent = x->parent;
+	if (x == x->parent->left) {
+		x->parent->left = y;
+	} else {
+		x->parent->right = y;
+	}
+	y->right = x;
+	x->parent = y;
+}
+
+CS_API cs_rbtree_node_t *cs_rbtree_add(cs_rbtree_t *t, cs_rbtree_node_t *n) {
+	cs_rbtree_node_t *x;
+	cs_rbtree_node_t *y;
+	cs_rbtree_node_t *nil = t->nil;
+
+	n->left = n->right = nil;
+	y = t->root;
+	x = t->root->left;
+	
+	while (x != nil) {
+		if (CS_TREE_RB_EQ(t, n->key, x->key)) {
+			return (x);
+		}
+		y = x;
+		x = CS_TREE_RB_LT(t, n->key, x->key) ? x->left : x->right;
+		if (x->key == nil->key) {
+			break;
+		}
+	}
+	n->parent = y;
+	if ((y == t->root) || CS_TREE_RB_LT(t, n->key, y->key)) {
+		y->left = n;
+	} else {
+		y->right = n;
+	}
+
+	CS_TREE_RB_RED(n);
+	while (CS_TREE_RB_ISRED(n->parent)) { 
+		/* use sentinel instead of checking for root */
+		//printf("CS_TREE_RB_ISRED:%08x\n",n->parent->key);
+		if (n->parent == n->parent->parent->left) {
+			y = n->parent->parent->right;
+			if (CS_TREE_RB_ISRED(y)) {
+				CS_TREE_RB_BLACK(n->parent);
+				CS_TREE_RB_BLACK(y);
+				CS_TREE_RB_RED(n->parent->parent);
+				n = n->parent->parent;
+			} else {
+				if (n == n->parent->right) {
+					n = n->parent;
+					cs_rbtree_left_rotate(t, n);
+				}
+				CS_TREE_RB_BLACK(n->parent);
+				CS_TREE_RB_RED(n->parent->parent);
+				cs_rbtree_right_rotate(t, n->parent->parent);
+			} 
+		} else {
+			y = n->parent->parent->left;
+			if (CS_TREE_RB_ISRED(y)) {
+				CS_TREE_RB_BLACK(n->parent);
+				CS_TREE_RB_BLACK(y);
+				CS_TREE_RB_RED(n->parent->parent);
+				n = n->parent->parent;
+			} else {
+				if (n == n->parent->left) {
+					n = n->parent;
+					cs_rbtree_right_rotate(t, n);
+				}
+				CS_TREE_RB_BLACK(n->parent);
+				CS_TREE_RB_RED(n->parent->parent);
+				cs_rbtree_left_rotate(t, n->parent->parent);
+			} 
+		}
+	}
+ 	CS_TREE_RB_BLACK(t->root->left);
+	
+	return (NULL);
+}
+
+static cs_inline void cs_rbtree_deletefixup(
+  cs_rbtree_t *t, cs_rbtree_node_t *x) {
+	cs_rbtree_node_t *root = t->root->left;
+	cs_rbtree_node_t *w;
+	
+	while (CS_TREE_RB_ISBLACK(x) && (root != x)) {
+		if (x == x->parent->left) {
+			w = x->parent->right;
+			if (CS_TREE_RB_ISRED(w)) {
+				CS_TREE_RB_BLACK(w);
+				CS_TREE_RB_RED(x->parent);
+				cs_rbtree_left_rotate(t, x->parent);
+				w = x->parent->right;
+			}
+			if (CS_TREE_RB_ISBLACK(w->right) && CS_TREE_RB_ISBLACK(w->left)) { 
+				CS_TREE_RB_RED(w);
+				x = x->parent;
+			} else {
+				if (CS_TREE_RB_ISBLACK(w->right)) {
+					CS_TREE_RB_BLACK(w->left);
+					CS_TREE_RB_RED(w);
+					cs_rbtree_right_rotate(t, w);
+					w = x->parent->right;
+				}
+				CS_TREE_RB_CPYCOLOR(w, x->parent);
+				CS_TREE_RB_BLACK(x->parent);
+				CS_TREE_RB_BLACK(w->right);
+				cs_rbtree_left_rotate(t, x->parent);
+				x = root;
+			}
+		} else {
+			w = x->parent->left;
+			if(CS_TREE_RB_ISRED(w)) {
+				CS_TREE_RB_BLACK(w);
+				CS_TREE_RB_RED(x->parent);
+				cs_rbtree_right_rotate(t, x->parent);
+				w = x->parent->left;
+			}
+			if (CS_TREE_RB_ISBLACK(w->right) && CS_TREE_RB_ISBLACK(w->left)) { 
+	  			CS_TREE_RB_RED(w);
+	  			x = x->parent;
+			} else {
+				if (CS_TREE_RB_ISBLACK(w->left)) {
+					CS_TREE_RB_BLACK(w->right);
+					CS_TREE_RB_RED(w);
+					cs_rbtree_left_rotate(t, w);
+					w = x->parent->left;
+				}
+				CS_TREE_RB_CPYCOLOR(w, x->parent);
+				CS_TREE_RB_BLACK(x->parent);
+				CS_TREE_RB_BLACK(w->left);
+				cs_rbtree_right_rotate(t, x->parent);
+				x = root;
+			}
+		}
+	}
+	CS_TREE_RB_BLACK(x);
+}
+
+static cs_inline cs_rbtree_node_t *cs_rbtree_successor(
+  cs_rbtree_t *t, cs_rbtree_node_t *n) {
+	cs_rbtree_node_t *y;
+	cs_rbtree_node_t *nil = t->nil;
+	cs_rbtree_node_t *root = t->root;
+
+	if (nil != (y = n->right)) {
+		while (y->left != nil) {
+			y = y->left;
+		}
+	} else {
+		y = n->parent;
+		while (n == y->right) {
+			n = y;
+			y = y->parent;
+		}
+		if (y == root){
+			return (nil);
+		}
+	}
+
+	return (y);
+}
+
+CS_API void cs_rbtree_del(cs_rbtree_t *t, cs_rbtree_node_t *n) {
+	cs_rbtree_node_t *y;
+	cs_rbtree_node_t *x;
+	cs_rbtree_node_t *nil = t->nil;
+	cs_rbtree_node_t *root = t->root;
+	
+	y = ((n->left == nil) || (n->right == nil)) 
+    ? n : cs_rbtree_successor(t, n);
+	x = (y->left == nil) ? y->right : y->left;
+	if (root == (x->parent = y->parent)) {
+		root->left = x;
+	} else {
+		if (y == y->parent->left) {
+			y->parent->left = x;
+		} else {
+			y->parent->right = x;
+		}
+	}
+	if (y != n) {
+		if(CS_TREE_RB_ISBLACK(y)){
+			cs_rbtree_deletefixup(t, x);
+		}	  
+		
+		y->left = n->left;
+		y->right = n->right;
+		y->parent = n->parent;
+		CS_TREE_RB_CPYCOLOR(y, n);
+		n->left->parent = n->right->parent = y;
+		if (n == n->parent->left) {
+			n->parent->left = y;
+		} else {
+			n->parent->right = y;
+		}
+	} else {
+		if (CS_TREE_RB_ISBLACK(y)) {
+			cs_rbtree_deletefixup(t, x);
+		}
+	}
+}
+
+static void cs_rbtree_order_internal(
+  cs_rbtree_t *t, 
+  cs_rbtree_node_t *n, 
+  void (*cs_rbtree_callback)(cs_rbtree_node_t *)) {
+	if (n == t->nil) {
+		return;
+	}
+	cs_rbtree_order_internal(t, n->left, cs_rbtree_callback);
+	cs_rbtree_callback(n);
+	cs_rbtree_order_internal(t,n->right, cs_rbtree_callback);
+}
+
+CS_API void cs_rbtree_order(
+  cs_rbtree_t *t, 
+  void (*cs_rbtree_callback)(cs_rbtree_node_t *)) {
+	cs_rbtree_order_internal(t, t->root->left, cs_rbtree_callback);
+}
+
+CS_API cs_rbtree_node_t *cs_rbtree_query(cs_rbtree_t *t, void *k) {
+	cs_rbtree_node_t *x = t->root->left;
+	cs_rbtree_node_t *nil = t->nil;
+
+	if (x == nil) {
+		return (NULL);
+	}
+	while (!CS_TREE_RB_EQ(t, k, x->key)) {
+		if (CS_TREE_RB_LT(t, k, x->key)) {
+			x = x->left;
+		} else {
+			x = x->right;
+		}
+		if (x->key == nil->key) {
+			return (NULL);
+		}
+	}
+	
+  return (x);
+}
+
+CS_API cs_rbtree_node_t *cs_rbtree_min(cs_rbtree_t *t) {
+	cs_rbtree_node_t *n = t->root;
+
+	while (n->left != t->nil) {
+    n = n->left;
+		if (n->key == t->nil->key) {
+			return (NULL);
+		}
+  }
+	
+  return (n);
+}
+
+CS_API cs_rbtree_node_t *cs_rbtree_max(cs_rbtree_t *t) {
+	cs_rbtree_node_t *n = t->root->left;
+	
+	while (n->right != t->nil) {
+    n = n->right;
+		if (n->key == t->nil->key) {
+			return (NULL);
+		}
+  }
+	
+  return (n);
+}

+ 908 - 0
libcs/src/cs_str.c

@@ -0,0 +1,908 @@
+/**
+ * cs_str.c
+ * Copyright (c) 2007-2018 ls
+ **/
+#include "../inc/cs.h"
+#include "../inc/cs_str.h"
+
+static const u_char _hex_char[] = "0123456789ABCDEF";
+
+/* everything except: ! ( ) * - . 0-9 A-Z _ a-z */
+static const u_char _uri_char[] = {
+	/*
+	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
+	*/
+  /*  00 -  0F control chars */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
+  /*  20 -  2F space " # $ % & ' + , / */
+	1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,  
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  /*  30 -  3F : ; < = > ? */
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F @ */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  50 -  5F [ \ ] ^ */
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,  /*  70 -  7F { | } ~ DEL */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
+};
+
+CS_API size_t cs_strerror(u_char *err, const char *fmt, ...) {
+	size_t n;
+	va_list ap;
+    
+  if (!err) {
+		return (0);
+	}
+
+  va_start(ap, fmt);
+  n = (size_t)vsnprintf((char *)err, CS_KB, fmt, ap);
+  va_end(ap);
+	
+	return (n);
+}
+
+CS_API size_t cs_strlen(const u_char *s) {
+	const u_char *str;
+	
+	for (str = s; *str; ++str);
+	
+	return (str - s);
+}
+
+CS_API u_char *cs_tolower(u_char *s) {
+	u_char *c;
+	
+	for (c = s; *c; c++) {
+		if (*c >= 'A' && *c <= 'Z') {
+			*c |= 32;
+		}
+	}
+	
+	return (s);
+}
+
+CS_API u_char *cs_toupper(u_char *s) {
+	u_char *c;
+	
+	for (c = s; *c; c++) {
+		if (*c >= 'a' && *c <= 'z') {
+			*c &= ~32;
+		}
+	}
+	
+	return (s);
+}
+
+CS_API u_char *cs_strcpy(u_char *d, const u_char *s) {
+	u_char *save = d;
+	
+	for (; (*d = *s) != '\0'; ++s, ++d);
+	
+	return (save);
+}
+
+CS_API u_char *cs_strncpy(u_char *d, const u_char *s, size_t n) {
+	if (n != 0) {
+		u_char *to = d;
+		const u_char *from = s;
+		
+		do {
+			if ((*to++ = *from++) == 0) {
+				/* NUL pad the remaining n-1 bytes */
+				while (--n != 0)
+					*to++ = 0;
+				break;
+			}
+		} while (--n != 0);
+	}
+
+	return (d);
+}
+
+CS_API u_char *cs_strcat(u_char *d, const u_char *s) {
+	u_char *save = d;
+
+	for (; *d; ++d);
+	while ((*d++ = *s++) != '\0');
+
+	return (save);
+}
+
+CS_API u_char *cs_strncat(u_char *d, const u_char *s, size_t n) {
+	u_char *dst = d;
+	const u_char *str = s;
+
+	if (n != 0) {
+		while (*dst != 0) {
+			dst++;
+		}
+		do {
+			if ((*dst = *str++) == 0) {
+				break;
+			}
+			dst++;
+		} while (--n != 0);
+		*dst = 0;
+	}
+
+	return (d);
+}
+
+CS_API int cs_strcmp(const u_char *s1, const u_char *s2) {
+	while (*s1 == *s2++) {
+		if (*s1++ == 0) {
+			return (0);
+		}
+	}
+
+	return (*s1 - *--s2);
+}
+
+CS_API int cs_strncmp(const u_char *s1, const u_char *s2, size_t n) {
+	if (n == 0) {
+		return (0);
+	}
+
+	do {
+		if (*s1 != *s2++) {
+			return (*s1 - *--s2);
+		}
+		if (*s1++ == 0) {
+			break;
+		}
+	} while (--n != 0);
+
+	return (0);
+}
+
+CS_API int cs_strcasecmp(u_char *s1, u_char *s2) {
+  u_char c1, c2;
+     
+	for ( ; ; ) {
+    c1 = *s1++;
+    c2 = *s2++;
+		
+    c1 = (u_char)((c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1);
+    c2 = (u_char)((c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2);
+		
+    if (c1 == c2) {			
+      if (c1) {
+        continue;
+      }
+
+      return (0);
+    }
+
+    return (c1 - c2);
+  }
+}
+
+CS_API int cs_strncasecmp(u_char *s1, u_char *s2, size_t n) {
+	u_char c1, c2;
+	
+	while (n) {
+    c1 = *s1++;
+    c2 = *s2++;
+		
+    c1 = (u_char)((c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1);
+    c2 = (u_char)((c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2);
+		
+    if (c1 == c2) {			
+      if (c1) {
+        n--;
+        continue;
+      }
+
+      return (0);
+    }		
+    return (c1 - c2);
+  }
+	
+  return (0);
+}
+
+CS_API u_char *cs_strchr(const u_char *s, u_char c) {
+	const u_char *p = s;
+	
+	for ( ; ; ) {
+		if (*p == c) {
+			return ((u_char *)p);
+		}
+		if (!*p) {
+			return (NULL);
+		}
+		p++;
+	}
+}
+
+CS_API u_char *cs_strstr(const u_char *s, u_char *d) {
+	register u_char *cp = (u_char *)s;
+	register u_char *s1, *s2;
+	
+	if (!*d) {
+		return ((u_char *)s);
+	}
+	
+	while (*cp) {
+		s1 = cp;
+		s2 = (u_char *)d;
+		
+		while (*s1 && *s2 && !(*s1 - *s2)) {
+			s1++, s2++;
+		}
+		if (!*s2) {
+			return (cp);
+		}
+		cp++;
+	}
+	
+  return (NULL);
+}
+
+CS_API u_char *cs_strrchr(const u_char *s, u_char c) {
+	u_char *start = (u_char *)s;
+	
+	/* find end of string */
+	while (*s++);
+	/* search towards front */
+	while (--s != start && *s != c);
+	
+	if (*s == c) { /* char found ? */
+		return ((u_char *)s);
+	}
+	
+	return (NULL);
+}
+
+CS_API size_t cs_strchrcpy(u_char *d, const u_char *s, u_char c) {
+	register u_char *cp = (u_char *)s;
+
+	while (*cp) {
+		if (*cp == c) {
+			break;
+		}
+		if (!*cp) {
+			return (0);
+		}
+		*d++ = *cp;
+		cp++;
+	}
+
+	return (cp - s);
+}
+
+CS_API u_char *cs_strdup(u_char *s, size_t n) {
+	u_char *cp;
+	
+	cp = (u_char *)malloc(n + sizeof(u_char));
+	if (cp) {
+		cs_bcpy(cp, s, n);
+		*(cp + n) = '\0';
+
+		return (cp);
+	}
+
+	return (NULL);
+}
+
+CS_API cs_buf_t *cs_buf_init(u_char *p, size_t n) {
+	cs_buf_t *buf = (cs_buf_t *)p;
+
+	buf->buf = p + sizeof(cs_buf_t);
+	buf->used = 0;
+	buf->size = n - sizeof(cs_buf_t);
+
+	return (buf);
+}
+
+CS_API cs_buf_t *cs_buf_new(size_t n) {
+	cs_buf_t *buf = NULL;
+
+	u_char *p = (u_char *)cs_malloc((sizeof(cs_buf_t) + n) * sizeof(u_char));
+	if (p) {
+		buf = (cs_buf_t *)p;
+		buf->buf = p + sizeof(cs_buf_t);
+		buf->size = n;
+		buf->used = 0;
+	}
+
+	return (buf);
+}
+
+CS_API void cs_buf_free(cs_buf_t *buf) {
+	if (buf) {
+		cs_free(buf);
+	}
+}
+
+CS_API size_t cs_buf_cat(cs_buf_t *d, cs_buf_t *s) {
+	size_t n = d->used;
+
+	if ((d->used + s->used) > d->size) {
+		n = s->size - s->used;
+	}
+
+	cs_bcpy(d->buf + d->used, s->buf, n);
+	d->used += n;
+
+	return (n);
+}
+
+CS_API u_char *cs_buf_bcpy(cs_buf_t *buf, u_char *s, size_t n) {
+	u_char *p = buf->buf;
+
+	cs_bcpy(buf->buf + buf->used, s, n);
+	buf->used = n;
+
+	return (p);
+}
+
+CS_API u_char *cs_buf_bcat(cs_buf_t *buf, u_char *s, size_t n) {
+	u_char *p = buf->buf;
+	size_t len = n;
+
+	if ((buf->used + n) > buf->size) {
+		len = buf->size - buf->used;
+	}
+
+	cs_bcpy(buf->buf + buf->used, s, len);
+	buf->used += len;
+
+	return (p);
+}
+
+CS_API int cs_buf_cmp(cs_buf_t *d, cs_buf_t *s) {
+	if (d->used == s->used) {
+		return cs_bcmp(d->buf, s->buf, d->used);
+	}
+	return (int)(d->used - s->used);
+}
+
+CS_API cs_int_t	cs_atoi(u_char *s, size_t n) {
+	cs_int_t  value, neg, c;
+	
+  if (n == 0) {
+    return (0);
+  }
+	
+	c = *s;
+	if (c == '-') {
+		neg = 1;
+		s++;
+		n--;
+	} else {
+		neg = 0;
+	}
+	
+  for (value = 0; n--; s++) {
+    if (*s < '0' || *s > '9') {
+      return (0);
+  	}
+
+    value = value * 10 + (*s - '0');
+  }
+	
+	if (neg) {
+		value = value * -1;
+	}
+	
+	return (value);
+}
+
+CS_API cs_int64_t cs_atoi64(u_char *s, size_t n) {
+	cs_int64_t  value;
+	cs_int_t neg, c;
+	
+  if (n == 0) {
+  	return (0);
+  }
+
+	c = *s;
+	if (c == '-') {
+		neg = 1;
+		s++;
+		n--;
+	} else {
+		neg = 0;
+	}
+
+  for (value = 0; n--; s++) {
+    if (*s < '0' || *s > '9') {
+      return (0);
+    }
+		
+    value = value * 10 + (*s - '0');
+  }
+
+	if (neg) {
+		value = value * -1;
+	}
+	
+  return (value);
+}
+
+CS_API cs_uint_t cs_atou(u_char *s, size_t n) {
+	cs_uint_t value;
+	
+  if (n == 0) {
+    return (0);
+  }
+	
+  for (value = 0; n--; s++) {
+    if (*s < '0' || *s > '9') {
+      return (0);
+    }
+		
+    value = value * 10 + (*s - '0');
+  }
+	
+  return (value);
+}
+
+CS_API cs_int_t	cs_htoi(u_char *s, size_t n) {
+	u_char c, ch;
+	cs_int_t value;
+
+	for (value = 0; n--; s++){
+		ch = *s;
+		if (ch >= '0' && ch <= '9') {
+      value = value * 16 + (ch - '0');
+      continue;
+    }
+		
+    c = (u_char) (ch | 0x20);
+		
+    if (c >= 'a' && c <= 'f') {
+      value = value * 16 + (c - 'a' + 10);
+      continue;
+    }
+	}
+
+	return (value);
+}
+
+CS_API cs_uint_t cs_htou(u_char *s, size_t n) {
+	u_char c, ch;
+	cs_uint_t value;
+	
+	for (value = 0; n--; s++){
+		ch = *s;
+		if (ch >= '0' && ch <= '9') {
+      value = value * 16 + (ch - '0');
+      continue;
+    }
+		
+    c = (u_char) (ch | 0x20);
+		
+    if (c >= 'a' && c <= 'f') {
+      value = value * 16 + (c - 'a' + 10);
+      continue;
+    }
+	}
+	
+	return (value);
+}
+
+CS_API cs_int_t cs_btoi(u_char *s, size_t n) {
+	u_char c, ch;
+	cs_int_t value;	
+	
+	for (value = 0; n--; s++) {
+		ch = _hex_char[(*s) >> 4];
+		if (ch >= '0' && ch <= '9') {
+      value = value * 16 + (ch - '0');            
+    } else {
+			c = (u_char) (ch | 0x20);
+			
+			if (c >= 'a' && c <= 'f') {
+				value = value * 16 + (c - 'a' + 10);				
+			}
+		}		
+        
+		ch = _hex_char[(*s) & 0x0f];
+		if (ch >= '0' && ch <= '9') {
+      value = value * 16 + (ch - '0');            
+    } else {
+			c = (u_char) (ch | 0x20);
+			
+			if (c >= 'a' && c <= 'f') {
+				value = value * 16 + (c - 'a' + 10);				
+			}
+		}
+	}
+	
+	return (value);
+}
+
+CS_API cs_int_t cs_btou(u_char *s, size_t n) {
+	u_char c, ch;
+	cs_uint_t value;	
+	
+	for (value = 0; n--; s++) {
+		ch = _hex_char[(*s) >> 4];
+		if (ch >= '0' && ch <= '9') {
+      value = value * 16 + (ch - '0');            
+    } else {
+			c = (u_char) (ch | 0x20);
+			
+			if (c >= 'a' && c <= 'f') {
+				value = value * 16 + (c - 'a' + 10);				
+			}
+		}		
+        
+		ch = _hex_char[(*s) & 0x0f];
+		if (ch >= '0' && ch <= '9') {
+      value = value * 16 + (ch - '0');            
+    } else {
+			c = (u_char) (ch | 0x20);
+			
+			if (c >= 'a' && c <= 'f') {
+				value = value * 16 + (c - 'a' + 10);				
+			}
+		}
+	}
+	
+	return (value);
+}
+
+CS_API size_t cs_uri_escape(u_char *d, const u_char *s) {
+	u_char	*cp = d;
+	size_t n;
+
+	if (d == NULL) {
+		n = 0;
+		while (*s) {
+			if (_uri_char[*s]) {
+				n++;
+			}
+			s++;
+		}
+
+		return (n);
+	}
+	
+	while (*s) {
+		if (_uri_char[*s]) {
+			*d++ = '%';
+			*d++ = _hex_char[(*s) >> 4];
+			*d++ = _hex_char[(*s) & 0x0f];
+			s++;		
+		} else {
+			*d++ = *s++;
+		}
+	}
+	
+	return (d - cp);
+}
+
+CS_API size_t cs_uri_unescape(u_char *d, const u_char *s) {
+	register unsigned char high, low;
+	u_char *cp = d;
+
+	while ((*s) != '\0') {
+		if (*s == '%') {
+			X2C(*(s + 1), high);
+			if (high < 0x10) {
+				X2C(*(s + 2), low);
+				if (low < 0x10) {
+					high = (u_char)((high << 4) | low);
+					*d = high;
+					s += 2;
+				}
+			}
+		} else if (*s == '+') {
+			*d = ' ';
+		} else {
+			*d = *s;
+		}		
+		d++;
+		s++;
+	}
+	
+	return (d - cp);
+}
+
+/* from ngx_string.c */
+CS_API size_t cs_json_escape(u_char *d, u_char *s, size_t n) {
+	u_char ch, *cp = d;
+  size_t len;
+
+  if (d == NULL) {
+    len = 0;
+
+    while (n) {
+      ch = *s++;
+
+      if (ch == '\\' || ch == '"') {
+        len++;
+      } else if (ch <= 0x1f) {
+        len += sizeof("\\u001F") - 2;
+      }
+
+      n--;
+    }
+
+    return (len);
+  }
+
+  while (n) {
+    ch = *s++;
+
+    if (ch > 0x1f) {
+      if (ch == '\\' || ch == '"') {
+        *d++ = '\\';
+      }
+
+      *d++ = ch;
+    } else {
+      *d++ = '\\'; *d++ = 'u'; *d++ = '0'; *d++ = '0';
+      *d++ = '0' + (ch >> 4);
+
+      ch &= 0xf;
+
+      *d++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10);
+    }
+
+    n--;
+  }
+
+  return (d - cp);
+}
+
+CS_API size_t cs_hex_dump(u_char *d, u_char *s, size_t n) { 
+	u_char *cp = d;
+	
+  while (n--) {
+    *d++ = _hex_char[*s >> 4];
+    *d++ = _hex_char[*s++ & 0xf];
+  }
+	
+  return (d - cp);
+}
+
+CS_API size_t cs_hex_undump(u_char *d, const u_char *s, size_t n) {
+	size_t i = 0;
+	register u_char ch, digit;
+	u_char *cp = d;
+	
+	for (i = 0; i < n; i++) {
+		X2C(*(s + i) ,ch);
+		ch = ch << 4;
+		i++;
+		X2C(*(s + i), digit);
+		ch += digit;
+		*d++ = ch;
+	}
+	
+	return (d - cp);
+}
+
+/* from ngx_string.c */
+static void _cs_base64_encode_internal(
+	cs_buf_t *dst, cs_buf_t *src, const u_char *basis, cs_uint_t padding) {
+	u_char *d, *s;
+  size_t len;
+
+  len = src->used;
+  s = src->buf;
+  d = dst->buf;
+
+  while (len > 2) {
+    *d++ = basis[(s[0] >> 2) & 0x3f];
+    *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];
+    *d++ = basis[((s[1] & 0x0f) << 2) | (s[2] >> 6)];
+    *d++ = basis[s[2] & 0x3f];
+
+    s += 3;
+    len -= 3;
+  }
+
+  if (len) {
+    *d++ = basis[(s[0] >> 2) & 0x3f];
+
+    if (len == 1) {
+      *d++ = basis[(s[0] & 3) << 4];
+      if (padding) {
+        *d++ = '=';
+      }
+    } else {
+      *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];
+      *d++ = basis[(s[1] & 0x0f) << 2];
+    }
+
+    if (padding) {
+      *d++ = '=';
+    }
+  }
+
+  dst->used = d - dst->buf;
+}
+
+/* from ngx_string.c */
+CS_API void cs_base64_encode(cs_buf_t *d, cs_buf_t *s) {
+  static u_char   basis64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  _cs_base64_encode_internal(d, s, basis64, 1);
+}
+
+/* from ngx_string.c */
+static cs_int_t _cs_base64_decode_internal(
+	cs_buf_t *dst, cs_buf_t *src, const u_char *basis) {
+  size_t len;
+  u_char *d, *s;
+
+  for (len = 0; len < src->used; len++) {
+    if (src->buf[len] == '=') {
+      break;
+    }
+
+    if (basis[src->buf[len]] == 77) {
+      return (CS_ERR);
+    }
+  }
+
+  if (len % 4 == 1) {
+    return (CS_ERR);
+  }
+
+  s = src->buf;
+  d = dst->buf;
+
+  while (len > 3) {
+    *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
+    *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
+    *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]);
+
+    s += 4;
+    len -= 4;
+  }
+
+  if (len > 1) {
+    *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
+  }
+
+  if (len > 2) {
+    *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
+  }
+
+  dst->used = d - dst->buf;
+
+  return (CS_OK);
+}
+
+/* from ngx_string.c */
+CS_API cs_int_t cs_base64_decode(cs_buf_t *d, cs_buf_t *s) {
+  static u_char   basis64[] = {
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
+    77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
+    77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
+
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+    77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
+  };
+
+  return _cs_base64_decode_internal(d, s, basis64);
+}
+
+/* from ngx_string.c */
+CS_API cs_uint_t cs_utf8_decode(u_char **s, size_t n) {
+	size_t    len;
+  cs_uint_t  u, i, valid;
+	
+  u = **s;
+	
+  if(u > 0xf0){
+    u &= 0x07;
+    valid = 0xffff;
+    len = 3;		
+  } else if (u > 0xe0) {		
+    u &= 0x0f;
+    valid = 0x7ff;
+    len = 2;		
+  } else if (u > 0xc0) {		
+    u &= 0x1f;
+    valid = 0x7f;
+    len = 1;
+  } else{
+    (*s)++;
+
+    return (0xffffffff);
+  }
+	
+  if (n - 1 < len) {
+    return (0xfffffffe);
+  }
+	
+  (*s)++;
+	
+  while (len) {
+    i = *(*s)++;
+		
+    if (i < 0x80) {
+      return (0xffffffff);
+  	}
+		
+    u = (u << 6) | (i & 0x3f);		
+    len--;
+  }
+	
+  if (u > valid) {
+    return (u);
+  }
+	
+  return (0xffffffff);
+}
+
+/* from ngx_string.c */
+CS_API size_t cs_utf8_length(u_char *s, size_t n) {
+	u_char  c, *last;
+  size_t  len;
+	
+  last = s + n;
+  for (len = 0; s < last; len++) {		
+    c = *s;
+    if (c < 0x80) {
+      s++;
+      continue;
+    }
+		
+    if (cs_utf8_decode(&s, n) > 0x10ffff) {
+      /* invalid UTF-8 */
+      return n;
+    }
+  }
+
+  return (len);
+}
+
+/*
+djb2
+	this algorithm (k=33) was first reported by dan bernstein many years ago 
+in comp.lang.c.
+another version of this algorithm (now favored by bernstein) uses xor:
+	hash(i) = hash(i - 1) * 33 ^ str[i];
+	the magic of number 33 (why it works better than many other constants, 
+prime or not)
+has never been adequately explained.
+*/
+CS_API cs_uint_t cs_hash_djb2(const u_char *s) {
+	const u_char *cp = s;
+	cs_uint_t hash = 5381;
+
+	while (*cp) {
+		hash = ((hash << 5) + hash) + *cp; /* hash = hash * 33 + c */
+		cp++;
+	}
+
+	return (hash);
+}
+
+CS_API cs_uint64_t cs_hash_djb2_64(const u_char *s) {
+	const u_char *cp = s;
+	cs_uint64_t hash = 5381;
+	
+	while (*cp) {
+		hash = ((hash << 5) + hash) + *cp; /* hash = hash * 33 + c */
+		cp++;
+	}
+	
+	return (hash);
+}

+ 344 - 0
libcs/src/cs_tcp.c

@@ -0,0 +1,344 @@
+/**
+ * cs_tcp.h
+ * Copyright (c) 2007-2018 ls
+ **/
+#include "../inc/cs.h"
+#include "../inc/cs_str.h"
+#include "../inc/cs_tcp.h"
+
+#ifdef _WIN32
+#pragma comment(lib,"ws2_32.lib")
+
+//#define EINPROGRESS WSAEWOULDBLOCK
+
+CS_API int cs_tcp_init() {
+	WSADATA wsaData;
+	WSAStartup(MAKEWORD(2, 2), &wsaData);
+
+	return (CS_OK);
+}
+
+CS_API void cs_tcp_cleanup() {
+	WSACleanup();
+}
+#endif
+
+static int cs_tcp_v6_only(cs_sock_t s) {
+  int yes = 1;
+  if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1) {
+    cs_tcp_close(s);
+
+    return (CS_ERR);
+  }
+
+  return (CS_OK);
+}
+
+static int cs_tcp_reuseaddr(cs_sock_t fd) {
+  int yes = 1;
+  /* Make sure connection-intensive things like the redis benckmark
+   * will be able to close/open sockets a zillion of times */
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
+    // cs_strerror(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
+    return (CS_ERR);
+  }
+
+  return (CS_OK);
+}
+
+static int _cs_tcp_listen(
+	cs_sock_t s, struct sockaddr *sa, socklen_t len, int backlog) {
+  if (bind(s,sa,len) == -1) {
+    // cs_strerror(err, "bind: %s", strerror(errno));
+    cs_tcp_close(s);
+
+    return (CS_ERR);
+  }
+
+  if (listen(s, backlog) == -1) {
+    // cs_strerror(err, "listen: %s", strerror(errno));
+    cs_tcp_close(s);
+
+    return (CS_ERR);
+  }
+
+  return (CS_OK);
+}
+
+static cs_sock_t _cs_tcp_server(
+	int port, char *bindaddr, int af, int backlog) {
+  cs_sock_t s = -1;
+	int rv;
+  char _port[6];  /* strlen("65535") */
+  struct addrinfo hints, *servinfo, *p;
+
+#ifdef _WIN32
+  sprintf_s(_port, 6, "%d", port);
+#else
+  snprintf(_port, 6, "%d", port);
+#endif
+
+  cs_bzero(&hints, sizeof(hints));
+  hints.ai_family = af;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE;    /* No effect if bindaddr != NULL */
+
+  if ((rv = getaddrinfo(bindaddr, _port, &hints, &servinfo)) != 0) {
+    // cs_strerror(err, "%s", gai_strerror(rv));
+    return (CS_ERR);
+  }
+
+  for (p = servinfo; p != NULL; p = p->ai_next) {
+    #ifdef _WIN32
+	  if ((s = WSASocket(
+      p->ai_family, 
+      p->ai_socktype, 
+      p->ai_protocol, 
+      NULL, 
+      0, 
+      WSA_FLAG_OVERLAPPED)) == -1) {
+		  continue;
+	  }
+    #else
+    if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+			continue;
+		}
+    #endif 
+
+    if (af == AF_INET6 && cs_tcp_v6_only(s) == CS_ERR) {
+			goto error;
+		}
+
+    if (cs_tcp_reuseaddr(s) == CS_ERR) {
+			goto error;
+		}
+
+    if (_cs_tcp_listen(s, p->ai_addr, p->ai_addrlen, backlog) == CS_ERR) {
+			goto error;
+		}
+    
+    goto end;
+  }
+
+  if (p == NULL) {
+    // cs_strerror(err, "unable to bind socket, errno: %d", errno);
+    goto error;
+  }
+
+error:
+    if (s != -1) {
+			cs_tcp_close(s);
+		}
+    s = CS_ERR;
+end:
+    freeaddrinfo(servinfo);
+
+    return (s);
+}
+
+/*
+static cs_sock_t cs_tcp_v4_create() {
+	cs_sock_t s;
+	int flag = 1;
+	
+#ifdef _WIN32
+	if ((s = WSASocket(
+    AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == -1) {
+		return (CS_ERR);
+	}
+#else
+	if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+		return (CS_ERR);
+	}
+#endif
+	if (setsockopt(
+    s, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)) == 1) {
+		return (CS_ERR);
+	}
+	
+	return (s);
+}
+
+static int cs_tcp_v4_listen(
+	cs_sock_t s, struct sockaddr *sa, unsigned int len) {
+	if (bind(s, sa, len) == -1) {
+		cs_tcp_close(s);
+
+		return (CS_ERR);
+	}
+	if (listen(s, 511) == -1) { // 511 is from nginx
+		cs_tcp_close(s);
+
+		return (CS_ERR);
+	}
+
+	return (CS_OK);
+}
+// */
+
+CS_API cs_sock_t cs_tcp_v4_server(int port, char *bindaddr) {
+	return _cs_tcp_server(port, bindaddr, AF_INET, 511);
+}
+
+CS_API cs_sock_t cs_tcp_v6_server(int port, char *bindaddr) {
+	return _cs_tcp_server(port, bindaddr, AF_INET6, 511);
+}
+
+static cs_inline cs_sock_t _cs_tcp_generic_accept(
+  cs_sock_t s, struct sockaddr *sa, unsigned int *len) {
+	cs_sock_t fd;
+
+	while (1) {
+		fd = accept(s, sa, len);
+		if (fd == -1) {
+			if (cs_sock_errno == EINTR) {
+				continue;
+			} else {
+				// cs_strerror(err, "accept: %s", strerror(errno));
+				return (CS_ERR);
+			}
+		}
+		break;
+	}
+
+	return (fd);
+}
+
+CS_API cs_sock_t cs_tcp_accept(
+	cs_sock_t s, u_char *ip, size_t ip_len, int *port) {
+	cs_sock_t fd;
+  struct sockaddr_storage sa;
+  socklen_t salen = sizeof(sa);
+
+  if ((fd = _cs_tcp_generic_accept(s, (struct sockaddr*)&sa, &salen)) == -1) {
+    return CS_ERR;
+	}
+
+  if (sa.ss_family == AF_INET) {
+    struct sockaddr_in *s = (struct sockaddr_in *)&sa;
+    if (ip) {
+			inet_ntop(AF_INET, (void*)&(s->sin_addr), (char *)ip, ip_len);
+		}
+    if (port) {
+			*port = ntohs(s->sin_port);
+		}
+  } else {
+    struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
+    if (ip) {
+			inet_ntop(AF_INET6, (void*)&(s->sin6_addr), (char *)ip, ip_len);
+		}
+    if (port) {
+			*port = ntohs(s->sin6_port);
+		}
+  }
+
+  return (fd);
+}
+
+static int _cs_tcp_nodelay(cs_sock_t fd, int yes) {
+	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&yes, sizeof(yes)) 
+    == -1) {
+		// cs_strerror(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
+		return (CS_ERR);
+	}
+
+	return (CS_OK);
+}
+
+CS_API int cs_tcp_enable_nodelay(cs_sock_t fd) {
+	return _cs_tcp_nodelay(fd, 1);
+}
+
+CS_API int cs_tcp_disable_nodelay(cs_sock_t fd) {
+	return _cs_tcp_nodelay(fd, 0);
+}
+
+CS_API int cs_tcp_keepalive(cs_sock_t fd) {
+	int yes = 1;
+	
+	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&yes, sizeof(yes)) 
+    == -1) {
+		return (CS_ERR);
+	}
+	
+	return (CS_OK);
+}
+
+/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
+ * number of milliseconds, or disable it if the 'ms' argument is zero. */
+CS_API int cs_tcp_sndtimeo(cs_sock_t fd, long long ms) {
+  struct timeval tv;
+
+  tv.tv_sec = ms / 1000;
+  tv.tv_usec = (ms % 1000) * 1000;
+  if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
+    // cs_strerror(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno));
+    return (CS_ERR);
+  }
+
+  return (CS_OK);
+}
+
+CS_API int cs_tcp_rcvtimeo(cs_sock_t fd, long long ms) {
+  struct timeval tv;
+
+  tv.tv_sec = ms / 1000;
+  tv.tv_usec = (ms % 1000) * 1000;
+  if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+    // cs_strerror(err, "setsockopt SO_RCVTIMEO: %s", strerror(errno));
+    return (CS_ERR);
+  }
+
+  return (CS_OK);
+}
+
+static int _cs_tcp_blocking(cs_sock_t fd, int yes) {
+  int flags;
+
+  /* Set the socket blocking (if yes is zero) or non-blocking.
+   * Note that fcntl(2) for F_GETFL and F_SETFL can't be
+   * interrupted by a signal. */
+#ifdef _WIN32
+	u_long arg = yes;
+	if (ioctlsocket(fd, FIONBIO, &arg) == SOCKET_ERROR){		
+		return (CS_ERR);
+	}
+#else
+  if ((flags = fcntl(fd, F_GETFL)) == -1) {
+    // cs_strerror(err, "fcntl(F_GETFL): %s", strerror(errno));
+    return (CS_ERR);
+  }
+
+  if (yes) {
+    flags |= O_NONBLOCK;
+	} else {
+		flags &= ~O_NONBLOCK;
+	}
+  if (fcntl(fd, F_SETFL, flags) == -1) {
+    // cs_strerror(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));
+    return (CS_ERR);
+  }
+#endif
+
+  return (CS_OK);
+}
+
+CS_API int cs_tcp_nonblocking(cs_sock_t fd) {
+	return _cs_tcp_blocking(fd, 1);
+}
+
+CS_API int cs_tcp_blocking(cs_sock_t fd) {
+	return _cs_tcp_blocking(fd, 0);
+}
+
+CS_API int cs_tcp_close(cs_sock_t fd) {
+#ifdef _WIN32
+	shutdown(fd, SD_BOTH);
+	closesocket(fd);
+#else
+	shutdown(fd, SHUT_RDWR);
+	close(fd);	
+#endif
+
+	return (CS_OK);
+}

+ 11 - 0
libcs/src/dll.c

@@ -0,0 +1,11 @@
+#include "../inc/cs.h"
+
+#ifdef _WIN32
+BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) {	
+	return(TRUE);
+}
+#endif
+
+CS_API const u_char *cs_libversion() {
+	return ((const u_char *)CS_VERSION);
+}

+ 135 - 0
libcs/src/ev/ae_epoll.c

@@ -0,0 +1,135 @@
+/* Linux epoll(2) based ae.c module
+ *
+ * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "../../inc/ae.h"
+
+#include <sys/epoll.h>
+
+typedef struct aeApiState {
+    int epfd;
+    struct epoll_event *events;
+} aeApiState;
+
+static int aeApiCreate(aeEventLoop *eventLoop) {
+    aeApiState *state = zmalloc(sizeof(aeApiState));
+
+    if (!state) return -1;
+    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
+    if (!state->events) {
+        zfree(state);
+        return -1;
+    }
+    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
+    if (state->epfd == -1) {
+        zfree(state->events);
+        zfree(state);
+        return -1;
+    }
+    eventLoop->apidata = state;
+    return 0;
+}
+
+static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
+    aeApiState *state = eventLoop->apidata;
+
+    state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);
+    return 0;
+}
+
+static void aeApiFree(aeEventLoop *eventLoop) {
+    aeApiState *state = eventLoop->apidata;
+
+    close(state->epfd);
+    zfree(state->events);
+    zfree(state);
+}
+
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+    struct epoll_event ee = {0}; /* avoid valgrind warning */
+    /* If the fd was already monitored for some event, we need a MOD
+     * operation. Otherwise we need an ADD operation. */
+    int op = eventLoop->events[fd].mask == AE_NONE ?
+            EPOLL_CTL_ADD : EPOLL_CTL_MOD;
+
+    ee.events = 0;
+    mask |= eventLoop->events[fd].mask; /* Merge old events */
+    if (mask & AE_READABLE) ee.events |= EPOLLIN;
+    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
+    ee.data.fd = fd;
+    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
+    return 0;
+}
+
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
+    aeApiState *state = eventLoop->apidata;
+    struct epoll_event ee = {0}; /* avoid valgrind warning */
+    int mask = eventLoop->events[fd].mask & (~delmask);
+
+    ee.events = 0;
+    if (mask & AE_READABLE) ee.events |= EPOLLIN;
+    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
+    ee.data.fd = fd;
+    if (mask != AE_NONE) {
+        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);
+    } else {
+        /* Note, Kernel < 2.6.9 requires a non null event pointer even for
+         * EPOLL_CTL_DEL. */
+        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);
+    }
+}
+
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
+    aeApiState *state = eventLoop->apidata;
+    int retval, numevents = 0;
+
+    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
+            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
+    if (retval > 0) {
+        int j;
+
+        numevents = retval;
+        for (j = 0; j < numevents; j++) {
+            int mask = 0;
+            struct epoll_event *e = state->events+j;
+
+            if (e->events & EPOLLIN) mask |= AE_READABLE;
+            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
+            if (e->events & EPOLLERR) mask |= AE_WRITABLE;
+            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
+            eventLoop->fired[j].fd = e->data.fd;
+            eventLoop->fired[j].mask = mask;
+        }
+    }
+    return numevents;
+}
+
+static char *aeApiName(void) {
+    return "epoll";
+}

+ 320 - 0
libcs/src/ev/ae_evport.c

@@ -0,0 +1,320 @@
+/* ae.c module for illumos event ports.
+ *
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "../../inc/ae.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <port.h>
+#include <poll.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+
+static int evport_debug = 0;
+
+/*
+ * This file implements the ae API using event ports, present on Solaris-based
+ * systems since Solaris 10.  Using the event port interface, we associate file
+ * descriptors with the port.  Each association also includes the set of poll(2)
+ * events that the consumer is interested in (e.g., POLLIN and POLLOUT).
+ *
+ * There's one tricky piece to this implementation: when we return events via
+ * aeApiPoll, the corresponding file descriptors become dissociated from the
+ * port.  This is necessary because poll events are level-triggered, so if the
+ * fd didn't become dissociated, it would immediately fire another event since
+ * the underlying state hasn't changed yet.  We must re-associate the file
+ * descriptor, but only after we know that our caller has actually read from it.
+ * The ae API does not tell us exactly when that happens, but we do know that
+ * it must happen by the time aeApiPoll is called again.  Our solution is to
+ * keep track of the last fds returned by aeApiPoll and re-associate them next
+ * time aeApiPoll is invoked.
+ *
+ * To summarize, in this module, each fd association is EITHER (a) represented
+ * only via the in-kernel association OR (b) represented by pending_fds and
+ * pending_masks.  (b) is only true for the last fds we returned from aeApiPoll,
+ * and only until we enter aeApiPoll again (at which point we restore the
+ * in-kernel association).
+ */
+#define MAX_EVENT_BATCHSZ 512
+
+typedef struct aeApiState {
+    int     portfd;                             /* event port */
+    int     npending;                           /* # of pending fds */
+    int     pending_fds[MAX_EVENT_BATCHSZ];     /* pending fds */
+    int     pending_masks[MAX_EVENT_BATCHSZ];   /* pending fds' masks */
+} aeApiState;
+
+static int aeApiCreate(aeEventLoop *eventLoop) {
+    int i;
+    aeApiState *state = zmalloc(sizeof(aeApiState));
+    if (!state) return -1;
+
+    state->portfd = port_create();
+    if (state->portfd == -1) {
+        zfree(state);
+        return -1;
+    }
+
+    state->npending = 0;
+
+    for (i = 0; i < MAX_EVENT_BATCHSZ; i++) {
+        state->pending_fds[i] = -1;
+        state->pending_masks[i] = AE_NONE;
+    }
+
+    eventLoop->apidata = state;
+    return 0;
+}
+
+static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
+    /* Nothing to resize here. */
+    return 0;
+}
+
+static void aeApiFree(aeEventLoop *eventLoop) {
+    aeApiState *state = eventLoop->apidata;
+
+    close(state->portfd);
+    zfree(state);
+}
+
+static int aeApiLookupPending(aeApiState *state, int fd) {
+    int i;
+
+    for (i = 0; i < state->npending; i++) {
+        if (state->pending_fds[i] == fd)
+            return (i);
+    }
+
+    return (-1);
+}
+
+/*
+ * Helper function to invoke port_associate for the given fd and mask.
+ */
+static int aeApiAssociate(const char *where, int portfd, int fd, int mask) {
+    int events = 0;
+    int rv, err;
+
+    if (mask & AE_READABLE)
+        events |= POLLIN;
+    if (mask & AE_WRITABLE)
+        events |= POLLOUT;
+
+    if (evport_debug)
+        fprintf(stderr, "%s: port_associate(%d, 0x%x) = ", where, fd, events);
+
+    rv = port_associate(portfd, PORT_SOURCE_FD, fd, events,
+        (void *)(uintptr_t)mask);
+    err = errno;
+
+    if (evport_debug)
+        fprintf(stderr, "%d (%s)\n", rv, rv == 0 ? "no error" : strerror(err));
+
+    if (rv == -1) {
+        fprintf(stderr, "%s: port_associate: %s\n", where, strerror(err));
+
+        if (err == EAGAIN)
+            fprintf(stderr, "aeApiAssociate: event port limit exceeded.");
+    }
+
+    return rv;
+}
+
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+    int fullmask, pfd;
+
+    if (evport_debug)
+        fprintf(stderr, "aeApiAddEvent: fd %d mask 0x%x\n", fd, mask);
+
+    /*
+     * Since port_associate's "events" argument replaces any existing events, we
+     * must be sure to include whatever events are already associated when
+     * we call port_associate() again.
+     */
+    fullmask = mask | eventLoop->events[fd].mask;
+    pfd = aeApiLookupPending(state, fd);
+
+    if (pfd != -1) {
+        /*
+         * This fd was recently returned from aeApiPoll.  It should be safe to
+         * assume that the consumer has processed that poll event, but we play
+         * it safer by simply updating pending_mask.  The fd will be
+         * re-associated as usual when aeApiPoll is called again.
+         */
+        if (evport_debug)
+            fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd);
+        state->pending_masks[pfd] |= fullmask;
+        return 0;
+    }
+
+    return (aeApiAssociate("aeApiAddEvent", state->portfd, fd, fullmask));
+}
+
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+    int fullmask, pfd;
+
+    if (evport_debug)
+        fprintf(stderr, "del fd %d mask 0x%x\n", fd, mask);
+
+    pfd = aeApiLookupPending(state, fd);
+
+    if (pfd != -1) {
+        if (evport_debug)
+            fprintf(stderr, "deleting event from pending fd %d\n", fd);
+
+        /*
+         * This fd was just returned from aeApiPoll, so it's not currently
+         * associated with the port.  All we need to do is update
+         * pending_mask appropriately.
+         */
+        state->pending_masks[pfd] &= ~mask;
+
+        if (state->pending_masks[pfd] == AE_NONE)
+            state->pending_fds[pfd] = -1;
+
+        return;
+    }
+
+    /*
+     * The fd is currently associated with the port.  Like with the add case
+     * above, we must look at the full mask for the file descriptor before
+     * updating that association.  We don't have a good way of knowing what the
+     * events are without looking into the eventLoop state directly.  We rely on
+     * the fact that our caller has already updated the mask in the eventLoop.
+     */
+
+    fullmask = eventLoop->events[fd].mask;
+    if (fullmask == AE_NONE) {
+        /*
+         * We're removing *all* events, so use port_dissociate to remove the
+         * association completely.  Failure here indicates a bug.
+         */
+        if (evport_debug)
+            fprintf(stderr, "aeApiDelEvent: port_dissociate(%d)\n", fd);
+
+        if (port_dissociate(state->portfd, PORT_SOURCE_FD, fd) != 0) {
+            perror("aeApiDelEvent: port_dissociate");
+            abort(); /* will not return */
+        }
+    } else if (aeApiAssociate("aeApiDelEvent", state->portfd, fd,
+        fullmask) != 0) {
+        /*
+         * ENOMEM is a potentially transient condition, but the kernel won't
+         * generally return it unless things are really bad.  EAGAIN indicates
+         * we've reached an resource limit, for which it doesn't make sense to
+         * retry (counter-intuitively).  All other errors indicate a bug.  In any
+         * of these cases, the best we can do is to abort.
+         */
+        abort(); /* will not return */
+    }
+}
+
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
+    aeApiState *state = eventLoop->apidata;
+    struct timespec timeout, *tsp;
+    int mask, i;
+    uint_t nevents;
+    port_event_t event[MAX_EVENT_BATCHSZ];
+
+    /*
+     * If we've returned fd events before, we must re-associate them with the
+     * port now, before calling port_get().  See the block comment at the top of
+     * this file for an explanation of why.
+     */
+    for (i = 0; i < state->npending; i++) {
+        if (state->pending_fds[i] == -1)
+            /* This fd has since been deleted. */
+            continue;
+
+        if (aeApiAssociate("aeApiPoll", state->portfd,
+            state->pending_fds[i], state->pending_masks[i]) != 0) {
+            /* See aeApiDelEvent for why this case is fatal. */
+            abort();
+        }
+
+        state->pending_masks[i] = AE_NONE;
+        state->pending_fds[i] = -1;
+    }
+
+    state->npending = 0;
+
+    if (tvp != NULL) {
+        timeout.tv_sec = tvp->tv_sec;
+        timeout.tv_nsec = tvp->tv_usec * 1000;
+        tsp = &timeout;
+    } else {
+        tsp = NULL;
+    }
+
+    /*
+     * port_getn can return with errno == ETIME having returned some events (!).
+     * So if we get ETIME, we check nevents, too.
+     */
+    nevents = 1;
+    if (port_getn(state->portfd, event, MAX_EVENT_BATCHSZ, &nevents,
+        tsp) == -1 && (errno != ETIME || nevents == 0)) {
+        if (errno == ETIME || errno == EINTR)
+            return 0;
+
+        /* Any other error indicates a bug. */
+        perror("aeApiPoll: port_get");
+        abort();
+    }
+
+    state->npending = nevents;
+
+    for (i = 0; i < nevents; i++) {
+            mask = 0;
+            if (event[i].portev_events & POLLIN)
+                mask |= AE_READABLE;
+            if (event[i].portev_events & POLLOUT)
+                mask |= AE_WRITABLE;
+
+            eventLoop->fired[i].fd = event[i].portev_object;
+            eventLoop->fired[i].mask = mask;
+
+            if (evport_debug)
+                fprintf(stderr, "aeApiPoll: fd %d mask 0x%x\n",
+                    (int)event[i].portev_object, mask);
+
+            state->pending_fds[i] = event[i].portev_object;
+            state->pending_masks[i] = (uintptr_t)event[i].portev_user;
+    }
+
+    return nevents;
+}
+
+static char *aeApiName(void) {
+    return "evport";
+}

+ 138 - 0
libcs/src/ev/ae_kqueue.c

@@ -0,0 +1,138 @@
+/* Kqueue(2)-based ae.c module
+ *
+ * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "../../inc/ae.h"
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+typedef struct aeApiState {
+    int kqfd;
+    struct kevent *events;
+} aeApiState;
+
+static int aeApiCreate(aeEventLoop *eventLoop) {
+    aeApiState *state = zmalloc(sizeof(aeApiState));
+
+    if (!state) return -1;
+    state->events = zmalloc(sizeof(struct kevent)*eventLoop->setsize);
+    if (!state->events) {
+        zfree(state);
+        return -1;
+    }
+    state->kqfd = kqueue();
+    if (state->kqfd == -1) {
+        zfree(state->events);
+        zfree(state);
+        return -1;
+    }
+    eventLoop->apidata = state;
+    return 0;
+}
+
+static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
+    aeApiState *state = eventLoop->apidata;
+
+    state->events = zrealloc(state->events, sizeof(struct kevent)*setsize);
+    return 0;
+}
+
+static void aeApiFree(aeEventLoop *eventLoop) {
+    aeApiState *state = eventLoop->apidata;
+
+    close(state->kqfd);
+    zfree(state->events);
+    zfree(state);
+}
+
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+    struct kevent ke;
+
+    if (mask & AE_READABLE) {
+        EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
+    }
+    if (mask & AE_WRITABLE) {
+        EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
+    }
+    return 0;
+}
+
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+    struct kevent ke;
+
+    if (mask & AE_READABLE) {
+        EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
+    }
+    if (mask & AE_WRITABLE) {
+        EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
+        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
+    }
+}
+
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
+    aeApiState *state = eventLoop->apidata;
+    int retval, numevents = 0;
+
+    if (tvp != NULL) {
+        struct timespec timeout;
+        timeout.tv_sec = tvp->tv_sec;
+        timeout.tv_nsec = tvp->tv_usec * 1000;
+        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize,
+                        &timeout);
+    } else {
+        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize,
+                        NULL);
+    }
+
+    if (retval > 0) {
+        int j;
+
+        numevents = retval;
+        for(j = 0; j < numevents; j++) {
+            int mask = 0;
+            struct kevent *e = state->events+j;
+
+            if (e->filter == EVFILT_READ) mask |= AE_READABLE;
+            if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;
+            eventLoop->fired[j].fd = e->ident;
+            eventLoop->fired[j].mask = mask;
+        }
+    }
+    return numevents;
+}
+
+static char *aeApiName(void) {
+    return "kqueue";
+}

+ 106 - 0
libcs/src/ev/ae_select.c

@@ -0,0 +1,106 @@
+/* Select()-based ae.c module.
+ *
+ * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "../../inc/ae.h"
+
+#include <sys/select.h>
+#include <string.h>
+
+typedef struct aeApiState {
+    fd_set rfds, wfds;
+    /* We need to have a copy of the fd sets as it's not safe to reuse
+     * FD sets after select(). */
+    fd_set _rfds, _wfds;
+} aeApiState;
+
+static int aeApiCreate(aeEventLoop *eventLoop) {
+    aeApiState *state = zmalloc(sizeof(aeApiState));
+
+    if (!state) return -1;
+    FD_ZERO(&state->rfds);
+    FD_ZERO(&state->wfds);
+    eventLoop->apidata = state;
+    return 0;
+}
+
+static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
+    /* Just ensure we have enough room in the fd_set type. */
+    if (setsize >= FD_SETSIZE) return -1;
+    return 0;
+}
+
+static void aeApiFree(aeEventLoop *eventLoop) {
+    zfree(eventLoop->apidata);
+}
+
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+
+    if (mask & AE_READABLE) FD_SET(fd,&state->rfds);
+    if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds);
+    return 0;
+}
+
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
+    aeApiState *state = eventLoop->apidata;
+
+    if (mask & AE_READABLE) FD_CLR(fd,&state->rfds);
+    if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds);
+}
+
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
+    aeApiState *state = eventLoop->apidata;
+    int retval, j, numevents = 0;
+
+    memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));
+    memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));
+
+    retval = select(eventLoop->maxfd+1,
+                &state->_rfds,&state->_wfds,NULL,tvp);
+    if (retval > 0) {
+        for (j = 0; j <= eventLoop->maxfd; j++) {
+            int mask = 0;
+            aeFileEvent *fe = &eventLoop->events[j];
+
+            if (fe->mask == AE_NONE) continue;
+            if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))
+                mask |= AE_READABLE;
+            if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))
+                mask |= AE_WRITABLE;
+            eventLoop->fired[numevents].fd = j;
+            eventLoop->fired[numevents].mask = mask;
+            numevents++;
+        }
+    }
+    return numevents;
+}
+
+static char *aeApiName(void) {
+    return "select";
+}

+ 281 - 0
libcs/src/hash/md5.c

@@ -0,0 +1,281 @@
+/*
+ *  RFC 1321 compliant MD5 implementation
+ *
+ *  Copyright (C) 2001-2003  Christophe Devine
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "../../inc/cs.h"
+#include "../../inc/cs_str.h"
+
+#ifndef uint8
+#define uint8  unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+#define uint32_t unsigned int
+#define uint8_t unsigned char
+#define int_least16_t int
+
+typedef struct
+{
+    uint32 total[2];
+    uint32 state[4];
+    uint8 buffer[64];
+}md5_context;
+
+#define GET_UINT32(n,b,i)                       \
+{                                               \
+    (n) = ( (uint32) (b)[(i)    ]       )       \
+        | ( (uint32) (b)[(i) + 1] <<  8 )       \
+        | ( (uint32) (b)[(i) + 2] << 16 )       \
+        | ( (uint32) (b)[(i) + 3] << 24 );      \
+}
+
+#define PUT_UINT32(n,b,i)                       \
+{                                               \
+    (b)[(i)    ] = (uint8) ( (n)       );       \
+    (b)[(i) + 1] = (uint8) ( (n) >>  8 );       \
+    (b)[(i) + 2] = (uint8) ( (n) >> 16 );       \
+    (b)[(i) + 3] = (uint8) ( (n) >> 24 );       \
+}
+
+static void md5_starts( md5_context *ctx )
+{
+    ctx->total[0] = 0;
+    ctx->total[1] = 0;
+
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+}
+
+static void md5_process( md5_context *ctx, uint8 data[64] )
+{
+    uint32 X[16], A, B, C, D;
+
+    GET_UINT32( X[0],  data,  0 );
+    GET_UINT32( X[1],  data,  4 );
+    GET_UINT32( X[2],  data,  8 );
+    GET_UINT32( X[3],  data, 12 );
+    GET_UINT32( X[4],  data, 16 );
+    GET_UINT32( X[5],  data, 20 );
+    GET_UINT32( X[6],  data, 24 );
+    GET_UINT32( X[7],  data, 28 );
+    GET_UINT32( X[8],  data, 32 );
+    GET_UINT32( X[9],  data, 36 );
+    GET_UINT32( X[10], data, 40 );
+    GET_UINT32( X[11], data, 44 );
+    GET_UINT32( X[12], data, 48 );
+    GET_UINT32( X[13], data, 52 );
+    GET_UINT32( X[14], data, 56 );
+    GET_UINT32( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t)                                \
+{                                                       \
+    a += F(b,c,d) + X[k] + t; a = S(a,s) + b;           \
+}
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+    P( A, B, C, D,  0,  7, 0xD76AA478 );
+    P( D, A, B, C,  1, 12, 0xE8C7B756 );
+    P( C, D, A, B,  2, 17, 0x242070DB );
+    P( B, C, D, A,  3, 22, 0xC1BDCEEE );
+    P( A, B, C, D,  4,  7, 0xF57C0FAF );
+    P( D, A, B, C,  5, 12, 0x4787C62A );
+    P( C, D, A, B,  6, 17, 0xA8304613 );
+    P( B, C, D, A,  7, 22, 0xFD469501 );
+    P( A, B, C, D,  8,  7, 0x698098D8 );
+    P( D, A, B, C,  9, 12, 0x8B44F7AF );
+    P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+    P( B, C, D, A, 11, 22, 0x895CD7BE );
+    P( A, B, C, D, 12,  7, 0x6B901122 );
+    P( D, A, B, C, 13, 12, 0xFD987193 );
+    P( C, D, A, B, 14, 17, 0xA679438E );
+    P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+    P( A, B, C, D,  1,  5, 0xF61E2562 );
+    P( D, A, B, C,  6,  9, 0xC040B340 );
+    P( C, D, A, B, 11, 14, 0x265E5A51 );
+    P( B, C, D, A,  0, 20, 0xE9B6C7AA );
+    P( A, B, C, D,  5,  5, 0xD62F105D );
+    P( D, A, B, C, 10,  9, 0x02441453 );
+    P( C, D, A, B, 15, 14, 0xD8A1E681 );
+    P( B, C, D, A,  4, 20, 0xE7D3FBC8 );
+    P( A, B, C, D,  9,  5, 0x21E1CDE6 );
+    P( D, A, B, C, 14,  9, 0xC33707D6 );
+    P( C, D, A, B,  3, 14, 0xF4D50D87 );
+    P( B, C, D, A,  8, 20, 0x455A14ED );
+    P( A, B, C, D, 13,  5, 0xA9E3E905 );
+    P( D, A, B, C,  2,  9, 0xFCEFA3F8 );
+    P( C, D, A, B,  7, 14, 0x676F02D9 );
+    P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+
+    P( A, B, C, D,  5,  4, 0xFFFA3942 );
+    P( D, A, B, C,  8, 11, 0x8771F681 );
+    P( C, D, A, B, 11, 16, 0x6D9D6122 );
+    P( B, C, D, A, 14, 23, 0xFDE5380C );
+    P( A, B, C, D,  1,  4, 0xA4BEEA44 );
+    P( D, A, B, C,  4, 11, 0x4BDECFA9 );
+    P( C, D, A, B,  7, 16, 0xF6BB4B60 );
+    P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+    P( A, B, C, D, 13,  4, 0x289B7EC6 );
+    P( D, A, B, C,  0, 11, 0xEAA127FA );
+    P( C, D, A, B,  3, 16, 0xD4EF3085 );
+    P( B, C, D, A,  6, 23, 0x04881D05 );
+    P( A, B, C, D,  9,  4, 0xD9D4D039 );
+    P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+    P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+    P( B, C, D, A,  2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+    P( A, B, C, D,  0,  6, 0xF4292244 );
+    P( D, A, B, C,  7, 10, 0x432AFF97 );
+    P( C, D, A, B, 14, 15, 0xAB9423A7 );
+    P( B, C, D, A,  5, 21, 0xFC93A039 );
+    P( A, B, C, D, 12,  6, 0x655B59C3 );
+    P( D, A, B, C,  3, 10, 0x8F0CCC92 );
+    P( C, D, A, B, 10, 15, 0xFFEFF47D );
+    P( B, C, D, A,  1, 21, 0x85845DD1 );
+    P( A, B, C, D,  8,  6, 0x6FA87E4F );
+    P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+    P( C, D, A, B,  6, 15, 0xA3014314 );
+    P( B, C, D, A, 13, 21, 0x4E0811A1 );
+    P( A, B, C, D,  4,  6, 0xF7537E82 );
+    P( D, A, B, C, 11, 10, 0xBD3AF235 );
+    P( C, D, A, B,  2, 15, 0x2AD7D2BB );
+    P( B, C, D, A,  9, 21, 0xEB86D391 );
+
+#undef F
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+}
+
+static void md5_update( md5_context *ctx, uint8 *input, uint32 length )
+{
+    uint32 left, fill;
+
+    if( ! length ) return;
+
+    left = ctx->total[0] & 0x3F;
+    fill = 64 - left;
+
+    ctx->total[0] += length;
+    ctx->total[0] &= 0xFFFFFFFF;
+
+    if( ctx->total[0] < length )
+        ctx->total[1]++;
+
+    if( left && length >= fill )
+    {
+        memcpy( (void *) (ctx->buffer + left),
+                (void *) input, fill );
+        md5_process( ctx, ctx->buffer );
+        length -= fill;
+        input  += fill;
+        left = 0;
+    }
+
+    while( length >= 64 )
+    {
+        md5_process( ctx, input );
+        length -= 64;
+        input  += 64;
+    }
+
+    if( length )
+    {
+        memcpy( (void *) (ctx->buffer + left),
+                (void *) input, length );
+    }
+}
+
+static uint8 md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void md5_finish( md5_context *ctx, uint8 digest[16] )
+{
+    uint32 last, padn;
+    uint32 high, low;
+    uint8 msglen[8];
+
+    high = ( ctx->total[0] >> 29 )
+         | ( ctx->total[1] <<  3 );
+    low  = ( ctx->total[0] <<  3 );
+
+    PUT_UINT32( low,  msglen, 0 );
+    PUT_UINT32( high, msglen, 4 );
+
+    last = ctx->total[0] & 0x3F;
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+    md5_update( ctx, md5_padding, padn );
+    md5_update( ctx, msglen, 8 );
+
+    PUT_UINT32( ctx->state[0], digest,  0 );
+    PUT_UINT32( ctx->state[1], digest,  4 );
+    PUT_UINT32( ctx->state[2], digest,  8 );
+    PUT_UINT32( ctx->state[3], digest, 12 );
+}
+
+CS_API u_char *cs_md5(u_char *d, const u_char *s, size_t n) {
+    int i;
+	md5_context ctx;
+	u_char md5sum[16];
+	static u_char hex[]	= "0123456789abcdef";
+	u_char *dst = d;
+	
+	md5_starts(&ctx);
+	md5_update(&ctx, (uint8 *)s, n);
+	md5_finish(&ctx, md5sum);
+	
+	for (i = 0; i < 16; i++) {
+		*d++ = hex[md5sum[i] >> 4];
+        *d++ = hex[md5sum[i] & 0xf];
+	}
+
+	return (dst);
+}

+ 468 - 0
libcs/src/hash/sha1.c

@@ -0,0 +1,468 @@
+/*
+ *  sha1.c
+ *
+ *  Description:
+ *      This file implements the Secure Hashing Algorithm 1 as
+ *      defined in FIPS PUB 180-1 published April 17, 1995.
+ *
+ *      The SHA-1, produces a 160-bit message digest for a given
+ *      data stream.  It should take about 2**n steps to find a
+ *      message with the same digest as a given message and
+ *      2**(n/2) to find any two messages with the same digest,
+ *      when n is the digest size in bits.  Therefore, this
+ *      algorithm can serve as a means of providing a
+ *      "fingerprint" for a message.
+ *
+ *  Portability Issues:
+ *      SHA-1 is defined in terms of 32-bit "words".  This code
+ *      uses <stdint.h> (included via "sha1.h" to define 32 and 8
+ *      bit unsigned integer types.  If your C compiler does not
+ *      support 32 bit unsigned integers, this code is not
+ *      appropriate.
+ *
+ *  Caveats:
+ *      SHA-1 is designed to work with messages less than 2^64 bits
+ *      long.  Although SHA-1 allows a message digest to be generated
+ *      for messages of any number of bits less than 2^64, this
+ *      implementation only works with messages with a length that is
+ *      a multiple of the size of an 8-bit character.
+ *
+*/
+#include "../../inc/cs.h"
+#include "../../inc/cs_str.h"
+
+#ifndef uint8
+#define uint8  unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+#define uint32_t unsigned int
+#define uint8_t unsigned char
+#define int_least16_t int
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum
+{
+    shaSuccess = 0,
+	shaNull,            /* Null pointer parameter */
+	shaInputTooLong,    /* input data too long */
+	shaStateError       /* called Input after Result */
+};
+#endif
+#define SHA1HashSize 20
+/*
+*  This structure will hold context information for the SHA-1
+*  hashing operation
+*/
+typedef struct SHA1Context
+{
+    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */
+	
+    uint32_t Length_Low;            /* Message length in bits      */
+    uint32_t Length_High;           /* Message length in bits      */
+	
+	/* Index into message block array   */
+    int_least16_t Message_Block_Index;
+    uint8_t Message_Block[64];      /* 512-bit message blocks      */
+	
+    int Computed;               /* Is the digest computed?         */
+    int Corrupted;             /* Is the message digest corrupted? */
+} SHA1Context;
+
+ /*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ *    name              meaning
+ *  uint32_t         unsigned 32 bit integer
+ *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
+ *  int_least16_t    integer of >= 16 bits
+ *
+*/
+
+/*
+ *  Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+                (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+static void SHA1PadMessage(SHA1Context *);
+static void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ *  SHA1Reset
+ *
+ *  Description:
+ *      This function will initialize the SHA1Context in preparation
+ *      for computing a new SHA1 message digest.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to reset.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+static int SHA1Reset(SHA1Context *context)
+{
+    if (!context)
+    {
+        return shaNull;
+    }
+
+    context->Length_Low             = 0;
+    context->Length_High            = 0;
+    context->Message_Block_Index    = 0;
+
+    context->Intermediate_Hash[0]   = 0x67452301;
+    context->Intermediate_Hash[1]   = 0xEFCDAB89;
+    context->Intermediate_Hash[2]   = 0x98BADCFE;
+    context->Intermediate_Hash[3]   = 0x10325476;
+    context->Intermediate_Hash[4]   = 0xC3D2E1F0;
+
+    context->Computed   = 0;
+    context->Corrupted  = 0;
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1Result
+ *
+ *  Description:
+ *      This function will return the 160-bit message digest into the
+ *      Message_Digest array  provided by the caller.
+ *      NOTE: The first octet of hash is stored in the 0th element,
+ *            the last octet of hash in the 19th element.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to use to calculate the SHA-1 hash.
+ *      Message_Digest: [out]
+ *          Where the digest is returned.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+static int SHA1Result( SHA1Context *context,
+                uint8_t Message_Digest[SHA1HashSize])
+{
+    int i;
+
+    if (!context || !Message_Digest)
+    {
+        return shaNull;
+    }
+
+    if (context->Corrupted)
+    {
+        return context->Corrupted;
+    }
+
+    if (!context->Computed)
+    {
+        SHA1PadMessage(context);
+        for(i=0; i<64; ++i)
+        {
+            /* message may be sensitive, clear it out */
+            context->Message_Block[i] = 0;
+        }
+        context->Length_Low = 0;    /* and clear length */
+        context->Length_High = 0;
+        context->Computed = 1;
+
+    }
+
+    for(i = 0; i < SHA1HashSize; ++i)
+    {
+        Message_Digest[i] = context->Intermediate_Hash[i>>2]
+                            >> 8 * ( 3 - ( i & 0x03 ) );
+    }
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1Input
+ *
+ *  Description:
+ *      This function accepts an array of octets as the next portion
+ *      of the message.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The SHA context to update
+ *      message_array: [in]
+ *          An array of characters representing the next portion of
+ *          the message.
+ *      length: [in]
+ *          The length of the message in message_array
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+static int SHA1Input(    SHA1Context    *context,
+                  const uint8_t  *message_array,
+                  unsigned       length)
+{
+    if (!length)
+    {
+        return shaSuccess;
+    }
+
+    if (!context || !message_array)
+    {
+        return shaNull;
+    }
+
+    if (context->Computed)
+    {
+        context->Corrupted = shaStateError;
+
+        return shaStateError;
+    }
+
+    if (context->Corrupted)
+    {
+         return context->Corrupted;
+    }
+    while(length-- && !context->Corrupted)
+    {
+    context->Message_Block[context->Message_Block_Index++] =
+                    (*message_array & 0xFF);
+
+    context->Length_Low += 8;
+    if (context->Length_Low == 0)
+    {
+        context->Length_High++;
+        if (context->Length_High == 0)
+        {
+            /* Message is too long */
+            context->Corrupted = 1;
+        }
+    }
+
+    if (context->Message_Block_Index == 64)
+    {
+        SHA1ProcessMessageBlock(context);
+    }
+
+    message_array++;
+    }
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1ProcessMessageBlock
+ *
+ *  Description:
+ *      This function will process the next 512 bits of the message
+ *      stored in the Message_Block array.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the
+ *      names used in the publication.
+ *
+ *
+ */
+static void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+    const uint32_t K[] =    {       /* Constants defined in SHA-1   */
+                            0x5A827999,
+                            0x6ED9EBA1,
+                            0x8F1BBCDC,
+                            0xCA62C1D6
+                            };
+    int           t;                 /* Loop counter                */
+    uint32_t      temp;              /* Temporary word value        */
+    uint32_t      W[80];             /* Word sequence               */
+    uint32_t      A, B, C, D, E;     /* Word buffers                */
+
+    /*
+     *  Initialize the first 16 words in the array W
+     */
+    for(t = 0; t < 16; t++)
+    {
+        W[t] = context->Message_Block[t * 4] << 24;
+        W[t] |= context->Message_Block[t * 4 + 1] << 16;
+        W[t] |= context->Message_Block[t * 4 + 2] << 8;
+        W[t] |= context->Message_Block[t * 4 + 3];
+    }
+
+    for(t = 16; t < 80; t++)
+    {
+       W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = context->Intermediate_Hash[0];
+    B = context->Intermediate_Hash[1];
+    C = context->Intermediate_Hash[2];
+    D = context->Intermediate_Hash[3];
+    E = context->Intermediate_Hash[4];
+
+    for(t = 0; t < 20; t++)
+    {
+        temp =  SHA1CircularShift(5,A) +
+                ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+
+        B = A;
+        A = temp;
+    }
+
+    for(t = 20; t < 40; t++)
+    {
+        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 40; t < 60; t++)
+    {
+        temp = SHA1CircularShift(5,A) +
+               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 60; t < 80; t++)
+    {
+        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    context->Intermediate_Hash[0] += A;
+    context->Intermediate_Hash[1] += B;
+    context->Intermediate_Hash[2] += C;
+    context->Intermediate_Hash[3] += D;
+    context->Intermediate_Hash[4] += E;
+
+    context->Message_Block_Index = 0;
+}
+
+/*
+ *  SHA1PadMessage
+ *
+
+ *  Description:
+ *      According to the standard, the message must be padded to an even
+ *      512 bits.  The first padding bit must be a '1'.  The last 64
+ *      bits represent the length of the original message.  All bits in
+ *      between should be 0.  This function will pad the message
+ *      according to those rules by filling the Message_Block array
+ *      accordingly.  It will also call the ProcessMessageBlock function
+ *      provided appropriately.  When it returns, it can be assumed that
+ *      the message digest has been computed.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to pad
+ *      ProcessMessageBlock: [in]
+ *          The appropriate SHA*ProcessMessageBlock function
+ *  Returns:
+ *      Nothing.
+ *
+ */
+
+static void SHA1PadMessage(SHA1Context *context)
+{
+    /*
+     *  Check to see if the current message block is too small to hold
+     *  the initial padding bits and length.  If so, we will pad the
+     *  block, process it, and then continue padding into a second
+     *  block.
+     */
+    if (context->Message_Block_Index > 55)
+    {
+        context->Message_Block[context->Message_Block_Index++] = 0x80;
+        while(context->Message_Block_Index < 64)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+
+        SHA1ProcessMessageBlock(context);
+
+        while(context->Message_Block_Index < 56)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+    }
+    else
+    {
+        context->Message_Block[context->Message_Block_Index++] = 0x80;
+        while(context->Message_Block_Index < 56)
+        {
+
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+    }
+
+    /*
+     *  Store the message length as the last 8 octets
+     */
+    context->Message_Block[56] = context->Length_High >> 24;
+    context->Message_Block[57] = context->Length_High >> 16;
+    context->Message_Block[58] = context->Length_High >> 8;
+    context->Message_Block[59] = context->Length_High;
+    context->Message_Block[60] = context->Length_Low >> 24;
+    context->Message_Block[61] = context->Length_Low >> 16;
+    context->Message_Block[62] = context->Length_Low >> 8;
+    context->Message_Block[63] = context->Length_Low;
+
+    SHA1ProcessMessageBlock(context);
+}
+
+CS_API u_char *cs_sha1(u_char *d, const u_char *s, size_t n) {
+	SHA1Context sha;
+	int i, err;
+	uint8_t Message_Digest[20];
+	static u_char hex[]	= "0123456789abcdef";
+	u_char *dst = d;
+	
+	err = SHA1Reset(&sha);
+	if (err) {
+		return (NULL);
+	}
+	err = SHA1Input(&sha, (const unsigned char *)s, n);
+	if (err) {
+		return(NULL);
+	}
+	err = SHA1Result(&sha, Message_Digest);
+	if (err) {
+	    return(NULL);
+	} else {
+		for (i = 0; i < 20 ; ++i) {
+			*d++ = hex[Message_Digest[i] >> 4];
+			*d++ = hex[Message_Digest[i] & 0xf];
+		}
+	}
+	return (dst);
+}

+ 52 - 0
libcs/src/ngx_alloc.c

@@ -0,0 +1,52 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#include "../inc/cs.h"
+#include "../inc/cs_str.h"
+#include "../inc/ngx_alloc.h"
+
+void *ngx_alloc(size_t size) {
+  void  *p;
+
+  p = malloc(size);
+
+  return (p);
+}
+
+void *ngx_calloc(size_t size) {
+  void  *p;
+
+  p = ngx_alloc(size);
+
+  if (p) {
+    cs_bzero(p, size);
+  }
+
+  return p;
+}
+
+#if (NGX_HAVE_POSIX_MEMALIGN)
+void *ngx_memalign(size_t alignment, size_t size) {
+  void  *p;
+  int    err;
+
+  err = posix_memalign(&p, alignment, size);
+
+  if (err) {
+    p = NULL;
+  }
+
+  return p;
+}
+#elif (NGX_HAVE_MEMALIGN)
+void *ngx_memalign(size_t alignment, size_t size) {
+  void  *p;
+
+  p = memalign(alignment, size);
+    
+  return (p);
+}
+#endif

+ 258 - 0
libcs/src/ngx_palloc.c

@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#include "../inc/cs.h"
+#include "../inc/cs_os.h"
+#include "../inc/cs_str.h"
+#include "../inc/ngx_palloc.h"
+#include "../inc/ngx_alloc.h"
+
+static cs_inline void *ngx_palloc_small(
+  ngx_pool_t *pool, size_t size, cs_uint_t align);
+static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
+static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);
+
+CS_API ngx_pool_t *ngx_create_pool(size_t size) {
+  ngx_pool_t  *p;
+
+  p = ngx_memalign(NGX_POOL_ALIGNMENT, size);
+  if (p == NULL) {
+    return (NULL);
+  }
+
+  p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+  p->d.end = (u_char *) p + size;
+  p->d.next = NULL;
+  p->d.failed = 0;
+
+  size = size - sizeof(ngx_pool_t);
+  p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
+
+  p->current = p;
+  p->large = NULL;
+
+  return (p);
+}
+
+CS_API void ngx_destroy_pool(ngx_pool_t *pool) {
+  ngx_pool_t          *p, *n;
+  ngx_pool_large_t    *l;
+
+  #ifdef CS_DEBUG
+  int count = 0;
+  #endif
+  for (l = pool->large; l; l = l->next) {
+    if (l->alloc) {
+      ngx_free(l->alloc);
+    }
+    #ifdef CS_DEBUG
+    count++;
+    #endif
+  }
+
+  #ifdef CS_DEBUG
+    printf("large count: %d\n", count);
+    count = 0;
+  #endif
+    
+  for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
+    #ifdef CS_DEBUG
+    count++;
+    #endif
+    ngx_free(p);
+    if (n == NULL) {
+      break;
+    }
+  }
+  #ifdef CS_DEBUG
+    printf("pool count: %d\n", count);
+    count = 0;
+  #endif
+}
+
+CS_API void ngx_reset_pool(ngx_pool_t *pool) {
+  ngx_pool_t        *p;
+  ngx_pool_large_t  *l;
+
+  for (l = pool->large; l; l = l->next) {
+    if (l->alloc) {
+      ngx_free(l->alloc);
+    }
+  }
+
+  for (p = pool; p; p = p->d.next) {
+    p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+    p->d.failed = 0;
+  }
+
+  pool->current = pool;
+  pool->large = NULL;
+}
+
+
+CS_API void *ngx_palloc(ngx_pool_t *pool, size_t size) {
+#if !(NGX_DEBUG_PALLOC)
+  if (size <= pool->max) {
+    return ngx_palloc_small(pool, size, 1);
+  }
+#endif
+
+  return ngx_palloc_large(pool, size);
+}
+
+
+CS_API void *ngx_pnalloc(ngx_pool_t *pool, size_t size) {
+#if !(NGX_DEBUG_PALLOC)
+  if (size <= pool->max) {
+    return ngx_palloc_small(pool, size, 0);
+  }
+#endif
+
+  return ngx_palloc_large(pool, size);
+}
+
+static cs_inline void *ngx_palloc_small(
+  ngx_pool_t *pool, size_t size, cs_uint_t align) {
+  u_char      *m;
+  ngx_pool_t  *p;
+
+  p = pool->current;
+
+  do {
+    m = p->d.last;
+
+    if (align) {
+      m = cs_align_ptr(m, CS_ALIGNMENT);
+    }
+
+    if ((size_t) (p->d.end - m) >= size) {
+      p->d.last = m + size;
+
+      return m;
+    }
+
+    p = p->d.next;
+  } while (p);
+
+  return (ngx_palloc_block(pool, size));
+}
+
+
+static void *ngx_palloc_block(ngx_pool_t *pool, size_t size) {
+  u_char      *m;
+  size_t       psize;
+  ngx_pool_t  *p, *new;
+
+  psize = (size_t) (pool->d.end - (u_char *) pool);
+
+  m = ngx_memalign(NGX_POOL_ALIGNMENT, psize);
+  if (m == NULL) {
+    return (NULL);
+  }
+
+  new = (ngx_pool_t *) m;
+
+  new->d.end = m + psize;
+  new->d.next = NULL;
+  new->d.failed = 0;
+
+  m += sizeof(ngx_pool_data_t);
+  m = cs_align_ptr(m, CS_ALIGNMENT);
+  new->d.last = m + size;
+
+  for (p = pool->current; p->d.next; p = p->d.next) {
+    if (p->d.failed++ > 4) {
+      pool->current = p->d.next;
+    }
+  }
+
+  p->d.next = new;
+
+  return (m);
+}
+
+static void *ngx_palloc_large(ngx_pool_t *pool, size_t size) {
+  void              *p;
+  cs_uint_t          n;
+  ngx_pool_large_t  *large;
+
+  p = ngx_alloc(size);
+  if (p == NULL) {
+    return (NULL);
+  }
+
+  n = 0;
+
+  for (large = pool->large; large; large = large->next) {
+    if (large->alloc == NULL) {
+      large->alloc = p;
+      return p;
+    }
+
+    if (n++ > 3) {
+      break;
+    }
+  }
+
+  large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
+  if (large == NULL) {
+    ngx_free(p);
+    return (NULL);
+  }
+
+  large->alloc = p;
+  large->next = pool->large;
+  pool->large = large;
+
+  return (p);
+}
+
+CS_API void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment) {
+  void              *p;
+  ngx_pool_large_t  *large;
+
+  p = ngx_memalign(alignment, size);
+  if (p == NULL) {
+    return (NULL);
+  }
+
+  large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
+  if (large == NULL) {
+    ngx_free(p);
+    return (NULL);
+  }
+
+  large->alloc = p;
+  large->next = pool->large;
+  pool->large = large;
+
+  return (p);
+}
+
+CS_API cs_int_t ngx_pfree(ngx_pool_t *pool, void *p) {
+  ngx_pool_large_t  *l;
+
+  for (l = pool->large; l; l = l->next) {
+    if (p == l->alloc) {
+      ngx_free(l->alloc);
+      l->alloc = NULL;
+
+      return (CS_OK);
+    }
+  }
+
+  return (CS_DECLINED);
+}
+
+CS_API void *ngx_pcalloc(ngx_pool_t *pool, size_t size) {
+  void *p;
+
+  p = ngx_palloc(pool, size);
+  if (p) {
+    cs_bzero(p, size);
+  }
+
+  return (p);
+}