From: Rusty Russell Date: Mon, 2 Jun 2008 02:39:51 +0000 (+1000) Subject: Move modules to ccan/ tools to tools/ X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=650c775ff00cccd03fc84e7789a03c51d9839004 Move modules to ccan/ tools to tools/ Requires minor fixups. "depends" now prefixes ccan/ (allows for non-ccan deps later). --- diff --git a/Makefile b/Makefile index 76b79e33..b60874d7 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ # Hacky makefile to compile everything and run the tests in some kind of sane order. # V=--verbose for verbose tests. -CFLAGS=-O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. +CFLAGS=-O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -Iccan -I. -ALL=$(patsubst %/test, %, $(wildcard */test)) +ALL=$(patsubst ccan/%/test, ccan/%, $(wildcard ccan/*/test)) ALL_DEPENDS=$(patsubst %, %/.depends, $(ALL)) test-all: $(ALL_DEPENDS) @@ -16,13 +16,13 @@ distclean: clean $(ALL_DEPENDS): %/.depends: %/_info @$< depends > $@ || ( rm -f $@; exit 1 ) -test-%: ccan_tools/run_tests +test-ccan/%: tools/run_tests @echo Testing $*... - @if ccan_tools/run_tests $(V) $* | grep ^'not ok'; then exit 1; else exit 0; fi + @if tools/run_tests $(V) ccan/$* | grep ^'not ok'; then exit 1; else exit 0; fi -ccanlint: ccan_tools/ccanlint/ccanlint +ccanlint: tools/ccanlint/ccanlint -clean: ccan_tools-clean - rm -f `find . -name '*.o'` +clean: tools-clean + rm -f `find . -name '*.o'` `find . -name '.depends'` -include ccan_tools/Makefile +include tools/Makefile diff --git a/README b/README index 65bb8531..9019fbb2 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ -ccan_tools: +tools: This is currently a bootstrap junkyard for ccan tools. It is expected that some of this code, being generally useful, will be shuffled out to their own modules over time. -other: +ccan: The beginnings of a ccan repository. diff --git a/alignof/_info.c b/alignof/_info.c deleted file mode 100644 index b5afa025..00000000 --- a/alignof/_info.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include "config.h" - -/** - * alignof - ALIGNOF() macro to determine alignment of a type. - * - * Many platforms have requirements that certain types must be aligned - * to certain address boundaries, such as ints needing to be on 4-byte - * boundaries. Attempting to access variables with incorrect - * alignment may cause performance loss or even program failure (eg. a - * bus signal). - * - * There are times which it's useful to be able to programatically - * access these requirements, such as for dynamic allocators. - * - * Example: - * #include - * #include "alignof/alignoff.h" - * - * int main(int argc, char *argv[]) - * { - * char arr[sizeof(int)]; - * - * if ((unsigned long)arr % ALIGNOF(int)) { - * printf("arr %p CANNOT hold an int\n", arr); - * exit(1); - * } else { - * printf("arr %p CAN hold an int\n", arr); - * exit(0); - * } - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { - printf("build_assert\n"); - return 0; - } - - return 1; -} diff --git a/alignof/alignof.h b/alignof/alignof.h deleted file mode 100644 index d146e4cd..00000000 --- a/alignof/alignof.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CCAN_ALIGNOF_H -#define CCAN_ALIGNOF_H -#include "config.h" - -/** - * ALIGNOF - get the alignment of a type - * @t: the type to test - * - * This returns a safe alignment for the given type. - */ -#if HAVE_ALIGNOF -/* A GCC extension. */ -#define ALIGNOF(t) __alignof__(t) -#else -/* Alignment by measuring structure padding. */ -#define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) -#endif - -#endif /* CCAN_ALIGNOF_H */ diff --git a/alignof/test/run.c b/alignof/test/run.c deleted file mode 100644 index f76fb669..00000000 --- a/alignof/test/run.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include "alignof/alignof.h" - -/* Alignment is remarkably difficult to test. The rules may be more - * complex than ALIGNOF() can know: eg. on i386 __alignof__(double) == 8, but - * __alignof__(struct containing double) == 4. - * - * Technically, we can only test that we give *at least* the alignment which - * naturally occurs, and that accesses work. - * - * For the moment, we work around double. */ -struct lots_of_types -{ - char c; - short s; - char c2; - int i; - char c3; - float f; - char c4; - double d; - char c5; -}; - -int main(int argc, char *argv[]) -{ - struct lots_of_types lots_of_types, *lp = malloc(sizeof(*lp)); - char c; - short s; - char c2; - int i; - char c3; - float f; - char c4; - double d; - - /* Make sure we use all the variables. */ - c = 0; - c2 = c3 = c4 = c; - - plan_tests(15); - ok1((unsigned long)&c % ALIGNOF(char) == 0); - ok1((unsigned long)&s % ALIGNOF(short) == 0); - ok1((unsigned long)&i % ALIGNOF(int) == 0); - ok1((unsigned long)&f % ALIGNOF(float) == 0); - ok1((unsigned long)&d % ALIGNOF(double) == 0); - - ok1((unsigned long)&lots_of_types.c % ALIGNOF(char) == 0); - ok1((unsigned long)&lots_of_types.s % ALIGNOF(short) == 0); - ok1((unsigned long)&lots_of_types.i % ALIGNOF(int) == 0); - ok1((unsigned long)&lots_of_types.f % ALIGNOF(float) == 0); - ok1(offsetof(struct lots_of_types, d) % ALIGNOF(double) == 0); - - ok1((unsigned long)&lp->c % ALIGNOF(char) == 0); - ok1((unsigned long)&lp->s % ALIGNOF(short) == 0); - ok1((unsigned long)&lp->i % ALIGNOF(int) == 0); - ok1((unsigned long)&lp->f % ALIGNOF(float) == 0); - ok1((unsigned long)&lp->d % ALIGNOF(double) == 0); - exit(exit_status()); -} diff --git a/alloc/_info.c b/alloc/_info.c deleted file mode 100644 index 59a66380..00000000 --- a/alloc/_info.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include "config.h" - -/** - * alloc - memory allocator routines - * - * The alloc module implements a simple allocator which you can use to - * dynamically allocate space within a region of memory. This can be useful - * for suballocations within a given region, or a memory-mapped file. - * - * All metadata is kept within the memory handed to the allocator: you only - * need hand the pointer and the size of the memory to each call. - * - * The region contents is always in offsets, so it can be mapped in different - * places, but is not endian-safe. - * - * Example: - * #include - * #include - * #include - * #include - * #include "alloc/alloc.h" - * - * static void usage(const char *name) - * { - * errx(1, "Usage: %s --create \n" - * " %s --check \n" - * " %s --alloc \n" - * " %s --free= \n", name, name, name); - * } - * - * // Create a memory mapped file, and allocate from within it - * int main(int argc, char *argv[]) - * { - * void *a, *p; - * int fd; - * enum { CREATE, CHECK, ALLOC, FREE } cmd; - * - * if (argc != 3) - * usage(argv[0]); - * - * if (strcmp(argv[1], "--create") == 0) - * cmd = CREATE; - * else if (strcmp(argv[1], "--check") == 0) - * cmd = CHECK; - * else if (strcmp(argv[1], "--alloc") == 0) - * cmd = ALLOC; - * else if (strncmp(argv[1], "--free=", strlen("--free=")) == 0) - * cmd = FREE; - * else - * usage(argv[0]); - * - * if (cmd == CREATE) { - * fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0600); - * if (fd < 0) - * err(1, "Could not create %s", argv[2]); - * if (ftruncate(fd, 1048576) != 0) - * err(1, "Could not set length on %s", argv[2]); - * } else { - * fd = open(argv[2], O_RDWR); - * if (fd < 0) - * err(1, "Could not open %s", argv[2]); - * } - * - * a = mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0); - * if (a == MAP_FAILED) - * err(1, "Could not map %s", argv[2]); - * - * switch (cmd) { - * case CREATE: - * alloc_init(a, 1048576); - * break; - * case CHECK: - * if (!alloc_check(a, 1048576)) - * err(1, "Region is corrupt"); - * break; - * case ALLOC: - * p = alloc_get(a, 1048576, 1024, 16); - * if (!p) - * errx(1, "Could not allocate"); - * printf("%zu\n", (char *)p - (char *)a); - * break; - * case FREE: - * p = (char *)a + atol(argv[1] + strlen("--free=")); - * alloc_free(a, 1048576, p); - * break; - * } - * return 0; - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { - printf("build_assert\n"); - return 0; - } - - return 1; -} diff --git a/alloc/alloc.c b/alloc/alloc.c deleted file mode 100644 index f7a05e2d..00000000 --- a/alloc/alloc.c +++ /dev/null @@ -1,1119 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "alloc.h" -#include "build_assert/build_assert.h" -#include "alignof/alignof.h" -#include "config.h" - -/* FIXME: We assume getpagesize() doesnt change. Remapping file with - * different pagesize should still work. */ - -/* FIXME: Doesn't handle non-page-aligned poolsize. */ - -/* FIXME: Reduce. */ -#define MIN_SIZE (getpagesize() * 2) - -/* What's the granularity of sub-page allocs? */ -#define BITMAP_GRANULARITY 4 - -/* File layout: - * - * file := pagestates pad uniform-cache metadata - * pagestates := pages * 2-bits-per-page - * pad := pad to next ALIGNOF(metaheader) - * - * metadata := metalen next-ptr metabits - * metabits := freeblock | bitblock | uniformblock - * freeblock := FREE + - * bitblock := BITMAP + 2-bits-per-bit-in-page + pad-to-byte - * uniformblock := UNIFORM + 14-bit-byte-len + bits + pad-to-byte - */ -#define UNIFORM_CACHE_NUM 16 -struct uniform_cache -{ - uint16_t size[UNIFORM_CACHE_NUM]; - /* These could be u32 if we're prepared to limit size. */ - unsigned long page[UNIFORM_CACHE_NUM]; -}; - -struct metaheader -{ - /* Next meta header, or 0 */ - unsigned long next; - /* Bits start here. */ -}; - -/* Assumes a is a power of two. */ -static unsigned long align_up(unsigned long x, unsigned long a) -{ - return (x + a - 1) & ~(a - 1); -} - -static unsigned long align_down(unsigned long x, unsigned long a) -{ - return x & ~(a - 1); -} - -static unsigned long div_up(unsigned long x, unsigned long a) -{ - return (x + a - 1) / a; -} - -/* It turns out that we spend a lot of time dealing with bit pairs. - * These routines manipulate them. - */ -static uint8_t get_bit_pair(const uint8_t *bits, unsigned long index) -{ - return bits[index * 2 / CHAR_BIT] >> (index * 2 % CHAR_BIT) & 3; -} - -static void set_bit_pair(uint8_t *bits, unsigned long index, uint8_t val) -{ - bits[index * 2 / CHAR_BIT] &= ~(3 << (index * 2 % CHAR_BIT)); - bits[index * 2 / CHAR_BIT] |= (val << (index * 2 % CHAR_BIT)); -} - -/* This is used for page states and subpage allocations */ -enum alloc_state -{ - FREE, - TAKEN, - TAKEN_START, - SPECIAL, /* Sub-page allocation for page states. */ -}; - -/* The types for subpage metadata. */ -enum sub_metadata_type -{ - /* FREE is same as alloc state */ - BITMAP = 1, /* bitmap allocated page */ - UNIFORM, /* uniform size allocated page */ -}; - -/* Page states are represented by bitpairs, at the start of the pool. */ -#define BITS_PER_PAGE 2 - -/* How much metadata info per byte? */ -#define METADATA_PER_BYTE (CHAR_BIT / 2) - -static uint8_t *get_page_statebits(const void *pool) -{ - return (uint8_t *)pool + sizeof(struct uniform_cache); -} - -static enum alloc_state get_page_state(const void *pool, unsigned long page) -{ - return get_bit_pair(get_page_statebits(pool), page); -} - -static void set_page_state(void *pool, unsigned long page, enum alloc_state s) -{ - set_bit_pair(get_page_statebits(pool), page, s); -} - -/* The offset of metadata for a subpage allocation is found at the end - * of the subpage */ -#define SUBPAGE_METAOFF (getpagesize() - sizeof(unsigned long)) - -/* This is the length of metadata in bits. It consists of two bits - * for every BITMAP_GRANULARITY of usable bytes in the page, then two - * bits for the tailer.. */ -#define BITMAP_METABITLEN \ - ((div_up(SUBPAGE_METAOFF, BITMAP_GRANULARITY) + 1) * BITS_PER_PAGE) - -/* This is the length in bytes. */ -#define BITMAP_METALEN (div_up(BITMAP_METABITLEN, CHAR_BIT)) - -static struct metaheader *first_mheader(void *pool, unsigned long poolsize) -{ - unsigned int pagestatelen; - - pagestatelen = align_up(div_up(poolsize/getpagesize() * BITS_PER_PAGE, - CHAR_BIT), - ALIGNOF(struct metaheader)); - return (struct metaheader *)(get_page_statebits(pool) + pagestatelen); -} - -static struct metaheader *next_mheader(void *pool, struct metaheader *mh) -{ - if (!mh->next) - return NULL; - - return (struct metaheader *)((char *)pool + mh->next); -} - -static unsigned long pool_offset(void *pool, void *p) -{ - return (char *)p - (char *)pool; -} - -void alloc_init(void *pool, unsigned long poolsize) -{ - /* FIXME: Alignment assumptions about pool. */ - unsigned long len, i; - struct metaheader *mh; - - if (poolsize < MIN_SIZE) - return; - - mh = first_mheader(pool, poolsize); - - /* Mark all page states FREE, all uniform caches zero, and all of - * metaheader bitmap which takes rest of first page. */ - len = align_up(pool_offset(pool, mh + 1), getpagesize()); - BUILD_ASSERT(FREE == 0); - memset(pool, 0, len); - - /* Mark the pagestate and metadata page(s) allocated. */ - set_page_state(pool, 0, TAKEN_START); - for (i = 1; i < div_up(len, getpagesize()); i++) - set_page_state(pool, i, TAKEN); -} - -/* Two bits per element, representing page states. Returns 0 on fail. - * off is used to allocate from subpage bitmaps, which use the first 2 - * bits as the type, so the real bitmap is offset by 1. */ -static unsigned long alloc_from_bitmap(uint8_t *bits, unsigned long off, - unsigned long elems, - unsigned long want, unsigned long align) -{ - long i; - unsigned long free; - - free = 0; - /* We allocate from far end, to increase ability to expand metadata. */ - for (i = elems - 1; i >= 0; i--) { - switch (get_bit_pair(bits, off+i)) { - case FREE: - if (++free >= want) { - unsigned long j; - - /* They might ask for large alignment. */ - if (align && i % align) - continue; - - set_bit_pair(bits, off+i, TAKEN_START); - for (j = i+1; j < i + want; j++) - set_bit_pair(bits, off+j, TAKEN); - return off+i; - } - break; - case SPECIAL: - case TAKEN_START: - case TAKEN: - free = 0; - break; - } - } - - return 0; -} - -static unsigned long alloc_get_pages(void *pool, unsigned long poolsize, - unsigned long pages, unsigned long align) -{ - return alloc_from_bitmap(get_page_statebits(pool), - 0, poolsize / getpagesize(), pages, - align / getpagesize()); -} - -/* Offset to metadata is at end of page. */ -static unsigned long *metadata_off(void *pool, unsigned long page) -{ - return (unsigned long *) - ((char *)pool + (page+1)*getpagesize() - sizeof(unsigned long)); -} - -static uint8_t *get_page_metadata(void *pool, unsigned long page) -{ - return (uint8_t *)pool + *metadata_off(pool, page); -} - -static void set_page_metadata(void *pool, unsigned long page, uint8_t *meta) -{ - *metadata_off(pool, page) = meta - (uint8_t *)pool; -} - -static unsigned long sub_page_alloc(void *pool, unsigned long page, - unsigned long size, unsigned long align) -{ - uint8_t *bits = get_page_metadata(pool, page); - unsigned long i; - enum sub_metadata_type type; - - type = get_bit_pair(bits, 0); - - /* If this is a uniform page, we can't allocate from it. */ - if (type == UNIFORM) - return 0; - - assert(type == BITMAP); - - /* We use a standart bitmap, but offset because of that BITMAP - * header. */ - i = alloc_from_bitmap(bits, 1, SUBPAGE_METAOFF/BITMAP_GRANULARITY, - div_up(size, BITMAP_GRANULARITY), - align / BITMAP_GRANULARITY); - - /* Can't allocate? */ - if (i == 0) - return 0; - - /* i-1 because of the header. */ - return page*getpagesize() + (i-1)*BITMAP_GRANULARITY; -} - -/* We look at the page states to figure out where the allocation for this - * metadata ends. */ -static unsigned long get_metalen(void *pool, unsigned long poolsize, - struct metaheader *mh) -{ - unsigned long i, first, pages = poolsize / getpagesize(); - - first = pool_offset(pool, mh + 1)/getpagesize(); - - for (i = first + 1; i < pages && get_page_state(pool,i) == TAKEN; i++); - - return i * getpagesize() - pool_offset(pool, mh + 1); -} - -static unsigned int uniform_metalen(unsigned int usize) -{ - unsigned int metalen; - - assert(usize < (1 << 14)); - - /* Two bits for the header, 14 bits for size, then one bit for each - * element the page can hold. Round up to number of bytes. */ - metalen = div_up(2 + 14 + SUBPAGE_METAOFF / usize, CHAR_BIT); - - /* To ensure metaheader is always aligned, round bytes up. */ - metalen = align_up(metalen, ALIGNOF(struct metaheader)); - - return metalen; -} - -static unsigned int decode_usize(uint8_t *meta) -{ - return ((unsigned)meta[1] << (CHAR_BIT-2)) | (meta[0] >> 2); -} - -static void encode_usize(uint8_t *meta, unsigned int usize) -{ - meta[0] = (UNIFORM | (usize << 2)); - meta[1] = (usize >> (CHAR_BIT - 2)); -} - -static uint8_t *alloc_metaspace(void *pool, unsigned long poolsize, - struct metaheader *mh, unsigned long bytes, - enum sub_metadata_type type) -{ - uint8_t *meta = (uint8_t *)(mh + 1); - unsigned long free = 0, len, i, metalen; - - metalen = get_metalen(pool, poolsize, mh); - - /* Walk through metadata looking for free. */ - for (i = 0; i < metalen * METADATA_PER_BYTE; i += len) { - switch (get_bit_pair(meta, i)) { - case FREE: - len = 1; - free++; - if (free == bytes * METADATA_PER_BYTE) { - /* Mark this as a bitmap. */ - set_bit_pair(meta, i - free + 1, type); - return meta + (i - free + 1)/METADATA_PER_BYTE; - } - break; - case BITMAP: - /* Skip over this allocated part. */ - len = BITMAP_METALEN * METADATA_PER_BYTE; - free = 0; - break; - case UNIFORM: - /* Figure metalen given usize. */ - len = decode_usize(meta + i / METADATA_PER_BYTE); - len = uniform_metalen(len) * METADATA_PER_BYTE; - free = 0; - break; - default: - assert(0); - return NULL; - } - } - return NULL; -} - -/* We need this many bytes of metadata. */ -static uint8_t *new_metadata(void *pool, unsigned long poolsize, - unsigned long bytes, enum sub_metadata_type type) -{ - struct metaheader *mh, *newmh; - unsigned long page; - uint8_t *meta; - - for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)) - if ((meta = alloc_metaspace(pool, poolsize, mh, bytes, type))) - return meta; - - /* No room for metadata? Can we expand an existing one? */ - for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ - unsigned long nextpage; - - /* We start on this page. */ - nextpage = pool_offset(pool, (char *)(mh+1))/getpagesize(); - /* Iterate through any other pages we own. */ - while (get_page_state(pool, ++nextpage) == TAKEN); - - /* Now, can we grab that page? */ - if (get_page_state(pool, nextpage) != FREE) - continue; - - /* OK, expand metadata, do it again. */ - set_page_state(pool, nextpage, TAKEN); - BUILD_ASSERT(FREE == 0); - memset((char *)pool + nextpage*getpagesize(), 0, getpagesize()); - return alloc_metaspace(pool, poolsize, mh, bytes, type); - } - - /* No metadata left at all? */ - page = alloc_get_pages(pool, poolsize, div_up(bytes, getpagesize()), 1); - if (!page) - return NULL; - - newmh = (struct metaheader *)((char *)pool + page * getpagesize()); - BUILD_ASSERT(FREE == 0); - memset(newmh + 1, 0, getpagesize() - sizeof(*mh)); - - /* Sew it into linked list */ - mh = first_mheader(pool,poolsize); - newmh->next = mh->next; - mh->next = pool_offset(pool, newmh); - - return alloc_metaspace(pool, poolsize, newmh, bytes, type); -} - -static void alloc_free_pages(void *pool, unsigned long pagenum) -{ - assert(get_page_state(pool, pagenum) == TAKEN_START); - set_page_state(pool, pagenum, FREE); - while (get_page_state(pool, ++pagenum) == TAKEN) - set_page_state(pool, pagenum, FREE); -} - -static void maybe_transform_uniform_page(void *pool, unsigned long offset) -{ - /* FIXME: If possible and page isn't full, change to a bitmap */ -} - -/* Returns 0 or the size of the uniform alloc to use */ -static unsigned long suitable_for_uc(unsigned long size, unsigned long align) -{ - unsigned long num_elems, wastage, usize; - unsigned long bitmap_cost; - - if (size == 0) - size = 1; - - /* Fix up silly alignments. */ - usize = align_up(size, align); - - /* How many can fit in this page? */ - num_elems = SUBPAGE_METAOFF / usize; - - /* Can happen with bigger alignments. */ - if (!num_elems) - return 0; - - /* Usize maxes out at 14 bits. */ - if (usize >= (1 << 14)) - return 0; - - /* How many bytes would be left at the end? */ - wastage = SUBPAGE_METAOFF % usize; - - /* If we can get a larger allocation within alignment constraints, we - * should do it, otherwise might as well leave wastage at the end. */ - usize += align_down(wastage / num_elems, align); - - /* Bitmap allocation costs 2 bits per BITMAP_GRANULARITY bytes, plus - * however much we waste in rounding up to BITMAP_GRANULARITY. */ - bitmap_cost = 2 * div_up(size, BITMAP_GRANULARITY) - + CHAR_BIT * (align_up(size, BITMAP_GRANULARITY) - size); - - /* Our cost is 1 bit, plus usize overhead */ - if (bitmap_cost < 1 + (usize - size) * CHAR_BIT) - return 0; - - return usize; -} - -static unsigned long uniform_alloc(void *pool, unsigned long poolsize, - struct uniform_cache *uc, - unsigned long ucnum) -{ - uint8_t *metadata = get_page_metadata(pool, uc->page[ucnum]) + 2; - unsigned long i, max; - - /* Simple one-bit-per-object bitmap. */ - max = SUBPAGE_METAOFF / uc->size[ucnum]; - for (i = 0; i < max; i++) { - if (!(metadata[i / CHAR_BIT] & (1 << (i % CHAR_BIT)))) { - metadata[i / CHAR_BIT] |= (1 << (i % CHAR_BIT)); - return uc->page[ucnum] * getpagesize() - + i * uc->size[ucnum]; - } - } - - return 0; -} - -static unsigned long new_uniform_page(void *pool, unsigned long poolsize, - unsigned long usize) -{ - unsigned long page, metalen; - uint8_t *metadata; - - page = alloc_get_pages(pool, poolsize, 1, 1); - if (page == 0) - return 0; - - metalen = uniform_metalen(usize); - - /* Get metadata for page. */ - metadata = new_metadata(pool, poolsize, metalen, UNIFORM); - if (!metadata) { - alloc_free_pages(pool, page); - return 0; - } - - encode_usize(metadata, usize); - - BUILD_ASSERT(FREE == 0); - memset(metadata + 2, 0, metalen - 2); - - /* Actually, this is a subpage page now. */ - set_page_state(pool, page, SPECIAL); - - /* Set metadata pointer for page. */ - set_page_metadata(pool, page, metadata); - - return page; -} - -static unsigned long alloc_sub_page(void *pool, unsigned long poolsize, - unsigned long size, unsigned long align) -{ - unsigned long i, usize; - uint8_t *metadata; - struct uniform_cache *uc = pool; - - usize = suitable_for_uc(size, align); - if (usize) { - /* Look for a uniform page. */ - for (i = 0; i < UNIFORM_CACHE_NUM; i++) { - if (uc->size[i] == usize) { - unsigned long ret; - ret = uniform_alloc(pool, poolsize, uc, i); - if (ret != 0) - return ret; - /* OK, that one is full, remove from cache. */ - uc->size[i] = 0; - break; - } - } - - /* OK, try a new uniform page. Use random discard for now. */ - i = random() % UNIFORM_CACHE_NUM; - maybe_transform_uniform_page(pool, uc->page[i]); - - uc->page[i] = new_uniform_page(pool, poolsize, usize); - if (uc->page[i]) { - uc->size[i] = usize; - return uniform_alloc(pool, poolsize, uc, i); - } - uc->size[i] = 0; - } - - /* Look for partial page. */ - for (i = 0; i < poolsize / getpagesize(); i++) { - unsigned long ret; - if (get_page_state(pool, i) != SPECIAL) - continue; - - ret = sub_page_alloc(pool, i, size, align); - if (ret) - return ret; - } - - /* Create new SUBPAGE page. */ - i = alloc_get_pages(pool, poolsize, 1, 1); - if (i == 0) - return 0; - - /* Get metadata for page. */ - metadata = new_metadata(pool, poolsize, BITMAP_METALEN, BITMAP); - if (!metadata) { - alloc_free_pages(pool, i); - return 0; - } - - /* Actually, this is a subpage page now. */ - set_page_state(pool, i, SPECIAL); - - /* Set metadata pointer for page. */ - set_page_metadata(pool, i, metadata); - - /* Do allocation like normal */ - return sub_page_alloc(pool, i, size, align); -} - -static bool bitmap_page_is_empty(uint8_t *meta) -{ - unsigned int i; - - /* Skip the header (first bit of metadata). */ - for (i = 1; i < SUBPAGE_METAOFF/BITMAP_GRANULARITY+1; i++) - if (get_bit_pair(meta, i) != FREE) - return false; - - return true; -} - -static bool uniform_page_is_empty(uint8_t *meta) -{ - unsigned int i, metalen; - - metalen = uniform_metalen(decode_usize(meta)); - - /* Skip the header (first two bytes of metadata). */ - for (i = 2; i < metalen + 2; i++) { - BUILD_ASSERT(FREE == 0); - if (meta[i]) - return false; - } - return true; -} - -static bool special_page_is_empty(void *pool, unsigned long page) -{ - uint8_t *meta; - enum sub_metadata_type type; - - meta = get_page_metadata(pool, page); - type = get_bit_pair(meta, 0); - - switch (type) { - case UNIFORM: - return uniform_page_is_empty(meta); - case BITMAP: - return bitmap_page_is_empty(meta); - default: - assert(0); - } -} - -static void clear_special_metadata(void *pool, unsigned long page) -{ - uint8_t *meta; - enum sub_metadata_type type; - - meta = get_page_metadata(pool, page); - type = get_bit_pair(meta, 0); - - switch (type) { - case UNIFORM: - /* First two bytes are the header, rest is already FREE */ - BUILD_ASSERT(FREE == 0); - memset(meta, 0, 2); - break; - case BITMAP: - /* First two bits is the header. */ - BUILD_ASSERT(BITMAP_METALEN > 1); - meta[0] = 0; - break; - default: - assert(0); - } -} - -/* Returns true if we cleaned any pages. */ -static bool clean_empty_subpages(void *pool, unsigned long poolsize) -{ - unsigned long i; - bool progress = false; - - for (i = 0; i < poolsize/getpagesize(); i++) { - if (get_page_state(pool, i) != SPECIAL) - continue; - - if (special_page_is_empty(pool, i)) { - clear_special_metadata(pool, i); - set_page_state(pool, i, FREE); - progress = true; - } - } - return progress; -} - -/* Returns true if we cleaned any pages. */ -static bool clean_metadata(void *pool, unsigned long poolsize) -{ - struct metaheader *mh, *prev_mh = NULL; - bool progress = false; - - for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ - uint8_t *meta; - long i; - unsigned long metalen = get_metalen(pool, poolsize, mh); - - meta = (uint8_t *)(mh + 1); - BUILD_ASSERT(FREE == 0); - for (i = metalen - 1; i > 0; i--) - if (meta[i] != 0) - break; - - /* Completely empty? */ - if (prev_mh && i == metalen) { - alloc_free_pages(pool, - pool_offset(pool, mh)/getpagesize()); - prev_mh->next = mh->next; - mh = prev_mh; - progress = true; - } else { - uint8_t *p; - - /* Some pages at end are free? */ - for (p = (uint8_t *)(mh+1) + metalen - getpagesize(); - p > meta + i; - p -= getpagesize()) { - set_page_state(pool, - pool_offset(pool, p) - / getpagesize(), - FREE); - progress = true; - } - } - } - - return progress; -} - -void *alloc_get(void *pool, unsigned long poolsize, - unsigned long size, unsigned long align) -{ - bool subpage_clean = false, metadata_clean = false; - unsigned long ret; - - if (poolsize < MIN_SIZE) - return NULL; - -again: - /* Sub-page allocations have an overhead of ~12%. */ - if (size + size/8 >= getpagesize() || align >= getpagesize()) { - unsigned long pages = div_up(size, getpagesize()); - - ret = alloc_get_pages(pool, poolsize, pages, align) - * getpagesize(); - } else - ret = alloc_sub_page(pool, poolsize, size, align); - - if (ret != 0) - return (char *)pool + ret; - - /* Allocation failed: garbage collection. */ - if (!subpage_clean) { - subpage_clean = true; - if (clean_empty_subpages(pool, poolsize)) - goto again; - } - - if (!metadata_clean) { - metadata_clean = true; - if (clean_metadata(pool, poolsize)) - goto again; - } - - /* FIXME: Compact metadata? */ - return NULL; -} - -static void bitmap_free(void *pool, unsigned long pagenum, unsigned long off, - uint8_t *metadata) -{ - assert(off % BITMAP_GRANULARITY == 0); - - off /= BITMAP_GRANULARITY; - - /* Offset by one because first bit is used for header. */ - off++; - - set_bit_pair(metadata, off++, FREE); - while (off < SUBPAGE_METAOFF / BITMAP_GRANULARITY - && get_bit_pair(metadata, off) == TAKEN) - set_bit_pair(metadata, off++, FREE); -} - -static void uniform_free(void *pool, unsigned long pagenum, unsigned long off, - uint8_t *metadata) -{ - unsigned int usize, bit; - - usize = decode_usize(metadata); - /* Must have been this size. */ - assert(off % usize == 0); - bit = off / usize; - - /* Skip header. */ - metadata += 2; - - /* Must have been allocated. */ - assert(metadata[bit / CHAR_BIT] & (1 << (bit % CHAR_BIT))); - metadata[bit / CHAR_BIT] &= ~(1 << (bit % CHAR_BIT)); -} - -static void subpage_free(void *pool, unsigned long pagenum, void *free) -{ - unsigned long off = (unsigned long)free % getpagesize(); - uint8_t *metadata = get_page_metadata(pool, pagenum); - enum sub_metadata_type type; - - type = get_bit_pair(metadata, 0); - - assert(off < SUBPAGE_METAOFF); - - switch (type) { - case BITMAP: - bitmap_free(pool, pagenum, off, metadata); - break; - case UNIFORM: - uniform_free(pool, pagenum, off, metadata); - break; - default: - assert(0); - } -} - -void alloc_free(void *pool, unsigned long poolsize, void *free) -{ - unsigned long pagenum; - struct metaheader *mh; - - if (!free) - return; - - assert(poolsize >= MIN_SIZE); - - mh = first_mheader(pool, poolsize); - assert((char *)free >= (char *)(mh + 1)); - assert((char *)pool + poolsize > (char *)free); - - pagenum = pool_offset(pool, free) / getpagesize(); - - if (get_page_state(pool, pagenum) == SPECIAL) - subpage_free(pool, pagenum, free); - else { - assert((unsigned long)free % getpagesize() == 0); - alloc_free_pages(pool, pagenum); - } -} - -static bool is_metadata_page(void *pool, unsigned long poolsize, - unsigned long page) -{ - struct metaheader *mh; - - for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ - unsigned long start, end; - - start = pool_offset(pool, mh); - end = pool_offset(pool, (char *)(mh+1) - + get_metalen(pool, poolsize, mh)); - if (page >= start/getpagesize() && page < end/getpagesize()) - return true; - } - return false; -} - -static bool check_bitmap_metadata(void *pool, unsigned long *mhoff) -{ - enum alloc_state last_state = FREE; - unsigned int i; - - for (i = 0; i < SUBPAGE_METAOFF / BITMAP_GRANULARITY; i++) { - enum alloc_state state; - - /* +1 because header is the first byte. */ - state = get_bit_pair((uint8_t *)pool + *mhoff, i+1); - switch (state) { - case SPECIAL: - return false; - case TAKEN: - if (last_state == FREE) - return false; - break; - default: - break; - } - last_state = state; - } - return true; -} - -static bool check_uniform_metadata(void *pool, unsigned long *mhoff) -{ - uint8_t *meta = (uint8_t *)pool + *mhoff; - unsigned int i, usize; - struct uniform_cache *uc = pool; - - usize = decode_usize(meta); - if (usize == 0 || suitable_for_uc(usize, 1) != usize) - return false; - - /* If it's in uniform cache, make sure that agrees on size. */ - for (i = 0; i < UNIFORM_CACHE_NUM; i++) { - uint8_t *ucm; - - if (!uc->size[i]) - continue; - - ucm = get_page_metadata(pool, uc->page[i]); - if (ucm != meta) - continue; - - if (usize != uc->size[i]) - return false; - } - return true; -} - -static bool check_subpage(void *pool, unsigned long poolsize, - unsigned long page) -{ - unsigned long *mhoff = metadata_off(pool, page); - - if (*mhoff + sizeof(struct metaheader) > poolsize) - return false; - - if (*mhoff % ALIGNOF(struct metaheader) != 0) - return false; - - /* It must point to a metadata page. */ - if (!is_metadata_page(pool, poolsize, *mhoff / getpagesize())) - return false; - - /* Header at start of subpage allocation */ - switch (get_bit_pair((uint8_t *)pool + *mhoff, 0)) { - case BITMAP: - return check_bitmap_metadata(pool, mhoff); - case UNIFORM: - return check_uniform_metadata(pool, mhoff); - default: - return false; - } - -} - -bool alloc_check(void *pool, unsigned long poolsize) -{ - unsigned long i; - struct metaheader *mh; - enum alloc_state last_state = FREE; - bool was_metadata = false; - - if (poolsize < MIN_SIZE) - return true; - - if (get_page_state(pool, 0) != TAKEN_START) - return false; - - /* First check metadata pages. */ - /* Metadata pages will be marked TAKEN. */ - for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ - unsigned long start, end; - - start = pool_offset(pool, mh); - if (start + sizeof(*mh) > poolsize) - return false; - - end = pool_offset(pool, (char *)(mh+1) - + get_metalen(pool, poolsize, mh)); - if (end > poolsize) - return false; - - /* Non-first pages should start on a page boundary. */ - if (mh != first_mheader(pool, poolsize) - && start % getpagesize() != 0) - return false; - - /* It should end on a page boundary. */ - if (end % getpagesize() != 0) - return false; - } - - for (i = 0; i < poolsize / getpagesize(); i++) { - enum alloc_state state = get_page_state(pool, i); - bool is_metadata = is_metadata_page(pool, poolsize,i); - - switch (state) { - case FREE: - /* metadata pages are never free. */ - if (is_metadata) - return false; - case TAKEN_START: - break; - case TAKEN: - /* This should continue a previous block. */ - if (last_state == FREE) - return false; - if (is_metadata != was_metadata) - return false; - break; - case SPECIAL: - /* Check metadata pointer etc. */ - if (!check_subpage(pool, poolsize, i)) - return false; - } - last_state = state; - was_metadata = is_metadata; - } - return true; -} - -void alloc_visualize(FILE *out, void *pool, unsigned long poolsize) -{ - struct metaheader *mh; - struct uniform_cache *uc = pool; - unsigned long pagebitlen, metadata_pages, count[1<size[i] != 0); - fprintf(out, "Uniform cache (%lu entries):\n", tot); - for (i = 0; i < UNIFORM_CACHE_NUM; i++) { - unsigned int j, total = 0; - uint8_t *meta; - - if (!uc->size[i]) - continue; - - /* First two bytes are header. */ - meta = get_page_metadata(pool, uc->page[i]) + 2; - - for (j = 0; j < SUBPAGE_METAOFF / uc->size[i]; j++) - if (meta[j / 8] & (1 << (j % 8))) - total++; - - printf(" %u: %u/%u (%u%% density)\n", - uc->size[j], total, SUBPAGE_METAOFF / uc->size[i], - (total * 100) / (SUBPAGE_METAOFF / uc->size[i])); - } - - memset(count, 0, sizeof(count)); - for (i = 0; i < poolsize / getpagesize(); i++) - count[get_page_state(pool, i)]++; - - mh = first_mheader(pool, poolsize); - pagebitlen = (uint8_t *)mh - get_page_statebits(pool); - fprintf(out, "%lu bytes of page bits: FREE/TAKEN/TAKEN_START/SUBPAGE = %lu/%lu/%lu/%lu\n", - pagebitlen, count[0], count[1], count[2], count[3]); - - /* One metadata page for every page of page bits. */ - metadata_pages = div_up(pagebitlen, getpagesize()); - - /* Now do each metadata page. */ - for (; mh; mh = next_mheader(pool,mh)) { - unsigned long free = 0, bitmapblocks = 0, uniformblocks = 0, - len = 0, uniformlen = 0, bitmaplen = 0, metalen; - uint8_t *meta = (uint8_t *)(mh + 1); - - metalen = get_metalen(pool, poolsize, mh); - metadata_pages += (sizeof(*mh) + metalen) / getpagesize(); - - for (i = 0; i < metalen * METADATA_PER_BYTE; i += len) { - switch (get_bit_pair(meta, i)) { - case FREE: - len = 1; - free++; - break; - case BITMAP: - /* Skip over this allocated part. */ - len = BITMAP_METALEN * CHAR_BIT; - bitmapblocks++; - bitmaplen += len; - break; - case UNIFORM: - /* Skip over this part. */ - len = decode_usize(meta + i/METADATA_PER_BYTE); - len = uniform_metalen(len) * METADATA_PER_BYTE; - uniformblocks++; - uniformlen += len; - break; - default: - assert(0); - } - } - - fprintf(out, "Metadata %lu-%lu: %lu free, %lu bitmapblocks, %lu uniformblocks, %lu%% density\n", - pool_offset(pool, mh), - pool_offset(pool, (char *)(mh+1) + metalen), - free, bitmapblocks, uniformblocks, - (bitmaplen + uniformlen) * 100 - / (free + bitmaplen + uniformlen)); - } - - /* Account for total pages allocated. */ - tot = (count[1] + count[2] - metadata_pages) * getpagesize(); - - fprintf(out, "Total metadata bytes = %lu\n", - metadata_pages * getpagesize()); - - /* Now do every subpage. */ - for (i = 0; i < poolsize / getpagesize(); i++) { - uint8_t *meta; - unsigned int j, allocated; - enum sub_metadata_type type; - - if (get_page_state(pool, i) != SPECIAL) - continue; - - memset(count, 0, sizeof(count)); - - meta = get_page_metadata(pool, i); - type = get_bit_pair(meta, 0); - - if (type == BITMAP) { - for (j = 0; j < SUBPAGE_METAOFF/BITMAP_GRANULARITY; j++) - count[get_page_state(meta, j)]++; - allocated = (count[1] + count[2]) * BITMAP_GRANULARITY; - fprintf(out, "Subpage bitmap "); - } else { - unsigned int usize = decode_usize(meta); - - assert(type == UNIFORM); - fprintf(out, "Subpage uniform (%u) ", usize); - meta += 2; - for (j = 0; j < SUBPAGE_METAOFF / usize; j++) - count[!!(meta[j / 8] & (1 << (j % 8)))]++; - allocated = count[1] * usize; - } - fprintf(out, "%lu: FREE/TAKEN/TAKEN_START = %lu/%lu/%lu %u%% density\n", - i, count[0], count[1], count[2], - allocated * 100 / getpagesize()); - tot += allocated; - } - - /* This is optimistic, since we overalloc in several cases. */ - fprintf(out, "Best possible allocation density = %lu%%\n", - tot * 100 / poolsize); -} diff --git a/alloc/alloc.h b/alloc/alloc.h deleted file mode 100644 index 29c29d07..00000000 --- a/alloc/alloc.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef ALLOC_H -#define ALLOC_H -#include -#include - -void alloc_init(void *pool, unsigned long poolsize); -void *alloc_get(void *pool, unsigned long poolsize, - unsigned long size, unsigned long align); -void alloc_free(void *pool, unsigned long poolsize, void *free); -bool alloc_check(void *pool, unsigned long poolsize); - -void alloc_visualize(FILE *out, void *pool, unsigned long poolsize); -#endif /* ALLOC_H */ diff --git a/alloc/test/run.c b/alloc/test/run.c deleted file mode 100644 index 8cfe8b42..00000000 --- a/alloc/test/run.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "alloc/alloc.h" -#include "tap/tap.h" -#include "alloc/alloc.c" -#include - -#define POOL_ORD 16 -#define POOL_SIZE (1 << POOL_ORD) - -#define sort(p, num, cmp) \ - qsort((p), (num), sizeof(*p), (int(*)(const void *, const void *))cmp) - -static int addr_cmp(void **a, void **b) -{ - return (*a) - (*b); -} - -static bool unique(void *p[], unsigned int num) -{ - unsigned int i; - - for (i = 1; i < num; i++) - if (p[i] == p[i-1]) - return false; - return true; -} - -static bool free_every_second_one(void *mem, unsigned int num, void *p[]) -{ - unsigned int i; - - /* Free every second one. */ - for (i = 0; i < num; i += 2) { - alloc_free(mem, POOL_SIZE, p[i]); - if (!alloc_check(mem, POOL_SIZE)) - return false; - } - for (i = 1; i < num; i += 2) { - alloc_free(mem, POOL_SIZE, p[i]); - if (!alloc_check(mem, POOL_SIZE)) - return false; - } - return true; -} - - -int main(int argc, char *argv[]) -{ - void *mem; - unsigned int i, num, max_size; - void *p[POOL_SIZE]; - - plan_tests(139); - - /* FIXME: Needs to be page aligned for now. */ - posix_memalign(&mem, 1 << POOL_ORD, POOL_SIZE); - - /* Small pool, all allocs fail, even 0-length. */ - alloc_init(mem, 0); - ok1(alloc_check(mem, 0)); - ok1(alloc_get(mem, 0, 1, 1) == NULL); - ok1(alloc_get(mem, 0, 128, 1) == NULL); - ok1(alloc_get(mem, 0, 0, 1) == NULL); - - /* Free of NULL should work. */ - alloc_free(mem, 0, NULL); - - alloc_init(mem, POOL_SIZE); - ok1(alloc_check(mem, POOL_SIZE)); - /* Find largest allocation which works. */ - for (max_size = POOL_SIZE * 2; max_size; max_size--) { - p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); - if (p[0]) - break; - } - ok1(max_size < POOL_SIZE); - ok1(max_size > 0); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Free it, should be able to reallocate it. */ - alloc_free(mem, POOL_SIZE, p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - - p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); - ok1(p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - alloc_free(mem, POOL_SIZE, p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Allocate a whole heap. */ - for (i = 0; i < POOL_SIZE; i++) { - p[i] = alloc_get(mem, POOL_SIZE, 1, 1); - if (!p[i]) - break; - } - - /* Uncomment this for a more intuitive view of what the - * allocator looks like after all these 1 byte allocs. */ -#if 0 - alloc_visualize(stderr, mem, POOL_SIZE); -#endif - - num = i; - /* Can't allocate this many. */ - ok1(num != POOL_SIZE); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Sort them. */ - sort(p, num, addr_cmp); - - /* Uniqueness check */ - ok1(unique(p, num)); - - ok1(free_every_second_one(mem, num, p)); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Should be able to reallocate max size. */ - p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); - ok1(p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Re-initializing should be the same as freeing everything */ - alloc_init(mem, POOL_SIZE); - ok1(alloc_check(mem, POOL_SIZE)); - p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); - ok1(p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - alloc_free(mem, POOL_SIZE, p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Alignment constraints should be met, as long as powers of two */ - for (i = 0; i < POOL_ORD-1; i++) { - p[i] = alloc_get(mem, POOL_SIZE, i, 1 << i); - ok1(p[i]); - ok1(((unsigned long)p[i] % (1 << i)) == 0); - ok1(alloc_check(mem, POOL_SIZE)); - } - - for (i = 0; i < POOL_ORD-1; i++) { - alloc_free(mem, POOL_SIZE, p[i]); - ok1(alloc_check(mem, POOL_SIZE)); - } - - /* Alignment constraints for a single-byte allocation. */ - for (i = 0; i < POOL_ORD; i++) { - p[0] = alloc_get(mem, POOL_SIZE, 1, 1 << i); - ok1(p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - alloc_free(mem, POOL_SIZE, p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - } - - /* Alignment check for a 0-byte allocation. Corner case. */ - p[0] = alloc_get(mem, POOL_SIZE, 0, 1 << (POOL_ORD - 1)); - ok1(alloc_check(mem, POOL_SIZE)); - alloc_free(mem, POOL_SIZE, p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Force the testing of split metadata. */ - alloc_init(mem, POOL_SIZE); - for (i = 0; i < POOL_SIZE; i++) { - p[i] = alloc_get(mem, POOL_SIZE, getpagesize(), getpagesize()); - if (!p[i]) - break; - } - ok1(alloc_check(mem, POOL_SIZE)); - - /* Sort them. */ - sort(p, i-1, addr_cmp); - - /* Free all but the one next to the metadata. */ - for (i = 1; p[i]; i++) - alloc_free(mem, POOL_SIZE, p[i]); - ok1(alloc_check(mem, POOL_SIZE)); - - /* Now do a whole heap of subpage allocs. */ - for (i = 1; i < POOL_SIZE; i++) { - p[i] = alloc_get(mem, POOL_SIZE, 1, 1); - if (!p[i]) - break; - } - ok1(alloc_check(mem, POOL_SIZE)); - - /* Free up our page next to metadata, and should be able to alloc */ - alloc_free(mem, POOL_SIZE, p[0]); - ok1(alloc_check(mem, POOL_SIZE)); - p[0] = alloc_get(mem, POOL_SIZE, 1, 1); - ok1(p[0]); - - /* Clean up. */ - for (i = 0; p[i]; i++) - alloc_free(mem, POOL_SIZE, p[i]); - ok1(alloc_check(mem, POOL_SIZE)); - - return exit_status(); -} diff --git a/build_assert/_info.c b/build_assert/_info.c deleted file mode 100644 index 555aae21..00000000 --- a/build_assert/_info.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include "config.h" - -/** - * build_assert - routines for build-time assertions - * - * This code provides routines which will cause compilation to fail should some - * assertion be untrue: such failures are preferable to run-time assertions, - * but much more limited since they can only depends on compile-time constants. - * - * These assertions are most useful when two parts of the code must be kept in - * sync: it is better to avoid such cases if possible, but seconds best is to - * detect invalid changes at build time. - * - * For example, a tricky piece of code might rely on a certain element being at - * the start of the structure. To ensure that future changes don't break it, - * you would catch such changes in your code like so: - * - * Example: - * char *foo_string(struct foo *foo) - * { - * // This trick requires that the string be first in the structure - * BUILD_ASSERT(offsetof(struct foo, string) == 0); - * return (char *)foo; - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) - /* Nothing. */ - return 0; - - return 1; -} diff --git a/build_assert/build_assert.h b/build_assert/build_assert.h deleted file mode 100644 index 4b0d75e4..00000000 --- a/build_assert/build_assert.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CCAN_BUILD_ASSERT_H -#define CCAN_BUILD_ASSERT_H - -/** - * BUILD_ASSERT - assert a build-time dependency. - * @cond: the compile-time condition which must be true. - * - * Your compile will fail if the condition isn't true, or can't be evaluated - * by the compiler. This can only be used within a function. - * - * Example: - * char *foo_to_char(struct foo *foo) - * { - * // This code needs string to be at start of foo. - * BUILD_ASSERT(offsetof(struct foo, string) == 0); - * return (char *)foo; - * } - */ -#define BUILD_ASSERT(cond) \ - do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) - -/** - * EXPR_BUILD_ASSERT - assert a build-time dependency, as an expression. - * @cond: the compile-time condition which must be true. - * - * Your compile will fail if the condition isn't true, or can't be evaluated - * by the compiler. This can be used in an expression: its value is "0". - * - * Example: - * #define foo_to_char(foo) \ - * ((char *)(foo) \ - * + EXPR_BUILD_ASSERT(offsetof(struct foo, string) == 0)) - */ -#define EXPR_BUILD_ASSERT(cond) \ - (sizeof(char [1 - 2*!(cond)]) - 1) - -#endif /* CCAN_BUILD_ASSERT_H */ diff --git a/build_assert/test/compile_fail-expr.c b/build_assert/test/compile_fail-expr.c deleted file mode 100644 index 41cdc0f8..00000000 --- a/build_assert/test/compile_fail-expr.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "build_assert/build_assert.h" - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - return EXPR_BUILD_ASSERT(1 == 0); -#else - return 0; -#endif -} diff --git a/build_assert/test/compile_fail.c b/build_assert/test/compile_fail.c deleted file mode 100644 index a6867db5..00000000 --- a/build_assert/test/compile_fail.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "build_assert/build_assert.h" - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - BUILD_ASSERT(1 == 0); -#endif - return 0; -} diff --git a/build_assert/test/compile_ok.c b/build_assert/test/compile_ok.c deleted file mode 100644 index bc5541f5..00000000 --- a/build_assert/test/compile_ok.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "build_assert/build_assert.h" - -int main(int argc, char *argv[]) -{ - BUILD_ASSERT(1 == 1); - return 0; -} diff --git a/build_assert/test/run-EXPR_BUILD_ASSERT.c b/build_assert/test/run-EXPR_BUILD_ASSERT.c deleted file mode 100644 index 7fd0c49f..00000000 --- a/build_assert/test/run-EXPR_BUILD_ASSERT.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "build_assert/build_assert.h" -#include "tap/tap.h" - -int main(int argc, char *argv[]) -{ - plan_tests(1); - ok1(EXPR_BUILD_ASSERT(1 == 1) == 0); - return exit_status(); -} diff --git a/ccan/alignof/_info.c b/ccan/alignof/_info.c new file mode 100644 index 00000000..4cccba33 --- /dev/null +++ b/ccan/alignof/_info.c @@ -0,0 +1,45 @@ +#include +#include +#include "config.h" + +/** + * alignof - ALIGNOF() macro to determine alignment of a type. + * + * Many platforms have requirements that certain types must be aligned + * to certain address boundaries, such as ints needing to be on 4-byte + * boundaries. Attempting to access variables with incorrect + * alignment may cause performance loss or even program failure (eg. a + * bus signal). + * + * There are times which it's useful to be able to programatically + * access these requirements, such as for dynamic allocators. + * + * Example: + * #include + * #include "alignof/alignoff.h" + * + * int main(int argc, char *argv[]) + * { + * char arr[sizeof(int)]; + * + * if ((unsigned long)arr % ALIGNOF(int)) { + * printf("arr %p CANNOT hold an int\n", arr); + * exit(1); + * } else { + * printf("arr %p CAN hold an int\n", arr); + * exit(0); + * } + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/build_assert\n"); + return 0; + } + + return 1; +} diff --git a/ccan/alignof/alignof.h b/ccan/alignof/alignof.h new file mode 100644 index 00000000..d146e4cd --- /dev/null +++ b/ccan/alignof/alignof.h @@ -0,0 +1,19 @@ +#ifndef CCAN_ALIGNOF_H +#define CCAN_ALIGNOF_H +#include "config.h" + +/** + * ALIGNOF - get the alignment of a type + * @t: the type to test + * + * This returns a safe alignment for the given type. + */ +#if HAVE_ALIGNOF +/* A GCC extension. */ +#define ALIGNOF(t) __alignof__(t) +#else +/* Alignment by measuring structure padding. */ +#define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) +#endif + +#endif /* CCAN_ALIGNOF_H */ diff --git a/ccan/alignof/test/run.c b/ccan/alignof/test/run.c new file mode 100644 index 00000000..f76fb669 --- /dev/null +++ b/ccan/alignof/test/run.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include "alignof/alignof.h" + +/* Alignment is remarkably difficult to test. The rules may be more + * complex than ALIGNOF() can know: eg. on i386 __alignof__(double) == 8, but + * __alignof__(struct containing double) == 4. + * + * Technically, we can only test that we give *at least* the alignment which + * naturally occurs, and that accesses work. + * + * For the moment, we work around double. */ +struct lots_of_types +{ + char c; + short s; + char c2; + int i; + char c3; + float f; + char c4; + double d; + char c5; +}; + +int main(int argc, char *argv[]) +{ + struct lots_of_types lots_of_types, *lp = malloc(sizeof(*lp)); + char c; + short s; + char c2; + int i; + char c3; + float f; + char c4; + double d; + + /* Make sure we use all the variables. */ + c = 0; + c2 = c3 = c4 = c; + + plan_tests(15); + ok1((unsigned long)&c % ALIGNOF(char) == 0); + ok1((unsigned long)&s % ALIGNOF(short) == 0); + ok1((unsigned long)&i % ALIGNOF(int) == 0); + ok1((unsigned long)&f % ALIGNOF(float) == 0); + ok1((unsigned long)&d % ALIGNOF(double) == 0); + + ok1((unsigned long)&lots_of_types.c % ALIGNOF(char) == 0); + ok1((unsigned long)&lots_of_types.s % ALIGNOF(short) == 0); + ok1((unsigned long)&lots_of_types.i % ALIGNOF(int) == 0); + ok1((unsigned long)&lots_of_types.f % ALIGNOF(float) == 0); + ok1(offsetof(struct lots_of_types, d) % ALIGNOF(double) == 0); + + ok1((unsigned long)&lp->c % ALIGNOF(char) == 0); + ok1((unsigned long)&lp->s % ALIGNOF(short) == 0); + ok1((unsigned long)&lp->i % ALIGNOF(int) == 0); + ok1((unsigned long)&lp->f % ALIGNOF(float) == 0); + ok1((unsigned long)&lp->d % ALIGNOF(double) == 0); + exit(exit_status()); +} diff --git a/ccan/alloc/_info.c b/ccan/alloc/_info.c new file mode 100644 index 00000000..03e20afc --- /dev/null +++ b/ccan/alloc/_info.c @@ -0,0 +1,103 @@ +#include +#include +#include "config.h" + +/** + * alloc - memory allocator routines + * + * The alloc module implements a simple allocator which you can use to + * dynamically allocate space within a region of memory. This can be useful + * for suballocations within a given region, or a memory-mapped file. + * + * All metadata is kept within the memory handed to the allocator: you only + * need hand the pointer and the size of the memory to each call. + * + * The region contents is always in offsets, so it can be mapped in different + * places, but is not endian-safe. + * + * Example: + * #include + * #include + * #include + * #include + * #include "alloc/alloc.h" + * + * static void usage(const char *name) + * { + * errx(1, "Usage: %s --create \n" + * " %s --check \n" + * " %s --alloc \n" + * " %s --free= \n", name, name, name); + * } + * + * // Create a memory mapped file, and allocate from within it + * int main(int argc, char *argv[]) + * { + * void *a, *p; + * int fd; + * enum { CREATE, CHECK, ALLOC, FREE } cmd; + * + * if (argc != 3) + * usage(argv[0]); + * + * if (strcmp(argv[1], "--create") == 0) + * cmd = CREATE; + * else if (strcmp(argv[1], "--check") == 0) + * cmd = CHECK; + * else if (strcmp(argv[1], "--alloc") == 0) + * cmd = ALLOC; + * else if (strncmp(argv[1], "--free=", strlen("--free=")) == 0) + * cmd = FREE; + * else + * usage(argv[0]); + * + * if (cmd == CREATE) { + * fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0600); + * if (fd < 0) + * err(1, "Could not create %s", argv[2]); + * if (ftruncate(fd, 1048576) != 0) + * err(1, "Could not set length on %s", argv[2]); + * } else { + * fd = open(argv[2], O_RDWR); + * if (fd < 0) + * err(1, "Could not open %s", argv[2]); + * } + * + * a = mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0); + * if (a == MAP_FAILED) + * err(1, "Could not map %s", argv[2]); + * + * switch (cmd) { + * case CREATE: + * alloc_init(a, 1048576); + * break; + * case CHECK: + * if (!alloc_check(a, 1048576)) + * err(1, "Region is corrupt"); + * break; + * case ALLOC: + * p = alloc_get(a, 1048576, 1024, 16); + * if (!p) + * errx(1, "Could not allocate"); + * printf("%zu\n", (char *)p - (char *)a); + * break; + * case FREE: + * p = (char *)a + atol(argv[1] + strlen("--free=")); + * alloc_free(a, 1048576, p); + * break; + * } + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/build_assert\n"); + return 0; + } + + return 1; +} diff --git a/ccan/alloc/alloc.c b/ccan/alloc/alloc.c new file mode 100644 index 00000000..f7a05e2d --- /dev/null +++ b/ccan/alloc/alloc.c @@ -0,0 +1,1119 @@ +#include +#include +#include +#include +#include +#include +#include "alloc.h" +#include "build_assert/build_assert.h" +#include "alignof/alignof.h" +#include "config.h" + +/* FIXME: We assume getpagesize() doesnt change. Remapping file with + * different pagesize should still work. */ + +/* FIXME: Doesn't handle non-page-aligned poolsize. */ + +/* FIXME: Reduce. */ +#define MIN_SIZE (getpagesize() * 2) + +/* What's the granularity of sub-page allocs? */ +#define BITMAP_GRANULARITY 4 + +/* File layout: + * + * file := pagestates pad uniform-cache metadata + * pagestates := pages * 2-bits-per-page + * pad := pad to next ALIGNOF(metaheader) + * + * metadata := metalen next-ptr metabits + * metabits := freeblock | bitblock | uniformblock + * freeblock := FREE + + * bitblock := BITMAP + 2-bits-per-bit-in-page + pad-to-byte + * uniformblock := UNIFORM + 14-bit-byte-len + bits + pad-to-byte + */ +#define UNIFORM_CACHE_NUM 16 +struct uniform_cache +{ + uint16_t size[UNIFORM_CACHE_NUM]; + /* These could be u32 if we're prepared to limit size. */ + unsigned long page[UNIFORM_CACHE_NUM]; +}; + +struct metaheader +{ + /* Next meta header, or 0 */ + unsigned long next; + /* Bits start here. */ +}; + +/* Assumes a is a power of two. */ +static unsigned long align_up(unsigned long x, unsigned long a) +{ + return (x + a - 1) & ~(a - 1); +} + +static unsigned long align_down(unsigned long x, unsigned long a) +{ + return x & ~(a - 1); +} + +static unsigned long div_up(unsigned long x, unsigned long a) +{ + return (x + a - 1) / a; +} + +/* It turns out that we spend a lot of time dealing with bit pairs. + * These routines manipulate them. + */ +static uint8_t get_bit_pair(const uint8_t *bits, unsigned long index) +{ + return bits[index * 2 / CHAR_BIT] >> (index * 2 % CHAR_BIT) & 3; +} + +static void set_bit_pair(uint8_t *bits, unsigned long index, uint8_t val) +{ + bits[index * 2 / CHAR_BIT] &= ~(3 << (index * 2 % CHAR_BIT)); + bits[index * 2 / CHAR_BIT] |= (val << (index * 2 % CHAR_BIT)); +} + +/* This is used for page states and subpage allocations */ +enum alloc_state +{ + FREE, + TAKEN, + TAKEN_START, + SPECIAL, /* Sub-page allocation for page states. */ +}; + +/* The types for subpage metadata. */ +enum sub_metadata_type +{ + /* FREE is same as alloc state */ + BITMAP = 1, /* bitmap allocated page */ + UNIFORM, /* uniform size allocated page */ +}; + +/* Page states are represented by bitpairs, at the start of the pool. */ +#define BITS_PER_PAGE 2 + +/* How much metadata info per byte? */ +#define METADATA_PER_BYTE (CHAR_BIT / 2) + +static uint8_t *get_page_statebits(const void *pool) +{ + return (uint8_t *)pool + sizeof(struct uniform_cache); +} + +static enum alloc_state get_page_state(const void *pool, unsigned long page) +{ + return get_bit_pair(get_page_statebits(pool), page); +} + +static void set_page_state(void *pool, unsigned long page, enum alloc_state s) +{ + set_bit_pair(get_page_statebits(pool), page, s); +} + +/* The offset of metadata for a subpage allocation is found at the end + * of the subpage */ +#define SUBPAGE_METAOFF (getpagesize() - sizeof(unsigned long)) + +/* This is the length of metadata in bits. It consists of two bits + * for every BITMAP_GRANULARITY of usable bytes in the page, then two + * bits for the tailer.. */ +#define BITMAP_METABITLEN \ + ((div_up(SUBPAGE_METAOFF, BITMAP_GRANULARITY) + 1) * BITS_PER_PAGE) + +/* This is the length in bytes. */ +#define BITMAP_METALEN (div_up(BITMAP_METABITLEN, CHAR_BIT)) + +static struct metaheader *first_mheader(void *pool, unsigned long poolsize) +{ + unsigned int pagestatelen; + + pagestatelen = align_up(div_up(poolsize/getpagesize() * BITS_PER_PAGE, + CHAR_BIT), + ALIGNOF(struct metaheader)); + return (struct metaheader *)(get_page_statebits(pool) + pagestatelen); +} + +static struct metaheader *next_mheader(void *pool, struct metaheader *mh) +{ + if (!mh->next) + return NULL; + + return (struct metaheader *)((char *)pool + mh->next); +} + +static unsigned long pool_offset(void *pool, void *p) +{ + return (char *)p - (char *)pool; +} + +void alloc_init(void *pool, unsigned long poolsize) +{ + /* FIXME: Alignment assumptions about pool. */ + unsigned long len, i; + struct metaheader *mh; + + if (poolsize < MIN_SIZE) + return; + + mh = first_mheader(pool, poolsize); + + /* Mark all page states FREE, all uniform caches zero, and all of + * metaheader bitmap which takes rest of first page. */ + len = align_up(pool_offset(pool, mh + 1), getpagesize()); + BUILD_ASSERT(FREE == 0); + memset(pool, 0, len); + + /* Mark the pagestate and metadata page(s) allocated. */ + set_page_state(pool, 0, TAKEN_START); + for (i = 1; i < div_up(len, getpagesize()); i++) + set_page_state(pool, i, TAKEN); +} + +/* Two bits per element, representing page states. Returns 0 on fail. + * off is used to allocate from subpage bitmaps, which use the first 2 + * bits as the type, so the real bitmap is offset by 1. */ +static unsigned long alloc_from_bitmap(uint8_t *bits, unsigned long off, + unsigned long elems, + unsigned long want, unsigned long align) +{ + long i; + unsigned long free; + + free = 0; + /* We allocate from far end, to increase ability to expand metadata. */ + for (i = elems - 1; i >= 0; i--) { + switch (get_bit_pair(bits, off+i)) { + case FREE: + if (++free >= want) { + unsigned long j; + + /* They might ask for large alignment. */ + if (align && i % align) + continue; + + set_bit_pair(bits, off+i, TAKEN_START); + for (j = i+1; j < i + want; j++) + set_bit_pair(bits, off+j, TAKEN); + return off+i; + } + break; + case SPECIAL: + case TAKEN_START: + case TAKEN: + free = 0; + break; + } + } + + return 0; +} + +static unsigned long alloc_get_pages(void *pool, unsigned long poolsize, + unsigned long pages, unsigned long align) +{ + return alloc_from_bitmap(get_page_statebits(pool), + 0, poolsize / getpagesize(), pages, + align / getpagesize()); +} + +/* Offset to metadata is at end of page. */ +static unsigned long *metadata_off(void *pool, unsigned long page) +{ + return (unsigned long *) + ((char *)pool + (page+1)*getpagesize() - sizeof(unsigned long)); +} + +static uint8_t *get_page_metadata(void *pool, unsigned long page) +{ + return (uint8_t *)pool + *metadata_off(pool, page); +} + +static void set_page_metadata(void *pool, unsigned long page, uint8_t *meta) +{ + *metadata_off(pool, page) = meta - (uint8_t *)pool; +} + +static unsigned long sub_page_alloc(void *pool, unsigned long page, + unsigned long size, unsigned long align) +{ + uint8_t *bits = get_page_metadata(pool, page); + unsigned long i; + enum sub_metadata_type type; + + type = get_bit_pair(bits, 0); + + /* If this is a uniform page, we can't allocate from it. */ + if (type == UNIFORM) + return 0; + + assert(type == BITMAP); + + /* We use a standart bitmap, but offset because of that BITMAP + * header. */ + i = alloc_from_bitmap(bits, 1, SUBPAGE_METAOFF/BITMAP_GRANULARITY, + div_up(size, BITMAP_GRANULARITY), + align / BITMAP_GRANULARITY); + + /* Can't allocate? */ + if (i == 0) + return 0; + + /* i-1 because of the header. */ + return page*getpagesize() + (i-1)*BITMAP_GRANULARITY; +} + +/* We look at the page states to figure out where the allocation for this + * metadata ends. */ +static unsigned long get_metalen(void *pool, unsigned long poolsize, + struct metaheader *mh) +{ + unsigned long i, first, pages = poolsize / getpagesize(); + + first = pool_offset(pool, mh + 1)/getpagesize(); + + for (i = first + 1; i < pages && get_page_state(pool,i) == TAKEN; i++); + + return i * getpagesize() - pool_offset(pool, mh + 1); +} + +static unsigned int uniform_metalen(unsigned int usize) +{ + unsigned int metalen; + + assert(usize < (1 << 14)); + + /* Two bits for the header, 14 bits for size, then one bit for each + * element the page can hold. Round up to number of bytes. */ + metalen = div_up(2 + 14 + SUBPAGE_METAOFF / usize, CHAR_BIT); + + /* To ensure metaheader is always aligned, round bytes up. */ + metalen = align_up(metalen, ALIGNOF(struct metaheader)); + + return metalen; +} + +static unsigned int decode_usize(uint8_t *meta) +{ + return ((unsigned)meta[1] << (CHAR_BIT-2)) | (meta[0] >> 2); +} + +static void encode_usize(uint8_t *meta, unsigned int usize) +{ + meta[0] = (UNIFORM | (usize << 2)); + meta[1] = (usize >> (CHAR_BIT - 2)); +} + +static uint8_t *alloc_metaspace(void *pool, unsigned long poolsize, + struct metaheader *mh, unsigned long bytes, + enum sub_metadata_type type) +{ + uint8_t *meta = (uint8_t *)(mh + 1); + unsigned long free = 0, len, i, metalen; + + metalen = get_metalen(pool, poolsize, mh); + + /* Walk through metadata looking for free. */ + for (i = 0; i < metalen * METADATA_PER_BYTE; i += len) { + switch (get_bit_pair(meta, i)) { + case FREE: + len = 1; + free++; + if (free == bytes * METADATA_PER_BYTE) { + /* Mark this as a bitmap. */ + set_bit_pair(meta, i - free + 1, type); + return meta + (i - free + 1)/METADATA_PER_BYTE; + } + break; + case BITMAP: + /* Skip over this allocated part. */ + len = BITMAP_METALEN * METADATA_PER_BYTE; + free = 0; + break; + case UNIFORM: + /* Figure metalen given usize. */ + len = decode_usize(meta + i / METADATA_PER_BYTE); + len = uniform_metalen(len) * METADATA_PER_BYTE; + free = 0; + break; + default: + assert(0); + return NULL; + } + } + return NULL; +} + +/* We need this many bytes of metadata. */ +static uint8_t *new_metadata(void *pool, unsigned long poolsize, + unsigned long bytes, enum sub_metadata_type type) +{ + struct metaheader *mh, *newmh; + unsigned long page; + uint8_t *meta; + + for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)) + if ((meta = alloc_metaspace(pool, poolsize, mh, bytes, type))) + return meta; + + /* No room for metadata? Can we expand an existing one? */ + for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ + unsigned long nextpage; + + /* We start on this page. */ + nextpage = pool_offset(pool, (char *)(mh+1))/getpagesize(); + /* Iterate through any other pages we own. */ + while (get_page_state(pool, ++nextpage) == TAKEN); + + /* Now, can we grab that page? */ + if (get_page_state(pool, nextpage) != FREE) + continue; + + /* OK, expand metadata, do it again. */ + set_page_state(pool, nextpage, TAKEN); + BUILD_ASSERT(FREE == 0); + memset((char *)pool + nextpage*getpagesize(), 0, getpagesize()); + return alloc_metaspace(pool, poolsize, mh, bytes, type); + } + + /* No metadata left at all? */ + page = alloc_get_pages(pool, poolsize, div_up(bytes, getpagesize()), 1); + if (!page) + return NULL; + + newmh = (struct metaheader *)((char *)pool + page * getpagesize()); + BUILD_ASSERT(FREE == 0); + memset(newmh + 1, 0, getpagesize() - sizeof(*mh)); + + /* Sew it into linked list */ + mh = first_mheader(pool,poolsize); + newmh->next = mh->next; + mh->next = pool_offset(pool, newmh); + + return alloc_metaspace(pool, poolsize, newmh, bytes, type); +} + +static void alloc_free_pages(void *pool, unsigned long pagenum) +{ + assert(get_page_state(pool, pagenum) == TAKEN_START); + set_page_state(pool, pagenum, FREE); + while (get_page_state(pool, ++pagenum) == TAKEN) + set_page_state(pool, pagenum, FREE); +} + +static void maybe_transform_uniform_page(void *pool, unsigned long offset) +{ + /* FIXME: If possible and page isn't full, change to a bitmap */ +} + +/* Returns 0 or the size of the uniform alloc to use */ +static unsigned long suitable_for_uc(unsigned long size, unsigned long align) +{ + unsigned long num_elems, wastage, usize; + unsigned long bitmap_cost; + + if (size == 0) + size = 1; + + /* Fix up silly alignments. */ + usize = align_up(size, align); + + /* How many can fit in this page? */ + num_elems = SUBPAGE_METAOFF / usize; + + /* Can happen with bigger alignments. */ + if (!num_elems) + return 0; + + /* Usize maxes out at 14 bits. */ + if (usize >= (1 << 14)) + return 0; + + /* How many bytes would be left at the end? */ + wastage = SUBPAGE_METAOFF % usize; + + /* If we can get a larger allocation within alignment constraints, we + * should do it, otherwise might as well leave wastage at the end. */ + usize += align_down(wastage / num_elems, align); + + /* Bitmap allocation costs 2 bits per BITMAP_GRANULARITY bytes, plus + * however much we waste in rounding up to BITMAP_GRANULARITY. */ + bitmap_cost = 2 * div_up(size, BITMAP_GRANULARITY) + + CHAR_BIT * (align_up(size, BITMAP_GRANULARITY) - size); + + /* Our cost is 1 bit, plus usize overhead */ + if (bitmap_cost < 1 + (usize - size) * CHAR_BIT) + return 0; + + return usize; +} + +static unsigned long uniform_alloc(void *pool, unsigned long poolsize, + struct uniform_cache *uc, + unsigned long ucnum) +{ + uint8_t *metadata = get_page_metadata(pool, uc->page[ucnum]) + 2; + unsigned long i, max; + + /* Simple one-bit-per-object bitmap. */ + max = SUBPAGE_METAOFF / uc->size[ucnum]; + for (i = 0; i < max; i++) { + if (!(metadata[i / CHAR_BIT] & (1 << (i % CHAR_BIT)))) { + metadata[i / CHAR_BIT] |= (1 << (i % CHAR_BIT)); + return uc->page[ucnum] * getpagesize() + + i * uc->size[ucnum]; + } + } + + return 0; +} + +static unsigned long new_uniform_page(void *pool, unsigned long poolsize, + unsigned long usize) +{ + unsigned long page, metalen; + uint8_t *metadata; + + page = alloc_get_pages(pool, poolsize, 1, 1); + if (page == 0) + return 0; + + metalen = uniform_metalen(usize); + + /* Get metadata for page. */ + metadata = new_metadata(pool, poolsize, metalen, UNIFORM); + if (!metadata) { + alloc_free_pages(pool, page); + return 0; + } + + encode_usize(metadata, usize); + + BUILD_ASSERT(FREE == 0); + memset(metadata + 2, 0, metalen - 2); + + /* Actually, this is a subpage page now. */ + set_page_state(pool, page, SPECIAL); + + /* Set metadata pointer for page. */ + set_page_metadata(pool, page, metadata); + + return page; +} + +static unsigned long alloc_sub_page(void *pool, unsigned long poolsize, + unsigned long size, unsigned long align) +{ + unsigned long i, usize; + uint8_t *metadata; + struct uniform_cache *uc = pool; + + usize = suitable_for_uc(size, align); + if (usize) { + /* Look for a uniform page. */ + for (i = 0; i < UNIFORM_CACHE_NUM; i++) { + if (uc->size[i] == usize) { + unsigned long ret; + ret = uniform_alloc(pool, poolsize, uc, i); + if (ret != 0) + return ret; + /* OK, that one is full, remove from cache. */ + uc->size[i] = 0; + break; + } + } + + /* OK, try a new uniform page. Use random discard for now. */ + i = random() % UNIFORM_CACHE_NUM; + maybe_transform_uniform_page(pool, uc->page[i]); + + uc->page[i] = new_uniform_page(pool, poolsize, usize); + if (uc->page[i]) { + uc->size[i] = usize; + return uniform_alloc(pool, poolsize, uc, i); + } + uc->size[i] = 0; + } + + /* Look for partial page. */ + for (i = 0; i < poolsize / getpagesize(); i++) { + unsigned long ret; + if (get_page_state(pool, i) != SPECIAL) + continue; + + ret = sub_page_alloc(pool, i, size, align); + if (ret) + return ret; + } + + /* Create new SUBPAGE page. */ + i = alloc_get_pages(pool, poolsize, 1, 1); + if (i == 0) + return 0; + + /* Get metadata for page. */ + metadata = new_metadata(pool, poolsize, BITMAP_METALEN, BITMAP); + if (!metadata) { + alloc_free_pages(pool, i); + return 0; + } + + /* Actually, this is a subpage page now. */ + set_page_state(pool, i, SPECIAL); + + /* Set metadata pointer for page. */ + set_page_metadata(pool, i, metadata); + + /* Do allocation like normal */ + return sub_page_alloc(pool, i, size, align); +} + +static bool bitmap_page_is_empty(uint8_t *meta) +{ + unsigned int i; + + /* Skip the header (first bit of metadata). */ + for (i = 1; i < SUBPAGE_METAOFF/BITMAP_GRANULARITY+1; i++) + if (get_bit_pair(meta, i) != FREE) + return false; + + return true; +} + +static bool uniform_page_is_empty(uint8_t *meta) +{ + unsigned int i, metalen; + + metalen = uniform_metalen(decode_usize(meta)); + + /* Skip the header (first two bytes of metadata). */ + for (i = 2; i < metalen + 2; i++) { + BUILD_ASSERT(FREE == 0); + if (meta[i]) + return false; + } + return true; +} + +static bool special_page_is_empty(void *pool, unsigned long page) +{ + uint8_t *meta; + enum sub_metadata_type type; + + meta = get_page_metadata(pool, page); + type = get_bit_pair(meta, 0); + + switch (type) { + case UNIFORM: + return uniform_page_is_empty(meta); + case BITMAP: + return bitmap_page_is_empty(meta); + default: + assert(0); + } +} + +static void clear_special_metadata(void *pool, unsigned long page) +{ + uint8_t *meta; + enum sub_metadata_type type; + + meta = get_page_metadata(pool, page); + type = get_bit_pair(meta, 0); + + switch (type) { + case UNIFORM: + /* First two bytes are the header, rest is already FREE */ + BUILD_ASSERT(FREE == 0); + memset(meta, 0, 2); + break; + case BITMAP: + /* First two bits is the header. */ + BUILD_ASSERT(BITMAP_METALEN > 1); + meta[0] = 0; + break; + default: + assert(0); + } +} + +/* Returns true if we cleaned any pages. */ +static bool clean_empty_subpages(void *pool, unsigned long poolsize) +{ + unsigned long i; + bool progress = false; + + for (i = 0; i < poolsize/getpagesize(); i++) { + if (get_page_state(pool, i) != SPECIAL) + continue; + + if (special_page_is_empty(pool, i)) { + clear_special_metadata(pool, i); + set_page_state(pool, i, FREE); + progress = true; + } + } + return progress; +} + +/* Returns true if we cleaned any pages. */ +static bool clean_metadata(void *pool, unsigned long poolsize) +{ + struct metaheader *mh, *prev_mh = NULL; + bool progress = false; + + for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ + uint8_t *meta; + long i; + unsigned long metalen = get_metalen(pool, poolsize, mh); + + meta = (uint8_t *)(mh + 1); + BUILD_ASSERT(FREE == 0); + for (i = metalen - 1; i > 0; i--) + if (meta[i] != 0) + break; + + /* Completely empty? */ + if (prev_mh && i == metalen) { + alloc_free_pages(pool, + pool_offset(pool, mh)/getpagesize()); + prev_mh->next = mh->next; + mh = prev_mh; + progress = true; + } else { + uint8_t *p; + + /* Some pages at end are free? */ + for (p = (uint8_t *)(mh+1) + metalen - getpagesize(); + p > meta + i; + p -= getpagesize()) { + set_page_state(pool, + pool_offset(pool, p) + / getpagesize(), + FREE); + progress = true; + } + } + } + + return progress; +} + +void *alloc_get(void *pool, unsigned long poolsize, + unsigned long size, unsigned long align) +{ + bool subpage_clean = false, metadata_clean = false; + unsigned long ret; + + if (poolsize < MIN_SIZE) + return NULL; + +again: + /* Sub-page allocations have an overhead of ~12%. */ + if (size + size/8 >= getpagesize() || align >= getpagesize()) { + unsigned long pages = div_up(size, getpagesize()); + + ret = alloc_get_pages(pool, poolsize, pages, align) + * getpagesize(); + } else + ret = alloc_sub_page(pool, poolsize, size, align); + + if (ret != 0) + return (char *)pool + ret; + + /* Allocation failed: garbage collection. */ + if (!subpage_clean) { + subpage_clean = true; + if (clean_empty_subpages(pool, poolsize)) + goto again; + } + + if (!metadata_clean) { + metadata_clean = true; + if (clean_metadata(pool, poolsize)) + goto again; + } + + /* FIXME: Compact metadata? */ + return NULL; +} + +static void bitmap_free(void *pool, unsigned long pagenum, unsigned long off, + uint8_t *metadata) +{ + assert(off % BITMAP_GRANULARITY == 0); + + off /= BITMAP_GRANULARITY; + + /* Offset by one because first bit is used for header. */ + off++; + + set_bit_pair(metadata, off++, FREE); + while (off < SUBPAGE_METAOFF / BITMAP_GRANULARITY + && get_bit_pair(metadata, off) == TAKEN) + set_bit_pair(metadata, off++, FREE); +} + +static void uniform_free(void *pool, unsigned long pagenum, unsigned long off, + uint8_t *metadata) +{ + unsigned int usize, bit; + + usize = decode_usize(metadata); + /* Must have been this size. */ + assert(off % usize == 0); + bit = off / usize; + + /* Skip header. */ + metadata += 2; + + /* Must have been allocated. */ + assert(metadata[bit / CHAR_BIT] & (1 << (bit % CHAR_BIT))); + metadata[bit / CHAR_BIT] &= ~(1 << (bit % CHAR_BIT)); +} + +static void subpage_free(void *pool, unsigned long pagenum, void *free) +{ + unsigned long off = (unsigned long)free % getpagesize(); + uint8_t *metadata = get_page_metadata(pool, pagenum); + enum sub_metadata_type type; + + type = get_bit_pair(metadata, 0); + + assert(off < SUBPAGE_METAOFF); + + switch (type) { + case BITMAP: + bitmap_free(pool, pagenum, off, metadata); + break; + case UNIFORM: + uniform_free(pool, pagenum, off, metadata); + break; + default: + assert(0); + } +} + +void alloc_free(void *pool, unsigned long poolsize, void *free) +{ + unsigned long pagenum; + struct metaheader *mh; + + if (!free) + return; + + assert(poolsize >= MIN_SIZE); + + mh = first_mheader(pool, poolsize); + assert((char *)free >= (char *)(mh + 1)); + assert((char *)pool + poolsize > (char *)free); + + pagenum = pool_offset(pool, free) / getpagesize(); + + if (get_page_state(pool, pagenum) == SPECIAL) + subpage_free(pool, pagenum, free); + else { + assert((unsigned long)free % getpagesize() == 0); + alloc_free_pages(pool, pagenum); + } +} + +static bool is_metadata_page(void *pool, unsigned long poolsize, + unsigned long page) +{ + struct metaheader *mh; + + for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ + unsigned long start, end; + + start = pool_offset(pool, mh); + end = pool_offset(pool, (char *)(mh+1) + + get_metalen(pool, poolsize, mh)); + if (page >= start/getpagesize() && page < end/getpagesize()) + return true; + } + return false; +} + +static bool check_bitmap_metadata(void *pool, unsigned long *mhoff) +{ + enum alloc_state last_state = FREE; + unsigned int i; + + for (i = 0; i < SUBPAGE_METAOFF / BITMAP_GRANULARITY; i++) { + enum alloc_state state; + + /* +1 because header is the first byte. */ + state = get_bit_pair((uint8_t *)pool + *mhoff, i+1); + switch (state) { + case SPECIAL: + return false; + case TAKEN: + if (last_state == FREE) + return false; + break; + default: + break; + } + last_state = state; + } + return true; +} + +static bool check_uniform_metadata(void *pool, unsigned long *mhoff) +{ + uint8_t *meta = (uint8_t *)pool + *mhoff; + unsigned int i, usize; + struct uniform_cache *uc = pool; + + usize = decode_usize(meta); + if (usize == 0 || suitable_for_uc(usize, 1) != usize) + return false; + + /* If it's in uniform cache, make sure that agrees on size. */ + for (i = 0; i < UNIFORM_CACHE_NUM; i++) { + uint8_t *ucm; + + if (!uc->size[i]) + continue; + + ucm = get_page_metadata(pool, uc->page[i]); + if (ucm != meta) + continue; + + if (usize != uc->size[i]) + return false; + } + return true; +} + +static bool check_subpage(void *pool, unsigned long poolsize, + unsigned long page) +{ + unsigned long *mhoff = metadata_off(pool, page); + + if (*mhoff + sizeof(struct metaheader) > poolsize) + return false; + + if (*mhoff % ALIGNOF(struct metaheader) != 0) + return false; + + /* It must point to a metadata page. */ + if (!is_metadata_page(pool, poolsize, *mhoff / getpagesize())) + return false; + + /* Header at start of subpage allocation */ + switch (get_bit_pair((uint8_t *)pool + *mhoff, 0)) { + case BITMAP: + return check_bitmap_metadata(pool, mhoff); + case UNIFORM: + return check_uniform_metadata(pool, mhoff); + default: + return false; + } + +} + +bool alloc_check(void *pool, unsigned long poolsize) +{ + unsigned long i; + struct metaheader *mh; + enum alloc_state last_state = FREE; + bool was_metadata = false; + + if (poolsize < MIN_SIZE) + return true; + + if (get_page_state(pool, 0) != TAKEN_START) + return false; + + /* First check metadata pages. */ + /* Metadata pages will be marked TAKEN. */ + for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){ + unsigned long start, end; + + start = pool_offset(pool, mh); + if (start + sizeof(*mh) > poolsize) + return false; + + end = pool_offset(pool, (char *)(mh+1) + + get_metalen(pool, poolsize, mh)); + if (end > poolsize) + return false; + + /* Non-first pages should start on a page boundary. */ + if (mh != first_mheader(pool, poolsize) + && start % getpagesize() != 0) + return false; + + /* It should end on a page boundary. */ + if (end % getpagesize() != 0) + return false; + } + + for (i = 0; i < poolsize / getpagesize(); i++) { + enum alloc_state state = get_page_state(pool, i); + bool is_metadata = is_metadata_page(pool, poolsize,i); + + switch (state) { + case FREE: + /* metadata pages are never free. */ + if (is_metadata) + return false; + case TAKEN_START: + break; + case TAKEN: + /* This should continue a previous block. */ + if (last_state == FREE) + return false; + if (is_metadata != was_metadata) + return false; + break; + case SPECIAL: + /* Check metadata pointer etc. */ + if (!check_subpage(pool, poolsize, i)) + return false; + } + last_state = state; + was_metadata = is_metadata; + } + return true; +} + +void alloc_visualize(FILE *out, void *pool, unsigned long poolsize) +{ + struct metaheader *mh; + struct uniform_cache *uc = pool; + unsigned long pagebitlen, metadata_pages, count[1<size[i] != 0); + fprintf(out, "Uniform cache (%lu entries):\n", tot); + for (i = 0; i < UNIFORM_CACHE_NUM; i++) { + unsigned int j, total = 0; + uint8_t *meta; + + if (!uc->size[i]) + continue; + + /* First two bytes are header. */ + meta = get_page_metadata(pool, uc->page[i]) + 2; + + for (j = 0; j < SUBPAGE_METAOFF / uc->size[i]; j++) + if (meta[j / 8] & (1 << (j % 8))) + total++; + + printf(" %u: %u/%u (%u%% density)\n", + uc->size[j], total, SUBPAGE_METAOFF / uc->size[i], + (total * 100) / (SUBPAGE_METAOFF / uc->size[i])); + } + + memset(count, 0, sizeof(count)); + for (i = 0; i < poolsize / getpagesize(); i++) + count[get_page_state(pool, i)]++; + + mh = first_mheader(pool, poolsize); + pagebitlen = (uint8_t *)mh - get_page_statebits(pool); + fprintf(out, "%lu bytes of page bits: FREE/TAKEN/TAKEN_START/SUBPAGE = %lu/%lu/%lu/%lu\n", + pagebitlen, count[0], count[1], count[2], count[3]); + + /* One metadata page for every page of page bits. */ + metadata_pages = div_up(pagebitlen, getpagesize()); + + /* Now do each metadata page. */ + for (; mh; mh = next_mheader(pool,mh)) { + unsigned long free = 0, bitmapblocks = 0, uniformblocks = 0, + len = 0, uniformlen = 0, bitmaplen = 0, metalen; + uint8_t *meta = (uint8_t *)(mh + 1); + + metalen = get_metalen(pool, poolsize, mh); + metadata_pages += (sizeof(*mh) + metalen) / getpagesize(); + + for (i = 0; i < metalen * METADATA_PER_BYTE; i += len) { + switch (get_bit_pair(meta, i)) { + case FREE: + len = 1; + free++; + break; + case BITMAP: + /* Skip over this allocated part. */ + len = BITMAP_METALEN * CHAR_BIT; + bitmapblocks++; + bitmaplen += len; + break; + case UNIFORM: + /* Skip over this part. */ + len = decode_usize(meta + i/METADATA_PER_BYTE); + len = uniform_metalen(len) * METADATA_PER_BYTE; + uniformblocks++; + uniformlen += len; + break; + default: + assert(0); + } + } + + fprintf(out, "Metadata %lu-%lu: %lu free, %lu bitmapblocks, %lu uniformblocks, %lu%% density\n", + pool_offset(pool, mh), + pool_offset(pool, (char *)(mh+1) + metalen), + free, bitmapblocks, uniformblocks, + (bitmaplen + uniformlen) * 100 + / (free + bitmaplen + uniformlen)); + } + + /* Account for total pages allocated. */ + tot = (count[1] + count[2] - metadata_pages) * getpagesize(); + + fprintf(out, "Total metadata bytes = %lu\n", + metadata_pages * getpagesize()); + + /* Now do every subpage. */ + for (i = 0; i < poolsize / getpagesize(); i++) { + uint8_t *meta; + unsigned int j, allocated; + enum sub_metadata_type type; + + if (get_page_state(pool, i) != SPECIAL) + continue; + + memset(count, 0, sizeof(count)); + + meta = get_page_metadata(pool, i); + type = get_bit_pair(meta, 0); + + if (type == BITMAP) { + for (j = 0; j < SUBPAGE_METAOFF/BITMAP_GRANULARITY; j++) + count[get_page_state(meta, j)]++; + allocated = (count[1] + count[2]) * BITMAP_GRANULARITY; + fprintf(out, "Subpage bitmap "); + } else { + unsigned int usize = decode_usize(meta); + + assert(type == UNIFORM); + fprintf(out, "Subpage uniform (%u) ", usize); + meta += 2; + for (j = 0; j < SUBPAGE_METAOFF / usize; j++) + count[!!(meta[j / 8] & (1 << (j % 8)))]++; + allocated = count[1] * usize; + } + fprintf(out, "%lu: FREE/TAKEN/TAKEN_START = %lu/%lu/%lu %u%% density\n", + i, count[0], count[1], count[2], + allocated * 100 / getpagesize()); + tot += allocated; + } + + /* This is optimistic, since we overalloc in several cases. */ + fprintf(out, "Best possible allocation density = %lu%%\n", + tot * 100 / poolsize); +} diff --git a/ccan/alloc/alloc.h b/ccan/alloc/alloc.h new file mode 100644 index 00000000..29c29d07 --- /dev/null +++ b/ccan/alloc/alloc.h @@ -0,0 +1,13 @@ +#ifndef ALLOC_H +#define ALLOC_H +#include +#include + +void alloc_init(void *pool, unsigned long poolsize); +void *alloc_get(void *pool, unsigned long poolsize, + unsigned long size, unsigned long align); +void alloc_free(void *pool, unsigned long poolsize, void *free); +bool alloc_check(void *pool, unsigned long poolsize); + +void alloc_visualize(FILE *out, void *pool, unsigned long poolsize); +#endif /* ALLOC_H */ diff --git a/ccan/alloc/test/run.c b/ccan/alloc/test/run.c new file mode 100644 index 00000000..8cfe8b42 --- /dev/null +++ b/ccan/alloc/test/run.c @@ -0,0 +1,195 @@ +#include "alloc/alloc.h" +#include "tap/tap.h" +#include "alloc/alloc.c" +#include + +#define POOL_ORD 16 +#define POOL_SIZE (1 << POOL_ORD) + +#define sort(p, num, cmp) \ + qsort((p), (num), sizeof(*p), (int(*)(const void *, const void *))cmp) + +static int addr_cmp(void **a, void **b) +{ + return (*a) - (*b); +} + +static bool unique(void *p[], unsigned int num) +{ + unsigned int i; + + for (i = 1; i < num; i++) + if (p[i] == p[i-1]) + return false; + return true; +} + +static bool free_every_second_one(void *mem, unsigned int num, void *p[]) +{ + unsigned int i; + + /* Free every second one. */ + for (i = 0; i < num; i += 2) { + alloc_free(mem, POOL_SIZE, p[i]); + if (!alloc_check(mem, POOL_SIZE)) + return false; + } + for (i = 1; i < num; i += 2) { + alloc_free(mem, POOL_SIZE, p[i]); + if (!alloc_check(mem, POOL_SIZE)) + return false; + } + return true; +} + + +int main(int argc, char *argv[]) +{ + void *mem; + unsigned int i, num, max_size; + void *p[POOL_SIZE]; + + plan_tests(139); + + /* FIXME: Needs to be page aligned for now. */ + posix_memalign(&mem, 1 << POOL_ORD, POOL_SIZE); + + /* Small pool, all allocs fail, even 0-length. */ + alloc_init(mem, 0); + ok1(alloc_check(mem, 0)); + ok1(alloc_get(mem, 0, 1, 1) == NULL); + ok1(alloc_get(mem, 0, 128, 1) == NULL); + ok1(alloc_get(mem, 0, 0, 1) == NULL); + + /* Free of NULL should work. */ + alloc_free(mem, 0, NULL); + + alloc_init(mem, POOL_SIZE); + ok1(alloc_check(mem, POOL_SIZE)); + /* Find largest allocation which works. */ + for (max_size = POOL_SIZE * 2; max_size; max_size--) { + p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); + if (p[0]) + break; + } + ok1(max_size < POOL_SIZE); + ok1(max_size > 0); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Free it, should be able to reallocate it. */ + alloc_free(mem, POOL_SIZE, p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + + p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); + ok1(p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + alloc_free(mem, POOL_SIZE, p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Allocate a whole heap. */ + for (i = 0; i < POOL_SIZE; i++) { + p[i] = alloc_get(mem, POOL_SIZE, 1, 1); + if (!p[i]) + break; + } + + /* Uncomment this for a more intuitive view of what the + * allocator looks like after all these 1 byte allocs. */ +#if 0 + alloc_visualize(stderr, mem, POOL_SIZE); +#endif + + num = i; + /* Can't allocate this many. */ + ok1(num != POOL_SIZE); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Sort them. */ + sort(p, num, addr_cmp); + + /* Uniqueness check */ + ok1(unique(p, num)); + + ok1(free_every_second_one(mem, num, p)); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Should be able to reallocate max size. */ + p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); + ok1(p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Re-initializing should be the same as freeing everything */ + alloc_init(mem, POOL_SIZE); + ok1(alloc_check(mem, POOL_SIZE)); + p[0] = alloc_get(mem, POOL_SIZE, max_size, 1); + ok1(p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + alloc_free(mem, POOL_SIZE, p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Alignment constraints should be met, as long as powers of two */ + for (i = 0; i < POOL_ORD-1; i++) { + p[i] = alloc_get(mem, POOL_SIZE, i, 1 << i); + ok1(p[i]); + ok1(((unsigned long)p[i] % (1 << i)) == 0); + ok1(alloc_check(mem, POOL_SIZE)); + } + + for (i = 0; i < POOL_ORD-1; i++) { + alloc_free(mem, POOL_SIZE, p[i]); + ok1(alloc_check(mem, POOL_SIZE)); + } + + /* Alignment constraints for a single-byte allocation. */ + for (i = 0; i < POOL_ORD; i++) { + p[0] = alloc_get(mem, POOL_SIZE, 1, 1 << i); + ok1(p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + alloc_free(mem, POOL_SIZE, p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + } + + /* Alignment check for a 0-byte allocation. Corner case. */ + p[0] = alloc_get(mem, POOL_SIZE, 0, 1 << (POOL_ORD - 1)); + ok1(alloc_check(mem, POOL_SIZE)); + alloc_free(mem, POOL_SIZE, p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Force the testing of split metadata. */ + alloc_init(mem, POOL_SIZE); + for (i = 0; i < POOL_SIZE; i++) { + p[i] = alloc_get(mem, POOL_SIZE, getpagesize(), getpagesize()); + if (!p[i]) + break; + } + ok1(alloc_check(mem, POOL_SIZE)); + + /* Sort them. */ + sort(p, i-1, addr_cmp); + + /* Free all but the one next to the metadata. */ + for (i = 1; p[i]; i++) + alloc_free(mem, POOL_SIZE, p[i]); + ok1(alloc_check(mem, POOL_SIZE)); + + /* Now do a whole heap of subpage allocs. */ + for (i = 1; i < POOL_SIZE; i++) { + p[i] = alloc_get(mem, POOL_SIZE, 1, 1); + if (!p[i]) + break; + } + ok1(alloc_check(mem, POOL_SIZE)); + + /* Free up our page next to metadata, and should be able to alloc */ + alloc_free(mem, POOL_SIZE, p[0]); + ok1(alloc_check(mem, POOL_SIZE)); + p[0] = alloc_get(mem, POOL_SIZE, 1, 1); + ok1(p[0]); + + /* Clean up. */ + for (i = 0; p[i]; i++) + alloc_free(mem, POOL_SIZE, p[i]); + ok1(alloc_check(mem, POOL_SIZE)); + + return exit_status(); +} diff --git a/ccan/build_assert/_info.c b/ccan/build_assert/_info.c new file mode 100644 index 00000000..555aae21 --- /dev/null +++ b/ccan/build_assert/_info.c @@ -0,0 +1,38 @@ +#include +#include +#include "config.h" + +/** + * build_assert - routines for build-time assertions + * + * This code provides routines which will cause compilation to fail should some + * assertion be untrue: such failures are preferable to run-time assertions, + * but much more limited since they can only depends on compile-time constants. + * + * These assertions are most useful when two parts of the code must be kept in + * sync: it is better to avoid such cases if possible, but seconds best is to + * detect invalid changes at build time. + * + * For example, a tricky piece of code might rely on a certain element being at + * the start of the structure. To ensure that future changes don't break it, + * you would catch such changes in your code like so: + * + * Example: + * char *foo_string(struct foo *foo) + * { + * // This trick requires that the string be first in the structure + * BUILD_ASSERT(offsetof(struct foo, string) == 0); + * return (char *)foo; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + /* Nothing. */ + return 0; + + return 1; +} diff --git a/ccan/build_assert/build_assert.h b/ccan/build_assert/build_assert.h new file mode 100644 index 00000000..4b0d75e4 --- /dev/null +++ b/ccan/build_assert/build_assert.h @@ -0,0 +1,37 @@ +#ifndef CCAN_BUILD_ASSERT_H +#define CCAN_BUILD_ASSERT_H + +/** + * BUILD_ASSERT - assert a build-time dependency. + * @cond: the compile-time condition which must be true. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can only be used within a function. + * + * Example: + * char *foo_to_char(struct foo *foo) + * { + * // This code needs string to be at start of foo. + * BUILD_ASSERT(offsetof(struct foo, string) == 0); + * return (char *)foo; + * } + */ +#define BUILD_ASSERT(cond) \ + do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) + +/** + * EXPR_BUILD_ASSERT - assert a build-time dependency, as an expression. + * @cond: the compile-time condition which must be true. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can be used in an expression: its value is "0". + * + * Example: + * #define foo_to_char(foo) \ + * ((char *)(foo) \ + * + EXPR_BUILD_ASSERT(offsetof(struct foo, string) == 0)) + */ +#define EXPR_BUILD_ASSERT(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#endif /* CCAN_BUILD_ASSERT_H */ diff --git a/ccan/build_assert/test/compile_fail-expr.c b/ccan/build_assert/test/compile_fail-expr.c new file mode 100644 index 00000000..41cdc0f8 --- /dev/null +++ b/ccan/build_assert/test/compile_fail-expr.c @@ -0,0 +1,10 @@ +#include "build_assert/build_assert.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + return EXPR_BUILD_ASSERT(1 == 0); +#else + return 0; +#endif +} diff --git a/ccan/build_assert/test/compile_fail.c b/ccan/build_assert/test/compile_fail.c new file mode 100644 index 00000000..a6867db5 --- /dev/null +++ b/ccan/build_assert/test/compile_fail.c @@ -0,0 +1,9 @@ +#include "build_assert/build_assert.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + BUILD_ASSERT(1 == 0); +#endif + return 0; +} diff --git a/ccan/build_assert/test/compile_ok.c b/ccan/build_assert/test/compile_ok.c new file mode 100644 index 00000000..bc5541f5 --- /dev/null +++ b/ccan/build_assert/test/compile_ok.c @@ -0,0 +1,7 @@ +#include "build_assert/build_assert.h" + +int main(int argc, char *argv[]) +{ + BUILD_ASSERT(1 == 1); + return 0; +} diff --git a/ccan/build_assert/test/run-EXPR_BUILD_ASSERT.c b/ccan/build_assert/test/run-EXPR_BUILD_ASSERT.c new file mode 100644 index 00000000..7fd0c49f --- /dev/null +++ b/ccan/build_assert/test/run-EXPR_BUILD_ASSERT.c @@ -0,0 +1,9 @@ +#include "build_assert/build_assert.h" +#include "tap/tap.h" + +int main(int argc, char *argv[]) +{ + plan_tests(1); + ok1(EXPR_BUILD_ASSERT(1 == 1) == 0); + return exit_status(); +} diff --git a/ccan/check_type/_info.c b/ccan/check_type/_info.c new file mode 100644 index 00000000..176e445a --- /dev/null +++ b/ccan/check_type/_info.c @@ -0,0 +1,30 @@ +#include +#include +#include "config.h" + +/** + * check_type - routines for compile time type checking + * + * C has fairly weak typing: ints get automatically converted to longs, signed + * to unsigned, etc. There are some cases where this is best avoided, and + * these macros provide methods for evoking warnings (or build errors) when + * a precise type isn't used. + * + * On compilers which don't support typeof() these routines are less effective, + * since they have to use sizeof() which can only distiguish between types of + * different size. + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { +#if !HAVE_TYPEOF + printf("ccan/build_assert\n"); +#endif + return 0; + } + + return 1; +} diff --git a/ccan/check_type/check_type.h b/ccan/check_type/check_type.h new file mode 100644 index 00000000..e05236f8 --- /dev/null +++ b/ccan/check_type/check_type.h @@ -0,0 +1,63 @@ +#ifndef CCAN_CHECK_TYPE_H +#define CCAN_CHECK_TYPE_H +#include "config.h" + +/** + * check_type - issue a warning or build failure if type is not correct. + * @expr: the expression whose type we should check (not evaluated). + * @type: the exact type we expect the expression to be. + * + * This macro is usually used within other macros to try to ensure that a macro + * argument is of the expected type. No type promotion of the expression is + * done: an unsigned int is not the same as an int! + * + * check_type() always evaluates to 1. + * + * If your compiler does not support typeof, then the best we can do is fail + * to compile if the sizes of the types are unequal (a less complete check). + * + * Example: + * // They should always pass a 64-bit value to _set_some_value! + * #define set_some_value(expr) \ + * _set_some_value((check_type((expr), uint64_t), (expr))) + */ + +/** + * check_types_match - issue a warning or build failure if types are not same. + * @expr1: the first expression (not evaluated). + * @expr2: the second expression (not evaluated). + * + * This macro is usually used within other macros to try to ensure that + * arguments are of identical types. No type promotion of the expressions is + * done: an unsigned int is not the same as an int! + * + * check_types_match() always evaluates to 1. + * + * If your compiler does not support typeof, then the best we can do is fail + * to compile if the sizes of the types are unequal (a less complete check). + * + * Example: + * // Do subtraction to get to enclosing type, but make sure that + * // pointer is of correct type for that member. + * #define container_of(mbr_ptr, encl_type, mbr) \ + * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ + * ((encl_type *) \ + * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) + */ +#if HAVE_TYPEOF +#define check_type(expr, type) \ + ((typeof(expr) *)0 != (type *)0) + +#define check_types_match(expr1, expr2) \ + ((typeof(expr1) *)0 != (typeof(expr2) *)0) +#else +#include "build_assert/build_assert.h" +/* Without typeof, we can only test the sizes. */ +#define check_type(expr, type) \ + EXPR_BUILD_ASSERT(sizeof(expr) == sizeof(type)) + +#define check_types_match(expr1, expr2) \ + EXPR_BUILD_ASSERT(sizeof(expr1) == sizeof(expr2)) +#endif /* HAVE_TYPEOF */ + +#endif /* CCAN_CHECK_TYPE_H */ diff --git a/ccan/check_type/test/compile_fail-check_type.c b/ccan/check_type/test/compile_fail-check_type.c new file mode 100644 index 00000000..d19fe86f --- /dev/null +++ b/ccan/check_type/test/compile_fail-check_type.c @@ -0,0 +1,9 @@ +#include "check_type/check_type.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + check_type(argc, char); +#endif + return 0; +} diff --git a/ccan/check_type/test/compile_fail-check_type_unsigned.c b/ccan/check_type/test/compile_fail-check_type_unsigned.c new file mode 100644 index 00000000..6b18acb5 --- /dev/null +++ b/ccan/check_type/test/compile_fail-check_type_unsigned.c @@ -0,0 +1,14 @@ +#include "check_type/check_type.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if HAVE_TYPEOF + check_type(argc, unsigned int); +#else + /* This doesn't work without typeof, so just fail */ +#error "Fail without typeof" +#endif +#endif + return 0; +} diff --git a/ccan/check_type/test/compile_fail-check_types_match.c b/ccan/check_type/test/compile_fail-check_types_match.c new file mode 100644 index 00000000..bc1f9c31 --- /dev/null +++ b/ccan/check_type/test/compile_fail-check_types_match.c @@ -0,0 +1,10 @@ +#include "check_type/check_type.h" + +int main(int argc, char *argv[]) +{ + unsigned char x = argc; +#ifdef FAIL + check_types_match(argc, x); +#endif + return x; +} diff --git a/ccan/check_type/test/run.c b/ccan/check_type/test/run.c new file mode 100644 index 00000000..f4b33c16 --- /dev/null +++ b/ccan/check_type/test/run.c @@ -0,0 +1,22 @@ +#include "check_type/check_type.h" +#include "tap/tap.h" + +int main(int argc, char *argv[]) +{ + int x = 0, y = 0; + + plan_tests(9); + + ok1(check_type(argc, int) == 0); + ok1(check_type(&argc, int *) == 0); + ok1(check_types_match(argc, argc) == 0); + ok1(check_types_match(argc, x) == 0); + ok1(check_types_match(&argc, &x) == 0); + + ok1(check_type(x++, int) == 0); + ok(x == 0, "check_type does not evaluate expression"); + ok1(check_types_match(x++, y++) == 0); + ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); + + return exit_status(); +} diff --git a/ccan/container_of/_info.c b/ccan/container_of/_info.c new file mode 100644 index 00000000..96c12d88 --- /dev/null +++ b/ccan/container_of/_info.c @@ -0,0 +1,47 @@ +#include +#include +#include "config.h" + +/** + * container_of - routine for upcasting + * + * It is often convenient to create code where the caller registers a pointer + * to a generic structure and a callback. The callback might know that the + * pointer points to within a larger structure, and container_of gives a + * convenient and fairly type-safe way of returning to the enclosing structure. + * + * This idiom is an alternative to providing a void * pointer for every + * callback. + * + * Example: + * struct info + * { + * int my_stuff; + * struct timer timer; + * }; + * + * static void my_timer_callback(struct timer *timer) + * { + * struct info *info = container_of(timer, struct info, timer); + * printf("my_stuff is %u\n", info->my_stuff); + * } + * + * int main() + * { + * struct info info = { .my_stuff = 1 }; + * + * register_timer(&info.timer); + * ... + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/check_type\n"); + return 0; + } + + return 1; +} diff --git a/ccan/container_of/container_of.h b/ccan/container_of/container_of.h new file mode 100644 index 00000000..1f4b18e4 --- /dev/null +++ b/ccan/container_of/container_of.h @@ -0,0 +1,65 @@ +#ifndef CCAN_CONTAINER_OF_H +#define CCAN_CONTAINER_OF_H +#include + +#include "config.h" +#include "check_type/check_type.h" + +/** + * container_of - get pointer to enclosing structure + * @member_ptr: pointer to the structure member + * @containing_type: the type this member is within + * @member: the name of this member within the structure. + * + * Given a pointer to a member of a structure, this macro does pointer + * subtraction to return the pointer to the enclosing type. + * + * Example: + * struct info + * { + * int some_other_field; + * struct foo my_foo; + * }; + * + * struct info *foo_to_info(struct foo *foop) + * { + * return container_of(foo, struct info, my_foo); + * } + */ +#define container_of(member_ptr, containing_type, member) \ + ((containing_type *) \ + ((char *)(member_ptr) - offsetof(containing_type, member)) \ + - check_types_match(*(member_ptr), ((containing_type *)0)->member)) + + +/** + * container_of_var - get pointer to enclosing structure using a variable + * @member_ptr: pointer to the structure member + * @var: a pointer to a structure of same type as this member is within + * @member: the name of this member within the structure. + * + * Given a pointer to a member of a structure, this macro does pointer + * subtraction to return the pointer to the enclosing type. + * + * Example: + * struct info + * { + * int some_other_field; + * struct foo my_foo; + * }; + * + * struct info *foo_to_info(struct foo *foop) + * { + * struct info *i = container_of_var(foo, i, my_foo); + * return i; + * } + */ +#ifdef HAVE_TYPEOF +#define container_of_var(member_ptr, var, member) \ + container_of(member_ptr, typeof(*var), member) +#else +#define container_of_var(member_ptr, var, member) \ + ((void *)((char *)(member_ptr) - offsetof(containing_type, member))) +#endif + +#endif /* CCAN_CONTAINER_OF_H */ diff --git a/ccan/container_of/test/compile_fail-bad-type.c b/ccan/container_of/test/compile_fail-bad-type.c new file mode 100644 index 00000000..01dfd454 --- /dev/null +++ b/ccan/container_of/test/compile_fail-bad-type.c @@ -0,0 +1,22 @@ +#include "container_of/container_of.h" +#include + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }; + int *intp = &foo.a; + char *p; + +#ifdef FAIL + /* p is a char *, but this gives a struct foo * */ + p = container_of(intp, struct foo, a); +#else + p = (char *)intp; +#endif + return p == NULL; +} diff --git a/ccan/container_of/test/compile_fail-types.c b/ccan/container_of/test/compile_fail-types.c new file mode 100644 index 00000000..69f02daf --- /dev/null +++ b/ccan/container_of/test/compile_fail-types.c @@ -0,0 +1,21 @@ +#include "container_of/container_of.h" +#include + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }, *foop; + int *intp = &foo.a; + +#ifdef FAIL + /* b is a char, but intp is an int * */ + foop = container_of(intp, struct foo, b); +#else + foop = NULL; +#endif + return intp == NULL; +} diff --git a/ccan/container_of/test/compile_fail-var-types.c b/ccan/container_of/test/compile_fail-var-types.c new file mode 100644 index 00000000..5c776798 --- /dev/null +++ b/ccan/container_of/test/compile_fail-var-types.c @@ -0,0 +1,21 @@ +#include "container_of/container_of.h" +#include + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }, *foop; + int *intp = &foo.a; + +#ifdef FAIL + /* b is a char, but intp is an int * */ + foop = container_of_var(intp, foop, b); +#else + foop = NULL; +#endif + return intp == NULL; +} diff --git a/ccan/container_of/test/run.c b/ccan/container_of/test/run.c new file mode 100644 index 00000000..dd57204d --- /dev/null +++ b/ccan/container_of/test/run.c @@ -0,0 +1,21 @@ +#include "container_of/container_of.h" +#include "tap/tap.h" + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }; + int *intp = &foo.a; + char *charp = &foo.b; + + plan_tests(4); + ok1(container_of(intp, struct foo, a) == &foo); + ok1(container_of(charp, struct foo, b) == &foo); + ok1(container_of_var(intp, &foo, a) == &foo); + ok1(container_of_var(charp, &foo, b) == &foo); + return exit_status(); +} diff --git a/ccan/list/_info.c b/ccan/list/_info.c new file mode 100644 index 00000000..dc3c3e64 --- /dev/null +++ b/ccan/list/_info.c @@ -0,0 +1,63 @@ +#include +#include +#include "config.h" + +/** + * list - double linked list routines + * + * The list header contains routines for manipulating double linked lists. + * It defines two types: struct list_head used for anchoring lists, and + * struct list_node which is usually embedded in the structure which is placed + * in the list. + * + * Example: + * #include + * #include "list/list.h" + * + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + * + * struct child { + * const char *name; + * struct list_node list; + * }; + * + * int main(int argc, char *argv[]) + * { + * struct parent p; + * struct child *c; + * unsigned int i; + * + * if (argc < 2) + * errx(1, "Usage: %s parent children...", argv[0]); + * + * p.name = argv[1]; + * for (i = 2; i < argc, i++) { + * c = malloc(sizeof(*c)); + * c->name = argv[i]; + * list_add(&p.children, &c->list); + * p.num_children++; + * } + * + * printf("%s has %u children:", p.name, p.num_children); + * list_for_each(&p.children, c, list) + * printf("%s ", c->name); + * printf("\n"); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/container_of\n"); + return 0; + } + + return 1; +} diff --git a/ccan/list/list.c b/ccan/list/list.c new file mode 100644 index 00000000..b72f8f67 --- /dev/null +++ b/ccan/list/list.c @@ -0,0 +1,33 @@ +#include +#include +#include "list/list.h" + +struct list_head *list_check(struct list_head *h, const char *abortstr) +{ + struct list_node *n, *p; + int count = 0; + + if (h->n.next == &h->n) { + if (h->n.prev != &h->n) { + if (!abortstr) + return NULL; + fprintf(stderr, "%s: prev corrupt in empty %p\n", + abortstr, h); + abort(); + } + return h; + } + + for (p = &h->n, n = h->n.next; n != &h->n; p = n, n = n->next) { + count++; + if (n->prev != p) { + if (!abortstr) + return NULL; + fprintf(stderr, + "%s: prev corrupt in node %p (%u) of %p\n", + abortstr, n, count, h); + abort(); + } + } + return h; +} diff --git a/ccan/list/list.h b/ccan/list/list.h new file mode 100644 index 00000000..c664d834 --- /dev/null +++ b/ccan/list/list.h @@ -0,0 +1,253 @@ +#ifndef CCAN_LIST_H +#define CCAN_LIST_H +#include +#include "container_of/container_of.h" + +/** + * struct list_node - an entry in a doubly-linked list + * @next: next entry (self if empty) + * @prev: previous entry (self if empty) + * + * This is used as an entry in a linked list. + * Example: + * struct child { + * const char *name; + * // Linked list of all us children. + * struct list_node list; + * }; + */ +struct list_node +{ + struct list_node *next, *prev; +}; + +/** + * struct list_head - the head of a doubly-linked list + * @h: the list_head (containing next and prev pointers) + * + * This is used as the head of a linked list. + * Example: + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + */ +struct list_head +{ + struct list_node n; +}; + +/** + * list_check - check a list for consistency + * @h: the list_head + * @abortstr: the location to print on aborting, or NULL. + * + * Because list_nodes have redundant information, consistency checking between + * the back and forward links can be done. This is useful as a debugging check. + * If @abortstr is non-NULL, that will be printed in a diagnostic if the list + * is inconsistent, and the function will abort. + * + * Returns the list head if the list is consistent, NULL if not (it + * can never return NULL if @abortstr is set). + * + * Example: + * static void dump_parent(struct parent *p) + * { + * struct child *c; + * + * printf("%s (%u children):\n", p->name, parent->num_children); + * list_check(&p->children, "bad child list"); + * list_for_each(&p->children, c, list) + * printf(" -> %s\n", c->name); + * } + */ +struct list_head *list_check(struct list_head *h, const char *abortstr); + +#ifdef CCAN_LIST_DEBUG +#define debug_list(h) list_check((h), __func__) +#else +#define debug_list(h) (h) +#endif + +/** + * list_head_init - initialize a list_head + * @h: the list_head to set to the empty list + * + * Example: + * list_head_init(&parent->children); + * parent->num_children = 0; + */ +static inline void list_head_init(struct list_head *h) +{ + h->n.next = h->n.prev = &h->n; +} + +/** + * LIST_HEAD - define and initalized empty list_head + * @name: the name of the list. + * + * The LIST_HEAD macro defines a list_head and initializes it to an empty + * list. It can be prepended by "static" to define a static list_head. + * + * Example: + * // Header: + * extern struct list_head my_list; + * + * // C file: + * LIST_HEAD(my_list); + */ +#define LIST_HEAD(name) \ + struct list_head name = { { &name.n, &name.n } } + +/** + * list_add - add an entry at the start of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * list_add(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add(struct list_head *h, struct list_node *n) +{ + n->next = h->n.next; + n->prev = &h->n; + h->n.next->prev = n; + h->n.next = n; + (void)debug_list(h); +} + +/** + * list_add_tail - add an entry at the end of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * list_add_tail(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add_tail(struct list_head *h, struct list_node *n) +{ + n->next = &h->n; + n->prev = h->n.prev; + h->n.prev->next = n; + h->n.prev = n; + (void)debug_list(h); +} + +/** + * list_del - delete an entry from a linked list. + * @n: the list_node to delete from the list. + * + * Example: + * list_del(&child->list); + * parent->num_children--; + */ +static inline void list_del(struct list_node *n) +{ + n->next->prev = n->prev; + n->prev->next = n->next; + (void)debug_list(n->next); +#ifdef CCAN_LIST_DEBUG + /* Catch use-after-del. */ + n->next = n->prev = NULL; +#endif +} + +/** + * list_empty - is a list empty? + * @h: the list_head + * + * If the list is empty, returns true. + * + * Example: + * assert(list_empty(&parent->children) == (parent->num_children == 0)); + */ +static inline bool list_empty(struct list_head *h) +{ + (void)debug_list(h); + return h->n.next == &h->n; +} + +/** + * list_entry - convert a list_node back into the structure containing it. + * @n: the list_node + * @type: the type of the entry + * @member: the list_node member of the type + * + * Example: + * struct child *c; + * // First list entry is children.next; convert back to child. + * c = list_entry(parent->children.next, struct child, list); + */ +#define list_entry(n, type, member) container_of(n, type, member) + +/** + * list_top - get the first entry in a list + * @h: the list_head + * @type: the type of the entry + * @member: the list_node member of the type + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *first; + * first = list_top(&parent->children, struct child, list); + */ +#define list_top(h, type, member) \ + list_entry(_list_top(h), type, member) + +static inline struct list_node *_list_top(struct list_head *h) +{ + (void)debug_list(h); + if (list_empty(h)) + return NULL; + return h->n.next; +} + +/** + * list_for_each - iterate through a list. + * @h: the list_head + * @i: the structure containing the list_node + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. + * + * Example: + * struct child *c; + * list_for_each(&parent->children, c, list) + * printf("Name: %s\n", c->name); + */ +#define list_for_each(h, i, member) \ + for (i = container_of_var(debug_list(h)->n.next, i, member); \ + &i->member != &(h)->n; \ + i = container_of_var(i->member.next, i, member)) + +/** + * list_for_each_safe - iterate through a list, maybe during deletion + * @h: the list_head + * @i: the structure containing the list_node + * @nxt: the structure containing the list_node + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. The extra variable + * @nxt is used to hold the next element, so you can delete @i from the list. + * + * Example: + * struct child *c, *n; + * list_for_each_safe(&parent->children, c, n, list) { + * list_del(&c->list); + * parent->num_children--; + * } + */ +#define list_for_each_safe(h, i, nxt, member) \ + for (i = container_of_var(debug_list(h)->n.next, i, member), \ + nxt = container_of_var(i->member.next, i, member); \ + &i->member != &(h)->n; \ + i = nxt, nxt = container_of_var(i->member.next, i, member)) +#endif /* CCAN_LIST_H */ diff --git a/ccan/list/test/run.c b/ccan/list/test/run.c new file mode 100644 index 00000000..66b9630d --- /dev/null +++ b/ccan/list/test/run.c @@ -0,0 +1,118 @@ +#include "list/list.h" +#include "tap/tap.h" +#include "list/list.c" + +struct parent { + const char *name; + struct list_head children; + unsigned int num_children; +}; + +struct child { + const char *name; + struct list_node list; +}; + +static LIST_HEAD(static_list); + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + + plan_tests(41); + /* Test LIST_HEAD, list_empty and check_list */ + ok1(list_empty(&static_list)); + ok1(list_check(&static_list, NULL)); + + parent.num_children = 0; + list_head_init(&parent.children); + /* Test list_head_init */ + ok1(list_empty(&parent.children)); + ok1(list_check(&parent.children, NULL)); + + c2.name = "c2"; + list_add(&parent.children, &c2.list); + /* Test list_add and !list_empty. */ + ok1(!list_empty(&parent.children)); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &parent.children.n); + ok1(parent.children.n.next == &c2.list); + ok1(parent.children.n.prev == &c2.list); + /* Test list_check */ + ok1(list_check(&parent.children, NULL)); + + c1.name = "c1"; + list_add(&parent.children, &c1.list); + /* Test list_add and !list_empty. */ + ok1(!list_empty(&parent.children)); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &c1.list); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c2.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + /* Test list_check */ + ok1(list_check(&parent.children, NULL)); + + c3.name = "c3"; + list_add_tail(&parent.children, &c3.list); + /* Test list_add_tail and !list_empty. */ + ok1(!list_empty(&parent.children)); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c3.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + ok1(c2.list.next == &c3.list); + ok1(c2.list.prev == &c1.list); + ok1(c3.list.next == &parent.children.n); + ok1(c3.list.prev == &c2.list); + /* Test list_check */ + ok1(list_check(&parent.children, NULL)); + + /* Test list_top */ + ok1(list_top(&parent.children, struct child, list) == &c1); + + /* Test list_for_each. */ + i = 0; + list_for_each(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test list_for_each_safe and list_del. */ + i = 0; + list_for_each_safe(&parent.children, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + list_del(&c->list); + ok1(list_check(&parent.children, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(list_empty(&parent.children)); + return exit_status(); +} diff --git a/ccan/noerr/_info.c b/ccan/noerr/_info.c new file mode 100644 index 00000000..96fbbcd1 --- /dev/null +++ b/ccan/noerr/_info.c @@ -0,0 +1,54 @@ +#include +#include +#include "config.h" + +/** + * noerr - routines for cleaning up without blatting errno + * + * It is a good idea to follow the standard C convention of setting errno in + * your own helper functions. Unfortunately, care must be taken in the error + * paths as most standard functions can (and do) overwrite errno, even if they + * succeed. + * + * Example: + * #include + * #include + * #include + * + * bool write_string_to_file(const char *file, const char *string) + * { + * int ret, fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0600); + * if (fd < 0) + * return false; + * ret = write(fd, string, strlen(string)); + * if (ret < 0) { + * // Preserve errno from write above. + * close_noerr(fd); + * unlink_noerr(file); + * return false; + * } + * if (close(fd) != 0) { + * // Again, preserve errno. + * unlink_noerr(file); + * return false; + * } + * // A short write means out of space. + * if (ret < strlen(string)) { + * unlink(file); + * errno = ENOSPC; + * return false; + * } + * return true; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + /* Nothing. */ + return 0; + + return 1; +} diff --git a/ccan/noerr/noerr.c b/ccan/noerr/noerr.c new file mode 100644 index 00000000..d6df81f7 --- /dev/null +++ b/ccan/noerr/noerr.c @@ -0,0 +1,29 @@ +#include "noerr.h" +#include +#include + +int close_noerr(int fd) +{ + int saved_errno = errno, ret; + + if (close(fd) != 0) + ret = errno; + else + ret = 0; + + errno = saved_errno; + return ret; +} + +int unlink_noerr(const char *pathname) +{ + int saved_errno = errno, ret; + + if (unlink(pathname) != 0) + ret = errno; + else + ret = 0; + + errno = saved_errno; + return ret; +} diff --git a/ccan/noerr/noerr.h b/ccan/noerr/noerr.h new file mode 100644 index 00000000..559ba613 --- /dev/null +++ b/ccan/noerr/noerr.h @@ -0,0 +1,22 @@ +#ifndef NOERR_H +#define NOERR_H + +/** + * close_noerr - close without stomping errno. + * @fd: the file descriptor to close. + * + * errno is saved and restored across the call to close: if an error occurs, + * the resulting (non-zero) errno is returned. + */ +int close_noerr(int fd); + +/** + * unlink_noerr - unlink a file without stomping errno. + * @pathname: the path to unlink. + * + * errno is saved and restored across the call to unlink: if an error occurs, + * the resulting (non-zero) errno is returned. + */ +int unlink_noerr(const char *pathname); + +#endif /* NOERR_H */ diff --git a/ccan/noerr/test/run.c b/ccan/noerr/test/run.c new file mode 100644 index 00000000..6d3c6837 --- /dev/null +++ b/ccan/noerr/test/run.c @@ -0,0 +1,48 @@ +#include "noerr/noerr.h" +#include "tap/tap.h" +#include "noerr/noerr.c" +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + /* tempnam(3) is generally a bad idea, but OK here. */ + char *name = tempnam(NULL, "noerr"); + int fd; + + plan_tests(12); + /* Should fail to unlink. */ + ok1(unlink(name) != 0); + ok1(errno == ENOENT); + + /* This one should not set errno. */ + errno = 100; + ok1(unlink_noerr(name) == ENOENT); + ok1(errno == 100); + + /* Should fail to close. */ + ok1(close(-1) != 0); + ok1(errno == EBADF); + + /* This one should not set errno. */ + errno = 100; + ok1(close_noerr(-1) == EBADF); + ok1(errno == 100); + + /* Test successful close/unlink doesn't hit errno either. */ + fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600); + assert(fd >= 0); + + errno = 100; + ok1(close_noerr(fd) == 0); + ok1(errno == 100); + + errno = 100; + ok1(unlink_noerr(name) == 0); + ok1(errno == 100); + + return exit_status(); +} diff --git a/ccan/string/_info.c b/ccan/string/_info.c new file mode 100644 index 00000000..9cb691ce --- /dev/null +++ b/ccan/string/_info.c @@ -0,0 +1,35 @@ +#include +#include +#include "config.h" + +/** + * string - string helper routines + * + * This is a grab bag of modules for string comparisons, designed to enhance + * the standard string.h. + * + * Example: + * #include "string/string.h" + * + * int main(int argc, char *argv[]) + * { + * if (argv[1] && streq(argv[1], "--verbose")) + * printf("verbose set\n"); + * if (argv[1] && strstarts(argv[1], "--")) + * printf("Some option set\n"); + * if (argv[1] && strends(argv[1], "cow-powers")) + * printf("Magic option set\n"); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + /* Nothing. */ + return 0; + + return 1; +} diff --git a/ccan/string/string.h b/ccan/string/string.h new file mode 100644 index 00000000..f4997c69 --- /dev/null +++ b/ccan/string/string.h @@ -0,0 +1,46 @@ +#ifndef CCAN_STRING_H +#define CCAN_STRING_H +#include +#include + +/** + * streq - Are two strings equal? + * @a: first string + * @b: first string + * + * This macro is arguably more readable than "!strcmp(a, b)". + * + * Example: + * if (streq(str, "")) + * printf("String is empty!\n"); + */ +#define streq(a,b) (strcmp((a),(b)) == 0) + +/** + * strstarts - Does this string start with this prefix? + * @str: string to test + * @prefix: prefix to look for at start of str + * + * Example: + * if (strstarts(str, "foo")) + * printf("String %s begins with 'foo'!\n", str); + */ +#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) + +/** + * strends - Does this string end with this postfix? + * @str: string to test + * @postfix: postfix to look for at end of str + * + * Example: + * if (strends(str, "foo")) + * printf("String %s end with 'foo'!\n", str); + */ +static inline bool strends(const char *str, const char *postfix) +{ + if (strlen(str) < strlen(postfix)) + return false; + + return streq(str + strlen(str) - strlen(postfix), postfix); +} +#endif /* CCAN_STRING_H */ diff --git a/ccan/string/test/run.c b/ccan/string/test/run.c new file mode 100644 index 00000000..fded0e49 --- /dev/null +++ b/ccan/string/test/run.c @@ -0,0 +1,77 @@ +#include +#include +#include "string/string.h" +#include "tap/tap.h" + +/* FIXME: ccanize */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +static char *substrings[] = { "far", "bar", "baz", "b", "ba", "z", "ar" }; + +static char *strdup_rev(const char *s) +{ + char *ret = strdup(s); + unsigned int i; + + for (i = 0; i < strlen(s); i++) + ret[i] = s[strlen(s) - i - 1]; + return ret; +} + +int main(int argc, char *argv[]) +{ + unsigned int i, j, n; + char *strings[ARRAY_SIZE(substrings) * ARRAY_SIZE(substrings)]; + + n = 0; + for (i = 0; i < ARRAY_SIZE(substrings); i++) { + for (j = 0; j < ARRAY_SIZE(substrings); j++) { + strings[n] = malloc(strlen(substrings[i]) + + strlen(substrings[j]) + 1); + sprintf(strings[n++], "%s%s", + substrings[i], substrings[j]); + } + } + + plan_tests(n * n * 5); + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + unsigned int k, identical = 0; + char *reva, *revb; + + /* Find first difference. */ + for (k = 0; strings[i][k]==strings[j][k]; k++) { + if (k == strlen(strings[i])) { + identical = 1; + break; + } + } + + if (identical) + ok1(streq(strings[i], strings[j])); + else + ok1(!streq(strings[i], strings[j])); + + /* Postfix test should be equivalent to prefix + * test on reversed string. */ + reva = strdup_rev(strings[i]); + revb = strdup_rev(strings[j]); + + if (!strings[i][k]) { + ok1(strstarts(strings[j], strings[i])); + ok1(strends(revb, reva)); + } else { + ok1(!strstarts(strings[j], strings[i])); + ok1(!strends(revb, reva)); + } + if (!strings[j][k]) { + ok1(strstarts(strings[i], strings[j])); + ok1(strends(reva, revb)); + } else { + ok1(!strstarts(strings[i], strings[j])); + ok1(!strends(reva, revb)); + } + } + } + return exit_status(); +} diff --git a/ccan/talloc/TODO b/ccan/talloc/TODO new file mode 100644 index 00000000..0671a6dd --- /dev/null +++ b/ccan/talloc/TODO @@ -0,0 +1,2 @@ +- Remove talloc.h cruft +- Restore errno around (successful) talloc_free. diff --git a/ccan/talloc/_info.c b/ccan/talloc/_info.c new file mode 100644 index 00000000..bc48736f --- /dev/null +++ b/ccan/talloc/_info.c @@ -0,0 +1,103 @@ +#include +#include +#include "config.h" + +/** + * talloc - tree allocator routines + * + * Talloc is a hierarchical memory pool system with destructors: you keep your + * objects in heirarchies reflecting their lifetime. Every pointer returned + * from talloc() is itself a valid talloc context, from which other talloc()s + * can be attached. This means you can do this: + * + * struct foo *X = talloc(mem_ctx, struct foo); + * X->name = talloc_strdup(X, "foo"); + * + * and the pointer X->name would be a "child" of the talloc context "X" which + * is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) then it is + * all destroyed, whereas if you do talloc_free(X) then just X and X->name are + * destroyed, and if you do talloc_free(X->name) then just the name element of + * X is destroyed. + * + * If you think about this, then what this effectively gives you is an n-ary + * tree, where you can free any part of the tree with talloc_free(). + * + * Talloc has been measured with a time overhead of around 4% over glibc + * malloc, and 48/80 bytes per allocation (32/64 bit). + * + * This version is based on svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc revision 23158. + * + * Example: + * #include + * #include + * #include + * #include "talloc/talloc.h" + * + * // A structure containing a popened comman. + * struct command + * { + * FILE *f; + * const char *command; + * }; + * + * // When struct command is freed, we also want to pclose pipe. + * static int close_cmd(struct command *cmd) + * { + * pclose(cmd->f); + * // 0 means "we succeeded, continue freeing" + * return 0; + * } + * + * // This function opens a writable pipe to the given command. + * struct command *open_output_cmd(const void *ctx, char *fmt, ...) + * { + * va_list ap; + * struct command *cmd = talloc(ctx, struct command); + * + * if (!cmd) + * return NULL; + * + * va_start(ap, fmt); + * cmd->command = talloc_vasprintf(cmd, fmt, ap); + * va_end(ap); + * if (!cmd->command) { + * talloc_free(cmd); + * return NULL; + * } + * + * cmd->f = popen(cmd->command, "w"); + * if (!cmd->f) { + * talloc_free(cmd); + * return NULL; + * } + * talloc_set_destructor(cmd, close_cmd); + * return cmd; + * } + * + * int main(int argc, char *argv[]) + * { + * struct command *cmd; + * + * if (argc != 2) + * errx(1, "Usage: %s \n"); + * + * cmd = open_output_cmd(NULL, "%s hello", argv[1]); + * if (!cmd) + * err(1, "Running '%s hello'", argv[1]); + * fprintf(cmd->f, "This is a test\n"); + * talloc_free(cmd); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/typesafe_cb\n"); + return 0; + } + + return 1; +} diff --git a/ccan/talloc/talloc.3.xml b/ccan/talloc/talloc.3.xml new file mode 100644 index 00000000..83ca67a4 --- /dev/null +++ b/ccan/talloc/talloc.3.xml @@ -0,0 +1,739 @@ + + + + + talloc + 3 + + + talloc +hierarchical reference counted memory pool system with destructors + + +#include <talloc/talloc.h> + + DESCRIPTION + + If you are used to talloc from Samba3 then please read this + carefully, as talloc has changed a lot. + + + The new talloc is a hierarchical, reference counted memory pool + system with destructors. Quite a mouthful really, but not too bad + once you get used to it. + + + Perhaps the biggest change from Samba3 is that there is no + distinction between a "talloc context" and a "talloc pointer". Any + pointer returned from talloc() is itself a valid talloc context. + This means you can do this: + + + struct foo *X = talloc(mem_ctx, struct foo); + X->name = talloc_strdup(X, "foo"); + + + and the pointer X->name + would be a "child" of the talloc context X which is itself a child of + mem_ctx. So if you do + talloc_free(mem_ctx) then + it is all destroyed, whereas if you do talloc_free(X) then just X and X->name are destroyed, and if + you do talloc_free(X->name) then just + the name element of X is + destroyed. + + + If you think about this, then what this effectively gives you is an + n-ary tree, where you can free any part of the tree with + talloc_free(). + + + If you find this confusing, then I suggest you run the testsuite program to watch talloc + in action. You may also like to add your own tests to testsuite.c to clarify how some + particular situation is handled. + + + TALLOC API + + The following is a complete guide to the talloc API. Read it all at + least twice. + + (type *)talloc(const void *ctx, type); + + The talloc() macro is the core of the talloc library. It takes a + memory ctx and a type, and returns a pointer to a new + area of memory of the given type. + + + The returned pointer is itself a talloc context, so you can use + it as the ctx argument to more + calls to talloc() if you wish. + + + The returned pointer is a "child" of the supplied context. This + means that if you talloc_free() the ctx then the new child disappears as + well. Alternatively you can free just the child. + + + The ctx argument to talloc() + can be NULL, in which case a new top level context is created. + + + void *talloc_size(const void *ctx, size_t size); + + The function talloc_size() should be used when you don't have a + convenient type to pass to talloc(). Unlike talloc(), it is not + type safe (as it returns a void *), so you are on your own for + type checking. + + + (typeof(ptr)) talloc_ptrtype(const void *ctx, ptr); + + The talloc_ptrtype() macro should be used when you have a pointer and + want to allocate memory to point at with this pointer. When compiling + with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() + and talloc_get_name() will return the current location in the source file. + and not the type. + + + int talloc_free(void *ptr); + + The talloc_free() function frees a piece of talloc memory, and + all its children. You can call talloc_free() on any pointer + returned by talloc(). + + + The return value of talloc_free() indicates success or failure, + with 0 returned for success and -1 for failure. The only + possible failure condition is if ptr had a destructor attached to it and + the destructor returned -1. See talloc_set_destructor() + for details on destructors. + + + If this pointer has an additional parent when talloc_free() is + called then the memory is not actually released, but instead the + most recently established parent is destroyed. See talloc_reference() + for details on establishing additional parents. + + + For more control on which parent is removed, see talloc_unlink(). + + + talloc_free() operates recursively on its children. + + + void *talloc_reference(const void *ctx, const void *ptr); + + The talloc_reference() function makes ctx an additional parent of ptr. + + + The return value of talloc_reference() is always the original + pointer ptr, unless talloc ran + out of memory in creating the reference in which case it will + return NULL (each additional reference consumes around 48 bytes + of memory on intel x86 platforms). + + + If ptr is NULL, then the + function is a no-op, and simply returns NULL. + + + After creating a reference you can free it in one of the + following ways: + + + + + + you can talloc_free() any parent of the original pointer. + That will reduce the number of parents of this pointer by 1, + and will cause this pointer to be freed if it runs out of + parents. + + + + + you can talloc_free() the pointer itself. That will destroy + the most recently established parent to the pointer and leave + the pointer as a child of its current parent. + + + + + + For more control on which parent to remove, see talloc_unlink(). + + + int talloc_unlink(const void *ctx, const void *ptr); + + The talloc_unlink() function removes a specific parent from + ptr. The ctx passed must either be a context used + in talloc_reference() with this pointer, or must be a direct + parent of ptr. + + + Note that if the parent has already been removed using + talloc_free() then this function will fail and will return -1. + Likewise, if ptr is NULL, then + the function will make no modifications and return -1. + + + Usually you can just use talloc_free() instead of + talloc_unlink(), but sometimes it is useful to have the + additional control on which parent is removed. + + + void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); + + The function talloc_set_destructor() sets the destructor for the pointer ptr. A destructor is a function that is called + when the memory used by a pointer is about to be released. The + destructor receives ptr as an + argument, and should return 0 for success and -1 for failure. + + + The destructor can do anything + it wants to, including freeing other pieces of memory. A common + use for destructors is to clean up operating system resources + (such as open file descriptors) contained in the structure the + destructor is placed on. + + + You can only place one destructor on a pointer. If you need more + than one destructor then you can create a zero-length child of + the pointer and place an additional destructor on that. + + + To remove a destructor call talloc_set_destructor() with NULL for + the destructor. + + + If your destructor attempts to talloc_free() the pointer that it + is the destructor for then talloc_free() will return -1 and the + free will be ignored. This would be a pointless operation + anyway, as the destructor is only called when the memory is just + about to go away. + + + int talloc_increase_ref_count(const void *<emphasis role="italic">ptr</emphasis>); + + The talloc_increase_ref_count(ptr) function is exactly equivalent to: + + talloc_reference(NULL, ptr); + + You can use either syntax, depending on which you think is + clearer in your code. + + + It returns 0 on success and -1 on failure. + + + size_t talloc_reference_count(const void *<emphasis role="italic">ptr</emphasis>); + + Return the number of references to the pointer. + + + void talloc_set_name(const void *ptr, const char *fmt, ...); + + Each talloc pointer has a "name". The name is used principally + for debugging purposes, although it is also possible to set and + get the name on a pointer in as a way of "marking" pointers in + your code. + + + The main use for names on pointer is for "talloc reports". See + talloc_report_depth_cb(), + talloc_report_depth_file(), + talloc_report() + talloc_report() + and talloc_report_full() + for details. Also see talloc_enable_leak_report() + and talloc_enable_leak_report_full(). + + + The talloc_set_name() function allocates memory as a child of the + pointer. It is logically equivalent to: + + talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); + + Note that multiple calls to talloc_set_name() will allocate more + memory without releasing the name. All of the memory is released + when the ptr is freed using talloc_free(). + + + void talloc_set_name_const(const void *<emphasis role="italic">ptr</emphasis>, const char *<emphasis role="italic">name</emphasis>); + + The function talloc_set_name_const() is just like + talloc_set_name(), but it takes a string constant, and is much + faster. It is extensively used by the "auto naming" macros, such + as talloc_p(). + + + This function does not allocate any memory. It just copies the + supplied pointer into the internal representation of the talloc + ptr. This means you must not pass a name pointer to memory that will + disappear before ptr is freed + with talloc_free(). + + + void *talloc_named(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, ...); + + The talloc_named() function creates a named talloc pointer. It + is equivalent to: + + ptr = talloc_size(ctx, size); +talloc_set_name(ptr, fmt, ....); + + void *talloc_named_const(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>, const char *<emphasis role="italic">name</emphasis>); + + This is equivalent to: + + ptr = talloc_size(ctx, size); +talloc_set_name_const(ptr, name); + + const char *talloc_get_name(const void *<emphasis role="italic">ptr</emphasis>); + + This returns the current name for the given talloc pointer, + ptr. See talloc_set_name() + for details. + + + void *talloc_init(const char *<emphasis role="italic">fmt</emphasis>, ...); + + This function creates a zero length named talloc context as a top + level context. It is equivalent to: + + talloc_named(NULL, 0, fmt, ...); + + void *talloc_new(void *<emphasis role="italic">ctx</emphasis>); + + This is a utility macro that creates a new memory context hanging + off an exiting context, automatically naming it "talloc_new: + __location__" where __location__ is the source line it is called + from. It is particularly useful for creating a new temporary + working context. + + + (<emphasis role="italic">type</emphasis> *)talloc_realloc(const void *<emphasis role="italic">ctx</emphasis>, void *<emphasis role="italic">ptr</emphasis>, <emphasis role="italic">type</emphasis>, <emphasis role="italic">count</emphasis>); + + The talloc_realloc() macro changes the size of a talloc pointer. + It has the following equivalences: + + talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type); +talloc_realloc(ctx, ptr, type, 0) ==> talloc_free(ptr); + + The ctx argument is only used + if ptr is not NULL, otherwise + it is ignored. + + + talloc_realloc() returns the new pointer, or NULL on failure. + The call will fail either due to a lack of memory, or because the + pointer has more than one parent (see talloc_reference()). + + + void *talloc_realloc_size(const void *ctx, void *ptr, size_t size); + + the talloc_realloc_size() function is useful when the type is not + known so the type-safe talloc_realloc() cannot be used. + + + TYPE *talloc_steal(const void *<emphasis role="italic">new_ctx</emphasis>, const TYPE *<emphasis role="italic">ptr</emphasis>); + + The talloc_steal() function changes the parent context of a + talloc pointer. It is typically used when the context that the + pointer is currently a child of is going to be freed and you wish + to keep the memory for a longer time. + + + The talloc_steal() function returns the pointer that you pass it. + It does not have any failure modes. + + + NOTE: It is possible to produce loops in the parent/child + relationship if you are not careful with talloc_steal(). No + guarantees are provided as to your sanity or the safety of your + data if you do this. + + + TYPE *talloc_move(const void *<emphasis role="italic">new_ctx</emphasis>, TYPE **<emphasis role="italic">ptr</emphasis>); + + The talloc_move() function is a wrapper around + talloc_steal() which zeros the source pointer after the + move. This avoids a potential source of bugs where a + programmer leaves a pointer in two structures, and uses the + pointer from the old structure after it has been moved to a + new one. + + + size_t talloc_total_size(const void *<emphasis role="italic">ptr</emphasis>); + + The talloc_total_size() function returns the total size in bytes + used by this pointer and all child pointers. Mostly useful for + debugging. + + + Passing NULL is allowed, but it will only give a meaningful + result if talloc_enable_leak_report() or + talloc_enable_leak_report_full() has been called. + + + size_t talloc_total_blocks(const void *<emphasis role="italic">ptr</emphasis>); + + The talloc_total_blocks() function returns the total memory block + count used by this pointer and all child pointers. Mostly useful + for debugging. + + + Passing NULL is allowed, but it will only give a meaningful + result if talloc_enable_leak_report() or + talloc_enable_leak_report_full() has been called. + + + void talloc_report(const void *ptr, FILE *f); + + The talloc_report() function prints a summary report of all + memory used by ptr. One line + of report is printed for each immediate child of ptr, showing the + total memory and number of blocks used by that child. + + + You can pass NULL for the pointer, in which case a report is + printed for the top level memory context, but only if + talloc_enable_leak_report() or talloc_enable_leak_report_full() + has been called. + + + void talloc_report_full(const void *<emphasis role="italic">ptr</emphasis>, FILE *<emphasis role="italic">f</emphasis>); + + This provides a more detailed report than talloc_report(). It + will recursively print the entire tree of memory referenced by + the pointer. References in the tree are shown by giving the name + of the pointer that is referenced. + + + You can pass NULL for the pointer, in which case a report is + printed for the top level memory context, but only if + talloc_enable_leak_report() or talloc_enable_leak_report_full() + has been called. + + + + + void talloc_report_depth_cb + const void *ptr + int depth + int max_depth + void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *priv) + void *priv + + + This provides a more flexible reports than talloc_report(). It + will recursively call the callback for the entire tree of memory + referenced by the pointer. References in the tree are passed with + is_ref = 1 and the pointer that is referenced. + + + You can pass NULL for the pointer, in which case a report is + printed for the top level memory context, but only if + talloc_enable_leak_report() or talloc_enable_leak_report_full() + has been called. + + + The recursion is stopped when depth >= max_depth. + max_depth = -1 means only stop at leaf nodes. + + + + + void talloc_report_depth_file + const void *ptr + int depth + int max_depth + FILE *f + + + This provides a more flexible reports than talloc_report(). It + will let you specify the depth and max_depth. + + + void talloc_enable_leak_report(void); + + This enables calling of talloc_report(NULL, stderr) when the + program exits. In Samba4 this is enabled by using the + --leak-report command line option. + + + For it to be useful, this function must be called before any + other talloc function as it establishes a "null context" that + acts as the top of the tree. If you don't call this function + first then passing NULL to talloc_report() or + talloc_report_full() won't give you the full tree printout. + + + Here is a typical talloc report: + + talloc report on 'null_context' (total 267 bytes in 15 blocks) +libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks +libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks +iconv(UTF8,CP850) contains 42 bytes in 2 blocks +libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks +iconv(CP850,UTF8) contains 42 bytes in 2 blocks +iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks +iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks + + + void talloc_enable_leak_report_full(void); + + This enables calling of talloc_report_full(NULL, stderr) when the + program exits. In Samba4 this is enabled by using the + --leak-report-full command line option. + + + For it to be useful, this function must be called before any + other talloc function as it establishes a "null context" that + acts as the top of the tree. If you don't call this function + first then passing NULL to talloc_report() or + talloc_report_full() won't give you the full tree printout. + + + Here is a typical full report: + + full talloc report on 'root' (total 18 bytes in 8 blocks) +p1 contains 18 bytes in 7 blocks (ref 0) + r1 contains 13 bytes in 2 blocks (ref 0) + reference to: p2 + p2 contains 1 bytes in 1 blocks (ref 1) + x3 contains 1 bytes in 1 blocks (ref 0) + x2 contains 1 bytes in 1 blocks (ref 0) + x1 contains 1 bytes in 1 blocks (ref 0) + + + (<emphasis role="italic">type</emphasis> *)talloc_zero(const void *<emphasis role="italic">ctx</emphasis>, <emphasis role="italic">type</emphasis>); + + The talloc_zero() macro is equivalent to: + + ptr = talloc(ctx, type); +if (ptr) memset(ptr, 0, sizeof(type)); + + void *talloc_zero_size(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>) + + The talloc_zero_size() function is useful when you don't have a + known type. + + + void *talloc_memdup(const void *<emphasis role="italic">ctx</emphasis>, const void *<emphasis role="italic">p</emphasis>, size_t size); + + The talloc_memdup() function is equivalent to: + + ptr = talloc_size(ctx, size); +if (ptr) memcpy(ptr, p, size); + + char *talloc_strdup(const void *<emphasis role="italic">ctx</emphasis>, const char *<emphasis role="italic">p</emphasis>); + + The talloc_strdup() function is equivalent to: + + ptr = talloc_size(ctx, strlen(p)+1); +if (ptr) memcpy(ptr, p, strlen(p)+1); + + This function sets the name of the new pointer to the passed + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_strndup(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">p</emphasis>, size_t <emphasis role="italic">n</emphasis>); + + The talloc_strndup() function is the talloc equivalent of the C + library function strndup(3). + + + This function sets the name of the new pointer to the passed + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_append_string(const void *<emphasis role="italic">t</emphasis>, char *<emphasis role="italic">orig</emphasis>, const char *<emphasis role="italic">append</emphasis>); + + The talloc_append_string() function appends the given formatted + string to the given string. + + + This function sets the name of the new pointer to the new + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_vasprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, va_list <emphasis role="italic">ap</emphasis>); + + The talloc_vasprintf() function is the talloc equivalent of the C + library function vasprintf(3). + + + This function sets the name of the new pointer to the new + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_asprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, ...); + + The talloc_asprintf() function is the talloc equivalent of the C + library function asprintf(3). + + + This function sets the name of the new pointer to the passed + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_asprintf_append(char *s, const char *fmt, ...); + + The talloc_asprintf_append() function appends the given formatted + string to the given string. + + + This function sets the name of the new pointer to the new + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + (type *)talloc_array(const void *ctx, type, uint_t count); + + The talloc_array() macro is equivalent to: + + (type *)talloc_size(ctx, sizeof(type) * count); + + except that it provides integer overflow protection for the + multiply, returning NULL if the multiply overflows. + + + void *talloc_array_size(const void *ctx, size_t size, uint_t count); + + The talloc_array_size() function is useful when the type is not + known. It operates in the same way as talloc_array(), but takes a + size instead of a type. + + + (typeof(ptr)) talloc_array_ptrtype(const void *ctx, ptr, uint_t count); + + The talloc_ptrtype() macro should be used when you have a pointer to an array + and want to allocate memory of an array to point at with this pointer. When compiling + with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size() + and talloc_get_name() will return the current location in the source file. + and not the type. + + + void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size) + + This is a non-macro version of talloc_realloc(), which is useful + as libraries sometimes want a realloc function pointer. A + realloc(3) implementation encapsulates the functionality of + malloc(3), free(3) and realloc(3) in one call, which is why it is + useful to be able to pass around a single function pointer. + + + void *talloc_autofree_context(void); + + This is a handy utility function that returns a talloc context + which will be automatically freed on program exit. This can be + used to reduce the noise in memory leak reports. + + + void *talloc_check_name(const void *ptr, const char *name); + + This function checks if a pointer has the specified name. If it does then the pointer is + returned. It it doesn't then NULL is returned. + + + (type *)talloc_get_type(const void *ptr, type); + + This macro allows you to do type checking on talloc pointers. It + is particularly useful for void* private pointers. It is + equivalent to this: + + (type *)talloc_check_name(ptr, #type) + + talloc_set_type(const void *ptr, type); + + This macro allows you to force the name of a pointer to be a + particular type. This can be + used in conjunction with talloc_get_type() to do type checking on + void* pointers. + + + It is equivalent to this: + + talloc_set_name_const(ptr, #type) + + + PERFORMANCE + + All the additional features of talloc(3) over malloc(3) do come at a + price. We have a simple performance test in Samba4 that measures + talloc() versus malloc() performance, and it seems that talloc() is + about 10% slower than malloc() on my x86 Debian Linux box. For + Samba, the great reduction in code complexity that we get by using + talloc makes this worthwhile, especially as the total overhead of + talloc/malloc in Samba is already quite small. + + + SEE ALSO + + malloc(3), strndup(3), vasprintf(3), asprintf(3), + + + + COPYRIGHT/LICENSE + + Copyright (C) Andrew Tridgell 2004 + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at + your option) any later version. + + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + + diff --git a/ccan/talloc/talloc.c b/ccan/talloc/talloc.c new file mode 100644 index 00000000..d624b917 --- /dev/null +++ b/ccan/talloc/talloc.c @@ -0,0 +1,1403 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#include "talloc.h" +#include +#include +#include + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(INTPTR_MIN) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if HAVE_BUILTIN_EXPECT +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static void *autofree_context; + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + size_t size; + unsigned flags; +}; + +/* 16 byte alignment seems to keep everyone happy */ +#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) +#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) + +/* panic if we get a bad magic value */ +static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if (tc->flags & TALLOC_FLAG_FREE) { + TALLOC_ABORT("Bad talloc magic value - double free"); + } else { + TALLOC_ABORT("Bad talloc magic value - unknown value"); + } + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + + tc->size = size; + tc->flags = TALLOC_MAGIC; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context)) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *_talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + + +/* + internal talloc_free call +*/ +static inline int _talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs)) { + int is_child; + /* check this is a reference from a child or grantchild + * back to it's parent or grantparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free(tc->refs); + if (is_child) { + return _talloc_free(ptr); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + tc->flags |= TALLOC_FLAG_FREE; + free(tc); + return 0; +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + + + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free(h); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (likely(tc->name)) { + _talloc_set_name_const(tc->name, ".name"); + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + const char *name; + va_list ap; + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + /* + * samba3 expects talloc_report_depth_cb(NULL, ...) + * reports all talloc'ed memory, so we need to enable + * null_tracking + */ + talloc_enable_null_tracking(); + + ptr = __talloc(NULL, 0); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + return __talloc(context, size); +} + +/* + externally callable talloc_set_name_const() +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + _talloc_set_name_const(ptr, name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + int saved_errno = errno, ret; + ret = _talloc_free(ptr); + if (ret == 0) + errno = saved_errno; + return ret; +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + _talloc_free(ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } +#else + new_ptr = realloc(tc, size + TC_HDR_SIZE); +#endif + if (unlikely(!new_ptr)) { + tc->flags &= ~TALLOC_FLAG_FREE; + return NULL; + } + + tc = (struct talloc_chunk *)new_ptr; + tc->flags &= ~TALLOC_FLAG_FREE; + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = _talloc_steal(new_ctx, *pptr); + (*pptr) = NULL; + return ret; +} + +/* + return the total size of a talloc pool (subtree) +*/ +size_t talloc_total_size(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +size_t talloc_total_blocks(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the number of external references to a pointer +*/ +size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = talloc_get_name(ptr); + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +void talloc_disable_null_tracking(void) +{ + _talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + char *ret; + if (!p) { + return NULL; + } + ret = (char *)talloc_memdup(t, p, strlen(p) + 1); + if (likely(ret)) { + _talloc_set_name_const(ret, ret); + } + return ret; +} + +/* + append to a talloced string +*/ +char *talloc_append_string(char *orig, const char *append) +{ + char *ret; + size_t olen = strlen(orig); + size_t alenz; + + if (!append) + return orig; + + alenz = strlen(append) + 1; + + ret = talloc_realloc(NULL, orig, char, olen + alenz); + if (!ret) + return NULL; + + /* append the string with the trailing \0 */ + memcpy(&ret[olen], append, alenz); + + _talloc_set_name_const(ret, ret); + + return ret; +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + size_t len; + char *ret; + + for (len=0; lensize - 1; + + va_copy(ap2, ap); + len = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (len <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, s_len + len+1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s+s_len, len+1, fmt, ap2); + va_end(ap2); + _talloc_set_name_const(s, s); + + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +static void talloc_autofree(void) +{ + _talloc_free(autofree_context); +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + atexit(talloc_autofree); + } + return autofree_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} + +/* + find a parent of this context that has the given name, if any +*/ +void *talloc_find_parent_byname(const void *context, const char *name) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (tc->name && strcmp(tc->name, name) == 0) { + return TC_PTR_FROM_CHUNK(tc); + } + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return NULL; +} + +/* + show the parentage of a context +*/ +void talloc_show_parents(const void *context, FILE *file) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + fprintf(file, "talloc no parents for NULL\n"); + return; + } + + tc = talloc_chunk_from_ptr(context); + fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); + while (tc) { + fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +int talloc_is_parent(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return 0; +} diff --git a/ccan/talloc/talloc.h b/ccan/talloc/talloc.h new file mode 100644 index 00000000..7da8d975 --- /dev/null +++ b/ccan/talloc/talloc.h @@ -0,0 +1,951 @@ +#ifndef CCAN_TALLOC_H +#define CCAN_TALLOC_H +/* + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include "config.h" +#include "ccan/typesafe_cb/typesafe_cb.h" + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#if HAVE_ATTRIBUTE_PRINTF +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if HAVE_TYPEOF +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#else +#define _TALLOC_TYPEOF(ptr) void * +#endif + +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/** + * talloc - allocate dynamic memory for a type + * @ctx: context to be parent of this allocation, or NULL. + * @type: the type to be allocated. + * + * The talloc() macro is the core of the talloc library. It takes a memory + * context and a type, and returns a pointer to a new area of memory of the + * given type. + * + * The returned pointer is itself a talloc context, so you can use it as the + * context argument to more calls to talloc if you wish. + * + * The returned pointer is a "child" of @ctx. This means that if you + * talloc_free() @ctx then the new child disappears as well. Alternatively you + * can free just the child. + * + * @ctx can be NULL, in which case a new top level context is created. + * + * Example: + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * + * See Also: + * talloc_zero, talloc_array, talloc_steal, talloc_free. + */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) + +/** + * talloc_free - free talloc'ed memory and its children + * @ptr: the talloced pointer to free + * + * The talloc_free() function frees a piece of talloc memory, and all its + * children. You can call talloc_free() on any pointer returned by talloc(). + * + * The return value of talloc_free() indicates success or failure, with 0 + * returned for success and -1 for failure. The only possible failure condition + * is if the pointer had a destructor attached to it and the destructor + * returned -1. See talloc_set_destructor() for details on destructors. + * errno will be preserved unless the talloc_free fails. + * + * If this pointer has an additional parent when talloc_free() is called then + * the memory is not actually released, but instead the most recently + * established parent is destroyed. See talloc_reference() for details on + * establishing additional parents. + * + * For more control on which parent is removed, see talloc_unlink(). + * + * talloc_free() operates recursively on its children. + * + * Example: + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * // Frees a and b + * talloc_free(a); + * + * See Also: + * talloc_set_destructor, talloc_unlink + */ +int talloc_free(void *ptr); + +/** + * talloc_set_destructor: set a destructor for when this pointer is freed + * @ptr: the talloc pointer to set the destructor on + * @destructor: the function to be called + * + * The function talloc_set_destructor() sets the "destructor" for the pointer + * @ptr. A destructor is a function that is called when the memory used by a + * pointer is about to be released. The destructor receives the pointer as an + * argument, and should return 0 for success and -1 for failure. + * + * The destructor can do anything it wants to, including freeing other pieces + * of memory. A common use for destructors is to clean up operating system + * resources (such as open file descriptors) contained in the structure the + * destructor is placed on. + * + * You can only place one destructor on a pointer. If you need more than one + * destructor then you can create a zero-length child of the pointer and place + * an additional destructor on that. + * + * To remove a destructor call talloc_set_destructor() with NULL for the + * destructor. + * + * If your destructor attempts to talloc_free() the pointer that it is the + * destructor for then talloc_free() will return -1 and the free will be + * ignored. This would be a pointless operation anyway, as the destructor is + * only called when the memory is just about to go away. + * + * Example: + * static int destroy_fd(int *fd) + * { + * close(*fd); + * return 0; + * } + * + * int *open_file(const char *filename) + * { + * int *fd = talloc(NULL, int); + * *fd = open(filename, O_RDONLY); + * if (*fd < 0) { + * talloc_free(fd); + * return NULL; + * } + * // Whenever they free this, we close the file. + * talloc_set_destructor(fd, destroy_fd); + * return fd; + * } + * + * See Also: + * talloc, talloc_free + */ +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), typesafe_cb(int, (function), (ptr))) + +/** + * talloc_zero - allocate zeroed dynamic memory for a type + * @ctx: context to be parent of this allocation, or NULL. + * @type: the type to be allocated. + * + * The talloc_zero() macro is equivalent to: + * + * ptr = talloc(ctx, type); + * if (ptr) memset(ptr, 0, sizeof(type)); + * + * Example: + * unsigned int *a, *b; + * a = talloc_zero(NULL, unsigned int); + * b = talloc_zero(a, unsigned int); + * + * See Also: + * talloc, talloc_zero_size, talloc_zero_array + */ +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) + +/** + * talloc_array - allocate dynamic memory for an array of a given type + * @ctx: context to be parent of this allocation, or NULL. + * @type: the type to be allocated. + * @count: the number of elements to be allocated. + * + * The talloc_array() macro is a safe way of allocating an array. It is + * equivalent to: + * + * (type *)talloc_size(ctx, sizeof(type) * count); + * + * except that it provides integer overflow protection for the multiply, + * returning NULL if the multiply overflows. + * + * Example: + * unsigned int *a, *b; + * a = talloc_zero(NULL, unsigned int); + * b = talloc_array(a, unsigned int, 100); + * + * See Also: + * talloc, talloc_zero_array + */ +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) + +/** + * talloc_size - allocate a particular size of memory + * @ctx: context to be parent of this allocation, or NULL. + * @size: the number of bytes to allocate + * + * The function talloc_size() should be used when you don't have a convenient + * type to pass to talloc(). Unlike talloc(), it is not type safe (as it + * returns a void *), so you are on your own for type checking. + * + * Best to use talloc() or talloc_array() instead. + * + * Example: + * void *mem = talloc_size(NULL, 100); + * + * See Also: + * talloc, talloc_array, talloc_zero_size + */ +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) + +#ifdef HAVE_TYPEOF +/** + * talloc_steal - change/set the parent context of a talloc pointer + * @ctx: the new parent + * @ptr: the talloc pointer to reparent + * + * The talloc_steal() function changes the parent context of a talloc + * pointer. It is typically used when the context that the pointer is currently + * a child of is going to be freed and you wish to keep the memory for a longer + * time. + * + * The talloc_steal() function returns the pointer that you pass it. It does + * not have any failure modes. + * + * NOTE: It is possible to produce loops in the parent/child relationship if + * you are not careful with talloc_steal(). No guarantees are provided as to + * your sanity or the safety of your data if you do this. + * + * talloc_steal (new_ctx, NULL) will return NULL with no sideeffects. + * + * Example: + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(NULL, unsigned int); + * // Reparent b to a as if we'd done 'b = talloc(a, unsigned int)'. + * talloc_steal(a, b); + * + * See Also: + * talloc_reference + */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) _talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); _talloc_steal_ret; }) /* this extremely strange macro is to avoid some braindamaged warning stupidity in gcc 4.1.x */ +#else +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif /* HAVE_TYPEOF */ + +/** + * talloc_report_full - report all the memory used by a pointer and children. + * @ptr: the context to report on + * @f: the file to report to + * + * Recursively print the entire tree of memory referenced by the + * pointer. References in the tree are shown by giving the name of the pointer + * that is referenced. + * + * You can pass NULL for the pointer, in which case a report is printed for the + * top level memory context, but only if talloc_enable_null_tracking() has been + * called. + * + * Example: + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * fprintf(stderr, "Dumping memory tree for a:\n"); + * talloc_report_full(a, stderr); + * + * See Also: + * talloc_report + */ +void talloc_report_full(const void *ptr, FILE *f); + +/** + * talloc_reference - add an additional parent to a context + * @ctx: the additional parent + * @ptr: the talloc pointer + * + * The talloc_reference() function makes @ctx an additional parent of @ptr. + * + * The return value of talloc_reference() is always the original pointer @ptr, + * unless talloc ran out of memory in creating the reference in which case it + * will return NULL (each additional reference consumes around 48 bytes of + * memory on intel x86 platforms). + * + * If @ptr is NULL, then the function is a no-op, and simply returns NULL. + * + * After creating a reference you can free it in one of the following ways: + * + * - you can talloc_free() any parent of the original pointer. That will + * reduce the number of parents of this pointer by 1, and will cause this + * pointer to be freed if it runs out of parents. + * + * - you can talloc_free() the pointer itself. That will destroy the most + * recently established parent to the pointer and leave the pointer as a + * child of its current parent. + * + * For more control on which parent to remove, see talloc_unlink(). + * Example: + * unsigned int *a, *b, *c; + * a = talloc(NULL, unsigned int); + * b = talloc(NULL, unsigned int); + * c = talloc(a, unsigned int); + * // b also serves as a parent of c. + * talloc_reference(b, c); + */ +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) + +/** + * talloc_unlink: remove a specific parent from a talloc pointer. + * @context: the parent to remove + * @ptr: the talloc pointer + * + * The talloc_unlink() function removes a specific parent from @ptr. The + * context passed must either be a context used in talloc_reference() with this + * pointer, or must be a direct parent of @ptr. + * + * Note that if the parent has already been removed using talloc_free() then + * this function will fail and will return -1. Likewise, if @ptr is NULL, + * then the function will make no modifications and return -1. + * + * Usually you can just use talloc_free() instead of talloc_unlink(), but + * sometimes it is useful to have the additional control on which parent is + * removed. + * Example: + * unsigned int *a, *b, *c; + * a = talloc(NULL, unsigned int); + * b = talloc(NULL, unsigned int); + * c = talloc(a, unsigned int); + * // b also serves as a parent of c. + * talloc_reference(b, c); + * talloc_unlink(b, c); + */ +int talloc_unlink(const void *context, void *ptr); + +/** + * talloc_report - print a summary of memory used by a pointer + * + * The talloc_report() function prints a summary report of all memory + * used by @ptr. One line of report is printed for each immediate child of + * @ptr, showing the total memory and number of blocks used by that child. + * + * You can pass NULL for the pointer, in which case a report is printed for the + * top level memory context, but only if talloc_enable_null_tracking() has been + * called. + * + * Example: + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * fprintf(stderr, "Summary of memory tree for a:\n"); + * talloc_report(a, stderr); + * + * See Also: + * talloc_report_full + */ +void talloc_report(const void *ptr, FILE *f); + +/** + * talloc_ptrtype - allocate a size of memory suitable for this pointer + * @ctx: context to be parent of this allocation, or NULL. + * @ptr: the pointer whose type we are to allocate + * + * The talloc_ptrtype() macro should be used when you have a pointer and + * want to allocate memory to point at with this pointer. When compiling + * with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() + * and talloc_get_name() will return the current location in the source file. + * and not the type. + * + * Example: + * unsigned int *a = talloc_ptrtype(NULL, a); + */ +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +/** + * talloc_free_children - free talloc'ed memory's children only + * @ptr: the talloced pointer whose children we want to free + * + * talloc_free_children() walks along the list of all children of a talloc + * context @ptr and talloc_free()s only the children, not the context itself. + * Example: + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * // Frees b + * talloc_free_children(a); + */ +void talloc_free_children(void *ptr); + +/** + * talloc_new - create a new context + * @ctx: the context to use as a parent. + * + * This is a utility macro that creates a new memory context hanging off an + * exiting context, automatically naming it "talloc_new: __location__" where + * __location__ is the source line it is called from. It is particularly useful + * for creating a new temporary working context. + */ +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +/** + * talloc_zero_size - allocate a particular size of zeroed memory + * + * The talloc_zero_size() function is useful when you don't have a known type. + */ +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +/** + * talloc_zero_array - allocate an array of zeroed types + * @ctx: context to be parent of this allocation, or NULL. + * @type: the type to be allocated. + * @count: the number of elements to be allocated. + * + * Just like talloc_array, but zeroes the memory. + */ +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) + +/** + * talloc_zero_array - allocate an array of zeroed types + * @ctx: context to be parent of this allocation, or NULL. + * @type: the type to be allocated. + * @count: the number of elements to be allocated. + * + * Just like talloc_array, but zeroes the memory. + */ +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) + +/** + * talloc_array_ptrtype - allocate an array of memory suitable for this pointer + * @ctx: context to be parent of this allocation, or NULL. + * @ptr: the pointer whose type we are to allocate + * @count: the number of elements for the array + * + * Like talloc_ptrtype(), except it allocates an array. + */ +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) + +/** + * talloc_realloc - resize a talloc array + * @ctx: the parent to assign (if p is NULL) + * @p: the memory to reallocate + * @type: the type of the object to allocate + * @count: the number of objects to reallocate + * + * The talloc_realloc() macro changes the size of a talloc pointer. The "count" + * argument is the number of elements of type "type" that you want the + * resulting pointer to hold. + * + * talloc_realloc() has the following equivalences: + * + * talloc_realloc(context, NULL, type, 1) ==> talloc(context, type); + * talloc_realloc(context, NULL, type, N) ==> talloc_array(context, type, N); + * talloc_realloc(context, ptr, type, 0) ==> talloc_free(ptr); + * + * The "context" argument is only used if "ptr" is NULL, otherwise it is + * ignored. + * + * talloc_realloc() returns the new pointer, or NULL on failure. The call will + * fail either due to a lack of memory, or because the pointer has more than + * one parent (see talloc_reference()). + */ +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) + +/** + * talloc_realloc_size - resize talloc memory + * @ctx: the parent to assign (if p is NULL) + * @ptr: the memory to reallocate + * @size: the new size of memory. + * + * The talloc_realloc_size() function is useful when the type is not known so + * the typesafe talloc_realloc() cannot be used. + */ +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +/** + * talloc_strdup - duplicate a string + * @ctx: the talloc context for the new string + * @p: the string to copy + * + * The talloc_strdup() function is equivalent to: + * + * ptr = talloc_size(ctx, strlen(p)+1); + * if (ptr) memcpy(ptr, p, strlen(p)+1); + * + * This functions sets the name of the new pointer to the passed string. This + * is equivalent to: + * + * talloc_set_name_const(ptr, ptr) + */ +char *talloc_strdup(const void *t, const char *p); + +/** + * talloc_strndup - duplicate a limited length of a string + * @ctx: the talloc context for the new string + * @p: the string to copy + * @n: the maximum length of the returned string. + * + * The talloc_strndup() function is the talloc equivalent of the C library + * function strndup(): the result will be truncated to @n characters before + * the nul terminator. + * + * This functions sets the name of the new pointer to the passed string. This + * is equivalent to: + * + * talloc_set_name_const(ptr, ptr) + */ +char *talloc_strndup(const void *t, const char *p, size_t n); + +/** + * talloc_memdup - duplicate some talloc memory + * + * The talloc_memdup() function is equivalent to: + * + * ptr = talloc_size(ctx, size); + * if (ptr) memcpy(ptr, p, size); + */ +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +/** + * talloc_asprintf - sprintf into a talloc buffer. + * @t: The context to allocate the buffer from + * @fmt: printf-style format for the buffer. + * + * The talloc_asprintf() function is the talloc equivalent of the C library + * function asprintf(). + * + * This functions sets the name of the new pointer to the new string. This is + * equivalent to: + * + * talloc_set_name_const(ptr, ptr) + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +/** + * talloc_append_string - concatenate onto a tallocated string + * @orig: the tallocated string to append to + * @append: the string to add, or NULL to add nothing. + * + * The talloc_append_string() function appends the given formatted string to + * the given string. + * + * This function sets the name of the new pointer to the new string. This is + * equivalent to: + * + * talloc_set_name_const(ptr, ptr) + */ +char *talloc_append_string(char *orig, const char *append); + +/** + * talloc_asprintf_append - sprintf onto the end of a talloc buffer. + * @s: The tallocated string buffer + * @fmt: printf-style format to append to the buffer. + * + * The talloc_asprintf_append() function appends the given formatted string to + * the given string. + * + * This functions sets the name of the new pointer to the new string. This is + * equivalent to: + * talloc_set_name_const(ptr, ptr) + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +/** + * talloc_vasprintf - vsprintf into a talloc buffer. + * @t: The context to allocate the buffer from + * @fmt: printf-style format for the buffer + * @ap: va_list arguments + * + * The talloc_vasprintf() function is the talloc equivalent of the C library + * function vasprintf() + * + * This functions sets the name of the new pointer to the new string. This is + * equivalent to: + * + * talloc_set_name_const(ptr, ptr) + */ +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +/** + * talloc_vasprintf_append - sprintf onto the end of a talloc buffer. + * @t: The context to allocate the buffer from + * @fmt: printf-style format for the buffer + * @ap: va_list arguments + * + * The talloc_vasprintf_append() function is equivalent to + * talloc_asprintf_append(), except it takes a va_list. + */ +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +/** + * talloc_set_type - force the name of a pointer to a particular type + * @ptr: the talloc pointer + * @type: the type whose name to set the ptr name to. + * + * This macro allows you to force the name of a pointer to be a particular + * type. This can be used in conjunction with talloc_get_type() to do type + * checking on void* pointers. + * + * It is equivalent to this: + * talloc_set_name_const(ptr, #type) + */ +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) + +/** + * talloc_get_type - convert a talloced pointer with typechecking + * @ptr: the talloc pointer + * @type: the type which we expect the talloced pointer to be. + * + * This macro allows you to do type checking on talloc pointers. It is + * particularly useful for void* private pointers. It is equivalent to this: + * + * (type *)talloc_check_name(ptr, #type) + */ +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) + +/** + * talloc_find_parent_byname - find a talloc parent by type + * @ptr: the talloc pointer + * @type: the type we're looking for + * + * Find a parent memory context of the current context that has the given + * name. This can be very useful in complex programs where it may be difficult + * to pass all information down to the level you need, but you know the + * structure you want is a parent of another context. + */ +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +/** + * talloc_increase_ref_count - hold a reference to a talloc pointer + * @ptr: the talloc pointer + * + * The talloc_increase_ref_count(ptr) function is exactly equivalent to: + * + * talloc_reference(NULL, ptr); + * + * You can use either syntax, depending on which you think is clearer in your + * code. + * + * It returns 0 on success and -1 on failure. + */ +int talloc_increase_ref_count(const void *ptr); + +/** + * talloc_set_name - set the name for a talloc pointer + * @ptr: the talloc pointer + * @fmt: the printf-style format string for the name + * + * Each talloc pointer has a "name". The name is used principally for debugging + * purposes, although it is also possible to set and get the name on a pointer + * in as a way of "marking" pointers in your code. + * + * The main use for names on pointer is for "talloc reports". See + * talloc_report() and talloc_report_full() for details. Also see + * talloc_enable_leak_report() and talloc_enable_leak_report_full(). + * + * The talloc_set_name() function allocates memory as a child of the + * pointer. It is logically equivalent to: + * talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); + * + * Note that multiple calls to talloc_set_name() will allocate more memory + * without releasing the name. All of the memory is released when the ptr is + * freed using talloc_free(). + */ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +/** + * talloc_set_name_const - set a talloc pointer name to a string constant + * @ptr: the talloc pointer to name + * @name: the strucng constant. + * + * The function talloc_set_name_const() is just like talloc_set_name(), but it + * takes a string constant, and is much faster. It is extensively used by the + * "auto naming" macros, such as talloc(). + * + * This function does not allocate any memory. It just copies the supplied + * pointer into the internal representation of the talloc ptr. This means you + * must not pass a name pointer to memory that will disappear before the ptr is + * freed with talloc_free(). + */ +void talloc_set_name_const(const void *ptr, const char *name); + +/** + * talloc_named - create a specifically-named talloc pointer + * @context: the parent context for the allocation + * @size: the size to allocate + * @fmt: the printf-style format for the name + * + * The talloc_named() function creates a named talloc pointer. It is equivalent + * to: + * + * ptr = talloc_size(context, size); + * talloc_set_name(ptr, fmt, ....); + */ +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); + +/** + * talloc_named_const - create a specifically-named talloc pointer + * @context: the parent context for the allocation + * @size: the size to allocate + * @name: the string constant to use as the name + * + * This is equivalent to: + * + * ptr = talloc_size(context, size); + * talloc_set_name_const(ptr, name); + */ +void *talloc_named_const(const void *context, size_t size, const char *name); + +/** + * talloc_get_name - get the name of a talloc pointer + * @ptr: the talloc pointer + * + * This returns the current name for the given talloc pointer. See + * talloc_set_name() for details. + */ +const char *talloc_get_name(const void *ptr); + +/** + * talloc_check_name - check if a pointer has the specified name + * @ptr: the talloc pointer + * @name: the name to compare with the pointer's name + * + * This function checks if a pointer has the specified name. If it does then + * the pointer is returned. It it doesn't then NULL is returned. + */ +void *talloc_check_name(const void *ptr, const char *name); + +/** + * talloc_init - create a top-level context of particular name + * @fmt: the printf-style format of the name + * + * This function creates a zero length named talloc context as a top level + * context. It is equivalent to: + * + * talloc_named(NULL, 0, fmt, ...); + */ +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); + +/** + * talloc_total_size - get the bytes used by the pointer and its children + * @ptr: the talloc pointer + * + * The talloc_total_size() function returns the total size in bytes used by + * this pointer and all child pointers. Mostly useful for debugging. + * + * Passing NULL is allowed, but it will only give a meaningful result if + * talloc_enable_leak_report() or talloc_enable_leak_report_full() has been + * called. + */ +size_t talloc_total_size(const void *ptr); + +/** + * talloc_total_blocks - get the number of allocations for the pointer + * @ptr: the talloc pointer + * + * The talloc_total_blocks() function returns the total allocations used by + * this pointer and all child pointers. Mostly useful for debugging. For + * example, a pointer with no children will return "1". + * + * Passing NULL is allowed, but it will only give a meaningful result if + * talloc_enable_leak_report() or talloc_enable_leak_report_full() has been + * called. + */ +size_t talloc_total_blocks(const void *ptr); + +/** + * talloc_report_depth_cb - walk the entire talloc tree under a talloc pointer + * @ptr: the talloc pointer to recurse under + * @depth: the current depth of traversal + * @max_depth: maximum depth to traverse, or -1 for no maximum + * @callback: the function to call on each pointer + * @private_data: pointer to hand to @callback. + * + * This provides a more flexible reports than talloc_report(). It will + * recursively call the callback for the entire tree of memory referenced by + * the pointer. References in the tree are passed with is_ref = 1 and the + * pointer that is referenced. + * + * You can pass NULL for the pointer, in which case a report is printed for the + * top level memory context, but only if talloc_enable_leak_report() or + * talloc_enable_leak_report_full() has been called. + * + * The recursion is stopped when depth >= max_depth. max_depth = -1 means only + * stop at leaf nodes. + */ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); + +/** + * talloc_report_depth_file - report talloc usage to a maximum depth + * @ptr: the talloc pointer to recurse under + * @depth: the current depth of traversal + * @max_depth: maximum depth to traverse, or -1 for no maximum + * @f: the file to report to + * + * This provides a more flexible reports than talloc_report(). It will let you + * specify the depth and max_depth. + */ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); + +/** + * talloc_enable_null_tracking - enable tracking of top-level tallocs + * + * This enables tracking of the NULL memory context without enabling leak + * reporting on exit. Useful for when you want to do your own leak reporting + * call via talloc_report_null_full(); + */ +void talloc_enable_null_tracking(void); + +/** + * talloc_disable_null_tracking - enable tracking of top-level tallocs + * + * This disables tracking of the NULL memory context. + */ +void talloc_disable_null_tracking(void); + +/** + * talloc_enable_leak_report - call talloc_report on program exit + * + * This enables calling of talloc_report(NULL, stderr) when the program + * exits. In Samba4 this is enabled by using the --leak-report command line + * option. + * + * For it to be useful, this function must be called before any other talloc + * function as it establishes a "null context" that acts as the top of the + * tree. If you don't call this function first then passing NULL to + * talloc_report() or talloc_report_full() won't give you the full tree + * printout. + * + * Here is a typical talloc report: + * + * talloc report on 'null_context' (total 267 bytes in 15 blocks) + * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + * iconv(UTF8,CP850) contains 42 bytes in 2 blocks + * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + * iconv(CP850,UTF8) contains 42 bytes in 2 blocks + * iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks + * iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks + */ +void talloc_enable_leak_report(void); + +/** + * talloc_enable_leak_report - call talloc_report_full on program exit + * + * This enables calling of talloc_report_full(NULL, stderr) when the program + * exits. In Samba4 this is enabled by using the --leak-report-full command + * line option. + * + * For it to be useful, this function must be called before any other talloc + * function as it establishes a "null context" that acts as the top of the + * tree. If you don't call this function first then passing NULL to + * talloc_report() or talloc_report_full() won't give you the full tree + * printout. + * + * Here is a typical full report: + * + * full talloc report on 'root' (total 18 bytes in 8 blocks) + * p1 contains 18 bytes in 7 blocks (ref 0) + * r1 contains 13 bytes in 2 blocks (ref 0) + * reference to: p2 + * p2 contains 1 bytes in 1 blocks (ref 1) + * x3 contains 1 bytes in 1 blocks (ref 0) + * x2 contains 1 bytes in 1 blocks (ref 0) + * x1 contains 1 bytes in 1 blocks (ref 0) + */ +void talloc_enable_leak_report_full(void); + +/** + * talloc_autofree_context - a context which will be freed at exit + * + * This is a handy utility function that returns a talloc context which will be + * automatically freed on program exit. This can be used to reduce the noise in + * memory leak reports. + */ +void *talloc_autofree_context(void); + +/** + * talloc_get_size - get the size of an allocation + * @ctx: the talloc pointer whose allocation to measure. + * + * This function lets you know the amount of memory alloced so far by this + * context. It does NOT account for subcontext memory. This can be used to + * calculate the size of an array. + */ +size_t talloc_get_size(const void *ctx); + +/** + * talloc_find_parent_byname - find a parent of this context with this name + * @ctx: the context whose ancestors to search + * @name: the name to look for + * + * Find a parent memory context of @ctx that has the given name. This can be + * very useful in complex programs where it may be difficult to pass all + * information down to the level you need, but you know the structure you want + * is a parent of another context. + */ +void *talloc_find_parent_byname(const void *ctx, const char *name); + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); + +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +#endif /* CCAN_TALLOC_H */ diff --git a/ccan/talloc/test/run.c b/ccan/talloc/test/run.c new file mode 100644 index 00000000..7369186c --- /dev/null +++ b/ccan/talloc/test/run.c @@ -0,0 +1,871 @@ +/* + Unix SMB/CIFS implementation. + + local testing of talloc routines. + + Copyright (C) Andrew Tridgell 2004 + Converted to ccan tests by Rusty Russell 2008 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "talloc/talloc.c" +#include +#include "tap/tap.h" + +#define torture_assert(test, expr, str) \ + ok(expr, "failure: %s [\n%s: Expression %s failed: %s\n]\n", \ + test, __location__, #expr, str) + +#define torture_assert_str_equal(test, arg1, arg2, desc) \ + ok(strcmp(arg1, arg2) == 0, \ + "failure: %s [\n%s: Expected %s, got %s: %s\n]\n", \ + test, __location__, arg1, arg2, desc) + +#define CHECK_SIZE(test, ptr, tsize) \ + ok(talloc_total_size(ptr) == (tsize), \ + "failed: %s [\nwrong '%s' tree size: got %u expected %u\n]\n", \ + test, #ptr, \ + (unsigned)talloc_total_size(ptr), \ + (unsigned)tsize) + +#define CHECK_BLOCKS(test, ptr, tblocks) \ + ok(talloc_total_blocks(ptr) == (tblocks), \ + "failed: %s [\nwrong '%s' tree blocks: got %u expected %u\n]\n", \ + test, #ptr, \ + (unsigned)talloc_total_blocks(ptr), \ + (unsigned)tblocks) + +#define CHECK_PARENT(test, ptr, parent) \ + ok(talloc_parent(ptr) == (parent), \ + "failed: %s [\n'%s' has wrong parent: got %p expected %p\n]\n", \ + test, #ptr, \ + talloc_parent(ptr), \ + (parent)) + +/* + test references +*/ +static bool test_ref1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 2, "x2"); + talloc_named_const(p1, 3, "x3"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("ref1", p1, 5); + CHECK_BLOCKS("ref1", p2, 1); + CHECK_BLOCKS("ref1", r1, 2); + + talloc_free(p2); + + CHECK_BLOCKS("ref1", p1, 5); + CHECK_BLOCKS("ref1", p2, 1); + CHECK_BLOCKS("ref1", r1, 1); + + talloc_free(p1); + + CHECK_BLOCKS("ref1", r1, 1); + + talloc_free(r1); + + if (talloc_reference(root, NULL)) { + return false; + } + + CHECK_BLOCKS("ref1", root, 1); + + CHECK_SIZE("ref1", root, 0); + + talloc_free(root); + return true; +} + +/* + test references +*/ +static bool test_ref2(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("ref2", p1, 5); + CHECK_BLOCKS("ref2", p2, 1); + CHECK_BLOCKS("ref2", r1, 2); + + talloc_free(ref); + + CHECK_BLOCKS("ref2", p1, 5); + CHECK_BLOCKS("ref2", p2, 1); + CHECK_BLOCKS("ref2", r1, 1); + + talloc_free(p2); + + CHECK_BLOCKS("ref2", p1, 4); + CHECK_BLOCKS("ref2", r1, 1); + + talloc_free(p1); + + CHECK_BLOCKS("ref2", r1, 1); + + talloc_free(r1); + + CHECK_SIZE("ref2", root, 0); + + talloc_free(root); + return true; +} + +/* + test references +*/ +static bool test_ref3(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(root, 1, "p2"); + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(p2, r1); + + CHECK_BLOCKS("ref3", p1, 2); + CHECK_BLOCKS("ref3", p2, 2); + CHECK_BLOCKS("ref3", r1, 1); + + talloc_free(p1); + + CHECK_BLOCKS("ref3", p2, 2); + CHECK_BLOCKS("ref3", r1, 1); + + talloc_free(p2); + + CHECK_SIZE("ref3", root, 0); + + talloc_free(root); + + return true; +} + +/* + test references +*/ +static bool test_ref4(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("ref4", p1, 5); + CHECK_BLOCKS("ref4", p2, 1); + CHECK_BLOCKS("ref4", r1, 2); + + talloc_free(r1); + + CHECK_BLOCKS("ref4", p1, 5); + CHECK_BLOCKS("ref4", p2, 1); + + talloc_free(p2); + + CHECK_BLOCKS("ref4", p1, 4); + + talloc_free(p1); + + CHECK_SIZE("ref4", root, 0); + + talloc_free(root); + + return true; +} + + +/* + test references +*/ +static bool test_unlink1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("unlink", p1, 7); + CHECK_BLOCKS("unlink", p2, 1); + CHECK_BLOCKS("unlink", r1, 2); + + talloc_unlink(r1, p2); + + CHECK_BLOCKS("unlink", p1, 6); + CHECK_BLOCKS("unlink", p2, 1); + CHECK_BLOCKS("unlink", r1, 1); + + talloc_free(p1); + + CHECK_SIZE("unlink", root, 0); + + talloc_free(root); + + return true; +} + +static int fail_destructor(void *ptr) +{ + return -1; +} + +/* + miscellaneous tests to try to get a higher test coverage percentage +*/ +static bool test_misc(void) +{ + void *root, *p1; + char *p2; + double *d; + const char *name; + + root = talloc_new(NULL); + + p1 = talloc_size(root, 0x7fffffff); + torture_assert("misc", !p1, "failed: large talloc allowed\n"); + + p1 = talloc_strdup(root, "foo"); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_free(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_unlink(NULL, p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + p2 = talloc_strdup(p1, "foo"); + torture_assert("misc", talloc_unlink(root, p2) == -1, + "failed: talloc_unlink() of non-reference context should return -1\n"); + torture_assert("misc", talloc_unlink(p1, p2) == 0, + "failed: talloc_unlink() of parent should succeed\n"); + talloc_free(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + + name = talloc_set_name(p1, "my name is %s", "foo"); + torture_assert_str_equal("misc", talloc_get_name(p1), "my name is foo", + "failed: wrong name after talloc_set_name(my name is foo)"); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + + talloc_set_name_const(p1, NULL); + torture_assert_str_equal ("misc", talloc_get_name(p1), "UNNAMED", + "failed: wrong name after talloc_set_name(NULL)"); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + + torture_assert("misc", talloc_free(NULL) == -1, + "talloc_free(NULL) should give -1\n"); + + talloc_set_destructor(p1, fail_destructor); + torture_assert("misc", talloc_free(p1) == -1, + "Failed destructor should cause talloc_free to fail\n"); + talloc_set_destructor(p1, NULL); + + + p2 = (char *)talloc_zero_size(p1, 20); + torture_assert("misc", p2[19] == 0, "Failed to give zero memory\n"); + talloc_free(p2); + + torture_assert("misc", talloc_strdup(root, NULL) == NULL, + "failed: strdup on NULL should give NULL\n"); + + p2 = talloc_strndup(p1, "foo", 2); + torture_assert("misc", strcmp("fo", p2) == 0, + "strndup doesn't work\n"); + p2 = talloc_asprintf_append(p2, "o%c", 'd'); + torture_assert("misc", strcmp("food", p2) == 0, + "talloc_asprintf_append doesn't work\n"); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 3); + + p2 = talloc_asprintf_append(NULL, "hello %s", "world"); + torture_assert("misc", strcmp("hello world", p2) == 0, + "talloc_asprintf_append doesn't work\n"); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 3); + talloc_free(p2); + + d = talloc_array(p1, double, 0x20000000); + torture_assert("misc", !d, "failed: integer overflow not detected\n"); + + d = talloc_realloc(p1, d, double, 0x20000000); + torture_assert("misc", !d, "failed: integer overflow not detected\n"); + + talloc_free(p1); + CHECK_BLOCKS("misc", root, 1); + + p1 = talloc_named(root, 100, "%d bytes", 100); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + talloc_unlink(root, p1); + + p1 = talloc_init("%d bytes", 200); + p2 = talloc_asprintf(p1, "my test '%s'", "string"); + torture_assert_str_equal("misc", p2, "my test 'string'", + "failed: talloc_asprintf(\"my test '%%s'\", \"string\") gave: \"%s\""); + CHECK_BLOCKS("misc", p1, 3); + CHECK_SIZE("misc", p2, 17); + CHECK_BLOCKS("misc", root, 1); + talloc_unlink(NULL, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = (char *)talloc_named_const(root, 20, "p2"); + (void)talloc_reference(p1, p2); + talloc_unlink(root, p2); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + talloc_unlink(p1, p2); + talloc_unlink(root, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = (char *)talloc_named_const(root, 20, "p2"); + (void)talloc_reference(NULL, p2); + talloc_unlink(root, p2); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_unlink(NULL, p2); + talloc_unlink(root, p1); + + /* Test that talloc_unlink is a no-op */ + + torture_assert("misc", talloc_unlink(root, NULL) == -1, + "failed: talloc_unlink(root, NULL) == -1\n"); + + CHECK_SIZE("misc", root, 0); + + talloc_free(root); + + CHECK_SIZE("misc", NULL, 0); + + talloc_enable_leak_report(); + talloc_enable_leak_report_full(); + + return true; +} + + +/* + test realloc +*/ +static bool test_realloc(void) +{ + void *root, *p1, *p2; + + root = talloc_new(NULL); + + p1 = talloc_size(root, 10); + CHECK_SIZE("realloc", p1, 10); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE("realloc", p1, 20); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, NULL, 30); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, p2, 40); + + CHECK_SIZE("realloc", p2, 40); + CHECK_SIZE("realloc", root, 60); + CHECK_BLOCKS("realloc", p1, 4); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE("realloc", p1, 60); + + talloc_increase_ref_count(p2); + torture_assert("realloc", talloc_realloc_size(NULL, p2, 5) == NULL, + "failed: talloc_realloc() on a referenced pointer should fail\n"); + CHECK_BLOCKS("realloc", p1, 4); + + talloc_realloc_size(NULL, p2, 0); + talloc_realloc_size(NULL, p2, 0); + CHECK_BLOCKS("realloc", p1, 3); + + torture_assert("realloc", talloc_realloc_size(NULL, p1, 0x7fffffff) == NULL, + "failed: oversize talloc should fail\n"); + + talloc_realloc_size(NULL, p1, 0); + + CHECK_BLOCKS("realloc", root, 1); + CHECK_SIZE("realloc", root, 0); + + talloc_free(root); + + return true; +} + +/* + test realloc with a child +*/ +static bool test_realloc_child(void) +{ + void *root; + struct el2 { + const char *name; + } *el2; + struct el1 { + int count; + struct el2 **list, **list2, **list3; + } *el1; + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + el1->list = talloc(el1, struct el2 *); + el1->list[0] = talloc(el1->list, struct el2); + el1->list[0]->name = talloc_strdup(el1->list[0], "testing"); + + el1->list2 = talloc(el1, struct el2 *); + el1->list2[0] = talloc(el1->list2, struct el2); + el1->list2[0]->name = talloc_strdup(el1->list2[0], "testing2"); + + el1->list3 = talloc(el1, struct el2 *); + el1->list3[0] = talloc(el1->list3, struct el2); + el1->list3[0]->name = talloc_strdup(el1->list3[0], "testing2"); + + el2 = talloc(el1->list, struct el2); + el2 = talloc(el1->list2, struct el2); + el2 = talloc(el1->list3, struct el2); + + el1->list = talloc_realloc(el1, el1->list, struct el2 *, 100); + el1->list2 = talloc_realloc(el1, el1->list2, struct el2 *, 200); + el1->list3 = talloc_realloc(el1, el1->list3, struct el2 *, 300); + + talloc_free(root); + + return true; +} + +/* + test type checking +*/ +static bool test_type(void) +{ + void *root; + struct el1 { + int count; + }; + struct el2 { + int count; + }; + struct el1 *el1; + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + + el1->count = 1; + + torture_assert("type", talloc_get_type(el1, struct el1) == el1, + "type check failed on el1\n"); + torture_assert("type", talloc_get_type(el1, struct el2) == NULL, + "type check failed on el1 with el2\n"); + talloc_set_type(el1, struct el2); + torture_assert("type", talloc_get_type(el1, struct el2) == (struct el2 *)el1, + "type set failed on el1 with el2\n"); + + talloc_free(root); + + return true; +} + +/* + test steal +*/ +static bool test_steal(void) +{ + void *root, *p1, *p2; + + root = talloc_new(NULL); + + p1 = talloc_array(root, char, 10); + CHECK_SIZE("steal", p1, 10); + + p2 = talloc_realloc(root, NULL, char, 20); + CHECK_SIZE("steal", p1, 10); + CHECK_SIZE("steal", root, 30); + + torture_assert("steal", talloc_steal(p1, NULL) == NULL, + "failed: stealing NULL should give NULL\n"); + + torture_assert("steal", talloc_steal(p1, p1) == p1, + "failed: stealing to ourselves is a nop\n"); + CHECK_BLOCKS("steal", root, 3); + CHECK_SIZE("steal", root, 30); + + talloc_steal(NULL, p1); + talloc_steal(NULL, p2); + CHECK_BLOCKS("steal", root, 1); + CHECK_SIZE("steal", root, 0); + + talloc_free(p1); + talloc_steal(root, p2); + CHECK_BLOCKS("steal", root, 2); + CHECK_SIZE("steal", root, 20); + + talloc_free(p2); + + CHECK_BLOCKS("steal", root, 1); + CHECK_SIZE("steal", root, 0); + + talloc_free(root); + + p1 = talloc_size(NULL, 3); + CHECK_SIZE("steal", NULL, 3); + talloc_free(p1); + + return true; +} + +/* + test move +*/ +static bool test_move(void) +{ + void *root; + struct t_move { + char *p; + int *x; + } *t1, *t2; + + root = talloc_new(NULL); + + t1 = talloc(root, struct t_move); + t2 = talloc(root, struct t_move); + t1->p = talloc_strdup(t1, "foo"); + t1->x = talloc(t1, int); + *t1->x = 42; + + t2->p = talloc_move(t2, &t1->p); + t2->x = talloc_move(t2, &t1->x); + torture_assert("move", t1->p == NULL && t1->x == NULL && + strcmp(t2->p, "foo") == 0 && *t2->x == 42, + "talloc move failed"); + + talloc_free(root); + + return true; +} + +/* + test talloc_realloc_fn +*/ +static bool test_realloc_fn(void) +{ + void *root, *p1; + + root = talloc_new(NULL); + + p1 = talloc_realloc_fn(root, NULL, 10); + CHECK_BLOCKS("realloc_fn", root, 2); + CHECK_SIZE("realloc_fn", root, 10); + p1 = talloc_realloc_fn(root, p1, 20); + CHECK_BLOCKS("realloc_fn", root, 2); + CHECK_SIZE("realloc_fn", root, 20); + p1 = talloc_realloc_fn(root, p1, 0); + CHECK_BLOCKS("realloc_fn", root, 1); + CHECK_SIZE("realloc_fn", root, 0); + + talloc_free(root); + + return true; +} + + +static bool test_unref_reparent(void) +{ + void *root, *p1, *p2, *c1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "orig parent"); + p2 = talloc_named_const(root, 1, "parent by reference"); + + c1 = talloc_named_const(p1, 1, "child"); + talloc_reference(p2, c1); + + CHECK_PARENT("unref_reparent", c1, p1); + + talloc_free(p1); + + CHECK_PARENT("unref_reparent", c1, p2); + + talloc_unlink(p2, c1); + + CHECK_SIZE("unref_reparent", root, 1); + + talloc_free(p2); + talloc_free(root); + + return true; +} + +static bool test_lifeless(void) +{ + void *top = talloc_new(NULL); + char *parent, *child; + void *child_owner = talloc_new(NULL); + + parent = talloc_strdup(top, "parent"); + child = talloc_strdup(parent, "child"); + (void)talloc_reference(child, parent); + (void)talloc_reference(child_owner, child); + talloc_unlink(top, parent); + talloc_free(child); + talloc_free(top); + talloc_free(child_owner); + talloc_free(child); + + return true; +} + +static int loop_destructor_count; + +static int test_loop_destructor(char *ptr) +{ + loop_destructor_count++; + return 0; +} + +static bool test_loop(void) +{ + void *top = talloc_new(NULL); + char *parent; + struct req1 { + char *req2, *req3; + } *req1; + + parent = talloc_strdup(top, "parent"); + req1 = talloc(parent, struct req1); + req1->req2 = talloc_strdup(req1, "req2"); + talloc_set_destructor(req1->req2, test_loop_destructor); + req1->req3 = talloc_strdup(req1, "req3"); + (void)talloc_reference(req1->req3, req1); + talloc_free(parent); + talloc_free(top); + + torture_assert("loop", loop_destructor_count == 1, + "FAILED TO FIRE LOOP DESTRUCTOR\n"); + loop_destructor_count = 0; + + return true; +} + +static int fail_destructor_str(char *ptr) +{ + return -1; +} + +static bool test_free_parent_deny_child(void) +{ + void *top = talloc_new(NULL); + char *level1; + char *level2; + char *level3; + + level1 = talloc_strdup(top, "level1"); + level2 = talloc_strdup(level1, "level2"); + level3 = talloc_strdup(level2, "level3"); + + talloc_set_destructor(level3, fail_destructor_str); + talloc_free(level1); + talloc_set_destructor(level3, NULL); + + CHECK_PARENT("free_parent_deny_child", level3, top); + + talloc_free(top); + + return true; +} + +static bool test_talloc_ptrtype(void) +{ + void *top = talloc_new(NULL); + struct struct1 { + int foo; + int bar; + } *s1, *s2, **s3, ***s4; + const char *location1; + const char *location2; + const char *location3; + const char *location4; + + s1 = talloc_ptrtype(top, s1);location1 = __location__; + + ok1(talloc_get_size(s1) == sizeof(struct struct1)); + + ok1(strcmp(location1, talloc_get_name(s1)) == 0); + + s2 = talloc_array_ptrtype(top, s2, 10);location2 = __location__; + + ok1(talloc_get_size(s2) == (sizeof(struct struct1) * 10)); + + ok1(strcmp(location2, talloc_get_name(s2)) == 0); + + s3 = talloc_array_ptrtype(top, s3, 10);location3 = __location__; + + ok1(talloc_get_size(s3) == (sizeof(struct struct1 *) * 10)); + + torture_assert_str_equal("ptrtype", location3, talloc_get_name(s3), + "talloc_array_ptrtype() sets the wrong name"); + + s4 = talloc_array_ptrtype(top, s4, 10);location4 = __location__; + + ok1(talloc_get_size(s4) == (sizeof(struct struct1 **) * 10)); + + torture_assert_str_equal("ptrtype", location4, talloc_get_name(s4), + "talloc_array_ptrtype() sets the wrong name"); + + talloc_free(top); + + return true; +} + +static int _test_talloc_free_in_destructor(void **ptr) +{ + talloc_free(*ptr); + return 0; +} + +static bool test_talloc_free_in_destructor(void) +{ + void *level0; + void *level1; + void *level2; + void *level3; + void *level4; + void **level5; + + level0 = talloc_new(NULL); + level1 = talloc_new(level0); + level2 = talloc_new(level1); + level3 = talloc_new(level2); + level4 = talloc_new(level3); + level5 = talloc(level4, void *); + + *level5 = level3; + (void)talloc_reference(level0, level3); + (void)talloc_reference(level3, level3); + (void)talloc_reference(level5, level3); + + talloc_set_destructor(level5, _test_talloc_free_in_destructor); + + talloc_free(level1); + + talloc_free(level0); + + return true; +} + +static bool test_autofree(void) +{ + /* autofree test would kill smbtorture */ + void *p; + p = talloc_autofree_context(); + talloc_free(p); + + p = talloc_autofree_context(); + talloc_free(p); + + return true; +} + +struct torture_context; +static bool torture_local_talloc(struct torture_context *tctx) +{ + bool ret = true; + + setlinebuf(stdout); + + talloc_disable_null_tracking(); + talloc_enable_null_tracking(); + + ret &= test_ref1(); + ret &= test_ref2(); + ret &= test_ref3(); + ret &= test_ref4(); + ret &= test_unlink1(); + ret &= test_misc(); + ret &= test_realloc(); + ret &= test_realloc_child(); + ret &= test_steal(); + ret &= test_move(); + ret &= test_unref_reparent(); + ret &= test_realloc_fn(); + ret &= test_type(); + ret &= test_lifeless(); + ret &= test_loop(); + ret &= test_free_parent_deny_child(); + ret &= test_talloc_ptrtype(); + ret &= test_talloc_free_in_destructor(); + ret &= test_autofree(); + + return ret; +} + +int main(void) +{ + plan_tests(134); + + torture_local_talloc(NULL); + return exit_status(); +} + diff --git a/ccan/tap/_info.c b/ccan/tap/_info.c new file mode 100644 index 00000000..2e628914 --- /dev/null +++ b/ccan/tap/_info.c @@ -0,0 +1,55 @@ +#include +#include +#include "config.h" + +/** + * tap - Test Anything Protocol + * + * The tap package produces simple-to-parse mainly-human-readable test + * output to assist in the writing of test cases. It is based on the + * (now-defunct) libtap, which is based on Perl's CPAN TAP module. Its + * output can be parsed by a harness such as CPAN's Prove. + * + * CCAN testcases are expected to output the TAP format, usually using + * this package. + * + * For more information about TAP, see: + * http://en.wikipedia.org/wiki/Test_Anything_Protocol + * + * Based on the original libtap, Copyright (c) 2004 Nik Clayton. + * + * Example: + * #include + * #include "tap/tap.h" + * + * // Run some simple (but overly chatty) tests on strcmp(). + * int main(int argc, char *argv[]) + * { + * const char a[] = "a", another_a[] = "a"; + * const char b[] = "b"; + * const char ab[] = "ab"; + * + * plan_tests(4); + * diag("Testing different pointers (%p/%p) with same contents", + * a, another_a); + * ok1(strcmp(a, another_a) == 0); + * + * diag("'a' comes before 'b'"); + * ok1(strcmp(a, b) < 0); + * ok1(strcmp(b, a) > 0); + * + * diag("'ab' comes after 'a'"); + * ok1(strcmp(ab, a) > 0); + * return exit_status(); + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + return 0; + + return 1; +} diff --git a/ccan/tap/tap.3 b/ccan/tap/tap.3 new file mode 100644 index 00000000..5395aef7 --- /dev/null +++ b/ccan/tap/tap.3 @@ -0,0 +1,361 @@ +.Dd December 20, 2004 +.Os +.Dt TAP 3 +.Sh NAME +.Nm tap +.Nd write tests that implement the Test Anything Protocol +.Sh SYNOPSIS +.In tap.h +.Sh DESCRIPTION +The +.Nm +library provides functions for writing test scripts that produce output +consistent with the Test Anything Protocol. A test harness that parses +this protocol can run these tests and produce useful reports indicating +their success or failure. +.Ss PRINTF STRINGS +In the descriptions that follow, for any function that takes as the +last two parameters +.Dq Fa char * , Fa ... +it can be assumed that the +.Fa char * +is a +.Fn printf +-like format string, and the optional arguments are values to be placed +in that string. +.Ss TEST PLANS +.Bl -tag -width indent +.It Xo +.Ft void +.Fn plan_tests "unsigned int" +.Xc +.It Xo +.Ft void +.Fn plan_no_plan "void" +.Xc +.It Xo +.Ft void +.Fn plan_skip_all "char *" "..." +.Xc +.El +.Pp +You must first specify a test plan. This indicates how many tests you +intend to run, and allows the test harness to notice if any tests were +missed, or if the test program exited prematurely. +.Pp +To do this, use +.Fn plan_tests . +The function will cause your program to exit prematurely if you specify +0 tests. +.Pp +In some situations you may not know how many tests you will be running, or +you are developing your test program, and do not want to update the +.Fn plan_tests +parameter every time you make a change. For those situations use +.Fn plan_no_plan . +It indicates to the test harness that an indeterminate number +of tests will be run. +.Pp +Both +.Fn plan_tests +and +.Fn plan_no_plan +will cause your test program to exit prematurely with a diagnostic +message if they are called more than once. +.Pp +If your test program detects at run time that some required functionality +is missing (for example, it relies on a database connection which is not +present, or a particular configuration option that has not been included +in the running kernel) use +.Fn plan_skip_all , +passing as parameters a string to display indicating the reason for skipping +the tests. +.Ss SIMPLE TESTS +.Bl -tag -width indent +.It Xo +.Ft unsigned int +.Fn ok "expression" "char *" "..." +.Xc +.It Xo +.Ft unsigned int +.Fn ok1 "expression" +.Xc +.It Xo +.Ft unsigned int +.Fn pass "char *" "..." +.Xc +.It Xo +.Ft unsigned int +.Fn fail "char *" "..." +.Xc +.El +.Pp +Tests are implemented as expressions checked by calls to the +.Fn ok +and +.Fn ok1 +macros. In both cases +.Fa expression +should evaluate to true if the test succeeded. +.Pp +.Fn ok +allows you to specify a name, or comment, describing the test which will +be included in the output. +.Fn ok1 +is for those times when the expression to be tested is self +explanatory and does not need an associated comment. In those cases +the test expression becomes the comment. +.Pp +These four calls are equivalent: +.Bd -literal -offset indent +int i = 5; + +ok(i == 5, "i equals 5"); /* Overly verbose */ +ok(i == 5, "i equals %d", i); /* Just to demonstrate printf-like + behaviour of the test name */ +ok(i == 5, "i == 5"); /* Needless repetition */ +ok1(i == 5); /* Just right */ +.Ed +.Pp +It is good practice to ensure that the test name describes the meaning +behind the test rather than what you are testing. Viz +.Bd -literal -offset indent +ok(db != NULL, "db is not NULL"); /* Not bad, but */ +ok(db != NULL, "Database conn. succeeded"); /* this is better */ +.Ed +.Pp +.Fn ok +and +.Fn ok1 +return 1 if the expression evaluated to true, and 0 if it evaluated to +false. This lets you chain calls from +.Fn ok +to +.Fn diag +to only produce diagnostic output if the test failed. For example, this +code will include diagnostic information about why the database connection +failed, but only if the test failed. +.Bd -literal -offset indent +if (!ok(db != NULL, "Database conn. succeeded")) { + diag("Database error code: %d", dberrno); +} +.Ed +.Pp +You also have +.Fn pass +and +.Fn fail . +From the Test::More documentation: +.Bd -literal -offset indent +Sometimes you just want to say that the tests have passed. +Usually the case is you've got some complicated condition +that is difficult to wedge into an ok(). In this case, +you can simply use pass() (to declare the test ok) or fail +(for not ok). + +Use these very, very, very sparingly. +.Ed +.Pp +These are synonyms for ok(1, ...) and ok(0, ...). +.Ss SKIPPING TESTS +.Bl -tag -width indent +.It Xo +.Ft void +.Fn skip "unsigned int" "char *" "..." +.Xc +.It Xo +.Fn skip_if "expression" "unsigned int" "char *" "..." +.Xc +.El +.Pp +Sets of tests can be skipped. Ordinarily you would do this because +the test can't be run in this particular testing environment. +.Pp +For example, suppose some tests should be run as root. If the test is +not being run as root then the tests should be skipped. In this +implementation, skipped tests are flagged as being ok, with a special +message indicating that they were skipped. It is your responsibility +to ensure that the number of tests skipped (the first parameter to +.Fn skip ) +is correct for the number of tests to skip. +.Pp +One way of implementing this is with a +.Dq do { } while(0); +loop, or an +.Dq if( ) { } else { } +construct, to ensure that there are no additional side effects from the +skipped tests. +.Bd -literal -offset indent +if(getuid() != 0) { + skip(1, "because test only works as root"); +} else { + ok(do_something_as_root() == 0, "Did something as root"); +} +.Ed +.Pp +A convenient macro is provided to assist with this. The previous example could +be re-written as follows. +.Bd -literal -offset indent +skip_if(getuid() != 0, 1, "because test only works as root") { + ok(do_something_as_root() == 0, "Did something as root"); +} +.Ed +.Ss MARKING TESTS AS Dq TODO +.Bl -tag -width indent +.It Xo +.Ft void +.Fn todo_start "char *" "..." +.Xc +.It Xo +.Ft void +.Fn todo_end "void" +.Xc +.El +.Pp +Sets of tests can be flagged as being +.Dq TODO . +These are tests that you expect to fail, probably because you haven't +fixed a bug, or finished a new feature yet. These tests will still be +run, but with additional output that indicates that they are expected +to fail. Should a test start to succeed unexpectedly, tools like +.Xr prove 1 +will indicate this, and you can move the test out of the todo +block. This is much more useful than simply commenting out (or +.Dq #ifdef 0 ... #endif ) +the tests. +.Bd -literal -offset indent +todo_start("dwim() not returning true yet"); + +ok(dwim(), "Did what the user wanted"); + +todo_end(); +.Ed +.Pp +Should +.Fn dwim +ever start succeeding you will know about it as soon as you run the +tests. Note that +.Em unlike +the +.Fn skip_* +family, additional code between +.Fn todo_start +and +.Fn todo_end +.Em is +executed. +.Ss SKIP vs. TODO +From the Test::More documentation; +.Bd -literal -offset indent +If it's something the user might not be able to do, use SKIP. +This includes optional modules that aren't installed, running +under an OS that doesn't have some feature (like fork() or +symlinks), or maybe you need an Internet connection and one +isn't available. + +If it's something the programmer hasn't done yet, use TODO. +This is for any code you haven't written yet, or bugs you have +yet to fix, but want to put tests in your testing script +(always a good idea). +.Ed +.Ss DIAGNOSTIC OUTPUT +.Bl -tag -width indent +.It Xo +.Fr void +.Fn diag "char *" "..." +.Xc +.El +.Pp +If your tests need to produce diagnostic output, use +.Fn diag . +It ensures that the output will not be considered by the TAP test harness. +.Fn diag +adds the necessary trailing +.Dq \en +for you. +.Bd -literal -offset indent +diag("Expected return code 0, got return code %d", rcode); +.Ed +.Ss EXIT STATUS +.Bl -tag -width indent +.It Xo +.Fr int +.Fn exit_status void +.Xc +.El +.Pp +For maximum compatability your test program should return a particular +exit code. This is calculated by +.Fn exit_status +so it is sufficient to always return from +.Fn main +with either +.Dq return exit_status(); +or +.Dq exit(exit_status()); +as appropriate. +.Sh EXAMPLES +The +.Pa tests +directory in the source distribution contains numerous tests of +.Nm +functionality, written using +.Nm . +Examine them for examples of how to construct test suites. +.Sh COMPATABILITY +.Nm +strives to be compatible with the Perl Test::More and Test::Harness +modules. The test suite verifies that +.Nm +is bug-for-bug compatible with their behaviour. This is why some +functions which would more naturally return nothing return constant +values. +.Pp +If the +.Lb libpthread +is found at compile time, +.Nm +.Em should +be thread safe. Indications to the contrary (and test cases that expose +incorrect behaviour) are very welcome. +.Sh SEE ALSO +.Xr Test::More 1 , +.Xr Test::Harness 1 , +.Xr prove 1 +.Sh STANDARDS +.Nm +requires a +.St -isoC-99 +compiler. Some of the +.Nm +functionality is implemented as variadic macros, and that functionality +was not formally codified until C99. Patches to use +.Nm +with earlier compilers that have their own implementation of variadic +macros will be gratefully received. +.Sh HISTORY +.Nm +was written to help improve the quality and coverage of the FreeBSD +regression test suite, and released in the hope that others find it +a useful tool to help improve the quality of their code. +.Sh AUTHORS +.An "Nik Clayton" Aq nik@ngo.org.uk , +.Aq nik@FreeBSD.org +.Pp +.Nm +would not exist without the efforts of +.An "Michael G Schwern" Aq schqern@pobox.com , +.An "Andy Lester" Aq andy@petdance.com , +and the countless others who have worked on the Perl QA programme. +.Sh BUGS +Ideally, running the tests would have no side effects on the behaviour +of the application you are testing. However, it is not always possible +to avoid them. The following side effects of using +.Nm +are known. +.Bl -bullet -offset indent +.It +stdout is set to unbuffered mode after calling any of the +.Fn plan_* +functions. +.El diff --git a/ccan/tap/tap.c b/ccan/tap/tap.c new file mode 100644 index 00000000..4bbd977b --- /dev/null +++ b/ccan/tap/tap.c @@ -0,0 +1,430 @@ +/*- + * Copyright (c) 2004 Nik Clayton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "tap.h" + +static int no_plan = 0; +static int skip_all = 0; +static int have_plan = 0; +static unsigned int test_count = 0; /* Number of tests that have been run */ +static unsigned int e_tests = 0; /* Expected number of tests to run */ +static unsigned int failures = 0; /* Number of tests that failed */ +static char *todo_msg = NULL; +static char *todo_msg_fixed = "libtap malloc issue"; +static int todo = 0; +static int test_died = 0; + +/* Encapsulate the pthread code in a conditional. In the absence of + libpthread the code does nothing */ +#ifdef HAVE_LIBPTHREAD +#include +static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER; +# define LOCK pthread_mutex_lock(&M) +# define UNLOCK pthread_mutex_unlock(&M) +#else +# define LOCK +# define UNLOCK +#endif + +static void +_expected_tests(unsigned int tests) +{ + + printf("1..%d\n", tests); + e_tests = tests; +} + +static void +diagv(char *fmt, va_list ap) +{ + fputs("# ", stderr); + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); +} + +static void +_diag(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + diagv(fmt, ap); + va_end(ap); +} + +/* + * Generate a test result. + * + * ok -- boolean, indicates whether or not the test passed. + * test_name -- the name of the test, may be NULL + * test_comment -- a comment to print afterwards, may be NULL + */ +unsigned int +_gen_result(int ok, const char *func, char *file, unsigned int line, + char *test_name, ...) +{ + va_list ap; + char *local_test_name = NULL; + char *c; + int name_is_digits; + + LOCK; + + test_count++; + + /* Start by taking the test name and performing any printf() + expansions on it */ + if(test_name != NULL) { + va_start(ap, test_name); + vasprintf(&local_test_name, test_name, ap); + va_end(ap); + + /* Make sure the test name contains more than digits + and spaces. Emit an error message and exit if it + does */ + if(local_test_name) { + name_is_digits = 1; + for(c = local_test_name; *c != '\0'; c++) { + if(!isdigit(*c) && !isspace(*c)) { + name_is_digits = 0; + break; + } + } + + if(name_is_digits) { + _diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name); + _diag(" Very confusing."); + } + } + } + + if(!ok) { + printf("not "); + failures++; + } + + printf("ok %d", test_count); + + if(test_name != NULL) { + printf(" - "); + + /* Print the test name, escaping any '#' characters it + might contain */ + if(local_test_name != NULL) { + flockfile(stdout); + for(c = local_test_name; *c != '\0'; c++) { + if(*c == '#') + fputc('\\', stdout); + fputc((int)*c, stdout); + } + funlockfile(stdout); + } else { /* vasprintf() failed, use a fixed message */ + printf("%s", todo_msg_fixed); + } + } + + /* If we're in a todo_start() block then flag the test as being + TODO. todo_msg should contain the message to print at this + point. If it's NULL then asprintf() failed, and we should + use the fixed message. + + This is not counted as a failure, so decrement the counter if + the test failed. */ + if(todo) { + printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); + if(!ok) + failures--; + } + + printf("\n"); + + if(!ok) + _diag(" Failed %stest (%s:%s() at line %d)", + todo ? "(TODO) " : "", file, func, line); + + free(local_test_name); + + UNLOCK; + + /* We only care (when testing) that ok is positive, but here we + specifically only want to return 1 or 0 */ + return ok ? 1 : 0; +} + +/* + * Cleanup at the end of the run, produce any final output that might be + * required. + */ +static void +_cleanup(void) +{ + + LOCK; + + /* If plan_no_plan() wasn't called, and we don't have a plan, + and we're not skipping everything, then something happened + before we could produce any output */ + if(!no_plan && !have_plan && !skip_all) { + _diag("Looks like your test died before it could output anything."); + UNLOCK; + return; + } + + if(test_died) { + _diag("Looks like your test died just after %d.", test_count); + UNLOCK; + return; + } + + + /* No plan provided, but now we know how many tests were run, and can + print the header at the end */ + if(!skip_all && (no_plan || !have_plan)) { + printf("1..%d\n", test_count); + } + + if((have_plan && !no_plan) && e_tests < test_count) { + _diag("Looks like you planned %d tests but ran %d extra.", + e_tests, test_count - e_tests); + UNLOCK; + return; + } + + if((have_plan || !no_plan) && e_tests > test_count) { + _diag("Looks like you planned %d tests but only ran %d.", + e_tests, test_count); + if(failures) { + _diag("Looks like you failed %d tests of %d run.", + failures, test_count); + } + UNLOCK; + return; + } + + if(failures) + _diag("Looks like you failed %d tests of %d.", + failures, test_count); + + UNLOCK; +} + +/* + * Initialise the TAP library. Will only do so once, however many times it's + * called. + */ +static void +_tap_init(void) +{ + static int run_once = 0; + + if(!run_once) { + atexit(_cleanup); + + /* stdout needs to be unbuffered so that the output appears + in the same place relative to stderr output as it does + with Test::Harness */ + setbuf(stdout, 0); + run_once = 1; + } +} + +/* + * Note that there's no plan. + */ +void +plan_no_plan(void) +{ + + LOCK; + + _tap_init(); + + if(have_plan != 0) { + fprintf(stderr, "You tried to plan twice!\n"); + test_died = 1; + UNLOCK; + exit(255); + } + + have_plan = 1; + no_plan = 1; + + UNLOCK; +} + +/* + * Note that the plan is to skip all tests + */ +void +plan_skip_all(char *reason) +{ + + LOCK; + + _tap_init(); + + skip_all = 1; + + printf("1..0"); + + if(reason != NULL) + printf(" # Skip %s", reason); + + printf("\n"); + + UNLOCK; +} + +/* + * Note the number of tests that will be run. + */ +void +plan_tests(unsigned int tests) +{ + + LOCK; + + _tap_init(); + + if(have_plan != 0) { + fprintf(stderr, "You tried to plan twice!\n"); + test_died = 1; + UNLOCK; + exit(255); + } + + if(tests == 0) { + fprintf(stderr, "You said to run 0 tests! You've got to run something.\n"); + test_died = 1; + UNLOCK; + exit(255); + } + + have_plan = 1; + + _expected_tests(tests); + + UNLOCK; +} + +void +diag(char *fmt, ...) +{ + va_list ap; + + LOCK; + + va_start(ap, fmt); + diagv(fmt, ap); + va_end(ap); + + UNLOCK; +} + +void +skip(unsigned int n, char *fmt, ...) +{ + va_list ap; + char *skip_msg; + + LOCK; + + va_start(ap, fmt); + vasprintf(&skip_msg, fmt, ap); + va_end(ap); + + while(n-- > 0) { + test_count++; + printf("ok %d # skip %s\n", test_count, + skip_msg != NULL ? + skip_msg : "libtap():malloc() failed"); + } + + free(skip_msg); + + UNLOCK; +} + +void +todo_start(char *fmt, ...) +{ + va_list ap; + + LOCK; + + va_start(ap, fmt); + vasprintf(&todo_msg, fmt, ap); + va_end(ap); + + todo = 1; + + UNLOCK; +} + +void +todo_end(void) +{ + + LOCK; + + todo = 0; + free(todo_msg); + + UNLOCK; +} + +int +exit_status(void) +{ + int r; + + LOCK; + + /* If there's no plan, just return the number of failures */ + if(no_plan || !have_plan) { + UNLOCK; + return failures; + } + + /* Ran too many tests? Return the number of tests that were run + that shouldn't have been */ + if(e_tests < test_count) { + r = test_count - e_tests; + UNLOCK; + return r; + } + + /* Return the number of tests that failed + the number of tests + that weren't run */ + r = failures + e_tests - test_count; + UNLOCK; + + return r; +} diff --git a/ccan/tap/tap.h b/ccan/tap/tap.h new file mode 100644 index 00000000..f854d3e3 --- /dev/null +++ b/ccan/tap/tap.h @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2004 Nik Clayton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * plan_tests - announce the number of tests you plan to run + * @tests: the number of tests + * + * This should be the first call in your test program: it allows tracing + * of failures which mean that not all tests are run. + * + * If you don't know how many tests will actually be run, assume all of them + * and use skip() if you don't actually run some tests. + * + * Example: + * plan_tests(13); + */ +void plan_tests(unsigned int tests); + +#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) && !defined(__GNUC__) +# error "Needs gcc or C99 compiler for variadic macros." +#else + +/** + * ok1 - Simple conditional test + * @e: the expression which we expect to be true. + * + * This is the simplest kind of test: if the expression is true, the + * test passes. The name of the test which is printed will simply be + * file name, line number, and the expression itself. + * + * Example: + * ok1(init_subsystem() == 1); + */ +# define ok1(e) ((e) ? \ + _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \ + _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) + +/** + * ok - Conditional test with a name + * @e: the expression which we expect to be true. + * @...: the printf-style name of the test. + * + * If the expression is true, the test passes. The name of the test will be + * the filename, line number, and the printf-style string. This can be clearer + * than simply the expression itself. + * + * Example: + * ok1(init_subsystem() == 1); + * ok(init_subsystem() == 0, "Second initialization should fail"); + */ +# define ok(e, ...) ((e) ? \ + _gen_result(1, __func__, __FILE__, __LINE__, \ + __VA_ARGS__) : \ + _gen_result(0, __func__, __FILE__, __LINE__, \ + __VA_ARGS__)) + +/** + * pass - Note that a test passed + * @...: the printf-style name of the test. + * + * For complicated code paths, it can be easiest to simply call pass() in one + * branch and fail() in another. + * + * Example: + * x = do_something(); + * if (!checkable(x) || check_value(x)) + * pass("do_something() returned a valid value"); + * else + * fail("do_something() returned an invalid value"); + */ +# define pass(...) ok(1, __VA_ARGS__) + +/** + * fail - Note that a test failed + * @...: the printf-style name of the test. + * + * For complicated code paths, it can be easiest to simply call pass() in one + * branch and fail() in another. + */ +# define fail(...) ok(0, __VA_ARGS__) + +/* I don't find these to be useful. */ +# define skip_if(cond, n, ...) \ + if (cond) skip((n), __VA_ARGS__); \ + else + +# define skip_start(test, n, ...) \ + do { \ + if((test)) { \ + skip(n, __VA_ARGS__); \ + continue; \ + } + +# define skip_end } while(0) + +#ifndef PRINTF_ATTRIBUTE +#ifdef __GNUC__ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...) + PRINTF_ATTRIBUTE(5, 6); + +/** + * diag - print a diagnostic message (use instead of printf/fprintf) + * @fmt: the format of the printf-style message + * + * diag ensures that the output will not be considered to be a test + * result by the TAP test harness. It will append '\n' for you. + * + * Example: + * diag("Now running complex tests"); + */ +void diag(char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); + +/** + * skip - print a diagnostic message (use instead of printf/fprintf) + * @n: number of tests you're skipping. + * @fmt: the format of the reason you're skipping the tests. + * + * Sometimes tests cannot be run because the test system lacks some feature: + * you should explicitly document that you're skipping tests using skip(). + * + * From the Test::More documentation: + * If it's something the user might not be able to do, use SKIP. This + * includes optional modules that aren't installed, running under an OS that + * doesn't have some feature (like fork() or symlinks), or maybe you need an + * Internet connection and one isn't available. + * + * Example: + * #ifdef HAVE_SOME_FEATURE + * ok1(test_some_feature()); + * #else + * skip(1, "Don't have SOME_FEATURE"); + * #endif + */ +void skip(unsigned int n, char *fmt, ...) PRINTF_ATTRIBUTE(2, 3); + +/** + * todo_start - mark tests that you expect to fail. + * @fmt: the reason they currently fail. + * + * It's extremely useful to write tests before you implement the matching fix + * or features: surround these tests by todo_start()/todo_end(). These tests + * will still be run, but with additional output that indicates that they are + * expected to fail. + * + * This way, should a test start to succeed unexpectedly, tools like prove(1) + * will indicate this and you can move the test out of the todo block. This + * is much more useful than simply commenting out (or '#if 0') the tests. + * + * From the Test::More documentation: + * If it's something the programmer hasn't done yet, use TODO. This is for + * any code you haven't written yet, or bugs you have yet to fix, but want to + * put tests in your testing script (always a good idea). + * + * Example: + * todo_start("dwim() not returning true yet"); + * ok(dwim(), "Did what the user wanted"); + * todo_end(); + */ +void todo_start(char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); + +/** + * todo_end - end of tests you expect to fail. + * + * See todo_start(). + */ +void todo_end(void); + +/** + * exit_status - the value that main should return. + * + * For maximum compatability your test program should return a particular exit + * code (ie. 0 if all tests were run, and every test which was expected to + * succeed succeeded). + * + * Example: + * exit(exit_status()); + */ +int exit_status(void); + +/** + * plan_no_plan - I have no idea how many tests I'm going to run. + * + * In some situations you may not know how many tests you will be running, or + * you are developing your test program, and do not want to update the + * plan_tests() call every time you make a change. For those situations use + * plan_no_plan() instead of plan_tests(). It indicates to the test harness + * that an indeterminate number of tests will be run. + * + * Remember, if you fail to plan, you plan to fail. + * + * Example: + * plan_no_plan(); + * while (random() % 2) + * ok1(some_test()); + * exit(exit_status()); + */ +void plan_no_plan(void); + +/** + * plan_skip_all - Indicate that you will skip all tests. + * @reason: the string indicating why you can't run any tests. + * + * If your test program detects at run time that some required functionality + * is missing (for example, it relies on a database connection which is not + * present, or a particular configuration option that has not been included + * in the running kernel) use plan_skip_all() instead of plan_tests(). + * + * Example: + * if (!have_some_feature) { + * plan_skip_all("Need some_feature support"); + * exit(exit_status()); + * } + * plan_tests(13); + */ +void plan_skip_all(char *reason); + +#endif /* C99 or gcc */ diff --git a/ccan/tap/test/run.c b/ccan/tap/test/run.c new file mode 100644 index 00000000..2f718cc2 --- /dev/null +++ b/ccan/tap/test/run.c @@ -0,0 +1,118 @@ +/* We use the fact that pipes have a buffer greater than the size of + * any output, and change stdout and stderr to use that. + * + * Since we don't use libtap for output, this looks like one big test. */ +#include "tap/tap.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We dup stderr to here. */ +static int stderrfd; + +/* Simple replacement for err() */ +static void failmsg(const char *fmt, ...) +{ + char buf[1024]; + va_list ap; + + /* Write into buffer. */ + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + write(stderrfd, "# ", 2); + write(stderrfd, buf, strlen(buf)); + write(stderrfd, "\n", 1); + _exit(1); +} + +static void expect(int fd, const char *pattern) +{ + char buffer[PIPE_BUF+1]; + int r; + + r = read(fd, buffer, sizeof(buffer)-1); + if (r < 0) + failmsg("reading from pipe"); + buffer[r] = '\0'; + + if (fnmatch(pattern, buffer, 0) != 0) + failmsg("Expected '%s' got '%s'", pattern, buffer); +} + +int main(int argc, char *argv[]) +{ + int p[2]; + int stdoutfd; + + printf("1..1\n"); + fflush(stdout); + stderrfd = dup(STDERR_FILENO); + if (stderrfd < 0) + err(1, "dup of stderr failed"); + + stdoutfd = dup(STDOUT_FILENO); + if (stdoutfd < 0) + err(1, "dup of stdout failed"); + + if (pipe(p) != 0) + failmsg("pipe failed"); + + if (dup2(p[1], STDERR_FILENO) < 0 || dup2(p[1], STDOUT_FILENO) < 0) + failmsg("Duplicating file descriptor"); + + plan_tests(10); + expect(p[0], "1..10\n"); + + ok(1, "msg1"); + expect(p[0], "ok 1 - msg1\n"); + + ok(0, "msg2"); + expect(p[0], "not ok 2 - msg2\n" + "# Failed test (*tap/test/run.c:main() at line 77)\n"); + + ok1(true); + expect(p[0], "ok 3 - true\n"); + + ok1(false); + expect(p[0], "not ok 4 - false\n" + "# Failed test (*tap/test/run.c:main() at line 84)\n"); + + pass("passed"); + expect(p[0], "ok 5 - passed\n"); + + fail("failed"); + expect(p[0], "not ok 6 - failed\n" + "# Failed test (*tap/test/run.c:main() at line 91)\n"); + + skip(2, "skipping %s", "test"); + expect(p[0], "ok 7 # skip skipping test\n" + "ok 8 # skip skipping test\n"); + + todo_start("todo"); + ok1(false); + expect(p[0], "not ok 9 - false # TODO todo\n" + "# Failed (TODO) test (*tap/test/run.c:main() at line 100)\n"); + ok1(true); + expect(p[0], "ok 10 - true # TODO todo\n"); + todo_end(); + + if (exit_status() != 3) + failmsg("Expected exit status 3, not %i", exit_status()); + +#if 0 + /* Manually run the atexit command. */ + _cleanup(); + expect(p[0], "# Looks like you failed 2 tests of 9.\n"); +#endif + + write(stdoutfd, "ok 1 - All passed\n", strlen("ok 1 - All passed\n")); + _exit(0); +} diff --git a/ccan/typesafe_cb/_info.c b/ccan/typesafe_cb/_info.c new file mode 100644 index 00000000..725b2665 --- /dev/null +++ b/ccan/typesafe_cb/_info.c @@ -0,0 +1,59 @@ +#include +#include +#include "config.h" + +/** + * typesafe_cb - macros for safe callbacks. + * + * The basis of the typesafe_cb header is cast_if_type(): a + * conditional cast macro. If an expression exactly matches a given + * type, it is cast to the target type, otherwise it is left alone. + * + * This allows us to create functions which take a small number of + * specific types, rather than being forced to use a void *. In + * particular, it is useful for creating typesafe callbacks as the + * helpers typesafe_cb(), typesafe_cb_preargs() and + * typesafe_cb_postargs() demonstrate. + * + * The standard way of passing arguments to callback functions in C is + * to use a void pointer, which the callback then casts back to the + * expected type. This unfortunately subverts the type checking the + * compiler would perform if it were a direct call. Here's an example: + * + * static void my_callback(void *_obj) + * { + * struct obj *obj = _obj; + * ... + * } + * ... + * register_callback(my_callback, &my_obj); + * + * If we wanted to use the natural type for my_callback (ie. "void + * my_callback(struct obj *obj)"), we could make register_callback() + * take a void * as its first argument, but this would subvert all + * type checking. We really want register_callback() to accept only + * the exactly correct function type to match the argument, or a + * function which takes a void *. + * + * This is where typesafe_cb() comes in: it uses cast_if_type() to + * cast the callback function if it matches the argument type: + * + * void _register_callback(void (*cb)(void *arg), void *arg); + * #define register_callback(cb, arg) \ + * _register_callback(typesafe_cb(void, (cb), (arg)), (arg)) + * + * On compilers which don't support the extensions required + * cast_if_type() and friend become an unconditional cast, so your + * code will compile but you won't get type checking. + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + return 0; + } + + return 1; +} diff --git a/ccan/typesafe_cb/test/compile_fail-cast_if_type.c b/ccan/typesafe_cb/test/compile_fail-cast_if_type.c new file mode 100644 index 00000000..1e417cba --- /dev/null +++ b/ccan/typesafe_cb/test/compile_fail-cast_if_type.c @@ -0,0 +1,25 @@ +#include "typesafe_cb/typesafe_cb.h" + +void _set_some_value(void *val); + +void _set_some_value(void *val) +{ +} + +#define set_some_value(expr) \ + _set_some_value(cast_if_type((expr), unsigned long, void *)) + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + int x = 0; + set_some_value(x); +#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if cast_if_type is a noop." +#endif +#else + void *p = 0; + set_some_value(p); +#endif + return 0; +} diff --git a/ccan/typesafe_cb/test/compile_fail-typesafe_cb-int.c b/ccan/typesafe_cb/test/compile_fail-typesafe_cb-int.c new file mode 100644 index 00000000..54d0e7ba --- /dev/null +++ b/ccan/typesafe_cb/test/compile_fail-typesafe_cb-int.c @@ -0,0 +1,27 @@ +#include "typesafe_cb/typesafe_cb.h" +#include + +void _callback(void (*fn)(void *arg), void *arg); +void _callback(void (*fn)(void *arg), void *arg) +{ + fn(arg); +} + +/* Callback is set up to warn if arg isn't a pointer (since it won't + * pass cleanly to _callback's second arg. */ +#define callback(fn, arg) \ + _callback(typesafe_cb(void, (fn), (arg)), (arg)) + +void my_callback(int something); +void my_callback(int something) +{ +} + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + /* This fails due to arg, not due to cast. */ + callback(my_callback, 100); +#endif + return 0; +} diff --git a/ccan/typesafe_cb/test/compile_fail-typesafe_cb.c b/ccan/typesafe_cb/test/compile_fail-typesafe_cb.c new file mode 100644 index 00000000..d305d5fb --- /dev/null +++ b/ccan/typesafe_cb/test/compile_fail-typesafe_cb.c @@ -0,0 +1,33 @@ +#include "typesafe_cb/typesafe_cb.h" +#include + +static void _register_callback(void (*cb)(void *arg), void *arg) +{ +} + +#define register_callback(cb, arg) \ + _register_callback(typesafe_cb(void, (cb), (arg)), (arg)) + +static void my_callback(char *p) +{ +} + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + int *p; +#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if cast_if_type is a noop." +#endif +#else + char *p; +#endif + p = NULL; + + /* This should work always. */ + register_callback(my_callback, "hello world"); + + /* This will fail with FAIL defined */ + register_callback(my_callback, p); + return 0; +} diff --git a/ccan/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c b/ccan/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c new file mode 100644 index 00000000..099eb257 --- /dev/null +++ b/ccan/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c @@ -0,0 +1,27 @@ +#include "typesafe_cb/typesafe_cb.h" +#include + +static void _register_callback(void (*cb)(void *arg, int x), void *arg) +{ +} +#define register_callback(cb, arg) \ + _register_callback(typesafe_cb_postargs(void, (cb), (arg), int), (arg)) + +static void my_callback(char *p, int x) +{ +} + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + int *p; +#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if cast_if_type is a noop." +#endif +#else + char *p; +#endif + p = NULL; + register_callback(my_callback, p); + return 0; +} diff --git a/ccan/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c b/ccan/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c new file mode 100644 index 00000000..38daec51 --- /dev/null +++ b/ccan/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c @@ -0,0 +1,28 @@ +#include "typesafe_cb/typesafe_cb.h" +#include + +static void _register_callback(void (*cb)(int x, void *arg), void *arg) +{ +} + +#define register_callback(cb, arg) \ + _register_callback(typesafe_cb_preargs(void, (cb), (arg), int), (arg)) + +static void my_callback(int x, char *p) +{ +} + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + int *p; +#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if cast_if_type is a noop." +#endif +#else + char *p; +#endif + p = NULL; + register_callback(my_callback, p); + return 0; +} diff --git a/ccan/typesafe_cb/test/run.c b/ccan/typesafe_cb/test/run.c new file mode 100644 index 00000000..b04fae88 --- /dev/null +++ b/ccan/typesafe_cb/test/run.c @@ -0,0 +1,148 @@ +#include "typesafe_cb/typesafe_cb.h" +#include +#include "tap/tap.h" + +static char dummy = 0; + +/* The example usage. */ +static void _set_some_value(void *val) +{ + ok1(val == &dummy); +} + +#define set_some_value(expr) \ + _set_some_value(cast_if_type((expr), unsigned long, void *)) + +static void _callback_onearg(void (*fn)(void *arg), void *arg) +{ + fn(arg); +} + +static void _callback_preargs(void (*fn)(int a, int b, void *arg), void *arg) +{ + fn(1, 2, arg); +} + +static void _callback_postargs(void (*fn)(void *arg, int a, int b), void *arg) +{ + fn(arg, 1, 2); +} + +#define callback_onearg(cb, arg) \ + _callback_onearg(typesafe_cb(void, (cb), (arg)), (arg)) + +#define callback_preargs(cb, arg) \ + _callback_preargs(typesafe_cb_preargs(void, (cb), (arg), int, int), (arg)) + +#define callback_postargs(cb, arg) \ + _callback_postargs(typesafe_cb_postargs(void, (cb), (arg), int, int), (arg)) + +static void my_callback_onearg(char *p) +{ + ok1(strcmp(p, "hello world") == 0); +} + +static void my_callback_onearg_const(const char *p) +{ + ok1(strcmp(p, "hello world") == 0); +} + +static void my_callback_onearg_volatile(volatile char *p) +{ + ok1(strcmp((char *)p, "hello world") == 0); +} + +static void my_callback_preargs(int a, int b, char *p) +{ + ok1(a == 1); + ok1(b == 2); + ok1(strcmp(p, "hello world") == 0); +} + +static void my_callback_preargs_const(int a, int b, const char *p) +{ + ok1(a == 1); + ok1(b == 2); + ok1(strcmp(p, "hello world") == 0); +} + +static void my_callback_preargs_volatile(int a, int b, volatile char *p) +{ + ok1(a == 1); + ok1(b == 2); + ok1(strcmp((char *)p, "hello world") == 0); +} + +static void my_callback_postargs(char *p, int a, int b) +{ + ok1(a == 1); + ok1(b == 2); + ok1(strcmp(p, "hello world") == 0); +} + +static void my_callback_postargs_const(const char *p, int a, int b) +{ + ok1(a == 1); + ok1(b == 2); + ok1(strcmp(p, "hello world") == 0); +} + +static void my_callback_postargs_volatile(volatile char *p, int a, int b) +{ + ok1(a == 1); + ok1(b == 2); + ok1(strcmp((char *)p, "hello world") == 0); +} + +/* This is simply a compile test; we promised cast_if_type can be in a + * static initializer. */ +struct callback_onearg +{ + void (*fn)(void *arg); + void *arg; +}; + +struct callback_onearg cb_onearg += { typesafe_cb(void, my_callback_onearg, "hello world"), "hello world" }; + +struct callback_preargs +{ + void (*fn)(int a, int b, void *arg); + void *arg; +}; + +struct callback_preargs cb_preargs += { typesafe_cb_preargs(void, my_callback_preargs, "hi", int, int), "hi" }; + +struct callback_postargs +{ + void (*fn)(void *arg, int a, int b); + void *arg; +}; + +struct callback_postargs cb_postargs += { typesafe_cb_postargs(void, my_callback_postargs, "hi", int, int), "hi" }; + +int main(int argc, char *argv[]) +{ + void *p = &dummy; + unsigned long l = (unsigned long)p; + + plan_tests(2 + 3 + 9 + 9); + set_some_value(p); + set_some_value(l); + + callback_onearg(my_callback_onearg, "hello world"); + callback_onearg(my_callback_onearg_const, "hello world"); + callback_onearg(my_callback_onearg_volatile, "hello world"); + + callback_preargs(my_callback_preargs, "hello world"); + callback_preargs(my_callback_preargs_const, "hello world"); + callback_preargs(my_callback_preargs_volatile, "hello world"); + + callback_postargs(my_callback_postargs, "hello world"); + callback_postargs(my_callback_postargs_const, "hello world"); + callback_postargs(my_callback_postargs_volatile, "hello world"); + + return exit_status(); +} diff --git a/ccan/typesafe_cb/typesafe_cb.h b/ccan/typesafe_cb/typesafe_cb.h new file mode 100644 index 00000000..cc4ea8ad --- /dev/null +++ b/ccan/typesafe_cb/typesafe_cb.h @@ -0,0 +1,120 @@ +#ifndef CCAN_CAST_IF_TYPE_H +#define CCAN_CAST_IF_TYPE_H +#include "config.h" + +#if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P +/** + * cast_if_type - only cast an expression if it is of a given type + * @expr: the expression to cast + * @oktype: the type we allow + * @desttype: the type to cast to + * + * This macro is used to create functions which allow multiple types. + * The result of this macro is used somewhere that a @desttype type is + * expected: if @expr was of type @oktype, it will be cast to + * @desttype type. As a result, if @expr is any type other than + * @oktype or @desttype, a compiler warning will be issued. + * + * This macro can be used in static initializers. + * + * This is merely useful for warnings: if the compiler does not + * support the primitives required for cast_if_type(), it becomes an + * unconditional cast, and the @oktype argument is not used. In + * particular, this means that @oktype can be a type which uses + * the "typeof": it will not be evaluated if typeof is not supported. + * + * Example: + * // We can take either an unsigned long or a void *. + * void _set_some_value(void *val); + * #define set_some_value(expr) \ + * _set_some_value(cast_if_type((expr), unsigned long, void *)) + */ +#define cast_if_type(expr, oktype, desttype) \ +__builtin_choose_expr(__builtin_types_compatible_p(typeof(1?(expr):0), oktype), \ + (desttype)(expr), (expr)) +#else +#define cast_if_type(expr, oktype, desttype) ((desttype)(expr)) +#endif + +/** + * typesafe_cb - cast a callback function if it matches the arg + * @rettype: the return type of the callback function + * @fn: the callback function to cast + * @arg: the (pointer) argument to hand to the callback function. + * + * If a callback function takes a single argument, this macro does + * appropriate casts to a function which takes a single void * argument if the + * callback provided matches the @arg (or a const or volatile version). + * + * It is assumed that @arg is of pointer type: usually @arg is passed + * or assigned to a void * elsewhere anyway. + * + * Example: + * void _register_callback(void (*fn)(void *arg), void *arg); + * #define register_callback(fn, arg) \ + * _register_callback(typesafe_cb(void, (fn), (arg)), (arg)) + */ +#define typesafe_cb(rettype, fn, arg) \ + cast_if_type(cast_if_type(cast_if_type((fn), \ + rettype (*)(const typeof(arg)), \ + rettype (*)(void *)), \ + rettype (*)(volatile typeof(arg)), \ + rettype (*)(void *)), \ + rettype (*)(typeof(arg)), \ + rettype (*)(void *)) + +/** + * typesafe_cb_preargs - cast a callback function if it matches the arg + * @rettype: the return type of the callback function + * @fn: the callback function to cast + * @arg: the (pointer) argument to hand to the callback function. + * + * This is a version of typesafe_cb() for callbacks that take other arguments + * before the @arg. + * + * Example: + * void _register_callback(void (*fn)(int, void *arg), void *arg); + * #define register_callback(fn, arg) \ + * _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ + * (arg)) + */ +#define typesafe_cb_preargs(rettype, fn, arg, ...) \ + cast_if_type(cast_if_type(cast_if_type((fn), \ + rettype (*)(__VA_ARGS__, \ + const typeof(arg)), \ + rettype (*)(__VA_ARGS__, \ + void *)), \ + rettype (*)(__VA_ARGS__, \ + volatile typeof(arg)), \ + rettype (*)(__VA_ARGS__, void *)), \ + rettype (*)(__VA_ARGS__, typeof(arg)), \ + rettype (*)(__VA_ARGS__, void *)) + +/** + * typesafe_cb_postargs - cast a callback function if it matches the arg + * @rettype: the return type of the callback function + * @fn: the callback function to cast + * @arg: the (pointer) argument to hand to the callback function. + * + * This is a version of typesafe_cb() for callbacks that take other arguments + * after the @arg. + * + * Example: + * void _register_callback(void (*fn)(void *arg, int), void *arg); + * #define register_callback(fn, arg) \ + * _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ + * (arg)) + */ +#define typesafe_cb_postargs(rettype, fn, arg, ...) \ + cast_if_type(cast_if_type(cast_if_type((fn), \ + rettype (*)(const typeof(arg), \ + __VA_ARGS__), \ + rettype (*)(void *, \ + __VA_ARGS__)), \ + rettype (*)(volatile typeof(arg), \ + __VA_ARGS__), \ + rettype (*)(void *, __VA_ARGS__)), \ + rettype (*)(typeof(arg), __VA_ARGS__), \ + rettype (*)(void *, __VA_ARGS__)) + +#endif /* CCAN_CAST_IF_TYPE_H */ diff --git a/ccan_tools/Makefile b/ccan_tools/Makefile deleted file mode 100644 index 7cf228ee..00000000 --- a/ccan_tools/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -ccan_tools/run_tests: ccan_tools/run_tests.o tap/tap.o talloc/talloc.o - -ccan_tools/doc_extract: ccan_tools/doc_extract.c talloc/talloc.o - -ccan_tools/namespacize: ccan_tools/namespacize.c talloc/talloc.o - -ccan_tools-clean: ccanlint-clean - rm -f run_tests doc_extract - -include ccan_tools/ccanlint/Makefile diff --git a/ccan_tools/ccanlint/Makefile b/ccan_tools/ccanlint/Makefile deleted file mode 100644 index ddc5534b..00000000 --- a/ccan_tools/ccanlint/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -OBJS := ccan_tools/ccanlint/no_info.o \ - ccan_tools/ccanlint/has_main_header.o \ - ccan_tools/ccanlint/has_tests.o \ - ccan_tools/ccanlint/trailing_whitespace.o \ - ccan_tools/ccanlint/idempotent.o \ - -FUTURE:=ccan_tools/ccanlint/if_have_not_ifdef.o \ - ccan_tools/ccanlint/needs_depends.o \ - ccan_tools/ccanlint/has_info_documentation.o \ - ccan_tools/ccanlint/has_header_documentation.o \ - ccan_tools/ccanlint/has_tests.o \ - ccan_tools/ccanlint/builds_ok.o \ - ccan_tools/ccanlint/builds_ok_all_have_variants.o \ - ccan_tools/ccanlint/run_tests.o \ - ccan_tools/ccanlint/test_coverage.o \ - -ccan_tools/ccanlint/generated-init-tests: $(OBJS) - cat $(OBJS:.o=.c) | sed -n 's/^struct ccanlint \([A-Za-z0-9_]*\) = {/{ extern struct ccanlint \1; list_add(\&tests, \&\1.list); }/p' >$@ - -ccan_tools/ccanlint/ccanlint.o: ccan_tools/ccanlint/generated-init-tests - -ccan_tools/ccanlint/ccanlint: \ - $(OBJS) \ - ccan_tools/ccanlint/ccanlint.o \ - ccan_tools/ccanlint/get_file_lines.o \ - ccan_tools/ccanlint/file_analysis.o \ - talloc/talloc.o noerr/noerr.o - -ccanlint-clean: - $(RM) ccan_tools/ccanlint/generated-init-tests - diff --git a/ccan_tools/ccanlint/ccanlint.c b/ccan_tools/ccanlint/ccanlint.c deleted file mode 100644 index 001ee689..00000000 --- a/ccan_tools/ccanlint/ccanlint.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * ccanlint: assorted checks and advice for a ccan package - * Copyright (C) 2008 Rusty Russell - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#include "ccanlint.h" -#include -#include -#include -#include -#include -#include -#include - -static unsigned int verbose = 0; -static LIST_HEAD(tests); - -static void init_tests(void) -{ -#include "generated-init-tests" -} - -static void usage(const char *name) -{ - fprintf(stderr, "Usage: %s [-s] [-v] [-d ]\n" - " -v: verbose mode\n" - " -s: simply give one line per FAIL and total score\n" - " -d: use this directory instead of the current one\n", - name); - exit(1); -} - -static void indent_print(const char *string) -{ - while (*string) { - unsigned int line = strcspn(string, "\n"); - printf("\t%.*s", line, string); - if (string[line] == '\n') { - printf("\n"); - line++; - } - string += line; - } -} - -bool ask(const char *question) -{ - char reply[2]; - - printf("%s ", question); - fflush(stdout); - - return fgets(reply, sizeof(reply), stdin) != NULL - && toupper(reply[0]) == 'Y'; -} - -static bool run_test(const struct ccanlint *i, - bool summary, - unsigned int *score, - unsigned int *total_score, - struct manifest *m) -{ - void *result; - unsigned int this_score; - - if (i->total_score) - *total_score += i->total_score; - - result = i->check(m); - if (!result) { - if (verbose) - printf(" %s: OK\n", i->name); - if (i->total_score) - *score += i->total_score; - return true; - } - - if (i->score) - this_score = i->score(m, result); - else - this_score = 0; - - *score += this_score; - if (summary) { - printf("%s FAILED (%u/%u)\n", - i->name, this_score, i->total_score); - - if (verbose) - indent_print(i->describe(m, result)); - return false; - } - - printf("%s\n", i->describe(m, result)); - - if (i->handle) - i->handle(m, result); - - return false; -} - -int main(int argc, char *argv[]) -{ - int c; - bool summary = false; - unsigned int score, total_score; - struct manifest *m; - const struct ccanlint *i; - - /* I'd love to use long options, but that's not standard. */ - /* FIXME: getopt_long ccan package? */ - while ((c = getopt(argc, argv, "sd:v")) != -1) { - switch (c) { - case 'd': - if (chdir(optarg) != 0) - err(1, "Changing into directory '%s'", optarg); - break; - case 's': - summary = true; - break; - case 'v': - verbose++; - break; - default: - usage(argv[0]); - } - } - - if (optind < argc) - usage(argv[0]); - - m = get_manifest(); - - init_tests(); - - /* If you don't pass the compulsory tests, you don't even get a score */ - if (verbose) - printf("Compulsory tests:\n"); - list_for_each(&tests, i, list) - if (!i->total_score && !run_test(i, summary, NULL, NULL, m)) - exit(1); - - if (verbose) - printf("\nNormal tests:\n"); - score = total_score = 0; - list_for_each(&tests, i, list) - if (i->total_score) - run_test(i, summary, &score, &total_score, m); - - printf("Total score: %u/%u\n", score, total_score); - - return 0; -} diff --git a/ccan_tools/ccanlint/ccanlint.h b/ccan_tools/ccanlint/ccanlint.h deleted file mode 100644 index 51f555ec..00000000 --- a/ccan_tools/ccanlint/ccanlint.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef CCAN_LINT_H -#define CCAN_LINT_H -#include -#include - -struct manifest { - char *basename; - struct ccan_file *info_file; - - struct list_head c_files; - struct list_head h_files; - - struct list_head run_tests; - struct list_head compile_ok_tests; - struct list_head compile_fail_tests; - struct list_head other_test_files; - - struct list_head other_files; -}; - -struct manifest *get_manifest(void); - -struct ccanlint { - struct list_node list; - - /* Unique name of test */ - const char *name; - - /* Total score that this test is worth. 0 means compulsory tests. */ - unsigned int total_score; - - /* If this returns non-NULL, it means the check failed. */ - void *(*check)(struct manifest *m); - - /* The non-NULL return from check is passed to one of these: */ - - /* So, what did this get out of the total_score? (NULL means 0). */ - unsigned int (*score)(struct manifest *m, void *check_result); - - /* Verbose description of what was wrong. */ - const char *(*describe)(struct manifest *m, void *check_result); - - /* Can we do something about it? (NULL if not) */ - void (*handle)(struct manifest *m, void *check_result); -}; - -/* Ask the user a yes/no question: the answer is NO if there's an error. */ -bool ask(const char *question); - -struct ccan_file { - struct list_node list; - - char *name; - - unsigned int num_lines; - char **lines; -}; - -/* Use this rather than accessing f->lines directly: loads on demand. */ -char **get_ccan_file_lines(struct ccan_file *f); - -/* Call the reporting on every line in the file. sofar contains - * previous results. */ -char *report_on_lines(struct list_head *files, - char *(*report)(const char *), - char *sofar); - -/* The critical tests which mean fail if they don't pass. */ -extern struct ccanlint no_info; -extern struct ccanlint has_main_header; - -/* Normal tests. */ -extern struct ccanlint trailing_whitespace; - - -#endif /* CCAN_LINT_H */ diff --git a/ccan_tools/ccanlint/file_analysis.c b/ccan_tools/ccanlint/file_analysis.c deleted file mode 100644 index 1341e57b..00000000 --- a/ccan_tools/ccanlint/file_analysis.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "ccanlint.h" -#include "get_file_lines.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -char **get_ccan_file_lines(struct ccan_file *f) -{ - if (!f->lines) - f->lines = get_file_lines(f, f->name, &f->num_lines); - return f->lines; -} - -static void add_files(struct manifest *m, const char *dir) -{ - DIR *d; - struct dirent *ent; - - if (dir[0]) - d = opendir(dir); - else - d = opendir("."); - if (!d) - err(1, "Opening directory %s", dir[0] ? dir : "."); - - while ((ent = readdir(d)) != NULL) { - struct stat st; - struct ccan_file *f; - struct list_head *dest; - bool is_c_src; - - if (ent->d_name[0] == '.') - continue; - - f = talloc(m, struct ccan_file); - f->lines = NULL; - f->name = talloc_asprintf(f, "%s%s", dir, ent->d_name); - if (lstat(f->name, &st) != 0) - err(1, "lstat %s", f->name); - - if (S_ISDIR(st.st_mode)) { - f->name = talloc_append_string(f->name, "/"); - add_files(m, f->name); - continue; - } - if (!S_ISREG(st.st_mode)) { - talloc_free(f); - continue; - } - - if (streq(f->name, "_info.c")) { - m->info_file = f; - continue; - } - - is_c_src = strends(f->name, ".c"); - if (!is_c_src && !strends(f->name, ".h")) - dest = &m->other_files; - else if (!strchr(f->name, '/')) { - if (is_c_src) - dest = &m->c_files; - else - dest = &m->h_files; - } else if (strstarts(f->name, "test/")) { - if (is_c_src) { - if (strstarts(f->name, "test/run")) - dest = &m->run_tests; - else if (strstarts(f->name, "test/compile_ok")) - dest = &m->compile_ok_tests; - else if (strstarts(f->name, "test/compile_fail")) - dest = &m->compile_fail_tests; - else - dest = &m->other_test_files; - } else - dest = &m->other_test_files; - } else - dest = &m->other_files; - - list_add(dest, &f->list); - } - closedir(d); -} - -char *report_on_lines(struct list_head *files, - char *(*report)(const char *), - char *sofar) -{ - struct ccan_file *f; - - list_for_each(files, f, list) { - unsigned int i; - char **lines = get_ccan_file_lines(f); - - for (i = 0; i < f->num_lines; i++) { - char *r = report(lines[i]); - if (!r) - continue; - - sofar = talloc_asprintf_append(sofar, - "%s:%u:%s\n", - f->name, i+1, r); - talloc_free(r); - } - } - return sofar; -} - -struct manifest *get_manifest(void) -{ - struct manifest *m = talloc(NULL, struct manifest); - unsigned int len; - - m->info_file = NULL; - list_head_init(&m->c_files); - list_head_init(&m->h_files); - list_head_init(&m->run_tests); - list_head_init(&m->compile_ok_tests); - list_head_init(&m->compile_fail_tests); - list_head_init(&m->other_test_files); - list_head_init(&m->other_files); - - /* *This* is why people hate C. */ - len = 32; - m->basename = talloc_array(m, char, len); - while (!getcwd(m->basename, len)) { - if (errno != ERANGE) - err(1, "Getting current directory"); - m->basename = talloc_realloc(m, m->basename, char, len *= 2); - } - - len = strlen(m->basename); - while (len && m->basename[len-1] == '/') - m->basename[--len] = '\0'; - - m->basename = strrchr(m->basename, '/'); - if (!m->basename) - errx(1, "I don't expect to be run from the root directory"); - m->basename++; - - add_files(m, ""); - return m; -} diff --git a/ccan_tools/ccanlint/get_file_lines.c b/ccan_tools/ccanlint/get_file_lines.c deleted file mode 100644 index e9ef302b..00000000 --- a/ccan_tools/ccanlint/get_file_lines.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "get_file_lines.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void *grab_fd(const void *ctx, int fd) -{ - int ret; - unsigned int max = 16384, size = 0; - char *buffer; - - buffer = talloc_array(ctx, char, max+1); - while ((ret = read(fd, buffer + size, max - size)) > 0) { - size += ret; - if (size == max) - buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1); - } - if (ret < 0) { - talloc_free(buffer); - buffer = NULL; - } else - buffer[size] = '\0'; - - return buffer; -} - -/* This version adds one byte (for nul term) */ -static void *grab_file(const void *ctx, const char *filename) -{ - int fd; - char *buffer; - - if (streq(filename, "-")) - fd = dup(STDIN_FILENO); - else - fd = open(filename, O_RDONLY, 0); - - if (fd < 0) - return NULL; - - buffer = grab_fd(ctx, fd); - close_noerr(fd); - return buffer; -} - -/* This is a dumb one which copies. We could mangle instead. */ -static char **split(const void *ctx, const char *text, const char *delims, - unsigned int *nump) -{ - char **lines = NULL; - unsigned int max = 64, num = 0; - - lines = talloc_array(ctx, char *, max+1); - - while (*text != '\0') { - unsigned int len = strcspn(text, delims); - lines[num] = talloc_array(lines, char, len + 1); - memcpy(lines[num], text, len); - lines[num][len] = '\0'; - text += len; - text += strspn(text, delims); - if (++num == max) - lines = talloc_realloc(ctx, lines, char *, max*=2 + 1); - } - lines[num] = NULL; - if (nump) - *nump = num; - return lines; -} - -char **get_file_lines(void *ctx, const char *name, unsigned int *num_lines) -{ - char *buffer = grab_file(ctx, name); - - if (!buffer) - err(1, "Getting file %s", name); - - return split(buffer, buffer, "\n", num_lines); -} diff --git a/ccan_tools/ccanlint/get_file_lines.h b/ccan_tools/ccanlint/get_file_lines.h deleted file mode 100644 index 2f8455a5..00000000 --- a/ccan_tools/ccanlint/get_file_lines.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef GET_FILE_LINES_H -#define GET_FILE_LINES_H - -char **get_file_lines(void *ctx, const char *name, unsigned int *num_lines); - -#endif /* GET_FILE_LINES_H */ diff --git a/ccan_tools/ccanlint/has_main_header.c b/ccan_tools/ccanlint/has_main_header.c deleted file mode 100644 index 75e3f91d..00000000 --- a/ccan_tools/ccanlint/has_main_header.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "ccanlint.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void *check_has_main_header(struct manifest *m) -{ - struct ccan_file *f; - - list_for_each(&m->h_files, f, list) { - if (strstarts(f->name, m->basename) - && strlen(f->name) == strlen(m->basename) + 2) - return NULL; - } - return m; -} - -static const char *describe_has_main_header(struct manifest *m, - void *check_result) -{ - return talloc_asprintf(m, - "You have no %s/%s.h header file.\n\n" - "CCAN modules have a name, the same as the directory name. They're\n" - "expected to have an interface in the header of the same name.\n", - m->basename, m->basename); -} - -struct ccanlint has_main_header = { - .name = "No main header file", - .check = check_has_main_header, - .describe = describe_has_main_header, -}; diff --git a/ccan_tools/ccanlint/has_tests.c b/ccan_tools/ccanlint/has_tests.c deleted file mode 100644 index 7a187ab2..00000000 --- a/ccan_tools/ccanlint/has_tests.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "ccanlint.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static char test_is_not_dir[] = "test is not a directory"; - -static void *check_has_tests(struct manifest *m) -{ - struct stat st; - - if (lstat("test", &st) != 0) { - if (errno != ENOENT) - err(1, "statting test/"); - return "You have no test directory"; - } - - if (!S_ISDIR(st.st_mode)) - return test_is_not_dir; - - if (list_empty(&m->run_tests) && list_empty(&m->compile_ok_tests)) { - if (list_empty(&m->compile_fail_tests)) - return "You have no tests in the test directory"; - else - return "You have no positive tests in the test directory"; - } - return NULL; -} - -static const char *describe_has_tests(struct manifest *m, void *check_result) -{ - return talloc_asprintf(m, "%s\n\n" - "CCAN modules have a directory called test/ which contains tests.\n" - "There are three kinds of tests: run, compile_ok and compile_fail:\n" - "you can tell which type of test a C file is by its name, eg 'run.c'\n" - "and 'run-simple.c' are both run tests.\n\n" - "The simplest kind of test is a run test, which must compile with no\n" - "warnings, and then run: it is expected to use libtap to report its\n" - "results in a simple and portable format.\n" - "compile_ok tests are a subset of run tests: they must compile and\n" - "link, but aren't run.\n" - "compile_fail tests are tests which should fail to compile (or emit\n" - "warnings) or link when FAIL is defined, but should compile and link\n" - "when it's not defined: this helps ensure unrelated errors don't make\n" - "compilation fail.\n\n" - "Note that the tests are not linked against the files in the\n" - "above: you should directly #include those C files you want. This\n" - "allows access to static functions and use special effects inside\n" - "test files\n", (char *)check_result); -} - -static void handle_no_tests(struct manifest *m, void *check_result) -{ - FILE *run; - struct ccan_file *i; - - if (check_result == test_is_not_dir) - return; - - if (!ask("Should I create a template test/run.c file for you?")) - return; - - if (mkdir("test", 0600) != 0) { - if (errno != EEXIST) - err(1, "Creating test/ directory"); - } - - run = fopen("test/run.c", "w"); - if (!run) - err(1, "Trying to create a test/run.c"); - - fputs("/* Include the main header first, to test it works */\n", run); - fprintf(run, "#include \"%s/%s.h\"\n", m->basename, m->basename); - fputs("/* Include the C files directly. */\n", run); - list_for_each(&m->c_files, i, list) - fprintf(run, "#include \"%s/%s\"\n", m->basename, i->name); - fputs("#include \"tap/tap.h\"\n", run); - fputs("\n", run); - - fputs("int main(int argc, char *argv[])\n", run); - fputs("{\n", run); - fputs("\t/* This is how many tests you plan to run\n", run); - fputs("\tplan_tests(3);\n", run); - fputs("\n", run); - fputs("\t/* Simple thing we expect to succeed */\n", run); - fputs("\tok1(some_test())\n", run); - fputs("\t/* Same, with an explicit description of the test. */\n", run); - fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run); - fputs("\t/* How to print out messages for debugging. */\n", run); - fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run); - fputs("\t/* Conditional tests must be explicitly skipped. */\n", run); - fputs("#if HAVE_SOME_FEATURE\n", run); - fputs("\tok1(test_some_feature())\n", run); - fputs("#else\n", run); - fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run); - fputs("#endif\n", run); - fputs("\n", run); - fputs("\t/* This exits depending on whether all tests passed */\n", run); - fputs("\return exit_status()\n", run); - - fclose(run); -} - -struct ccanlint has_tests = { - .name = "No tests", - .check = check_has_tests, - .describe = describe_has_tests, - .handle = handle_no_tests, -}; diff --git a/ccan_tools/ccanlint/idempotent.c b/ccan_tools/ccanlint/idempotent.c deleted file mode 100644 index 44565575..00000000 --- a/ccan_tools/ccanlint/idempotent.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "ccanlint.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char explain[] -= "Headers usually start with the C preprocessor lines to prevent multiple\n" - "inclusions. These look like the following:\n" - "#ifndef MY_HEADER_H\n" - "#define MY_HEADER_H\n" - "...\n" - "#endif /* MY_HEADER_H */\n"; - -static char *report_idem(struct ccan_file *f, char *sofar) -{ - char **lines; - char *secondline; - - lines = get_ccan_file_lines(f); - if (f->num_lines < 3) - /* FIXME: We assume small headers probably uninteresting. */ - return NULL; - - if (!strstarts(lines[0], "#ifndef ")) - return talloc_asprintf_append(sofar, - "%s:1:expect first line to be #ifndef.\n", f->name); - - secondline = talloc_asprintf(f, "#define %s", - lines[0] + strlen("#ifndef ")); - if (!streq(lines[1], secondline)) - return talloc_asprintf_append(sofar, - "%s:2:expect second line to be '%s'.\n", - f->name, secondline); - - return sofar; -} - -static void *check_idempotent(struct manifest *m) -{ - struct ccan_file *f; - char *report = NULL; - - list_for_each(&m->h_files, f, list) - report = report_idem(f, report); - - return report; -} - -static const char *describe_idempotent(struct manifest *m, void *check_result) -{ - return talloc_asprintf(check_result, - "Some headers not idempotent:\n" - "%s\n%s", (char *)check_result, - explain); -} - -struct ccanlint idempotent = { - .name = "Headers are #ifndef/#define idempotent wrapped", - .total_score = 1, - .check = check_idempotent, - .describe = describe_idempotent, -}; diff --git a/ccan_tools/ccanlint/no_info.c b/ccan_tools/ccanlint/no_info.c deleted file mode 100644 index 9ee5f709..00000000 --- a/ccan_tools/ccanlint/no_info.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "ccanlint.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void *check_no_info(struct manifest *m) -{ - if (m->info_file) - return NULL; - return m; -} - -static const char *describe_no_info(struct manifest *m, void *check_result) -{ - return "You have no _info.c file.\n\n" - "The file _info.c contains the metadata for a ccan package: things\n" - "like the dependencies, the documentation for the package as a whole\n" - "and license information.\n"; -} - -static const char template[] = - "#include \n" - "#include \"config.h\"\n" - "\n" - "/**\n" - " * %s - YOUR-ONE-LINE-DESCRIPTION-HERE\n" - " *\n" - " * This code ... YOUR-BRIEF-SUMMARY-HERE\n" - " *\n" - " * Example:\n" - " * FULLY-COMPILABLE-INDENTED-TRIVIAL-BUT-USEFUL-EXAMPLE-HERE\n" - " */\n" - "int main(int argc, char *argv[])\n" - "{\n" - " /* Expect exactly one argument\n" - " if (argc != 2)\n" - " return 1;\n" - "\n" - " if (strcmp(argv[1], \"depends\") == 0) {\n" - " PRINTF-CCAN-PACKAGES-YOU-NEED-ONE-PER-LINE-IF-ANY\n" - " return 0;\n" - " }\n" - "\n" - " return 1;\n" - "}\n"; - -static void create_info_template(struct manifest *m, void *check_result) -{ - FILE *info; - - if (!ask("Should I create a template _info.c file for you?")) - return; - - info = fopen("_info.c", "w"); - if (!info) - err(1, "Trying to create a template _info.c"); - - if (fprintf(info, template, m->basename) < 0) { - unlink_noerr("_info.c"); - err(1, "Writing template into _info.c"); - } - fclose(info); -} - -struct ccanlint no_info = { - .name = "No _info.c file", - .check = check_no_info, - .describe = describe_no_info, - .handle = create_info_template, -}; diff --git a/ccan_tools/ccanlint/trailing_whitespace.c b/ccan_tools/ccanlint/trailing_whitespace.c deleted file mode 100644 index 3aeec6e5..00000000 --- a/ccan_tools/ccanlint/trailing_whitespace.c +++ /dev/null @@ -1,42 +0,0 @@ -/* Trailing whitespace test. Almost embarrassing, but trivial. */ -#include "ccanlint.h" -#include -#include - -static char *report_on_trailing_whitespace(const char *line) -{ - if (!strends(line, " ") && !strends(line, "\t")) - return NULL; - - if (strlen(line) > 20) - return talloc_asprintf(line, "...'%s'", - line + strlen(line) - 20); - return talloc_asprintf(line, "'%s'", line); -} - -static void *check_trailing_whitespace(struct manifest *m) -{ - char *report; - - report = report_on_lines(&m->c_files, report_on_trailing_whitespace, - NULL); - report = report_on_lines(&m->h_files, report_on_trailing_whitespace, - report); - - return report; -} - -static const char *describe_trailing_whitespace(struct manifest *m, - void *check_result) -{ - return talloc_asprintf(check_result, - "Some source files have trailing whitespace:\n" - "%s", (char *)check_result); -} - -struct ccanlint trailing_whitespace = { - .name = "Lines with unnecessary trailing whitespace", - .total_score = 1, - .check = check_trailing_whitespace, - .describe = describe_trailing_whitespace, -}; diff --git a/ccan_tools/doc_extract.c b/ccan_tools/doc_extract.c deleted file mode 100644 index b4ac0d3e..00000000 --- a/ccan_tools/doc_extract.c +++ /dev/null @@ -1,107 +0,0 @@ -/* This merely extracts, doesn't do XML or anything. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "talloc/talloc.h" - -/* Is A == B ? */ -#define streq(a,b) (strcmp((a),(b)) == 0) - -/* Does A start with B ? */ -#define strstarts(a,b) (strncmp((a),(b),strlen(b)) == 0) - -/* This version adds one byte (for nul term) */ -static void *grab_file(void *ctx, const char *filename) -{ - unsigned int max = 16384, size = 0; - int ret, fd; - char *buffer; - - if (streq(filename, "-")) - fd = dup(STDIN_FILENO); - else - fd = open(filename, O_RDONLY, 0); - - if (fd < 0) - return NULL; - - buffer = talloc_array(ctx, char, max+1); - while ((ret = read(fd, buffer + size, max - size)) > 0) { - size += ret; - if (size == max) - buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1); - } - if (ret < 0) { - talloc_free(buffer); - buffer = NULL; - } else - buffer[size] = '\0'; - close(fd); - return buffer; -} - -/* This is a dumb one which copies. We could mangle instead. */ -static char **split(const char *text) -{ - char **lines = NULL; - unsigned int max = 64, num = 0; - - lines = talloc_array(text, char *, max+1); - - while (*text != '\0') { - unsigned int len = strcspn(text, "\n"); - lines[num] = talloc_array(lines, char, len + 1); - memcpy(lines[num], text, len); - lines[num][len] = '\0'; - text += len + 1; - if (++num == max) - lines = talloc_realloc(text, lines, char *, max*=2 + 1); - } - lines[num] = NULL; - return lines; -} - -int main(int argc, char *argv[]) -{ - unsigned int i, j; - - for (i = 1; i < argc; i++) { - char *file; - char **lines; - bool printing = false, printed = false; - - file = grab_file(NULL, argv[i]); - if (!file) - err(1, "Reading file %s", argv[i]); - lines = split(file); - - for (j = 0; lines[j]; j++) { - if (streq(lines[j], "/**")) { - printing = true; - if (printed++) - puts("\n"); - } else if (streq(lines[j], " */")) - printing = false; - else if (printing) { - if (strstarts(lines[j], " * ")) - puts(lines[j] + 3); - else if (strstarts(lines[j], " *")) - puts(lines[j] + 2); - else - errx(1, "Malformed line %s:%u", - argv[i], j); - } - } - talloc_free(file); - } - return 0; -} - - - diff --git a/ccan_tools/namespacize.c b/ccan_tools/namespacize.c deleted file mode 100644 index e4a3973f..00000000 --- a/ccan_tools/namespacize.c +++ /dev/null @@ -1,691 +0,0 @@ -/* Code to move a ccan module into the ccan_ namespace. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "talloc/talloc.h" - -#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. -Iccan_tools/libtap/src/" - -#define IDENT_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "abcdefghijklmnopqrstuvwxyz" \ - "01234567889_" - -static bool verbose = false; -static int indent = 0; -#define verbose(args...) \ - do { if (verbose) { \ - unsigned int _i; \ - for (_i = 0; _i < indent; _i++) printf(" "); \ - printf(args); \ - } \ - } while(0) -#define verbose_indent() (indent += 2) -#define verbose_unindent() (indent -= 2) - -#define streq(a,b) (strcmp((a),(b)) == 0) - -#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) - -static inline bool strends(const char *str, const char *postfix) -{ - if (strlen(str) < strlen(postfix)) - return false; - - return streq(str + strlen(str) - strlen(postfix), postfix); -} - -static int close_no_errno(int fd) -{ - int ret = 0, serrno = errno; - if (close(fd) < 0) - ret = errno; - errno = serrno; - return ret; -} - -static int unlink_no_errno(const char *filename) -{ - int ret = 0, serrno = errno; - if (unlink(filename) < 0) - ret = errno; - errno = serrno; - return ret; -} - -static void *grab_fd(const void *ctx, int fd) -{ - int ret; - unsigned int max = 16384, size = 0; - char *buffer; - - buffer = talloc_array(ctx, char, max+1); - while ((ret = read(fd, buffer + size, max - size)) > 0) { - size += ret; - if (size == max) - buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1); - } - if (ret < 0) { - talloc_free(buffer); - buffer = NULL; - } else - buffer[size] = '\0'; - - return buffer; -} - -/* This version adds one byte (for nul term) */ -static void *grab_file(const void *ctx, const char *filename) -{ - int fd; - char *buffer; - - if (streq(filename, "-")) - fd = dup(STDIN_FILENO); - else - fd = open(filename, O_RDONLY, 0); - - if (fd < 0) - return NULL; - - buffer = grab_fd(ctx, fd); - close_no_errno(fd); - return buffer; -} - -/* This is a dumb one which copies. We could mangle instead. */ -static char **split(const void *ctx, const char *text, const char *delims, - unsigned int *nump) -{ - char **lines = NULL; - unsigned int max = 64, num = 0; - - lines = talloc_array(ctx, char *, max+1); - - while (*text != '\0') { - unsigned int len = strcspn(text, delims); - lines[num] = talloc_array(lines, char, len + 1); - memcpy(lines[num], text, len); - lines[num][len] = '\0'; - text += len; - text += strspn(text, delims); - if (++num == max) - lines = talloc_realloc(ctx, lines, char *, max*=2 + 1); - } - lines[num] = NULL; - if (nump) - *nump = num; - return lines; -} - -static char **get_dir(const char *dir) -{ - DIR *d; - struct dirent *ent; - char **names = NULL; - unsigned int size = 0; - - d = opendir(dir); - if (!d) - return NULL; - - while ((ent = readdir(d)) != NULL) { - names = talloc_realloc(dir, names, char *, size + 2); - names[size++] - = talloc_asprintf(names, "%s/%s", dir, ent->d_name); - } - names[size++] = NULL; - closedir(d); - return names; -} - -static char ** __attribute__((format(printf, 2, 3))) -lines_from_cmd(const void *ctx, char *format, ...) -{ - va_list ap; - char *cmd, *buffer; - FILE *p; - - va_start(ap, format); - cmd = talloc_vasprintf(ctx, format, ap); - va_end(ap); - - p = popen(cmd, "r"); - if (!p) - err(1, "Executing '%s'", cmd); - - buffer = grab_fd(ctx, fileno(p)); - if (!buffer) - err(1, "Reading from '%s'", cmd); - pclose(p); - - return split(ctx, buffer, "\n", NULL); -} - -struct replace -{ - struct replace *next; - char *string; -}; - -static void __attribute__((noreturn)) usage(void) -{ - errx(1, "Usage:\n" - "namespacize [--verbose] \n" - "namespacize [--verbose] --adjust ...\n" - "The first form converts dir/ to insert 'ccan_' prefixes, and\n" - "then adjusts any other ccan directories at the same level which\n" - "are effected.\n" - "--adjust does an adjustment for each directory, in case a\n" - "dependency has been namespacized\n"); -} - -static void add_replace(struct replace **repl, const char *str) -{ - struct replace *new, *i; - - /* Avoid duplicates. */ - for (i = *repl; i; i = i->next) - if (streq(i->string, str)) - return; - - new = talloc(*repl, struct replace); - new->next = *repl; - new->string = talloc_strdup(new, str); - *repl = new; -} - -static void add_replace_tok(struct replace **repl, const char *s) -{ - struct replace *new; - unsigned int len = strspn(s, IDENT_CHARS); - - new = talloc(*repl, struct replace); - new->next = *repl; - new->string = talloc_strndup(new, s, len); - *repl = new; -} - -static char *basename(const void *ctx, const char *dir) -{ - char *p = strrchr(dir, '/'); - - if (!p) - return (char *)dir; - return talloc_strdup(ctx, p+1); -} - -static void look_for_macros(char *contents, struct replace **repl) -{ - char *p; - enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART; - - /* Look for lines of form #define X */ - for (p = contents; *p; p++) { - if (*p == '\n') - state = LINESTART; - else if (!isspace(*p)) { - if (state == LINESTART && *p == '#') - state = HASH; - else if (state==HASH && !strncmp(p, "define", 6)) { - state = DEFINE; - p += 5; - } else if (state == DEFINE) { - unsigned int len; - - len = strspn(p, IDENT_CHARS); - if (len) { - char *s; - s = talloc_strndup(contents, p, len); - /* Don't wrap idempotent wrappers */ - if (!strstarts(s, "CCAN_")) { - verbose("Found %s\n", s); - add_replace(repl, s); - } - } - state = NONE; - } else - state = NONE; - } - } -} - -/* Blank out preprocessor lines, and eliminate \ */ -static void preprocess(char *p) -{ - char *s; - - /* We assume backslashes are only used for macros. */ - while ((s = strstr(p, "\\\n")) != NULL) - s[0] = s[1] = ' '; - - /* Now eliminate # lines. */ - if (p[0] == '#') { - unsigned int i; - for (i = 0; p[i] != '\n'; i++) - p[i] = ' '; - } - while ((s = strstr(p, "\n#")) != NULL) { - unsigned int i; - for (i = 1; s[i] != '\n'; i++) - s[i] = ' '; - } -} - -static char *get_statement(const void *ctx, char **p) -{ - unsigned brackets = 0; - bool seen_brackets = false; - char *answer = talloc_strdup(ctx, ""); - - for (;;) { - if ((*p)[0] == '/' && (*p)[1] == '/') - *p += strcspn(*p, "\n"); - else if ((*p)[0] == '/' && (*p)[1] == '*') - *p = strstr(*p, "*/") + 1; - else { - char c = **p; - if (c == ';' && !brackets) { - (*p)++; - return answer; - } - /* Compress whitespace into a single ' ' */ - if (isspace(c)) { - c = ' '; - while (isspace((*p)[1])) - (*p)++; - } else if (c == '{' || c == '(' || c == '[') { - if (c == '(') - seen_brackets = true; - brackets++; - } else if (c == '}' || c == ')' || c == ']') - brackets--; - - if (answer[0] != '\0' || c != ' ') { - answer = talloc_realloc(NULL, answer, char, - strlen(answer) + 2); - answer[strlen(answer)+1] = '\0'; - answer[strlen(answer)] = c; - } - if (c == '}' && seen_brackets && brackets == 0) { - (*p)++; - return answer; - } - } - (*p)++; - if (**p == '\0') - return NULL; - } -} - -/* This hack should handle well-formatted code. */ -static void look_for_definitions(char *contents, struct replace **repl) -{ - char *stmt, *p = contents; - - preprocess(contents); - - while ((stmt = get_statement(contents, &p)) != NULL) { - int i, len; - - /* Definition of struct/union? */ - if ((strncmp(stmt, "struct", 5) == 0 - || strncmp(stmt, "union", 5) == 0) - && strchr(stmt, '{') && stmt[7] != '{') - add_replace_tok(repl, stmt+7); - - /* Definition of var or typedef? */ - for (i = strlen(stmt)-1; i >= 0; i--) - if (strspn(stmt+i, IDENT_CHARS) == 0) - break; - - if (i != strlen(stmt)-1) { - add_replace_tok(repl, stmt+i+1); - continue; - } - - /* function or array declaration? */ - len = strspn(stmt, IDENT_CHARS "* "); - if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) { - if (strspn(stmt + len + 1, IDENT_CHARS) != 0) { - for (i = len-1; i >= 0; i--) - if (strspn(stmt+i, IDENT_CHARS) == 0) - break; - if (i != len-1) { - add_replace_tok(repl, stmt+i+1); - continue; - } - } else { - /* Pointer to function? */ - len++; - len += strspn(stmt + len, " *"); - i = strspn(stmt + len, IDENT_CHARS); - if (i > 0 && stmt[len + i] == ')') - add_replace_tok(repl, stmt+len); - } - } - } -} - -/* FIXME: Only does main header, should chase local includes. */ -static void analyze_headers(const char *dir, struct replace **repl) -{ - char *hdr, *contents; - - /* Get hold of header, assume that's it. */ - hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir)); - contents = grab_file(dir, hdr); - if (!contents) - err(1, "Reading %s", hdr); - - verbose("Looking in %s for macros\n", hdr); - verbose_indent(); - look_for_macros(contents, repl); - verbose_unindent(); - - verbose("Looking in %s for symbols\n", hdr); - verbose_indent(); - look_for_definitions(contents, repl); - verbose_unindent(); -} - -static void write_replacement_file(const char *dir, struct replace **repl) -{ - char *replname = talloc_asprintf(dir, "%s/.namespacize", dir); - int fd; - struct replace *r; - - fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644); - if (fd < 0) { - if (errno == EEXIST) - errx(1, "%s already exists: can't namespacize twice", - replname); - err(1, "Opening %s", replname); - } - - for (r = *repl; r; r = r->next) { - if (write(fd,r->string,strlen(r->string)) != strlen(r->string) - || write(fd, "\n", 1) != 1) { - unlink_no_errno(replname); - if (errno == 0) - errx(1, "Short write to %s: disk full?", - replname); - errx(1, "Writing to %s", replname); - } - } - - close(fd); -} - -static int unlink_destroy(char *name) -{ - unlink(name); - return 0; -} - -static char *find_word(char *f, const char *str) -{ - char *p = f; - - while ((p = strstr(p, str)) != NULL) { - /* Check it's not in the middle of a word. */ - if (p > f && (isalnum(p[-1]) || p[-1] == '_')) { - p++; - continue; - } - if (isalnum(p[strlen(str)]) || p[strlen(str)] == '_') { - p++; - continue; - } - return p; - } - return NULL; -} - -/* This is horribly inefficient but simple. */ -static const char *rewrite_file(const char *filename, - const struct replace *repl) -{ - char *newname, *file; - int fd; - - verbose("Rewriting %s\n", filename); - file = grab_file(filename, filename); - if (!file) - err(1, "Reading file %s", filename); - - for (; repl; repl = repl->next) { - char *p; - - while ((p = find_word(file, repl->string)) != NULL) { - unsigned int off; - char *new = talloc_array(file, char, strlen(file)+6); - - off = p - file; - memcpy(new, file, off); - if (isupper(repl->string[0])) - memcpy(new + off, "CCAN_", 5); - else - memcpy(new + off, "ccan_", 5); - strcpy(new + off + 5, file + off); - file = new; - } - } - - /* If we exit for some reason, we want this erased. */ - newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp", - filename); - fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644); - if (fd < 0) - err(1, "Creating %s", newname); - - talloc_set_destructor(newname, unlink_destroy); - if (write(fd, file, strlen(file)) != strlen(file)) { - if (errno == 0) - errx(1, "Short write to %s: disk full?", newname); - errx(1, "Writing to %s", newname); - } - close(fd); - return newname; -} - -struct adjusted -{ - struct adjusted *next; - const char *file; - const char *tmpfile; -}; - -static void setup_adjust_files(const char *dir, - const struct replace *repl, - struct adjusted **adj) -{ - char **files; - - for (files = get_dir(dir); *files; files++) { - if (strends(*files, "/test")) - setup_adjust_files(*files, repl, adj); - else if (strends(*files, ".c") || strends(*files, ".h")) { - struct adjusted *a = talloc(dir, struct adjusted); - a->next = *adj; - a->file = *files; - a->tmpfile = rewrite_file(a->file, repl); - *adj = a; - } - } -} - -/* This is the "commit" stage, so we hope it won't fail. */ -static void rename_files(const struct adjusted *adj) -{ - while (adj) { - if (rename(adj->tmpfile, adj->file) != 0) - warn("Could not rename over '%s', we're in trouble", - adj->file); - adj = adj->next; - } -} - -static void convert_dir(const char *dir) -{ - char *name; - struct replace *replace = NULL; - struct adjusted *adj = NULL; - - /* Remove any ugly trailing slashes. */ - name = talloc_strdup(NULL, dir); - while (strends(name, "/")) - name[strlen(name)-1] = '\0'; - - analyze_headers(name, &replace); - write_replacement_file(name, &replace); - setup_adjust_files(name, replace, &adj); - rename_files(adj); - talloc_free(name); - talloc_free(replace); -} - -static struct replace *read_replacement_file(const char *depdir) -{ - struct replace *repl = NULL; - char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir); - char *file, **line; - - file = grab_file(replname, replname); - if (!file) { - if (errno != ENOENT) - err(1, "Opening %s", replname); - return NULL; - } - - for (line = split(file, file, "\n", NULL); *line; line++) - add_replace(&repl, *line); - return repl; -} - -static char *build_info(const void *ctx, const char *dir) -{ - char *file, *cfile, *cmd; - - cfile = talloc_asprintf(ctx, "%s/%s", dir, "_info.c"); - file = talloc_asprintf(cfile, "%s/%s", dir, "_info"); - cmd = talloc_asprintf(file, "gcc " CFLAGS " -o %s %s", file, cfile); - if (system(cmd) != 0) - errx(1, "Failed to compile %s", file); - - return file; -} - -static char **get_deps(const void *ctx, const char *dir) -{ - char **deps, *cmd; - - cmd = talloc_asprintf(ctx, "%s depends", build_info(ctx, dir)); - deps = lines_from_cmd(cmd, cmd); - if (!deps) - err(1, "Could not run '%s'", cmd); - return deps; -} - -static char *parent_dir(const void *ctx, const char *dir) -{ - char *parent, *slash; - - parent = talloc_strdup(ctx, dir); - slash = strrchr(parent, '/'); - if (slash) - *slash = '\0'; - else - parent = talloc_strdup(ctx, "."); - return parent; -} - -static void adjust_dir(const char *dir) -{ - char *parent = parent_dir(NULL, dir); - char **deps; - - verbose("Adjusting %s\n", dir); - verbose_indent(); - for (deps = get_deps(parent, dir); *deps; deps++) { - char *depdir; - struct adjusted *adj = NULL; - struct replace *repl; - - depdir = talloc_asprintf(parent, "%s/%s", parent, *deps); - repl = read_replacement_file(depdir); - if (repl) { - verbose("%s has been namespacized\n", depdir); - setup_adjust_files(parent, repl, &adj); - rename_files(adj); - } else - verbose("%s has not been namespacized\n", depdir); - talloc_free(depdir); - } - verbose_unindent(); -} - -static void adjust_dependents(const char *dir) -{ - char *parent = parent_dir(NULL, dir); - char *base = basename(parent, dir); - char **file; - - verbose("Looking for dependents in %s\n", parent); - verbose_indent(); - for (file = get_dir(parent); *file; file++) { - char *infoc, **deps; - bool isdep = false; - - if (basename(*file, *file)[0] == '.') - continue; - - infoc = talloc_asprintf(*file, "%s/_info.c", *file); - if (access(infoc, R_OK) != 0) - continue; - - for (deps = get_deps(*file, *file); *deps; deps++) { - if (streq(*deps, base)) - isdep = true; - } - if (isdep) - adjust_dir(*file); - else - verbose("%s is not dependent\n", *file); - } - verbose_unindent(); -} - -int main(int argc, char *argv[]) -{ - if (argv[1] && streq(argv[1], "--verbose")) { - verbose = true; - argv++; - argc--; - } - - if (argc == 2) { - verbose("Namespacizing %s\n", argv[1]); - verbose_indent(); - convert_dir(argv[1]); - adjust_dependents(argv[1]); - verbose_unindent(); - return 0; - } - - if (argc > 2 && streq(argv[1], "--adjust")) { - unsigned int i; - - for (i = 2; i < argc; i++) - adjust_dir(argv[i]); - return 0; - } - usage(); -} diff --git a/ccan_tools/run_tests.c b/ccan_tools/run_tests.c deleted file mode 100644 index c7526449..00000000 --- a/ccan_tools/run_tests.c +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "tap/tap.h" -#include "talloc/talloc.h" -#include "../string/string.h" - -#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I." - -/* FIXME: Use build bug later. */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -static struct test *tests = NULL; -static struct obj *objs = NULL; -static int verbose; - -struct test_type -{ - const char *name; - void (*testfn)(struct test_type *t, const char *name); -}; - -struct test -{ - struct test *next; - struct test_type *type; - char *name; -}; - -struct obj -{ - struct obj *next; - char *name; -}; - -static char *output_name(const char *name) -{ - char *ret; - - assert(strends(name, ".c")); - - ret = talloc_strdup(name, name); - ret[strlen(ret) - 2] = '\0'; - return ret; -} - -static char *obj_list(void) -{ - char *list = talloc_strdup(objs, ""); - struct obj *i; - - for (i = objs; i; i = i->next) - list = talloc_asprintf_append(list, "%s ", i->name); - - /* FIXME */ - list = talloc_asprintf_append(list, "tap/tap.o"); - return list; -} - -static void compile_objs(void) -{ - struct obj *i; - - for (i = objs; i; i = i->next) { - char *cmd = talloc_asprintf(i, "gcc " CFLAGS " -o %s.o -c %s%s", - output_name(i->name), i->name, - verbose ? "" : "> /dev/null 2>&1"); - ok(system(cmd) == 0, "%s", cmd); - } -} - -static void cleanup_objs(void) -{ - struct obj *i; - - for (i = objs; i; i = i->next) - unlink(talloc_asprintf(i, "%s.o", output_name(i->name))); -} - -static void add_test(const char *testdir, const char *name, struct test_type *t) -{ - struct test *test = talloc(testdir, struct test); - - test->next = tests; - test->type = t; - test->name = talloc_asprintf(test, "%s/%s", testdir, name); - tests = test; -} - -static void add_obj(const char *testdir, const char *name) -{ - struct obj *obj = talloc(testdir, struct obj); - - obj->next = objs; - obj->name = talloc_asprintf(obj, "%s/%s", testdir, name); - objs = obj; -} - -static int build(const char *name, int fail) -{ - const char *cmd; - int ret; - - cmd = talloc_asprintf(name, "gcc " CFLAGS " %s -o %s %s %s%s", - fail ? "-DFAIL" : "", - output_name(name), name, obj_list(), - verbose ? "" : "> /dev/null 2>&1"); - - if (verbose) - fprintf(stderr, "Running %s\n", cmd); - - ret = system(cmd); - if (ret == -1) - diag("cmd '%s' failed to execute", cmd); - - return ret; -} - -static void compile_ok(struct test_type *t, const char *name) -{ - ok(build(name, 0) == 0, "%s %s", t->name, name); -} - -static void compile_fail(struct test_type *t, const char *name) -{ - if (build(name, 0) != 0) - fail("non-FAIL build %s", name); - else - ok(build(name, 1) > 0, "%s %s", t->name, name); -} - -static void run(const char *name) -{ - if (system(output_name(name)) == -1) - fail("running %s had error %m", name); -} - -static void cleanup(const char *name) -{ - unlink(output_name(name)); -} - -static struct test_type test_types[] = { - { "compile_ok", compile_ok }, - { "compile_fail", compile_fail }, - { "run", compile_ok }, -}; - -int main(int argc, char *argv[]) -{ - DIR *dir; - struct dirent *d; - char *testdir; - struct test *test; - unsigned int num_tests = 0, num_objs = 0; - - if (argc > 1 && streq(argv[1], "--verbose")) { - verbose = 1; - argc--; - argv++; - } - - if (argc != 2) - errx(1, "Usage: run_tests [--verbose] "); - - testdir = talloc_asprintf(NULL, "%s/test", argv[1]); - dir = opendir(testdir); - if (!dir) - err(1, "Opening '%s'", testdir); - - while ((d = readdir(dir)) != NULL) { - unsigned int i; - if (d->d_name[0] == '.' || !strends(d->d_name, ".c")) - continue; - - for (i = 0; i < ARRAY_SIZE(test_types); i++) { - if (strstarts(d->d_name, test_types[i].name)) { - add_test(testdir, d->d_name, &test_types[i]); - num_tests++; - break; - } - } - if (i == ARRAY_SIZE(test_types)) { - add_obj(testdir, d->d_name); - num_objs++; - } - } - - plan_tests(num_tests + num_objs); - /* First all the extra object compilations. */ - compile_objs(); - - /* Do all the test compilations. */ - for (test = tests; test; test = test->next) - test->type->testfn(test->type, test->name); - - cleanup_objs(); - - /* Now run all the ones which wanted to run. */ - for (test = tests; test; test = test->next) { - if (streq(test->type->name, "run")) - run(test->name); - cleanup(test->name); - } - - exit(exit_status()); -} diff --git a/ccan_tools/test_all.sh b/ccan_tools/test_all.sh deleted file mode 100755 index 3c65c21c..00000000 --- a/ccan_tools/test_all.sh +++ /dev/null @@ -1,22 +0,0 @@ -#! /bin/sh - -# First, test normal config. -if ! make -s; then - echo Normal config failed. - exit 1 -fi - -# Now, remove one HAVE_ at a time. -cp config.h original-config.h -trap "mv original-config.h config.h && rm -f .newconfig" EXIT - -while grep -q '1$' config.h; do - tr '\012' @ < config.h | sed 's/1@/0@/' | tr @ '\012' > .newconfig - diff -u config.h .newconfig - mv .newconfig config.h - if ! make -s; then - echo Failed config: - cat config.h - exit 1 - fi -done diff --git a/check_type/_info.c b/check_type/_info.c deleted file mode 100644 index 06e90eb4..00000000 --- a/check_type/_info.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include "config.h" - -/** - * check_type - routines for compile time type checking - * - * C has fairly weak typing: ints get automatically converted to longs, signed - * to unsigned, etc. There are some cases where this is best avoided, and - * these macros provide methods for evoking warnings (or build errors) when - * a precise type isn't used. - * - * On compilers which don't support typeof() these routines are less effective, - * since they have to use sizeof() which can only distiguish between types of - * different size. - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { -#if !HAVE_TYPEOF - printf("build_assert\n"); -#endif - return 0; - } - - return 1; -} diff --git a/check_type/check_type.h b/check_type/check_type.h deleted file mode 100644 index e05236f8..00000000 --- a/check_type/check_type.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef CCAN_CHECK_TYPE_H -#define CCAN_CHECK_TYPE_H -#include "config.h" - -/** - * check_type - issue a warning or build failure if type is not correct. - * @expr: the expression whose type we should check (not evaluated). - * @type: the exact type we expect the expression to be. - * - * This macro is usually used within other macros to try to ensure that a macro - * argument is of the expected type. No type promotion of the expression is - * done: an unsigned int is not the same as an int! - * - * check_type() always evaluates to 1. - * - * If your compiler does not support typeof, then the best we can do is fail - * to compile if the sizes of the types are unequal (a less complete check). - * - * Example: - * // They should always pass a 64-bit value to _set_some_value! - * #define set_some_value(expr) \ - * _set_some_value((check_type((expr), uint64_t), (expr))) - */ - -/** - * check_types_match - issue a warning or build failure if types are not same. - * @expr1: the first expression (not evaluated). - * @expr2: the second expression (not evaluated). - * - * This macro is usually used within other macros to try to ensure that - * arguments are of identical types. No type promotion of the expressions is - * done: an unsigned int is not the same as an int! - * - * check_types_match() always evaluates to 1. - * - * If your compiler does not support typeof, then the best we can do is fail - * to compile if the sizes of the types are unequal (a less complete check). - * - * Example: - * // Do subtraction to get to enclosing type, but make sure that - * // pointer is of correct type for that member. - * #define container_of(mbr_ptr, encl_type, mbr) \ - * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ - * ((encl_type *) \ - * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) - */ -#if HAVE_TYPEOF -#define check_type(expr, type) \ - ((typeof(expr) *)0 != (type *)0) - -#define check_types_match(expr1, expr2) \ - ((typeof(expr1) *)0 != (typeof(expr2) *)0) -#else -#include "build_assert/build_assert.h" -/* Without typeof, we can only test the sizes. */ -#define check_type(expr, type) \ - EXPR_BUILD_ASSERT(sizeof(expr) == sizeof(type)) - -#define check_types_match(expr1, expr2) \ - EXPR_BUILD_ASSERT(sizeof(expr1) == sizeof(expr2)) -#endif /* HAVE_TYPEOF */ - -#endif /* CCAN_CHECK_TYPE_H */ diff --git a/check_type/test/compile_fail-check_type.c b/check_type/test/compile_fail-check_type.c deleted file mode 100644 index d19fe86f..00000000 --- a/check_type/test/compile_fail-check_type.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "check_type/check_type.h" - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - check_type(argc, char); -#endif - return 0; -} diff --git a/check_type/test/compile_fail-check_type_unsigned.c b/check_type/test/compile_fail-check_type_unsigned.c deleted file mode 100644 index 6b18acb5..00000000 --- a/check_type/test/compile_fail-check_type_unsigned.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "check_type/check_type.h" - -int main(int argc, char *argv[]) -{ -#ifdef FAIL -#if HAVE_TYPEOF - check_type(argc, unsigned int); -#else - /* This doesn't work without typeof, so just fail */ -#error "Fail without typeof" -#endif -#endif - return 0; -} diff --git a/check_type/test/compile_fail-check_types_match.c b/check_type/test/compile_fail-check_types_match.c deleted file mode 100644 index bc1f9c31..00000000 --- a/check_type/test/compile_fail-check_types_match.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "check_type/check_type.h" - -int main(int argc, char *argv[]) -{ - unsigned char x = argc; -#ifdef FAIL - check_types_match(argc, x); -#endif - return x; -} diff --git a/check_type/test/run.c b/check_type/test/run.c deleted file mode 100644 index f4b33c16..00000000 --- a/check_type/test/run.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "check_type/check_type.h" -#include "tap/tap.h" - -int main(int argc, char *argv[]) -{ - int x = 0, y = 0; - - plan_tests(9); - - ok1(check_type(argc, int) == 0); - ok1(check_type(&argc, int *) == 0); - ok1(check_types_match(argc, argc) == 0); - ok1(check_types_match(argc, x) == 0); - ok1(check_types_match(&argc, &x) == 0); - - ok1(check_type(x++, int) == 0); - ok(x == 0, "check_type does not evaluate expression"); - ok1(check_types_match(x++, y++) == 0); - ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); - - return exit_status(); -} diff --git a/container_of/_info.c b/container_of/_info.c deleted file mode 100644 index 7705e38c..00000000 --- a/container_of/_info.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include "config.h" - -/** - * container_of - routine for upcasting - * - * It is often convenient to create code where the caller registers a pointer - * to a generic structure and a callback. The callback might know that the - * pointer points to within a larger structure, and container_of gives a - * convenient and fairly type-safe way of returning to the enclosing structure. - * - * This idiom is an alternative to providing a void * pointer for every - * callback. - * - * Example: - * struct info - * { - * int my_stuff; - * struct timer timer; - * }; - * - * static void my_timer_callback(struct timer *timer) - * { - * struct info *info = container_of(timer, struct info, timer); - * printf("my_stuff is %u\n", info->my_stuff); - * } - * - * int main() - * { - * struct info info = { .my_stuff = 1 }; - * - * register_timer(&info.timer); - * ... - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { - printf("check_type\n"); - return 0; - } - - return 1; -} diff --git a/container_of/container_of.h b/container_of/container_of.h deleted file mode 100644 index 1f4b18e4..00000000 --- a/container_of/container_of.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef CCAN_CONTAINER_OF_H -#define CCAN_CONTAINER_OF_H -#include - -#include "config.h" -#include "check_type/check_type.h" - -/** - * container_of - get pointer to enclosing structure - * @member_ptr: pointer to the structure member - * @containing_type: the type this member is within - * @member: the name of this member within the structure. - * - * Given a pointer to a member of a structure, this macro does pointer - * subtraction to return the pointer to the enclosing type. - * - * Example: - * struct info - * { - * int some_other_field; - * struct foo my_foo; - * }; - * - * struct info *foo_to_info(struct foo *foop) - * { - * return container_of(foo, struct info, my_foo); - * } - */ -#define container_of(member_ptr, containing_type, member) \ - ((containing_type *) \ - ((char *)(member_ptr) - offsetof(containing_type, member)) \ - - check_types_match(*(member_ptr), ((containing_type *)0)->member)) - - -/** - * container_of_var - get pointer to enclosing structure using a variable - * @member_ptr: pointer to the structure member - * @var: a pointer to a structure of same type as this member is within - * @member: the name of this member within the structure. - * - * Given a pointer to a member of a structure, this macro does pointer - * subtraction to return the pointer to the enclosing type. - * - * Example: - * struct info - * { - * int some_other_field; - * struct foo my_foo; - * }; - * - * struct info *foo_to_info(struct foo *foop) - * { - * struct info *i = container_of_var(foo, i, my_foo); - * return i; - * } - */ -#ifdef HAVE_TYPEOF -#define container_of_var(member_ptr, var, member) \ - container_of(member_ptr, typeof(*var), member) -#else -#define container_of_var(member_ptr, var, member) \ - ((void *)((char *)(member_ptr) - offsetof(containing_type, member))) -#endif - -#endif /* CCAN_CONTAINER_OF_H */ diff --git a/container_of/test/compile_fail-bad-type.c b/container_of/test/compile_fail-bad-type.c deleted file mode 100644 index 01dfd454..00000000 --- a/container_of/test/compile_fail-bad-type.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "container_of/container_of.h" -#include - -struct foo { - int a; - char b; -}; - -int main(int argc, char *argv[]) -{ - struct foo foo = { .a = 1, .b = 2 }; - int *intp = &foo.a; - char *p; - -#ifdef FAIL - /* p is a char *, but this gives a struct foo * */ - p = container_of(intp, struct foo, a); -#else - p = (char *)intp; -#endif - return p == NULL; -} diff --git a/container_of/test/compile_fail-types.c b/container_of/test/compile_fail-types.c deleted file mode 100644 index 69f02daf..00000000 --- a/container_of/test/compile_fail-types.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "container_of/container_of.h" -#include - -struct foo { - int a; - char b; -}; - -int main(int argc, char *argv[]) -{ - struct foo foo = { .a = 1, .b = 2 }, *foop; - int *intp = &foo.a; - -#ifdef FAIL - /* b is a char, but intp is an int * */ - foop = container_of(intp, struct foo, b); -#else - foop = NULL; -#endif - return intp == NULL; -} diff --git a/container_of/test/compile_fail-var-types.c b/container_of/test/compile_fail-var-types.c deleted file mode 100644 index 5c776798..00000000 --- a/container_of/test/compile_fail-var-types.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "container_of/container_of.h" -#include - -struct foo { - int a; - char b; -}; - -int main(int argc, char *argv[]) -{ - struct foo foo = { .a = 1, .b = 2 }, *foop; - int *intp = &foo.a; - -#ifdef FAIL - /* b is a char, but intp is an int * */ - foop = container_of_var(intp, foop, b); -#else - foop = NULL; -#endif - return intp == NULL; -} diff --git a/container_of/test/run.c b/container_of/test/run.c deleted file mode 100644 index dd57204d..00000000 --- a/container_of/test/run.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "container_of/container_of.h" -#include "tap/tap.h" - -struct foo { - int a; - char b; -}; - -int main(int argc, char *argv[]) -{ - struct foo foo = { .a = 1, .b = 2 }; - int *intp = &foo.a; - char *charp = &foo.b; - - plan_tests(4); - ok1(container_of(intp, struct foo, a) == &foo); - ok1(container_of(charp, struct foo, b) == &foo); - ok1(container_of_var(intp, &foo, a) == &foo); - ok1(container_of_var(charp, &foo, b) == &foo); - return exit_status(); -} diff --git a/list/_info.c b/list/_info.c deleted file mode 100644 index 0402b490..00000000 --- a/list/_info.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include "config.h" - -/** - * list - double linked list routines - * - * The list header contains routines for manipulating double linked lists. - * It defines two types: struct list_head used for anchoring lists, and - * struct list_node which is usually embedded in the structure which is placed - * in the list. - * - * Example: - * #include - * #include "list/list.h" - * - * struct parent { - * const char *name; - * struct list_head children; - * unsigned int num_children; - * }; - * - * struct child { - * const char *name; - * struct list_node list; - * }; - * - * int main(int argc, char *argv[]) - * { - * struct parent p; - * struct child *c; - * unsigned int i; - * - * if (argc < 2) - * errx(1, "Usage: %s parent children...", argv[0]); - * - * p.name = argv[1]; - * for (i = 2; i < argc, i++) { - * c = malloc(sizeof(*c)); - * c->name = argv[i]; - * list_add(&p.children, &c->list); - * p.num_children++; - * } - * - * printf("%s has %u children:", p.name, p.num_children); - * list_for_each(&p.children, c, list) - * printf("%s ", c->name); - * printf("\n"); - * return 0; - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { - printf("container_of\n"); - return 0; - } - - return 1; -} diff --git a/list/list.c b/list/list.c deleted file mode 100644 index b72f8f67..00000000 --- a/list/list.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include "list/list.h" - -struct list_head *list_check(struct list_head *h, const char *abortstr) -{ - struct list_node *n, *p; - int count = 0; - - if (h->n.next == &h->n) { - if (h->n.prev != &h->n) { - if (!abortstr) - return NULL; - fprintf(stderr, "%s: prev corrupt in empty %p\n", - abortstr, h); - abort(); - } - return h; - } - - for (p = &h->n, n = h->n.next; n != &h->n; p = n, n = n->next) { - count++; - if (n->prev != p) { - if (!abortstr) - return NULL; - fprintf(stderr, - "%s: prev corrupt in node %p (%u) of %p\n", - abortstr, n, count, h); - abort(); - } - } - return h; -} diff --git a/list/list.h b/list/list.h deleted file mode 100644 index c664d834..00000000 --- a/list/list.h +++ /dev/null @@ -1,253 +0,0 @@ -#ifndef CCAN_LIST_H -#define CCAN_LIST_H -#include -#include "container_of/container_of.h" - -/** - * struct list_node - an entry in a doubly-linked list - * @next: next entry (self if empty) - * @prev: previous entry (self if empty) - * - * This is used as an entry in a linked list. - * Example: - * struct child { - * const char *name; - * // Linked list of all us children. - * struct list_node list; - * }; - */ -struct list_node -{ - struct list_node *next, *prev; -}; - -/** - * struct list_head - the head of a doubly-linked list - * @h: the list_head (containing next and prev pointers) - * - * This is used as the head of a linked list. - * Example: - * struct parent { - * const char *name; - * struct list_head children; - * unsigned int num_children; - * }; - */ -struct list_head -{ - struct list_node n; -}; - -/** - * list_check - check a list for consistency - * @h: the list_head - * @abortstr: the location to print on aborting, or NULL. - * - * Because list_nodes have redundant information, consistency checking between - * the back and forward links can be done. This is useful as a debugging check. - * If @abortstr is non-NULL, that will be printed in a diagnostic if the list - * is inconsistent, and the function will abort. - * - * Returns the list head if the list is consistent, NULL if not (it - * can never return NULL if @abortstr is set). - * - * Example: - * static void dump_parent(struct parent *p) - * { - * struct child *c; - * - * printf("%s (%u children):\n", p->name, parent->num_children); - * list_check(&p->children, "bad child list"); - * list_for_each(&p->children, c, list) - * printf(" -> %s\n", c->name); - * } - */ -struct list_head *list_check(struct list_head *h, const char *abortstr); - -#ifdef CCAN_LIST_DEBUG -#define debug_list(h) list_check((h), __func__) -#else -#define debug_list(h) (h) -#endif - -/** - * list_head_init - initialize a list_head - * @h: the list_head to set to the empty list - * - * Example: - * list_head_init(&parent->children); - * parent->num_children = 0; - */ -static inline void list_head_init(struct list_head *h) -{ - h->n.next = h->n.prev = &h->n; -} - -/** - * LIST_HEAD - define and initalized empty list_head - * @name: the name of the list. - * - * The LIST_HEAD macro defines a list_head and initializes it to an empty - * list. It can be prepended by "static" to define a static list_head. - * - * Example: - * // Header: - * extern struct list_head my_list; - * - * // C file: - * LIST_HEAD(my_list); - */ -#define LIST_HEAD(name) \ - struct list_head name = { { &name.n, &name.n } } - -/** - * list_add - add an entry at the start of a linked list. - * @h: the list_head to add the node to - * @n: the list_node to add to the list. - * - * The list_node does not need to be initialized; it will be overwritten. - * Example: - * list_add(&parent->children, &child->list); - * parent->num_children++; - */ -static inline void list_add(struct list_head *h, struct list_node *n) -{ - n->next = h->n.next; - n->prev = &h->n; - h->n.next->prev = n; - h->n.next = n; - (void)debug_list(h); -} - -/** - * list_add_tail - add an entry at the end of a linked list. - * @h: the list_head to add the node to - * @n: the list_node to add to the list. - * - * The list_node does not need to be initialized; it will be overwritten. - * Example: - * list_add_tail(&parent->children, &child->list); - * parent->num_children++; - */ -static inline void list_add_tail(struct list_head *h, struct list_node *n) -{ - n->next = &h->n; - n->prev = h->n.prev; - h->n.prev->next = n; - h->n.prev = n; - (void)debug_list(h); -} - -/** - * list_del - delete an entry from a linked list. - * @n: the list_node to delete from the list. - * - * Example: - * list_del(&child->list); - * parent->num_children--; - */ -static inline void list_del(struct list_node *n) -{ - n->next->prev = n->prev; - n->prev->next = n->next; - (void)debug_list(n->next); -#ifdef CCAN_LIST_DEBUG - /* Catch use-after-del. */ - n->next = n->prev = NULL; -#endif -} - -/** - * list_empty - is a list empty? - * @h: the list_head - * - * If the list is empty, returns true. - * - * Example: - * assert(list_empty(&parent->children) == (parent->num_children == 0)); - */ -static inline bool list_empty(struct list_head *h) -{ - (void)debug_list(h); - return h->n.next == &h->n; -} - -/** - * list_entry - convert a list_node back into the structure containing it. - * @n: the list_node - * @type: the type of the entry - * @member: the list_node member of the type - * - * Example: - * struct child *c; - * // First list entry is children.next; convert back to child. - * c = list_entry(parent->children.next, struct child, list); - */ -#define list_entry(n, type, member) container_of(n, type, member) - -/** - * list_top - get the first entry in a list - * @h: the list_head - * @type: the type of the entry - * @member: the list_node member of the type - * - * If the list is empty, returns NULL. - * - * Example: - * struct child *first; - * first = list_top(&parent->children, struct child, list); - */ -#define list_top(h, type, member) \ - list_entry(_list_top(h), type, member) - -static inline struct list_node *_list_top(struct list_head *h) -{ - (void)debug_list(h); - if (list_empty(h)) - return NULL; - return h->n.next; -} - -/** - * list_for_each - iterate through a list. - * @h: the list_head - * @i: the structure containing the list_node - * @member: the list_node member of the structure - * - * This is a convenient wrapper to iterate @i over the entire list. It's - * a for loop, so you can break and continue as normal. - * - * Example: - * struct child *c; - * list_for_each(&parent->children, c, list) - * printf("Name: %s\n", c->name); - */ -#define list_for_each(h, i, member) \ - for (i = container_of_var(debug_list(h)->n.next, i, member); \ - &i->member != &(h)->n; \ - i = container_of_var(i->member.next, i, member)) - -/** - * list_for_each_safe - iterate through a list, maybe during deletion - * @h: the list_head - * @i: the structure containing the list_node - * @nxt: the structure containing the list_node - * @member: the list_node member of the structure - * - * This is a convenient wrapper to iterate @i over the entire list. It's - * a for loop, so you can break and continue as normal. The extra variable - * @nxt is used to hold the next element, so you can delete @i from the list. - * - * Example: - * struct child *c, *n; - * list_for_each_safe(&parent->children, c, n, list) { - * list_del(&c->list); - * parent->num_children--; - * } - */ -#define list_for_each_safe(h, i, nxt, member) \ - for (i = container_of_var(debug_list(h)->n.next, i, member), \ - nxt = container_of_var(i->member.next, i, member); \ - &i->member != &(h)->n; \ - i = nxt, nxt = container_of_var(i->member.next, i, member)) -#endif /* CCAN_LIST_H */ diff --git a/list/test/run.c b/list/test/run.c deleted file mode 100644 index 66b9630d..00000000 --- a/list/test/run.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "list/list.h" -#include "tap/tap.h" -#include "list/list.c" - -struct parent { - const char *name; - struct list_head children; - unsigned int num_children; -}; - -struct child { - const char *name; - struct list_node list; -}; - -static LIST_HEAD(static_list); - -int main(int argc, char *argv[]) -{ - struct parent parent; - struct child c1, c2, c3, *c, *n; - unsigned int i; - - plan_tests(41); - /* Test LIST_HEAD, list_empty and check_list */ - ok1(list_empty(&static_list)); - ok1(list_check(&static_list, NULL)); - - parent.num_children = 0; - list_head_init(&parent.children); - /* Test list_head_init */ - ok1(list_empty(&parent.children)); - ok1(list_check(&parent.children, NULL)); - - c2.name = "c2"; - list_add(&parent.children, &c2.list); - /* Test list_add and !list_empty. */ - ok1(!list_empty(&parent.children)); - ok1(c2.list.next == &parent.children.n); - ok1(c2.list.prev == &parent.children.n); - ok1(parent.children.n.next == &c2.list); - ok1(parent.children.n.prev == &c2.list); - /* Test list_check */ - ok1(list_check(&parent.children, NULL)); - - c1.name = "c1"; - list_add(&parent.children, &c1.list); - /* Test list_add and !list_empty. */ - ok1(!list_empty(&parent.children)); - ok1(c2.list.next == &parent.children.n); - ok1(c2.list.prev == &c1.list); - ok1(parent.children.n.next == &c1.list); - ok1(parent.children.n.prev == &c2.list); - ok1(c1.list.next == &c2.list); - ok1(c1.list.prev == &parent.children.n); - /* Test list_check */ - ok1(list_check(&parent.children, NULL)); - - c3.name = "c3"; - list_add_tail(&parent.children, &c3.list); - /* Test list_add_tail and !list_empty. */ - ok1(!list_empty(&parent.children)); - ok1(parent.children.n.next == &c1.list); - ok1(parent.children.n.prev == &c3.list); - ok1(c1.list.next == &c2.list); - ok1(c1.list.prev == &parent.children.n); - ok1(c2.list.next == &c3.list); - ok1(c2.list.prev == &c1.list); - ok1(c3.list.next == &parent.children.n); - ok1(c3.list.prev == &c2.list); - /* Test list_check */ - ok1(list_check(&parent.children, NULL)); - - /* Test list_top */ - ok1(list_top(&parent.children, struct child, list) == &c1); - - /* Test list_for_each. */ - i = 0; - list_for_each(&parent.children, c, list) { - switch (i++) { - case 0: - ok1(c == &c1); - break; - case 1: - ok1(c == &c2); - break; - case 2: - ok1(c == &c3); - break; - } - if (i > 2) - break; - } - ok1(i == 3); - - /* Test list_for_each_safe and list_del. */ - i = 0; - list_for_each_safe(&parent.children, c, n, list) { - switch (i++) { - case 0: - ok1(c == &c1); - break; - case 1: - ok1(c == &c2); - break; - case 2: - ok1(c == &c3); - break; - } - list_del(&c->list); - ok1(list_check(&parent.children, NULL)); - if (i > 2) - break; - } - ok1(i == 3); - ok1(list_empty(&parent.children)); - return exit_status(); -} diff --git a/noerr/_info.c b/noerr/_info.c deleted file mode 100644 index 96fbbcd1..00000000 --- a/noerr/_info.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include "config.h" - -/** - * noerr - routines for cleaning up without blatting errno - * - * It is a good idea to follow the standard C convention of setting errno in - * your own helper functions. Unfortunately, care must be taken in the error - * paths as most standard functions can (and do) overwrite errno, even if they - * succeed. - * - * Example: - * #include - * #include - * #include - * - * bool write_string_to_file(const char *file, const char *string) - * { - * int ret, fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0600); - * if (fd < 0) - * return false; - * ret = write(fd, string, strlen(string)); - * if (ret < 0) { - * // Preserve errno from write above. - * close_noerr(fd); - * unlink_noerr(file); - * return false; - * } - * if (close(fd) != 0) { - * // Again, preserve errno. - * unlink_noerr(file); - * return false; - * } - * // A short write means out of space. - * if (ret < strlen(string)) { - * unlink(file); - * errno = ENOSPC; - * return false; - * } - * return true; - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) - /* Nothing. */ - return 0; - - return 1; -} diff --git a/noerr/noerr.c b/noerr/noerr.c deleted file mode 100644 index d6df81f7..00000000 --- a/noerr/noerr.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "noerr.h" -#include -#include - -int close_noerr(int fd) -{ - int saved_errno = errno, ret; - - if (close(fd) != 0) - ret = errno; - else - ret = 0; - - errno = saved_errno; - return ret; -} - -int unlink_noerr(const char *pathname) -{ - int saved_errno = errno, ret; - - if (unlink(pathname) != 0) - ret = errno; - else - ret = 0; - - errno = saved_errno; - return ret; -} diff --git a/noerr/noerr.h b/noerr/noerr.h deleted file mode 100644 index 559ba613..00000000 --- a/noerr/noerr.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef NOERR_H -#define NOERR_H - -/** - * close_noerr - close without stomping errno. - * @fd: the file descriptor to close. - * - * errno is saved and restored across the call to close: if an error occurs, - * the resulting (non-zero) errno is returned. - */ -int close_noerr(int fd); - -/** - * unlink_noerr - unlink a file without stomping errno. - * @pathname: the path to unlink. - * - * errno is saved and restored across the call to unlink: if an error occurs, - * the resulting (non-zero) errno is returned. - */ -int unlink_noerr(const char *pathname); - -#endif /* NOERR_H */ diff --git a/noerr/test/run.c b/noerr/test/run.c deleted file mode 100644 index 6d3c6837..00000000 --- a/noerr/test/run.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "noerr/noerr.h" -#include "tap/tap.h" -#include "noerr/noerr.c" -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - /* tempnam(3) is generally a bad idea, but OK here. */ - char *name = tempnam(NULL, "noerr"); - int fd; - - plan_tests(12); - /* Should fail to unlink. */ - ok1(unlink(name) != 0); - ok1(errno == ENOENT); - - /* This one should not set errno. */ - errno = 100; - ok1(unlink_noerr(name) == ENOENT); - ok1(errno == 100); - - /* Should fail to close. */ - ok1(close(-1) != 0); - ok1(errno == EBADF); - - /* This one should not set errno. */ - errno = 100; - ok1(close_noerr(-1) == EBADF); - ok1(errno == 100); - - /* Test successful close/unlink doesn't hit errno either. */ - fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600); - assert(fd >= 0); - - errno = 100; - ok1(close_noerr(fd) == 0); - ok1(errno == 100); - - errno = 100; - ok1(unlink_noerr(name) == 0); - ok1(errno == 100); - - return exit_status(); -} diff --git a/string/_info.c b/string/_info.c deleted file mode 100644 index 2d1709f7..00000000 --- a/string/_info.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include "config.h" - -/** - * string - string helper routines - * - * This is a grab bag of modules for string comparisons, designed to enhance - * the standard string.h. - * - * Example: - * #include "ccan/string.h" - * - * int main(int argc, char *argv[]) - * { - * if (argv[1] && streq(argv[1], "--verbose")) - * printf("verbose set\n"); - * if (argv[1] && strstarts(argv[1], "--")) - * printf("Some option set\n"); - * if (argv[1] && strends(argv[1], "cow-powers")) - * printf("Magic option set\n"); - * return 0; - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) - /* Nothing. */ - return 0; - - return 1; -} diff --git a/string/string.h b/string/string.h deleted file mode 100644 index f4997c69..00000000 --- a/string/string.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef CCAN_STRING_H -#define CCAN_STRING_H -#include -#include - -/** - * streq - Are two strings equal? - * @a: first string - * @b: first string - * - * This macro is arguably more readable than "!strcmp(a, b)". - * - * Example: - * if (streq(str, "")) - * printf("String is empty!\n"); - */ -#define streq(a,b) (strcmp((a),(b)) == 0) - -/** - * strstarts - Does this string start with this prefix? - * @str: string to test - * @prefix: prefix to look for at start of str - * - * Example: - * if (strstarts(str, "foo")) - * printf("String %s begins with 'foo'!\n", str); - */ -#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) - -/** - * strends - Does this string end with this postfix? - * @str: string to test - * @postfix: postfix to look for at end of str - * - * Example: - * if (strends(str, "foo")) - * printf("String %s end with 'foo'!\n", str); - */ -static inline bool strends(const char *str, const char *postfix) -{ - if (strlen(str) < strlen(postfix)) - return false; - - return streq(str + strlen(str) - strlen(postfix), postfix); -} -#endif /* CCAN_STRING_H */ diff --git a/string/test/run.c b/string/test/run.c deleted file mode 100644 index fded0e49..00000000 --- a/string/test/run.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include "string/string.h" -#include "tap/tap.h" - -/* FIXME: ccanize */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) - -static char *substrings[] = { "far", "bar", "baz", "b", "ba", "z", "ar" }; - -static char *strdup_rev(const char *s) -{ - char *ret = strdup(s); - unsigned int i; - - for (i = 0; i < strlen(s); i++) - ret[i] = s[strlen(s) - i - 1]; - return ret; -} - -int main(int argc, char *argv[]) -{ - unsigned int i, j, n; - char *strings[ARRAY_SIZE(substrings) * ARRAY_SIZE(substrings)]; - - n = 0; - for (i = 0; i < ARRAY_SIZE(substrings); i++) { - for (j = 0; j < ARRAY_SIZE(substrings); j++) { - strings[n] = malloc(strlen(substrings[i]) - + strlen(substrings[j]) + 1); - sprintf(strings[n++], "%s%s", - substrings[i], substrings[j]); - } - } - - plan_tests(n * n * 5); - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - unsigned int k, identical = 0; - char *reva, *revb; - - /* Find first difference. */ - for (k = 0; strings[i][k]==strings[j][k]; k++) { - if (k == strlen(strings[i])) { - identical = 1; - break; - } - } - - if (identical) - ok1(streq(strings[i], strings[j])); - else - ok1(!streq(strings[i], strings[j])); - - /* Postfix test should be equivalent to prefix - * test on reversed string. */ - reva = strdup_rev(strings[i]); - revb = strdup_rev(strings[j]); - - if (!strings[i][k]) { - ok1(strstarts(strings[j], strings[i])); - ok1(strends(revb, reva)); - } else { - ok1(!strstarts(strings[j], strings[i])); - ok1(!strends(revb, reva)); - } - if (!strings[j][k]) { - ok1(strstarts(strings[i], strings[j])); - ok1(strends(reva, revb)); - } else { - ok1(!strstarts(strings[i], strings[j])); - ok1(!strends(reva, revb)); - } - } - } - return exit_status(); -} diff --git a/talloc/TODO b/talloc/TODO deleted file mode 100644 index 0671a6dd..00000000 --- a/talloc/TODO +++ /dev/null @@ -1,2 +0,0 @@ -- Remove talloc.h cruft -- Restore errno around (successful) talloc_free. diff --git a/talloc/_info.c b/talloc/_info.c deleted file mode 100644 index 18da417f..00000000 --- a/talloc/_info.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include "config.h" - -/** - * talloc - tree allocator routines - * - * Talloc is a hierarchical memory pool system with destructors: you keep your - * objects in heirarchies reflecting their lifetime. Every pointer returned - * from talloc() is itself a valid talloc context, from which other talloc()s - * can be attached. This means you can do this: - * - * struct foo *X = talloc(mem_ctx, struct foo); - * X->name = talloc_strdup(X, "foo"); - * - * and the pointer X->name would be a "child" of the talloc context "X" which - * is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) then it is - * all destroyed, whereas if you do talloc_free(X) then just X and X->name are - * destroyed, and if you do talloc_free(X->name) then just the name element of - * X is destroyed. - * - * If you think about this, then what this effectively gives you is an n-ary - * tree, where you can free any part of the tree with talloc_free(). - * - * Talloc has been measured with a time overhead of around 4% over glibc - * malloc, and 48/80 bytes per allocation (32/64 bit). - * - * This version is based on svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc revision 23158. - * - * Example: - * #include - * #include - * #include - * #include "talloc/talloc.h" - * - * // A structure containing a popened comman. - * struct command - * { - * FILE *f; - * const char *command; - * }; - * - * // When struct command is freed, we also want to pclose pipe. - * static int close_cmd(struct command *cmd) - * { - * pclose(cmd->f); - * // 0 means "we succeeded, continue freeing" - * return 0; - * } - * - * // This function opens a writable pipe to the given command. - * struct command *open_output_cmd(const void *ctx, char *fmt, ...) - * { - * va_list ap; - * struct command *cmd = talloc(ctx, struct command); - * - * if (!cmd) - * return NULL; - * - * va_start(ap, fmt); - * cmd->command = talloc_vasprintf(cmd, fmt, ap); - * va_end(ap); - * if (!cmd->command) { - * talloc_free(cmd); - * return NULL; - * } - * - * cmd->f = popen(cmd->command, "w"); - * if (!cmd->f) { - * talloc_free(cmd); - * return NULL; - * } - * talloc_set_destructor(cmd, close_cmd); - * return cmd; - * } - * - * int main(int argc, char *argv[]) - * { - * struct command *cmd; - * - * if (argc != 2) - * errx(1, "Usage: %s \n"); - * - * cmd = open_output_cmd(NULL, "%s hello", argv[1]); - * if (!cmd) - * err(1, "Running '%s hello'", argv[1]); - * fprintf(cmd->f, "This is a test\n"); - * talloc_free(cmd); - * return 0; - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { - printf("typesafe_cb\n"); - return 0; - } - - return 1; -} diff --git a/talloc/talloc.3.xml b/talloc/talloc.3.xml deleted file mode 100644 index 83ca67a4..00000000 --- a/talloc/talloc.3.xml +++ /dev/null @@ -1,739 +0,0 @@ - - - - - talloc - 3 - - - talloc -hierarchical reference counted memory pool system with destructors - - -#include <talloc/talloc.h> - - DESCRIPTION - - If you are used to talloc from Samba3 then please read this - carefully, as talloc has changed a lot. - - - The new talloc is a hierarchical, reference counted memory pool - system with destructors. Quite a mouthful really, but not too bad - once you get used to it. - - - Perhaps the biggest change from Samba3 is that there is no - distinction between a "talloc context" and a "talloc pointer". Any - pointer returned from talloc() is itself a valid talloc context. - This means you can do this: - - - struct foo *X = talloc(mem_ctx, struct foo); - X->name = talloc_strdup(X, "foo"); - - - and the pointer X->name - would be a "child" of the talloc context X which is itself a child of - mem_ctx. So if you do - talloc_free(mem_ctx) then - it is all destroyed, whereas if you do talloc_free(X) then just X and X->name are destroyed, and if - you do talloc_free(X->name) then just - the name element of X is - destroyed. - - - If you think about this, then what this effectively gives you is an - n-ary tree, where you can free any part of the tree with - talloc_free(). - - - If you find this confusing, then I suggest you run the testsuite program to watch talloc - in action. You may also like to add your own tests to testsuite.c to clarify how some - particular situation is handled. - - - TALLOC API - - The following is a complete guide to the talloc API. Read it all at - least twice. - - (type *)talloc(const void *ctx, type); - - The talloc() macro is the core of the talloc library. It takes a - memory ctx and a type, and returns a pointer to a new - area of memory of the given type. - - - The returned pointer is itself a talloc context, so you can use - it as the ctx argument to more - calls to talloc() if you wish. - - - The returned pointer is a "child" of the supplied context. This - means that if you talloc_free() the ctx then the new child disappears as - well. Alternatively you can free just the child. - - - The ctx argument to talloc() - can be NULL, in which case a new top level context is created. - - - void *talloc_size(const void *ctx, size_t size); - - The function talloc_size() should be used when you don't have a - convenient type to pass to talloc(). Unlike talloc(), it is not - type safe (as it returns a void *), so you are on your own for - type checking. - - - (typeof(ptr)) talloc_ptrtype(const void *ctx, ptr); - - The talloc_ptrtype() macro should be used when you have a pointer and - want to allocate memory to point at with this pointer. When compiling - with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() - and talloc_get_name() will return the current location in the source file. - and not the type. - - - int talloc_free(void *ptr); - - The talloc_free() function frees a piece of talloc memory, and - all its children. You can call talloc_free() on any pointer - returned by talloc(). - - - The return value of talloc_free() indicates success or failure, - with 0 returned for success and -1 for failure. The only - possible failure condition is if ptr had a destructor attached to it and - the destructor returned -1. See talloc_set_destructor() - for details on destructors. - - - If this pointer has an additional parent when talloc_free() is - called then the memory is not actually released, but instead the - most recently established parent is destroyed. See talloc_reference() - for details on establishing additional parents. - - - For more control on which parent is removed, see talloc_unlink(). - - - talloc_free() operates recursively on its children. - - - void *talloc_reference(const void *ctx, const void *ptr); - - The talloc_reference() function makes ctx an additional parent of ptr. - - - The return value of talloc_reference() is always the original - pointer ptr, unless talloc ran - out of memory in creating the reference in which case it will - return NULL (each additional reference consumes around 48 bytes - of memory on intel x86 platforms). - - - If ptr is NULL, then the - function is a no-op, and simply returns NULL. - - - After creating a reference you can free it in one of the - following ways: - - - - - - you can talloc_free() any parent of the original pointer. - That will reduce the number of parents of this pointer by 1, - and will cause this pointer to be freed if it runs out of - parents. - - - - - you can talloc_free() the pointer itself. That will destroy - the most recently established parent to the pointer and leave - the pointer as a child of its current parent. - - - - - - For more control on which parent to remove, see talloc_unlink(). - - - int talloc_unlink(const void *ctx, const void *ptr); - - The talloc_unlink() function removes a specific parent from - ptr. The ctx passed must either be a context used - in talloc_reference() with this pointer, or must be a direct - parent of ptr. - - - Note that if the parent has already been removed using - talloc_free() then this function will fail and will return -1. - Likewise, if ptr is NULL, then - the function will make no modifications and return -1. - - - Usually you can just use talloc_free() instead of - talloc_unlink(), but sometimes it is useful to have the - additional control on which parent is removed. - - - void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); - - The function talloc_set_destructor() sets the destructor for the pointer ptr. A destructor is a function that is called - when the memory used by a pointer is about to be released. The - destructor receives ptr as an - argument, and should return 0 for success and -1 for failure. - - - The destructor can do anything - it wants to, including freeing other pieces of memory. A common - use for destructors is to clean up operating system resources - (such as open file descriptors) contained in the structure the - destructor is placed on. - - - You can only place one destructor on a pointer. If you need more - than one destructor then you can create a zero-length child of - the pointer and place an additional destructor on that. - - - To remove a destructor call talloc_set_destructor() with NULL for - the destructor. - - - If your destructor attempts to talloc_free() the pointer that it - is the destructor for then talloc_free() will return -1 and the - free will be ignored. This would be a pointless operation - anyway, as the destructor is only called when the memory is just - about to go away. - - - int talloc_increase_ref_count(const void *<emphasis role="italic">ptr</emphasis>); - - The talloc_increase_ref_count(ptr) function is exactly equivalent to: - - talloc_reference(NULL, ptr); - - You can use either syntax, depending on which you think is - clearer in your code. - - - It returns 0 on success and -1 on failure. - - - size_t talloc_reference_count(const void *<emphasis role="italic">ptr</emphasis>); - - Return the number of references to the pointer. - - - void talloc_set_name(const void *ptr, const char *fmt, ...); - - Each talloc pointer has a "name". The name is used principally - for debugging purposes, although it is also possible to set and - get the name on a pointer in as a way of "marking" pointers in - your code. - - - The main use for names on pointer is for "talloc reports". See - talloc_report_depth_cb(), - talloc_report_depth_file(), - talloc_report() - talloc_report() - and talloc_report_full() - for details. Also see talloc_enable_leak_report() - and talloc_enable_leak_report_full(). - - - The talloc_set_name() function allocates memory as a child of the - pointer. It is logically equivalent to: - - talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); - - Note that multiple calls to talloc_set_name() will allocate more - memory without releasing the name. All of the memory is released - when the ptr is freed using talloc_free(). - - - void talloc_set_name_const(const void *<emphasis role="italic">ptr</emphasis>, const char *<emphasis role="italic">name</emphasis>); - - The function talloc_set_name_const() is just like - talloc_set_name(), but it takes a string constant, and is much - faster. It is extensively used by the "auto naming" macros, such - as talloc_p(). - - - This function does not allocate any memory. It just copies the - supplied pointer into the internal representation of the talloc - ptr. This means you must not pass a name pointer to memory that will - disappear before ptr is freed - with talloc_free(). - - - void *talloc_named(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, ...); - - The talloc_named() function creates a named talloc pointer. It - is equivalent to: - - ptr = talloc_size(ctx, size); -talloc_set_name(ptr, fmt, ....); - - void *talloc_named_const(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>, const char *<emphasis role="italic">name</emphasis>); - - This is equivalent to: - - ptr = talloc_size(ctx, size); -talloc_set_name_const(ptr, name); - - const char *talloc_get_name(const void *<emphasis role="italic">ptr</emphasis>); - - This returns the current name for the given talloc pointer, - ptr. See talloc_set_name() - for details. - - - void *talloc_init(const char *<emphasis role="italic">fmt</emphasis>, ...); - - This function creates a zero length named talloc context as a top - level context. It is equivalent to: - - talloc_named(NULL, 0, fmt, ...); - - void *talloc_new(void *<emphasis role="italic">ctx</emphasis>); - - This is a utility macro that creates a new memory context hanging - off an exiting context, automatically naming it "talloc_new: - __location__" where __location__ is the source line it is called - from. It is particularly useful for creating a new temporary - working context. - - - (<emphasis role="italic">type</emphasis> *)talloc_realloc(const void *<emphasis role="italic">ctx</emphasis>, void *<emphasis role="italic">ptr</emphasis>, <emphasis role="italic">type</emphasis>, <emphasis role="italic">count</emphasis>); - - The talloc_realloc() macro changes the size of a talloc pointer. - It has the following equivalences: - - talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type); -talloc_realloc(ctx, ptr, type, 0) ==> talloc_free(ptr); - - The ctx argument is only used - if ptr is not NULL, otherwise - it is ignored. - - - talloc_realloc() returns the new pointer, or NULL on failure. - The call will fail either due to a lack of memory, or because the - pointer has more than one parent (see talloc_reference()). - - - void *talloc_realloc_size(const void *ctx, void *ptr, size_t size); - - the talloc_realloc_size() function is useful when the type is not - known so the type-safe talloc_realloc() cannot be used. - - - TYPE *talloc_steal(const void *<emphasis role="italic">new_ctx</emphasis>, const TYPE *<emphasis role="italic">ptr</emphasis>); - - The talloc_steal() function changes the parent context of a - talloc pointer. It is typically used when the context that the - pointer is currently a child of is going to be freed and you wish - to keep the memory for a longer time. - - - The talloc_steal() function returns the pointer that you pass it. - It does not have any failure modes. - - - NOTE: It is possible to produce loops in the parent/child - relationship if you are not careful with talloc_steal(). No - guarantees are provided as to your sanity or the safety of your - data if you do this. - - - TYPE *talloc_move(const void *<emphasis role="italic">new_ctx</emphasis>, TYPE **<emphasis role="italic">ptr</emphasis>); - - The talloc_move() function is a wrapper around - talloc_steal() which zeros the source pointer after the - move. This avoids a potential source of bugs where a - programmer leaves a pointer in two structures, and uses the - pointer from the old structure after it has been moved to a - new one. - - - size_t talloc_total_size(const void *<emphasis role="italic">ptr</emphasis>); - - The talloc_total_size() function returns the total size in bytes - used by this pointer and all child pointers. Mostly useful for - debugging. - - - Passing NULL is allowed, but it will only give a meaningful - result if talloc_enable_leak_report() or - talloc_enable_leak_report_full() has been called. - - - size_t talloc_total_blocks(const void *<emphasis role="italic">ptr</emphasis>); - - The talloc_total_blocks() function returns the total memory block - count used by this pointer and all child pointers. Mostly useful - for debugging. - - - Passing NULL is allowed, but it will only give a meaningful - result if talloc_enable_leak_report() or - talloc_enable_leak_report_full() has been called. - - - void talloc_report(const void *ptr, FILE *f); - - The talloc_report() function prints a summary report of all - memory used by ptr. One line - of report is printed for each immediate child of ptr, showing the - total memory and number of blocks used by that child. - - - You can pass NULL for the pointer, in which case a report is - printed for the top level memory context, but only if - talloc_enable_leak_report() or talloc_enable_leak_report_full() - has been called. - - - void talloc_report_full(const void *<emphasis role="italic">ptr</emphasis>, FILE *<emphasis role="italic">f</emphasis>); - - This provides a more detailed report than talloc_report(). It - will recursively print the entire tree of memory referenced by - the pointer. References in the tree are shown by giving the name - of the pointer that is referenced. - - - You can pass NULL for the pointer, in which case a report is - printed for the top level memory context, but only if - talloc_enable_leak_report() or talloc_enable_leak_report_full() - has been called. - - - - - void talloc_report_depth_cb - const void *ptr - int depth - int max_depth - void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *priv) - void *priv - - - This provides a more flexible reports than talloc_report(). It - will recursively call the callback for the entire tree of memory - referenced by the pointer. References in the tree are passed with - is_ref = 1 and the pointer that is referenced. - - - You can pass NULL for the pointer, in which case a report is - printed for the top level memory context, but only if - talloc_enable_leak_report() or talloc_enable_leak_report_full() - has been called. - - - The recursion is stopped when depth >= max_depth. - max_depth = -1 means only stop at leaf nodes. - - - - - void talloc_report_depth_file - const void *ptr - int depth - int max_depth - FILE *f - - - This provides a more flexible reports than talloc_report(). It - will let you specify the depth and max_depth. - - - void talloc_enable_leak_report(void); - - This enables calling of talloc_report(NULL, stderr) when the - program exits. In Samba4 this is enabled by using the - --leak-report command line option. - - - For it to be useful, this function must be called before any - other talloc function as it establishes a "null context" that - acts as the top of the tree. If you don't call this function - first then passing NULL to talloc_report() or - talloc_report_full() won't give you the full tree printout. - - - Here is a typical talloc report: - - talloc report on 'null_context' (total 267 bytes in 15 blocks) -libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks -libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks -iconv(UTF8,CP850) contains 42 bytes in 2 blocks -libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks -iconv(CP850,UTF8) contains 42 bytes in 2 blocks -iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks -iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks - - - void talloc_enable_leak_report_full(void); - - This enables calling of talloc_report_full(NULL, stderr) when the - program exits. In Samba4 this is enabled by using the - --leak-report-full command line option. - - - For it to be useful, this function must be called before any - other talloc function as it establishes a "null context" that - acts as the top of the tree. If you don't call this function - first then passing NULL to talloc_report() or - talloc_report_full() won't give you the full tree printout. - - - Here is a typical full report: - - full talloc report on 'root' (total 18 bytes in 8 blocks) -p1 contains 18 bytes in 7 blocks (ref 0) - r1 contains 13 bytes in 2 blocks (ref 0) - reference to: p2 - p2 contains 1 bytes in 1 blocks (ref 1) - x3 contains 1 bytes in 1 blocks (ref 0) - x2 contains 1 bytes in 1 blocks (ref 0) - x1 contains 1 bytes in 1 blocks (ref 0) - - - (<emphasis role="italic">type</emphasis> *)talloc_zero(const void *<emphasis role="italic">ctx</emphasis>, <emphasis role="italic">type</emphasis>); - - The talloc_zero() macro is equivalent to: - - ptr = talloc(ctx, type); -if (ptr) memset(ptr, 0, sizeof(type)); - - void *talloc_zero_size(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>) - - The talloc_zero_size() function is useful when you don't have a - known type. - - - void *talloc_memdup(const void *<emphasis role="italic">ctx</emphasis>, const void *<emphasis role="italic">p</emphasis>, size_t size); - - The talloc_memdup() function is equivalent to: - - ptr = talloc_size(ctx, size); -if (ptr) memcpy(ptr, p, size); - - char *talloc_strdup(const void *<emphasis role="italic">ctx</emphasis>, const char *<emphasis role="italic">p</emphasis>); - - The talloc_strdup() function is equivalent to: - - ptr = talloc_size(ctx, strlen(p)+1); -if (ptr) memcpy(ptr, p, strlen(p)+1); - - This function sets the name of the new pointer to the passed - string. This is equivalent to: - - talloc_set_name_const(ptr, ptr) - - char *talloc_strndup(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">p</emphasis>, size_t <emphasis role="italic">n</emphasis>); - - The talloc_strndup() function is the talloc equivalent of the C - library function strndup(3). - - - This function sets the name of the new pointer to the passed - string. This is equivalent to: - - talloc_set_name_const(ptr, ptr) - - char *talloc_append_string(const void *<emphasis role="italic">t</emphasis>, char *<emphasis role="italic">orig</emphasis>, const char *<emphasis role="italic">append</emphasis>); - - The talloc_append_string() function appends the given formatted - string to the given string. - - - This function sets the name of the new pointer to the new - string. This is equivalent to: - - talloc_set_name_const(ptr, ptr) - - char *talloc_vasprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, va_list <emphasis role="italic">ap</emphasis>); - - The talloc_vasprintf() function is the talloc equivalent of the C - library function vasprintf(3). - - - This function sets the name of the new pointer to the new - string. This is equivalent to: - - talloc_set_name_const(ptr, ptr) - - char *talloc_asprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, ...); - - The talloc_asprintf() function is the talloc equivalent of the C - library function asprintf(3). - - - This function sets the name of the new pointer to the passed - string. This is equivalent to: - - talloc_set_name_const(ptr, ptr) - - char *talloc_asprintf_append(char *s, const char *fmt, ...); - - The talloc_asprintf_append() function appends the given formatted - string to the given string. - - - This function sets the name of the new pointer to the new - string. This is equivalent to: - - talloc_set_name_const(ptr, ptr) - - (type *)talloc_array(const void *ctx, type, uint_t count); - - The talloc_array() macro is equivalent to: - - (type *)talloc_size(ctx, sizeof(type) * count); - - except that it provides integer overflow protection for the - multiply, returning NULL if the multiply overflows. - - - void *talloc_array_size(const void *ctx, size_t size, uint_t count); - - The talloc_array_size() function is useful when the type is not - known. It operates in the same way as talloc_array(), but takes a - size instead of a type. - - - (typeof(ptr)) talloc_array_ptrtype(const void *ctx, ptr, uint_t count); - - The talloc_ptrtype() macro should be used when you have a pointer to an array - and want to allocate memory of an array to point at with this pointer. When compiling - with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size() - and talloc_get_name() will return the current location in the source file. - and not the type. - - - void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size) - - This is a non-macro version of talloc_realloc(), which is useful - as libraries sometimes want a realloc function pointer. A - realloc(3) implementation encapsulates the functionality of - malloc(3), free(3) and realloc(3) in one call, which is why it is - useful to be able to pass around a single function pointer. - - - void *talloc_autofree_context(void); - - This is a handy utility function that returns a talloc context - which will be automatically freed on program exit. This can be - used to reduce the noise in memory leak reports. - - - void *talloc_check_name(const void *ptr, const char *name); - - This function checks if a pointer has the specified name. If it does then the pointer is - returned. It it doesn't then NULL is returned. - - - (type *)talloc_get_type(const void *ptr, type); - - This macro allows you to do type checking on talloc pointers. It - is particularly useful for void* private pointers. It is - equivalent to this: - - (type *)talloc_check_name(ptr, #type) - - talloc_set_type(const void *ptr, type); - - This macro allows you to force the name of a pointer to be a - particular type. This can be - used in conjunction with talloc_get_type() to do type checking on - void* pointers. - - - It is equivalent to this: - - talloc_set_name_const(ptr, #type) - - - PERFORMANCE - - All the additional features of talloc(3) over malloc(3) do come at a - price. We have a simple performance test in Samba4 that measures - talloc() versus malloc() performance, and it seems that talloc() is - about 10% slower than malloc() on my x86 Debian Linux box. For - Samba, the great reduction in code complexity that we get by using - talloc makes this worthwhile, especially as the total overhead of - talloc/malloc in Samba is already quite small. - - - SEE ALSO - - malloc(3), strndup(3), vasprintf(3), asprintf(3), - - - - COPYRIGHT/LICENSE - - Copyright (C) Andrew Tridgell 2004 - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at - your option) any later version. - - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - - diff --git a/talloc/talloc.c b/talloc/talloc.c deleted file mode 100644 index d624b917..00000000 --- a/talloc/talloc.c +++ /dev/null @@ -1,1403 +0,0 @@ -/* - Samba Unix SMB/CIFS implementation. - - Samba trivial allocation library - new interface - - NOTE: Please read talloc_guide.txt for full documentation - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Stefan Metzmacher 2006 - - ** NOTE! The following LGPL license applies to the talloc - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* - inspired by http://swapped.cc/halloc/ -*/ - -#include "talloc.h" -#include -#include -#include - -/* use this to force every realloc to change the pointer, to stress test - code that might not cope */ -#define ALWAYS_REALLOC 0 - - -#define MAX_TALLOC_SIZE 0x10000000 -#define TALLOC_MAGIC 0xe814ec70 -#define TALLOC_FLAG_FREE 0x01 -#define TALLOC_FLAG_LOOP 0x02 -#define TALLOC_MAGIC_REFERENCE ((const char *)1) - -/* by default we abort when given a bad pointer (such as when talloc_free() is called - on a pointer that came from malloc() */ -#ifndef TALLOC_ABORT -#define TALLOC_ABORT(reason) abort() -#endif - -#ifndef discard_const_p -#if defined(INTPTR_MIN) -# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) -#else -# define discard_const_p(type, ptr) ((type *)(ptr)) -#endif -#endif - -/* these macros gain us a few percent of speed on gcc */ -#if HAVE_BUILTIN_EXPECT -/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 - as its first argument */ -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#else -#define likely(x) x -#define unlikely(x) x -#endif - -/* this null_context is only used if talloc_enable_leak_report() or - talloc_enable_leak_report_full() is called, otherwise it remains - NULL -*/ -static void *null_context; -static void *autofree_context; - -struct talloc_reference_handle { - struct talloc_reference_handle *next, *prev; - void *ptr; -}; - -typedef int (*talloc_destructor_t)(void *); - -struct talloc_chunk { - struct talloc_chunk *next, *prev; - struct talloc_chunk *parent, *child; - struct talloc_reference_handle *refs; - talloc_destructor_t destructor; - const char *name; - size_t size; - unsigned flags; -}; - -/* 16 byte alignment seems to keep everyone happy */ -#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) -#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) - -/* panic if we get a bad magic value */ -static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) -{ - const char *pp = (const char *)ptr; - struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); - if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { - if (tc->flags & TALLOC_FLAG_FREE) { - TALLOC_ABORT("Bad talloc magic value - double free"); - } else { - TALLOC_ABORT("Bad talloc magic value - unknown value"); - } - } - return tc; -} - -/* hook into the front of the list */ -#define _TLIST_ADD(list, p) \ -do { \ - if (!(list)) { \ - (list) = (p); \ - (p)->next = (p)->prev = NULL; \ - } else { \ - (list)->prev = (p); \ - (p)->next = (list); \ - (p)->prev = NULL; \ - (list) = (p); \ - }\ -} while (0) - -/* remove an element from a list - element doesn't have to be in list. */ -#define _TLIST_REMOVE(list, p) \ -do { \ - if ((p) == (list)) { \ - (list) = (p)->next; \ - if (list) (list)->prev = NULL; \ - } else { \ - if ((p)->prev) (p)->prev->next = (p)->next; \ - if ((p)->next) (p)->next->prev = (p)->prev; \ - } \ - if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ -} while (0) - - -/* - return the parent chunk of a pointer -*/ -static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return NULL; - } - - tc = talloc_chunk_from_ptr(ptr); - while (tc->prev) tc=tc->prev; - - return tc->parent; -} - -void *talloc_parent(const void *ptr) -{ - struct talloc_chunk *tc = talloc_parent_chunk(ptr); - return tc? TC_PTR_FROM_CHUNK(tc) : NULL; -} - -/* - find parents name -*/ -const char *talloc_parent_name(const void *ptr) -{ - struct talloc_chunk *tc = talloc_parent_chunk(ptr); - return tc? tc->name : NULL; -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -static inline void *__talloc(const void *context, size_t size) -{ - struct talloc_chunk *tc; - - if (unlikely(context == NULL)) { - context = null_context; - } - - if (unlikely(size >= MAX_TALLOC_SIZE)) { - return NULL; - } - - tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); - if (unlikely(tc == NULL)) return NULL; - - tc->size = size; - tc->flags = TALLOC_MAGIC; - tc->destructor = NULL; - tc->child = NULL; - tc->name = NULL; - tc->refs = NULL; - - if (likely(context)) { - struct talloc_chunk *parent = talloc_chunk_from_ptr(context); - - if (parent->child) { - parent->child->parent = NULL; - tc->next = parent->child; - tc->next->prev = tc; - } else { - tc->next = NULL; - } - tc->parent = parent; - tc->prev = NULL; - parent->child = tc; - } else { - tc->next = tc->prev = tc->parent = NULL; - } - - return TC_PTR_FROM_CHUNK(tc); -} - -/* - setup a destructor to be called on free of a pointer - the destructor should return 0 on success, or -1 on failure. - if the destructor fails then the free is failed, and the memory can - be continued to be used -*/ -void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->destructor = destructor; -} - -/* - increase the reference count on a piece of memory. -*/ -int talloc_increase_ref_count(const void *ptr) -{ - if (unlikely(!talloc_reference(null_context, ptr))) { - return -1; - } - return 0; -} - -/* - helper for talloc_reference() - - this is referenced by a function pointer and should not be inline -*/ -static int talloc_reference_destructor(struct talloc_reference_handle *handle) -{ - struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); - _TLIST_REMOVE(ptr_tc->refs, handle); - return 0; -} - -/* - more efficient way to add a name to a pointer - the name must point to a - true string constant -*/ -static inline void _talloc_set_name_const(const void *ptr, const char *name) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->name = name; -} - -/* - internal talloc_named_const() -*/ -static inline void *_talloc_named_const(const void *context, size_t size, const char *name) -{ - void *ptr; - - ptr = __talloc(context, size); - if (unlikely(ptr == NULL)) { - return NULL; - } - - _talloc_set_name_const(ptr, name); - - return ptr; -} - -/* - make a secondary reference to a pointer, hanging off the given context. - the pointer remains valid until both the original caller and this given - context are freed. - - the major use for this is when two different structures need to reference the - same underlying data, and you want to be able to free the two instances separately, - and in either order -*/ -void *_talloc_reference(const void *context, const void *ptr) -{ - struct talloc_chunk *tc; - struct talloc_reference_handle *handle; - if (unlikely(ptr == NULL)) return NULL; - - tc = talloc_chunk_from_ptr(ptr); - handle = (struct talloc_reference_handle *)_talloc_named_const(context, - sizeof(struct talloc_reference_handle), - TALLOC_MAGIC_REFERENCE); - if (unlikely(handle == NULL)) return NULL; - - /* note that we hang the destructor off the handle, not the - main context as that allows the caller to still setup their - own destructor on the context if they want to */ - talloc_set_destructor(handle, talloc_reference_destructor); - handle->ptr = discard_const_p(void, ptr); - _TLIST_ADD(tc->refs, handle); - return handle->ptr; -} - - -/* - internal talloc_free call -*/ -static inline int _talloc_free(void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return -1; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (unlikely(tc->refs)) { - int is_child; - /* check this is a reference from a child or grantchild - * back to it's parent or grantparent - * - * in that case we need to remove the reference and - * call another instance of talloc_free() on the current - * pointer. - */ - is_child = talloc_is_parent(tc->refs, ptr); - _talloc_free(tc->refs); - if (is_child) { - return _talloc_free(ptr); - } - return -1; - } - - if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { - /* we have a free loop - stop looping */ - return 0; - } - - if (unlikely(tc->destructor)) { - talloc_destructor_t d = tc->destructor; - if (d == (talloc_destructor_t)-1) { - return -1; - } - tc->destructor = (talloc_destructor_t)-1; - if (d(ptr) == -1) { - tc->destructor = d; - return -1; - } - tc->destructor = NULL; - } - - if (tc->parent) { - _TLIST_REMOVE(tc->parent->child, tc); - if (tc->parent->child) { - tc->parent->child->parent = tc->parent; - } - } else { - if (tc->prev) tc->prev->next = tc->next; - if (tc->next) tc->next->prev = tc->prev; - } - - tc->flags |= TALLOC_FLAG_LOOP; - - while (tc->child) { - /* we need to work out who will own an abandoned child - if it cannot be freed. In priority order, the first - choice is owner of any remaining reference to this - pointer, the second choice is our parent, and the - final choice is the null context. */ - void *child = TC_PTR_FROM_CHUNK(tc->child); - const void *new_parent = null_context; - if (unlikely(tc->child->refs)) { - struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - if (unlikely(_talloc_free(child) == -1)) { - if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - talloc_steal(new_parent, child); - } - } - - tc->flags |= TALLOC_FLAG_FREE; - free(tc); - return 0; -} - -/* - move a lump of memory from one talloc context to another return the - ptr on success, or NULL if it could not be transferred. - passing NULL as ptr will always return NULL with no side effects. -*/ -void *_talloc_steal(const void *new_ctx, const void *ptr) -{ - struct talloc_chunk *tc, *new_tc; - - if (unlikely(!ptr)) { - return NULL; - } - - if (unlikely(new_ctx == NULL)) { - new_ctx = null_context; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (unlikely(new_ctx == NULL)) { - if (tc->parent) { - _TLIST_REMOVE(tc->parent->child, tc); - if (tc->parent->child) { - tc->parent->child->parent = tc->parent; - } - } else { - if (tc->prev) tc->prev->next = tc->next; - if (tc->next) tc->next->prev = tc->prev; - } - - tc->parent = tc->next = tc->prev = NULL; - return discard_const_p(void, ptr); - } - - new_tc = talloc_chunk_from_ptr(new_ctx); - - if (unlikely(tc == new_tc || tc->parent == new_tc)) { - return discard_const_p(void, ptr); - } - - if (tc->parent) { - _TLIST_REMOVE(tc->parent->child, tc); - if (tc->parent->child) { - tc->parent->child->parent = tc->parent; - } - } else { - if (tc->prev) tc->prev->next = tc->next; - if (tc->next) tc->next->prev = tc->prev; - } - - tc->parent = new_tc; - if (new_tc->child) new_tc->child->parent = NULL; - _TLIST_ADD(new_tc->child, tc); - - return discard_const_p(void, ptr); -} - - - -/* - remove a secondary reference to a pointer. This undo's what - talloc_reference() has done. The context and pointer arguments - must match those given to a talloc_reference() -*/ -static inline int talloc_unreference(const void *context, const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - struct talloc_reference_handle *h; - - if (unlikely(context == NULL)) { - context = null_context; - } - - for (h=tc->refs;h;h=h->next) { - struct talloc_chunk *p = talloc_parent_chunk(h); - if (p == NULL) { - if (context == NULL) break; - } else if (TC_PTR_FROM_CHUNK(p) == context) { - break; - } - } - if (h == NULL) { - return -1; - } - - return _talloc_free(h); -} - -/* - remove a specific parent context from a pointer. This is a more - controlled varient of talloc_free() -*/ -int talloc_unlink(const void *context, void *ptr) -{ - struct talloc_chunk *tc_p, *new_p; - void *new_parent; - - if (ptr == NULL) { - return -1; - } - - if (context == NULL) { - context = null_context; - } - - if (talloc_unreference(context, ptr) == 0) { - return 0; - } - - if (context == NULL) { - if (talloc_parent_chunk(ptr) != NULL) { - return -1; - } - } else { - if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { - return -1; - } - } - - tc_p = talloc_chunk_from_ptr(ptr); - - if (tc_p->refs == NULL) { - return _talloc_free(ptr); - } - - new_p = talloc_parent_chunk(tc_p->refs); - if (new_p) { - new_parent = TC_PTR_FROM_CHUNK(new_p); - } else { - new_parent = NULL; - } - - if (talloc_unreference(new_parent, ptr) != 0) { - return -1; - } - - talloc_steal(new_parent, ptr); - - return 0; -} - -/* - add a name to an existing pointer - va_list version -*/ -static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); - -static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->name = talloc_vasprintf(ptr, fmt, ap); - if (likely(tc->name)) { - _talloc_set_name_const(tc->name, ".name"); - } - return tc->name; -} - -/* - add a name to an existing pointer -*/ -const char *talloc_set_name(const void *ptr, const char *fmt, ...) -{ - const char *name; - va_list ap; - va_start(ap, fmt); - name = talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - return name; -} - - -/* - create a named talloc pointer. Any talloc pointer can be named, and - talloc_named() operates just like talloc() except that it allows you - to name the pointer. -*/ -void *talloc_named(const void *context, size_t size, const char *fmt, ...) -{ - va_list ap; - void *ptr; - const char *name; - - ptr = __talloc(context, size); - if (unlikely(ptr == NULL)) return NULL; - - va_start(ap, fmt); - name = talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - - if (unlikely(name == NULL)) { - _talloc_free(ptr); - return NULL; - } - - return ptr; -} - -/* - return the name of a talloc ptr, or "UNNAMED" -*/ -const char *talloc_get_name(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { - return ".reference"; - } - if (likely(tc->name)) { - return tc->name; - } - return "UNNAMED"; -} - - -/* - check if a pointer has the given name. If it does, return the pointer, - otherwise return NULL -*/ -void *talloc_check_name(const void *ptr, const char *name) -{ - const char *pname; - if (unlikely(ptr == NULL)) return NULL; - pname = talloc_get_name(ptr); - if (likely(pname == name || strcmp(pname, name) == 0)) { - return discard_const_p(void, ptr); - } - return NULL; -} - - -/* - this is for compatibility with older versions of talloc -*/ -void *talloc_init(const char *fmt, ...) -{ - va_list ap; - void *ptr; - const char *name; - - /* - * samba3 expects talloc_report_depth_cb(NULL, ...) - * reports all talloc'ed memory, so we need to enable - * null_tracking - */ - talloc_enable_null_tracking(); - - ptr = __talloc(NULL, 0); - if (unlikely(ptr == NULL)) return NULL; - - va_start(ap, fmt); - name = talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - - if (unlikely(name == NULL)) { - _talloc_free(ptr); - return NULL; - } - - return ptr; -} - -/* - this is a replacement for the Samba3 talloc_destroy_pool functionality. It - should probably not be used in new code. It's in here to keep the talloc - code consistent across Samba 3 and 4. -*/ -void talloc_free_children(void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return; - } - - tc = talloc_chunk_from_ptr(ptr); - - while (tc->child) { - /* we need to work out who will own an abandoned child - if it cannot be freed. In priority order, the first - choice is owner of any remaining reference to this - pointer, the second choice is our parent, and the - final choice is the null context. */ - void *child = TC_PTR_FROM_CHUNK(tc->child); - const void *new_parent = null_context; - if (unlikely(tc->child->refs)) { - struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - if (unlikely(_talloc_free(child) == -1)) { - if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - talloc_steal(new_parent, child); - } - } -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -void *_talloc(const void *context, size_t size) -{ - return __talloc(context, size); -} - -/* - externally callable talloc_set_name_const() -*/ -void talloc_set_name_const(const void *ptr, const char *name) -{ - _talloc_set_name_const(ptr, name); -} - -/* - create a named talloc pointer. Any talloc pointer can be named, and - talloc_named() operates just like talloc() except that it allows you - to name the pointer. -*/ -void *talloc_named_const(const void *context, size_t size, const char *name) -{ - return _talloc_named_const(context, size, name); -} - -/* - free a talloc pointer. This also frees all child pointers of this - pointer recursively - - return 0 if the memory is actually freed, otherwise -1. The memory - will not be freed if the ref_count is > 1 or the destructor (if - any) returns non-zero -*/ -int talloc_free(void *ptr) -{ - int saved_errno = errno, ret; - ret = _talloc_free(ptr); - if (ret == 0) - errno = saved_errno; - return ret; -} - - - -/* - A talloc version of realloc. The context argument is only used if - ptr is NULL -*/ -void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) -{ - struct talloc_chunk *tc; - void *new_ptr; - - /* size zero is equivalent to free() */ - if (unlikely(size == 0)) { - _talloc_free(ptr); - return NULL; - } - - if (unlikely(size >= MAX_TALLOC_SIZE)) { - return NULL; - } - - /* realloc(NULL) is equivalent to malloc() */ - if (ptr == NULL) { - return _talloc_named_const(context, size, name); - } - - tc = talloc_chunk_from_ptr(ptr); - - /* don't allow realloc on referenced pointers */ - if (unlikely(tc->refs)) { - return NULL; - } - - /* by resetting magic we catch users of the old memory */ - tc->flags |= TALLOC_FLAG_FREE; - -#if ALWAYS_REALLOC - new_ptr = malloc(size + TC_HDR_SIZE); - if (new_ptr) { - memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); - free(tc); - } -#else - new_ptr = realloc(tc, size + TC_HDR_SIZE); -#endif - if (unlikely(!new_ptr)) { - tc->flags &= ~TALLOC_FLAG_FREE; - return NULL; - } - - tc = (struct talloc_chunk *)new_ptr; - tc->flags &= ~TALLOC_FLAG_FREE; - if (tc->parent) { - tc->parent->child = tc; - } - if (tc->child) { - tc->child->parent = tc; - } - - if (tc->prev) { - tc->prev->next = tc; - } - if (tc->next) { - tc->next->prev = tc; - } - - tc->size = size; - _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); - - return TC_PTR_FROM_CHUNK(tc); -} - -/* - a wrapper around talloc_steal() for situations where you are moving a pointer - between two structures, and want the old pointer to be set to NULL -*/ -void *_talloc_move(const void *new_ctx, const void *_pptr) -{ - const void **pptr = discard_const_p(const void *,_pptr); - void *ret = _talloc_steal(new_ctx, *pptr); - (*pptr) = NULL; - return ret; -} - -/* - return the total size of a talloc pool (subtree) -*/ -size_t talloc_total_size(const void *ptr) -{ - size_t total = 0; - struct talloc_chunk *c, *tc; - - if (ptr == NULL) { - ptr = null_context; - } - if (ptr == NULL) { - return 0; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (tc->flags & TALLOC_FLAG_LOOP) { - return 0; - } - - tc->flags |= TALLOC_FLAG_LOOP; - - total = tc->size; - for (c=tc->child;c;c=c->next) { - total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); - } - - tc->flags &= ~TALLOC_FLAG_LOOP; - - return total; -} - -/* - return the total number of blocks in a talloc pool (subtree) -*/ -size_t talloc_total_blocks(const void *ptr) -{ - size_t total = 0; - struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); - - if (tc->flags & TALLOC_FLAG_LOOP) { - return 0; - } - - tc->flags |= TALLOC_FLAG_LOOP; - - total++; - for (c=tc->child;c;c=c->next) { - total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); - } - - tc->flags &= ~TALLOC_FLAG_LOOP; - - return total; -} - -/* - return the number of external references to a pointer -*/ -size_t talloc_reference_count(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - struct talloc_reference_handle *h; - size_t ret = 0; - - for (h=tc->refs;h;h=h->next) { - ret++; - } - return ret; -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, - void (*callback)(const void *ptr, - int depth, int max_depth, - int is_ref, - void *private_data), - void *private_data) -{ - struct talloc_chunk *c, *tc; - - if (ptr == NULL) { - ptr = null_context; - } - if (ptr == NULL) return; - - tc = talloc_chunk_from_ptr(ptr); - - if (tc->flags & TALLOC_FLAG_LOOP) { - return; - } - - callback(ptr, depth, max_depth, 0, private_data); - - if (max_depth >= 0 && depth >= max_depth) { - return; - } - - tc->flags |= TALLOC_FLAG_LOOP; - for (c=tc->child;c;c=c->next) { - if (c->name == TALLOC_MAGIC_REFERENCE) { - struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); - callback(h->ptr, depth + 1, max_depth, 1, private_data); - } else { - talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); - } - } - tc->flags &= ~TALLOC_FLAG_LOOP; -} - -static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) -{ - const char *name = talloc_get_name(ptr); - FILE *f = (FILE *)_f; - - if (is_ref) { - fprintf(f, "%*sreference to: %s\n", depth*4, "", name); - return; - } - - if (depth == 0) { - fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", - (max_depth < 0 ? "full " :""), name, - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr)); - return; - } - - fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", - depth*4, "", - name, - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr), - (int)talloc_reference_count(ptr), ptr); - -#if 0 - fprintf(f, "content: "); - if (talloc_total_size(ptr)) { - int tot = talloc_total_size(ptr); - int i; - - for (i = 0; i < tot; i++) { - if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { - fprintf(f, "%c", ((char *)ptr)[i]); - } else { - fprintf(f, "~%02x", ((char *)ptr)[i]); - } - } - } - fprintf(f, "\n"); -#endif -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) -{ - talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); - fflush(f); -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_full(const void *ptr, FILE *f) -{ - talloc_report_depth_file(ptr, 0, -1, f); -} - -/* - report on memory usage by all children of a pointer -*/ -void talloc_report(const void *ptr, FILE *f) -{ - talloc_report_depth_file(ptr, 0, 1, f); -} - -/* - report on any memory hanging off the null context -*/ -static void talloc_report_null(void) -{ - if (talloc_total_size(null_context) != 0) { - talloc_report(null_context, stderr); - } -} - -/* - report on any memory hanging off the null context -*/ -static void talloc_report_null_full(void) -{ - if (talloc_total_size(null_context) != 0) { - talloc_report_full(null_context, stderr); - } -} - -/* - enable tracking of the NULL context -*/ -void talloc_enable_null_tracking(void) -{ - if (null_context == NULL) { - null_context = _talloc_named_const(NULL, 0, "null_context"); - } -} - -/* - disable tracking of the NULL context -*/ -void talloc_disable_null_tracking(void) -{ - _talloc_free(null_context); - null_context = NULL; -} - -/* - enable leak reporting on exit -*/ -void talloc_enable_leak_report(void) -{ - talloc_enable_null_tracking(); - atexit(talloc_report_null); -} - -/* - enable full leak reporting on exit -*/ -void talloc_enable_leak_report_full(void) -{ - talloc_enable_null_tracking(); - atexit(talloc_report_null_full); -} - -/* - talloc and zero memory. -*/ -void *_talloc_zero(const void *ctx, size_t size, const char *name) -{ - void *p = _talloc_named_const(ctx, size, name); - - if (p) { - memset(p, '\0', size); - } - - return p; -} - -/* - memdup with a talloc. -*/ -void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) -{ - void *newp = _talloc_named_const(t, size, name); - - if (likely(newp)) { - memcpy(newp, p, size); - } - - return newp; -} - -/* - strdup with a talloc -*/ -char *talloc_strdup(const void *t, const char *p) -{ - char *ret; - if (!p) { - return NULL; - } - ret = (char *)talloc_memdup(t, p, strlen(p) + 1); - if (likely(ret)) { - _talloc_set_name_const(ret, ret); - } - return ret; -} - -/* - append to a talloced string -*/ -char *talloc_append_string(char *orig, const char *append) -{ - char *ret; - size_t olen = strlen(orig); - size_t alenz; - - if (!append) - return orig; - - alenz = strlen(append) + 1; - - ret = talloc_realloc(NULL, orig, char, olen + alenz); - if (!ret) - return NULL; - - /* append the string with the trailing \0 */ - memcpy(&ret[olen], append, alenz); - - _talloc_set_name_const(ret, ret); - - return ret; -} - -/* - strndup with a talloc -*/ -char *talloc_strndup(const void *t, const char *p, size_t n) -{ - size_t len; - char *ret; - - for (len=0; lensize - 1; - - va_copy(ap2, ap); - len = vsnprintf(&c, 1, fmt, ap2); - va_end(ap2); - - if (len <= 0) { - /* Either the vsnprintf failed or the format resulted in - * no characters being formatted. In the former case, we - * ought to return NULL, in the latter we ought to return - * the original string. Most current callers of this - * function expect it to never return NULL. - */ - return s; - } - - s = talloc_realloc(NULL, s, char, s_len + len+1); - if (!s) return NULL; - - va_copy(ap2, ap); - vsnprintf(s+s_len, len+1, fmt, ap2); - va_end(ap2); - _talloc_set_name_const(s, s); - - return s; -} - -/* - Realloc @p s to append the formatted result of @p fmt and return @p - s, which may have moved. Good for gradually accumulating output - into a string buffer. - */ -char *talloc_asprintf_append(char *s, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - s = talloc_vasprintf_append(s, fmt, ap); - va_end(ap); - return s; -} - -/* - alloc an array, checking for integer overflow in the array size -*/ -void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return _talloc_named_const(ctx, el_size * count, name); -} - -/* - alloc an zero array, checking for integer overflow in the array size -*/ -void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return _talloc_zero(ctx, el_size * count, name); -} - -/* - realloc an array, checking for integer overflow in the array size -*/ -void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return _talloc_realloc(ctx, ptr, el_size * count, name); -} - -/* - a function version of talloc_realloc(), so it can be passed as a function pointer - to libraries that want a realloc function (a realloc function encapsulates - all the basic capabilities of an allocation library, which is why this is useful) -*/ -void *talloc_realloc_fn(const void *context, void *ptr, size_t size) -{ - return _talloc_realloc(context, ptr, size, NULL); -} - - -static int talloc_autofree_destructor(void *ptr) -{ - autofree_context = NULL; - return 0; -} - -static void talloc_autofree(void) -{ - _talloc_free(autofree_context); -} - -/* - return a context which will be auto-freed on exit - this is useful for reducing the noise in leak reports -*/ -void *talloc_autofree_context(void) -{ - if (autofree_context == NULL) { - autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); - talloc_set_destructor(autofree_context, talloc_autofree_destructor); - atexit(talloc_autofree); - } - return autofree_context; -} - -size_t talloc_get_size(const void *context) -{ - struct talloc_chunk *tc; - - if (context == NULL) - return 0; - - tc = talloc_chunk_from_ptr(context); - - return tc->size; -} - -/* - find a parent of this context that has the given name, if any -*/ -void *talloc_find_parent_byname(const void *context, const char *name) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - return NULL; - } - - tc = talloc_chunk_from_ptr(context); - while (tc) { - if (tc->name && strcmp(tc->name, name) == 0) { - return TC_PTR_FROM_CHUNK(tc); - } - while (tc && tc->prev) tc = tc->prev; - if (tc) { - tc = tc->parent; - } - } - return NULL; -} - -/* - show the parentage of a context -*/ -void talloc_show_parents(const void *context, FILE *file) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - fprintf(file, "talloc no parents for NULL\n"); - return; - } - - tc = talloc_chunk_from_ptr(context); - fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); - while (tc) { - fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); - while (tc && tc->prev) tc = tc->prev; - if (tc) { - tc = tc->parent; - } - } - fflush(file); -} - -/* - return 1 if ptr is a parent of context -*/ -int talloc_is_parent(const void *context, const void *ptr) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - return 0; - } - - tc = talloc_chunk_from_ptr(context); - while (tc) { - if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; - while (tc && tc->prev) tc = tc->prev; - if (tc) { - tc = tc->parent; - } - } - return 0; -} diff --git a/talloc/talloc.h b/talloc/talloc.h deleted file mode 100644 index 5566a040..00000000 --- a/talloc/talloc.h +++ /dev/null @@ -1,951 +0,0 @@ -#ifndef CCAN_TALLOC_H -#define CCAN_TALLOC_H -/* - Copyright (C) Andrew Tridgell 2004-2005 - Copyright (C) Stefan Metzmacher 2006 - - ** NOTE! The following LGPL license applies to the talloc - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include "config.h" -#include "typesafe_cb/typesafe_cb.h" - -/* - this uses a little trick to allow __LINE__ to be stringified -*/ -#ifndef __location__ -#define __TALLOC_STRING_LINE1__(s) #s -#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) -#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) -#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ -#endif - -#if HAVE_ATTRIBUTE_PRINTF -/** Use gcc attribute to check printf fns. a1 is the 1-based index of - * the parameter containing the format, and a2 the index of the first - * argument. Note that some gcc 2.x versions don't handle this - * properly **/ -#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) -#else -#define PRINTF_ATTRIBUTE(a1, a2) -#endif - -/* try to make talloc_set_destructor() and talloc_steal() type safe, - if we have a recent gcc */ -#if HAVE_TYPEOF -#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) -#else -#define _TALLOC_TYPEOF(ptr) void * -#endif - -#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) - -/** - * talloc - allocate dynamic memory for a type - * @ctx: context to be parent of this allocation, or NULL. - * @type: the type to be allocated. - * - * The talloc() macro is the core of the talloc library. It takes a memory - * context and a type, and returns a pointer to a new area of memory of the - * given type. - * - * The returned pointer is itself a talloc context, so you can use it as the - * context argument to more calls to talloc if you wish. - * - * The returned pointer is a "child" of @ctx. This means that if you - * talloc_free() @ctx then the new child disappears as well. Alternatively you - * can free just the child. - * - * @ctx can be NULL, in which case a new top level context is created. - * - * Example: - * unsigned int *a, *b; - * a = talloc(NULL, unsigned int); - * b = talloc(a, unsigned int); - * - * See Also: - * talloc_zero, talloc_array, talloc_steal, talloc_free. - */ -#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) - -/** - * talloc_free - free talloc'ed memory and its children - * @ptr: the talloced pointer to free - * - * The talloc_free() function frees a piece of talloc memory, and all its - * children. You can call talloc_free() on any pointer returned by talloc(). - * - * The return value of talloc_free() indicates success or failure, with 0 - * returned for success and -1 for failure. The only possible failure condition - * is if the pointer had a destructor attached to it and the destructor - * returned -1. See talloc_set_destructor() for details on destructors. - * errno will be preserved unless the talloc_free fails. - * - * If this pointer has an additional parent when talloc_free() is called then - * the memory is not actually released, but instead the most recently - * established parent is destroyed. See talloc_reference() for details on - * establishing additional parents. - * - * For more control on which parent is removed, see talloc_unlink(). - * - * talloc_free() operates recursively on its children. - * - * Example: - * unsigned int *a, *b; - * a = talloc(NULL, unsigned int); - * b = talloc(a, unsigned int); - * // Frees a and b - * talloc_free(a); - * - * See Also: - * talloc_set_destructor, talloc_unlink - */ -int talloc_free(void *ptr); - -/** - * talloc_set_destructor: set a destructor for when this pointer is freed - * @ptr: the talloc pointer to set the destructor on - * @destructor: the function to be called - * - * The function talloc_set_destructor() sets the "destructor" for the pointer - * @ptr. A destructor is a function that is called when the memory used by a - * pointer is about to be released. The destructor receives the pointer as an - * argument, and should return 0 for success and -1 for failure. - * - * The destructor can do anything it wants to, including freeing other pieces - * of memory. A common use for destructors is to clean up operating system - * resources (such as open file descriptors) contained in the structure the - * destructor is placed on. - * - * You can only place one destructor on a pointer. If you need more than one - * destructor then you can create a zero-length child of the pointer and place - * an additional destructor on that. - * - * To remove a destructor call talloc_set_destructor() with NULL for the - * destructor. - * - * If your destructor attempts to talloc_free() the pointer that it is the - * destructor for then talloc_free() will return -1 and the free will be - * ignored. This would be a pointless operation anyway, as the destructor is - * only called when the memory is just about to go away. - * - * Example: - * static int destroy_fd(int *fd) - * { - * close(*fd); - * return 0; - * } - * - * int *open_file(const char *filename) - * { - * int *fd = talloc(NULL, int); - * *fd = open(filename, O_RDONLY); - * if (*fd < 0) { - * talloc_free(fd); - * return NULL; - * } - * // Whenever they free this, we close the file. - * talloc_set_destructor(fd, destroy_fd); - * return fd; - * } - * - * See Also: - * talloc, talloc_free - */ -#define talloc_set_destructor(ptr, function) \ - _talloc_set_destructor((ptr), typesafe_cb(int, (function), (ptr))) - -/** - * talloc_zero - allocate zeroed dynamic memory for a type - * @ctx: context to be parent of this allocation, or NULL. - * @type: the type to be allocated. - * - * The talloc_zero() macro is equivalent to: - * - * ptr = talloc(ctx, type); - * if (ptr) memset(ptr, 0, sizeof(type)); - * - * Example: - * unsigned int *a, *b; - * a = talloc_zero(NULL, unsigned int); - * b = talloc_zero(a, unsigned int); - * - * See Also: - * talloc, talloc_zero_size, talloc_zero_array - */ -#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) - -/** - * talloc_array - allocate dynamic memory for an array of a given type - * @ctx: context to be parent of this allocation, or NULL. - * @type: the type to be allocated. - * @count: the number of elements to be allocated. - * - * The talloc_array() macro is a safe way of allocating an array. It is - * equivalent to: - * - * (type *)talloc_size(ctx, sizeof(type) * count); - * - * except that it provides integer overflow protection for the multiply, - * returning NULL if the multiply overflows. - * - * Example: - * unsigned int *a, *b; - * a = talloc_zero(NULL, unsigned int); - * b = talloc_array(a, unsigned int, 100); - * - * See Also: - * talloc, talloc_zero_array - */ -#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) - -/** - * talloc_size - allocate a particular size of memory - * @ctx: context to be parent of this allocation, or NULL. - * @size: the number of bytes to allocate - * - * The function talloc_size() should be used when you don't have a convenient - * type to pass to talloc(). Unlike talloc(), it is not type safe (as it - * returns a void *), so you are on your own for type checking. - * - * Best to use talloc() or talloc_array() instead. - * - * Example: - * void *mem = talloc_size(NULL, 100); - * - * See Also: - * talloc, talloc_array, talloc_zero_size - */ -#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) - -#ifdef HAVE_TYPEOF -/** - * talloc_steal - change/set the parent context of a talloc pointer - * @ctx: the new parent - * @ptr: the talloc pointer to reparent - * - * The talloc_steal() function changes the parent context of a talloc - * pointer. It is typically used when the context that the pointer is currently - * a child of is going to be freed and you wish to keep the memory for a longer - * time. - * - * The talloc_steal() function returns the pointer that you pass it. It does - * not have any failure modes. - * - * NOTE: It is possible to produce loops in the parent/child relationship if - * you are not careful with talloc_steal(). No guarantees are provided as to - * your sanity or the safety of your data if you do this. - * - * talloc_steal (new_ctx, NULL) will return NULL with no sideeffects. - * - * Example: - * unsigned int *a, *b; - * a = talloc(NULL, unsigned int); - * b = talloc(NULL, unsigned int); - * // Reparent b to a as if we'd done 'b = talloc(a, unsigned int)'. - * talloc_steal(a, b); - * - * See Also: - * talloc_reference - */ -#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) _talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); _talloc_steal_ret; }) /* this extremely strange macro is to avoid some braindamaged warning stupidity in gcc 4.1.x */ -#else -#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) -#endif /* HAVE_TYPEOF */ - -/** - * talloc_report_full - report all the memory used by a pointer and children. - * @ptr: the context to report on - * @f: the file to report to - * - * Recursively print the entire tree of memory referenced by the - * pointer. References in the tree are shown by giving the name of the pointer - * that is referenced. - * - * You can pass NULL for the pointer, in which case a report is printed for the - * top level memory context, but only if talloc_enable_null_tracking() has been - * called. - * - * Example: - * unsigned int *a, *b; - * a = talloc(NULL, unsigned int); - * b = talloc(a, unsigned int); - * fprintf(stderr, "Dumping memory tree for a:\n"); - * talloc_report_full(a, stderr); - * - * See Also: - * talloc_report - */ -void talloc_report_full(const void *ptr, FILE *f); - -/** - * talloc_reference - add an additional parent to a context - * @ctx: the additional parent - * @ptr: the talloc pointer - * - * The talloc_reference() function makes @ctx an additional parent of @ptr. - * - * The return value of talloc_reference() is always the original pointer @ptr, - * unless talloc ran out of memory in creating the reference in which case it - * will return NULL (each additional reference consumes around 48 bytes of - * memory on intel x86 platforms). - * - * If @ptr is NULL, then the function is a no-op, and simply returns NULL. - * - * After creating a reference you can free it in one of the following ways: - * - * - you can talloc_free() any parent of the original pointer. That will - * reduce the number of parents of this pointer by 1, and will cause this - * pointer to be freed if it runs out of parents. - * - * - you can talloc_free() the pointer itself. That will destroy the most - * recently established parent to the pointer and leave the pointer as a - * child of its current parent. - * - * For more control on which parent to remove, see talloc_unlink(). - * Example: - * unsigned int *a, *b, *c; - * a = talloc(NULL, unsigned int); - * b = talloc(NULL, unsigned int); - * c = talloc(a, unsigned int); - * // b also serves as a parent of c. - * talloc_reference(b, c); - */ -#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) - -/** - * talloc_unlink: remove a specific parent from a talloc pointer. - * @context: the parent to remove - * @ptr: the talloc pointer - * - * The talloc_unlink() function removes a specific parent from @ptr. The - * context passed must either be a context used in talloc_reference() with this - * pointer, or must be a direct parent of @ptr. - * - * Note that if the parent has already been removed using talloc_free() then - * this function will fail and will return -1. Likewise, if @ptr is NULL, - * then the function will make no modifications and return -1. - * - * Usually you can just use talloc_free() instead of talloc_unlink(), but - * sometimes it is useful to have the additional control on which parent is - * removed. - * Example: - * unsigned int *a, *b, *c; - * a = talloc(NULL, unsigned int); - * b = talloc(NULL, unsigned int); - * c = talloc(a, unsigned int); - * // b also serves as a parent of c. - * talloc_reference(b, c); - * talloc_unlink(b, c); - */ -int talloc_unlink(const void *context, void *ptr); - -/** - * talloc_report - print a summary of memory used by a pointer - * - * The talloc_report() function prints a summary report of all memory - * used by @ptr. One line of report is printed for each immediate child of - * @ptr, showing the total memory and number of blocks used by that child. - * - * You can pass NULL for the pointer, in which case a report is printed for the - * top level memory context, but only if talloc_enable_null_tracking() has been - * called. - * - * Example: - * unsigned int *a, *b; - * a = talloc(NULL, unsigned int); - * b = talloc(a, unsigned int); - * fprintf(stderr, "Summary of memory tree for a:\n"); - * talloc_report(a, stderr); - * - * See Also: - * talloc_report_full - */ -void talloc_report(const void *ptr, FILE *f); - -/** - * talloc_ptrtype - allocate a size of memory suitable for this pointer - * @ctx: context to be parent of this allocation, or NULL. - * @ptr: the pointer whose type we are to allocate - * - * The talloc_ptrtype() macro should be used when you have a pointer and - * want to allocate memory to point at with this pointer. When compiling - * with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() - * and talloc_get_name() will return the current location in the source file. - * and not the type. - * - * Example: - * unsigned int *a = talloc_ptrtype(NULL, a); - */ -#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) - -/** - * talloc_free_children - free talloc'ed memory's children only - * @ptr: the talloced pointer whose children we want to free - * - * talloc_free_children() walks along the list of all children of a talloc - * context @ptr and talloc_free()s only the children, not the context itself. - * Example: - * unsigned int *a, *b; - * a = talloc(NULL, unsigned int); - * b = talloc(a, unsigned int); - * // Frees b - * talloc_free_children(a); - */ -void talloc_free_children(void *ptr); - -/** - * talloc_new - create a new context - * @ctx: the context to use as a parent. - * - * This is a utility macro that creates a new memory context hanging off an - * exiting context, automatically naming it "talloc_new: __location__" where - * __location__ is the source line it is called from. It is particularly useful - * for creating a new temporary working context. - */ -#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) - -/** - * talloc_zero_size - allocate a particular size of zeroed memory - * - * The talloc_zero_size() function is useful when you don't have a known type. - */ -#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) - -/** - * talloc_zero_array - allocate an array of zeroed types - * @ctx: context to be parent of this allocation, or NULL. - * @type: the type to be allocated. - * @count: the number of elements to be allocated. - * - * Just like talloc_array, but zeroes the memory. - */ -#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) - -/** - * talloc_zero_array - allocate an array of zeroed types - * @ctx: context to be parent of this allocation, or NULL. - * @type: the type to be allocated. - * @count: the number of elements to be allocated. - * - * Just like talloc_array, but zeroes the memory. - */ -#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) - -/** - * talloc_array_ptrtype - allocate an array of memory suitable for this pointer - * @ctx: context to be parent of this allocation, or NULL. - * @ptr: the pointer whose type we are to allocate - * @count: the number of elements for the array - * - * Like talloc_ptrtype(), except it allocates an array. - */ -#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) - -/** - * talloc_realloc - resize a talloc array - * @ctx: the parent to assign (if p is NULL) - * @p: the memory to reallocate - * @type: the type of the object to allocate - * @count: the number of objects to reallocate - * - * The talloc_realloc() macro changes the size of a talloc pointer. The "count" - * argument is the number of elements of type "type" that you want the - * resulting pointer to hold. - * - * talloc_realloc() has the following equivalences: - * - * talloc_realloc(context, NULL, type, 1) ==> talloc(context, type); - * talloc_realloc(context, NULL, type, N) ==> talloc_array(context, type, N); - * talloc_realloc(context, ptr, type, 0) ==> talloc_free(ptr); - * - * The "context" argument is only used if "ptr" is NULL, otherwise it is - * ignored. - * - * talloc_realloc() returns the new pointer, or NULL on failure. The call will - * fail either due to a lack of memory, or because the pointer has more than - * one parent (see talloc_reference()). - */ -#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) - -/** - * talloc_realloc_size - resize talloc memory - * @ctx: the parent to assign (if p is NULL) - * @ptr: the memory to reallocate - * @size: the new size of memory. - * - * The talloc_realloc_size() function is useful when the type is not known so - * the typesafe talloc_realloc() cannot be used. - */ -#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) - -/** - * talloc_strdup - duplicate a string - * @ctx: the talloc context for the new string - * @p: the string to copy - * - * The talloc_strdup() function is equivalent to: - * - * ptr = talloc_size(ctx, strlen(p)+1); - * if (ptr) memcpy(ptr, p, strlen(p)+1); - * - * This functions sets the name of the new pointer to the passed string. This - * is equivalent to: - * - * talloc_set_name_const(ptr, ptr) - */ -char *talloc_strdup(const void *t, const char *p); - -/** - * talloc_strndup - duplicate a limited length of a string - * @ctx: the talloc context for the new string - * @p: the string to copy - * @n: the maximum length of the returned string. - * - * The talloc_strndup() function is the talloc equivalent of the C library - * function strndup(): the result will be truncated to @n characters before - * the nul terminator. - * - * This functions sets the name of the new pointer to the passed string. This - * is equivalent to: - * - * talloc_set_name_const(ptr, ptr) - */ -char *talloc_strndup(const void *t, const char *p, size_t n); - -/** - * talloc_memdup - duplicate some talloc memory - * - * The talloc_memdup() function is equivalent to: - * - * ptr = talloc_size(ctx, size); - * if (ptr) memcpy(ptr, p, size); - */ -#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) - -/** - * talloc_asprintf - sprintf into a talloc buffer. - * @t: The context to allocate the buffer from - * @fmt: printf-style format for the buffer. - * - * The talloc_asprintf() function is the talloc equivalent of the C library - * function asprintf(). - * - * This functions sets the name of the new pointer to the new string. This is - * equivalent to: - * - * talloc_set_name_const(ptr, ptr) - */ -char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); - -/** - * talloc_append_string - concatenate onto a tallocated string - * @orig: the tallocated string to append to - * @append: the string to add, or NULL to add nothing. - * - * The talloc_append_string() function appends the given formatted string to - * the given string. - * - * This function sets the name of the new pointer to the new string. This is - * equivalent to: - * - * talloc_set_name_const(ptr, ptr) - */ -char *talloc_append_string(char *orig, const char *append); - -/** - * talloc_asprintf_append - sprintf onto the end of a talloc buffer. - * @s: The tallocated string buffer - * @fmt: printf-style format to append to the buffer. - * - * The talloc_asprintf_append() function appends the given formatted string to - * the given string. - * - * This functions sets the name of the new pointer to the new string. This is - * equivalent to: - * talloc_set_name_const(ptr, ptr) - */ -char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); - -/** - * talloc_vasprintf - vsprintf into a talloc buffer. - * @t: The context to allocate the buffer from - * @fmt: printf-style format for the buffer - * @ap: va_list arguments - * - * The talloc_vasprintf() function is the talloc equivalent of the C library - * function vasprintf() - * - * This functions sets the name of the new pointer to the new string. This is - * equivalent to: - * - * talloc_set_name_const(ptr, ptr) - */ -char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); - -/** - * talloc_vasprintf_append - sprintf onto the end of a talloc buffer. - * @t: The context to allocate the buffer from - * @fmt: printf-style format for the buffer - * @ap: va_list arguments - * - * The talloc_vasprintf_append() function is equivalent to - * talloc_asprintf_append(), except it takes a va_list. - */ -char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); - -/** - * talloc_set_type - force the name of a pointer to a particular type - * @ptr: the talloc pointer - * @type: the type whose name to set the ptr name to. - * - * This macro allows you to force the name of a pointer to be a particular - * type. This can be used in conjunction with talloc_get_type() to do type - * checking on void* pointers. - * - * It is equivalent to this: - * talloc_set_name_const(ptr, #type) - */ -#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) - -/** - * talloc_get_type - convert a talloced pointer with typechecking - * @ptr: the talloc pointer - * @type: the type which we expect the talloced pointer to be. - * - * This macro allows you to do type checking on talloc pointers. It is - * particularly useful for void* private pointers. It is equivalent to this: - * - * (type *)talloc_check_name(ptr, #type) - */ -#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) - -/** - * talloc_find_parent_byname - find a talloc parent by type - * @ptr: the talloc pointer - * @type: the type we're looking for - * - * Find a parent memory context of the current context that has the given - * name. This can be very useful in complex programs where it may be difficult - * to pass all information down to the level you need, but you know the - * structure you want is a parent of another context. - */ -#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) - -/** - * talloc_increase_ref_count - hold a reference to a talloc pointer - * @ptr: the talloc pointer - * - * The talloc_increase_ref_count(ptr) function is exactly equivalent to: - * - * talloc_reference(NULL, ptr); - * - * You can use either syntax, depending on which you think is clearer in your - * code. - * - * It returns 0 on success and -1 on failure. - */ -int talloc_increase_ref_count(const void *ptr); - -/** - * talloc_set_name - set the name for a talloc pointer - * @ptr: the talloc pointer - * @fmt: the printf-style format string for the name - * - * Each talloc pointer has a "name". The name is used principally for debugging - * purposes, although it is also possible to set and get the name on a pointer - * in as a way of "marking" pointers in your code. - * - * The main use for names on pointer is for "talloc reports". See - * talloc_report() and talloc_report_full() for details. Also see - * talloc_enable_leak_report() and talloc_enable_leak_report_full(). - * - * The talloc_set_name() function allocates memory as a child of the - * pointer. It is logically equivalent to: - * talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); - * - * Note that multiple calls to talloc_set_name() will allocate more memory - * without releasing the name. All of the memory is released when the ptr is - * freed using talloc_free(). - */ -const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); - -/** - * talloc_set_name_const - set a talloc pointer name to a string constant - * @ptr: the talloc pointer to name - * @name: the strucng constant. - * - * The function talloc_set_name_const() is just like talloc_set_name(), but it - * takes a string constant, and is much faster. It is extensively used by the - * "auto naming" macros, such as talloc(). - * - * This function does not allocate any memory. It just copies the supplied - * pointer into the internal representation of the talloc ptr. This means you - * must not pass a name pointer to memory that will disappear before the ptr is - * freed with talloc_free(). - */ -void talloc_set_name_const(const void *ptr, const char *name); - -/** - * talloc_named - create a specifically-named talloc pointer - * @context: the parent context for the allocation - * @size: the size to allocate - * @fmt: the printf-style format for the name - * - * The talloc_named() function creates a named talloc pointer. It is equivalent - * to: - * - * ptr = talloc_size(context, size); - * talloc_set_name(ptr, fmt, ....); - */ -void *talloc_named(const void *context, size_t size, - const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); - -/** - * talloc_named_const - create a specifically-named talloc pointer - * @context: the parent context for the allocation - * @size: the size to allocate - * @name: the string constant to use as the name - * - * This is equivalent to: - * - * ptr = talloc_size(context, size); - * talloc_set_name_const(ptr, name); - */ -void *talloc_named_const(const void *context, size_t size, const char *name); - -/** - * talloc_get_name - get the name of a talloc pointer - * @ptr: the talloc pointer - * - * This returns the current name for the given talloc pointer. See - * talloc_set_name() for details. - */ -const char *talloc_get_name(const void *ptr); - -/** - * talloc_check_name - check if a pointer has the specified name - * @ptr: the talloc pointer - * @name: the name to compare with the pointer's name - * - * This function checks if a pointer has the specified name. If it does then - * the pointer is returned. It it doesn't then NULL is returned. - */ -void *talloc_check_name(const void *ptr, const char *name); - -/** - * talloc_init - create a top-level context of particular name - * @fmt: the printf-style format of the name - * - * This function creates a zero length named talloc context as a top level - * context. It is equivalent to: - * - * talloc_named(NULL, 0, fmt, ...); - */ -void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); - -/** - * talloc_total_size - get the bytes used by the pointer and its children - * @ptr: the talloc pointer - * - * The talloc_total_size() function returns the total size in bytes used by - * this pointer and all child pointers. Mostly useful for debugging. - * - * Passing NULL is allowed, but it will only give a meaningful result if - * talloc_enable_leak_report() or talloc_enable_leak_report_full() has been - * called. - */ -size_t talloc_total_size(const void *ptr); - -/** - * talloc_total_blocks - get the number of allocations for the pointer - * @ptr: the talloc pointer - * - * The talloc_total_blocks() function returns the total allocations used by - * this pointer and all child pointers. Mostly useful for debugging. For - * example, a pointer with no children will return "1". - * - * Passing NULL is allowed, but it will only give a meaningful result if - * talloc_enable_leak_report() or talloc_enable_leak_report_full() has been - * called. - */ -size_t talloc_total_blocks(const void *ptr); - -/** - * talloc_report_depth_cb - walk the entire talloc tree under a talloc pointer - * @ptr: the talloc pointer to recurse under - * @depth: the current depth of traversal - * @max_depth: maximum depth to traverse, or -1 for no maximum - * @callback: the function to call on each pointer - * @private_data: pointer to hand to @callback. - * - * This provides a more flexible reports than talloc_report(). It will - * recursively call the callback for the entire tree of memory referenced by - * the pointer. References in the tree are passed with is_ref = 1 and the - * pointer that is referenced. - * - * You can pass NULL for the pointer, in which case a report is printed for the - * top level memory context, but only if talloc_enable_leak_report() or - * talloc_enable_leak_report_full() has been called. - * - * The recursion is stopped when depth >= max_depth. max_depth = -1 means only - * stop at leaf nodes. - */ -void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, - void (*callback)(const void *ptr, - int depth, int max_depth, - int is_ref, - void *private_data), - void *private_data); - -/** - * talloc_report_depth_file - report talloc usage to a maximum depth - * @ptr: the talloc pointer to recurse under - * @depth: the current depth of traversal - * @max_depth: maximum depth to traverse, or -1 for no maximum - * @f: the file to report to - * - * This provides a more flexible reports than talloc_report(). It will let you - * specify the depth and max_depth. - */ -void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); - -/** - * talloc_enable_null_tracking - enable tracking of top-level tallocs - * - * This enables tracking of the NULL memory context without enabling leak - * reporting on exit. Useful for when you want to do your own leak reporting - * call via talloc_report_null_full(); - */ -void talloc_enable_null_tracking(void); - -/** - * talloc_disable_null_tracking - enable tracking of top-level tallocs - * - * This disables tracking of the NULL memory context. - */ -void talloc_disable_null_tracking(void); - -/** - * talloc_enable_leak_report - call talloc_report on program exit - * - * This enables calling of talloc_report(NULL, stderr) when the program - * exits. In Samba4 this is enabled by using the --leak-report command line - * option. - * - * For it to be useful, this function must be called before any other talloc - * function as it establishes a "null context" that acts as the top of the - * tree. If you don't call this function first then passing NULL to - * talloc_report() or talloc_report_full() won't give you the full tree - * printout. - * - * Here is a typical talloc report: - * - * talloc report on 'null_context' (total 267 bytes in 15 blocks) - * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks - * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks - * iconv(UTF8,CP850) contains 42 bytes in 2 blocks - * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks - * iconv(CP850,UTF8) contains 42 bytes in 2 blocks - * iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks - * iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks - */ -void talloc_enable_leak_report(void); - -/** - * talloc_enable_leak_report - call talloc_report_full on program exit - * - * This enables calling of talloc_report_full(NULL, stderr) when the program - * exits. In Samba4 this is enabled by using the --leak-report-full command - * line option. - * - * For it to be useful, this function must be called before any other talloc - * function as it establishes a "null context" that acts as the top of the - * tree. If you don't call this function first then passing NULL to - * talloc_report() or talloc_report_full() won't give you the full tree - * printout. - * - * Here is a typical full report: - * - * full talloc report on 'root' (total 18 bytes in 8 blocks) - * p1 contains 18 bytes in 7 blocks (ref 0) - * r1 contains 13 bytes in 2 blocks (ref 0) - * reference to: p2 - * p2 contains 1 bytes in 1 blocks (ref 1) - * x3 contains 1 bytes in 1 blocks (ref 0) - * x2 contains 1 bytes in 1 blocks (ref 0) - * x1 contains 1 bytes in 1 blocks (ref 0) - */ -void talloc_enable_leak_report_full(void); - -/** - * talloc_autofree_context - a context which will be freed at exit - * - * This is a handy utility function that returns a talloc context which will be - * automatically freed on program exit. This can be used to reduce the noise in - * memory leak reports. - */ -void *talloc_autofree_context(void); - -/** - * talloc_get_size - get the size of an allocation - * @ctx: the talloc pointer whose allocation to measure. - * - * This function lets you know the amount of memory alloced so far by this - * context. It does NOT account for subcontext memory. This can be used to - * calculate the size of an array. - */ -size_t talloc_get_size(const void *ctx); - -/** - * talloc_find_parent_byname - find a parent of this context with this name - * @ctx: the context whose ancestors to search - * @name: the name to look for - * - * Find a parent memory context of @ctx that has the given name. This can be - * very useful in complex programs where it may be difficult to pass all - * information down to the level you need, but you know the structure you want - * is a parent of another context. - */ -void *talloc_find_parent_byname(const void *ctx, const char *name); - -/* The following definitions come from talloc.c */ -void *_talloc(const void *context, size_t size); -void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); -size_t talloc_reference_count(const void *ptr); -void *_talloc_reference(const void *context, const void *ptr); - -void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); -void *talloc_parent(const void *ptr); -const char *talloc_parent_name(const void *ptr); -void *_talloc_steal(const void *new_ctx, const void *ptr); -void *_talloc_move(const void *new_ctx, const void *pptr); -void *_talloc_zero(const void *ctx, size_t size, const char *name); -void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); -void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); -void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); -void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); -void *talloc_realloc_fn(const void *context, void *ptr, size_t size); -void talloc_show_parents(const void *context, FILE *file); -int talloc_is_parent(const void *context, const void *ptr); - -#endif /* CCAN_TALLOC_H */ diff --git a/talloc/test/run.c b/talloc/test/run.c deleted file mode 100644 index 7369186c..00000000 --- a/talloc/test/run.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - local testing of talloc routines. - - Copyright (C) Andrew Tridgell 2004 - Converted to ccan tests by Rusty Russell 2008 - - ** NOTE! The following LGPL license applies to the talloc - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "talloc/talloc.c" -#include -#include "tap/tap.h" - -#define torture_assert(test, expr, str) \ - ok(expr, "failure: %s [\n%s: Expression %s failed: %s\n]\n", \ - test, __location__, #expr, str) - -#define torture_assert_str_equal(test, arg1, arg2, desc) \ - ok(strcmp(arg1, arg2) == 0, \ - "failure: %s [\n%s: Expected %s, got %s: %s\n]\n", \ - test, __location__, arg1, arg2, desc) - -#define CHECK_SIZE(test, ptr, tsize) \ - ok(talloc_total_size(ptr) == (tsize), \ - "failed: %s [\nwrong '%s' tree size: got %u expected %u\n]\n", \ - test, #ptr, \ - (unsigned)talloc_total_size(ptr), \ - (unsigned)tsize) - -#define CHECK_BLOCKS(test, ptr, tblocks) \ - ok(talloc_total_blocks(ptr) == (tblocks), \ - "failed: %s [\nwrong '%s' tree blocks: got %u expected %u\n]\n", \ - test, #ptr, \ - (unsigned)talloc_total_blocks(ptr), \ - (unsigned)tblocks) - -#define CHECK_PARENT(test, ptr, parent) \ - ok(talloc_parent(ptr) == (parent), \ - "failed: %s [\n'%s' has wrong parent: got %p expected %p\n]\n", \ - test, #ptr, \ - talloc_parent(ptr), \ - (parent)) - -/* - test references -*/ -static bool test_ref1(void) -{ - void *root, *p1, *p2, *ref, *r1; - - root = talloc_named_const(NULL, 0, "root"); - p1 = talloc_named_const(root, 1, "p1"); - p2 = talloc_named_const(p1, 1, "p2"); - talloc_named_const(p1, 1, "x1"); - talloc_named_const(p1, 2, "x2"); - talloc_named_const(p1, 3, "x3"); - - r1 = talloc_named_const(root, 1, "r1"); - ref = talloc_reference(r1, p2); - - CHECK_BLOCKS("ref1", p1, 5); - CHECK_BLOCKS("ref1", p2, 1); - CHECK_BLOCKS("ref1", r1, 2); - - talloc_free(p2); - - CHECK_BLOCKS("ref1", p1, 5); - CHECK_BLOCKS("ref1", p2, 1); - CHECK_BLOCKS("ref1", r1, 1); - - talloc_free(p1); - - CHECK_BLOCKS("ref1", r1, 1); - - talloc_free(r1); - - if (talloc_reference(root, NULL)) { - return false; - } - - CHECK_BLOCKS("ref1", root, 1); - - CHECK_SIZE("ref1", root, 0); - - talloc_free(root); - return true; -} - -/* - test references -*/ -static bool test_ref2(void) -{ - void *root, *p1, *p2, *ref, *r1; - - root = talloc_named_const(NULL, 0, "root"); - p1 = talloc_named_const(root, 1, "p1"); - talloc_named_const(p1, 1, "x1"); - talloc_named_const(p1, 1, "x2"); - talloc_named_const(p1, 1, "x3"); - p2 = talloc_named_const(p1, 1, "p2"); - - r1 = talloc_named_const(root, 1, "r1"); - ref = talloc_reference(r1, p2); - - CHECK_BLOCKS("ref2", p1, 5); - CHECK_BLOCKS("ref2", p2, 1); - CHECK_BLOCKS("ref2", r1, 2); - - talloc_free(ref); - - CHECK_BLOCKS("ref2", p1, 5); - CHECK_BLOCKS("ref2", p2, 1); - CHECK_BLOCKS("ref2", r1, 1); - - talloc_free(p2); - - CHECK_BLOCKS("ref2", p1, 4); - CHECK_BLOCKS("ref2", r1, 1); - - talloc_free(p1); - - CHECK_BLOCKS("ref2", r1, 1); - - talloc_free(r1); - - CHECK_SIZE("ref2", root, 0); - - talloc_free(root); - return true; -} - -/* - test references -*/ -static bool test_ref3(void) -{ - void *root, *p1, *p2, *ref, *r1; - - root = talloc_named_const(NULL, 0, "root"); - p1 = talloc_named_const(root, 1, "p1"); - p2 = talloc_named_const(root, 1, "p2"); - r1 = talloc_named_const(p1, 1, "r1"); - ref = talloc_reference(p2, r1); - - CHECK_BLOCKS("ref3", p1, 2); - CHECK_BLOCKS("ref3", p2, 2); - CHECK_BLOCKS("ref3", r1, 1); - - talloc_free(p1); - - CHECK_BLOCKS("ref3", p2, 2); - CHECK_BLOCKS("ref3", r1, 1); - - talloc_free(p2); - - CHECK_SIZE("ref3", root, 0); - - talloc_free(root); - - return true; -} - -/* - test references -*/ -static bool test_ref4(void) -{ - void *root, *p1, *p2, *ref, *r1; - - root = talloc_named_const(NULL, 0, "root"); - p1 = talloc_named_const(root, 1, "p1"); - talloc_named_const(p1, 1, "x1"); - talloc_named_const(p1, 1, "x2"); - talloc_named_const(p1, 1, "x3"); - p2 = talloc_named_const(p1, 1, "p2"); - - r1 = talloc_named_const(root, 1, "r1"); - ref = talloc_reference(r1, p2); - - CHECK_BLOCKS("ref4", p1, 5); - CHECK_BLOCKS("ref4", p2, 1); - CHECK_BLOCKS("ref4", r1, 2); - - talloc_free(r1); - - CHECK_BLOCKS("ref4", p1, 5); - CHECK_BLOCKS("ref4", p2, 1); - - talloc_free(p2); - - CHECK_BLOCKS("ref4", p1, 4); - - talloc_free(p1); - - CHECK_SIZE("ref4", root, 0); - - talloc_free(root); - - return true; -} - - -/* - test references -*/ -static bool test_unlink1(void) -{ - void *root, *p1, *p2, *ref, *r1; - - root = talloc_named_const(NULL, 0, "root"); - p1 = talloc_named_const(root, 1, "p1"); - talloc_named_const(p1, 1, "x1"); - talloc_named_const(p1, 1, "x2"); - talloc_named_const(p1, 1, "x3"); - p2 = talloc_named_const(p1, 1, "p2"); - - r1 = talloc_named_const(p1, 1, "r1"); - ref = talloc_reference(r1, p2); - - CHECK_BLOCKS("unlink", p1, 7); - CHECK_BLOCKS("unlink", p2, 1); - CHECK_BLOCKS("unlink", r1, 2); - - talloc_unlink(r1, p2); - - CHECK_BLOCKS("unlink", p1, 6); - CHECK_BLOCKS("unlink", p2, 1); - CHECK_BLOCKS("unlink", r1, 1); - - talloc_free(p1); - - CHECK_SIZE("unlink", root, 0); - - talloc_free(root); - - return true; -} - -static int fail_destructor(void *ptr) -{ - return -1; -} - -/* - miscellaneous tests to try to get a higher test coverage percentage -*/ -static bool test_misc(void) -{ - void *root, *p1; - char *p2; - double *d; - const char *name; - - root = talloc_new(NULL); - - p1 = talloc_size(root, 0x7fffffff); - torture_assert("misc", !p1, "failed: large talloc allowed\n"); - - p1 = talloc_strdup(root, "foo"); - talloc_increase_ref_count(p1); - talloc_increase_ref_count(p1); - talloc_increase_ref_count(p1); - CHECK_BLOCKS("misc", p1, 1); - CHECK_BLOCKS("misc", root, 2); - talloc_free(p1); - CHECK_BLOCKS("misc", p1, 1); - CHECK_BLOCKS("misc", root, 2); - talloc_unlink(NULL, p1); - CHECK_BLOCKS("misc", p1, 1); - CHECK_BLOCKS("misc", root, 2); - p2 = talloc_strdup(p1, "foo"); - torture_assert("misc", talloc_unlink(root, p2) == -1, - "failed: talloc_unlink() of non-reference context should return -1\n"); - torture_assert("misc", talloc_unlink(p1, p2) == 0, - "failed: talloc_unlink() of parent should succeed\n"); - talloc_free(p1); - CHECK_BLOCKS("misc", p1, 1); - CHECK_BLOCKS("misc", root, 2); - - name = talloc_set_name(p1, "my name is %s", "foo"); - torture_assert_str_equal("misc", talloc_get_name(p1), "my name is foo", - "failed: wrong name after talloc_set_name(my name is foo)"); - CHECK_BLOCKS("misc", p1, 2); - CHECK_BLOCKS("misc", root, 3); - - talloc_set_name_const(p1, NULL); - torture_assert_str_equal ("misc", talloc_get_name(p1), "UNNAMED", - "failed: wrong name after talloc_set_name(NULL)"); - CHECK_BLOCKS("misc", p1, 2); - CHECK_BLOCKS("misc", root, 3); - - torture_assert("misc", talloc_free(NULL) == -1, - "talloc_free(NULL) should give -1\n"); - - talloc_set_destructor(p1, fail_destructor); - torture_assert("misc", talloc_free(p1) == -1, - "Failed destructor should cause talloc_free to fail\n"); - talloc_set_destructor(p1, NULL); - - - p2 = (char *)talloc_zero_size(p1, 20); - torture_assert("misc", p2[19] == 0, "Failed to give zero memory\n"); - talloc_free(p2); - - torture_assert("misc", talloc_strdup(root, NULL) == NULL, - "failed: strdup on NULL should give NULL\n"); - - p2 = talloc_strndup(p1, "foo", 2); - torture_assert("misc", strcmp("fo", p2) == 0, - "strndup doesn't work\n"); - p2 = talloc_asprintf_append(p2, "o%c", 'd'); - torture_assert("misc", strcmp("food", p2) == 0, - "talloc_asprintf_append doesn't work\n"); - CHECK_BLOCKS("misc", p2, 1); - CHECK_BLOCKS("misc", p1, 3); - - p2 = talloc_asprintf_append(NULL, "hello %s", "world"); - torture_assert("misc", strcmp("hello world", p2) == 0, - "talloc_asprintf_append doesn't work\n"); - CHECK_BLOCKS("misc", p2, 1); - CHECK_BLOCKS("misc", p1, 3); - talloc_free(p2); - - d = talloc_array(p1, double, 0x20000000); - torture_assert("misc", !d, "failed: integer overflow not detected\n"); - - d = talloc_realloc(p1, d, double, 0x20000000); - torture_assert("misc", !d, "failed: integer overflow not detected\n"); - - talloc_free(p1); - CHECK_BLOCKS("misc", root, 1); - - p1 = talloc_named(root, 100, "%d bytes", 100); - CHECK_BLOCKS("misc", p1, 2); - CHECK_BLOCKS("misc", root, 3); - talloc_unlink(root, p1); - - p1 = talloc_init("%d bytes", 200); - p2 = talloc_asprintf(p1, "my test '%s'", "string"); - torture_assert_str_equal("misc", p2, "my test 'string'", - "failed: talloc_asprintf(\"my test '%%s'\", \"string\") gave: \"%s\""); - CHECK_BLOCKS("misc", p1, 3); - CHECK_SIZE("misc", p2, 17); - CHECK_BLOCKS("misc", root, 1); - talloc_unlink(NULL, p1); - - p1 = talloc_named_const(root, 10, "p1"); - p2 = (char *)talloc_named_const(root, 20, "p2"); - (void)talloc_reference(p1, p2); - talloc_unlink(root, p2); - CHECK_BLOCKS("misc", p2, 1); - CHECK_BLOCKS("misc", p1, 2); - CHECK_BLOCKS("misc", root, 3); - talloc_unlink(p1, p2); - talloc_unlink(root, p1); - - p1 = talloc_named_const(root, 10, "p1"); - p2 = (char *)talloc_named_const(root, 20, "p2"); - (void)talloc_reference(NULL, p2); - talloc_unlink(root, p2); - CHECK_BLOCKS("misc", p2, 1); - CHECK_BLOCKS("misc", p1, 1); - CHECK_BLOCKS("misc", root, 2); - talloc_unlink(NULL, p2); - talloc_unlink(root, p1); - - /* Test that talloc_unlink is a no-op */ - - torture_assert("misc", talloc_unlink(root, NULL) == -1, - "failed: talloc_unlink(root, NULL) == -1\n"); - - CHECK_SIZE("misc", root, 0); - - talloc_free(root); - - CHECK_SIZE("misc", NULL, 0); - - talloc_enable_leak_report(); - talloc_enable_leak_report_full(); - - return true; -} - - -/* - test realloc -*/ -static bool test_realloc(void) -{ - void *root, *p1, *p2; - - root = talloc_new(NULL); - - p1 = talloc_size(root, 10); - CHECK_SIZE("realloc", p1, 10); - - p1 = talloc_realloc_size(NULL, p1, 20); - CHECK_SIZE("realloc", p1, 20); - - talloc_new(p1); - - p2 = talloc_realloc_size(p1, NULL, 30); - - talloc_new(p1); - - p2 = talloc_realloc_size(p1, p2, 40); - - CHECK_SIZE("realloc", p2, 40); - CHECK_SIZE("realloc", root, 60); - CHECK_BLOCKS("realloc", p1, 4); - - p1 = talloc_realloc_size(NULL, p1, 20); - CHECK_SIZE("realloc", p1, 60); - - talloc_increase_ref_count(p2); - torture_assert("realloc", talloc_realloc_size(NULL, p2, 5) == NULL, - "failed: talloc_realloc() on a referenced pointer should fail\n"); - CHECK_BLOCKS("realloc", p1, 4); - - talloc_realloc_size(NULL, p2, 0); - talloc_realloc_size(NULL, p2, 0); - CHECK_BLOCKS("realloc", p1, 3); - - torture_assert("realloc", talloc_realloc_size(NULL, p1, 0x7fffffff) == NULL, - "failed: oversize talloc should fail\n"); - - talloc_realloc_size(NULL, p1, 0); - - CHECK_BLOCKS("realloc", root, 1); - CHECK_SIZE("realloc", root, 0); - - talloc_free(root); - - return true; -} - -/* - test realloc with a child -*/ -static bool test_realloc_child(void) -{ - void *root; - struct el2 { - const char *name; - } *el2; - struct el1 { - int count; - struct el2 **list, **list2, **list3; - } *el1; - - root = talloc_new(NULL); - - el1 = talloc(root, struct el1); - el1->list = talloc(el1, struct el2 *); - el1->list[0] = talloc(el1->list, struct el2); - el1->list[0]->name = talloc_strdup(el1->list[0], "testing"); - - el1->list2 = talloc(el1, struct el2 *); - el1->list2[0] = talloc(el1->list2, struct el2); - el1->list2[0]->name = talloc_strdup(el1->list2[0], "testing2"); - - el1->list3 = talloc(el1, struct el2 *); - el1->list3[0] = talloc(el1->list3, struct el2); - el1->list3[0]->name = talloc_strdup(el1->list3[0], "testing2"); - - el2 = talloc(el1->list, struct el2); - el2 = talloc(el1->list2, struct el2); - el2 = talloc(el1->list3, struct el2); - - el1->list = talloc_realloc(el1, el1->list, struct el2 *, 100); - el1->list2 = talloc_realloc(el1, el1->list2, struct el2 *, 200); - el1->list3 = talloc_realloc(el1, el1->list3, struct el2 *, 300); - - talloc_free(root); - - return true; -} - -/* - test type checking -*/ -static bool test_type(void) -{ - void *root; - struct el1 { - int count; - }; - struct el2 { - int count; - }; - struct el1 *el1; - - root = talloc_new(NULL); - - el1 = talloc(root, struct el1); - - el1->count = 1; - - torture_assert("type", talloc_get_type(el1, struct el1) == el1, - "type check failed on el1\n"); - torture_assert("type", talloc_get_type(el1, struct el2) == NULL, - "type check failed on el1 with el2\n"); - talloc_set_type(el1, struct el2); - torture_assert("type", talloc_get_type(el1, struct el2) == (struct el2 *)el1, - "type set failed on el1 with el2\n"); - - talloc_free(root); - - return true; -} - -/* - test steal -*/ -static bool test_steal(void) -{ - void *root, *p1, *p2; - - root = talloc_new(NULL); - - p1 = talloc_array(root, char, 10); - CHECK_SIZE("steal", p1, 10); - - p2 = talloc_realloc(root, NULL, char, 20); - CHECK_SIZE("steal", p1, 10); - CHECK_SIZE("steal", root, 30); - - torture_assert("steal", talloc_steal(p1, NULL) == NULL, - "failed: stealing NULL should give NULL\n"); - - torture_assert("steal", talloc_steal(p1, p1) == p1, - "failed: stealing to ourselves is a nop\n"); - CHECK_BLOCKS("steal", root, 3); - CHECK_SIZE("steal", root, 30); - - talloc_steal(NULL, p1); - talloc_steal(NULL, p2); - CHECK_BLOCKS("steal", root, 1); - CHECK_SIZE("steal", root, 0); - - talloc_free(p1); - talloc_steal(root, p2); - CHECK_BLOCKS("steal", root, 2); - CHECK_SIZE("steal", root, 20); - - talloc_free(p2); - - CHECK_BLOCKS("steal", root, 1); - CHECK_SIZE("steal", root, 0); - - talloc_free(root); - - p1 = talloc_size(NULL, 3); - CHECK_SIZE("steal", NULL, 3); - talloc_free(p1); - - return true; -} - -/* - test move -*/ -static bool test_move(void) -{ - void *root; - struct t_move { - char *p; - int *x; - } *t1, *t2; - - root = talloc_new(NULL); - - t1 = talloc(root, struct t_move); - t2 = talloc(root, struct t_move); - t1->p = talloc_strdup(t1, "foo"); - t1->x = talloc(t1, int); - *t1->x = 42; - - t2->p = talloc_move(t2, &t1->p); - t2->x = talloc_move(t2, &t1->x); - torture_assert("move", t1->p == NULL && t1->x == NULL && - strcmp(t2->p, "foo") == 0 && *t2->x == 42, - "talloc move failed"); - - talloc_free(root); - - return true; -} - -/* - test talloc_realloc_fn -*/ -static bool test_realloc_fn(void) -{ - void *root, *p1; - - root = talloc_new(NULL); - - p1 = talloc_realloc_fn(root, NULL, 10); - CHECK_BLOCKS("realloc_fn", root, 2); - CHECK_SIZE("realloc_fn", root, 10); - p1 = talloc_realloc_fn(root, p1, 20); - CHECK_BLOCKS("realloc_fn", root, 2); - CHECK_SIZE("realloc_fn", root, 20); - p1 = talloc_realloc_fn(root, p1, 0); - CHECK_BLOCKS("realloc_fn", root, 1); - CHECK_SIZE("realloc_fn", root, 0); - - talloc_free(root); - - return true; -} - - -static bool test_unref_reparent(void) -{ - void *root, *p1, *p2, *c1; - - root = talloc_named_const(NULL, 0, "root"); - p1 = talloc_named_const(root, 1, "orig parent"); - p2 = talloc_named_const(root, 1, "parent by reference"); - - c1 = talloc_named_const(p1, 1, "child"); - talloc_reference(p2, c1); - - CHECK_PARENT("unref_reparent", c1, p1); - - talloc_free(p1); - - CHECK_PARENT("unref_reparent", c1, p2); - - talloc_unlink(p2, c1); - - CHECK_SIZE("unref_reparent", root, 1); - - talloc_free(p2); - talloc_free(root); - - return true; -} - -static bool test_lifeless(void) -{ - void *top = talloc_new(NULL); - char *parent, *child; - void *child_owner = talloc_new(NULL); - - parent = talloc_strdup(top, "parent"); - child = talloc_strdup(parent, "child"); - (void)talloc_reference(child, parent); - (void)talloc_reference(child_owner, child); - talloc_unlink(top, parent); - talloc_free(child); - talloc_free(top); - talloc_free(child_owner); - talloc_free(child); - - return true; -} - -static int loop_destructor_count; - -static int test_loop_destructor(char *ptr) -{ - loop_destructor_count++; - return 0; -} - -static bool test_loop(void) -{ - void *top = talloc_new(NULL); - char *parent; - struct req1 { - char *req2, *req3; - } *req1; - - parent = talloc_strdup(top, "parent"); - req1 = talloc(parent, struct req1); - req1->req2 = talloc_strdup(req1, "req2"); - talloc_set_destructor(req1->req2, test_loop_destructor); - req1->req3 = talloc_strdup(req1, "req3"); - (void)talloc_reference(req1->req3, req1); - talloc_free(parent); - talloc_free(top); - - torture_assert("loop", loop_destructor_count == 1, - "FAILED TO FIRE LOOP DESTRUCTOR\n"); - loop_destructor_count = 0; - - return true; -} - -static int fail_destructor_str(char *ptr) -{ - return -1; -} - -static bool test_free_parent_deny_child(void) -{ - void *top = talloc_new(NULL); - char *level1; - char *level2; - char *level3; - - level1 = talloc_strdup(top, "level1"); - level2 = talloc_strdup(level1, "level2"); - level3 = talloc_strdup(level2, "level3"); - - talloc_set_destructor(level3, fail_destructor_str); - talloc_free(level1); - talloc_set_destructor(level3, NULL); - - CHECK_PARENT("free_parent_deny_child", level3, top); - - talloc_free(top); - - return true; -} - -static bool test_talloc_ptrtype(void) -{ - void *top = talloc_new(NULL); - struct struct1 { - int foo; - int bar; - } *s1, *s2, **s3, ***s4; - const char *location1; - const char *location2; - const char *location3; - const char *location4; - - s1 = talloc_ptrtype(top, s1);location1 = __location__; - - ok1(talloc_get_size(s1) == sizeof(struct struct1)); - - ok1(strcmp(location1, talloc_get_name(s1)) == 0); - - s2 = talloc_array_ptrtype(top, s2, 10);location2 = __location__; - - ok1(talloc_get_size(s2) == (sizeof(struct struct1) * 10)); - - ok1(strcmp(location2, talloc_get_name(s2)) == 0); - - s3 = talloc_array_ptrtype(top, s3, 10);location3 = __location__; - - ok1(talloc_get_size(s3) == (sizeof(struct struct1 *) * 10)); - - torture_assert_str_equal("ptrtype", location3, talloc_get_name(s3), - "talloc_array_ptrtype() sets the wrong name"); - - s4 = talloc_array_ptrtype(top, s4, 10);location4 = __location__; - - ok1(talloc_get_size(s4) == (sizeof(struct struct1 **) * 10)); - - torture_assert_str_equal("ptrtype", location4, talloc_get_name(s4), - "talloc_array_ptrtype() sets the wrong name"); - - talloc_free(top); - - return true; -} - -static int _test_talloc_free_in_destructor(void **ptr) -{ - talloc_free(*ptr); - return 0; -} - -static bool test_talloc_free_in_destructor(void) -{ - void *level0; - void *level1; - void *level2; - void *level3; - void *level4; - void **level5; - - level0 = talloc_new(NULL); - level1 = talloc_new(level0); - level2 = talloc_new(level1); - level3 = talloc_new(level2); - level4 = talloc_new(level3); - level5 = talloc(level4, void *); - - *level5 = level3; - (void)talloc_reference(level0, level3); - (void)talloc_reference(level3, level3); - (void)talloc_reference(level5, level3); - - talloc_set_destructor(level5, _test_talloc_free_in_destructor); - - talloc_free(level1); - - talloc_free(level0); - - return true; -} - -static bool test_autofree(void) -{ - /* autofree test would kill smbtorture */ - void *p; - p = talloc_autofree_context(); - talloc_free(p); - - p = talloc_autofree_context(); - talloc_free(p); - - return true; -} - -struct torture_context; -static bool torture_local_talloc(struct torture_context *tctx) -{ - bool ret = true; - - setlinebuf(stdout); - - talloc_disable_null_tracking(); - talloc_enable_null_tracking(); - - ret &= test_ref1(); - ret &= test_ref2(); - ret &= test_ref3(); - ret &= test_ref4(); - ret &= test_unlink1(); - ret &= test_misc(); - ret &= test_realloc(); - ret &= test_realloc_child(); - ret &= test_steal(); - ret &= test_move(); - ret &= test_unref_reparent(); - ret &= test_realloc_fn(); - ret &= test_type(); - ret &= test_lifeless(); - ret &= test_loop(); - ret &= test_free_parent_deny_child(); - ret &= test_talloc_ptrtype(); - ret &= test_talloc_free_in_destructor(); - ret &= test_autofree(); - - return ret; -} - -int main(void) -{ - plan_tests(134); - - torture_local_talloc(NULL); - return exit_status(); -} - diff --git a/tap/_info.c b/tap/_info.c deleted file mode 100644 index 7f415a23..00000000 --- a/tap/_info.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include "config.h" - -/** - * tap - Test Anything Protocol - * - * The tap package produces simple-to-parse mainly-human-readable test - * output to assist in the writing of test cases. It is based on the - * (now-defunct) libtap, which is based on Perl's CPAN TAP module. Its - * output can be parsed by a harness such as CPAN's Prove. - * - * CCAN testcases are expected to output the TAP format, usually using - * this package. - * - * For more information about TAP, see: - * http://en.wikipedia.org/wiki/Test_Anything_Protocol - * - * Based on the original libtap, Copyright (c) 2004 Nik Clayton. - * - * Example: - * #include - * #include "tap/tap.h" - * - * // Run some simple (but overly chatty) tests on strcmp(). - * int main(int argc, char *argv[]) - * { - * const char a[] = "a", another_a[] = "a"; - * const char b[] = "b"; - * const char ab[] = "ab"; - * - * plan_tests(4); - * diag("Testing different pointers (%p/%p) with same contents", - * a, another_a); - * ok1(strcmp(a, another_a) == 0); - * - * diag("'a' comes before 'b'"); - * ok1(strcmp(a, b) < 0); - * ok1(strcmp(b, a) > 0); - * - * diag("'ab' comes after 'a'"); - * ok1(strcmp(ab, a) > 0); - * return exit_status(); - * } - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) - return 0; - - if (strcmp(argv[1], "license") == 0) - return "BSD"; - - return 1; -} diff --git a/tap/tap.3 b/tap/tap.3 deleted file mode 100644 index 5395aef7..00000000 --- a/tap/tap.3 +++ /dev/null @@ -1,361 +0,0 @@ -.Dd December 20, 2004 -.Os -.Dt TAP 3 -.Sh NAME -.Nm tap -.Nd write tests that implement the Test Anything Protocol -.Sh SYNOPSIS -.In tap.h -.Sh DESCRIPTION -The -.Nm -library provides functions for writing test scripts that produce output -consistent with the Test Anything Protocol. A test harness that parses -this protocol can run these tests and produce useful reports indicating -their success or failure. -.Ss PRINTF STRINGS -In the descriptions that follow, for any function that takes as the -last two parameters -.Dq Fa char * , Fa ... -it can be assumed that the -.Fa char * -is a -.Fn printf --like format string, and the optional arguments are values to be placed -in that string. -.Ss TEST PLANS -.Bl -tag -width indent -.It Xo -.Ft void -.Fn plan_tests "unsigned int" -.Xc -.It Xo -.Ft void -.Fn plan_no_plan "void" -.Xc -.It Xo -.Ft void -.Fn plan_skip_all "char *" "..." -.Xc -.El -.Pp -You must first specify a test plan. This indicates how many tests you -intend to run, and allows the test harness to notice if any tests were -missed, or if the test program exited prematurely. -.Pp -To do this, use -.Fn plan_tests . -The function will cause your program to exit prematurely if you specify -0 tests. -.Pp -In some situations you may not know how many tests you will be running, or -you are developing your test program, and do not want to update the -.Fn plan_tests -parameter every time you make a change. For those situations use -.Fn plan_no_plan . -It indicates to the test harness that an indeterminate number -of tests will be run. -.Pp -Both -.Fn plan_tests -and -.Fn plan_no_plan -will cause your test program to exit prematurely with a diagnostic -message if they are called more than once. -.Pp -If your test program detects at run time that some required functionality -is missing (for example, it relies on a database connection which is not -present, or a particular configuration option that has not been included -in the running kernel) use -.Fn plan_skip_all , -passing as parameters a string to display indicating the reason for skipping -the tests. -.Ss SIMPLE TESTS -.Bl -tag -width indent -.It Xo -.Ft unsigned int -.Fn ok "expression" "char *" "..." -.Xc -.It Xo -.Ft unsigned int -.Fn ok1 "expression" -.Xc -.It Xo -.Ft unsigned int -.Fn pass "char *" "..." -.Xc -.It Xo -.Ft unsigned int -.Fn fail "char *" "..." -.Xc -.El -.Pp -Tests are implemented as expressions checked by calls to the -.Fn ok -and -.Fn ok1 -macros. In both cases -.Fa expression -should evaluate to true if the test succeeded. -.Pp -.Fn ok -allows you to specify a name, or comment, describing the test which will -be included in the output. -.Fn ok1 -is for those times when the expression to be tested is self -explanatory and does not need an associated comment. In those cases -the test expression becomes the comment. -.Pp -These four calls are equivalent: -.Bd -literal -offset indent -int i = 5; - -ok(i == 5, "i equals 5"); /* Overly verbose */ -ok(i == 5, "i equals %d", i); /* Just to demonstrate printf-like - behaviour of the test name */ -ok(i == 5, "i == 5"); /* Needless repetition */ -ok1(i == 5); /* Just right */ -.Ed -.Pp -It is good practice to ensure that the test name describes the meaning -behind the test rather than what you are testing. Viz -.Bd -literal -offset indent -ok(db != NULL, "db is not NULL"); /* Not bad, but */ -ok(db != NULL, "Database conn. succeeded"); /* this is better */ -.Ed -.Pp -.Fn ok -and -.Fn ok1 -return 1 if the expression evaluated to true, and 0 if it evaluated to -false. This lets you chain calls from -.Fn ok -to -.Fn diag -to only produce diagnostic output if the test failed. For example, this -code will include diagnostic information about why the database connection -failed, but only if the test failed. -.Bd -literal -offset indent -if (!ok(db != NULL, "Database conn. succeeded")) { - diag("Database error code: %d", dberrno); -} -.Ed -.Pp -You also have -.Fn pass -and -.Fn fail . -From the Test::More documentation: -.Bd -literal -offset indent -Sometimes you just want to say that the tests have passed. -Usually the case is you've got some complicated condition -that is difficult to wedge into an ok(). In this case, -you can simply use pass() (to declare the test ok) or fail -(for not ok). - -Use these very, very, very sparingly. -.Ed -.Pp -These are synonyms for ok(1, ...) and ok(0, ...). -.Ss SKIPPING TESTS -.Bl -tag -width indent -.It Xo -.Ft void -.Fn skip "unsigned int" "char *" "..." -.Xc -.It Xo -.Fn skip_if "expression" "unsigned int" "char *" "..." -.Xc -.El -.Pp -Sets of tests can be skipped. Ordinarily you would do this because -the test can't be run in this particular testing environment. -.Pp -For example, suppose some tests should be run as root. If the test is -not being run as root then the tests should be skipped. In this -implementation, skipped tests are flagged as being ok, with a special -message indicating that they were skipped. It is your responsibility -to ensure that the number of tests skipped (the first parameter to -.Fn skip ) -is correct for the number of tests to skip. -.Pp -One way of implementing this is with a -.Dq do { } while(0); -loop, or an -.Dq if( ) { } else { } -construct, to ensure that there are no additional side effects from the -skipped tests. -.Bd -literal -offset indent -if(getuid() != 0) { - skip(1, "because test only works as root"); -} else { - ok(do_something_as_root() == 0, "Did something as root"); -} -.Ed -.Pp -A convenient macro is provided to assist with this. The previous example could -be re-written as follows. -.Bd -literal -offset indent -skip_if(getuid() != 0, 1, "because test only works as root") { - ok(do_something_as_root() == 0, "Did something as root"); -} -.Ed -.Ss MARKING TESTS AS Dq TODO -.Bl -tag -width indent -.It Xo -.Ft void -.Fn todo_start "char *" "..." -.Xc -.It Xo -.Ft void -.Fn todo_end "void" -.Xc -.El -.Pp -Sets of tests can be flagged as being -.Dq TODO . -These are tests that you expect to fail, probably because you haven't -fixed a bug, or finished a new feature yet. These tests will still be -run, but with additional output that indicates that they are expected -to fail. Should a test start to succeed unexpectedly, tools like -.Xr prove 1 -will indicate this, and you can move the test out of the todo -block. This is much more useful than simply commenting out (or -.Dq #ifdef 0 ... #endif ) -the tests. -.Bd -literal -offset indent -todo_start("dwim() not returning true yet"); - -ok(dwim(), "Did what the user wanted"); - -todo_end(); -.Ed -.Pp -Should -.Fn dwim -ever start succeeding you will know about it as soon as you run the -tests. Note that -.Em unlike -the -.Fn skip_* -family, additional code between -.Fn todo_start -and -.Fn todo_end -.Em is -executed. -.Ss SKIP vs. TODO -From the Test::More documentation; -.Bd -literal -offset indent -If it's something the user might not be able to do, use SKIP. -This includes optional modules that aren't installed, running -under an OS that doesn't have some feature (like fork() or -symlinks), or maybe you need an Internet connection and one -isn't available. - -If it's something the programmer hasn't done yet, use TODO. -This is for any code you haven't written yet, or bugs you have -yet to fix, but want to put tests in your testing script -(always a good idea). -.Ed -.Ss DIAGNOSTIC OUTPUT -.Bl -tag -width indent -.It Xo -.Fr void -.Fn diag "char *" "..." -.Xc -.El -.Pp -If your tests need to produce diagnostic output, use -.Fn diag . -It ensures that the output will not be considered by the TAP test harness. -.Fn diag -adds the necessary trailing -.Dq \en -for you. -.Bd -literal -offset indent -diag("Expected return code 0, got return code %d", rcode); -.Ed -.Ss EXIT STATUS -.Bl -tag -width indent -.It Xo -.Fr int -.Fn exit_status void -.Xc -.El -.Pp -For maximum compatability your test program should return a particular -exit code. This is calculated by -.Fn exit_status -so it is sufficient to always return from -.Fn main -with either -.Dq return exit_status(); -or -.Dq exit(exit_status()); -as appropriate. -.Sh EXAMPLES -The -.Pa tests -directory in the source distribution contains numerous tests of -.Nm -functionality, written using -.Nm . -Examine them for examples of how to construct test suites. -.Sh COMPATABILITY -.Nm -strives to be compatible with the Perl Test::More and Test::Harness -modules. The test suite verifies that -.Nm -is bug-for-bug compatible with their behaviour. This is why some -functions which would more naturally return nothing return constant -values. -.Pp -If the -.Lb libpthread -is found at compile time, -.Nm -.Em should -be thread safe. Indications to the contrary (and test cases that expose -incorrect behaviour) are very welcome. -.Sh SEE ALSO -.Xr Test::More 1 , -.Xr Test::Harness 1 , -.Xr prove 1 -.Sh STANDARDS -.Nm -requires a -.St -isoC-99 -compiler. Some of the -.Nm -functionality is implemented as variadic macros, and that functionality -was not formally codified until C99. Patches to use -.Nm -with earlier compilers that have their own implementation of variadic -macros will be gratefully received. -.Sh HISTORY -.Nm -was written to help improve the quality and coverage of the FreeBSD -regression test suite, and released in the hope that others find it -a useful tool to help improve the quality of their code. -.Sh AUTHORS -.An "Nik Clayton" Aq nik@ngo.org.uk , -.Aq nik@FreeBSD.org -.Pp -.Nm -would not exist without the efforts of -.An "Michael G Schwern" Aq schqern@pobox.com , -.An "Andy Lester" Aq andy@petdance.com , -and the countless others who have worked on the Perl QA programme. -.Sh BUGS -Ideally, running the tests would have no side effects on the behaviour -of the application you are testing. However, it is not always possible -to avoid them. The following side effects of using -.Nm -are known. -.Bl -bullet -offset indent -.It -stdout is set to unbuffered mode after calling any of the -.Fn plan_* -functions. -.El diff --git a/tap/tap.c b/tap/tap.c deleted file mode 100644 index 4bbd977b..00000000 --- a/tap/tap.c +++ /dev/null @@ -1,430 +0,0 @@ -/*- - * Copyright (c) 2004 Nik Clayton - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#define _GNU_SOURCE -#include -#include -#include -#include - -#include "tap.h" - -static int no_plan = 0; -static int skip_all = 0; -static int have_plan = 0; -static unsigned int test_count = 0; /* Number of tests that have been run */ -static unsigned int e_tests = 0; /* Expected number of tests to run */ -static unsigned int failures = 0; /* Number of tests that failed */ -static char *todo_msg = NULL; -static char *todo_msg_fixed = "libtap malloc issue"; -static int todo = 0; -static int test_died = 0; - -/* Encapsulate the pthread code in a conditional. In the absence of - libpthread the code does nothing */ -#ifdef HAVE_LIBPTHREAD -#include -static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER; -# define LOCK pthread_mutex_lock(&M) -# define UNLOCK pthread_mutex_unlock(&M) -#else -# define LOCK -# define UNLOCK -#endif - -static void -_expected_tests(unsigned int tests) -{ - - printf("1..%d\n", tests); - e_tests = tests; -} - -static void -diagv(char *fmt, va_list ap) -{ - fputs("# ", stderr); - vfprintf(stderr, fmt, ap); - fputs("\n", stderr); -} - -static void -_diag(char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - diagv(fmt, ap); - va_end(ap); -} - -/* - * Generate a test result. - * - * ok -- boolean, indicates whether or not the test passed. - * test_name -- the name of the test, may be NULL - * test_comment -- a comment to print afterwards, may be NULL - */ -unsigned int -_gen_result(int ok, const char *func, char *file, unsigned int line, - char *test_name, ...) -{ - va_list ap; - char *local_test_name = NULL; - char *c; - int name_is_digits; - - LOCK; - - test_count++; - - /* Start by taking the test name and performing any printf() - expansions on it */ - if(test_name != NULL) { - va_start(ap, test_name); - vasprintf(&local_test_name, test_name, ap); - va_end(ap); - - /* Make sure the test name contains more than digits - and spaces. Emit an error message and exit if it - does */ - if(local_test_name) { - name_is_digits = 1; - for(c = local_test_name; *c != '\0'; c++) { - if(!isdigit(*c) && !isspace(*c)) { - name_is_digits = 0; - break; - } - } - - if(name_is_digits) { - _diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name); - _diag(" Very confusing."); - } - } - } - - if(!ok) { - printf("not "); - failures++; - } - - printf("ok %d", test_count); - - if(test_name != NULL) { - printf(" - "); - - /* Print the test name, escaping any '#' characters it - might contain */ - if(local_test_name != NULL) { - flockfile(stdout); - for(c = local_test_name; *c != '\0'; c++) { - if(*c == '#') - fputc('\\', stdout); - fputc((int)*c, stdout); - } - funlockfile(stdout); - } else { /* vasprintf() failed, use a fixed message */ - printf("%s", todo_msg_fixed); - } - } - - /* If we're in a todo_start() block then flag the test as being - TODO. todo_msg should contain the message to print at this - point. If it's NULL then asprintf() failed, and we should - use the fixed message. - - This is not counted as a failure, so decrement the counter if - the test failed. */ - if(todo) { - printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); - if(!ok) - failures--; - } - - printf("\n"); - - if(!ok) - _diag(" Failed %stest (%s:%s() at line %d)", - todo ? "(TODO) " : "", file, func, line); - - free(local_test_name); - - UNLOCK; - - /* We only care (when testing) that ok is positive, but here we - specifically only want to return 1 or 0 */ - return ok ? 1 : 0; -} - -/* - * Cleanup at the end of the run, produce any final output that might be - * required. - */ -static void -_cleanup(void) -{ - - LOCK; - - /* If plan_no_plan() wasn't called, and we don't have a plan, - and we're not skipping everything, then something happened - before we could produce any output */ - if(!no_plan && !have_plan && !skip_all) { - _diag("Looks like your test died before it could output anything."); - UNLOCK; - return; - } - - if(test_died) { - _diag("Looks like your test died just after %d.", test_count); - UNLOCK; - return; - } - - - /* No plan provided, but now we know how many tests were run, and can - print the header at the end */ - if(!skip_all && (no_plan || !have_plan)) { - printf("1..%d\n", test_count); - } - - if((have_plan && !no_plan) && e_tests < test_count) { - _diag("Looks like you planned %d tests but ran %d extra.", - e_tests, test_count - e_tests); - UNLOCK; - return; - } - - if((have_plan || !no_plan) && e_tests > test_count) { - _diag("Looks like you planned %d tests but only ran %d.", - e_tests, test_count); - if(failures) { - _diag("Looks like you failed %d tests of %d run.", - failures, test_count); - } - UNLOCK; - return; - } - - if(failures) - _diag("Looks like you failed %d tests of %d.", - failures, test_count); - - UNLOCK; -} - -/* - * Initialise the TAP library. Will only do so once, however many times it's - * called. - */ -static void -_tap_init(void) -{ - static int run_once = 0; - - if(!run_once) { - atexit(_cleanup); - - /* stdout needs to be unbuffered so that the output appears - in the same place relative to stderr output as it does - with Test::Harness */ - setbuf(stdout, 0); - run_once = 1; - } -} - -/* - * Note that there's no plan. - */ -void -plan_no_plan(void) -{ - - LOCK; - - _tap_init(); - - if(have_plan != 0) { - fprintf(stderr, "You tried to plan twice!\n"); - test_died = 1; - UNLOCK; - exit(255); - } - - have_plan = 1; - no_plan = 1; - - UNLOCK; -} - -/* - * Note that the plan is to skip all tests - */ -void -plan_skip_all(char *reason) -{ - - LOCK; - - _tap_init(); - - skip_all = 1; - - printf("1..0"); - - if(reason != NULL) - printf(" # Skip %s", reason); - - printf("\n"); - - UNLOCK; -} - -/* - * Note the number of tests that will be run. - */ -void -plan_tests(unsigned int tests) -{ - - LOCK; - - _tap_init(); - - if(have_plan != 0) { - fprintf(stderr, "You tried to plan twice!\n"); - test_died = 1; - UNLOCK; - exit(255); - } - - if(tests == 0) { - fprintf(stderr, "You said to run 0 tests! You've got to run something.\n"); - test_died = 1; - UNLOCK; - exit(255); - } - - have_plan = 1; - - _expected_tests(tests); - - UNLOCK; -} - -void -diag(char *fmt, ...) -{ - va_list ap; - - LOCK; - - va_start(ap, fmt); - diagv(fmt, ap); - va_end(ap); - - UNLOCK; -} - -void -skip(unsigned int n, char *fmt, ...) -{ - va_list ap; - char *skip_msg; - - LOCK; - - va_start(ap, fmt); - vasprintf(&skip_msg, fmt, ap); - va_end(ap); - - while(n-- > 0) { - test_count++; - printf("ok %d # skip %s\n", test_count, - skip_msg != NULL ? - skip_msg : "libtap():malloc() failed"); - } - - free(skip_msg); - - UNLOCK; -} - -void -todo_start(char *fmt, ...) -{ - va_list ap; - - LOCK; - - va_start(ap, fmt); - vasprintf(&todo_msg, fmt, ap); - va_end(ap); - - todo = 1; - - UNLOCK; -} - -void -todo_end(void) -{ - - LOCK; - - todo = 0; - free(todo_msg); - - UNLOCK; -} - -int -exit_status(void) -{ - int r; - - LOCK; - - /* If there's no plan, just return the number of failures */ - if(no_plan || !have_plan) { - UNLOCK; - return failures; - } - - /* Ran too many tests? Return the number of tests that were run - that shouldn't have been */ - if(e_tests < test_count) { - r = test_count - e_tests; - UNLOCK; - return r; - } - - /* Return the number of tests that failed + the number of tests - that weren't run */ - r = failures + e_tests - test_count; - UNLOCK; - - return r; -} diff --git a/tap/tap.h b/tap/tap.h deleted file mode 100644 index f854d3e3..00000000 --- a/tap/tap.h +++ /dev/null @@ -1,246 +0,0 @@ -/*- - * Copyright (c) 2004 Nik Clayton - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/** - * plan_tests - announce the number of tests you plan to run - * @tests: the number of tests - * - * This should be the first call in your test program: it allows tracing - * of failures which mean that not all tests are run. - * - * If you don't know how many tests will actually be run, assume all of them - * and use skip() if you don't actually run some tests. - * - * Example: - * plan_tests(13); - */ -void plan_tests(unsigned int tests); - -#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) && !defined(__GNUC__) -# error "Needs gcc or C99 compiler for variadic macros." -#else - -/** - * ok1 - Simple conditional test - * @e: the expression which we expect to be true. - * - * This is the simplest kind of test: if the expression is true, the - * test passes. The name of the test which is printed will simply be - * file name, line number, and the expression itself. - * - * Example: - * ok1(init_subsystem() == 1); - */ -# define ok1(e) ((e) ? \ - _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \ - _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) - -/** - * ok - Conditional test with a name - * @e: the expression which we expect to be true. - * @...: the printf-style name of the test. - * - * If the expression is true, the test passes. The name of the test will be - * the filename, line number, and the printf-style string. This can be clearer - * than simply the expression itself. - * - * Example: - * ok1(init_subsystem() == 1); - * ok(init_subsystem() == 0, "Second initialization should fail"); - */ -# define ok(e, ...) ((e) ? \ - _gen_result(1, __func__, __FILE__, __LINE__, \ - __VA_ARGS__) : \ - _gen_result(0, __func__, __FILE__, __LINE__, \ - __VA_ARGS__)) - -/** - * pass - Note that a test passed - * @...: the printf-style name of the test. - * - * For complicated code paths, it can be easiest to simply call pass() in one - * branch and fail() in another. - * - * Example: - * x = do_something(); - * if (!checkable(x) || check_value(x)) - * pass("do_something() returned a valid value"); - * else - * fail("do_something() returned an invalid value"); - */ -# define pass(...) ok(1, __VA_ARGS__) - -/** - * fail - Note that a test failed - * @...: the printf-style name of the test. - * - * For complicated code paths, it can be easiest to simply call pass() in one - * branch and fail() in another. - */ -# define fail(...) ok(0, __VA_ARGS__) - -/* I don't find these to be useful. */ -# define skip_if(cond, n, ...) \ - if (cond) skip((n), __VA_ARGS__); \ - else - -# define skip_start(test, n, ...) \ - do { \ - if((test)) { \ - skip(n, __VA_ARGS__); \ - continue; \ - } - -# define skip_end } while(0) - -#ifndef PRINTF_ATTRIBUTE -#ifdef __GNUC__ -#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) -#else -#define PRINTF_ATTRIBUTE(a1, a2) -#endif -#endif - -unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...) - PRINTF_ATTRIBUTE(5, 6); - -/** - * diag - print a diagnostic message (use instead of printf/fprintf) - * @fmt: the format of the printf-style message - * - * diag ensures that the output will not be considered to be a test - * result by the TAP test harness. It will append '\n' for you. - * - * Example: - * diag("Now running complex tests"); - */ -void diag(char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); - -/** - * skip - print a diagnostic message (use instead of printf/fprintf) - * @n: number of tests you're skipping. - * @fmt: the format of the reason you're skipping the tests. - * - * Sometimes tests cannot be run because the test system lacks some feature: - * you should explicitly document that you're skipping tests using skip(). - * - * From the Test::More documentation: - * If it's something the user might not be able to do, use SKIP. This - * includes optional modules that aren't installed, running under an OS that - * doesn't have some feature (like fork() or symlinks), or maybe you need an - * Internet connection and one isn't available. - * - * Example: - * #ifdef HAVE_SOME_FEATURE - * ok1(test_some_feature()); - * #else - * skip(1, "Don't have SOME_FEATURE"); - * #endif - */ -void skip(unsigned int n, char *fmt, ...) PRINTF_ATTRIBUTE(2, 3); - -/** - * todo_start - mark tests that you expect to fail. - * @fmt: the reason they currently fail. - * - * It's extremely useful to write tests before you implement the matching fix - * or features: surround these tests by todo_start()/todo_end(). These tests - * will still be run, but with additional output that indicates that they are - * expected to fail. - * - * This way, should a test start to succeed unexpectedly, tools like prove(1) - * will indicate this and you can move the test out of the todo block. This - * is much more useful than simply commenting out (or '#if 0') the tests. - * - * From the Test::More documentation: - * If it's something the programmer hasn't done yet, use TODO. This is for - * any code you haven't written yet, or bugs you have yet to fix, but want to - * put tests in your testing script (always a good idea). - * - * Example: - * todo_start("dwim() not returning true yet"); - * ok(dwim(), "Did what the user wanted"); - * todo_end(); - */ -void todo_start(char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); - -/** - * todo_end - end of tests you expect to fail. - * - * See todo_start(). - */ -void todo_end(void); - -/** - * exit_status - the value that main should return. - * - * For maximum compatability your test program should return a particular exit - * code (ie. 0 if all tests were run, and every test which was expected to - * succeed succeeded). - * - * Example: - * exit(exit_status()); - */ -int exit_status(void); - -/** - * plan_no_plan - I have no idea how many tests I'm going to run. - * - * In some situations you may not know how many tests you will be running, or - * you are developing your test program, and do not want to update the - * plan_tests() call every time you make a change. For those situations use - * plan_no_plan() instead of plan_tests(). It indicates to the test harness - * that an indeterminate number of tests will be run. - * - * Remember, if you fail to plan, you plan to fail. - * - * Example: - * plan_no_plan(); - * while (random() % 2) - * ok1(some_test()); - * exit(exit_status()); - */ -void plan_no_plan(void); - -/** - * plan_skip_all - Indicate that you will skip all tests. - * @reason: the string indicating why you can't run any tests. - * - * If your test program detects at run time that some required functionality - * is missing (for example, it relies on a database connection which is not - * present, or a particular configuration option that has not been included - * in the running kernel) use plan_skip_all() instead of plan_tests(). - * - * Example: - * if (!have_some_feature) { - * plan_skip_all("Need some_feature support"); - * exit(exit_status()); - * } - * plan_tests(13); - */ -void plan_skip_all(char *reason); - -#endif /* C99 or gcc */ diff --git a/tap/test/run.c b/tap/test/run.c deleted file mode 100644 index 97af45c8..00000000 --- a/tap/test/run.c +++ /dev/null @@ -1,117 +0,0 @@ -/* We use the fact that pipes have a buffer greater than the size of - * any output, and change stdout and stderr to use that. - * - * Since we don't use libtap for output, this looks like one big test. */ -#include "tap/tap.h" -#include -#include -#include -#include -#include -#include -#include -#include - -/* We dup stderr to here. */ -static int stderrfd; - -/* Simple replacement for err() */ -static void failmsg(const char *fmt, ...) -{ - char buf[1024]; - va_list ap; - - /* Write into buffer. */ - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - - write(stderrfd, "# ", 2); - write(stderrfd, buf, strlen(buf)); - write(stderrfd, "\n", 1); - _exit(1); -} - -static void expect(int fd, const char *str) -{ - char buffer[PIPE_BUF]; - int r; - - r = read(fd, buffer, sizeof(buffer)); - if (r < 0) - failmsg("reading from pipe"); - - if (strlen(str) != r || strncmp(str, buffer, r) != 0) - failmsg("Expected '%s' got '%.*s'", - str, r, buffer); -} - -int main(int argc, char *argv[]) -{ - int p[2]; - int stdoutfd; - - printf("1..1\n"); - fflush(stdout); - stderrfd = dup(STDERR_FILENO); - if (stderrfd < 0) - err(1, "dup of stderr failed"); - - stdoutfd = dup(STDOUT_FILENO); - if (stdoutfd < 0) - err(1, "dup of stdout failed"); - - if (pipe(p) != 0) - failmsg("pipe failed"); - - if (dup2(p[1], STDERR_FILENO) < 0 || dup2(p[1], STDOUT_FILENO) < 0) - failmsg("Duplicating file descriptor"); - - plan_tests(10); - expect(p[0], "1..10\n"); - - ok(1, "msg1"); - expect(p[0], "ok 1 - msg1\n"); - - ok(0, "msg2"); - expect(p[0], "not ok 2 - msg2\n" - "# Failed test (tap/test/run.c:main() at line 76)\n"); - - ok1(true); - expect(p[0], "ok 3 - true\n"); - - ok1(false); - expect(p[0], "not ok 4 - false\n" - "# Failed test (tap/test/run.c:main() at line 83)\n"); - - pass("passed"); - expect(p[0], "ok 5 - passed\n"); - - fail("failed"); - expect(p[0], "not ok 6 - failed\n" - "# Failed test (tap/test/run.c:main() at line 90)\n"); - - skip(2, "skipping %s", "test"); - expect(p[0], "ok 7 # skip skipping test\n" - "ok 8 # skip skipping test\n"); - - todo_start("todo"); - ok1(false); - expect(p[0], "not ok 9 - false # TODO todo\n" - "# Failed (TODO) test (tap/test/run.c:main() at line 99)\n"); - ok1(true); - expect(p[0], "ok 10 - true # TODO todo\n"); - todo_end(); - - if (exit_status() != 3) - failmsg("Expected exit status 3, not %i", exit_status()); - -#if 0 - /* Manually run the atexit command. */ - _cleanup(); - expect(p[0], "# Looks like you failed 2 tests of 9.\n"); -#endif - - write(stdoutfd, "ok 1 - All passed\n", strlen("ok 1 - All passed\n")); - _exit(0); -} diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 00000000..a9ed7fd6 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,10 @@ +tools/run_tests: tools/run_tests.o ccan/tap/tap.o ccan/talloc/talloc.o + +tools/doc_extract: tools/doc_extract.c ccan/talloc/talloc.o + +tools/namespacize: tools/namespacize.c ccan/talloc/talloc.o + +tools-clean: ccanlint-clean + rm -f run_tests doc_extract namespacize + +include tools/ccanlint/Makefile diff --git a/tools/ccanlint/Makefile b/tools/ccanlint/Makefile new file mode 100644 index 00000000..f3f00a6f --- /dev/null +++ b/tools/ccanlint/Makefile @@ -0,0 +1,32 @@ +OBJS := tools/ccanlint/no_info.o \ + tools/ccanlint/has_main_header.o \ + tools/ccanlint/has_tests.o \ + tools/ccanlint/trailing_whitespace.o \ + tools/ccanlint/idempotent.o \ + +FUTURE:=tools/ccanlint/if_have_not_ifdef.o \ + tools/ccanlint/needs_depends.o \ + tools/ccanlint/has_info_documentation.o \ + tools/ccanlint/has_header_documentation.o \ + tools/ccanlint/has_tests.o \ + tools/ccanlint/builds_ok.o \ + tools/ccanlint/builds_ok_all_have_variants.o \ + tools/ccanlint/run_tests.o \ + tools/ccanlint/test_coverage.o \ + +tools/ccanlint/generated-init-tests: $(OBJS) + cat $(OBJS:.o=.c) | sed -n 's/^struct ccanlint \([A-Za-z0-9_]*\) = {/{ extern struct ccanlint \1; list_add(\&tests, \&\1.list); }/p' >$@ + +tools/ccanlint/ccanlint.o: tools/ccanlint/generated-init-tests + +tools/ccanlint/ccanlint: \ + $(OBJS) \ + tools/ccanlint/ccanlint.o \ + tools/ccanlint/get_file_lines.o \ + tools/ccanlint/file_analysis.o \ + talloc/talloc.o noerr/noerr.o + +ccanlint-clean: + $(RM) tools/ccanlint/generated-init-tests + $(RM) tools/ccanlint/ccanlint + diff --git a/tools/ccanlint/ccanlint.c b/tools/ccanlint/ccanlint.c new file mode 100644 index 00000000..001ee689 --- /dev/null +++ b/tools/ccanlint/ccanlint.c @@ -0,0 +1,165 @@ +/* + * ccanlint: assorted checks and advice for a ccan package + * Copyright (C) 2008 Rusty Russell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "ccanlint.h" +#include +#include +#include +#include +#include +#include +#include + +static unsigned int verbose = 0; +static LIST_HEAD(tests); + +static void init_tests(void) +{ +#include "generated-init-tests" +} + +static void usage(const char *name) +{ + fprintf(stderr, "Usage: %s [-s] [-v] [-d ]\n" + " -v: verbose mode\n" + " -s: simply give one line per FAIL and total score\n" + " -d: use this directory instead of the current one\n", + name); + exit(1); +} + +static void indent_print(const char *string) +{ + while (*string) { + unsigned int line = strcspn(string, "\n"); + printf("\t%.*s", line, string); + if (string[line] == '\n') { + printf("\n"); + line++; + } + string += line; + } +} + +bool ask(const char *question) +{ + char reply[2]; + + printf("%s ", question); + fflush(stdout); + + return fgets(reply, sizeof(reply), stdin) != NULL + && toupper(reply[0]) == 'Y'; +} + +static bool run_test(const struct ccanlint *i, + bool summary, + unsigned int *score, + unsigned int *total_score, + struct manifest *m) +{ + void *result; + unsigned int this_score; + + if (i->total_score) + *total_score += i->total_score; + + result = i->check(m); + if (!result) { + if (verbose) + printf(" %s: OK\n", i->name); + if (i->total_score) + *score += i->total_score; + return true; + } + + if (i->score) + this_score = i->score(m, result); + else + this_score = 0; + + *score += this_score; + if (summary) { + printf("%s FAILED (%u/%u)\n", + i->name, this_score, i->total_score); + + if (verbose) + indent_print(i->describe(m, result)); + return false; + } + + printf("%s\n", i->describe(m, result)); + + if (i->handle) + i->handle(m, result); + + return false; +} + +int main(int argc, char *argv[]) +{ + int c; + bool summary = false; + unsigned int score, total_score; + struct manifest *m; + const struct ccanlint *i; + + /* I'd love to use long options, but that's not standard. */ + /* FIXME: getopt_long ccan package? */ + while ((c = getopt(argc, argv, "sd:v")) != -1) { + switch (c) { + case 'd': + if (chdir(optarg) != 0) + err(1, "Changing into directory '%s'", optarg); + break; + case 's': + summary = true; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + } + } + + if (optind < argc) + usage(argv[0]); + + m = get_manifest(); + + init_tests(); + + /* If you don't pass the compulsory tests, you don't even get a score */ + if (verbose) + printf("Compulsory tests:\n"); + list_for_each(&tests, i, list) + if (!i->total_score && !run_test(i, summary, NULL, NULL, m)) + exit(1); + + if (verbose) + printf("\nNormal tests:\n"); + score = total_score = 0; + list_for_each(&tests, i, list) + if (i->total_score) + run_test(i, summary, &score, &total_score, m); + + printf("Total score: %u/%u\n", score, total_score); + + return 0; +} diff --git a/tools/ccanlint/ccanlint.h b/tools/ccanlint/ccanlint.h new file mode 100644 index 00000000..51f555ec --- /dev/null +++ b/tools/ccanlint/ccanlint.h @@ -0,0 +1,76 @@ +#ifndef CCAN_LINT_H +#define CCAN_LINT_H +#include +#include + +struct manifest { + char *basename; + struct ccan_file *info_file; + + struct list_head c_files; + struct list_head h_files; + + struct list_head run_tests; + struct list_head compile_ok_tests; + struct list_head compile_fail_tests; + struct list_head other_test_files; + + struct list_head other_files; +}; + +struct manifest *get_manifest(void); + +struct ccanlint { + struct list_node list; + + /* Unique name of test */ + const char *name; + + /* Total score that this test is worth. 0 means compulsory tests. */ + unsigned int total_score; + + /* If this returns non-NULL, it means the check failed. */ + void *(*check)(struct manifest *m); + + /* The non-NULL return from check is passed to one of these: */ + + /* So, what did this get out of the total_score? (NULL means 0). */ + unsigned int (*score)(struct manifest *m, void *check_result); + + /* Verbose description of what was wrong. */ + const char *(*describe)(struct manifest *m, void *check_result); + + /* Can we do something about it? (NULL if not) */ + void (*handle)(struct manifest *m, void *check_result); +}; + +/* Ask the user a yes/no question: the answer is NO if there's an error. */ +bool ask(const char *question); + +struct ccan_file { + struct list_node list; + + char *name; + + unsigned int num_lines; + char **lines; +}; + +/* Use this rather than accessing f->lines directly: loads on demand. */ +char **get_ccan_file_lines(struct ccan_file *f); + +/* Call the reporting on every line in the file. sofar contains + * previous results. */ +char *report_on_lines(struct list_head *files, + char *(*report)(const char *), + char *sofar); + +/* The critical tests which mean fail if they don't pass. */ +extern struct ccanlint no_info; +extern struct ccanlint has_main_header; + +/* Normal tests. */ +extern struct ccanlint trailing_whitespace; + + +#endif /* CCAN_LINT_H */ diff --git a/tools/ccanlint/file_analysis.c b/tools/ccanlint/file_analysis.c new file mode 100644 index 00000000..1341e57b --- /dev/null +++ b/tools/ccanlint/file_analysis.c @@ -0,0 +1,149 @@ +#include "ccanlint.h" +#include "get_file_lines.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char **get_ccan_file_lines(struct ccan_file *f) +{ + if (!f->lines) + f->lines = get_file_lines(f, f->name, &f->num_lines); + return f->lines; +} + +static void add_files(struct manifest *m, const char *dir) +{ + DIR *d; + struct dirent *ent; + + if (dir[0]) + d = opendir(dir); + else + d = opendir("."); + if (!d) + err(1, "Opening directory %s", dir[0] ? dir : "."); + + while ((ent = readdir(d)) != NULL) { + struct stat st; + struct ccan_file *f; + struct list_head *dest; + bool is_c_src; + + if (ent->d_name[0] == '.') + continue; + + f = talloc(m, struct ccan_file); + f->lines = NULL; + f->name = talloc_asprintf(f, "%s%s", dir, ent->d_name); + if (lstat(f->name, &st) != 0) + err(1, "lstat %s", f->name); + + if (S_ISDIR(st.st_mode)) { + f->name = talloc_append_string(f->name, "/"); + add_files(m, f->name); + continue; + } + if (!S_ISREG(st.st_mode)) { + talloc_free(f); + continue; + } + + if (streq(f->name, "_info.c")) { + m->info_file = f; + continue; + } + + is_c_src = strends(f->name, ".c"); + if (!is_c_src && !strends(f->name, ".h")) + dest = &m->other_files; + else if (!strchr(f->name, '/')) { + if (is_c_src) + dest = &m->c_files; + else + dest = &m->h_files; + } else if (strstarts(f->name, "test/")) { + if (is_c_src) { + if (strstarts(f->name, "test/run")) + dest = &m->run_tests; + else if (strstarts(f->name, "test/compile_ok")) + dest = &m->compile_ok_tests; + else if (strstarts(f->name, "test/compile_fail")) + dest = &m->compile_fail_tests; + else + dest = &m->other_test_files; + } else + dest = &m->other_test_files; + } else + dest = &m->other_files; + + list_add(dest, &f->list); + } + closedir(d); +} + +char *report_on_lines(struct list_head *files, + char *(*report)(const char *), + char *sofar) +{ + struct ccan_file *f; + + list_for_each(files, f, list) { + unsigned int i; + char **lines = get_ccan_file_lines(f); + + for (i = 0; i < f->num_lines; i++) { + char *r = report(lines[i]); + if (!r) + continue; + + sofar = talloc_asprintf_append(sofar, + "%s:%u:%s\n", + f->name, i+1, r); + talloc_free(r); + } + } + return sofar; +} + +struct manifest *get_manifest(void) +{ + struct manifest *m = talloc(NULL, struct manifest); + unsigned int len; + + m->info_file = NULL; + list_head_init(&m->c_files); + list_head_init(&m->h_files); + list_head_init(&m->run_tests); + list_head_init(&m->compile_ok_tests); + list_head_init(&m->compile_fail_tests); + list_head_init(&m->other_test_files); + list_head_init(&m->other_files); + + /* *This* is why people hate C. */ + len = 32; + m->basename = talloc_array(m, char, len); + while (!getcwd(m->basename, len)) { + if (errno != ERANGE) + err(1, "Getting current directory"); + m->basename = talloc_realloc(m, m->basename, char, len *= 2); + } + + len = strlen(m->basename); + while (len && m->basename[len-1] == '/') + m->basename[--len] = '\0'; + + m->basename = strrchr(m->basename, '/'); + if (!m->basename) + errx(1, "I don't expect to be run from the root directory"); + m->basename++; + + add_files(m, ""); + return m; +} diff --git a/tools/ccanlint/get_file_lines.c b/tools/ccanlint/get_file_lines.c new file mode 100644 index 00000000..e9ef302b --- /dev/null +++ b/tools/ccanlint/get_file_lines.c @@ -0,0 +1,85 @@ +#include "get_file_lines.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *grab_fd(const void *ctx, int fd) +{ + int ret; + unsigned int max = 16384, size = 0; + char *buffer; + + buffer = talloc_array(ctx, char, max+1); + while ((ret = read(fd, buffer + size, max - size)) > 0) { + size += ret; + if (size == max) + buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1); + } + if (ret < 0) { + talloc_free(buffer); + buffer = NULL; + } else + buffer[size] = '\0'; + + return buffer; +} + +/* This version adds one byte (for nul term) */ +static void *grab_file(const void *ctx, const char *filename) +{ + int fd; + char *buffer; + + if (streq(filename, "-")) + fd = dup(STDIN_FILENO); + else + fd = open(filename, O_RDONLY, 0); + + if (fd < 0) + return NULL; + + buffer = grab_fd(ctx, fd); + close_noerr(fd); + return buffer; +} + +/* This is a dumb one which copies. We could mangle instead. */ +static char **split(const void *ctx, const char *text, const char *delims, + unsigned int *nump) +{ + char **lines = NULL; + unsigned int max = 64, num = 0; + + lines = talloc_array(ctx, char *, max+1); + + while (*text != '\0') { + unsigned int len = strcspn(text, delims); + lines[num] = talloc_array(lines, char, len + 1); + memcpy(lines[num], text, len); + lines[num][len] = '\0'; + text += len; + text += strspn(text, delims); + if (++num == max) + lines = talloc_realloc(ctx, lines, char *, max*=2 + 1); + } + lines[num] = NULL; + if (nump) + *nump = num; + return lines; +} + +char **get_file_lines(void *ctx, const char *name, unsigned int *num_lines) +{ + char *buffer = grab_file(ctx, name); + + if (!buffer) + err(1, "Getting file %s", name); + + return split(buffer, buffer, "\n", num_lines); +} diff --git a/tools/ccanlint/get_file_lines.h b/tools/ccanlint/get_file_lines.h new file mode 100644 index 00000000..2f8455a5 --- /dev/null +++ b/tools/ccanlint/get_file_lines.h @@ -0,0 +1,6 @@ +#ifndef GET_FILE_LINES_H +#define GET_FILE_LINES_H + +char **get_file_lines(void *ctx, const char *name, unsigned int *num_lines); + +#endif /* GET_FILE_LINES_H */ diff --git a/tools/ccanlint/has_main_header.c b/tools/ccanlint/has_main_header.c new file mode 100644 index 00000000..75e3f91d --- /dev/null +++ b/tools/ccanlint/has_main_header.c @@ -0,0 +1,41 @@ +#include "ccanlint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *check_has_main_header(struct manifest *m) +{ + struct ccan_file *f; + + list_for_each(&m->h_files, f, list) { + if (strstarts(f->name, m->basename) + && strlen(f->name) == strlen(m->basename) + 2) + return NULL; + } + return m; +} + +static const char *describe_has_main_header(struct manifest *m, + void *check_result) +{ + return talloc_asprintf(m, + "You have no %s/%s.h header file.\n\n" + "CCAN modules have a name, the same as the directory name. They're\n" + "expected to have an interface in the header of the same name.\n", + m->basename, m->basename); +} + +struct ccanlint has_main_header = { + .name = "No main header file", + .check = check_has_main_header, + .describe = describe_has_main_header, +}; diff --git a/tools/ccanlint/has_tests.c b/tools/ccanlint/has_tests.c new file mode 100644 index 00000000..7a187ab2 --- /dev/null +++ b/tools/ccanlint/has_tests.c @@ -0,0 +1,118 @@ +#include "ccanlint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char test_is_not_dir[] = "test is not a directory"; + +static void *check_has_tests(struct manifest *m) +{ + struct stat st; + + if (lstat("test", &st) != 0) { + if (errno != ENOENT) + err(1, "statting test/"); + return "You have no test directory"; + } + + if (!S_ISDIR(st.st_mode)) + return test_is_not_dir; + + if (list_empty(&m->run_tests) && list_empty(&m->compile_ok_tests)) { + if (list_empty(&m->compile_fail_tests)) + return "You have no tests in the test directory"; + else + return "You have no positive tests in the test directory"; + } + return NULL; +} + +static const char *describe_has_tests(struct manifest *m, void *check_result) +{ + return talloc_asprintf(m, "%s\n\n" + "CCAN modules have a directory called test/ which contains tests.\n" + "There are three kinds of tests: run, compile_ok and compile_fail:\n" + "you can tell which type of test a C file is by its name, eg 'run.c'\n" + "and 'run-simple.c' are both run tests.\n\n" + "The simplest kind of test is a run test, which must compile with no\n" + "warnings, and then run: it is expected to use libtap to report its\n" + "results in a simple and portable format.\n" + "compile_ok tests are a subset of run tests: they must compile and\n" + "link, but aren't run.\n" + "compile_fail tests are tests which should fail to compile (or emit\n" + "warnings) or link when FAIL is defined, but should compile and link\n" + "when it's not defined: this helps ensure unrelated errors don't make\n" + "compilation fail.\n\n" + "Note that the tests are not linked against the files in the\n" + "above: you should directly #include those C files you want. This\n" + "allows access to static functions and use special effects inside\n" + "test files\n", (char *)check_result); +} + +static void handle_no_tests(struct manifest *m, void *check_result) +{ + FILE *run; + struct ccan_file *i; + + if (check_result == test_is_not_dir) + return; + + if (!ask("Should I create a template test/run.c file for you?")) + return; + + if (mkdir("test", 0600) != 0) { + if (errno != EEXIST) + err(1, "Creating test/ directory"); + } + + run = fopen("test/run.c", "w"); + if (!run) + err(1, "Trying to create a test/run.c"); + + fputs("/* Include the main header first, to test it works */\n", run); + fprintf(run, "#include \"%s/%s.h\"\n", m->basename, m->basename); + fputs("/* Include the C files directly. */\n", run); + list_for_each(&m->c_files, i, list) + fprintf(run, "#include \"%s/%s\"\n", m->basename, i->name); + fputs("#include \"tap/tap.h\"\n", run); + fputs("\n", run); + + fputs("int main(int argc, char *argv[])\n", run); + fputs("{\n", run); + fputs("\t/* This is how many tests you plan to run\n", run); + fputs("\tplan_tests(3);\n", run); + fputs("\n", run); + fputs("\t/* Simple thing we expect to succeed */\n", run); + fputs("\tok1(some_test())\n", run); + fputs("\t/* Same, with an explicit description of the test. */\n", run); + fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run); + fputs("\t/* How to print out messages for debugging. */\n", run); + fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run); + fputs("\t/* Conditional tests must be explicitly skipped. */\n", run); + fputs("#if HAVE_SOME_FEATURE\n", run); + fputs("\tok1(test_some_feature())\n", run); + fputs("#else\n", run); + fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run); + fputs("#endif\n", run); + fputs("\n", run); + fputs("\t/* This exits depending on whether all tests passed */\n", run); + fputs("\return exit_status()\n", run); + + fclose(run); +} + +struct ccanlint has_tests = { + .name = "No tests", + .check = check_has_tests, + .describe = describe_has_tests, + .handle = handle_no_tests, +}; diff --git a/tools/ccanlint/idempotent.c b/tools/ccanlint/idempotent.c new file mode 100644 index 00000000..44565575 --- /dev/null +++ b/tools/ccanlint/idempotent.c @@ -0,0 +1,71 @@ +#include "ccanlint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char explain[] += "Headers usually start with the C preprocessor lines to prevent multiple\n" + "inclusions. These look like the following:\n" + "#ifndef MY_HEADER_H\n" + "#define MY_HEADER_H\n" + "...\n" + "#endif /* MY_HEADER_H */\n"; + +static char *report_idem(struct ccan_file *f, char *sofar) +{ + char **lines; + char *secondline; + + lines = get_ccan_file_lines(f); + if (f->num_lines < 3) + /* FIXME: We assume small headers probably uninteresting. */ + return NULL; + + if (!strstarts(lines[0], "#ifndef ")) + return talloc_asprintf_append(sofar, + "%s:1:expect first line to be #ifndef.\n", f->name); + + secondline = talloc_asprintf(f, "#define %s", + lines[0] + strlen("#ifndef ")); + if (!streq(lines[1], secondline)) + return talloc_asprintf_append(sofar, + "%s:2:expect second line to be '%s'.\n", + f->name, secondline); + + return sofar; +} + +static void *check_idempotent(struct manifest *m) +{ + struct ccan_file *f; + char *report = NULL; + + list_for_each(&m->h_files, f, list) + report = report_idem(f, report); + + return report; +} + +static const char *describe_idempotent(struct manifest *m, void *check_result) +{ + return talloc_asprintf(check_result, + "Some headers not idempotent:\n" + "%s\n%s", (char *)check_result, + explain); +} + +struct ccanlint idempotent = { + .name = "Headers are #ifndef/#define idempotent wrapped", + .total_score = 1, + .check = check_idempotent, + .describe = describe_idempotent, +}; diff --git a/tools/ccanlint/no_info.c b/tools/ccanlint/no_info.c new file mode 100644 index 00000000..9ee5f709 --- /dev/null +++ b/tools/ccanlint/no_info.c @@ -0,0 +1,78 @@ +#include "ccanlint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *check_no_info(struct manifest *m) +{ + if (m->info_file) + return NULL; + return m; +} + +static const char *describe_no_info(struct manifest *m, void *check_result) +{ + return "You have no _info.c file.\n\n" + "The file _info.c contains the metadata for a ccan package: things\n" + "like the dependencies, the documentation for the package as a whole\n" + "and license information.\n"; +} + +static const char template[] = + "#include \n" + "#include \"config.h\"\n" + "\n" + "/**\n" + " * %s - YOUR-ONE-LINE-DESCRIPTION-HERE\n" + " *\n" + " * This code ... YOUR-BRIEF-SUMMARY-HERE\n" + " *\n" + " * Example:\n" + " * FULLY-COMPILABLE-INDENTED-TRIVIAL-BUT-USEFUL-EXAMPLE-HERE\n" + " */\n" + "int main(int argc, char *argv[])\n" + "{\n" + " /* Expect exactly one argument\n" + " if (argc != 2)\n" + " return 1;\n" + "\n" + " if (strcmp(argv[1], \"depends\") == 0) {\n" + " PRINTF-CCAN-PACKAGES-YOU-NEED-ONE-PER-LINE-IF-ANY\n" + " return 0;\n" + " }\n" + "\n" + " return 1;\n" + "}\n"; + +static void create_info_template(struct manifest *m, void *check_result) +{ + FILE *info; + + if (!ask("Should I create a template _info.c file for you?")) + return; + + info = fopen("_info.c", "w"); + if (!info) + err(1, "Trying to create a template _info.c"); + + if (fprintf(info, template, m->basename) < 0) { + unlink_noerr("_info.c"); + err(1, "Writing template into _info.c"); + } + fclose(info); +} + +struct ccanlint no_info = { + .name = "No _info.c file", + .check = check_no_info, + .describe = describe_no_info, + .handle = create_info_template, +}; diff --git a/tools/ccanlint/trailing_whitespace.c b/tools/ccanlint/trailing_whitespace.c new file mode 100644 index 00000000..3aeec6e5 --- /dev/null +++ b/tools/ccanlint/trailing_whitespace.c @@ -0,0 +1,42 @@ +/* Trailing whitespace test. Almost embarrassing, but trivial. */ +#include "ccanlint.h" +#include +#include + +static char *report_on_trailing_whitespace(const char *line) +{ + if (!strends(line, " ") && !strends(line, "\t")) + return NULL; + + if (strlen(line) > 20) + return talloc_asprintf(line, "...'%s'", + line + strlen(line) - 20); + return talloc_asprintf(line, "'%s'", line); +} + +static void *check_trailing_whitespace(struct manifest *m) +{ + char *report; + + report = report_on_lines(&m->c_files, report_on_trailing_whitespace, + NULL); + report = report_on_lines(&m->h_files, report_on_trailing_whitespace, + report); + + return report; +} + +static const char *describe_trailing_whitespace(struct manifest *m, + void *check_result) +{ + return talloc_asprintf(check_result, + "Some source files have trailing whitespace:\n" + "%s", (char *)check_result); +} + +struct ccanlint trailing_whitespace = { + .name = "Lines with unnecessary trailing whitespace", + .total_score = 1, + .check = check_trailing_whitespace, + .describe = describe_trailing_whitespace, +}; diff --git a/tools/doc_extract.c b/tools/doc_extract.c new file mode 100644 index 00000000..b4ac0d3e --- /dev/null +++ b/tools/doc_extract.c @@ -0,0 +1,107 @@ +/* This merely extracts, doesn't do XML or anything. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "talloc/talloc.h" + +/* Is A == B ? */ +#define streq(a,b) (strcmp((a),(b)) == 0) + +/* Does A start with B ? */ +#define strstarts(a,b) (strncmp((a),(b),strlen(b)) == 0) + +/* This version adds one byte (for nul term) */ +static void *grab_file(void *ctx, const char *filename) +{ + unsigned int max = 16384, size = 0; + int ret, fd; + char *buffer; + + if (streq(filename, "-")) + fd = dup(STDIN_FILENO); + else + fd = open(filename, O_RDONLY, 0); + + if (fd < 0) + return NULL; + + buffer = talloc_array(ctx, char, max+1); + while ((ret = read(fd, buffer + size, max - size)) > 0) { + size += ret; + if (size == max) + buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1); + } + if (ret < 0) { + talloc_free(buffer); + buffer = NULL; + } else + buffer[size] = '\0'; + close(fd); + return buffer; +} + +/* This is a dumb one which copies. We could mangle instead. */ +static char **split(const char *text) +{ + char **lines = NULL; + unsigned int max = 64, num = 0; + + lines = talloc_array(text, char *, max+1); + + while (*text != '\0') { + unsigned int len = strcspn(text, "\n"); + lines[num] = talloc_array(lines, char, len + 1); + memcpy(lines[num], text, len); + lines[num][len] = '\0'; + text += len + 1; + if (++num == max) + lines = talloc_realloc(text, lines, char *, max*=2 + 1); + } + lines[num] = NULL; + return lines; +} + +int main(int argc, char *argv[]) +{ + unsigned int i, j; + + for (i = 1; i < argc; i++) { + char *file; + char **lines; + bool printing = false, printed = false; + + file = grab_file(NULL, argv[i]); + if (!file) + err(1, "Reading file %s", argv[i]); + lines = split(file); + + for (j = 0; lines[j]; j++) { + if (streq(lines[j], "/**")) { + printing = true; + if (printed++) + puts("\n"); + } else if (streq(lines[j], " */")) + printing = false; + else if (printing) { + if (strstarts(lines[j], " * ")) + puts(lines[j] + 3); + else if (strstarts(lines[j], " *")) + puts(lines[j] + 2); + else + errx(1, "Malformed line %s:%u", + argv[i], j); + } + } + talloc_free(file); + } + return 0; +} + + + diff --git a/tools/namespacize.c b/tools/namespacize.c new file mode 100644 index 00000000..e4a3973f --- /dev/null +++ b/tools/namespacize.c @@ -0,0 +1,691 @@ +/* Code to move a ccan module into the ccan_ namespace. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "talloc/talloc.h" + +#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. -Iccan_tools/libtap/src/" + +#define IDENT_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "abcdefghijklmnopqrstuvwxyz" \ + "01234567889_" + +static bool verbose = false; +static int indent = 0; +#define verbose(args...) \ + do { if (verbose) { \ + unsigned int _i; \ + for (_i = 0; _i < indent; _i++) printf(" "); \ + printf(args); \ + } \ + } while(0) +#define verbose_indent() (indent += 2) +#define verbose_unindent() (indent -= 2) + +#define streq(a,b) (strcmp((a),(b)) == 0) + +#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) + +static inline bool strends(const char *str, const char *postfix) +{ + if (strlen(str) < strlen(postfix)) + return false; + + return streq(str + strlen(str) - strlen(postfix), postfix); +} + +static int close_no_errno(int fd) +{ + int ret = 0, serrno = errno; + if (close(fd) < 0) + ret = errno; + errno = serrno; + return ret; +} + +static int unlink_no_errno(const char *filename) +{ + int ret = 0, serrno = errno; + if (unlink(filename) < 0) + ret = errno; + errno = serrno; + return ret; +} + +static void *grab_fd(const void *ctx, int fd) +{ + int ret; + unsigned int max = 16384, size = 0; + char *buffer; + + buffer = talloc_array(ctx, char, max+1); + while ((ret = read(fd, buffer + size, max - size)) > 0) { + size += ret; + if (size == max) + buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1); + } + if (ret < 0) { + talloc_free(buffer); + buffer = NULL; + } else + buffer[size] = '\0'; + + return buffer; +} + +/* This version adds one byte (for nul term) */ +static void *grab_file(const void *ctx, const char *filename) +{ + int fd; + char *buffer; + + if (streq(filename, "-")) + fd = dup(STDIN_FILENO); + else + fd = open(filename, O_RDONLY, 0); + + if (fd < 0) + return NULL; + + buffer = grab_fd(ctx, fd); + close_no_errno(fd); + return buffer; +} + +/* This is a dumb one which copies. We could mangle instead. */ +static char **split(const void *ctx, const char *text, const char *delims, + unsigned int *nump) +{ + char **lines = NULL; + unsigned int max = 64, num = 0; + + lines = talloc_array(ctx, char *, max+1); + + while (*text != '\0') { + unsigned int len = strcspn(text, delims); + lines[num] = talloc_array(lines, char, len + 1); + memcpy(lines[num], text, len); + lines[num][len] = '\0'; + text += len; + text += strspn(text, delims); + if (++num == max) + lines = talloc_realloc(ctx, lines, char *, max*=2 + 1); + } + lines[num] = NULL; + if (nump) + *nump = num; + return lines; +} + +static char **get_dir(const char *dir) +{ + DIR *d; + struct dirent *ent; + char **names = NULL; + unsigned int size = 0; + + d = opendir(dir); + if (!d) + return NULL; + + while ((ent = readdir(d)) != NULL) { + names = talloc_realloc(dir, names, char *, size + 2); + names[size++] + = talloc_asprintf(names, "%s/%s", dir, ent->d_name); + } + names[size++] = NULL; + closedir(d); + return names; +} + +static char ** __attribute__((format(printf, 2, 3))) +lines_from_cmd(const void *ctx, char *format, ...) +{ + va_list ap; + char *cmd, *buffer; + FILE *p; + + va_start(ap, format); + cmd = talloc_vasprintf(ctx, format, ap); + va_end(ap); + + p = popen(cmd, "r"); + if (!p) + err(1, "Executing '%s'", cmd); + + buffer = grab_fd(ctx, fileno(p)); + if (!buffer) + err(1, "Reading from '%s'", cmd); + pclose(p); + + return split(ctx, buffer, "\n", NULL); +} + +struct replace +{ + struct replace *next; + char *string; +}; + +static void __attribute__((noreturn)) usage(void) +{ + errx(1, "Usage:\n" + "namespacize [--verbose] \n" + "namespacize [--verbose] --adjust ...\n" + "The first form converts dir/ to insert 'ccan_' prefixes, and\n" + "then adjusts any other ccan directories at the same level which\n" + "are effected.\n" + "--adjust does an adjustment for each directory, in case a\n" + "dependency has been namespacized\n"); +} + +static void add_replace(struct replace **repl, const char *str) +{ + struct replace *new, *i; + + /* Avoid duplicates. */ + for (i = *repl; i; i = i->next) + if (streq(i->string, str)) + return; + + new = talloc(*repl, struct replace); + new->next = *repl; + new->string = talloc_strdup(new, str); + *repl = new; +} + +static void add_replace_tok(struct replace **repl, const char *s) +{ + struct replace *new; + unsigned int len = strspn(s, IDENT_CHARS); + + new = talloc(*repl, struct replace); + new->next = *repl; + new->string = talloc_strndup(new, s, len); + *repl = new; +} + +static char *basename(const void *ctx, const char *dir) +{ + char *p = strrchr(dir, '/'); + + if (!p) + return (char *)dir; + return talloc_strdup(ctx, p+1); +} + +static void look_for_macros(char *contents, struct replace **repl) +{ + char *p; + enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART; + + /* Look for lines of form #define X */ + for (p = contents; *p; p++) { + if (*p == '\n') + state = LINESTART; + else if (!isspace(*p)) { + if (state == LINESTART && *p == '#') + state = HASH; + else if (state==HASH && !strncmp(p, "define", 6)) { + state = DEFINE; + p += 5; + } else if (state == DEFINE) { + unsigned int len; + + len = strspn(p, IDENT_CHARS); + if (len) { + char *s; + s = talloc_strndup(contents, p, len); + /* Don't wrap idempotent wrappers */ + if (!strstarts(s, "CCAN_")) { + verbose("Found %s\n", s); + add_replace(repl, s); + } + } + state = NONE; + } else + state = NONE; + } + } +} + +/* Blank out preprocessor lines, and eliminate \ */ +static void preprocess(char *p) +{ + char *s; + + /* We assume backslashes are only used for macros. */ + while ((s = strstr(p, "\\\n")) != NULL) + s[0] = s[1] = ' '; + + /* Now eliminate # lines. */ + if (p[0] == '#') { + unsigned int i; + for (i = 0; p[i] != '\n'; i++) + p[i] = ' '; + } + while ((s = strstr(p, "\n#")) != NULL) { + unsigned int i; + for (i = 1; s[i] != '\n'; i++) + s[i] = ' '; + } +} + +static char *get_statement(const void *ctx, char **p) +{ + unsigned brackets = 0; + bool seen_brackets = false; + char *answer = talloc_strdup(ctx, ""); + + for (;;) { + if ((*p)[0] == '/' && (*p)[1] == '/') + *p += strcspn(*p, "\n"); + else if ((*p)[0] == '/' && (*p)[1] == '*') + *p = strstr(*p, "*/") + 1; + else { + char c = **p; + if (c == ';' && !brackets) { + (*p)++; + return answer; + } + /* Compress whitespace into a single ' ' */ + if (isspace(c)) { + c = ' '; + while (isspace((*p)[1])) + (*p)++; + } else if (c == '{' || c == '(' || c == '[') { + if (c == '(') + seen_brackets = true; + brackets++; + } else if (c == '}' || c == ')' || c == ']') + brackets--; + + if (answer[0] != '\0' || c != ' ') { + answer = talloc_realloc(NULL, answer, char, + strlen(answer) + 2); + answer[strlen(answer)+1] = '\0'; + answer[strlen(answer)] = c; + } + if (c == '}' && seen_brackets && brackets == 0) { + (*p)++; + return answer; + } + } + (*p)++; + if (**p == '\0') + return NULL; + } +} + +/* This hack should handle well-formatted code. */ +static void look_for_definitions(char *contents, struct replace **repl) +{ + char *stmt, *p = contents; + + preprocess(contents); + + while ((stmt = get_statement(contents, &p)) != NULL) { + int i, len; + + /* Definition of struct/union? */ + if ((strncmp(stmt, "struct", 5) == 0 + || strncmp(stmt, "union", 5) == 0) + && strchr(stmt, '{') && stmt[7] != '{') + add_replace_tok(repl, stmt+7); + + /* Definition of var or typedef? */ + for (i = strlen(stmt)-1; i >= 0; i--) + if (strspn(stmt+i, IDENT_CHARS) == 0) + break; + + if (i != strlen(stmt)-1) { + add_replace_tok(repl, stmt+i+1); + continue; + } + + /* function or array declaration? */ + len = strspn(stmt, IDENT_CHARS "* "); + if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) { + if (strspn(stmt + len + 1, IDENT_CHARS) != 0) { + for (i = len-1; i >= 0; i--) + if (strspn(stmt+i, IDENT_CHARS) == 0) + break; + if (i != len-1) { + add_replace_tok(repl, stmt+i+1); + continue; + } + } else { + /* Pointer to function? */ + len++; + len += strspn(stmt + len, " *"); + i = strspn(stmt + len, IDENT_CHARS); + if (i > 0 && stmt[len + i] == ')') + add_replace_tok(repl, stmt+len); + } + } + } +} + +/* FIXME: Only does main header, should chase local includes. */ +static void analyze_headers(const char *dir, struct replace **repl) +{ + char *hdr, *contents; + + /* Get hold of header, assume that's it. */ + hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir)); + contents = grab_file(dir, hdr); + if (!contents) + err(1, "Reading %s", hdr); + + verbose("Looking in %s for macros\n", hdr); + verbose_indent(); + look_for_macros(contents, repl); + verbose_unindent(); + + verbose("Looking in %s for symbols\n", hdr); + verbose_indent(); + look_for_definitions(contents, repl); + verbose_unindent(); +} + +static void write_replacement_file(const char *dir, struct replace **repl) +{ + char *replname = talloc_asprintf(dir, "%s/.namespacize", dir); + int fd; + struct replace *r; + + fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644); + if (fd < 0) { + if (errno == EEXIST) + errx(1, "%s already exists: can't namespacize twice", + replname); + err(1, "Opening %s", replname); + } + + for (r = *repl; r; r = r->next) { + if (write(fd,r->string,strlen(r->string)) != strlen(r->string) + || write(fd, "\n", 1) != 1) { + unlink_no_errno(replname); + if (errno == 0) + errx(1, "Short write to %s: disk full?", + replname); + errx(1, "Writing to %s", replname); + } + } + + close(fd); +} + +static int unlink_destroy(char *name) +{ + unlink(name); + return 0; +} + +static char *find_word(char *f, const char *str) +{ + char *p = f; + + while ((p = strstr(p, str)) != NULL) { + /* Check it's not in the middle of a word. */ + if (p > f && (isalnum(p[-1]) || p[-1] == '_')) { + p++; + continue; + } + if (isalnum(p[strlen(str)]) || p[strlen(str)] == '_') { + p++; + continue; + } + return p; + } + return NULL; +} + +/* This is horribly inefficient but simple. */ +static const char *rewrite_file(const char *filename, + const struct replace *repl) +{ + char *newname, *file; + int fd; + + verbose("Rewriting %s\n", filename); + file = grab_file(filename, filename); + if (!file) + err(1, "Reading file %s", filename); + + for (; repl; repl = repl->next) { + char *p; + + while ((p = find_word(file, repl->string)) != NULL) { + unsigned int off; + char *new = talloc_array(file, char, strlen(file)+6); + + off = p - file; + memcpy(new, file, off); + if (isupper(repl->string[0])) + memcpy(new + off, "CCAN_", 5); + else + memcpy(new + off, "ccan_", 5); + strcpy(new + off + 5, file + off); + file = new; + } + } + + /* If we exit for some reason, we want this erased. */ + newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp", + filename); + fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644); + if (fd < 0) + err(1, "Creating %s", newname); + + talloc_set_destructor(newname, unlink_destroy); + if (write(fd, file, strlen(file)) != strlen(file)) { + if (errno == 0) + errx(1, "Short write to %s: disk full?", newname); + errx(1, "Writing to %s", newname); + } + close(fd); + return newname; +} + +struct adjusted +{ + struct adjusted *next; + const char *file; + const char *tmpfile; +}; + +static void setup_adjust_files(const char *dir, + const struct replace *repl, + struct adjusted **adj) +{ + char **files; + + for (files = get_dir(dir); *files; files++) { + if (strends(*files, "/test")) + setup_adjust_files(*files, repl, adj); + else if (strends(*files, ".c") || strends(*files, ".h")) { + struct adjusted *a = talloc(dir, struct adjusted); + a->next = *adj; + a->file = *files; + a->tmpfile = rewrite_file(a->file, repl); + *adj = a; + } + } +} + +/* This is the "commit" stage, so we hope it won't fail. */ +static void rename_files(const struct adjusted *adj) +{ + while (adj) { + if (rename(adj->tmpfile, adj->file) != 0) + warn("Could not rename over '%s', we're in trouble", + adj->file); + adj = adj->next; + } +} + +static void convert_dir(const char *dir) +{ + char *name; + struct replace *replace = NULL; + struct adjusted *adj = NULL; + + /* Remove any ugly trailing slashes. */ + name = talloc_strdup(NULL, dir); + while (strends(name, "/")) + name[strlen(name)-1] = '\0'; + + analyze_headers(name, &replace); + write_replacement_file(name, &replace); + setup_adjust_files(name, replace, &adj); + rename_files(adj); + talloc_free(name); + talloc_free(replace); +} + +static struct replace *read_replacement_file(const char *depdir) +{ + struct replace *repl = NULL; + char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir); + char *file, **line; + + file = grab_file(replname, replname); + if (!file) { + if (errno != ENOENT) + err(1, "Opening %s", replname); + return NULL; + } + + for (line = split(file, file, "\n", NULL); *line; line++) + add_replace(&repl, *line); + return repl; +} + +static char *build_info(const void *ctx, const char *dir) +{ + char *file, *cfile, *cmd; + + cfile = talloc_asprintf(ctx, "%s/%s", dir, "_info.c"); + file = talloc_asprintf(cfile, "%s/%s", dir, "_info"); + cmd = talloc_asprintf(file, "gcc " CFLAGS " -o %s %s", file, cfile); + if (system(cmd) != 0) + errx(1, "Failed to compile %s", file); + + return file; +} + +static char **get_deps(const void *ctx, const char *dir) +{ + char **deps, *cmd; + + cmd = talloc_asprintf(ctx, "%s depends", build_info(ctx, dir)); + deps = lines_from_cmd(cmd, cmd); + if (!deps) + err(1, "Could not run '%s'", cmd); + return deps; +} + +static char *parent_dir(const void *ctx, const char *dir) +{ + char *parent, *slash; + + parent = talloc_strdup(ctx, dir); + slash = strrchr(parent, '/'); + if (slash) + *slash = '\0'; + else + parent = talloc_strdup(ctx, "."); + return parent; +} + +static void adjust_dir(const char *dir) +{ + char *parent = parent_dir(NULL, dir); + char **deps; + + verbose("Adjusting %s\n", dir); + verbose_indent(); + for (deps = get_deps(parent, dir); *deps; deps++) { + char *depdir; + struct adjusted *adj = NULL; + struct replace *repl; + + depdir = talloc_asprintf(parent, "%s/%s", parent, *deps); + repl = read_replacement_file(depdir); + if (repl) { + verbose("%s has been namespacized\n", depdir); + setup_adjust_files(parent, repl, &adj); + rename_files(adj); + } else + verbose("%s has not been namespacized\n", depdir); + talloc_free(depdir); + } + verbose_unindent(); +} + +static void adjust_dependents(const char *dir) +{ + char *parent = parent_dir(NULL, dir); + char *base = basename(parent, dir); + char **file; + + verbose("Looking for dependents in %s\n", parent); + verbose_indent(); + for (file = get_dir(parent); *file; file++) { + char *infoc, **deps; + bool isdep = false; + + if (basename(*file, *file)[0] == '.') + continue; + + infoc = talloc_asprintf(*file, "%s/_info.c", *file); + if (access(infoc, R_OK) != 0) + continue; + + for (deps = get_deps(*file, *file); *deps; deps++) { + if (streq(*deps, base)) + isdep = true; + } + if (isdep) + adjust_dir(*file); + else + verbose("%s is not dependent\n", *file); + } + verbose_unindent(); +} + +int main(int argc, char *argv[]) +{ + if (argv[1] && streq(argv[1], "--verbose")) { + verbose = true; + argv++; + argc--; + } + + if (argc == 2) { + verbose("Namespacizing %s\n", argv[1]); + verbose_indent(); + convert_dir(argv[1]); + adjust_dependents(argv[1]); + verbose_unindent(); + return 0; + } + + if (argc > 2 && streq(argv[1], "--adjust")) { + unsigned int i; + + for (i = 2; i < argc; i++) + adjust_dir(argv[i]); + return 0; + } + usage(); +} diff --git a/tools/run_tests.c b/tools/run_tests.c new file mode 100644 index 00000000..1a8d75ea --- /dev/null +++ b/tools/run_tests.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include "ccan/tap/tap.h" +#include "ccan/talloc/talloc.h" +#include "ccan/string/string.h" + +#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -Iccan -I." + +/* FIXME: Use build bug later. */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static struct test *tests = NULL; +static struct obj *objs = NULL; +static int verbose; + +struct test_type +{ + const char *name; + void (*testfn)(struct test_type *t, const char *name); +}; + +struct test +{ + struct test *next; + struct test_type *type; + char *name; +}; + +struct obj +{ + struct obj *next; + char *name; +}; + +static char *output_name(const char *name) +{ + char *ret; + + assert(strends(name, ".c")); + + ret = talloc_strdup(name, name); + ret[strlen(ret) - 2] = '\0'; + return ret; +} + +static char *obj_list(void) +{ + char *list = talloc_strdup(objs, ""); + struct obj *i; + + for (i = objs; i; i = i->next) + list = talloc_asprintf_append(list, "%s ", i->name); + + /* FIXME */ + list = talloc_asprintf_append(list, "ccan/tap/tap.o"); + return list; +} + +static void compile_objs(void) +{ + struct obj *i; + + for (i = objs; i; i = i->next) { + char *cmd = talloc_asprintf(i, "gcc " CFLAGS " -o %s.o -c %s%s", + output_name(i->name), i->name, + verbose ? "" : "> /dev/null 2>&1"); + ok(system(cmd) == 0, "%s", cmd); + } +} + +static void cleanup_objs(void) +{ + struct obj *i; + + for (i = objs; i; i = i->next) + unlink(talloc_asprintf(i, "%s.o", output_name(i->name))); +} + +static void add_test(const char *testdir, const char *name, struct test_type *t) +{ + struct test *test = talloc(testdir, struct test); + + test->next = tests; + test->type = t; + test->name = talloc_asprintf(test, "%s/%s", testdir, name); + tests = test; +} + +static void add_obj(const char *testdir, const char *name) +{ + struct obj *obj = talloc(testdir, struct obj); + + obj->next = objs; + obj->name = talloc_asprintf(obj, "%s/%s", testdir, name); + objs = obj; +} + +static int build(const char *name, int fail) +{ + const char *cmd; + int ret; + + cmd = talloc_asprintf(name, "gcc " CFLAGS " %s -o %s %s %s%s", + fail ? "-DFAIL" : "", + output_name(name), name, obj_list(), + verbose ? "" : "> /dev/null 2>&1"); + + if (verbose) + fprintf(stderr, "Running %s\n", cmd); + + ret = system(cmd); + if (ret == -1) + diag("cmd '%s' failed to execute", cmd); + + return ret; +} + +static void compile_ok(struct test_type *t, const char *name) +{ + ok(build(name, 0) == 0, "%s %s", t->name, name); +} + +static void compile_fail(struct test_type *t, const char *name) +{ + if (build(name, 0) != 0) + fail("non-FAIL build %s", name); + else + ok(build(name, 1) > 0, "%s %s", t->name, name); +} + +static void run(const char *name) +{ + if (system(output_name(name)) == -1) + fail("running %s had error %m", name); +} + +static void cleanup(const char *name) +{ + unlink(output_name(name)); +} + +static struct test_type test_types[] = { + { "compile_ok", compile_ok }, + { "compile_fail", compile_fail }, + { "run", compile_ok }, +}; + +int main(int argc, char *argv[]) +{ + DIR *dir; + struct dirent *d; + char *testdir; + struct test *test; + unsigned int num_tests = 0, num_objs = 0; + + if (argc > 1 && streq(argv[1], "--verbose")) { + verbose = 1; + argc--; + argv++; + } + + if (argc != 2) + errx(1, "Usage: run_tests [--verbose] "); + + testdir = talloc_asprintf(NULL, "%s/test", argv[1]); + dir = opendir(testdir); + if (!dir) + err(1, "Opening '%s'", testdir); + + while ((d = readdir(dir)) != NULL) { + unsigned int i; + if (d->d_name[0] == '.' || !strends(d->d_name, ".c")) + continue; + + for (i = 0; i < ARRAY_SIZE(test_types); i++) { + if (strstarts(d->d_name, test_types[i].name)) { + add_test(testdir, d->d_name, &test_types[i]); + num_tests++; + break; + } + } + if (i == ARRAY_SIZE(test_types)) { + add_obj(testdir, d->d_name); + num_objs++; + } + } + + plan_tests(num_tests + num_objs); + /* First all the extra object compilations. */ + compile_objs(); + + /* Do all the test compilations. */ + for (test = tests; test; test = test->next) + test->type->testfn(test->type, test->name); + + cleanup_objs(); + + /* Now run all the ones which wanted to run. */ + for (test = tests; test; test = test->next) { + if (streq(test->type->name, "run")) + run(test->name); + cleanup(test->name); + } + + exit(exit_status()); +} diff --git a/tools/test_all.sh b/tools/test_all.sh new file mode 100755 index 00000000..3c65c21c --- /dev/null +++ b/tools/test_all.sh @@ -0,0 +1,22 @@ +#! /bin/sh + +# First, test normal config. +if ! make -s; then + echo Normal config failed. + exit 1 +fi + +# Now, remove one HAVE_ at a time. +cp config.h original-config.h +trap "mv original-config.h config.h && rm -f .newconfig" EXIT + +while grep -q '1$' config.h; do + tr '\012' @ < config.h | sed 's/1@/0@/' | tr @ '\012' > .newconfig + diff -u config.h .newconfig + mv .newconfig config.h + if ! make -s; then + echo Failed config: + cat config.h + exit 1 + fi +done diff --git a/typesafe_cb/_info.c b/typesafe_cb/_info.c deleted file mode 100644 index 725b2665..00000000 --- a/typesafe_cb/_info.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include "config.h" - -/** - * typesafe_cb - macros for safe callbacks. - * - * The basis of the typesafe_cb header is cast_if_type(): a - * conditional cast macro. If an expression exactly matches a given - * type, it is cast to the target type, otherwise it is left alone. - * - * This allows us to create functions which take a small number of - * specific types, rather than being forced to use a void *. In - * particular, it is useful for creating typesafe callbacks as the - * helpers typesafe_cb(), typesafe_cb_preargs() and - * typesafe_cb_postargs() demonstrate. - * - * The standard way of passing arguments to callback functions in C is - * to use a void pointer, which the callback then casts back to the - * expected type. This unfortunately subverts the type checking the - * compiler would perform if it were a direct call. Here's an example: - * - * static void my_callback(void *_obj) - * { - * struct obj *obj = _obj; - * ... - * } - * ... - * register_callback(my_callback, &my_obj); - * - * If we wanted to use the natural type for my_callback (ie. "void - * my_callback(struct obj *obj)"), we could make register_callback() - * take a void * as its first argument, but this would subvert all - * type checking. We really want register_callback() to accept only - * the exactly correct function type to match the argument, or a - * function which takes a void *. - * - * This is where typesafe_cb() comes in: it uses cast_if_type() to - * cast the callback function if it matches the argument type: - * - * void _register_callback(void (*cb)(void *arg), void *arg); - * #define register_callback(cb, arg) \ - * _register_callback(typesafe_cb(void, (cb), (arg)), (arg)) - * - * On compilers which don't support the extensions required - * cast_if_type() and friend become an unconditional cast, so your - * code will compile but you won't get type checking. - */ -int main(int argc, char *argv[]) -{ - if (argc != 2) - return 1; - - if (strcmp(argv[1], "depends") == 0) { - return 0; - } - - return 1; -} diff --git a/typesafe_cb/test/compile_fail-cast_if_type.c b/typesafe_cb/test/compile_fail-cast_if_type.c deleted file mode 100644 index 1e417cba..00000000 --- a/typesafe_cb/test/compile_fail-cast_if_type.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "typesafe_cb/typesafe_cb.h" - -void _set_some_value(void *val); - -void _set_some_value(void *val) -{ -} - -#define set_some_value(expr) \ - _set_some_value(cast_if_type((expr), unsigned long, void *)) - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - int x = 0; - set_some_value(x); -#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P -#error "Unfortunately we don't fail if cast_if_type is a noop." -#endif -#else - void *p = 0; - set_some_value(p); -#endif - return 0; -} diff --git a/typesafe_cb/test/compile_fail-typesafe_cb-int.c b/typesafe_cb/test/compile_fail-typesafe_cb-int.c deleted file mode 100644 index 54d0e7ba..00000000 --- a/typesafe_cb/test/compile_fail-typesafe_cb-int.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "typesafe_cb/typesafe_cb.h" -#include - -void _callback(void (*fn)(void *arg), void *arg); -void _callback(void (*fn)(void *arg), void *arg) -{ - fn(arg); -} - -/* Callback is set up to warn if arg isn't a pointer (since it won't - * pass cleanly to _callback's second arg. */ -#define callback(fn, arg) \ - _callback(typesafe_cb(void, (fn), (arg)), (arg)) - -void my_callback(int something); -void my_callback(int something) -{ -} - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - /* This fails due to arg, not due to cast. */ - callback(my_callback, 100); -#endif - return 0; -} diff --git a/typesafe_cb/test/compile_fail-typesafe_cb.c b/typesafe_cb/test/compile_fail-typesafe_cb.c deleted file mode 100644 index d305d5fb..00000000 --- a/typesafe_cb/test/compile_fail-typesafe_cb.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "typesafe_cb/typesafe_cb.h" -#include - -static void _register_callback(void (*cb)(void *arg), void *arg) -{ -} - -#define register_callback(cb, arg) \ - _register_callback(typesafe_cb(void, (cb), (arg)), (arg)) - -static void my_callback(char *p) -{ -} - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - int *p; -#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P -#error "Unfortunately we don't fail if cast_if_type is a noop." -#endif -#else - char *p; -#endif - p = NULL; - - /* This should work always. */ - register_callback(my_callback, "hello world"); - - /* This will fail with FAIL defined */ - register_callback(my_callback, p); - return 0; -} diff --git a/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c b/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c deleted file mode 100644 index 099eb257..00000000 --- a/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "typesafe_cb/typesafe_cb.h" -#include - -static void _register_callback(void (*cb)(void *arg, int x), void *arg) -{ -} -#define register_callback(cb, arg) \ - _register_callback(typesafe_cb_postargs(void, (cb), (arg), int), (arg)) - -static void my_callback(char *p, int x) -{ -} - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - int *p; -#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P -#error "Unfortunately we don't fail if cast_if_type is a noop." -#endif -#else - char *p; -#endif - p = NULL; - register_callback(my_callback, p); - return 0; -} diff --git a/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c b/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c deleted file mode 100644 index 38daec51..00000000 --- a/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "typesafe_cb/typesafe_cb.h" -#include - -static void _register_callback(void (*cb)(int x, void *arg), void *arg) -{ -} - -#define register_callback(cb, arg) \ - _register_callback(typesafe_cb_preargs(void, (cb), (arg), int), (arg)) - -static void my_callback(int x, char *p) -{ -} - -int main(int argc, char *argv[]) -{ -#ifdef FAIL - int *p; -#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P -#error "Unfortunately we don't fail if cast_if_type is a noop." -#endif -#else - char *p; -#endif - p = NULL; - register_callback(my_callback, p); - return 0; -} diff --git a/typesafe_cb/test/run.c b/typesafe_cb/test/run.c deleted file mode 100644 index b04fae88..00000000 --- a/typesafe_cb/test/run.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "typesafe_cb/typesafe_cb.h" -#include -#include "tap/tap.h" - -static char dummy = 0; - -/* The example usage. */ -static void _set_some_value(void *val) -{ - ok1(val == &dummy); -} - -#define set_some_value(expr) \ - _set_some_value(cast_if_type((expr), unsigned long, void *)) - -static void _callback_onearg(void (*fn)(void *arg), void *arg) -{ - fn(arg); -} - -static void _callback_preargs(void (*fn)(int a, int b, void *arg), void *arg) -{ - fn(1, 2, arg); -} - -static void _callback_postargs(void (*fn)(void *arg, int a, int b), void *arg) -{ - fn(arg, 1, 2); -} - -#define callback_onearg(cb, arg) \ - _callback_onearg(typesafe_cb(void, (cb), (arg)), (arg)) - -#define callback_preargs(cb, arg) \ - _callback_preargs(typesafe_cb_preargs(void, (cb), (arg), int, int), (arg)) - -#define callback_postargs(cb, arg) \ - _callback_postargs(typesafe_cb_postargs(void, (cb), (arg), int, int), (arg)) - -static void my_callback_onearg(char *p) -{ - ok1(strcmp(p, "hello world") == 0); -} - -static void my_callback_onearg_const(const char *p) -{ - ok1(strcmp(p, "hello world") == 0); -} - -static void my_callback_onearg_volatile(volatile char *p) -{ - ok1(strcmp((char *)p, "hello world") == 0); -} - -static void my_callback_preargs(int a, int b, char *p) -{ - ok1(a == 1); - ok1(b == 2); - ok1(strcmp(p, "hello world") == 0); -} - -static void my_callback_preargs_const(int a, int b, const char *p) -{ - ok1(a == 1); - ok1(b == 2); - ok1(strcmp(p, "hello world") == 0); -} - -static void my_callback_preargs_volatile(int a, int b, volatile char *p) -{ - ok1(a == 1); - ok1(b == 2); - ok1(strcmp((char *)p, "hello world") == 0); -} - -static void my_callback_postargs(char *p, int a, int b) -{ - ok1(a == 1); - ok1(b == 2); - ok1(strcmp(p, "hello world") == 0); -} - -static void my_callback_postargs_const(const char *p, int a, int b) -{ - ok1(a == 1); - ok1(b == 2); - ok1(strcmp(p, "hello world") == 0); -} - -static void my_callback_postargs_volatile(volatile char *p, int a, int b) -{ - ok1(a == 1); - ok1(b == 2); - ok1(strcmp((char *)p, "hello world") == 0); -} - -/* This is simply a compile test; we promised cast_if_type can be in a - * static initializer. */ -struct callback_onearg -{ - void (*fn)(void *arg); - void *arg; -}; - -struct callback_onearg cb_onearg -= { typesafe_cb(void, my_callback_onearg, "hello world"), "hello world" }; - -struct callback_preargs -{ - void (*fn)(int a, int b, void *arg); - void *arg; -}; - -struct callback_preargs cb_preargs -= { typesafe_cb_preargs(void, my_callback_preargs, "hi", int, int), "hi" }; - -struct callback_postargs -{ - void (*fn)(void *arg, int a, int b); - void *arg; -}; - -struct callback_postargs cb_postargs -= { typesafe_cb_postargs(void, my_callback_postargs, "hi", int, int), "hi" }; - -int main(int argc, char *argv[]) -{ - void *p = &dummy; - unsigned long l = (unsigned long)p; - - plan_tests(2 + 3 + 9 + 9); - set_some_value(p); - set_some_value(l); - - callback_onearg(my_callback_onearg, "hello world"); - callback_onearg(my_callback_onearg_const, "hello world"); - callback_onearg(my_callback_onearg_volatile, "hello world"); - - callback_preargs(my_callback_preargs, "hello world"); - callback_preargs(my_callback_preargs_const, "hello world"); - callback_preargs(my_callback_preargs_volatile, "hello world"); - - callback_postargs(my_callback_postargs, "hello world"); - callback_postargs(my_callback_postargs_const, "hello world"); - callback_postargs(my_callback_postargs_volatile, "hello world"); - - return exit_status(); -} diff --git a/typesafe_cb/typesafe_cb.h b/typesafe_cb/typesafe_cb.h deleted file mode 100644 index cc4ea8ad..00000000 --- a/typesafe_cb/typesafe_cb.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef CCAN_CAST_IF_TYPE_H -#define CCAN_CAST_IF_TYPE_H -#include "config.h" - -#if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P -/** - * cast_if_type - only cast an expression if it is of a given type - * @expr: the expression to cast - * @oktype: the type we allow - * @desttype: the type to cast to - * - * This macro is used to create functions which allow multiple types. - * The result of this macro is used somewhere that a @desttype type is - * expected: if @expr was of type @oktype, it will be cast to - * @desttype type. As a result, if @expr is any type other than - * @oktype or @desttype, a compiler warning will be issued. - * - * This macro can be used in static initializers. - * - * This is merely useful for warnings: if the compiler does not - * support the primitives required for cast_if_type(), it becomes an - * unconditional cast, and the @oktype argument is not used. In - * particular, this means that @oktype can be a type which uses - * the "typeof": it will not be evaluated if typeof is not supported. - * - * Example: - * // We can take either an unsigned long or a void *. - * void _set_some_value(void *val); - * #define set_some_value(expr) \ - * _set_some_value(cast_if_type((expr), unsigned long, void *)) - */ -#define cast_if_type(expr, oktype, desttype) \ -__builtin_choose_expr(__builtin_types_compatible_p(typeof(1?(expr):0), oktype), \ - (desttype)(expr), (expr)) -#else -#define cast_if_type(expr, oktype, desttype) ((desttype)(expr)) -#endif - -/** - * typesafe_cb - cast a callback function if it matches the arg - * @rettype: the return type of the callback function - * @fn: the callback function to cast - * @arg: the (pointer) argument to hand to the callback function. - * - * If a callback function takes a single argument, this macro does - * appropriate casts to a function which takes a single void * argument if the - * callback provided matches the @arg (or a const or volatile version). - * - * It is assumed that @arg is of pointer type: usually @arg is passed - * or assigned to a void * elsewhere anyway. - * - * Example: - * void _register_callback(void (*fn)(void *arg), void *arg); - * #define register_callback(fn, arg) \ - * _register_callback(typesafe_cb(void, (fn), (arg)), (arg)) - */ -#define typesafe_cb(rettype, fn, arg) \ - cast_if_type(cast_if_type(cast_if_type((fn), \ - rettype (*)(const typeof(arg)), \ - rettype (*)(void *)), \ - rettype (*)(volatile typeof(arg)), \ - rettype (*)(void *)), \ - rettype (*)(typeof(arg)), \ - rettype (*)(void *)) - -/** - * typesafe_cb_preargs - cast a callback function if it matches the arg - * @rettype: the return type of the callback function - * @fn: the callback function to cast - * @arg: the (pointer) argument to hand to the callback function. - * - * This is a version of typesafe_cb() for callbacks that take other arguments - * before the @arg. - * - * Example: - * void _register_callback(void (*fn)(int, void *arg), void *arg); - * #define register_callback(fn, arg) \ - * _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ - * (arg)) - */ -#define typesafe_cb_preargs(rettype, fn, arg, ...) \ - cast_if_type(cast_if_type(cast_if_type((fn), \ - rettype (*)(__VA_ARGS__, \ - const typeof(arg)), \ - rettype (*)(__VA_ARGS__, \ - void *)), \ - rettype (*)(__VA_ARGS__, \ - volatile typeof(arg)), \ - rettype (*)(__VA_ARGS__, void *)), \ - rettype (*)(__VA_ARGS__, typeof(arg)), \ - rettype (*)(__VA_ARGS__, void *)) - -/** - * typesafe_cb_postargs - cast a callback function if it matches the arg - * @rettype: the return type of the callback function - * @fn: the callback function to cast - * @arg: the (pointer) argument to hand to the callback function. - * - * This is a version of typesafe_cb() for callbacks that take other arguments - * after the @arg. - * - * Example: - * void _register_callback(void (*fn)(void *arg, int), void *arg); - * #define register_callback(fn, arg) \ - * _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ - * (arg)) - */ -#define typesafe_cb_postargs(rettype, fn, arg, ...) \ - cast_if_type(cast_if_type(cast_if_type((fn), \ - rettype (*)(const typeof(arg), \ - __VA_ARGS__), \ - rettype (*)(void *, \ - __VA_ARGS__)), \ - rettype (*)(volatile typeof(arg), \ - __VA_ARGS__), \ - rettype (*)(void *, __VA_ARGS__)), \ - rettype (*)(typeof(arg), __VA_ARGS__), \ - rettype (*)(void *, __VA_ARGS__)) - -#endif /* CCAN_CAST_IF_TYPE_H */