New PHP5 APC - version 3.0.18, using PHP5 5.2.0-8+etch10,
[php5-apc.git] / apc_pool.c
diff --git a/apc_pool.c b/apc_pool.c
new file mode 100644 (file)
index 0000000..1d12258
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+  +----------------------------------------------------------------------+
+  | APC                                                                  |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2008 The PHP Group                                     |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gopal Vijayaraghavan <gopalv@yahoo-inc.com>                 |
+  +----------------------------------------------------------------------+
+
+   This software was contributed to PHP by Yahoo! Inc. in 2008.
+   
+   Future revisions and derivatives of this source code must acknowledge
+   Yahoo! Inc. as the original contributor of this module by
+   leaving this note intact in the source code.
+
+   All other licensing and usage conditions are those of the PHP Group.
+
+ */
+
+/* $Id: apc_pool.c,v 3.3 2008/01/09 12:30:39 gopalv Exp $ */
+
+
+#include "apc_pool.h"
+#include <assert.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+/* {{{ typedefs */
+typedef struct _pool_block
+{
+    size_t              avail;
+    size_t              capacity;
+    unsigned char       *mark;
+    struct _pool_block  *next;
+    unsigned             :0; /* this should align to word */
+    unsigned char       data[0]; 
+}pool_block;
+
+/*
+   parts in ? are optional and turned on for fun, memory loss,
+   and for something else that I forgot about ... ah, debugging
+
+                 |--------> data[0]         |<-- non word boundary (too)
+   +-------------+--------------+-----------+-------------+-------------->>>
+   | pool_block  | ?sizeinfo<1> | block<1>  | ?redzone<1> | ?sizeinfo<2> 
+   |             |  (size_t)    |           | padded left |    
+   +-------------+--------------+-----------+-------------+-------------->>>
+ */
+
+struct _apc_pool
+{
+    apc_malloc_t allocate;
+    apc_free_t   deallocate;
+
+    size_t     dsize;
+    void       *owner;
+
+    struct
+    {
+        unsigned int redzones:1;
+        unsigned int sizeinfo:1;
+    } options;
+
+    pool_block *head;
+};
+/* }}} */
+
+/* {{{ redzone code */
+static const unsigned char decaff[] =  {
+    0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad,
+    0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad,
+    0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad,
+    0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad
+};
+
+/* a redzone is at least 4 (0xde,0xca,0xc0,0xff) bytes */
+#define REDZONE_SIZE(size) \
+    ((ALIGNWORD((size)) > ((size) + 4)) ? \
+        (ALIGNWORD((size)) - (size)) : /* does not change realsize */\
+        ALIGNWORD((size)) - (size) + ALIGNWORD((sizeof(char)))) /* adds 1 word to realsize */
+    
+#define SIZEINFO_SIZE ALIGNWORD(sizeof(size_t))
+
+#define MARK_REDZONE(block, redsize) do {\
+       memcpy(block, decaff, redsize );\
+    } while(0)
+
+#define CHECK_REDZONE(block, redsize) (memcmp(block, decaff, redsize) == 0)
+
+/* }}} */
+
+#define APC_POOL_OPTION(pool, option) ((pool)->options.option)
+
+/* {{{ create_pool_block */
+static pool_block* create_pool_block(apc_pool *pool, size_t size)
+{
+    size_t realsize = sizeof(pool_block) + ALIGNWORD(size);
+    
+    pool_block* entry = pool->allocate(realsize);
+
+    entry->avail = entry->capacity = size;
+
+    entry->mark = entry->data;
+
+    entry->next = pool->head;
+
+    pool->head = entry;
+
+    return entry;
+}
+/* }}} */
+
+/* {{{ apc_pool_create */
+apc_pool* apc_pool_create(apc_pool_type pool_type, 
+                            apc_malloc_t allocate, 
+                            apc_free_t deallocate)
+{
+    apc_pool* pool = NULL;
+    size_t dsize = 0;
+
+    /* sanity checks */
+    assert(sizeof(decaff) > REDZONE_SIZE(ALIGNWORD(sizeof(char))));
+    assert(sizeof(pool_block) == ALIGNWORD(sizeof(pool_block)));
+
+    assert(APC_POOL_SIZE_MASK & (APC_POOL_SIZEINFO | APC_POOL_REDZONES) == 0);
+
+    switch(pool_type & APC_POOL_SIZE_MASK) {
+        case APC_SMALL_POOL:
+            dsize = 512;
+            break;
+
+        case APC_LARGE_POOL:
+            dsize = 8192;
+            break;
+
+        case APC_MEDIUM_POOL:
+            dsize = 4096;
+            break;
+
+        default:
+            return NULL;
+    }
+
+    pool = (apc_pool*)allocate(sizeof(apc_pool));
+
+    if(!pool) {
+        return NULL;
+    }
+
+    pool->allocate = allocate;
+    pool->deallocate = deallocate;
+    pool->dsize = dsize;
+    pool->head = NULL;
+
+    APC_POOL_OPTION(pool, redzones) = (pool_type & APC_POOL_REDZONES) != 0;
+    APC_POOL_OPTION(pool, sizeinfo) = (pool_type & APC_POOL_SIZEINFO) != 0;
+
+    if(!create_pool_block(pool, dsize)) {
+        deallocate(pool);
+        return NULL;
+    }
+
+    return pool; 
+}
+/* }}} */
+
+/* {{{ apc_pool_destroy */
+void apc_pool_destroy(apc_pool *pool)
+{
+
+    apc_free_t deallocate = pool->deallocate;
+    pool_block *entry;
+    pool_block *tmp;
+
+    entry = pool->head;
+
+    while(entry != NULL) {
+        tmp = entry->next;
+        deallocate(entry);
+        entry = tmp;
+    }
+
+    deallocate(pool);
+}
+/* }}} */
+
+/* {{{ apc_pool_alloc */
+void* apc_pool_alloc(apc_pool *pool, size_t size)
+{
+    unsigned char *p = NULL;
+    size_t realsize = ALIGNWORD(size);
+    size_t poolsize;
+    unsigned char *redzone  = NULL;
+    size_t redsize  = 0;
+    size_t *sizeinfo= NULL;
+
+    pool_block *entry;
+
+
+    if(APC_POOL_OPTION(pool, redzones)) {
+        redsize = REDZONE_SIZE(size); /* redsize might be re-using word size padding */
+        realsize = size + redsize;    /* recalculating realsize */
+    } else {
+        redsize = realsize - size; /* use padding space */
+    }
+
+    if(APC_POOL_OPTION(pool, sizeinfo)) {
+        realsize += ALIGNWORD(sizeof(size_t));
+    }
+
+
+    for(entry = pool->head; entry != NULL; entry = entry->next) {
+        if(entry->avail >= realsize) {
+            goto found;
+        }
+    }
+
+    poolsize = ALIGNSIZE(realsize, pool->dsize);
+
+    entry = create_pool_block(pool, poolsize);
+
+    if(!entry) {
+        return NULL;
+    }
+
+found:
+    p = entry->mark;
+
+    if(APC_POOL_OPTION(pool, sizeinfo)) {
+        sizeinfo = (size_t*)p;
+        p += SIZEINFO_SIZE;
+        *sizeinfo = size;
+    }
+    
+    redzone = p + size;
+
+    if(APC_POOL_OPTION(pool, redzones)) {
+        MARK_REDZONE(redzone, redsize);
+    }
+
+#ifdef VALGRIND_MAKE_MEM_NOACCESS
+    if(redsize != 0) {
+        VALGRIND_MAKE_MEM_NOACCESS(redzone, redsize);
+    }
+#endif
+
+    entry->avail -= realsize;
+    entry->mark  += realsize;
+
+#ifdef VALGRIND_MAKE_MEM_UNDEFINED
+    /* need to write before reading data off this */
+    VALGRIND_MAKE_MEM_UNDEFINED(p, size);
+#endif
+
+    return (void*)p;
+}
+/* }}} */
+
+/* {{{ apc_pool_free */
+/*
+ * free does not do anything other than
+ * check for redzone values when free'ing
+ * data areas.
+ */
+void apc_pool_free(apc_pool *pool, void *p)
+{
+    if(!APC_POOL_OPTION(pool, sizeinfo) || 
+        !APC_POOL_OPTION(pool, redzones)) {
+    }
+}
+/* }}} */
+
+/* {{{ apc_pool_check_integrity */
+/*
+ * Checking integrity at runtime, does an
+ * overwrite check only when the sizeinfo
+ * is set.
+ */
+int apc_pool_check_integrity(apc_pool *pool) 
+{
+    pool_block *entry;
+    size_t *sizeinfo = NULL;
+    unsigned char *start;
+    size_t realsize;
+    unsigned char   *redzone;
+    size_t redsize;
+
+    for(entry = pool->head; entry != NULL; entry = entry->next) {
+        start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block));
+        if((entry->mark - start) != (entry->capacity - entry->avail)) {
+            return 0;
+        }
+    }
+
+    if(!APC_POOL_OPTION(pool, sizeinfo) || 
+        !APC_POOL_OPTION(pool, redzones)) {
+        return 1;
+    }
+
+    for(entry = pool->head; entry != NULL; entry = entry->next) {
+        start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block));
+        
+        while(start < entry->mark) {
+            sizeinfo = (size_t*)start;
+            /* redzone starts where real data ends, in a non-word boundary
+             * redsize is at least 4 bytes + whatever's needed to make it
+             * to another word boundary.
+             */
+            redzone = start + SIZEINFO_SIZE + (*sizeinfo);
+            redsize = REDZONE_SIZE(*sizeinfo);
+            if(!CHECK_REDZONE(redzone, redsize))
+            {
+                /*
+                fprintf(stderr, "Redzone check failed for %p\n", 
+                                start + ALIGNWORD(sizeof(size_t)));*/
+                return 0;
+            }
+            realsize = SIZEINFO_SIZE + *sizeinfo + redsize;
+            start += realsize;
+        }
+    }
+
+    return 1;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: expandtab sw=4 ts=4 sts=4
+ */