summaryrefslogtreecommitdiffstats
path: root/hw4
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2014-01-14 22:27:34 +0800
committerLAN-TW <lantw44@gmail.com>2014-01-14 22:27:34 +0800
commit562b3936c0140c215bc01ecd7ebc40e004f5b3ba (patch)
tree45eadcc6507d5b4e6a4d8ad2fbb25867c8ba37b9 /hw4
parent08b63dcd438ee4ec68fe9eb61b6cd42ee6684963 (diff)
downloadsp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.tar
sp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.tar.gz
sp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.tar.bz2
sp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.tar.lz
sp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.tar.xz
sp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.tar.zst
sp2013-562b3936c0140c215bc01ecd7ebc40e004f5b3ba.zip
HW4: 基本的 HTTP header 解析
Diffstat (limited to 'hw4')
-rw-r--r--hw4/Makefile.am2
-rwxr-xr-xhw4/autogen.sh2
-rw-r--r--hw4/chttpd/chttpd-conn.c87
-rw-r--r--hw4/chttpd/chttpd-conn.h5
-rw-r--r--hw4/chttpd/chttpd-server.c60
-rw-r--r--hw4/chttpd/chttpd-server.h18
-rw-r--r--hw4/chttpd/chttpd-socket.h1
-rw-r--r--hw4/l4basic/l4array.c258
-rw-r--r--hw4/l4basic/l4array.h88
9 files changed, 501 insertions, 20 deletions
diff --git a/hw4/Makefile.am b/hw4/Makefile.am
index 62f22d8..b04879a 100644
--- a/hw4/Makefile.am
+++ b/hw4/Makefile.am
@@ -8,6 +8,8 @@ libl4basic_a_SOURCES = \
l4basic/memwrap.h \
l4basic/memwrap.c \
l4basic/l4common.h \
+ l4basic/l4array.h \
+ l4basic/l4array.c \
l4basic/l4list.h \
l4basic/l4list.c \
l4basic/l4str.h \
diff --git a/hw4/autogen.sh b/hw4/autogen.sh
index 3e7805e..70d6dde 100755
--- a/hw4/autogen.sh
+++ b/hw4/autogen.sh
@@ -17,7 +17,7 @@ echo "==> Downloading l4basic files"
git clone "$1" "tmp"
cd "tmp"
-for i in l4common.h l4list.[ch] l4posix.[ch] l4str.[ch]
+for i in l4common.h l4list.[ch] l4posix.[ch] l4str.[ch] l4array.[ch]
do
generate_file "$i" "../l4basic/l4basic.sed"
done
diff --git a/hw4/chttpd/chttpd-conn.c b/hw4/chttpd/chttpd-conn.c
index 494742d..893661d 100644
--- a/hw4/chttpd/chttpd-conn.c
+++ b/hw4/chttpd/chttpd-conn.c
@@ -3,18 +3,28 @@
# include "config.h"
#endif
+#define CHTTPD_SERVER_ENABLE_ERRMSG
+
#include "chttpd-conn.h"
#include "chttpd-log.h"
#include "chttpd-server.h"
+#include <l4array.h>
#include <l4list.h>
+#include <l4str.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
#include <stdlib.h>
+#include <stdio.h>
#include <unistd.h>
#define CHTTPD_CONN_THREAD_INIT \
ChttpdConn* conn = ptr_to_ChttpdConn; \
ChttpdServer* server = conn->server; \
- ChttpdLog* hlog = conn->hlog
+ ChttpdLog* hlog = conn->hlog; \
+ unsigned long long id = conn->id; \
+ pthread_sigmask (SIG_SETMASK, &server->conn_mask, NULL);
#define CHTTPD_CONN_THREAD_DESTROY \
chttpd_log_write (hlog, "[%4llu] terminated", conn->id); \
@@ -23,18 +33,93 @@
pthread_rwlock_unlock (&server->lock); \
return NULL
+static inline char* internal_memstr (const void* haystack,
+ size_t haystacklen, const char* needle) {
+
+ const char* haychar = haystack;
+ bool matched;
+
+ for (size_t i = 0; i < haystacklen; i++, haychar++) {
+ matched = true;
+ for (size_t j = 0; needle[j] != '\0'; j++) {
+ if (i + j >= haystacklen || haychar[j] != needle[j]) {
+ matched = false;
+ break;
+ }
+ }
+ if (matched) {
+ return (char*)haychar;
+ }
+ }
+ return NULL;
+}
+
+#define ERRLEN 256
void* chttpd_conn_admin (void* ptr_to_ChttpdConn) {
CHTTPD_CONN_THREAD_INIT;
+ chttpd_log_write (hlog, "[%4llu] sorry, function not implemented", id);
+
CHTTPD_CONN_THREAD_DESTROY;
}
void* chttpd_conn_http (void* ptr_to_ChttpdConn) {
CHTTPD_CONN_THREAD_INIT;
+ const char hdr_delim[] = "\015\012\015\012";
+ _Static_assert (LBS_STR_STATIC_STRLEN (hdr_delim) == 4,
+ "HTTP header delimiter length must be 4 bytes!");
+
+ const char line_delim[] = "\015\012";
+ _Static_assert (LBS_STR_STATIC_STRLEN (line_delim) == 2,
+ "HTTP line delimiter length must be 2bytes!");
+
+ char errmsg[ERRLEN];
+ LbsArray* hdr_buf = lbs_array_new (sizeof (char));
+ LbsArray* out_buf = lbs_array_new (sizeof (char));
+ size_t data_offset;
+
+ while (true) {
+ ssize_t r = read (conn->connfd, conn->buf, CHTTPD_CONN_BUF_SIZE);
+ if (r < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ chttpd_log_write (hlog, "[%4llu] incomplete header: read: %s",
+ id, get_errmsg (errno, errmsg, ERRLEN));
+ goto http_exit;
+ } else if (r == 0) {
+ chttpd_log_write (hlog, "[%4llu] incomplete header: premature EOF",
+ id);
+ goto http_exit;
+ }
+
+ const char* hdr_end = internal_memstr (conn->buf, r, hdr_delim);
+ if (hdr_end != NULL) {
+ data_offset = hdr_end - conn->buf;
+ lbs_array_append_mass (hdr_buf, conn->buf, data_offset);
+ lbs_array_append_data (hdr_buf, &(char){ '\0' });
+ data_offset += 4;
+ break;
+ } else {
+ lbs_array_append_mass (hdr_buf, conn->buf, r);
+ }
+ }
+
+ char* method_start = hdr_buf->data;
+ char* hdr_start = internal_memstr (hdr_buf->data, hdr_buf->len, line_delim);
+ if (hdr_start != NULL) {
+ *hdr_start = '\0';
+ hdr_start += 2;
+ }
+
+http_exit:
+ lbs_array_unref (hdr_buf);
+ lbs_array_unref (out_buf);
CHTTPD_CONN_THREAD_DESTROY;
}
+#undef ERRLEN
void chttpd_conn_ctor (void* conn_generic, unsigned long long id, int connfd,
ChttpdLog* hlog, ChttpdServer* server, LbsListMeta* slist) {
diff --git a/hw4/chttpd/chttpd-conn.h b/hw4/chttpd/chttpd-conn.h
index d3e1f54..58701fc 100644
--- a/hw4/chttpd/chttpd-conn.h
+++ b/hw4/chttpd/chttpd-conn.h
@@ -6,6 +6,8 @@
#include <l4list.h>
#include <sys/types.h>
+#define CHTTPD_CONN_BUF_SIZE 4096
+
typedef struct {
/* read-only */
unsigned long long id;
@@ -18,6 +20,9 @@ typedef struct {
/* read-write and lock */
pthread_rwlock_t lock;
pid_t pid;
+
+ /* private */
+ char buf[CHTTPD_CONN_BUF_SIZE];
} ChttpdConn;
void* chttpd_conn_admin (void* ptr_to_ChttpdConn); /* TODO: implement this */
diff --git a/hw4/chttpd/chttpd-server.c b/hw4/chttpd/chttpd-server.c
index 0fe1576..7d540d0 100644
--- a/hw4/chttpd/chttpd-server.c
+++ b/hw4/chttpd/chttpd-server.c
@@ -3,6 +3,8 @@
# include "config.h"
#endif
+#define CHTTPD_SERVER_ENABLE_ERRMSG
+
#include "memwrap.h"
#include "chttpd-log.h"
#include "chttpd-server.h"
@@ -241,13 +243,6 @@ static void server_notify_setter (int signo) {
static void dummy_handler (int signo) { }
-static char* get_errmsg (int errnum, char* buf, size_t buflen) {
- if (strerror_r (errnum, buf, buflen) != 0) {
- snprintf (buf, buflen, "Unknown error %d", errnum);
- }
- return buf;
-}
-
#define ERRLEN 256
static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
chttpd_server_notify = 1;
@@ -278,6 +273,7 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
/* Check whether we are going to shutdown the server */
if (server_shutdown) {
+ server_shutdown = 0;
pthread_mutex_lock (&chttpd_server_notify_mutex);
chttpd_server_notify = 1;
pthread_mutex_unlock (&chttpd_server_notify_mutex);
@@ -321,16 +317,19 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
/* Rebuild poll fd list */
if (slist->len > 0) {
pfds = xrealloc (pfds, sizeof (struct pollfd) * slist->len);
- nfds = slist->len;
unsigned c = 0;
- for (iter = slist->first; iter != NULL; iter = iter->next, c++) {
+ for (iter = slist->first; iter != NULL; iter = iter->next) {
ChttpdServer* server = iter->data;
- pfds[c] = (struct pollfd) {
+ if (server->attr_close) {
+ continue;
+ }
+ pfds[c++] = (struct pollfd) {
.fd = server->sockfd,
.events = POLLIN | POLLPRI,
.revents = 0
};
}
+ nfds = c;
} else {
free (pfds);
pfds = NULL;
@@ -353,19 +352,24 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
ChttpdServer* server = iter->data;
struct sockaddr_storage addr;
char* type;
+ size_t conn_count;
pthread_rwlock_rdlock (&server->lock);
addr = server->addr;
type = server->attr_admin ? "ADMIN" : "HTTP";
+ conn_count = server->conn->len;
pthread_rwlock_unlock (&server->lock);
char ipstr[INET6_ADDRSTRLEN];
+ _Static_assert (INET6_ADDRSTRLEN >= INET_ADDRSTRLEN,
+ "Why IPv6 address is shorter than IPv4 address?");
switch (addr.ss_family) {
case AF_UNIX:
CHTTPD_SOCKET_SOCKADDR_UN_SET_NULL (SOCKADDR_UN (&addr));
chttpd_log_write (hlog,
- "Server %u: type %s, UNIX socket, path %s",
- c, type, SOCKADDR_UN (&addr)->sun_path);
+ "Server %u: type %s, UNIX socket, path %s"
+ " (%zu active connections)",
+ c, type, SOCKADDR_UN (&addr)->sun_path, conn_count);
break;
case AF_INET:
if (!inet_ntop (AF_INET, &(SOCKADDR_IN (&addr)->sin_addr),
@@ -373,8 +377,9 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
strcpy (ipstr, "unknown");
}
chttpd_log_write (hlog, "Server %u: type %s, "
- "IPv4 socket, address %s, port %" PRIu16, c, type,
- ipstr, ntohs (SOCKADDR_IN (&addr)->sin_port));
+ "IPv4 socket, address %s, port %" PRIu16
+ " (%zu active connections)", c, type, ipstr,
+ ntohs (SOCKADDR_IN (&addr)->sin_port), conn_count);
break;
case AF_INET6:
if (!inet_ntop (AF_INET6, &(SOCKADDR_IN6 (&addr)->sin6_addr),
@@ -382,8 +387,9 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
strcpy (ipstr, "unknown");
}
chttpd_log_write (hlog, "Server %u: type %s, "
- "IPv6 socket, address %s, port %" PRIu16, c, type,
- ipstr, ntohs (SOCKADDR_IN6 (&addr)->sin6_port));
+ "IPv6 socket, address %s, port %" PRIu16
+ " (%zu active connections)", c, type, ipstr,
+ ntohs (SOCKADDR_IN6 (&addr)->sin6_port), conn_count);
break;
default:
chttpd_log_write (hlog,
@@ -399,7 +405,12 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
pthread_sigmask (SIG_UNBLOCK, &crit_mask, NULL);
/* Poll for new request */
- if (pfds == NULL) {
+ if (pfds == NULL || nfds <= 0) {
+ if (slist->len > 0) {
+ nanosleep (&(struct timespec){ 0, 500000000 }, NULL);
+ chttpd_log_write_str (hlog,
+ "[main] waiting for remaining connections");
+ }
continue;
}
@@ -412,10 +423,16 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) {
LbsList* iter = slist->first;
for (int i = 0; i < nfds; i++, iter = iter->next) {
+ ChttpdServer* server = iter->data;
+ for (; server->attr_close; iter = iter->next, server = iter->data);
+
if (pfds[i].revents & POLLIN || pfds[i].revents & POLLPRI) {
pfds[i].revents = 0; /* Reset revents */
- ChttpdServer* server = iter->data;
+ if (server->attr_close) {
+ continue;
+ }
+
int connfd = accept (server->sockfd, NULL, NULL);
if (connfd < 0) {
chttpd_log_write (hlog, "[main] accept: %s",
@@ -476,6 +493,13 @@ void chttpd_server_ctor (void* server_generic, ChttpdLog* hlog, bool is_admin) {
server->hlog = chttpd_log_ref (hlog);
server->conn = lbs_list_meta_new (chttpd_conn_dtor);
pthread_rwlock_init (&server->lock, NULL);
+
+ sigemptyset (&server->conn_mask);
+ sigaddset (&server->conn_mask, SIGINT);
+ sigaddset (&server->conn_mask, SIGTERM);
+ sigaddset (&server->conn_mask, SIGHUP);
+ sigaddset (&server->conn_mask, SIGUSR1);
+ sigaddset (&server->conn_mask, SIGUSR2);
}
void chttpd_server_dtor (void* server_generic) {
diff --git a/hw4/chttpd/chttpd-server.h b/hw4/chttpd/chttpd-server.h
index e85716b..e532adf 100644
--- a/hw4/chttpd/chttpd-server.h
+++ b/hw4/chttpd/chttpd-server.h
@@ -3,9 +3,26 @@
#include <l4list.h>
#include <pthread.h>
+#include <signal.h>
#include <stdbool.h>
#include <sys/socket.h>
+#if __STDC_VERSION__ < 201112L
+# undef _Static_assert
+# define _Static_assert(a,b)
+#endif
+
+#ifdef CHTTPD_SERVER_ENABLE_ERRMSG
+# include <stdio.h>
+# include <string.h>
+static inline char* get_errmsg (int errnum, char* buf, size_t buflen) {
+ if (strerror_r (errnum, buf, buflen) != 0) {
+ snprintf (buf, buflen, "Unknown error %d", errnum);
+ }
+ return buf;
+}
+#endif
+
typedef struct {
/* read-only */
int sockfd;
@@ -13,6 +30,7 @@ typedef struct {
socklen_t addrlen;
int attr_admin : 1;
ChttpdLog* hlog;
+ sigset_t conn_mask;
/* read-write and lock */
pthread_rwlock_t lock;
diff --git a/hw4/chttpd/chttpd-socket.h b/hw4/chttpd/chttpd-socket.h
index 7bafcad..edb1d7e 100644
--- a/hw4/chttpd/chttpd-socket.h
+++ b/hw4/chttpd/chttpd-socket.h
@@ -4,6 +4,7 @@
#include <l4common.h>
#include <netinet/in.h>
+#include <sys/socket.h>
#include <sys/un.h>
#define CHTTPD_SOCKET_NEW_ERROR_GETADDRINFO -1
diff --git a/hw4/l4basic/l4array.c b/hw4/l4basic/l4array.c
new file mode 100644
index 0000000..b59991d
--- /dev/null
+++ b/hw4/l4basic/l4array.c
@@ -0,0 +1,258 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "l4array.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+LbsArray* lbs_array_new_with_max (size_t size, size_t max) {
+ if (size <= 0) {
+ return NULL;
+ }
+
+ LbsArray* array = xmalloc (sizeof (LbsArray));
+ if (array == NULL) {
+ return NULL;
+ }
+
+ if (max > 0) {
+ void* data = xmalloc (size * max);
+ if (data == NULL) {
+ free (array);
+ return NULL;
+ }
+ array->data = data;
+ } else {
+ array->data = NULL;
+ }
+
+ array->free_func = NULL;
+ array->size = size;
+ array->len = max;
+ array->max = max;
+ array->ref_count = 1;
+ array->is_alloc = true;
+
+ return array;
+}
+
+int lbs_array_init_with_max (LbsArray* array, size_t size, size_t max) {
+ if (size <= 0) {
+ return -1;
+ }
+
+ if (max > 0) {
+ void* data = xmalloc (size * max);
+ if (data == NULL) {
+ return -1;
+ }
+ array->data = data;
+ } else {
+ array->data = NULL;
+ }
+
+ array->free_func = NULL;
+ array->size = size;
+ array->len = max;
+ array->max = max;
+ array->ref_count = 1;
+ array->is_alloc = false;
+
+ return 0;
+}
+
+LbsArray* lbs_array_copy (LbsArray* dest, const LbsArray* src) {
+ if (dest == NULL) {
+ dest = lbs_array_new_with_max (src->size, src->max);
+ if (dest == NULL) {
+ return NULL;
+ }
+ } else {
+ if (lbs_array_init_with_max (dest, src->size, src->max) < 0) {
+ return NULL;
+ }
+ }
+
+ dest->len = src->len;
+ dest->free_func = src->free_func;
+ if (src->data != NULL && dest->data != NULL) {
+ memcpy (dest->data, src->data, src->size * src->len);
+ }
+ return dest;
+}
+
+LbsArray* lbs_array_cat (LbsArray* dest, const LbsArray* more) {
+ if (dest == NULL) {
+ return lbs_array_copy (dest, more);
+ }
+
+ if (dest->size != more->size) {
+ return NULL;
+ }
+
+ int oldlen = dest->len;
+ if (lbs_array_set_len (dest, dest->len + more->len) < 0) {
+ return NULL;
+ }
+
+ memcpy (lbs_array_vp (dest, oldlen), more->data, more->size * more->len);
+ return dest;
+}
+
+void* lbs_array_ref_generic (void* array_generic) {
+ LbsArray* array = LBS_ARRAY (array_generic);
+ array->ref_count++;
+ return array;
+}
+
+void lbs_array_unref_generic (void* array_generic) {
+ if (array_generic == NULL) {
+ return;
+ }
+ LbsArray* array = LBS_ARRAY (array_generic);
+ array->ref_count--;
+ if (array->ref_count <= 0) {
+ lbs_array_free (array);
+ }
+}
+
+void lbs_array_free_generic (void* array_generic) {
+ if (array_generic == NULL) {
+ return;
+ }
+ LbsArray* array = LBS_ARRAY (array_generic);
+ if (array->free_func != NULL) {
+ size_t i = 0;
+ char* d = array->data;
+ for (; i < array->len; i++, d += array->size) {
+ (*(array->free_func)) (*((void**)d));
+ }
+ }
+ free (array->data);
+ if (array->is_alloc) {
+ free (array);
+ }
+}
+
+void* lbs_array_drop_struct (LbsArray* array) {
+ if (!array->is_alloc) {
+ return array->data;
+ }
+
+ void* data = array->data;
+ free (array);
+ return data;
+}
+
+LbsArray* lbs_array_make_struct (LbsArray* array,
+ size_t size, size_t len, size_t max, void* data) {
+
+ if (array == NULL) {
+ array = lbs_array_new (size);
+ if (array == NULL) {
+ return NULL;
+ }
+ } else {
+ if (lbs_array_init (array, size) < 0) {
+ return NULL;
+ }
+ }
+
+ array->len = len;
+ array->max = max;
+ array->data = data;
+ return array;
+}
+
+int lbs_array_set_len (LbsArray* array, size_t len) {
+ if (len > (array->max)){
+ if (lbs_array_set_max (array, len) < 0) {
+ return -1;
+ } else {
+ array->len = len;
+ }
+ } else {
+ array->len = len;
+ return 0;
+ }
+ return 0;
+}
+
+int lbs_array_set_max (LbsArray* array, size_t max) {
+ void* ptr = xrealloc (array->data, array->size * max);
+ if (ptr == NULL) {
+ return -1;
+ }
+
+ array->max = max;
+ array->data = ptr;
+ return 0;
+}
+
+int lbs_array_append_ptr (LbsArray* array, const void* ptr) {
+ return lbs_array_append_data (array, &ptr);
+}
+
+int lbs_array_append_data (LbsArray* array, const void* data) {
+ if (array->max < array->len + 1) {
+ if (array->max > 0) {
+ if (lbs_array_set_max (array, array->max * 2) < 0) {
+ return -1;
+ }
+ } else {
+ if (lbs_array_set_max (array, 1) < 0){
+ return -1;
+ }
+ }
+ }
+
+ memcpy (lbs_array_vp (array, array->len), data, array->size);
+ array->len++;
+ return 0;
+}
+
+int lbs_array_append_mass (LbsArray* array, const void* data, size_t count) {
+ bool need_expand = false;
+
+ while (array->max < array->len + count) {
+ need_expand = true;
+ if (array->max > 0) {
+ array->max *= 2;
+ } else {
+ array->max = 1;
+ }
+ }
+
+ if (need_expand) {
+ if (lbs_array_set_max (array, array->max) < 0) {
+ return -1;
+ }
+ }
+
+ memcpy (lbs_array_vp (array, array->len), data, array->size * count);
+ array->len += count;
+ return 0;
+}
+
+int lbs_array_remove (LbsArray* array) {
+ if (array->len <= 0) {
+ return -1;
+ }
+
+ array->len--;
+ if (array->len < array->max * 2) {
+ lbs_array_minimize (array);
+ }
+ return 0;
+}
+
+int lbs_array_minimize (LbsArray* array) {
+ if (array->max > array->len) {
+ void* ptr = xrealloc (array->data, array->size * array->len);
+ if (ptr == NULL) {
+ return -1;
+ }
+ array->max = array->len;
+ array->data = ptr;
+ }
+ return 0;
+}
diff --git a/hw4/l4basic/l4array.h b/hw4/l4basic/l4array.h
new file mode 100644
index 0000000..1b45995
--- /dev/null
+++ b/hw4/l4basic/l4array.h
@@ -0,0 +1,88 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#ifndef LBS_ARRAY_H
+#define LBS_ARRAY_H
+
+#include <l4common.h>
+
+typedef struct LbsArrayStruct {
+ /*< public >*/
+ void* data; /* data */
+ void (*free_func) (void* data);
+ /* function to free the element */
+
+ /*< private >*/
+ size_t size; /* element size */
+ size_t len; /* current length */
+ size_t max; /* maximal length */
+ unsigned ref_count; /* reference count */
+ bool is_alloc; /* is allocated using xmalloc */
+} LbsArray;
+
+#define LBS_ARRAY(x) ((LbsArray*)(x))
+
+#define lbs_array_new(size) \
+ (lbs_array_new_with_max (size, 0))
+LbsArray* lbs_array_new_with_max (size_t size, size_t max);
+
+#define lbs_array_init(array, size) \
+ (lbs_array_init_with_max (array, size, 0))
+int lbs_array_init_with_max (LbsArray* array, size_t size, size_t max);
+
+LbsArray* lbs_array_copy (LbsArray* dest, const LbsArray* src);
+LbsArray* lbs_array_cat (LbsArray* dest, const LbsArray* more);
+
+#define lbs_array_ref(array) \
+ (lbs_array_ref_generic (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)))
+#define lbs_array_unref(array) \
+ (lbs_array_unref_generic (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)))
+void* lbs_array_ref_generic (void* array);
+void lbs_array_unref_generic (void* array);
+
+#define lbs_array_free(array) \
+ (lbs_array_free_generic (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)))
+void lbs_array_free_generic (void* array);
+void* lbs_array_drop_struct (LbsArray* array);
+LbsArray* lbs_array_make_struct (LbsArray* array, size_t size, size_t len,
+ size_t max, void* data);
+
+#define lbs_array_get_data(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->data)
+#define lbs_array_get_size(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->size)
+#define lbs_array_get_len(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->len)
+#define lbs_array_get_max(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->max)
+#define lbs_array_get_ref_count(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->ref_count)
+#define lbs_array_get_is_alloc(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->is_alloc)
+#define lbs_array_get_free_func(array) \
+ (LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->free_func)
+
+int lbs_array_set_len (LbsArray* array, size_t len);
+int lbs_array_set_max (LbsArray* array, size_t max);
+#define lbs_array_set_free_func(array,value) \
+ ((LBS_COMMON_CHECK_TYPE ((array), LbsArray*)->free_func) = (value))
+
+#define lbs_array_append_var(array,var) \
+ (lbs_array_append_data ((array), (&(var))))
+int lbs_array_append_ptr (LbsArray* array, const void* ptr);
+int lbs_array_append_data (LbsArray* array, const void* data);
+int lbs_array_append_mass (LbsArray* array, const void* data,
+ size_t count);
+int lbs_array_remove (LbsArray* array);
+int lbs_array_minimize (LbsArray* array);
+#define lbs_array_push_back lbs_array_append_data
+#define lbs_array_push lbs_array_append_data
+#define lbs_array_pop_back lbs_array_remove
+#define lbs_array_pop lbs_array_remove
+
+#define lbs_array_vp(array, index) \
+ ((void*)(((char*)((array)->data))+(((array)->size)*(index))))
+#define lbs_array_v(array, type, index) \
+ (*(((type*)((array)->data))+(index)))
+#define lbs_array_index lbs_array_v
+#define lbs_array_index_ptr lbs_array_vp
+
+#endif /* LBS_ARRAY_H */