diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7881dd6 --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +cd deps/mpool-2.1.0/ +make +cd ../.. +node-waf clean +node-waf configure build diff --git a/deps/mpool-2.1.0/ChangeLog.1 b/deps/mpool-2.1.0/ChangeLog.1 new file mode 100644 index 0000000..40c4a53 --- /dev/null +++ b/deps/mpool-2.1.0/ChangeLog.1 @@ -0,0 +1,86 @@ +2006-05-31 Gray Watson <> + + * Version 2.1.0 released. + + * Added MPOOL_ERROR_PNT_OVER to distinguish between pointer + overwrite and mpool structure overwrite. + +2005-05-20 Gray Watson <> + + * Version 2.0.0 released. + + * First external publication of library. + +Thu Mar 4 10:14:21 1999 Gray Watson <> + + * Reworked the way the blocks were split up. + + * Removed the round arguments. Not used. + +Wed Mar 3 19:29:38 1999 Gray Watson <> + + * Moved to random(). Fucking rand() was hiding a lot of problems + from me. + + * Added some additional sanity checks in free(). + + * Added mpool_set_max_pages to the library. + +Thu Feb 25 12:41:51 1999 Gray Watson <> + + * Added log_function transaction callback. + +Thu Feb 25 09:53:33 1999 Gray Watson <> + + * Changed the default page size to 16 * getpagesize. + +Wed Feb 24 17:52:52 1999 Gray Watson <> + + * Major reworking of internals to simplify structures. + +Fri Feb 19 12:52:55 1999 Gray Watson <> + + * Made a number of changes to the internals which removed the + addr_to_block as a performance pig. + +Tue Feb 16 21:11:23 1999 Gray Watson <> + + * Added ability for free to look up in the free bit lists for + memory to use. + + * Added mpool_clear. Good idea. + +Thu Feb 11 02:53:45 1999 Gray Watson <> + + * Finally a working version. Looks much better. + + * Added rounding sizes so it will allocate aligned memory. + + * Added minimum size to the mpool_free function to speed it up. + +Wed Feb 10 23:30:48 1999 Gray Watson <> + + * Version 1 with new fine grained memory resolution almost + working. + +Fri May 2 02:26:28 1997 Gray Watson <> + + * Moved to MAP_PRIVATE from MAP_SHARED. + + * Fixed the min/max handling. + + * Added additional info to mpool_stat. + +Thu May 1 16:51:06 1997 Gray Watson <> + + * Added page-size information request. + + * Added better no-memory errors. + +Thu Apr 24 01:58:41 1997 Gray Watson <> + + * Added handling of null for debugging purposes. + +Mon Apr 14 03:31:26 1997 Gray Watson <> + + * Started the mpool routines. diff --git a/deps/mpool-2.1.0/Makefile b/deps/mpool-2.1.0/Makefile new file mode 100644 index 0000000..86c299b --- /dev/null +++ b/deps/mpool-2.1.0/Makefile @@ -0,0 +1,56 @@ +# +# $Id: Makefile.all,v 1.1.1.1 2005/05/20 19:58:29 gray Exp $ +# + +HFLS = mpool.h +OBJS = mpool.o + +CC = gcc + +CFLAGS = -g -I. $(DEFINES) -fPIC +#CFLAGS = -g -I. +LDFLAGS = +RANLIB = ranlib + +DESTDIR = /usr/local +TEST = mpool_t +LIBRARY = libmpool.a + +all : $(LIBRARY) $(UTIL) + +clean : + rm -f a.out core *.o *.t + rm -f $(LIBRARY) $(TEST) + +install : $(HFLS) $(LIBRARY) + install -c -m 444 $(HFLS) $(DESTDIR)/include + install -c -m 444 $(LIBRARY) $(DESTDIR)/lib + $(RANLIB) $(DESTDIR)/libo/$(LIBRARY) + +$(LIBRARY) : $(OBJS) + ar cr $(LIBRARY) $? + $(RANLIB) $@ + +tests : $(TEST) + +$(TEST) : $(TEST).o $(LIBRARY) + rm -f $@ + $(CC) $(LDFLAGS) $(TEST).o $(LIBRARY) + mv a.out $@ + +$(UTIL) : $(UTIL).o $(LIBRARY) + rm -f $@ + $(CC) $(LDFLAGS) $(UTIL).o $(LIBRARY) + mv a.out $@ + +.c.o : + rm -f $@ + $(CC) $(CFLAGS) -c $< -o $@ + +# +# Below are dependencies that are automatically generated by make +# depend. Please do not edit by hand. +# + +mpool.o: mpool.c mpool.h mpool_loc.h +mpool_t.o: mpool_t.c mpool.h diff --git a/deps/mpool-2.1.0/NEWS b/deps/mpool-2.1.0/NEWS new file mode 100644 index 0000000..f6c0ab2 --- /dev/null +++ b/deps/mpool-2.1.0/NEWS @@ -0,0 +1,11 @@ +------------------------------------------------------------------------------- +$Id: NEWS,v 1.2 2006/05/31 20:28:31 gray Exp $ +------------------------------------------------------------------------------- + +Version 2.1.0: + + * Added MPOOL_ERROR_PNT_OVER to show pointer overwrites. + +Version 2.0.0: + + * Initial external release of library after use since 1996. diff --git a/deps/mpool-2.1.0/README b/deps/mpool-2.1.0/README new file mode 100644 index 0000000..7277ff2 --- /dev/null +++ b/deps/mpool-2.1.0/README @@ -0,0 +1,46 @@ +------------------------------------------------------------------------------- +$Id: README,v 1.2 2005/05/22 19:49:30 gray Exp $ +------------------------------------------------------------------------------- + +BACKGROUND: + +This is a memory pool library which was written to allow a program to +have heaps that it could destroy without fragmenting memory. You can +have multiple heaps and reset them easily completely reclaiming the +memory (as opposed to standard heaps). + +With it you can mpool_open() a new heap, then mpool_alloc(), +mpool_calloc(), mpool_realloc(), mpool_free() to your heart's content. +Once you are done with the memory-pool you can run mpool_clear() or +mpool_close() and completely remove the memory associated with the +pools. This is very handy if you are working with some large blocks +of memory and want to reset back to a clean state. + +Check out the mpool.h file for more information. Sorry for minimal +docs. + +------------------------------------------------------------------------------- + +INSTALLATION: + +1) Typing 'make' should be enough to build libskip.a. +2) Typing 'make tests' should make the mpool_t test program. + +------------------------------------------------------------------------------- + +REPOSITORY: + +The newest versions of the library are available from: + + http://256.com/sources/mpool/ + +------------------------------------------------------------------------------- + +AUTHOR: + +If you have any questions or problems feel free to send me mail. + +Gray Watson +http://256.com/gray/ + +------------------------------------------------------------------------------- diff --git a/deps/mpool-2.1.0/mpool.c b/deps/mpool-2.1.0/mpool.c new file mode 100644 index 0000000..e183535 --- /dev/null +++ b/deps/mpool-2.1.0/mpool.c @@ -0,0 +1,1781 @@ +/* + * Memory pool routines. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $ + */ + +/* + * Memory-pool allocation routines. I got sick of the GNU mmalloc + * library which was close to what we needed but did not exactly do + * what I wanted. + * + * The following uses mmap from /dev/zero. It allows a number of + * allocations to be made inside of a memory pool then with a clear or + * close the pool can be reset without any memory fragmentation and + * growth problems. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define MPOOL_MAIN + +#include "mpool.h" +#include "mpool_loc.h" + +#ifdef __GNUC__ +#ident "$Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $" +#else +static char *rcs_id = "$Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $"; +#endif + +/* version */ +static char *version = "mpool library version 2.1.0"; + +/* local variables */ +static int enabled_b = 0; /* lib initialized? */ +static unsigned int min_bit_free_next = 0; /* min size of next pnt */ +static unsigned int min_bit_free_size = 0; /* min size of next + size */ +static unsigned long bit_array[MAX_BITS + 1]; /* size -> bit */ + +/****************************** local utilities ******************************/ + +/* + * static void startup + * + * DESCRIPTION: + * + * Perform any library level initialization. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * None. + */ +static void startup(void) +{ + int bit_c; + unsigned long size = 1; + + if (enabled_b) { + return; + } + + /* allocate our free bit array list */ + for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) { + bit_array[bit_c] = size; + + /* + * Note our minimum number of bits that can store a pointer. This + * is smallest address that we can have a linked list for. + */ + if (min_bit_free_next == 0 && size >= sizeof(void *)) { + min_bit_free_next = bit_c; + } + /* + * Note our minimum number of bits that can store a pointer and + * the size of the block. + */ + if (min_bit_free_size == 0 && size >= sizeof(mpool_free_t)) { + min_bit_free_size = bit_c; + } + + size *= 2; + } + + enabled_b = 1; +} + +/* + * static int size_to_bits + * + * DESCRIPTION: + * + * Calculate the number of bits in a size. + * + * RETURNS: + * + * Number of bits. + * + * ARGUMENTS: + * + * size -> Size of memory of which to calculate the number of bits. + */ +static int size_to_bits(const unsigned long size) +{ + int bit_c = 0; + + for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) { + if (size <= bit_array[bit_c]) { + break; + } + } + + return bit_c; +} + +/* + * static int size_to_free_bits + * + * DESCRIPTION: + * + * Calculate the number of bits in a size going on the free list. + * + * RETURNS: + * + * Number of bits. + * + * ARGUMENTS: + * + * size -> Size of memory of which to calculate the number of bits. + */ +static int size_to_free_bits(const unsigned long size) +{ + int bit_c = 0; + + if (size == 0) { + return 0; + } + + for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) { + if (size < bit_array[bit_c]) { + break; + } + } + + return bit_c - 1; +} + +/* + * static int bits_to_size + * + * DESCRIPTION: + * + * Calculate the size represented by a number of bits. + * + * RETURNS: + * + * Number of bits. + * + * ARGUMENTS: + * + * bit_n -> Number of bits + */ +static unsigned long bits_to_size(const int bit_n) +{ + if (bit_n > MAX_BITS) { + return bit_array[MAX_BITS]; + } + else { + return bit_array[bit_n]; + } +} + +/* + * static void *alloc_pages + * + * DESCRIPTION: + * + * Allocate space for a number of memory pages in the memory pool. + * + * RETURNS: + * + * Success - New pages of memory + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + * + * page_n -> Number of pages to alloc. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +static void *alloc_pages(mpool_t *mp_p, const unsigned int page_n, + int *error_p) +{ + void *mem, *fill_mem; + unsigned long size, fill; + int state; + + /* are we over our max-pages? */ + if (mp_p->mp_max_pages > 0 && mp_p->mp_page_c >= mp_p->mp_max_pages) { + SET_POINTER(error_p, MPOOL_ERROR_NO_PAGES); + return NULL; + } + + size = SIZE_OF_PAGES(mp_p, page_n); + +#ifdef DEBUG + (void)printf("allocating %u pages or %lu bytes\n", page_n, size); +#endif + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)) { + mem = sbrk(size); + if (mem == (void *)-1) { + SET_POINTER(error_p, MPOOL_ERROR_NO_MEM); + return NULL; + } + fill = (unsigned long)mem % mp_p->mp_page_size; + + if (fill > 0) { + fill = mp_p->mp_page_size - fill; + fill_mem = sbrk(fill); + if (fill_mem == (void *)-1) { + SET_POINTER(error_p, MPOOL_ERROR_NO_MEM); + return NULL; + } + if ((char *)fill_mem != (char *)mem + size) { + SET_POINTER(error_p, MPOOL_ERROR_SBRK_CONTIG); + return NULL; + } + mem = (char *)mem + fill; + } + } + else { + state = MAP_PRIVATE; +#ifdef MAP_FILE + state |= MAP_FILE; +#endif +#ifdef MAP_VARIABLE + state |= MAP_VARIABLE; +#endif + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_MAP_ANON)) { +#ifdef MAP_ANON + state |= MAP_ANON; +#elif defined MAP_ANONYMOUS + state |= MAP_ANONYMOUS; +#endif + } + + /* mmap from /dev/zero */ + mem = mmap((caddr_t)mp_p->mp_addr, size, PROT_READ | PROT_WRITE, state, + mp_p->mp_fd, mp_p->mp_top); + if (mem == (void *)MAP_FAILED) { + if (errno == ENOMEM) { + SET_POINTER(error_p, MPOOL_ERROR_NO_MEM); + } + else { + SET_POINTER(error_p, MPOOL_ERROR_MMAP); + } + return NULL; + } + mp_p->mp_top += size; + if (mp_p->mp_addr != NULL) { + mp_p->mp_addr = (char *)mp_p->mp_addr + size; + } + } + + mp_p->mp_page_c += page_n; + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return mem; +} + +/* + * static int free_pages + * + * DESCRIPTION: + * + * Free previously allocated pages of memory. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * pages <-> Pointer to memory pages that we are freeing. + * + * size -> Size of the block that we are freeing. + * + * sbrk_b -> Set to one if the pages were allocated with sbrk else mmap. + */ +static int free_pages(void *pages, const unsigned long size, + const int sbrk_b) +{ + if (! sbrk_b) { + (void)munmap((caddr_t)pages, size); + } + + return MPOOL_ERROR_NONE; +} + +/* + * static int check_magic + * + * DESCRIPTION: + * + * Check for the existance of the magic ID in a memory pointer. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * addr -> Address inside of the block that we are tryign to locate. + * + * size -> Size of the block. + */ +static int check_magic(const void *addr, const unsigned long size) +{ + const unsigned char *mem_p; + + /* set our starting point */ + mem_p = (unsigned char *)addr + size; + + if (*mem_p == FENCE_MAGIC0 && *(mem_p + 1) == FENCE_MAGIC1) { + return MPOOL_ERROR_NONE; + } + else { + return MPOOL_ERROR_PNT_OVER; + } +} + +/* + * static void write_magic + * + * DESCRIPTION: + * + * Write the magic ID to the address. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * addr -> Address where to write the magic. + */ +static void write_magic(const void *addr) +{ + *(unsigned char *)addr = FENCE_MAGIC0; + *((unsigned char *)addr + 1) = FENCE_MAGIC1; +} + +/* + * static void free_pointer + * + * DESCRIPTION: + * + * Moved a pointer into our free lists. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * addr <-> Address where to write the magic. We may write a next + * pointer to it. + * + * size -> Size of the address space. + */ +static int free_pointer(mpool_t *mp_p, void *addr, + const unsigned long size) +{ + unsigned int bit_n; + unsigned long real_size; + mpool_free_t free_pnt; + +#ifdef DEBUG + (void)printf("freeing a block at %lx of %lu bytes\n", (long)addr, size); +#endif + + if (size == 0) { + return MPOOL_ERROR_NONE; + } + + /* + * if the user size is larger then can fit in an entire block then + * we change the size + */ + if (size > MAX_BLOCK_USER_MEMORY(mp_p)) { + real_size = SIZE_OF_PAGES(mp_p, PAGES_IN_SIZE(mp_p, size)) - + sizeof(mpool_block_t); + } + else { + real_size = size; + } + + /* + * We use a specific free bits calculation here because if we are + * freeing 10 bytes then we will be putting it into the 8-byte free + * list and not the 16 byte list. size_to_bits(10) will return 4 + * instead of 3. + */ + bit_n = size_to_free_bits(real_size); + + /* + * Minimal error checking. We could go all the way through the + * list however this might be prohibitive. + */ + if (mp_p->mp_free[bit_n] == addr) { + return MPOOL_ERROR_IS_FREE; + } + + /* add the freed pointer to the free list */ + if (bit_n < min_bit_free_next) { + /* + * Yes we know this will lose 99% of the allocations but what else + * can we do? No space for a next pointer. + */ + if (mp_p->mp_free[bit_n] == NULL) { + mp_p->mp_free[bit_n] = addr; + } + } + else if (bit_n < min_bit_free_size) { + /* we copy, not assign, to maintain the free list */ + memcpy(addr, mp_p->mp_free + bit_n, sizeof(void *)); + mp_p->mp_free[bit_n] = addr; + } + else { + + /* setup our free list structure */ + free_pnt.mf_next_p = mp_p->mp_free[bit_n]; + free_pnt.mf_size = real_size; + + /* we copy the structure in since we don't know about alignment */ + memcpy(addr, &free_pnt, sizeof(free_pnt)); + mp_p->mp_free[bit_n] = addr; + } + + return MPOOL_ERROR_NONE; +} + +/* + * static int split_block + * + * DESCRIPTION: + * + * When freeing space in a multi-block chunk we have to create new + * blocks out of the upper areas being freed. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * free_addr -> Address that we are freeing. + * + * size -> Size of the space that we are taking from address. + */ +static int split_block(mpool_t *mp_p, void *free_addr, + const unsigned long size) +{ + mpool_block_t *block_p, *new_block_p; + int ret, page_n; + void *end_p; + + /* + * 1st we find the block pointer from our free addr. At this point + * the pointer must be the 1st one in the block if it is spans + * multiple blocks. + */ + block_p = (mpool_block_t *)((char *)free_addr - sizeof(mpool_block_t)); + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + page_n = PAGES_IN_SIZE(mp_p, size); + + /* we are creating a new block structure for the 2nd ... */ + new_block_p = (mpool_block_t *)((char *)block_p + + SIZE_OF_PAGES(mp_p, page_n)); + new_block_p->mb_magic = BLOCK_MAGIC; + /* New bounds is 1st block bounds. The 1st block's is reset below. */ + new_block_p->mb_bounds_p = block_p->mb_bounds_p; + /* Continue the linked list. The 1st block will point to us below. */ + new_block_p->mb_next_p = block_p->mb_next_p; + new_block_p->mb_magic2 = BLOCK_MAGIC; + + /* bounds for the 1st block are reset to the 1st page only */ + block_p->mb_bounds_p = (char *)new_block_p; + /* the next block pointer for the 1st block is now the new one */ + block_p->mb_next_p = new_block_p; + + /* only free the space in the 1st block if it is only 1 block in size */ + if (page_n == 1) { + /* now free the rest of the 1st block block */ + end_p = (char *)free_addr + size; + ret = free_pointer(mp_p, end_p, + (char *)block_p->mb_bounds_p - (char *)end_p); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + } + + /* now free the rest of the block */ + ret = free_pointer(mp_p, FIRST_ADDR_IN_BLOCK(new_block_p), + MEMORY_IN_BLOCK(new_block_p)); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + + return MPOOL_ERROR_NONE; +} + +/* + * static void *get_space + * + * DESCRIPTION: + * + * Moved a pointer into our free lists. + * + * RETURNS: + * + * Success - New address that we can use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * byte_size -> Size of the address space that we need. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +static void *get_space(mpool_t *mp_p, const unsigned long byte_size, + int *error_p) +{ + mpool_block_t *block_p; + mpool_free_t free_pnt; + int ret; + unsigned long size; + unsigned int bit_c, page_n, left; + void *free_addr = NULL, *free_end; + + size = byte_size; + while ((size & (sizeof(void *) - 1)) > 0) { + size++; + } + + /* + * First we check the free lists looking for something with enough + * pages. Maybe we should only look X bits higher in the list. + * + * XXX: this is where we'd do the best fit. We'd look for the + * closest match. We then could put the rest of the allocation that + * we did not use in a lower free list. Have a define which states + * how deep in the free list to go to find the closest match. + */ + for (bit_c = size_to_bits(size); bit_c <= MAX_BITS; bit_c++) { + if (mp_p->mp_free[bit_c] != NULL) { + free_addr = mp_p->mp_free[bit_c]; + break; + } + } + + /* + * If we haven't allocated any blocks or if the last block doesn't + * have enough memory then we need a new block. + */ + if (bit_c > MAX_BITS) { + + /* we need to allocate more space */ + + page_n = PAGES_IN_SIZE(mp_p, size); + + /* now we try and get the pages we need/want */ + block_p = alloc_pages(mp_p, page_n, error_p); + if (block_p == NULL) { + /* error_p set in alloc_pages */ + return NULL; + } + + /* init the block header */ + block_p->mb_magic = BLOCK_MAGIC; + block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(mp_p, page_n); + block_p->mb_next_p = mp_p->mp_first_p; + block_p->mb_magic2 = BLOCK_MAGIC; + + /* + * We insert it into the front of the queue. We could add it to + * the end but there is not much use. + */ + mp_p->mp_first_p = block_p; + if (mp_p->mp_last_p == NULL) { + mp_p->mp_last_p = block_p; + } + + free_addr = FIRST_ADDR_IN_BLOCK(block_p); + +#ifdef DEBUG + (void)printf("had to allocate space for %lx of %lu bytes\n", + (long)free_addr, size); +#endif + + free_end = (char *)free_addr + size; + left = (char *)block_p->mb_bounds_p - (char *)free_end; + } + else { + + if (bit_c < min_bit_free_next) { + mp_p->mp_free[bit_c] = NULL; + /* calculate the number of left over bytes */ + left = bits_to_size(bit_c) - size; + } + else if (bit_c < min_bit_free_next) { + /* grab the next pointer from the freed address into our list */ + memcpy(mp_p->mp_free + bit_c, free_addr, sizeof(void *)); + /* calculate the number of left over bytes */ + left = bits_to_size(bit_c) - size; + } + else { + /* grab the free structure from the address */ + memcpy(&free_pnt, free_addr, sizeof(free_pnt)); + mp_p->mp_free[bit_c] = free_pnt.mf_next_p; + + /* are we are splitting up a multiblock chunk into fewer blocks? */ + if (PAGES_IN_SIZE(mp_p, free_pnt.mf_size) > PAGES_IN_SIZE(mp_p, size)) { + ret = split_block(mp_p, free_addr, size); + if (ret != MPOOL_ERROR_NONE) { + SET_POINTER(error_p, ret); + return NULL; + } + /* left over memory was taken care of in split_block */ + left = 0; + } + else { + /* calculate the number of left over bytes */ + left = free_pnt.mf_size - size; + } + } + +#ifdef DEBUG + (void)printf("found a free block at %lx of %lu bytes\n", + (long)free_addr, left + size); +#endif + + free_end = (char *)free_addr + size; + } + + /* + * If we have memory left over then we free it so someone else can + * use it. We do not free the space if we just allocated a + * multi-block chunk because we need to have every allocation easily + * find the start of the block. Every user address % page-size + * should take us to the start of the block. + */ + if (left > 0 && size <= MAX_BLOCK_USER_MEMORY(mp_p)) { + /* free the rest of the block */ + ret = free_pointer(mp_p, free_end, left); + if (ret != MPOOL_ERROR_NONE) { + SET_POINTER(error_p, ret); + return NULL; + } + } + + /* update our bounds */ + if (free_addr > mp_p->mp_bounds_p) { + mp_p->mp_bounds_p = free_addr; + } + else if (free_addr < mp_p->mp_min_p) { + mp_p->mp_min_p = free_addr; + } + + return free_addr; +} + +/* + * static void *alloc_mem + * + * DESCRIPTION: + * + * Allocate space for bytes inside of an already open memory pool. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal malloc. + * + * byte_size -> Number of bytes to allocate in the pool. Must be >0. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +static void *alloc_mem(mpool_t *mp_p, const unsigned long byte_size, + int *error_p) +{ + unsigned long size, fence; + void *addr; + + /* make sure we have enough bytes */ + if (byte_size < MIN_ALLOCATION) { + size = MIN_ALLOCATION; + } + else { + size = byte_size; + } + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + fence = 0; + } + else { + fence = FENCE_SIZE; + } + + /* get our free space + the space for the fence post */ + addr = get_space(mp_p, size + fence, error_p); + if (addr == NULL) { + /* error_p set in get_space */ + return NULL; + } + + if (! BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + write_magic((char *)addr + size); + } + + /* maintain our stats */ + mp_p->mp_alloc_c++; + mp_p->mp_user_alloc += size; + if (mp_p->mp_user_alloc > mp_p->mp_max_alloc) { + mp_p->mp_max_alloc = mp_p->mp_user_alloc; + } + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return addr; +} + +/* + * static int free_mem + * + * DESCRIPTION: + * + * Free an address from a memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal free. + * + * addr <-> Address to free. + * + * size -> Size of the address being freed. + */ +static int free_mem(mpool_t *mp_p, void *addr, const unsigned long size) +{ + unsigned long old_size, fence; + int ret; + mpool_block_t *block_p; + + /* + * If the size is larger than a block then the allocation must be at + * the front of the block. + */ + if (size > MAX_BLOCK_USER_MEMORY(mp_p)) { + block_p = (mpool_block_t *)((char *)addr - sizeof(mpool_block_t)); + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + } + + /* make sure we have enough bytes */ + if (size < MIN_ALLOCATION) { + old_size = MIN_ALLOCATION; + } + else { + old_size = size; + } + + /* if we are packing the pool smaller */ + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + fence = 0; + } + else { + /* find the user's magic numbers if they were written */ + ret = check_magic(addr, old_size); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + fence = FENCE_SIZE; + } + + /* now we free the pointer */ + ret = free_pointer(mp_p, addr, old_size + fence); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + mp_p->mp_user_alloc -= old_size; + + /* adjust our stats */ + mp_p->mp_alloc_c--; + + return MPOOL_ERROR_NONE; +} + +/***************************** exported routines *****************************/ + +/* + * mpool_t *mpool_open + * + * DESCRIPTION: + * + * Open/allocate a new memory pool. + * + * RETURNS: + * + * Success - Pool pointer which must be passed to mpool_close to + * deallocate. + * + * Failure - NULL + * + * ARGUMENTS: + * + * flags -> Flags to set attributes of the memory pool. See the top + * of mpool.h. + * + * page_size -> Set the internal memory page-size. This must be a + * multiple of the getpagesize() value. Set to 0 for the default. + * + * start_addr -> Starting address to try and allocate memory pools. + * This is ignored if the MPOOL_FLAG_USE_SBRK is enabled. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size, + void *start_addr, int *error_p) +{ + mpool_block_t *block_p; + int page_n, ret; + mpool_t mp, *mp_p; + void *free_addr; + + if (! enabled_b) { + startup(); + } + + /* zero our temp struct */ + memset(&mp, 0, sizeof(mp)); + + mp.mp_magic = MPOOL_MAGIC; + mp.mp_flags = flags; + mp.mp_alloc_c = 0; + mp.mp_user_alloc = 0; + mp.mp_max_alloc = 0; + mp.mp_page_c = 0; + /* mp.mp_page_size set below */ + /* mp.mp_blocks_bit_n set below */ + /* mp.mp_fd set below */ + /* mp.mp_top set below */ + /* mp.mp_addr set below */ + mp.mp_log_func = NULL; + mp.mp_min_p = NULL; + mp.mp_bounds_p = NULL; + mp.mp_first_p = NULL; + mp.mp_last_p = NULL; + mp.mp_magic2 = MPOOL_MAGIC; + + /* get and sanity check our page size */ + if (page_size > 0) { + mp.mp_page_size = page_size; + if (mp.mp_page_size % getpagesize() != 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + } + else { + mp.mp_page_size = getpagesize() * DEFAULT_PAGE_MULT; + if (mp.mp_page_size % 1024 != 0) { + SET_POINTER(error_p, MPOOL_ERROR_PAGE_SIZE); + return NULL; + } + } + + if (BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)) { + mp.mp_fd = -1; + mp.mp_addr = NULL; + mp.mp_top = 0; + } + else if (BIT_IS_SET(flags, MPOOL_FLAG_USE_MAP_ANON)) { + mp.mp_fd = -1; + mp.mp_addr = start_addr; + mp.mp_top = 0; + } + else { + /* open dev-zero for our mmaping */ + mp.mp_fd = open("/dev/zero", O_RDWR, 0); + if (mp.mp_fd < 0) { + SET_POINTER(error_p, MPOOL_ERROR_OPEN_ZERO); + return NULL; + } + mp.mp_addr = start_addr; + /* we start at the front of the file */ + mp.mp_top = 0; + } + + /* + * Find out how many pages we need for our mpool structure. + * + * NOTE: this adds possibly unneeded space for mpool_block_t which + * may not be in this block. + */ + page_n = PAGES_IN_SIZE(&mp, sizeof(mpool_t)); + + /* now allocate us space for the actual struct */ + mp_p = alloc_pages(&mp, page_n, error_p); + if (mp_p == NULL) { + if (mp.mp_fd >= 0) { + (void)close(mp.mp_fd); + mp.mp_fd = -1; + } + return NULL; + } + + /* + * NOTE: we do not normally free the rest of the block here because + * we want to lesson the chance of an allocation overwriting the + * main structure. + */ + if (BIT_IS_SET(flags, MPOOL_FLAG_HEAVY_PACKING)) { + + /* we add a block header to the front of the block */ + block_p = (mpool_block_t *)mp_p; + + /* init the block header */ + block_p->mb_magic = BLOCK_MAGIC; + block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(&mp, page_n); + block_p->mb_next_p = NULL; + block_p->mb_magic2 = BLOCK_MAGIC; + + /* the mpool pointer is then the 2nd thing in the block */ + mp_p = FIRST_ADDR_IN_BLOCK(block_p); + free_addr = (char *)mp_p + sizeof(mpool_t); + + /* free the rest of the block */ + ret = free_pointer(&mp, free_addr, + (char *)block_p->mb_bounds_p - (char *)free_addr); + if (ret != MPOOL_ERROR_NONE) { + if (mp.mp_fd >= 0) { + (void)close(mp.mp_fd); + mp.mp_fd = -1; + } + /* NOTE: after this line mp_p will be invalid */ + (void)free_pages(block_p, SIZE_OF_PAGES(&mp, page_n), + BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)); + SET_POINTER(error_p, ret); + return NULL; + } + + /* + * NOTE: if we are HEAVY_PACKING then the 1st block with the mpool + * header is not on the block linked list. + */ + + /* now copy our tmp structure into our new memory area */ + memcpy(mp_p, &mp, sizeof(mpool_t)); + + /* we setup min/max to our current address which is as good as any */ + mp_p->mp_min_p = block_p; + mp_p->mp_bounds_p = block_p->mb_bounds_p; + } + else { + /* now copy our tmp structure into our new memory area */ + memcpy(mp_p, &mp, sizeof(mpool_t)); + + /* we setup min/max to our current address which is as good as any */ + mp_p->mp_min_p = mp_p; + mp_p->mp_bounds_p = (char *)mp_p + SIZE_OF_PAGES(mp_p, page_n); + } + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return mp_p; +} + +/* + * int mpool_close + * + * DESCRIPTION: + * + * Close/free a memory allocation pool previously opened with + * mpool_open. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +int mpool_close(mpool_t *mp_p) +{ + mpool_block_t *block_p, *next_p; + void *addr; + unsigned long size; + int ret, final = MPOOL_ERROR_NONE; + + /* special case, just return no-error */ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_CLOSE, 0, 0, NULL, NULL, 0); + } + + /* + * NOTE: if we are HEAVY_PACKING then the 1st block with the mpool + * header is not on the linked list. + */ + + /* free/invalidate the blocks */ + for (block_p = mp_p->mp_first_p; block_p != NULL; block_p = next_p) { + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + final = MPOOL_ERROR_POOL_OVER; + break; + } + block_p->mb_magic = 0; + block_p->mb_magic2 = 0; + /* record the next pointer because it might be invalidated below */ + next_p = block_p->mb_next_p; + ret = free_pages(block_p, (char *)block_p->mb_bounds_p - (char *)block_p, + BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)); + if (ret != MPOOL_ERROR_NONE) { + final = ret; + } + } + + /* close /dev/zero if necessary */ + if (mp_p->mp_fd >= 0) { + (void)close(mp_p->mp_fd); + mp_p->mp_fd = -1; + } + + /* invalidate the mpool before we ditch it */ + mp_p->mp_magic = 0; + mp_p->mp_magic2 = 0; + + /* last we munmap the mpool pointer itself */ + if (! BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)) { + + /* if we are heavy packing then we need to free the 1st block later */ + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_HEAVY_PACKING)) { + addr = (char *)mp_p - sizeof(mpool_block_t); + } + else { + addr = mp_p; + } + size = SIZE_OF_PAGES(mp_p, PAGES_IN_SIZE(mp_p, sizeof(mpool_t))); + + (void)munmap((caddr_t)addr, size); + } + + return final; +} + +/* + * int mpool_clear + * + * DESCRIPTION: + * + * Wipe an opened memory pool clean so we can start again. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +int mpool_clear(mpool_t *mp_p) +{ + mpool_block_t *block_p; + int final = MPOOL_ERROR_NONE, bit_n, ret; + void *first_p; + + /* special case, just return no-error */ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_CLEAR, 0, 0, NULL, NULL, 0); + } + + /* reset all of our free lists */ + for (bit_n = 0; bit_n <= MAX_BITS; bit_n++) { + mp_p->mp_free[bit_n] = NULL; + } + + /* free the blocks */ + for (block_p = mp_p->mp_first_p; + block_p != NULL; + block_p = block_p->mb_next_p) { + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + final = MPOOL_ERROR_POOL_OVER; + break; + } + + first_p = FIRST_ADDR_IN_BLOCK(block_p); + + /* free the memory */ + ret = free_pointer(mp_p, first_p, MEMORY_IN_BLOCK(block_p)); + if (ret != MPOOL_ERROR_NONE) { + final = ret; + } + } + + return final; +} + +/* + * void *mpool_alloc + * + * DESCRIPTION: + * + * Allocate space for bytes inside of an already open memory pool. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal malloc. + * + * byte_size -> Number of bytes to allocate in the pool. Must be >0. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +void *mpool_alloc(mpool_t *mp_p, const unsigned long byte_size, + int *error_p) +{ + void *addr; + + if (mp_p == NULL) { + /* special case -- do a normal malloc */ + addr = (void *)malloc(byte_size); + if (addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ALLOC); + return NULL; + } + else { + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return addr; + } + } + + if (mp_p->mp_magic != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_PNT); + return NULL; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + + if (byte_size == 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + + addr = alloc_mem(mp_p, byte_size, error_p); + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_ALLOC, byte_size, 0, addr, NULL, 0); + } + + return addr; +} + +/* + * void *mpool_calloc + * + * DESCRIPTION: + * + * Allocate space for elements of bytes in the memory pool and zero + * the space afterwards. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal calloc. + * + * ele_n -> Number of elements to allocate. + * + * ele_size -> Number of bytes per element being allocated. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +void *mpool_calloc(mpool_t *mp_p, const unsigned long ele_n, + const unsigned long ele_size, int *error_p) +{ + void *addr; + unsigned long byte_size; + + if (mp_p == NULL) { + /* special case -- do a normal calloc */ + addr = (void *)calloc(ele_n, ele_size); + if (addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ALLOC); + return NULL; + } + else { + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return addr; + } + + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_PNT); + return NULL; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + + if (ele_n == 0 || ele_size == 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + + byte_size = ele_n * ele_size; + addr = alloc_mem(mp_p, byte_size, error_p); + if (addr != NULL) { + memset(addr, 0, byte_size); + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_CALLOC, ele_size, ele_n, addr, NULL, 0); + } + + /* NOTE: error_p set above */ + return addr; +} + +/* + * int mpool_free + * + * DESCRIPTION: + * + * Free an address from a memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal free. + * + * addr <-> Address to free. + * + * size -> Size of the address being freed. + */ +int mpool_free(mpool_t *mp_p, void *addr, const unsigned long size) +{ + if (mp_p == NULL) { + /* special case -- do a normal free */ + free(addr); + return MPOOL_ERROR_NONE; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_FREE, size, 0, NULL, addr, 0); + } + + if (addr == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (size == 0) { + return MPOOL_ERROR_ARG_INVALID; + } + + return free_mem(mp_p, addr, size); +} + +/* + * void *mpool_resize + * + * DESCRIPTION: + * + * Reallocate an address in a mmeory pool to a new size. This is + * different from realloc in that it needs the old address' size. If + * you don't have it then you need to allocate new space, copy the + * data, and free the old pointer yourself. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal realloc. + * + * old_addr -> Previously allocated address. + * + * old_byte_size -> Size of the old address. Must be known, cannot be + * 0. + * + * new_byte_size -> New size of the allocation. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +void *mpool_resize(mpool_t *mp_p, void *old_addr, + const unsigned long old_byte_size, + const unsigned long new_byte_size, + int *error_p) +{ + unsigned long copy_size, new_size, old_size, fence; + void *new_addr; + mpool_block_t *block_p; + int ret; + + if (mp_p == NULL) { + /* special case -- do a normal realloc */ + new_addr = (void *)realloc(old_addr, new_byte_size); + if (new_addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ALLOC); + return NULL; + } + else { + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return new_addr; + } + } + + if (mp_p->mp_magic != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_PNT); + return NULL; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + + if (old_addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_NULL); + return NULL; + } + if (old_byte_size == 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + + /* + * If the size is larger than a block then the allocation must be at + * the front of the block. + */ + if (old_byte_size > MAX_BLOCK_USER_MEMORY(mp_p)) { + block_p = (mpool_block_t *)((char *)old_addr - sizeof(mpool_block_t)); + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + } + + /* make sure we have enough bytes */ + if (old_byte_size < MIN_ALLOCATION) { + old_size = MIN_ALLOCATION; + } + else { + old_size = old_byte_size; + } + + /* verify that the size matches exactly if we can */ + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + fence = 0; + } + else if (old_size > 0) { + ret = check_magic(old_addr, old_size); + if (ret != MPOOL_ERROR_NONE) { + SET_POINTER(error_p, ret); + return NULL; + } + fence = FENCE_SIZE; + } + + /* make sure we have enough bytes */ + if (new_byte_size < MIN_ALLOCATION) { + new_size = MIN_ALLOCATION; + } + else { + new_size = new_byte_size; + } + + /* + * NOTE: we could here see if the size is the same or less and then + * use the current memory and free the space above. This is harder + * than it sounds if we are changing the block size of the + * allocation. + */ + + /* we need to get another address */ + new_addr = alloc_mem(mp_p, new_byte_size, error_p); + if (new_addr == NULL) { + /* error_p set in mpool_alloc */ + return NULL; + } + + if (new_byte_size > old_byte_size) { + copy_size = old_byte_size; + } + else { + copy_size = new_byte_size; + } + memcpy(new_addr, old_addr, copy_size); + + /* free the old address */ + ret = free_mem(mp_p, old_addr, old_byte_size); + if (ret != MPOOL_ERROR_NONE) { + /* if the old free failed, try and free the new address */ + (void)free_mem(mp_p, new_addr, new_byte_size); + SET_POINTER(error_p, ret); + return NULL; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_RESIZE, new_byte_size, + 0, new_addr, old_addr, old_byte_size); + } + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return new_addr; +} + +/* + * int mpool_stats + * + * DESCRIPTION: + * + * Return stats from the memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p -> Pointer to the memory pool. + * + * page_size_p <- Pointer to an unsigned integer which, if not NULL, + * will be set to the page-size of the pool. + * + * num_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of pointers currently allocated in pool. + * + * user_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of user bytes allocated in this pool. + * + * max_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the maximum number of user bytes that have been + * allocated in this pool. + * + * tot_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the total amount of space (including administrative + * overhead) used by the pool. + */ +int mpool_stats(const mpool_t *mp_p, unsigned int *page_size_p, + unsigned long *num_alloced_p, + unsigned long *user_alloced_p, + unsigned long *max_alloced_p, + unsigned long *tot_alloced_p) +{ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + SET_POINTER(page_size_p, mp_p->mp_page_size); + SET_POINTER(num_alloced_p, mp_p->mp_alloc_c); + SET_POINTER(user_alloced_p, mp_p->mp_user_alloc); + SET_POINTER(max_alloced_p, mp_p->mp_max_alloc); + SET_POINTER(tot_alloced_p, SIZE_OF_PAGES(mp_p, mp_p->mp_page_c)); + + return MPOOL_ERROR_NONE; +} + +/* + * int mpool_set_log_func + * + * DESCRIPTION: + * + * Set a logging callback function to be called whenever there was a + * memory transaction. See mpool_log_func_t. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * log_func -> Log function (defined in mpool.h) which will be called + * with each mpool transaction. + */ +int mpool_set_log_func(mpool_t *mp_p, mpool_log_func_t log_func) +{ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + mp_p->mp_log_func = log_func; + + return MPOOL_ERROR_NONE; +} + +/* + * int mpool_set_max_pages + * + * DESCRIPTION: + * + * Set the maximum number of pages that the library will use. Once it + * hits the limit it will return MPOOL_ERROR_NO_PAGES. + * + * NOTE: if the MPOOL_FLAG_HEAVY_PACKING is set then this max-pages + * value will include the page with the mpool header structure in it. + * If the flag is _not_ set then the max-pages will not include this + * first page. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * max_pages -> Maximum number of pages used by the library. + */ +int mpool_set_max_pages(mpool_t *mp_p, const unsigned int max_pages) +{ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_HEAVY_PACKING)) { + mp_p->mp_max_pages = max_pages; + } + else { + /* + * If we are not heavy-packing the pool then we don't count the + * 1st page allocated which holds the mpool header structure. + */ + mp_p->mp_max_pages = max_pages + 1; + } + + return MPOOL_ERROR_NONE; +} + +/* + * const char *mpool_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error -> Error number that we are converting. + */ +const char *mpool_strerror(const int error) +{ + switch (error) { + case MPOOL_ERROR_NONE: + return "no error"; + break; + case MPOOL_ERROR_ARG_NULL: + return "function argument is null"; + break; + case MPOOL_ERROR_ARG_INVALID: + return "function argument is invalid"; + break; + case MPOOL_ERROR_PNT: + return "invalid mpool pointer"; + break; + case MPOOL_ERROR_POOL_OVER: + return "mpool structure was overwritten"; + break; + case MPOOL_ERROR_PAGE_SIZE: + return "could not get system page-size"; + break; + case MPOOL_ERROR_OPEN_ZERO: + return "could not open /dev/zero"; + break; + case MPOOL_ERROR_NO_MEM: + return "no memory available"; + break; + case MPOOL_ERROR_MMAP: + return "problems with mmap"; + break; + case MPOOL_ERROR_SIZE: + return "error processing requested size"; + break; + case MPOOL_ERROR_TOO_BIG: + return "allocation exceeds pool max size"; + break; + case MPOOL_ERROR_MEM: + return "invalid memory address"; + break; + case MPOOL_ERROR_MEM_OVER: + return "memory lower bounds overwritten"; + break; + case MPOOL_ERROR_NOT_FOUND: + return "memory block not found in pool"; + break; + case MPOOL_ERROR_IS_FREE: + return "memory address has already been freed"; + break; + case MPOOL_ERROR_BLOCK_STAT: + return "invalid internal block status"; + break; + case MPOOL_ERROR_FREE_ADDR: + return "invalid internal free address"; + break; + case MPOOL_ERROR_SBRK_CONTIG: + return "sbrk did not return contiguous memory"; + break; + case MPOOL_ERROR_NO_PAGES: + return "no available pages left in pool"; + break; + case MPOOL_ERROR_ALLOC: + return "system alloc function failed"; + break; + case MPOOL_ERROR_PNT_OVER: + return "user pointer admin space overwritten"; + break; + default: + break; + } + + return "invalid error code"; +} diff --git a/deps/mpool-2.1.0/mpool.h b/deps/mpool-2.1.0/mpool.h new file mode 100644 index 0000000..788a6af --- /dev/null +++ b/deps/mpool-2.1.0/mpool.h @@ -0,0 +1,470 @@ +/* + * Memory pool defines. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool.h,v 1.4 2006/05/31 20:26:11 gray Exp $ + */ + +#ifndef __MPOOL_H__ +#define __MPOOL_H__ + +#include + + +/* + * mpool flags to mpool_alloc or mpool_set_attr + */ + +/* + * Choose a best fit algorithm not first fit. This takes more CPU + * time but will result in a tighter heap. + */ +#define MPOOL_FLAG_BEST_FIT (1<<0) + +/* + * By default the library adds 2 bytes onto all allocations to insert + * a magic number that it can look for to determine how large a freed + * memory chunk is. This flag indicates that few if any frees are + * going to be performed on the pool and to not waste memory on these + * bytes. + */ +#define MPOOL_FLAG_NO_FREE (1<<1) + +/* + * This enables very heavy packing at the possible expense of CPU. + * This affects a number of parts of the library. + * + * By default the 1st page of memory is reserved for the main mpool + * structure. This flag will cause the rest of the 1st block to be + * available for use as user memory. + * + * By default the library looks through the memory when freed looking + * for a magic value. There is an internal max size that it will look + * and then it will give up. This flag forces it to look until it + * finds it. + */ +#define MPOOL_FLAG_HEAVY_PACKING (1<<2) + +/* + * Use sbrk not mmap to allocate pages. This is not recommended for + * normal use. + */ +#define MPOOL_FLAG_USE_SBRK (1<<3) + +/* + * Use MAP_ANON(YMOUS) instead of /dev/zero for mmap. + */ + +#define MPOOL_FLAG_USE_MAP_ANON (1<<4) + +/* + * Mpool error codes + */ +#define MPOOL_ERROR_NONE 1 /* no error */ +#define MPOOL_ERROR_ARG_NULL 2 /* function argument is null */ +#define MPOOL_ERROR_ARG_INVALID 3 /* function argument is invalid */ +#define MPOOL_ERROR_PNT 4 /* invalid mpool pointer */ +#define MPOOL_ERROR_POOL_OVER 5 /* mpool structure was overwritten */ +#define MPOOL_ERROR_PAGE_SIZE 6 /* could not get system page-size */ +#define MPOOL_ERROR_OPEN_ZERO 7 /* could not open /dev/zero */ +#define MPOOL_ERROR_NO_MEM 8 /* no memory available */ +#define MPOOL_ERROR_MMAP 9 /* problems with mmap */ +#define MPOOL_ERROR_SIZE 10 /* error processing requested size */ +#define MPOOL_ERROR_TOO_BIG 11 /* allocation exceeded max size */ +#define MPOOL_ERROR_MEM 12 /* invalid memory address */ +#define MPOOL_ERROR_MEM_OVER 13 /* memory lower bounds overwritten */ +#define MPOOL_ERROR_NOT_FOUND 14 /* memory block not found in pool */ +#define MPOOL_ERROR_IS_FREE 15 /* memory block already free */ +#define MPOOL_ERROR_BLOCK_STAT 16 /* invalid internal block status */ +#define MPOOL_ERROR_FREE_ADDR 17 /* invalid internal free address */ +#define MPOOL_ERROR_SBRK_CONTIG 18 /* sbrk did not return contiguous mem*/ +#define MPOOL_ERROR_NO_PAGES 19 /* ran out of pages in pool */ +#define MPOOL_ERROR_ALLOC 20 /* calloc,malloc,free,realloc failed */ +#define MPOOL_ERROR_PNT_OVER 21 /* pointer structure was overwritten */ + +/* + * Mpool function IDs for the mpool_log_func callback function. + */ +#define MPOOL_FUNC_CLOSE 1 /* mpool_close function called */ +#define MPOOL_FUNC_CLEAR 2 /* mpool_clear function called */ +#define MPOOL_FUNC_ALLOC 3 /* mpool_alloc function called */ +#define MPOOL_FUNC_CALLOC 4 /* mpool_calloc function called */ +#define MPOOL_FUNC_FREE 5 /* mpool_free function called */ +#define MPOOL_FUNC_RESIZE 6 /* mpool_resize function called */ + +/* + * void mpool_log_func_t + * + * DESCRIPTION: + * + * Mpool transaction log function. + * + * RETURNS: + * + * None. + * + * ARGUMENT: + * + * mp_p -> Associated mpool address. + * + * func_id -> Integer function ID which identifies which mpool + * function is being called. + * + * byte_size -> Optionally specified byte size. + * + * ele_n -> Optionally specified element number. For mpool_calloc + * only. + * + * new_addr -> Optionally specified new address. For mpool_alloc, + * mpool_calloc, and mpool_resize only. + * + * old_addr -> Optionally specified old address. For mpool_resize and + * mpool_free only. + * + * old_byte_size -> Optionally specified old byte size. For + * mpool_resize only. + */ +typedef void (*mpool_log_func_t)(const void *mp_p, + const int func_id, + const unsigned long byte_size, + const unsigned long ele_n, + const void *old_addr, const void *new_addr, + const unsigned long old_byte_size); + +#ifdef MPOOL_MAIN + +#include "mpool_loc.h" + +#else + +/* generic mpool type */ +typedef void mpool_t; + +#endif + +/*<<<<<<<<<< The below prototypes are auto-generated by fillproto */ + +/* + * mpool_t *mpool_open + * + * DESCRIPTION: + * + * Open/allocate a new memory pool. + * + * RETURNS: + * + * Success - Pool pointer which must be passed to mpool_close to + * deallocate. + * + * Failure - NULL + * + * ARGUMENTS: + * + * flags -> Flags to set attributes of the memory pool. See the top + * of mpool.h. + * + * page_size -> Set the internal memory page-size. This must be a + * multiple of the getpagesize() value. Set to 0 for the default. + * + * start_addr -> Starting address to try and allocate memory pools. + * This is ignored if the MPOOL_FLAG_USE_SBRK is enabled. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size, + void *start_addr, int *error_p); + +/* + * int mpool_close + * + * DESCRIPTION: + * + * Close/free a memory allocation pool previously opened with + * mpool_open. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +extern +int mpool_close(mpool_t *mp_p); + +/* + * int mpool_clear + * + * DESCRIPTION: + * + * Wipe an opened memory pool clean so we can start again. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +extern +int mpool_clear(mpool_t *mp_p); + +/* + * void *mpool_alloc + * + * DESCRIPTION: + * + * Allocate space for bytes inside of an already open memory pool. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal malloc. + * + * byte_size -> Number of bytes to allocate in the pool. Must be >0. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +void *mpool_alloc(mpool_t *mp_p, const unsigned long byte_size, + int *error_p); + +/* + * void *mpool_calloc + * + * DESCRIPTION: + * + * Allocate space for elements of bytes in the memory pool and zero + * the space afterwards. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal calloc. + * + * ele_n -> Number of elements to allocate. + * + * ele_size -> Number of bytes per element being allocated. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +void *mpool_calloc(mpool_t *mp_p, const unsigned long ele_n, + const unsigned long ele_size, int *error_p); + +/* + * int mpool_free + * + * DESCRIPTION: + * + * Free an address from a memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal free. + * + * addr <-> Address to free. + * + * size -> Size of the address being freed. + */ +extern +int mpool_free(mpool_t *mp_p, void *addr, const unsigned long size); + +/* + * void *mpool_resize + * + * DESCRIPTION: + * + * Reallocate an address in a mmeory pool to a new size. This is + * different from realloc in that it needs the old address' size. If + * you don't have it then you need to allocate new space, copy the + * data, and free the old pointer yourself. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal realloc. + * + * old_addr -> Previously allocated address. + * + * old_byte_size -> Size of the old address. Must be known, cannot be + * 0. + * + * new_byte_size -> New size of the allocation. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +void *mpool_resize(mpool_t *mp_p, void *old_addr, + const unsigned long old_byte_size, + const unsigned long new_byte_size, + int *error_p); + +/* + * int mpool_stats + * + * DESCRIPTION: + * + * Return stats from the memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p -> Pointer to the memory pool. + * + * page_size_p <- Pointer to an unsigned integer which, if not NULL, + * will be set to the page-size of the pool. + * + * num_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of pointers currently allocated in pool. + * + * user_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of user bytes allocated in this pool. + * + * max_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the maximum number of user bytes that have been + * allocated in this pool. + * + * tot_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the total amount of space (including administrative + * overhead) used by the pool. + */ +extern +int mpool_stats(const mpool_t *mp_p, unsigned int *page_size_p, + unsigned long *num_alloced_p, + unsigned long *user_alloced_p, + unsigned long *max_alloced_p, + unsigned long *tot_alloced_p); + +/* + * int mpool_set_log_func + * + * DESCRIPTION: + * + * Set a logging callback function to be called whenever there was a + * memory transaction. See mpool_log_func_t. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * log_func -> Log function (defined in mpool.h) which will be called + * with each mpool transaction. + */ +extern +int mpool_set_log_func(mpool_t *mp_p, mpool_log_func_t log_func); + +/* + * int mpool_set_max_pages + * + * DESCRIPTION: + * + * Set the maximum number of pages that the library will use. Once it + * hits the limit it will return MPOOL_ERROR_NO_PAGES. + * + * NOTE: if the MPOOL_FLAG_HEAVY_PACKING is set then this max-pages + * value will include the page with the mpool header structure in it. + * If the flag is _not_ set then the max-pages will not include this + * first page. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * max_pages -> Maximum number of pages used by the library. + */ +extern +int mpool_set_max_pages(mpool_t *mp_p, const unsigned int max_pages); + +/* + * const char *mpool_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error -> Error number that we are converting. + */ +extern +const char *mpool_strerror(const int error); + +/*<<<<<<<<<< This is end of the auto-generated output from fillproto. */ + +#endif /* ! __MPOOL_H__ */ diff --git a/deps/mpool-2.1.0/mpool_loc.h b/deps/mpool-2.1.0/mpool_loc.h new file mode 100644 index 0000000..3d33f5d --- /dev/null +++ b/deps/mpool-2.1.0/mpool_loc.h @@ -0,0 +1,116 @@ +/* + * Memory pool local defines. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool_loc.h,v 1.2 2005/05/20 20:08:54 gray Exp $ + */ + +#ifndef __MPOOL_LOC_H__ +#define __MPOOL_LOC_H__ + +#define MPOOL_MAGIC 0xABACABA /* magic for struct */ +#define BLOCK_MAGIC 0xB1B1007 /* magic for blocks */ +#define FENCE_MAGIC0 (unsigned char)(0xFAU) /* 1st magic mem byte */ +#define FENCE_MAGIC1 (unsigned char)(0xD3U) /* 2nd magic mem byte */ + +#define FENCE_SIZE 2 /* fence space */ +#define MIN_ALLOCATION (sizeof(mpool_free_t)) /* min alloc */ +#define MAX_FREE_SEARCH 10240 /* max size to search */ +#define MAX_FREE_LIST_SEARCH 100 /* max looking for free mem */ + +/* + * bitflag tools for Variable and a Flag + */ +#define BIT_FLAG(x) (1 << (x)) +#define BIT_SET(v,f) (v) |= (f) +#define BIT_CLEAR(v,f) (v) &= ~(f) +#define BIT_IS_SET(v,f) ((v) & (f)) +#define BIT_TOGGLE(v,f) (v) ^= (f) + +#define SET_POINTER(pnt, val) \ + do { \ + if ((pnt) != NULL) { \ + (*(pnt)) = (val); \ + } \ + } while(0) + +#define BLOCK_FLAG_USED BIT_FLAG(0) /* block is used */ +#define BLOCK_FLAG_FREE BIT_FLAG(1) /* block is free */ + +#define DEFAULT_PAGE_MULT 16 /* pagesize = this * getpagesize*/ + +/* How many pages SIZE bytes resides in. We add in the block header. */ +#define PAGES_IN_SIZE(mp_p, size) (((size) + sizeof(mpool_block_t) + \ + (mp_p)->mp_page_size - 1) / \ + (mp_p)->mp_page_size) +#define SIZE_OF_PAGES(mp_p, page_n) ((page_n) * (mp_p)->mp_page_size) +#define MAX_BITS 30 /* we only can allocate 1gb chunks */ + +#define MAX_BLOCK_USER_MEMORY(mp_p) ((mp_p)->mp_page_size - \ + sizeof(mpool_block_t)) +#define FIRST_ADDR_IN_BLOCK(block_p) (void *)((char *)(block_p) + \ + sizeof(mpool_block_t)) +#define MEMORY_IN_BLOCK(block_p) ((char *)(block_p)->mb_bounds_p - \ + ((char *)(block_p) + \ + sizeof(mpool_block_t))) + +typedef struct { + unsigned int mp_magic; /* magic number for struct */ + unsigned int mp_flags; /* flags for the struct */ + unsigned long mp_alloc_c; /* number of allocations */ + unsigned long mp_user_alloc; /* user bytes allocated */ + unsigned long mp_max_alloc; /* maximum user bytes allocated */ + unsigned int mp_page_c; /* number of pages allocated */ + unsigned int mp_max_pages; /* maximum number of pages to use */ + unsigned int mp_page_size; /* page-size of our system */ + int mp_fd; /* fd for /dev/zero if mmap-ing */ + off_t mp_top; /* top of our allocations in fd */ + mpool_log_func_t mp_log_func; /* log callback function */ + void *mp_addr; /* current address for mmaping */ + void *mp_min_p; /* min address in pool for checks */ + void *mp_bounds_p; /* max address in pool for checks */ + struct mpool_block_st *mp_first_p; /* first memory block we are using */ + struct mpool_block_st *mp_last_p; /* last memory block we are using */ + struct mpool_block_st *mp_free[MAX_BITS + 1]; /* free lists based on size */ + unsigned int mp_magic2; /* upper magic for overwrite sanity */ +} mpool_t; + +/* for debuggers to be able to interrogate the generic type in the .h file */ +typedef mpool_t mpool_ext_t; + +/* + * Block header structure. This structure *MUST* be long-word + * aligned. + */ +typedef struct mpool_block_st { + unsigned int mb_magic; /* magic number for block header */ + void *mb_bounds_p; /* block boundary location */ + struct mpool_block_st *mb_next_p; /* linked list next pointer */ + unsigned int mb_magic2; /* upper magic for overwrite sanity */ +} mpool_block_t; + +/* + * Free list structure. + */ +typedef struct { + void *mf_next_p; /* pointer to the next free address */ + unsigned long mf_size; /* size of the free block */ +} mpool_free_t; + +#endif /* ! __MPOOL_LOC_H__ */ diff --git a/deps/mpool-2.1.0/mpool_t.c b/deps/mpool-2.1.0/mpool_t.c new file mode 100644 index 0000000..ad1156b --- /dev/null +++ b/deps/mpool-2.1.0/mpool_t.c @@ -0,0 +1,914 @@ +/* + * Memory pool test program. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool_t.c,v 1.2 2005/05/20 20:08:55 gray Exp $ + */ + +/* + * Test program for the malloc library. Current it is interactive although + * should be script based. + */ + +#include +#include +#include +#include +#include +#include + +#include "mpool.h" + +#ifdef __GNUC__ +#ident "$Id: mpool_t.c,v 1.2 2005/05/20 20:08:55 gray Exp $" +#else +static char *rcs_id = "$Id: mpool_t.c,v 1.2 2005/05/20 20:08:55 gray Exp $"; +#endif + +#define DEFAULT_ITERATIONS 10000 +#define MAX_POINTERS 1024 +#define MAX_ALLOC (1024 * 1024) +#define MIN_AVAIL 10 + +#define RANDOM_VALUE(x) ((random() % ((x) * 10)) / 10) + +/* pointer tracking structure */ +struct pnt_info_st { + long pi_crc; /* crc of storage */ + long pi_size; /* size of storage */ + void *pi_pnt; /* pnt to storage */ + struct pnt_info_st *pi_next; /* pnt to next */ +}; + +typedef struct pnt_info_st pnt_info_t; + +static pnt_info_t *pointer_grid; + +/* argument variables */ +static int best_fit_b = 0; /* set best fit flag */ +static int heavy_pack_b = 0; /* set heavy pack flg*/ +static int interactive_b = 0; /* interactive flag */ +static int log_trxn_b = 0; /* log mem trxns */ +static long max_alloc = MAX_ALLOC; /* amt of mem to use */ +static int max_pages_n = 0; /* max # pages */ +static int use_malloc_b = 0; /* use system alloc */ +static int max_pointers = MAX_POINTERS; /* # of pnts to use */ +static int no_free_b = 0; /* set no free flag */ +static long page_size = 0; /* mpool pagesize */ +static int use_sbrk_b = 0; /* use sbrk not mmap */ +static unsigned int seed_random = 0; /* random seed */ +static int default_iter_n = DEFAULT_ITERATIONS; /* # of iters */ +static int verbose_b = 0; /* verbose flag */ + +/* + * static long hex_to_long + * + * DESCRIPTION: + * + * Hexadecimal string to integer translation. + * + * RETURNS: + * + * Long value of converted hex string. + * + * ARGUMENTS: + * + * str -> Hex string we are converting. + */ +static long hex_to_long(const char *str) +{ + long ret; + const char *str_p = str; + + /* strip off spaces */ + for (; *str_p == ' ' || *str_p == '\t'; str_p++) { + } + + /* skip a leading 0[xX] */ + if (*str_p == '0' && (*(str_p + 1) == 'x' || *(str_p + 1) == 'X')) { + str_p += 2; + } + + for (ret = 0;; str_p++) { + if (*str_p >= '0' && *str_p <= '9') { + ret = ret * 16 + (*str_p - '0'); + } + else if (*str_p >= 'a' && *str_p <= 'f') { + ret = ret * 16 + (*str_p - 'a' + 10); + } + else if (*str_p >= 'A' && *str_p <= 'F') { + ret = ret * 16 + (*str_p - 'A' + 10); + } + else { + break; + } + } + + return ret; +} + +/* + * static void* get_address + * + * DESCRIPTION: + * + * Read an address from the user. + * + * RETURNS: + * + * Address read in from user. + * + * ARGUMENTS: + * + * None. + */ +static void *get_address(void) +{ + char line[80]; + void *pnt; + + do { + (void)printf("Enter a hex address: "); + if (fgets(line, sizeof(line), stdin) == NULL) { + return NULL; + } + } while (line[0] == '\0'); + + pnt = (void *)hex_to_long(line); + + return pnt; +} + +/* + * static void do_random + * + * DESCRIPTION: + * + * Try ITER_N random program iterations, returns 1 on success else 0 + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * pool <-> Out memory pool. + * + * iter_n -> Number of iterations to run. + */ +static void do_random(mpool_t *pool, const int iter_n) +{ + int iter_c, free_c, ret; + long max = max_alloc, amount; + char *chunk_p; + void *new_pnt; + pnt_info_t *free_p, *used_p = NULL; + pnt_info_t *pnt_p, *last_p; + + if (use_malloc_b) { + pointer_grid = (pnt_info_t *)malloc(sizeof(pnt_info_t) * max_pointers); + } + else { + pointer_grid = (pnt_info_t *)mpool_alloc(pool, + sizeof(pnt_info_t) * max_pointers, + &ret); + } + if (pointer_grid == NULL) { + (void)printf("mpool_t: problems allocating %d pointer slots: %s\n", + max_pointers, strerror(errno)); + return; + } + + /* initialize free list */ + free_p = pointer_grid; + for (pnt_p = pointer_grid; pnt_p < pointer_grid + max_pointers; pnt_p++) { + pnt_p->pi_size = 0; + pnt_p->pi_pnt = NULL; + pnt_p->pi_next = pnt_p + 1; + } + /* redo the last next pointer */ + (pnt_p - 1)->pi_next = NULL; + free_c = max_pointers; + + for (iter_c = 0; iter_c < iter_n;) { + int which; + + /* special case when doing non-linear stuff, sbrk took all memory */ + if (max < MIN_AVAIL && free_c == max_pointers) { + break; + } + + if (free_c < max_pointers && used_p == NULL) { + (void)fprintf(stderr, "mpool_t: problem with test program free list\n"); + exit(1); + } + + /* decide whether to malloc a new pointer or free/realloc an existing */ + which = RANDOM_VALUE(4); + + /* + * < MIN_AVAIL means alloc as long as we have enough memory and + * there are free slots we do an allocation, else we free + */ + if (free_c == max_pointers + || (free_c > 0 && which < 3 && max >= MIN_AVAIL)) { + + while (1) { + amount = RANDOM_VALUE(max / 2); + if (amount > 0) { + break; + } + } + which = RANDOM_VALUE(9); + pnt_p = NULL; + + switch (which) { + + case 0: case 1: case 2: + pnt_p = free_p; + if (use_malloc_b) { + pnt_p->pi_pnt = malloc(amount); + } + else { + pnt_p->pi_pnt = mpool_alloc(pool, amount, &ret); + } + + if (verbose_b) { + (void)printf("%d: malloc %ld (max %ld) into slot %d. got %#lx\n", + iter_c + 1, amount, max, pnt_p - pointer_grid, + (long)pnt_p->pi_pnt); + } + + if (pnt_p->pi_pnt == NULL) { + (void)printf("malloc of %ld failed: %s\n", + amount, + (use_malloc_b ? strerror(errno) : mpool_strerror(ret))); + } + pnt_p->pi_size = amount; + break; + + case 3: case 4: case 5: + pnt_p = free_p; + if (use_malloc_b) { + pnt_p->pi_pnt = calloc(amount, sizeof(char)); + } + else { + pnt_p->pi_pnt = mpool_calloc(pool, amount, sizeof(char), &ret); + } + + if (verbose_b) { + (void)printf("%d: calloc %ld (max %ld) into slot %d. got %#lx\n", + iter_c + 1, amount, max, pnt_p - pointer_grid, + (long)pnt_p->pi_pnt); + } + + /* test the returned block to make sure that is has been cleared */ + if (pnt_p->pi_pnt == NULL) { + (void)printf("calloc of %ld failed: %s\n", + amount, + (use_malloc_b ? strerror(errno) : mpool_strerror(ret))); + } + else { + for (chunk_p = pnt_p->pi_pnt; + chunk_p < (char *)pnt_p->pi_pnt + amount; + chunk_p++) { + if (*chunk_p != '\0') { + (void)printf("calloc of %ld not zeroed on iteration #%d\n", + amount, iter_c + 1); + break; + } + } + pnt_p->pi_size = amount; + } + break; + + case 6: case 7: case 8: + if (free_c == max_pointers) { + continue; + } + + which = RANDOM_VALUE(max_pointers - free_c); + for (pnt_p = used_p; which > 0; which--) { + pnt_p = pnt_p->pi_next; + } + + if (use_malloc_b) { + new_pnt = realloc(pnt_p->pi_pnt, amount); + } + else { + new_pnt = mpool_resize(pool, pnt_p->pi_pnt, pnt_p->pi_size, amount, + &ret); + } + + if (verbose_b) { + (void)printf("%d: resize %#lx from %ld to %ld (max %ld) slot %d. " + "got %#lx\n", + iter_c + 1, (long)pnt_p->pi_pnt, pnt_p->pi_size, amount, + max, pnt_p - pointer_grid, (long)new_pnt); + } + + if (new_pnt == NULL) { + (void)printf("resize of %#lx old size %ld new size %ld failed: %s\n", + (long)pnt_p->pi_pnt, pnt_p->pi_size, amount, + (use_malloc_b ? strerror(errno) : mpool_strerror(ret))); + pnt_p->pi_pnt = NULL; + pnt_p->pi_size = 0; + } + else { + /* we effectively freed the old memory */ + max += pnt_p->pi_size; + pnt_p->pi_pnt = new_pnt; + pnt_p->pi_size = amount; + } + break; + + default: + break; + } + + if (pnt_p != NULL && pnt_p->pi_pnt != NULL) { + if (pnt_p == free_p) { + free_p = pnt_p->pi_next; + pnt_p->pi_next = used_p; + used_p = pnt_p; + free_c--; + } + + max -= amount; + iter_c++; + } + continue; + } + + /* + * choose a rand slot to free and make sure it is not a free-slot + */ + which = RANDOM_VALUE(max_pointers - free_c); + /* find pnt in the used list */ + last_p = NULL; + for (pnt_p = used_p, last_p = NULL; + pnt_p != NULL && which > 0; + last_p = pnt_p, pnt_p = pnt_p->pi_next, which--) { + } + if (pnt_p == NULL) { + /* huh? error here */ + abort(); + } + if (last_p == NULL) { + used_p = pnt_p->pi_next; + } + else { + last_p->pi_next = pnt_p->pi_next; + } + + if (use_malloc_b) { + free(pnt_p->pi_pnt); + } + else { + ret = mpool_free(pool, pnt_p->pi_pnt, pnt_p->pi_size); + if (ret != MPOOL_ERROR_NONE) { + (void)printf("free error on pointer '%#lx' of size %ld: %s\n", + (long)pnt_p->pi_pnt, pnt_p->pi_size, + mpool_strerror(ret)); + } + } + + if (verbose_b) { + (void)printf("%d: free'd %ld bytes from slot %d (%#lx)\n", + iter_c + 1, pnt_p->pi_size, pnt_p - pointer_grid, + (long)pnt_p->pi_pnt); + } + + pnt_p->pi_pnt = NULL; + pnt_p->pi_next = free_p; + free_p = pnt_p; + free_c++; + + max += pnt_p->pi_size; + iter_c++; + } + + /* free used pointers */ + for (pnt_p = pointer_grid; pnt_p < pointer_grid + max_pointers; pnt_p++) { + if (pnt_p->pi_pnt != NULL) { + if (use_malloc_b) { + free(pnt_p->pi_pnt); + } + else { + ret = mpool_free(pool, pnt_p->pi_pnt, pnt_p->pi_size); + if (ret != MPOOL_ERROR_NONE) { + (void)printf("free error on pointer '%#lx' of size %ld: %s\n", + (long)pnt_p->pi_pnt, pnt_p->pi_size, + mpool_strerror(ret)); + } + } + } + } + + if (use_malloc_b) { + free(pointer_grid); + } + else { + ret = mpool_free(pool, pointer_grid, sizeof(pnt_info_t) * max_pointers); + if (ret != MPOOL_ERROR_NONE) { + (void)printf("free error on grid pointer: %s\n", mpool_strerror(ret)); + } + } +} + +/* + * static void do_interactive + * + * DESCRIPTION: + * + * Run the interactive section of the program. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * pool <-> Out memory pool. + */ +static void do_interactive(mpool_t *pool) +{ + int len, ret; + char line[128], *line_p; + void *pnt, *new_pnt; + + (void)printf("Mpool test program. Type 'help' for assistance.\n"); + + for (;;) { + (void)printf("> "); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + line_p = strchr(line, '\n'); + if (line_p != NULL) { + *line_p = '\0'; + } + + len = strlen(line); + if (len == 0) { + continue; + } + + if (strncmp(line, "?", len) == 0 + || strncmp(line, "help", len) == 0) { + (void)printf("\thelp - print this message\n\n"); + + (void)printf("\tmalloc - allocate memory\n"); + (void)printf("\tcalloc - allocate/clear memory\n"); + (void)printf("\tresize - resize memory\n"); + (void)printf("\tfree - deallocate memory\n\n"); + + (void)printf("\tclear - clear the pool\n"); + (void)printf("\toverwrite - overwrite some memory to test errors\n"); + (void)printf("\trandom - randomly execute a number of [de] allocs\n"); + + (void)printf("\tquit - quit this test program\n"); + continue; + } + + if (strncmp(line, "quit", len) == 0) { + break; + } + + if (strncmp(line, "malloc", len) == 0) { + int size; + + (void)printf("How much to malloc: "); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + size = atoi(line); + pnt = mpool_alloc(pool, size, &ret); + if (pnt == NULL) { + (void)printf("malloc(%d) failed: %s\n", size, mpool_strerror(ret)); + } + else { + (void)printf("malloc(%d) returned '%#lx'\n", size, (long)pnt); + } + continue; + } + + if (strncmp(line, "calloc", len) == 0) { + int size; + + (void)printf("How much to calloc: "); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + size = atoi(line); + pnt = mpool_calloc(pool, size, sizeof(char), &ret); + if (pnt == NULL) { + (void)printf("calloc(%d) failed: %s\n", size, mpool_strerror(ret)); + } + else { + (void)printf("calloc(%d) returned '%#lx'\n", size, (long)pnt); + } + continue; + } + + if (strncmp(line, "resize", len) == 0) { + int size, old_size; + + pnt = get_address(); + + (void)printf("Old size of allocation: "); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + old_size = atoi(line); + (void)printf("New size of allocation: "); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + size = atoi(line); + + new_pnt = mpool_resize(pool, pnt, old_size, size, &ret); + if (new_pnt == NULL) { + (void)printf("resize(%#lx, %d) failed: %s\n", + (long)pnt, size, mpool_strerror(ret)); + } + else { + (void)printf("resize(%#lx, %d) returned '%#lx'\n", + (long)pnt, size, (long)new_pnt); + } + continue; + } + + if (strncmp(line, "free", len) == 0) { + int old_size; + + pnt = get_address(); + + (void)printf("Old minimum size we are freeing: "); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + old_size = atoi(line); + ret = mpool_free(pool, pnt, old_size); + if (ret != MPOOL_ERROR_NONE) { + (void)fprintf(stderr, "free failed: %s\n", mpool_strerror(ret)); + } + continue; + } + + if (strncmp(line, "clear", len) == 0) { + ret = mpool_clear(pool); + if (ret == MPOOL_ERROR_NONE) { + (void)fprintf(stderr, "clear succeeded\n"); + } + else { + (void)fprintf(stderr, "clear failed: %s\n", mpool_strerror(ret)); + } + continue; + } + + if (strncmp(line, "overwrite", len) == 0) { + char *overwrite = "OVERWRITTEN"; + + pnt = get_address(); + memcpy((char *)pnt, overwrite, strlen(overwrite)); + (void)printf("Done.\n"); + continue; + } + + /* do random heap hits */ + if (strncmp(line, "random", len) == 0) { + int iter_n; + + (void)printf("How many iterations[%d]: ", default_iter_n); + if (fgets(line, sizeof(line), stdin) == NULL) { + break; + } + if (line[0] == '\0' || line[0] == '\n') { + iter_n = default_iter_n; + } + else { + iter_n = atoi(line); + } + + do_random(pool, iter_n); + continue; + } + + (void)printf("Unknown command '%s'. Type 'help' for assistance.\n", line); + } +} + +/* + * static void log_func + * + * DESCRIPTION: + * + * Mpool transaction log function. + * + * RETURNS: + * + * None. + * + * ARGUMENT: + * + * mp_p -> Associated mpool address. + * + * func_id -> Integer function ID which identifies which mpool + * function is being called. + * + * byte_size -> Optionally specified byte size. + * + * ele_n -> Optionally specified element number. For mpool_calloc + * only. + * + * new_addr -> Optionally specified new address. For mpool_alloc, + * mpool_calloc, and mpool_resize only. + * + * old_addr -> Optionally specified old address. For mpool_resize and + * mpool_free only. + * + * old_byte_size -> Optionally specified old byte size. For + * mpool_resize only. + */ +static void log_func(const void *mp_p, const int func_id, + const unsigned long byte_size, + const unsigned long ele_n, + const void *new_addr, const void *old_addr, + const unsigned long old_byte_size) +{ + (void)printf("mp %#lx ", (long)mp_p); + + switch (func_id) { + + case MPOOL_FUNC_CLOSE: + (void)printf("close\n"); + break; + + case MPOOL_FUNC_CLEAR: + (void)printf("clear\n"); + break; + + case MPOOL_FUNC_ALLOC: + (void)printf("alloc %lu bytes got %#lx\n", + byte_size, (long)new_addr); + break; + + case MPOOL_FUNC_CALLOC: + (void)printf("calloc %lu ele size, %lu ele number, got %#lx\n", + byte_size, ele_n, (long)new_addr); + break; + + case MPOOL_FUNC_FREE: + (void)printf("free %#lx of %lu bytes\n", (long)old_addr, byte_size); + break; + + case MPOOL_FUNC_RESIZE: + (void)printf("resize %#lx of %lu bytes to %lu bytes, got %#lx\n", + (long)old_addr, old_byte_size, byte_size, + (long)new_addr); + break; + + default: + (void)printf("unknown function %d, %lu bytes\n", func_id, byte_size); + break; + } +} + +/* + * static void usage + * + * DESCRIPTION: + * + * Print a usage message. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * None. + */ +static void usage(void) +{ + (void)fprintf(stderr, + "Usage: mpool_t [-bhHilMnsv] [-m size] [-p number] " + "[-P size] [-S seed] [-t times]\n"); + (void)fprintf(stderr, + " -b set MPOOL_FLAG_BEST_FIT\n" + " -h set MPOOL_FLAG_NO_FREE\n" + " -H use system heap not mpool\n" + " -i turn on interactive mode\n" + " -l log memory transactions\n" + " -M max number pages in mpool\n" + " -n set MPOOL_FLAG_NO_FREE\n" + " -s use sbrk instead of mmap\n" + " -v enable verbose messages\n" + " -m size maximum allocation to test\n" + " -p max-pnts number of pointers to test\n" + " -S seed-rand seed for random function\n" + " -t interations number of iterations to run\n"); + exit(1); +} + +/* + * static void process_args + * + * DESCRIPTION: + * + * Process our arguments. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * None. + */ +static void process_args(int argc, char ** argv) +{ + argc--, argv++; + + /* process the args */ + for (; *argv != NULL; argv++, argc--) { + if (**argv != '-') { + continue; + } + + switch (*(*argv + 1)) { + + case 'b': + best_fit_b = 1; + break; + case 'h': + heavy_pack_b = 1; + break; + case 'H': + use_malloc_b = 1; + break; + case 'i': + interactive_b = 1; + break; + case 'l': + log_trxn_b = 1; + break; + case 'm': + argv++, argc--; + if (argc <= 0) { + usage(); + } + max_alloc = atoi(*argv); + break; + case 'M': + max_pages_n = 1; + break; + case 'n': + no_free_b = 1; + break; + case 'p': + argv++, argc--; + if (argc <= 0) { + usage(); + } + max_pointers = atoi(*argv); + break; + case 'P': + argv++, argc--; + if (argc <= 0) { + usage(); + } + page_size = atoi(*argv); + break; + case 'S': + argv++, argc--; + if (argc <= 0) { + usage(); + } + seed_random = atoi(*argv); + break; + case 't': + argv++, argc--; + if (argc <= 0) { + usage(); + } + default_iter_n = atoi(*argv); + break; + case 'v': + verbose_b = 1; + break; + default: + usage(); + break; + } + } +} + +/* + * Main routine + */ +int main(int argc, char **argv) +{ + int ret; + unsigned int flags = 0, pool_page_size; + unsigned long num_alloced, user_alloced, max_alloced, tot_alloced; + mpool_t *pool; + + process_args(argc, argv); + + /* repeat until we get a non 0 seed */ + while (seed_random == 0) { + seed_random = time(0) ^ getpid(); + } + (void)srandom(seed_random); + + (void)printf("Random seed is %u\n", seed_random); + + if (best_fit_b) { + flags |= MPOOL_FLAG_BEST_FIT; + } + if (heavy_pack_b) { + flags |= MPOOL_FLAG_HEAVY_PACKING; + } + if (no_free_b) { + flags |= MPOOL_FLAG_NO_FREE; + } + if (use_sbrk_b) { + flags |= MPOOL_FLAG_USE_SBRK; + } + + /* open our memory pool */ + pool = mpool_open(flags, page_size, NULL, &ret); + if (pool == NULL) { + (void)fprintf(stderr, "Error in mpool_open: %s\n", mpool_strerror(ret)); + exit(1); + } + + /* are we logging transactions */ + if (log_trxn_b) { + ret = mpool_set_log_func(pool, log_func); + if (ret != MPOOL_ERROR_NONE) { + (void)fprintf(stderr, "Error in mpool_set_log_func: %s\n", + mpool_strerror(ret)); + } + } + + if (max_pages_n > 0) { + ret = mpool_set_max_pages(pool, max_pages_n); + if (ret != MPOOL_ERROR_NONE) { + (void)fprintf(stderr, "Error in mpool_set_max_pages: %s\n", + mpool_strerror(ret)); + } + } + + if (interactive_b) { + do_interactive(pool); + } + else { + (void)printf("Running %d tests (use -i for interactive)...\n", + default_iter_n); + (void)fflush(stdout); + + do_random(pool, default_iter_n); + } + + /* get stats from the pool */ + ret = mpool_stats(pool, &pool_page_size, &num_alloced, &user_alloced, + &max_alloced, &tot_alloced); + if (ret == MPOOL_ERROR_NONE) { + (void)printf("Pool page size = %d. Number active allocated = %lu\n", + pool_page_size, num_alloced); + (void)printf("User bytes allocated = %lu. Max space allocated = %lu\n", + user_alloced, max_alloced); + (void)printf("Total space allocated = %lu\n", tot_alloced); + } + else { + (void)fprintf(stderr, "Error in mpool_stats: %s\n", mpool_strerror(ret)); + } + + /* close the pool */ + ret = mpool_close(pool); + if (ret != MPOOL_ERROR_NONE) { + (void)fprintf(stderr, "Error in mpool_close: %s\n", mpool_strerror(ret)); + exit(1); + } + + exit(0); +} diff --git a/src/database.cc b/src/database.cc index 289604c..f271f37 100644 --- a/src/database.cc +++ b/src/database.cc @@ -40,8 +40,6 @@ void Database::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareAndStep", PrepareAndStep); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "changes", Changes); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "lastInsertRowid", LastInsertRowid); target->Set(v8::String::NewSymbol("Database"), constructor_template->GetFunction()); @@ -140,14 +138,6 @@ Handle Database::Open(const Arguments& args) { return Undefined(); } -// // TODO: libeio'fy -// Handle Changes(const Arguments& args) { -// HandleScope scope; -// Database* dbo = ObjectWrap::Unwrap(args.This()); -// Local result = Integer::New(sqlite3_changes(dbo->db_)); -// return scope.Close(result); -// } - int Database::EIO_AfterClose(eio_req *req) { ev_unref(EV_DEFAULT_UC); @@ -214,15 +204,8 @@ Handle Database::Close(const Arguments& args) { } // // TODO: libeio'fy -// Handle LastInsertRowid(const Arguments& args) { -// HandleScope scope; -// Database* dbo = ObjectWrap::Unwrap(args.This()); -// Local result = Integer::New(sqlite3_last_insert_rowid(dbo->db_)); -// return scope.Close(result); -// }; - - // Hooks +// Hooks // static int CommitHook(void* v_this) { // HandleScope scope; // Database* db = static_cast(v_this); diff --git a/src/statement.h b/src/statement.h index c3509e4..0458666 100644 --- a/src/statement.h +++ b/src/statement.h @@ -51,22 +51,28 @@ class Statement : public EventEmitter { if (column_data_) FreeColumnData(); } - static Handle Bind(const Arguments& args); - static Handle BindObject(const Arguments& args); - static Handle BindArray(const Arguments& args); + static Handle Bind(const Arguments &args); + static Handle BindObject(const Arguments &args); + static Handle BindArray(const Arguments &args); static int EIO_BindArray(eio_req *req); static int EIO_AfterBindArray(eio_req *req); static int EIO_AfterFinalize(eio_req *req); static int EIO_Finalize(eio_req *req); - static Handle Finalize(const Arguments& args); + static Handle Finalize(const Arguments &args); - static Handle Reset(const Arguments& args); - static Handle ClearBindings(const Arguments& args); + static Handle Reset(const Arguments &args); + static Handle ClearBindings(const Arguments &args); static int EIO_AfterStep(eio_req *req); static int EIO_Step(eio_req *req); - static Handle Step(const Arguments& args); + static Handle Step(const Arguments &args); + + static int EIO_AfterFetchAll(eio_req *req); + static int EIO_FetchAll(eio_req *req); + static Handle FetchAll(const Arguments &args); + + void InitializeColumns(void); void FreeColumnData(void); bool HasCallback(); diff --git a/tests/old/speedtest-low-parallel.js b/tests/old/speedtest-low-parallel.js index 654b505..ebdf057 100644 --- a/tests/old/speedtest-low-parallel.js +++ b/tests/old/speedtest-low-parallel.js @@ -24,6 +24,7 @@ function getRows() { statement.finalize(function () { db.close(function () {}); }); d = ((new Date)-t0)/1000; puts("**** " + d + "s to fetch " + rows + " rows (" + (rows/d) + "/s)"); + getRowsFetchAll(); return; } rows++; @@ -34,6 +35,20 @@ function getRows() { }); } +function getRowsFetchAll() { + db.prepare("SELECT * FROM t1", function (error, statement) { + if (error) throw error; + t0 = new Date(); + + statement.fetchAll(function (error, rows) { + d = ((new Date)-t0)/1000; + puts("**** " + d + "s to fetchAll " + rows.length + " rows (" + (rows.length/d) + "/s)"); + statement.finalize(function () { db.close(function () {}); }); + return; + }); + }); +} + function createTable(db, callback) { db.prepareAndStep("CREATE TABLE t1 (id INTEGER PRIMARY KEY, alpha INTEGER)", function (error, statement) { if (error) throw error; diff --git a/tests/test-statement-fetchall.js b/tests/test-statement-fetchall.js new file mode 100644 index 0000000..702e946 --- /dev/null +++ b/tests/test-statement-fetchall.js @@ -0,0 +1,120 @@ +sys = require('sys'); +fs = require('fs'); +path = require('path'); + +TestSuite = require('async-testing/async_testing').TestSuite; +sqlite = require('sqlite3_bindings'); + +puts = sys.puts; +inspect = sys.inspect; + +var name = "Fetching all results"; +var suite = exports[name] = new TestSuite(name); + +function createTestTable(db, callback) { + db.prepare('CREATE TABLE table1 (id INTEGER, name TEXT, age FLOAT)', + function (error, createStatement) { + if (error) throw error; + createStatement.step(function (error, row) { + if (error) throw error; + callback(); + }); + }); +} + +var testRows = [ [ 1, "foo", 9 ] + , [ 2, "bar", 8 ] + , [ 3, "baz", 7 ] + , [ 4, "quux", 6 ] + , [ 5, "juju", 5 ] + ]; + +var testRowsExpected = [ { id: 5, name: 'juju', age: 5 } + , { id: 4, name: 'quux', age: 6 } + , { id: 3, name: 'baz', age: 7 } + , { id: 2, name: 'bar', age: 8 } + , { id: 1, name: 'foo', age: 9 } + ]; + +var tests = [ + { 'insert a row with lastinsertedid': + function (assert, finished) { + var self = this; + + self.db.open(':memory:', function (error) { + function selectStatementPrepared(error, statement) { + if (error) throw error; + statement.fetchAll(function (error, rows) { + if (error) throw error; + + assert.deepEqual(testRowsExpected, rows); + + self.db.close(function () { + finished(); + }); + }); + } + + createTestTable(self.db, + function () { + function insertRows(db, rows, callback) { + var i = rows.length; + db.prepare('INSERT INTO table1 (id, name, age) VALUES (?, ?, ?)', + function (error, statement) { + function doStep(i) { + statement.bindArray(rows[i], function () { + statement.step(function (error, row) { + if (error) throw error; + assert.ok(!row, "Row should be unset"); + statement.reset(); + if (i) { + doStep(--i); + } + else { + statement.finalize(function () { + callback(); + }); + } + }); + }); + } + + doStep(--i); + }); + } + + var selectSQL + = 'SELECT * from table1'; + + insertRows(self.db, testRows, function () { + self.db.prepare(selectSQL + , selectStatementPrepared); + }); + }); + }); + } + } +]; + +// order matters in our tests +for (var i=0,il=tests.length; i < il; i++) { + suite.addTests(tests[i]); +} + +var currentTest = 0; +var testCount = tests.length; + +suite.setup(function(finished, test) { + this.db = new sqlite.Database(); + finished(); +}); +suite.teardown(function(finished) { + if (this.db) this.db.close(function (error) { + finished(); + }); + ++currentTest == testCount; +}); + +if (module == require.main) { + suite.runTests(); +} diff --git a/wscript b/wscript index 942c1b6..a8b96cc 100644 --- a/wscript +++ b/wscript @@ -1,6 +1,6 @@ import Options from os import unlink, symlink -from os.path import exists +from os.path import exists, abspath srcdir = "." blddir = "build" @@ -20,12 +20,17 @@ def configure(conf): # conf.env.append_value('LIBPATH_PROFILER', '/usr/local/lib') # conf.env.append_value('LIB_PROFILER', 'profiler') + conf.env.append_value("LIBPATH_MPOOL", abspath("./deps/mpool-2.1.0/")) + conf.env.append_value("LIB_MPOOL", "mpool") + conf.env.append_value("CPPPATH_MPOOL", abspath("./deps/mpool-2.1.0/")) + + def build(bld): obj = bld.new_task_gen("cxx", "shlib", "node_addon") obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"] obj.target = "sqlite3_bindings" obj.source = "src/sqlite3_bindings.cc src/database.cc src/statement.cc" - obj.uselib = "SQLITE3 PROFILER" + obj.uselib = "SQLITE3 PROFILER MPOOL" t = 'sqlite3_bindings.node' def shutdown():