diff options
author | Richard Braun <rbraun@sceen.net> | 2013-06-07 20:44:18 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2013-06-07 20:44:18 +0200 |
commit | 271920aa32395874bb37145d3edb1158217095c9 (patch) | |
tree | 475b46ca857ea7903a9d818998871af7400d4bd9 | |
parent | d7e9a6374fdbd90a0ee592fc4233549bb02dfc8c (diff) |
Remove non data-structure code
The primary purpose of this project was testing code in userspace before
pushing it to the x15 microkernel. The xprintf and phys modules were
included quite some time ago now, along with the mem module which is also
present in the GNU Mach microkernel.
The secondary goal of this project was to create good implementations of
commonly used data structures, mainly the doubly linked list and the
red-black tree, under a BSD license so that they're reusable in proprietary
projects I have to work on. Such generic code remains in this repository.
-rw-r--r-- | Makefile.am | 54 | ||||
-rw-r--r-- | mem.c | 1816 | ||||
-rw-r--r-- | mem.h | 120 | ||||
-rw-r--r-- | mem_malloc.c | 153 | ||||
-rw-r--r-- | mem_malloc.h | 42 | ||||
-rw-r--r-- | phys.c | 777 | ||||
-rw-r--r-- | phys.h | 66 | ||||
-rw-r--r-- | test/test_mem.c | 57 | ||||
-rw-r--r-- | test/test_mem_cache.c | 145 | ||||
-rw-r--r-- | test/test_mem_cache_double_free.c | 75 | ||||
-rw-r--r-- | test/test_mem_cache_invalid_free.c | 74 | ||||
-rw-r--r-- | test/test_mem_cache_write_beyond.c | 90 | ||||
-rw-r--r-- | test/test_mem_cache_write_buftag.c | 92 | ||||
-rw-r--r-- | test/test_mem_cache_write_free.c | 106 | ||||
-rw-r--r-- | test/test_mem_offbyone.c | 58 | ||||
-rw-r--r-- | test/test_phys.c | 65 | ||||
-rw-r--r-- | test/test_xprintf.c | 298 | ||||
-rw-r--r-- | xprintf.c | 591 | ||||
-rw-r--r-- | xprintf.h | 58 |
19 files changed, 3 insertions, 4734 deletions
diff --git a/Makefile.am b/Makefile.am index 55da9a2..d685fce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,72 +25,24 @@ librbraun_la_SOURCES = \ list.c \ list.h \ macros.h \ - mem.c \ - mem.h \ - mem_malloc.c \ - mem_malloc.h \ - phys.c \ - phys.h \ rbtree.c \ rbtree.h \ rbtree_i.h \ rdxtree.c \ - rdxtree.h \ - xprintf.c \ - xprintf.h + rdxtree.h + librbraun_la_LIBADD = -lrt -lpthread bin_PROGRAMS = \ test_avltree \ - test_mem \ - test_mem_cache \ - test_mem_cache_invalid_free \ - test_mem_cache_double_free \ - test_mem_cache_write_free \ - test_mem_cache_write_beyond \ - test_mem_cache_write_buftag \ - test_mem_offbyone \ - test_phys \ test_rbtree \ - test_rdxtree \ - test_xprintf + test_rdxtree test_avltree_SOURCES = test/test_avltree.c test_avltree_LDADD = librbraun.la -test_mem_SOURCES = test/test_mem.c -test_mem_LDADD = librbraun.la - -test_mem_cache_SOURCES = test/test_mem_cache.c -test_mem_cache_LDADD = -lrt -lpthread - -test_mem_cache_invalid_free_SOURCES = test/test_mem_cache_invalid_free.c -test_mem_cache_invalid_free_LDADD = librbraun.la - -test_mem_cache_double_free_SOURCES = test/test_mem_cache_double_free.c -test_mem_cache_double_free_LDADD = librbraun.la - -test_mem_cache_write_free_SOURCES = test/test_mem_cache_write_free.c -test_mem_cache_write_free_LDADD = librbraun.la - -test_mem_cache_write_beyond_SOURCES = test/test_mem_cache_write_beyond.c -test_mem_cache_write_beyond_LDADD = librbraun.la - -test_mem_cache_write_buftag_SOURCES = test/test_mem_cache_write_buftag.c -test_mem_cache_write_buftag_LDADD = librbraun.la - -test_mem_offbyone_SOURCES = test/test_mem_offbyone.c -test_mem_offbyone_LDADD = librbraun.la - -test_phys_SOURCES = test/test_phys.c -test_phys_LDADD = -lrt -lpthread - test_rbtree_SOURCES = test/test_rbtree.c test_rbtree_LDADD = librbraun.la test_rdxtree_SOURCES = test/test_rdxtree.c test_rdxtree_LDADD = -lrt -lpthread - -test_xprintf_SOURCES = test/test_xprintf.c -test_xprintf_CFLAGS = $(AM_CFLAGS) -Wno-format -test_xprintf_LDADD = librbraun.la @@ -1,1816 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Richard Braun. - * 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 ``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 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. - * - * - * Object caching and general purpose memory allocator. - * - * This allocator is based on the following works : - * - "The Slab Allocator: An Object-Caching Kernel Memory Allocator", - * by Jeff Bonwick. - * - * It allows the allocation of objects (i.e. fixed-size typed buffers) from - * caches and is efficient in both space and time. This implementation follows - * many of the indications from the paper mentioned. The most notable - * differences are outlined below. - * - * The per-cache self-scaling hash table for buffer-to-bufctl conversion, - * described in 3.2.3 "Slab Layout for Large Objects", has been replaced by - * an AVL tree storing slabs, sorted by address. The use of a self-balancing - * tree for buffer-to-slab conversions provides a few advantages over a hash - * table. Unlike a hash table, a BST provides a "lookup nearest" operation, - * so obtaining the slab data (whether it is embedded in the slab or off - * slab) from a buffer address simply consists of a "lookup nearest towards - * 0" tree search. Storing slabs instead of buffers also considerably reduces - * the number of elements to retain. Finally, a self-balancing tree is a true - * self-scaling data structure, whereas a hash table requires periodic - * maintenance and complete resizing, which is expensive. The only drawback is - * that releasing a buffer to the slab layer takes logarithmic time instead of - * constant time. But as the data set size is kept reasonable (because slabs - * are stored instead of buffers) and because the CPU pool layer services most - * requests, avoiding many accesses to the slab layer, it is considered an - * acceptable tradeoff. - * - * This implementation uses per-cpu pools of objects, which service most - * allocation requests. These pools act as caches (but are named differently - * to avoid confusion with CPU caches) that reduce contention on multiprocessor - * systems. When a pool is empty and cannot provide an object, it is filled by - * transferring multiple objects from the slab layer. The symmetric case is - * handled likewise. - */ - -#include <time.h> -#include <errno.h> -#include <sched.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> -#include <sys/mman.h> - -#include "cpu.h" -#include "mem.h" -#include "list.h" -#include "error.h" -#include "macros.h" -#include "avltree.h" - -/* - * The system page size. - * - * This macro actually expands to a global variable that is set on - * initialization. - */ -#define PAGE_SIZE ((unsigned long)_pagesize) - -/* - * Minimum required alignment. - */ -#define MEM_ALIGN_MIN 8 - -/* - * Minimum number of buffers per slab. - * - * This value is ignored when the slab size exceeds a threshold. - */ -#define MEM_MIN_BUFS_PER_SLAB 8 - -/* - * Special slab size beyond which the minimum number of buffers per slab is - * ignored when computing the slab size of a cache. - */ -#define MEM_SLAB_SIZE_THRESHOLD (8 * PAGE_SIZE) - -/* - * Special buffer size under which slab data is unconditionnally allocated - * from its associated slab. - */ -#define MEM_BUF_SIZE_THRESHOLD (PAGE_SIZE / 8) - -/* - * Time (in seconds) between two garbage collection operations. - */ -#define MEM_GC_INTERVAL 15 - -/* - * The transfer size of a CPU pool is computed by dividing the pool size by - * this value. - */ -#define MEM_CPU_POOL_TRANSFER_RATIO 2 - -/* - * Shift for the first general cache size. - */ -#define MEM_CACHES_FIRST_SHIFT 5 - -/* - * Number of caches backing general purpose allocations. - */ -#define MEM_NR_MEM_CACHES 13 - -/* - * Per-processor cache of pre-constructed objects. - * - * The flags member is a read-only CPU-local copy of the parent cache flags. - */ -struct mem_cpu_pool { - pthread_mutex_t lock; - int flags; - int size; - int transfer_size; - int nr_objs; - void **array; -} __aligned(CPU_L1_SIZE); - -/* - * When a cache is created, its CPU pool type is determined from the buffer - * size. For small buffer sizes, many objects can be cached in a CPU pool. - * Conversely, for large buffer sizes, this would incur much overhead, so only - * a few objects are stored in a CPU pool. - */ -struct mem_cpu_pool_type { - size_t buf_size; - int array_size; - size_t array_align; - struct mem_cache *array_cache; -}; - -/* - * Buffer descriptor. - * - * For normal caches (i.e. without MEM_CF_VERIFY), bufctls are located at the - * end of (but inside) each buffer. If MEM_CF_VERIFY is set, bufctls are located - * after each buffer. - * - * When an object is allocated to a client, its bufctl isn't used. This memory - * is instead used for redzoning if cache debugging is in effect. - */ -union mem_bufctl { - union mem_bufctl *next; - unsigned long redzone; -}; - -/* - * Redzone guard word. - */ -#ifdef __LP64__ -#if _HOST_BIG_ENDIAN -#define MEM_REDZONE_WORD 0xfeedfacefeedfaceUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_REDZONE_WORD 0xcefaedfecefaedfeUL -#endif /* _HOST_BIG_ENDIAN */ -#else /* __LP64__ */ -#if _HOST_BIG_ENDIAN -#define MEM_REDZONE_WORD 0xfeedfaceUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_REDZONE_WORD 0xcefaedfeUL -#endif /* _HOST_BIG_ENDIAN */ -#endif /* __LP64__ */ - -/* - * Redzone byte for padding. - */ -#define MEM_REDZONE_BYTE 0xbb - -/* - * Buffer tag. - * - * This structure is only used for MEM_CF_VERIFY caches. It is located after - * the bufctl and includes information about the state of the buffer it - * describes (allocated or not). It should be thought of as a debugging - * extension of the bufctl. - */ -struct mem_buftag { - unsigned long state; -}; - -/* - * Values the buftag state member can take. - */ -#ifdef __LP64__ -#if _HOST_BIG_ENDIAN -#define MEM_BUFTAG_ALLOC 0xa110c8eda110c8edUL -#define MEM_BUFTAG_FREE 0xf4eeb10cf4eeb10cUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_BUFTAG_ALLOC 0xedc810a1edc810a1UL -#define MEM_BUFTAG_FREE 0x0cb1eef40cb1eef4UL -#endif /* _HOST_BIG_ENDIAN */ -#else /* __LP64__ */ -#if _HOST_BIG_ENDIAN -#define MEM_BUFTAG_ALLOC 0xa110c8edUL -#define MEM_BUFTAG_FREE 0xf4eeb10cUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_BUFTAG_ALLOC 0xedc810a1UL -#define MEM_BUFTAG_FREE 0x0cb1eef4UL -#endif /* _HOST_BIG_ENDIAN */ -#endif /* __LP64__ */ - -/* - * Free and uninitialized patterns. - * - * These values are unconditionnally 64-bit wide since buffers are at least - * 8-byte aligned. - */ -#if _HOST_BIG_ENDIAN -#define MEM_FREE_PATTERN 0xdeadbeefdeadbeefULL -#define MEM_UNINIT_PATTERN 0xbaddcafebaddcafeULL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_FREE_PATTERN 0xefbeaddeefbeaddeULL -#define MEM_UNINIT_PATTERN 0xfecaddbafecaddbaULL -#endif /* _HOST_BIG_ENDIAN */ - -/* - * Page-aligned collection of unconstructed buffers. - */ -struct mem_slab { - struct list list_node; - struct avltree_node tree_node; - unsigned long nr_refs; - union mem_bufctl *first_free; - void *addr; -}; - -/* - * Private cache creation flags. - */ -#define MEM_CREATE_INTERNAL 0x0100 /* Prevent off slab data */ - -/* - * Cache name buffer size. - */ -#define MEM_NAME_SIZE 32 - -/* - * Cache flags. - * - * The flags don't change once set and can be tested without locking. - */ -#define MEM_CF_DIRECT 0x0001 /* No buf-to-slab tree lookup */ -#define MEM_CF_SLAB_EXTERNAL 0x0002 /* Slab data is off slab */ - -/* - * Debugging flags - */ -#define MEM_CF_VERIFY 0x0100 /* Use debugging facilities */ - -/* - * Cache of objects. - * - * Locking order : cpu_pool -> cache. CPU pools locking is ordered by CPU ID. - * - * The partial slabs list is sorted by slab references. Slabs with a high - * number of references are placed first on the list to reduce fragmentation. - * Sorting occurs at insertion/removal of buffers in a slab. As the list - * is maintained sorted, and the number of references only changes by one, - * this is a very cheap operation in the average case and the worst (linear) - * case is very unlikely. - */ -struct mem_cache { - /* CPU pool layer */ - struct mem_cpu_pool cpu_pools[NR_CPUS]; - struct mem_cpu_pool_type *cpu_pool_type; - - /* Slab layer */ - pthread_mutex_t lock; - struct list node; /* Cache list linkage */ - struct list partial_slabs; - struct list free_slabs; - struct avltree active_slabs; - int flags; - size_t obj_size; /* User-provided size */ - size_t align; - size_t buf_size; /* Aligned object size */ - size_t bufctl_dist; /* Distance from buffer to bufctl */ - size_t slab_size; - size_t color; - size_t color_max; - unsigned long bufs_per_slab; - unsigned long nr_objs; /* Number of allocated objects */ - unsigned long nr_bufs; /* Total number of buffers */ - unsigned long nr_slabs; - unsigned long nr_free_slabs; - mem_cache_ctor_t ctor; - struct mem_source source; - char name[MEM_NAME_SIZE]; - size_t buftag_dist; /* Distance from buffer to buftag */ - size_t redzone_pad; /* Bytes from end of object to redzone word */ -}; - -/* - * Options for mem_cache_alloc_verify(). - */ -#define MEM_AV_NOCONSTRUCT 0 -#define MEM_AV_CONSTRUCT 1 - -/* - * Error codes for mem_cache_error(). - */ -#define MEM_ERR_INVALID 0 /* Invalid address being freed */ -#define MEM_ERR_DOUBLEFREE 1 /* Freeing already free address */ -#define MEM_ERR_BUFTAG 2 /* Invalid buftag content */ -#define MEM_ERR_MODIFIED 3 /* Buffer modified while free */ -#define MEM_ERR_REDZONE 4 /* Redzone violation */ - -/* - * See PAGE_SIZE. - */ -static long _pagesize; - -/* - * Available CPU pool types. - * - * For each entry, the CPU pool size applies from the entry buf_size - * (excluded) up to (and including) the buf_size of the preceding entry. - * - * See struct cpu_pool_type for a description of the values. - */ -static struct mem_cpu_pool_type mem_cpu_pool_types[] = { - { 32768, 1, 0, NULL }, - { 4096, 8, CPU_L1_SIZE, NULL }, - { 256, 64, CPU_L1_SIZE, NULL }, - { 0, 128, CPU_L1_SIZE, NULL } -}; - -/* - * Caches where CPU pool arrays are allocated from. - */ -static struct mem_cache mem_cpu_array_caches[ARRAY_SIZE(mem_cpu_pool_types)]; - -/* - * Cache for off slab data. - */ -static struct mem_cache mem_slab_cache; - -/* - * Cache for dynamically created caches. - */ -static struct mem_cache mem_cache_cache; - -/* - * General caches array. - */ -static struct mem_cache mem_caches[MEM_NR_MEM_CACHES]; - -/* - * List of all caches managed by the allocator. - */ -static struct list mem_cache_list; -static pthread_mutex_t mem_cache_list_lock; - -/* - * Default backend functions. - */ -static void * mem_default_alloc(size_t size); -static void mem_default_free(void *ptr, size_t size); - -/* - * Default source of memory. - */ -static struct mem_source mem_default_source = { - mem_default_alloc, - mem_default_free -}; - -#define mem_error(format, ...) \ - fprintf(stderr, "mem: error: %s(): " format "\n", __func__, \ - ## __VA_ARGS__) - -#define mem_warn(format, ...) \ - fprintf(stderr, "mem: warning: %s(): " format "\n", __func__, \ - ## __VA_ARGS__) - -#define mem_print(format, ...) \ - fprintf(stderr, format "\n", ## __VA_ARGS__) - -static void mem_cache_error(struct mem_cache *cache, void *buf, int error, - void *arg); -static void * mem_cache_alloc_from_slab(struct mem_cache *cache); -static void mem_cache_free_to_slab(struct mem_cache *cache, void *buf); - -#if CONFIG_MEM_USE_PHYS -#include "phys.h" - -static void * -mem_default_alloc(size_t size) -{ - return (void *)phys_alloc(size); -} - -static void -mem_default_free(void *ptr, size_t size) -{ - phys_free((phys_paddr_t)ptr, size); -} -#else /* CONFIG_MEM_USE_PHYS */ -static void * -mem_default_alloc(size_t size) -{ - void *addr; - - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) - return NULL; - - return addr; -} - -static void -mem_default_free(void *ptr, size_t size) -{ - munmap(ptr, size); -} -#endif /* CONFIG_MEM_USE_PHYS */ - -static void * -mem_buf_verify_bytes(void *buf, void *pattern, size_t size) -{ - char *ptr, *pattern_ptr, *end; - - end = buf + size; - - for (ptr = buf, pattern_ptr = pattern; ptr < end; ptr++, pattern_ptr++) - if (*ptr != *pattern_ptr) - return ptr; - - return NULL; -} - -static void * -mem_buf_verify(void *buf, uint64_t pattern, size_t size) -{ - uint64_t *ptr, *end; - - assert(P2ALIGNED((unsigned long)buf, sizeof(uint64_t))); - assert(P2ALIGNED(size, sizeof(uint64_t))); - - end = buf + size; - - for (ptr = buf; ptr < end; ptr++) - if (*ptr != pattern) - return mem_buf_verify_bytes(ptr, &pattern, sizeof(pattern)); - - return NULL; -} - -static void -mem_buf_fill(void *buf, uint64_t pattern, size_t size) -{ - uint64_t *ptr, *end; - - assert(P2ALIGNED((unsigned long)buf, sizeof(uint64_t))); - assert(P2ALIGNED(size, sizeof(uint64_t))); - - end = buf + size; - - for (ptr = buf; ptr < end; ptr++) - *ptr = pattern; -} - -static void * -mem_buf_verify_fill(void *buf, uint64_t old, uint64_t new, size_t size) -{ - uint64_t *ptr, *end; - - assert(P2ALIGNED((unsigned long)buf, sizeof(uint64_t))); - assert(P2ALIGNED(size, sizeof(uint64_t))); - - end = buf + size; - - for (ptr = buf; ptr < end; ptr++) { - if (*ptr != old) - return mem_buf_verify_bytes(ptr, &old, sizeof(old)); - - *ptr = new; - } - - return NULL; -} - -static inline union mem_bufctl * -mem_buf_to_bufctl(void *buf, struct mem_cache *cache) -{ - return (union mem_bufctl *)(buf + cache->bufctl_dist); -} - -static inline struct mem_buftag * -mem_buf_to_buftag(void *buf, struct mem_cache *cache) -{ - return (struct mem_buftag *)(buf + cache->buftag_dist); -} - -static inline void * -mem_bufctl_to_buf(union mem_bufctl *bufctl, struct mem_cache *cache) -{ - return (void *)bufctl - cache->bufctl_dist; -} - -static void -mem_slab_create_verify(struct mem_slab *slab, struct mem_cache *cache) -{ - struct mem_buftag *buftag; - size_t buf_size; - unsigned long buffers; - void *buf; - - buf_size = cache->buf_size; - buf = slab->addr; - buftag = mem_buf_to_buftag(buf, cache); - - for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) { - mem_buf_fill(buf, MEM_FREE_PATTERN, cache->bufctl_dist); - buftag->state = MEM_BUFTAG_FREE; - buf += buf_size; - buftag = mem_buf_to_buftag(buf, cache); - } -} - -/* - * Create an empty slab for a cache. - * - * The caller must drop all locks before calling this function. - */ -static struct mem_slab * -mem_slab_create(struct mem_cache *cache, size_t color) -{ - struct mem_slab *slab; - union mem_bufctl *bufctl; - size_t buf_size; - unsigned long buffers; - void *slab_buf; - - slab_buf = cache->source.alloc_fn(cache->slab_size); - - if (slab_buf == NULL) - return NULL; - - if (cache->flags & MEM_CF_SLAB_EXTERNAL) { - slab = mem_cache_alloc(&mem_slab_cache); - - if (slab == NULL) { - cache->source.free_fn(slab_buf, cache->slab_size); - return NULL; - } - } else { - slab = (struct mem_slab *)(slab_buf + cache->slab_size) - 1; - } - - list_node_init(&slab->list_node); - avltree_node_init(&slab->tree_node); - slab->nr_refs = 0; - slab->first_free = NULL; - slab->addr = slab_buf + color; - - buf_size = cache->buf_size; - bufctl = mem_buf_to_bufctl(slab->addr, cache); - - for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) { - bufctl->next = slab->first_free; - slab->first_free = bufctl; - bufctl = (union mem_bufctl *)((void *)bufctl + buf_size); - } - - if (cache->flags & MEM_CF_VERIFY) - mem_slab_create_verify(slab, cache); - - return slab; -} - -static void -mem_slab_destroy_verify(struct mem_slab *slab, struct mem_cache *cache) -{ - struct mem_buftag *buftag; - size_t buf_size; - unsigned long buffers; - void *buf, *addr; - - buf_size = cache->buf_size; - buf = slab->addr; - buftag = mem_buf_to_buftag(buf, cache); - - for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) { - if (buftag->state != MEM_BUFTAG_FREE) - mem_cache_error(cache, buf, MEM_ERR_BUFTAG, buftag); - - addr = mem_buf_verify(buf, MEM_FREE_PATTERN, cache->bufctl_dist); - - if (addr != NULL) - mem_cache_error(cache, buf, MEM_ERR_MODIFIED, addr); - - buf += buf_size; - buftag = mem_buf_to_buftag(buf, cache); - } -} - -/* - * Destroy a slab. - * - * The caller must drop all locks before calling this function. - */ -static void -mem_slab_destroy(struct mem_slab *slab, struct mem_cache *cache) -{ - void *slab_buf; - - assert(slab->nr_refs == 0); - assert(slab->first_free != NULL); - - if (cache->flags & MEM_CF_VERIFY) - mem_slab_destroy_verify(slab, cache); - - slab_buf = (void *)P2ALIGN((unsigned long)slab->addr, PAGE_SIZE); - cache->source.free_fn(slab_buf, cache->slab_size); - - if (cache->flags & MEM_CF_SLAB_EXTERNAL) - mem_cache_free(&mem_slab_cache, slab); -} - -static inline int -mem_slab_use_tree(int flags) -{ - return !(flags & MEM_CF_DIRECT) || (flags & MEM_CF_VERIFY); -} - -static inline int -mem_slab_cmp_lookup(const void *addr, const struct avltree_node *node) -{ - struct mem_slab *slab; - - slab = avltree_entry(node, struct mem_slab, tree_node); - - if (addr == slab->addr) - return 0; - else if (addr < slab->addr) - return -1; - else - return 1; -} - -static inline int -mem_slab_cmp_insert(const struct avltree_node *a, const struct avltree_node *b) -{ - struct mem_slab *slab; - - slab = avltree_entry(a, struct mem_slab, tree_node); - return mem_slab_cmp_lookup(slab->addr, b); -} - -static void -mem_cpu_pool_init(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache) -{ - pthread_mutex_init(&cpu_pool->lock, NULL); - cpu_pool->flags = cache->flags; - cpu_pool->size = 0; - cpu_pool->transfer_size = 0; - cpu_pool->nr_objs = 0; - cpu_pool->array = NULL; -} - -/* - * Return a CPU pool. - * - * This function will generally return the pool matching the CPU running the - * calling thread. Because of context switches and thread migration, the - * caller might be running on another processor after this function returns. - * Although not optimal, this should rarely happen, and it doesn't affect the - * allocator operations in any other way, as CPU pools are always valid, and - * their access is serialized by a lock. - */ -static inline struct mem_cpu_pool * -mem_cpu_pool_get(struct mem_cache *cache) -{ - return &cache->cpu_pools[cpu_id()]; -} - -static inline void -mem_cpu_pool_build(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache, - void **array) -{ - cpu_pool->size = cache->cpu_pool_type->array_size; - cpu_pool->transfer_size = (cpu_pool->size + MEM_CPU_POOL_TRANSFER_RATIO - 1) - / MEM_CPU_POOL_TRANSFER_RATIO; - cpu_pool->array = array; -} - -static inline void * -mem_cpu_pool_pop(struct mem_cpu_pool *cpu_pool) -{ - cpu_pool->nr_objs--; - return cpu_pool->array[cpu_pool->nr_objs]; -} - -static inline void -mem_cpu_pool_push(struct mem_cpu_pool *cpu_pool, void *obj) -{ - cpu_pool->array[cpu_pool->nr_objs] = obj; - cpu_pool->nr_objs++; -} - -static int -mem_cpu_pool_fill(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache) -{ - void *obj; - int i; - - pthread_mutex_lock(&cache->lock); - - for (i = 0; i < cpu_pool->transfer_size; i++) { - obj = mem_cache_alloc_from_slab(cache); - - if (obj == NULL) - break; - - mem_cpu_pool_push(cpu_pool, obj); - } - - pthread_mutex_unlock(&cache->lock); - - return i; -} - -static void -mem_cpu_pool_drain(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache) -{ - void *obj; - int i; - - pthread_mutex_lock(&cache->lock); - - for (i = cpu_pool->transfer_size; i > 0; i--) { - obj = mem_cpu_pool_pop(cpu_pool); - mem_cache_free_to_slab(cache, obj); - } - - pthread_mutex_unlock(&cache->lock); -} - -static void -mem_cache_error(struct mem_cache *cache, void *buf, int error, void *arg) -{ - struct mem_buftag *buftag; - - mem_error("cache: %s, buffer: %p", cache->name, buf); - - switch(error) { - case MEM_ERR_INVALID: - mem_error("freeing invalid address"); - break; - case MEM_ERR_DOUBLEFREE: - mem_error("attempting to free the same address twice"); - break; - case MEM_ERR_BUFTAG: - mem_error("invalid buftag content"); - buftag = arg; - mem_error("buftag state: %p", (void *)buftag->state); - break; - case MEM_ERR_MODIFIED: - mem_error("free buffer modified"); - mem_error("fault address: %p, offset in buffer: %td", arg, arg - buf); - break; - case MEM_ERR_REDZONE: - mem_error("write beyond end of buffer"); - mem_error("fault address: %p, offset in buffer: %td", arg, arg - buf); - break; - default: - mem_error("unknown error"); - } - - error_die(ERR_MEM_CACHE); - - /* - * Never reached. - */ -} - -/* - * Compute an appropriate slab size for the given cache. - * - * Once the slab size is known, this function sets the related properties - * (buffers per slab and maximum color). It can also set the MEM_CF_DIRECT - * and/or MEM_CF_SLAB_EXTERNAL flags depending on the resulting layout. - */ -static void -mem_cache_compute_sizes(struct mem_cache *cache, int flags) -{ - size_t i, buffers, buf_size, slab_size, free_slab_size, optimal_size; - size_t waste, waste_min; - int embed, optimal_embed; - - buf_size = cache->buf_size; - - if (buf_size < MEM_BUF_SIZE_THRESHOLD) - flags |= MEM_CREATE_INTERNAL; - - i = 0; - waste_min = (size_t)-1; - - do { - i++; - slab_size = P2ROUND(i * buf_size, PAGE_SIZE); - free_slab_size = slab_size; - - if (flags & MEM_CREATE_INTERNAL) - free_slab_size -= sizeof(struct mem_slab); - - buffers = free_slab_size / buf_size; - waste = free_slab_size % buf_size; - - if (buffers > i) - i = buffers; - - if (flags & MEM_CREATE_INTERNAL) - embed = 1; - else if (sizeof(struct mem_slab) <= waste) { - embed = 1; - waste -= sizeof(struct mem_slab); - } else { - embed = 0; - } - - if (waste <= waste_min) { - waste_min = waste; - optimal_size = slab_size; - optimal_embed = embed; - } - } while ((buffers < MEM_MIN_BUFS_PER_SLAB) - && (slab_size < MEM_SLAB_SIZE_THRESHOLD)); - - assert(!(flags & MEM_CREATE_INTERNAL) || optimal_embed); - - cache->slab_size = optimal_size; - slab_size = cache->slab_size - (optimal_embed - ? sizeof(struct mem_slab) - : 0); - cache->bufs_per_slab = slab_size / buf_size; - cache->color_max = slab_size % buf_size; - - if (cache->color_max >= PAGE_SIZE) - cache->color_max = PAGE_SIZE - 1; - - if (optimal_embed) { - if (cache->slab_size == PAGE_SIZE) - cache->flags |= MEM_CF_DIRECT; - } else { - cache->flags |= MEM_CF_SLAB_EXTERNAL; - } -} - -static void -mem_cache_init(struct mem_cache *cache, const char *name, - size_t obj_size, size_t align, mem_cache_ctor_t ctor, - const struct mem_source *source, int flags) -{ - struct mem_cpu_pool_type *cpu_pool_type; - size_t i, buf_size; - -#if CONFIG_MEM_VERIFY - cache->flags = MEM_CF_VERIFY; -#else - cache->flags = 0; -#endif - - if (flags & MEM_CACHE_VERIFY) - cache->flags |= MEM_CF_VERIFY; - - if (align < MEM_ALIGN_MIN) - align = MEM_ALIGN_MIN; - - assert(obj_size > 0); - assert(ISP2(align)); - assert(align < PAGE_SIZE); - - buf_size = P2ROUND(obj_size, align); - - if (source == NULL) - source = &mem_default_source; - - pthread_mutex_init(&cache->lock, NULL); - list_node_init(&cache->node); - list_init(&cache->partial_slabs); - list_init(&cache->free_slabs); - avltree_init(&cache->active_slabs); - cache->obj_size = obj_size; - cache->align = align; - cache->buf_size = buf_size; - cache->bufctl_dist = buf_size - sizeof(union mem_bufctl); - cache->color = 0; - cache->nr_objs = 0; - cache->nr_bufs = 0; - cache->nr_slabs = 0; - cache->nr_free_slabs = 0; - cache->ctor = ctor; - cache->source = *source; - strncpy(cache->name, name, MEM_NAME_SIZE); - cache->name[MEM_NAME_SIZE - 1] = '\0'; - cache->buftag_dist = 0; - cache->redzone_pad = 0; - - if (cache->flags & MEM_CF_VERIFY) { - cache->bufctl_dist = buf_size; - cache->buftag_dist = cache->bufctl_dist + sizeof(union mem_bufctl); - cache->redzone_pad = cache->bufctl_dist - cache->obj_size; - buf_size += sizeof(union mem_bufctl) + sizeof(struct mem_buftag); - buf_size = P2ROUND(buf_size, align); - cache->buf_size = buf_size; - } - - mem_cache_compute_sizes(cache, flags); - - for (cpu_pool_type = mem_cpu_pool_types; - buf_size <= cpu_pool_type->buf_size; - cpu_pool_type++); - - cache->cpu_pool_type = cpu_pool_type; - - for (i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) - mem_cpu_pool_init(&cache->cpu_pools[i], cache); - - pthread_mutex_lock(&mem_cache_list_lock); - list_insert_tail(&mem_cache_list, &cache->node); - pthread_mutex_unlock(&mem_cache_list_lock); -} - -struct mem_cache * -mem_cache_create(const char *name, size_t obj_size, size_t align, - mem_cache_ctor_t ctor, const struct mem_source *source, - int flags) -{ - struct mem_cache *cache; - - cache = mem_cache_alloc(&mem_cache_cache); - - if (cache == NULL) - return NULL; - - mem_cache_init(cache, name, obj_size, align, ctor, source, flags); - - return cache; -} - -static inline int -mem_cache_empty(struct mem_cache *cache) -{ - return cache->nr_objs == cache->nr_bufs; -} - -static int -mem_cache_grow(struct mem_cache *cache) -{ - struct mem_slab *slab; - size_t color; - int empty; - - pthread_mutex_lock(&cache->lock); - - if (!mem_cache_empty(cache)) { - pthread_mutex_unlock(&cache->lock); - return 1; - } - - color = cache->color; - cache->color += cache->align; - - if (cache->color > cache->color_max) - cache->color = 0; - - pthread_mutex_unlock(&cache->lock); - - slab = mem_slab_create(cache, color); - - pthread_mutex_lock(&cache->lock); - - if (slab != NULL) { - list_insert_tail(&cache->free_slabs, &slab->list_node); - cache->nr_bufs += cache->bufs_per_slab; - cache->nr_slabs++; - cache->nr_free_slabs++; - } - - /* - * Even if our slab creation failed, another thread might have succeeded - * in growing the cache. - */ - empty = mem_cache_empty(cache); - - pthread_mutex_unlock(&cache->lock); - - return !empty; -} - -static void -mem_cache_reap(struct mem_cache *cache) -{ - struct mem_slab *slab; - struct list dead_slabs; - - list_init(&dead_slabs); - - pthread_mutex_lock(&cache->lock); - - while (!list_empty(&cache->free_slabs)) { - slab = list_first_entry(&cache->free_slabs, struct mem_slab, list_node); - list_remove(&slab->list_node); - list_insert(&dead_slabs, &slab->list_node); - cache->nr_bufs -= cache->bufs_per_slab; - cache->nr_slabs--; - cache->nr_free_slabs--; - } - - pthread_mutex_unlock(&cache->lock); - - while (!list_empty(&dead_slabs)) { - slab = list_first_entry(&dead_slabs, struct mem_slab, list_node); - list_remove(&slab->list_node); - mem_slab_destroy(slab, cache); - } -} - -void -mem_cache_destroy(struct mem_cache *cache) -{ - struct mem_cpu_pool *cpu_pool; - void **ptr; - size_t i; - - pthread_mutex_lock(&mem_cache_list_lock); - list_remove(&cache->node); - pthread_mutex_unlock(&mem_cache_list_lock); - - for (i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) { - cpu_pool = &cache->cpu_pools[i]; - - pthread_mutex_lock(&cpu_pool->lock); - - if (cpu_pool->array == NULL) { - pthread_mutex_unlock(&cpu_pool->lock); - continue; - } - - pthread_mutex_lock(&cache->lock); - - for (ptr = cpu_pool->array + cpu_pool->nr_objs - 1; - ptr >= cpu_pool->array; - ptr--) - mem_cache_free_to_slab(cache, *ptr); - - pthread_mutex_unlock(&cache->lock); - - ptr = cpu_pool->array; - cpu_pool->size = 0; - cpu_pool->nr_objs = 0; - cpu_pool->array = NULL; - pthread_mutex_unlock(&cpu_pool->lock); - - mem_cache_free(cache->cpu_pool_type->array_cache, ptr); - } - - mem_cache_reap(cache); - -#ifndef NDEBUG - if (cache->nr_objs != 0) - mem_warn("'%s' not empty", cache->name); - else { - assert(list_empty(&cache->partial_slabs)); - assert(list_empty(&cache->free_slabs)); - assert(avltree_empty(&cache->active_slabs)); - assert(cache->nr_bufs == 0); - assert(cache->nr_slabs == 0); - } -#endif /* NDEBUG */ - - pthread_mutex_destroy(&cache->lock); - - for (i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) - pthread_mutex_destroy(&cache->cpu_pools[i].lock); - - mem_cache_free(&mem_cache_cache, cache); -} - -/* - * Allocate a raw (unconstructed) buffer from the slab layer of a cache. - * - * The cache must be locked before calling this function. - */ -static void * -mem_cache_alloc_from_slab(struct mem_cache *cache) -{ - struct mem_slab *slab; - union mem_bufctl *bufctl; - - if (!list_empty(&cache->partial_slabs)) - slab = list_first_entry(&cache->partial_slabs, struct mem_slab, - list_node); - else if (!list_empty(&cache->free_slabs)) - slab = list_first_entry(&cache->free_slabs, struct mem_slab, list_node); - else - return NULL; - - bufctl = slab->first_free; - assert(bufctl != NULL); - slab->first_free = bufctl->next; - slab->nr_refs++; - cache->nr_objs++; - - /* - * The slab has become complete. - */ - if (slab->nr_refs == cache->bufs_per_slab) { - list_remove(&slab->list_node); - - if (slab->nr_refs == 1) - cache->nr_free_slabs--; - } else if (slab->nr_refs == 1) { - /* - * The slab has become partial. - */ - list_remove(&slab->list_node); - list_insert_tail(&cache->partial_slabs, &slab->list_node); - cache->nr_free_slabs--; - } else if (!list_singular(&cache->partial_slabs)) { - struct list *node; - struct mem_slab *tmp; - - /* - * The slab remains partial. If there are more than one partial slabs, - * maintain the list sorted. - */ - - assert(slab->nr_refs > 1); - - for (node = list_prev(&slab->list_node); - !list_end(&cache->partial_slabs, node); - node = list_prev(node)) { - tmp = list_entry(node, struct mem_slab, list_node); - - if (tmp->nr_refs >= slab->nr_refs) - break; - } - - /* - * If the direct neighbor was found, the list is already sorted. - * If no slab was found, the slab is inserted at the head of the list. - */ - if (node != list_prev(&slab->list_node)) { - list_remove(&slab->list_node); - list_insert_after(node, &slab->list_node); - } - } - - if ((slab->nr_refs == 1) && mem_slab_use_tree(cache->flags)) - avltree_insert(&cache->active_slabs, &slab->tree_node, - mem_slab_cmp_insert); - - return mem_bufctl_to_buf(bufctl, cache); -} - -/* - * Release a buffer to the slab layer of a cache. - * - * The cache must be locked before calling this function. - */ -static void -mem_cache_free_to_slab(struct mem_cache *cache, void *buf) -{ - struct mem_slab *slab; - union mem_bufctl *bufctl; - - if (cache->flags & MEM_CF_DIRECT) { - assert(cache->slab_size == PAGE_SIZE); - slab = (struct mem_slab *)P2END((unsigned long)buf, cache->slab_size) - - 1; - } else { - struct avltree_node *node; - - node = avltree_lookup_nearest(&cache->active_slabs, buf, - mem_slab_cmp_lookup, AVLTREE_LEFT); - assert(node != NULL); - slab = avltree_entry(node, struct mem_slab, tree_node); - assert((unsigned long)buf < (P2ALIGN((unsigned long)slab->addr - + cache->slab_size, PAGE_SIZE))); - } - - assert(slab->nr_refs >= 1); - assert(slab->nr_refs <= cache->bufs_per_slab); - bufctl = mem_buf_to_bufctl(buf, cache); - bufctl->next = slab->first_free; - slab->first_free = bufctl; - slab->nr_refs--; - cache->nr_objs--; - - /* - * The slab has become free. - */ - if (slab->nr_refs == 0) { - if (mem_slab_use_tree(cache->flags)) - avltree_remove(&cache->active_slabs, &slab->tree_node); - - /* - * The slab was partial. - */ - if (cache->bufs_per_slab > 1) - list_remove(&slab->list_node); - - list_insert_tail(&cache->free_slabs, &slab->list_node); - cache->nr_free_slabs++; - } else if (slab->nr_refs == (cache->bufs_per_slab - 1)) { - /* - * The slab has become partial. - */ - list_insert(&cache->partial_slabs, &slab->list_node); - } else if (!list_singular(&cache->partial_slabs)) { - struct list *node; - struct mem_slab *tmp; - - /* - * The slab remains partial. If there are more than one partial slabs, - * maintain the list sorted. - */ - - assert(slab->nr_refs > 0); - - for (node = list_next(&slab->list_node); - !list_end(&cache->partial_slabs, node); - node = list_next(node)) { - tmp = list_entry(node, struct mem_slab, list_node); - - if (tmp->nr_refs <= slab->nr_refs) - break; - } - - /* - * If the direct neighbor was found, the list is already sorted. - * If no slab was found, the slab is inserted at the tail of the list. - */ - if (node != list_next(&slab->list_node)) { - list_remove(&slab->list_node); - list_insert_before(node, &slab->list_node); - } - } -} - -static void -mem_cache_alloc_verify(struct mem_cache *cache, void *buf, int construct) -{ - struct mem_buftag *buftag; - union mem_bufctl *bufctl; - void *addr; - - buftag = mem_buf_to_buftag(buf, cache); - - if (buftag->state != MEM_BUFTAG_FREE) - mem_cache_error(cache, buf, MEM_ERR_BUFTAG, buftag); - - addr = mem_buf_verify_fill(buf, MEM_FREE_PATTERN, MEM_UNINIT_PATTERN, - cache->bufctl_dist); - - if (addr != NULL) - mem_cache_error(cache, buf, MEM_ERR_MODIFIED, addr); - - addr = buf + cache->obj_size; - memset(addr, MEM_REDZONE_BYTE, cache->redzone_pad); - - bufctl = mem_buf_to_bufctl(buf, cache); - bufctl->redzone = MEM_REDZONE_WORD; - buftag->state = MEM_BUFTAG_ALLOC; - - if (construct && (cache->ctor != NULL)) - cache->ctor(buf); -} - -void * -mem_cache_alloc(struct mem_cache *cache) -{ - struct mem_cpu_pool *cpu_pool; - int filled; - void *buf; - - cpu_pool = mem_cpu_pool_get(cache); - - pthread_mutex_lock(&cpu_pool->lock); - -fast_alloc_retry: - if (likely(cpu_pool->nr_objs > 0)) { - buf = mem_cpu_pool_pop(cpu_pool); - pthread_mutex_unlock(&cpu_pool->lock); - - if (cpu_pool->flags & MEM_CF_VERIFY) - mem_cache_alloc_verify(cache, buf, MEM_AV_CONSTRUCT); - - return buf; - } - - if (cpu_pool->array != NULL) { - filled = mem_cpu_pool_fill(cpu_pool, cache); - - if (!filled) { - pthread_mutex_unlock(&cpu_pool->lock); - - filled = mem_cache_grow(cache); - - if (!filled) - return NULL; - - pthread_mutex_lock(&cpu_pool->lock); - } - - goto fast_alloc_retry; - } - - pthread_mutex_unlock(&cpu_pool->lock); - -slow_alloc_retry: - pthread_mutex_lock(&cache->lock); - buf = mem_cache_alloc_from_slab(cache); - pthread_mutex_unlock(&cache->lock); - - if (buf == NULL) { - filled = mem_cache_grow(cache); - - if (!filled) - return NULL; - - goto slow_alloc_retry; - } - - if (cache->flags & MEM_CF_VERIFY) - mem_cache_alloc_verify(cache, buf, MEM_AV_NOCONSTRUCT); - - if (cache->ctor != NULL) - cache->ctor(buf); - - return buf; -} - -static void -mem_cache_free_verify(struct mem_cache *cache, void *buf) -{ - struct avltree_node *node; - struct mem_buftag *buftag; - struct mem_slab *slab; - union mem_bufctl *bufctl; - unsigned char *redzone_byte; - unsigned long slabend; - - pthread_mutex_lock(&cache->lock); - node = avltree_lookup_nearest(&cache->active_slabs, buf, - mem_slab_cmp_lookup, AVLTREE_LEFT); - pthread_mutex_unlock(&cache->lock); - - if (node == NULL) - mem_cache_error(cache, buf, MEM_ERR_INVALID, NULL); - - slab = avltree_entry(node, struct mem_slab, tree_node); - slabend = P2ALIGN((unsigned long)slab->addr + cache->slab_size, PAGE_SIZE); - - if ((unsigned long)buf >= slabend) - mem_cache_error(cache, buf, MEM_ERR_INVALID, NULL); - - if ((((unsigned long)buf - (unsigned long)slab->addr) % cache->buf_size) - != 0) - mem_cache_error(cache, buf, MEM_ERR_INVALID, NULL); - - /* - * As the buffer address is valid, accessing its buftag is safe. - */ - buftag = mem_buf_to_buftag(buf, cache); - - if (buftag->state != MEM_BUFTAG_ALLOC) { - if (buftag->state == MEM_BUFTAG_FREE) - mem_cache_error(cache, buf, MEM_ERR_DOUBLEFREE, NULL); - else - mem_cache_error(cache, buf, MEM_ERR_BUFTAG, buftag); - } - - redzone_byte = buf + cache->obj_size; - bufctl = mem_buf_to_bufctl(buf, cache); - - while (redzone_byte < (unsigned char *)bufctl) { - if (*redzone_byte != MEM_REDZONE_BYTE) - mem_cache_error(cache, buf, MEM_ERR_REDZONE, redzone_byte); - - redzone_byte++; - } - - if (bufctl->redzone != MEM_REDZONE_WORD) { - unsigned long word; - - word = MEM_REDZONE_WORD; - redzone_byte = mem_buf_verify_bytes(&bufctl->redzone, &word, - sizeof(bufctl->redzone)); - mem_cache_error(cache, buf, MEM_ERR_REDZONE, redzone_byte); - } - - mem_buf_fill(buf, MEM_FREE_PATTERN, cache->bufctl_dist); - buftag->state = MEM_BUFTAG_FREE; -} - -void -mem_cache_free(struct mem_cache *cache, void *obj) -{ - struct mem_cpu_pool *cpu_pool; - void **array; - - cpu_pool = mem_cpu_pool_get(cache); - - if (cpu_pool->flags & MEM_CF_VERIFY) - mem_cache_free_verify(cache, obj); - - pthread_mutex_lock(&cpu_pool->lock); - -fast_free_retry: - if (likely(cpu_pool->nr_objs < cpu_pool->size)) { - mem_cpu_pool_push(cpu_pool, obj); - pthread_mutex_unlock(&cpu_pool->lock); - return; - } - - if (cpu_pool->array != NULL) { - mem_cpu_pool_drain(cpu_pool, cache); - goto fast_free_retry; - } - - pthread_mutex_unlock(&cpu_pool->lock); - - array = mem_cache_alloc(cache->cpu_pool_type->array_cache); - - if (array != NULL) { - pthread_mutex_lock(&cpu_pool->lock); - - /* - * Another thread may have built the CPU pool while the lock was - * dropped. - */ - if (cpu_pool->array != NULL) { - pthread_mutex_unlock(&cpu_pool->lock); - mem_cache_free(cache->cpu_pool_type->array_cache, array); - pthread_mutex_lock(&cpu_pool->lock); - goto fast_free_retry; - } - - mem_cpu_pool_build(cpu_pool, cache, array); - goto fast_free_retry; - } - - pthread_mutex_lock(&cache->lock); - mem_cache_free_to_slab(cache, obj); - pthread_mutex_unlock(&cache->lock); -} - -void -mem_cache_info(struct mem_cache *cache) -{ - struct mem_cache *cache_stats; - char flags_str[64]; - - if (cache == NULL) { - pthread_mutex_lock(&mem_cache_list_lock); - - list_for_each_entry(&mem_cache_list, cache, node) - mem_cache_info(cache); - - pthread_mutex_unlock(&mem_cache_list_lock); - - return; - } - - cache_stats = mem_alloc(sizeof(*cache_stats)); - - if (cache_stats == NULL) { - mem_warn("unable to allocate memory for cache stats"); - return; - } - - pthread_mutex_lock(&cache->lock); - cache_stats->flags = cache->flags; - cache_stats->obj_size = cache->obj_size; - cache_stats->align = cache->align; - cache_stats->buf_size = cache->buf_size; - cache_stats->bufctl_dist = cache->bufctl_dist; - cache_stats->slab_size = cache->slab_size; - cache_stats->color_max = cache->color_max; - cache_stats->bufs_per_slab = cache->bufs_per_slab; - cache_stats->nr_objs = cache->nr_objs; - cache_stats->nr_bufs = cache->nr_bufs; - cache_stats->nr_slabs = cache->nr_slabs; - cache_stats->nr_free_slabs = cache->nr_free_slabs; - strcpy(cache_stats->name, cache->name); - cache_stats->buftag_dist = cache->buftag_dist; - cache_stats->redzone_pad = cache->redzone_pad; - cache_stats->cpu_pool_type = cache->cpu_pool_type; - pthread_mutex_unlock(&cache->lock); - - snprintf(flags_str, sizeof(flags_str), "%s%s%s", - (cache_stats->flags & MEM_CF_DIRECT) ? " DIRECT" : "", - (cache_stats->flags & MEM_CF_SLAB_EXTERNAL) ? " SLAB_EXTERNAL" : "", - (cache_stats->flags & MEM_CF_VERIFY) ? " VERIFY" : ""); - - mem_print("name: %s", cache_stats->name); - mem_print("flags: 0x%x%s", cache_stats->flags, flags_str); - mem_print("obj_size: %zu", cache_stats->obj_size); - mem_print("align: %zu", cache_stats->align); - mem_print("buf_size: %zu", cache_stats->buf_size); - mem_print("bufctl_dist: %zu", cache_stats->bufctl_dist); - mem_print("slab_size: %zu", cache_stats->slab_size); - mem_print("color_max: %zu", cache_stats->color_max); - mem_print("bufs_per_slab: %lu", cache_stats->bufs_per_slab); - mem_print("nr_objs: %lu", cache_stats->nr_objs); - mem_print("nr_bufs: %lu", cache_stats->nr_bufs); - mem_print("nr_slabs: %lu", cache_stats->nr_slabs); - mem_print("nr_free_slabs: %lu", cache_stats->nr_free_slabs); - mem_print("buftag_dist: %zu", cache_stats->buftag_dist); - mem_print("redzone_pad: %zu", cache_stats->redzone_pad); - mem_print("cpu_pool_size: %d", cache_stats->cpu_pool_type->array_size); - mem_print("--"); - - mem_free(cache_stats, sizeof(*cache_stats)); -} - -static void * -mem_gc(void *arg) -{ - struct mem_cache *cache; - struct timespec ts; - int error; - - (void)arg; - - clock_gettime(CLOCK_MONOTONIC, &ts); - - for (;;) { - ts.tv_sec += MEM_GC_INTERVAL; - - do - error = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL); - while (error == EINTR); - - /* - * EINTR is the only expected error. - */ - assert(error == 0); - -#if 0 - mem_info(); - -#if CONFIG_MEM_USE_PHYS - phys_info(); -#endif /* CONFIG_MEM_USE_PHYS */ -#endif - - pthread_mutex_lock(&mem_cache_list_lock); - - list_for_each_entry(&mem_cache_list, cache, node) - mem_cache_reap(cache); - - pthread_mutex_unlock(&mem_cache_list_lock); - } - - return NULL; -} - -void -mem_setup(void) -{ - static int mem_initialized = 0; - struct mem_cpu_pool_type *cpu_pool_type; - char name[MEM_NAME_SIZE]; - pthread_t thread; - size_t i, size; - int error; - - if (mem_initialized) - return; - - mem_initialized = 1; - - _pagesize = sysconf(_SC_PAGESIZE); - assert(ISP2(_pagesize)); - - /* - * Make sure a bufctl can always be stored in a buffer. - */ - assert(sizeof(union mem_bufctl) <= MEM_ALIGN_MIN); - -#if CONFIG_MEM_USE_PHYS - phys_setup(); -#endif /* CONFIG_MEM_USE_PHYS */ - - list_init(&mem_cache_list); - pthread_mutex_init(&mem_cache_list_lock, NULL); - - for (i = 0; i < ARRAY_SIZE(mem_cpu_pool_types); i++) { - cpu_pool_type = &mem_cpu_pool_types[i]; - cpu_pool_type->array_cache = &mem_cpu_array_caches[i]; - sprintf(name, "mem_cpu_array_%d", cpu_pool_type->array_size); - size = sizeof(void *) * cpu_pool_type->array_size; - mem_cache_init(cpu_pool_type->array_cache, name, size, - cpu_pool_type->array_align, NULL, NULL, 0); - } - - /* - * Prevent off slab data for the slab cache to avoid infinite recursion. - */ - mem_cache_init(&mem_slab_cache, "mem_slab", sizeof(struct mem_slab), - 0, NULL, NULL, MEM_CREATE_INTERNAL); - mem_cache_init(&mem_cache_cache, "mem_cache", sizeof(struct mem_cache), - CPU_L1_SIZE, NULL, NULL, 0); - - size = 1 << MEM_CACHES_FIRST_SHIFT; - - for (i = 0; i < ARRAY_SIZE(mem_caches); i++) { - sprintf(name, "mem_%zu", size); - mem_cache_init(&mem_caches[i], name, size, 0, NULL, NULL, 0); - size <<= 1; - } - - error = pthread_create(&thread, NULL, mem_gc, NULL); - - if (error) - mem_error("unable to create garbage collection thread: %s", - strerror(error)); -} - -/* - * Return the mem cache index matching the given allocation size, which - * must be strictly greater than 0. - */ -static inline size_t -mem_get_index(size_t size) -{ - assert(size != 0); - - size = (size - 1) >> MEM_CACHES_FIRST_SHIFT; - - if (size == 0) - return 0; - else - return (sizeof(long) * CHAR_BIT) - __builtin_clzl(size); -} - -static void -mem_alloc_verify(struct mem_cache *cache, void *buf, size_t size) -{ - size_t redzone_size; - void *redzone; - - assert(size <= cache->obj_size); - - redzone = buf + size; - redzone_size = cache->obj_size - size; - memset(redzone, MEM_REDZONE_BYTE, redzone_size); -} - -void * -mem_alloc(size_t size) -{ - size_t index; - void *buf; - - if (size == 0) - return NULL; - - index = mem_get_index(size); - - if (index < ARRAY_SIZE(mem_caches)) { - struct mem_cache *cache; - - cache = &mem_caches[index]; - buf = mem_cache_alloc(cache); - - if ((buf != NULL) && (cache->flags & MEM_CF_VERIFY)) - mem_alloc_verify(cache, buf, size); - } else { - buf = mem_default_alloc(size); - } - - return buf; -} - -void * -mem_zalloc(size_t size) -{ - void *ptr; - - ptr = mem_alloc(size); - - if (ptr == NULL) - return NULL; - - memset(ptr, 0, size); - return ptr; -} - -static void -mem_free_verify(struct mem_cache *cache, void *buf, size_t size) -{ - unsigned char *redzone_byte, *redzone_end; - - assert(size <= cache->obj_size); - - redzone_byte = buf + size; - redzone_end = buf + cache->obj_size; - - while (redzone_byte < redzone_end) { - if (*redzone_byte != MEM_REDZONE_BYTE) - mem_cache_error(cache, buf, MEM_ERR_REDZONE, redzone_byte); - - redzone_byte++; - } -} - -void -mem_free(void *ptr, size_t size) -{ - size_t index; - - if ((ptr == NULL) || (size == 0)) - return; - - index = mem_get_index(size); - - if (index < ARRAY_SIZE(mem_caches)) { - struct mem_cache *cache; - - cache = &mem_caches[index]; - - if (cache->flags & MEM_CF_VERIFY) - mem_free_verify(cache, ptr, size); - - mem_cache_free(cache, ptr); - } else { - mem_default_free(ptr, size); - } -} - -void -mem_info(void) -{ - struct mem_cache *cache, *cache_stats; - size_t mem_usage, mem_reclaimable; - - cache_stats = mem_alloc(sizeof(*cache_stats)); - - if (cache_stats == NULL) { - mem_warn("unable to allocate memory for cache stats"); - return; - } - - mem_print("-- cache obj slab bufs objs bufs " - " total reclaimable"); - mem_print("-- name size size /slab usage count " - " memory memory"); - - pthread_mutex_lock(&mem_cache_list_lock); - - list_for_each_entry(&mem_cache_list, cache, node) { - pthread_mutex_lock(&cache->lock); - cache_stats->obj_size = cache->obj_size; - cache_stats->slab_size = cache->slab_size; - cache_stats->bufs_per_slab = cache->bufs_per_slab; - cache_stats->nr_objs = cache->nr_objs; - cache_stats->nr_bufs = cache->nr_bufs; - cache_stats->nr_slabs = cache->nr_slabs; - cache_stats->nr_free_slabs = cache->nr_free_slabs; - strcpy(cache_stats->name, cache->name); - pthread_mutex_unlock(&cache->lock); - - mem_usage = (cache_stats->nr_slabs * cache_stats->slab_size) >> 10; - mem_reclaimable = - (cache_stats->nr_free_slabs * cache_stats->slab_size) >> 10; - - mem_print("%-27s %6zu %3zuk %4lu %6lu %6lu %7zuk %10zuk", - cache_stats->name, cache_stats->obj_size, - cache_stats->slab_size >> 10, cache_stats->bufs_per_slab, - cache_stats->nr_objs, cache_stats->nr_bufs, mem_usage, - mem_reclaimable); - } - - pthread_mutex_unlock(&mem_cache_list_lock); - - mem_free(cache_stats, sizeof(*cache_stats)); -} @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Richard Braun. - * 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 ``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 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. - * - * - * Object caching and general purpose memory allocator. - */ - -#ifndef _MEM_H -#define _MEM_H - -#include <stddef.h> - -/* - * Backend source of memory for a cache. - */ -struct mem_source { - void * (*alloc_fn)(size_t); - void (*free_fn)(void *, size_t); -}; - -/* - * Object cache opaque declaration. - */ -struct mem_cache; - -/* - * Type for constructor functions. - * - * The pre-constructed state of an object is supposed to include only - * elements such as e.g. linked lists, locks, reference counters. Therefore - * constructors are expected to 1) never fail and 2) not need any - * user-provided data. The first constraint implies that object construction - * never performs dynamic resource allocation, which also means there is no - * need for destructors. - */ -typedef void (*mem_cache_ctor_t)(void *); - -/* - * Cache creation flags. - */ -#define MEM_CACHE_VERIFY 0x1 /* Use debugging facilities */ - -/* - * Create a cache. - */ -struct mem_cache * mem_cache_create(const char *name, size_t obj_size, - size_t align, mem_cache_ctor_t ctor, - const struct mem_source *source, int flags); - -/* - * Destroy a cache. - */ -void mem_cache_destroy(struct mem_cache *cache); - -/* - * Allocate an object from a cache. - */ -void * mem_cache_alloc(struct mem_cache *cache); - -/* - * Release an object to its cache. - */ -void mem_cache_free(struct mem_cache *cache, void *obj); - -/* - * Display internal cache stats on stderr. - * - * If cache is NULL, this function displays all managed caches. - */ -void mem_cache_info(struct mem_cache *cache); - -/* - * Set up the memory allocator module. - */ -void mem_setup(void); - -/* - * Allocate size bytes of uninitialized memory. - */ -void * mem_alloc(size_t size); - -/* - * Allocate size bytes of zeroed memory. - */ -void * mem_zalloc(size_t size); - -/* - * Release memory obtained with mem_alloc() or mem_zalloc(). - * - * The size argument must strictly match the value given at allocation time. - */ -void mem_free(void *ptr, size_t size); - -/* - * Display global memory information on stderr. - */ -void mem_info(void); - -#endif /* _MEM_H */ diff --git a/mem_malloc.c b/mem_malloc.c deleted file mode 100644 index 2f25e0b..0000000 --- a/mem_malloc.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 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. - * - * - * Libc-compatible malloc() functions implementation. - * - * Keep in mind this code is mainly used for tests. Also, initialization is - * not thread-safe. - */ - -#include <errno.h> -#include <stddef.h> -#include <string.h> - -#include "mem.h" -#include "macros.h" -#include "mem_malloc.h" - -struct btag { - void *addr; - size_t size; -} __aligned(8); - -void * -malloc(size_t size) -{ - struct btag *btag; - - mem_setup(); - - size += sizeof(*btag); - btag = mem_alloc(size); - - if (btag == NULL) { - errno = ENOMEM; - return NULL; - } - - btag->addr = btag; - btag->size = size; - - return btag + 1; -} - -void * -calloc(size_t nmemb, size_t size) -{ - size_t bytes; - void *buf; - - mem_setup(); - - bytes = nmemb * size; - buf = malloc(bytes); - - if (buf == NULL) - return NULL; - - memset(buf, 0, bytes); - - return buf; -} - -void * -realloc(void *ptr, size_t size) -{ - struct btag *btag; - size_t old_size; - char *buf; - - mem_setup(); - - if (ptr == NULL) - return malloc(size); - else if (size == 0) { - free(ptr); - return NULL; - } - - buf = malloc(size); - - if (buf == NULL) - return NULL; - - btag = (struct btag *)ptr - 1; - old_size = btag->size - sizeof(*btag); - memcpy(buf, ptr, MIN(old_size, size)); - mem_free(btag->addr, btag->size); - - return buf; -} - -int -posix_memalign(void **ptr, size_t align, size_t size) -{ - struct btag *btag; - char *buf; - - mem_setup(); - - if (!ISP2(align)) - return EINVAL; - - size += sizeof(*btag); - size = P2ROUND(size, align * 2); - buf = mem_alloc(size); - - if (buf == NULL) - return ENOMEM; - - btag = (struct btag *)P2ROUND((unsigned long)buf + sizeof(*btag), align) - - 1; - btag->addr = buf; - btag->size = size; - - *ptr = btag + 1; - return 0; -} - -void -free(void *ptr) -{ - struct btag *btag; - - mem_setup(); - - if (ptr == NULL) - return; - - btag = (struct btag *)ptr - 1; - mem_free(btag->addr, btag->size); -} diff --git a/mem_malloc.h b/mem_malloc.h deleted file mode 100644 index 6921c32..0000000 --- a/mem_malloc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 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. - * - * - * Libc-compatible malloc() functions. - * - * This code is messy, for testing purposes only, and disabled by default. - */ - -#ifndef _MEM_MALLOC_H -#define _MEM_MALLOC_H - -#include <stddef.h> - -void * malloc(size_t size); -void * calloc(size_t nmemb, size_t size); -void * realloc(void *ptr, size_t size); -int posix_memalign(void **ptr, size_t align, size_t size); -void free(void *ptr); - -#endif /* _MEM_MALLOC_H */ @@ -1,777 +0,0 @@ -/* - * Copyright (c) 2011 Richard Braun. - * 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 ``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 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. - * - * - * Page allocator. - * - * This implementation uses the binary buddy system to manage its heap. - * Descriptions of the buddy system can be found in the following works : - * - "UNIX Internals: The New Frontiers", by Uresh Vahalia. - * - "Dynamic Storage Allocation: A Survey and Critical Review", - * by Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles. - * - * In addition, this allocator uses per-cpu pools of pages for level 0 - * (i.e. single page) allocations. These pools act as caches (but are named - * differently to avoid confusion with CPU caches) that reduce contention on - * multiprocessor systems. When a pool is empty and cannot provide a page, - * it is filled by transferring multiple pages from the backend buddy system. - * The symmetric case is handled likewise. - */ - -#include <sched.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> -#include <sys/mman.h> - -#include "cpu.h" -#include "list.h" -#include "phys.h" -#include "error.h" -#include "macros.h" - -/* - * The system page size. - * - * This macro actually expands to a global variable that is set on - * initialization. - */ -#define PAGE_SIZE ((unsigned long)_pagesize) - -/* - * Maximum number of segments. - */ -#define PHYS_MAX_SEGMENTS 2 - -/* - * Segment boundaries. - */ -#define PHYS_ISADMA_LIMIT 0x1000000 -#define PHYS_NORMAL_LIMIT 0x30000000 - -/* - * Number of segment lists. - */ -#define PHYS_NR_SEG_LISTS 2 - -/* - * Segment list priorities. - * - * Higher priorities have lower numerical values. - */ -#define PHYS_SEGLIST_NORMAL 1 -#define PHYS_SEGLIST_ISADMA 0 - -/* - * Number of free block lists per segment. - */ -#define PHYS_NR_FREE_LISTS 11 - -/* - * The size of a CPU pool is computed by dividing the number of pages in its - * containing segment by this value. - */ -#define PHYS_CPU_POOL_RATIO 1024 - -/* - * Maximum number of pages in a CPU pool. - */ -#define PHYS_CPU_POOL_MAX_SIZE 128 - -/* - * The transfer size of a CPU pool is computed by dividing the pool size by - * this value. - */ -#define PHYS_CPU_POOL_TRANSFER_RATIO 2 - -/* - * Per-processor cache of pages. - */ -struct phys_cpu_pool { - pthread_mutex_t lock; - int size; - int transfer_size; - int nr_pages; - struct list pages; -} __aligned(CPU_L1_SIZE); - -/* - * Special level value. - * - * When a page is free, its level is the index of its free list. - */ -#define PHYS_LEVEL_ALLOCATED PHYS_NR_FREE_LISTS - -/* - * Doubly-linked list of free blocks. - */ -struct phys_free_list { - unsigned long size; - struct list blocks; -}; - -/* - * Segment name buffer size. - */ -#define PHYS_NAME_SIZE 16 - -/* - * Segment of contiguous memory. - */ -struct phys_seg { - struct phys_cpu_pool cpu_pools[NR_CPUS]; - - struct list node; - phys_paddr_t start; - phys_paddr_t end; - struct phys_page *pages; - struct phys_page *pages_end; - pthread_mutex_t lock; - struct phys_free_list free_lists[PHYS_NR_FREE_LISTS]; - unsigned long nr_free_pages; - char name[PHYS_NAME_SIZE]; -}; - -/* - * See PAGE_SIZE. - */ -static long _pagesize; - -/* - * Segment lists, ordered by priority (higher priority lists have lower - * numerical priorities). - */ -static struct list phys_seg_lists[PHYS_NR_SEG_LISTS]; - -/* - * Segment table. - */ -static struct phys_seg phys_segs[PHYS_MAX_SEGMENTS]; - -/* - * Number of loaded segments. - */ -static unsigned int phys_segs_size; - -/* - * Page/address conversion macros. - */ -#define phys_atop(addr) ((addr) / PAGE_SIZE) -#define phys_ptoa(pfn) ((pfn) * PAGE_SIZE) - -static void -phys_page_init(struct phys_page *page, struct phys_seg *seg, phys_paddr_t pa) -{ - page->seg = seg; - page->phys_addr = pa; - page->level = PHYS_LEVEL_ALLOCATED; -} - -static inline struct phys_page * -phys_page_lookup(phys_paddr_t pa) -{ - struct phys_seg *seg; - unsigned int i; - - for (i = 0; i < phys_segs_size; i++) { - seg = &phys_segs[i]; - - if ((pa >= seg->start) && (pa < seg->end)) - return &seg->pages[phys_atop(pa - seg->start)]; - } - - return NULL; -} - -static void -phys_free_list_init(struct phys_free_list *free_list) -{ - free_list->size = 0; - list_init(&free_list->blocks); -} - -static inline void -phys_free_list_insert(struct phys_free_list *free_list, struct phys_page *page) -{ - assert(page->level == PHYS_LEVEL_ALLOCATED); - - free_list->size++; - list_insert(&free_list->blocks, &page->node); -} - -static inline void -phys_free_list_remove(struct phys_free_list *free_list, struct phys_page *page) -{ - assert(free_list->size != 0); - assert(!list_empty(&free_list->blocks)); - assert(page->level < PHYS_NR_FREE_LISTS); - - free_list->size--; - list_remove(&page->node); -} - -static struct phys_page * -phys_seg_alloc_from_buddy(struct phys_seg *seg, unsigned int level) -{ - struct phys_free_list *free_list; - struct phys_page *page, *buddy; - unsigned int i; - - assert(level < PHYS_NR_FREE_LISTS); - - for (i = level; i < PHYS_NR_FREE_LISTS; i++) { - free_list = &seg->free_lists[i]; - - if (free_list->size != 0) - break; - } - - if (i == PHYS_NR_FREE_LISTS) - return NULL; - - page = list_first_entry(&free_list->blocks, struct phys_page, node); - phys_free_list_remove(free_list, page); - page->level = PHYS_LEVEL_ALLOCATED; - - while (i > level) { - i--; - buddy = &page[1 << i]; - phys_free_list_insert(&seg->free_lists[i], buddy); - buddy->level = i; - } - - seg->nr_free_pages -= (1 << level); - return page; -} - -static void -phys_seg_free_to_buddy(struct phys_seg *seg, struct phys_page *page, - unsigned int level) -{ - struct phys_page *buddy; - phys_paddr_t pa, buddy_pa; - unsigned int nr_pages; - - assert(page >= seg->pages); - assert(page < seg->pages_end); - assert(page->level == PHYS_LEVEL_ALLOCATED); - assert(level < PHYS_NR_FREE_LISTS); - - nr_pages = (1 << level); - pa = page->phys_addr; - - while (level < (PHYS_NR_FREE_LISTS - 1)) { - buddy_pa = pa ^ phys_ptoa(1 << level); - - if ((buddy_pa < seg->start) || (buddy_pa >= seg->end)) - break; - - buddy = &seg->pages[phys_atop(buddy_pa - seg->start)]; - - if (buddy->level != level) - break; - - phys_free_list_remove(&seg->free_lists[level], buddy); - buddy->level = PHYS_LEVEL_ALLOCATED; - level++; - pa &= -phys_ptoa(1 << level); - page = &seg->pages[phys_atop(pa - seg->start)]; - } - - phys_free_list_insert(&seg->free_lists[level], page); - page->level = level; - seg->nr_free_pages += nr_pages; -} - -static void -phys_cpu_pool_init(struct phys_cpu_pool *cpu_pool, int size) -{ - pthread_mutex_init(&cpu_pool->lock, NULL); - cpu_pool->size = size; - cpu_pool->transfer_size = (size + PHYS_CPU_POOL_TRANSFER_RATIO - 1) - / PHYS_CPU_POOL_TRANSFER_RATIO; - cpu_pool->nr_pages = 0; - list_init(&cpu_pool->pages); -} - -/* - * Return a CPU pool. - * - * This function will generally return the pool matching the CPU running the - * calling thread. Because of context switches and thread migration, the - * caller might be running on another processor after this function returns. - * Although not optimal, this should rarely happen, and it doesn't affect the - * allocator operations in any other way, as CPU pools are always valid, and - * their access is serialized by a lock. - */ -static inline struct phys_cpu_pool * -phys_cpu_pool_get(struct phys_seg *seg) -{ - return &seg->cpu_pools[cpu_id()]; -} - -static inline struct phys_page * -phys_cpu_pool_pop(struct phys_cpu_pool *cpu_pool) -{ - struct phys_page *page; - - assert(cpu_pool->nr_pages != 0); - cpu_pool->nr_pages--; - page = list_first_entry(&cpu_pool->pages, struct phys_page, node); - list_remove(&page->node); - return page; -} - -static inline void -phys_cpu_pool_push(struct phys_cpu_pool *cpu_pool, struct phys_page *page) -{ - assert(cpu_pool->nr_pages < cpu_pool->size); - cpu_pool->nr_pages++; - list_insert(&cpu_pool->pages, &page->node); -} - -static int -phys_cpu_pool_fill(struct phys_cpu_pool *cpu_pool, struct phys_seg *seg) -{ - struct phys_page *page; - int i; - - assert(cpu_pool->nr_pages == 0); - - pthread_mutex_lock(&seg->lock); - - for (i = 0; i < cpu_pool->transfer_size; i++) { - page = phys_seg_alloc_from_buddy(seg, 0); - - if (page == NULL) - break; - - phys_cpu_pool_push(cpu_pool, page); - } - - pthread_mutex_unlock(&seg->lock); - - return i; -} - -static void -phys_cpu_pool_drain(struct phys_cpu_pool *cpu_pool, struct phys_seg *seg) -{ - struct phys_page *page; - int i; - - assert(cpu_pool->nr_pages == cpu_pool->size); - - pthread_mutex_lock(&seg->lock); - - for (i = cpu_pool->transfer_size; i > 0; i--) { - page = phys_cpu_pool_pop(cpu_pool); - phys_seg_free_to_buddy(seg, page, 0); - } - - pthread_mutex_unlock(&seg->lock); -} - -static inline phys_paddr_t -phys_seg_start(struct phys_seg *seg) -{ - return seg->start; -} - -static inline phys_paddr_t -phys_seg_end(struct phys_seg *seg) -{ - return seg->end; -} - -static inline phys_paddr_t -phys_seg_size(struct phys_seg *seg) -{ - return phys_seg_end(seg) - phys_seg_start(seg); -} - -static int -phys_seg_compute_pool_size(struct phys_seg *seg) -{ - phys_paddr_t size; - - size = phys_atop(phys_seg_size(seg)) / PHYS_CPU_POOL_RATIO; - - if (size == 0) - size = 1; - else if (size > PHYS_CPU_POOL_MAX_SIZE) - size = PHYS_CPU_POOL_MAX_SIZE; - - return size; -} - -static void -phys_seg_init(struct phys_seg *seg, struct phys_page *pages) -{ - phys_paddr_t pa; - int pool_size; - unsigned int i; - - pool_size = phys_seg_compute_pool_size(seg); - - for (i = 0; i < ARRAY_SIZE(seg->cpu_pools); i++) - phys_cpu_pool_init(&seg->cpu_pools[i], pool_size); - - seg->pages = pages; - seg->pages_end = pages + phys_atop(phys_seg_size(seg)); - pthread_mutex_init(&seg->lock, NULL); - - for (i = 0; i < ARRAY_SIZE(seg->free_lists); i++) - phys_free_list_init(&seg->free_lists[i]); - - seg->nr_free_pages = 0; - - for (pa = phys_seg_start(seg); pa < phys_seg_end(seg); pa += PAGE_SIZE) - phys_page_init(&pages[phys_atop(pa - phys_seg_start(seg))], seg, pa); -} - -/* - * Return the level (i.e. the index in the free lists array) matching the - * given size. - */ -static inline unsigned int -phys_get_level(phys_size_t size) -{ - size = P2ROUND(size, PAGE_SIZE) / PAGE_SIZE; - assert(size != 0); - size--; - - if (size == 0) - return 0; - else - return (sizeof(size) * CHAR_BIT) - __builtin_clzl(size); -} - -static struct phys_page * -phys_seg_alloc(struct phys_seg *seg, phys_size_t size) -{ - struct phys_cpu_pool *cpu_pool; - struct phys_page *page; - unsigned int level; - int filled; - - level = phys_get_level(size); - - if (level == 0) { - cpu_pool = phys_cpu_pool_get(seg); - - pthread_mutex_lock(&cpu_pool->lock); - - if (cpu_pool->nr_pages == 0) { - filled = phys_cpu_pool_fill(cpu_pool, seg); - - if (!filled) { - pthread_mutex_unlock(&cpu_pool->lock); - return NULL; - } - } - - page = phys_cpu_pool_pop(cpu_pool); - pthread_mutex_unlock(&cpu_pool->lock); - } else { - pthread_mutex_lock(&seg->lock); - page = phys_seg_alloc_from_buddy(seg, level); - pthread_mutex_unlock(&seg->lock); - } - - return page; -} - -static void -phys_seg_free(struct phys_seg *seg, struct phys_page *page, phys_size_t size) -{ - struct phys_cpu_pool *cpu_pool; - unsigned int level; - - level = phys_get_level(size); - - if (level == 0) { - cpu_pool = phys_cpu_pool_get(seg); - - pthread_mutex_lock(&cpu_pool->lock); - - if (cpu_pool->nr_pages == cpu_pool->size) - phys_cpu_pool_drain(cpu_pool, seg); - - phys_cpu_pool_push(cpu_pool, page); - pthread_mutex_unlock(&cpu_pool->lock); - } else { - pthread_mutex_lock(&seg->lock); - phys_seg_free_to_buddy(seg, page, level); - pthread_mutex_unlock(&seg->lock); - } -} - -/* - * Load memory during initialization. - * - * This function partially initializes a segment. - */ -static void -phys_load_segment(const char *name, phys_paddr_t start, phys_paddr_t end, - unsigned int seg_list_prio) -{ - static int initialized = 0; - struct phys_seg *seg; - struct list *seg_list; - unsigned int i; - - assert(name != NULL); - assert(start < end); - assert(seg_list_prio < ARRAY_SIZE(phys_seg_lists)); - - if (!initialized) { - for (i = 0; i < ARRAY_SIZE(phys_seg_lists); i++) - list_init(&phys_seg_lists[i]); - - phys_segs_size = 0; - initialized = 1; - } - - if (phys_segs_size >= ARRAY_SIZE(phys_segs)) - error_die(ERR_NORES); - - seg_list = &phys_seg_lists[seg_list_prio]; - seg = &phys_segs[phys_segs_size]; - - list_insert_tail(seg_list, &seg->node); - seg->start = start; - seg->end = end; - strncpy(seg->name, name, PHYS_NAME_SIZE); - seg->name[sizeof(seg->name) - 1] = '\0'; - - phys_segs_size++; -} - -/* - * Loading segments is normally done by architecture-specific code. In - * this implementation, an Intel machine with two segments of RAM is - * virtualized. - */ -static void -phys_load_segments(void) -{ - phys_paddr_t start, end; - size_t size; - void *addr; - - /* - * Load the ISADMA segment. - */ - size = PHYS_ISADMA_LIMIT; - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) - error_die(ERR_NOMEM); - - start = (phys_paddr_t)addr; - end = start + size; - phys_load_segment("isadma", start, end, PHYS_SEGLIST_ISADMA); - - /* - * Load the normal segment. - */ - size = PHYS_NORMAL_LIMIT - PHYS_ISADMA_LIMIT; - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) - error_die(ERR_NOMEM); - - start = (phys_paddr_t)addr; - end = start + size; - phys_load_segment("normal", start, end, PHYS_SEGLIST_NORMAL); -} - -void -phys_setup(void) -{ - struct phys_seg *seg, *map_seg; - struct phys_page *page, *map; - struct list *seg_list; - phys_paddr_t map_size; - unsigned int i; - - _pagesize = sysconf(_SC_PAGESIZE); - assert(ISP2(_pagesize)); - - phys_load_segments(); - - /* - * Compute the memory map size. - */ - map_size = 0; - - for (i = 0; i < phys_segs_size; i++) - map_size += phys_atop(phys_seg_size(&phys_segs[i])); - - map_size = P2ROUND(map_size * sizeof(struct phys_page), PAGE_SIZE); - - /* - * Find a segment from which to allocate the memory map. - */ - for (seg_list = &phys_seg_lists[ARRAY_SIZE(phys_seg_lists) - 1]; - seg_list >= phys_seg_lists; - seg_list--) - list_for_each_entry(seg_list, map_seg, node) - if (map_size <= phys_seg_size(map_seg)) - goto found; - - error_die(ERR_NOMEM); - -found: - /* - * Allocate the memory map. - */ - map = (struct phys_page *)phys_seg_start(map_seg); - - /* - * Initialize the segments, associating them to the memory map. When - * the segments are initialized, all their pages are set allocated, - * with a block size of one (level 0). They are then released, which - * populates the free lists. - */ - for (i = 0; i < phys_segs_size; i++) { - seg = &phys_segs[i]; - phys_seg_init(seg, map); - - /* - * Don't release the memory map pages. - * - * XXX The memory map pages normally don't need descriptors, as they - * are never released. This implementation however can be used in - * cases where some memory is reserved at the start of all segments. - * In order not to require descriptors for the memory map, the segment - * where the map resides should be split. As it is quite cumbersome, - * no effort is made here to avoid wasting descriptors and the pages - * containing them. - */ - if (seg == map_seg) - page = seg->pages + phys_atop(map_size); - else - page = seg->pages; - - while (page < seg->pages_end) { - phys_seg_free_to_buddy(seg, page, 0); - page++; - } - - map += phys_atop(phys_seg_size(seg)); - } -} - -struct phys_page * -phys_alloc_pages(phys_size_t size) -{ - struct list *seg_list; - struct phys_seg *seg; - struct phys_page *page; - - for (seg_list = &phys_seg_lists[ARRAY_SIZE(phys_seg_lists) - 1]; - seg_list >= phys_seg_lists; - seg_list--) - list_for_each_entry(seg_list, seg, node) { - page = phys_seg_alloc(seg, size); - - if (page != NULL) - return page; - } - - return NULL; -} - -void -phys_free_pages(struct phys_page *page, phys_size_t size) -{ - phys_seg_free(page->seg, page, size); -} - -phys_paddr_t -phys_alloc(phys_size_t size) -{ - struct phys_page *page; - - page = phys_alloc_pages(size); - - /* - * XXX Rely on the system to never provide a virtual memory area - * starting at 0. - */ - if (page == NULL) - return 0; - - return page->phys_addr; -} - -void -phys_free(phys_paddr_t pa, phys_size_t size) -{ - struct phys_page *page; - - page = phys_page_lookup(pa); - assert(page != NULL); - phys_free_pages(page, size); -} - -void -phys_info(void) -{ - struct phys_seg *seg; - unsigned int i, j; - char name[16]; - - printf(" name"); - - for (i = 0; i < PHYS_NR_FREE_LISTS; i++) { - snprintf(name, sizeof(name), "#%u", (1 << i)); - printf(" %5s", name); - } - - printf("\n"); - - for (i = 0; i < phys_segs_size; i++) { - seg = &phys_segs[i]; - - printf("%8s", seg->name); - - pthread_mutex_lock(&seg->lock); - - for (j = 0; j < ARRAY_SIZE(seg->free_lists); j++) - printf(" %5lu", seg->free_lists[j].size); - - pthread_mutex_unlock(&seg->lock); - - printf("\n"); - } -} @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011 Richard Braun. - * 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 ``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 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. - * - * - * Page allocator. - */ - -#ifndef _PHYS_H -#define _PHYS_H - -#include "list.h" - -/* - * Physical address. - */ -typedef unsigned long phys_paddr_t; - -/* - * Memory range size. - */ -typedef unsigned long phys_size_t; - -/* - * Page descriptor. - */ -struct phys_page { - struct list node; - struct phys_seg *seg; - phys_paddr_t phys_addr; - unsigned int level; -}; - -void phys_setup(void); - -struct phys_page * phys_alloc_pages(phys_size_t size); - -void phys_free_pages(struct phys_page *page, phys_size_t size); - -phys_paddr_t phys_alloc(phys_size_t size); - -void phys_free(phys_paddr_t pa, phys_size_t size); - -void phys_info(void); - -#endif /* _PHYS_H */ diff --git a/test/test_mem.c b/test/test_mem.c deleted file mode 100644 index eb7a56c..0000000 --- a/test/test_mem.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -#define STRING "This is a test string." -#define STRING_SIZE (STRLEN(STRING) + 1) - -int -main(int argc, char *argv[]) -{ - char *s; - - (void)argc; - (void)argv; - - mem_setup(); - - s = mem_alloc(STRING_SIZE); - - if (s == NULL) { - fprintf(stderr, "unable to allocate memory\n"); - return 1; - } - - strcpy(s, STRING); - printf("string: '%s'\n", s); - mem_free(s, STRING_SIZE); - - return 0; -} diff --git a/test/test_mem_cache.c b/test/test_mem_cache.c deleted file mode 100644 index eaa043a..0000000 --- a/test/test_mem_cache.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <assert.h> -#include <stddef.h> -#include <pthread.h> - -#include <stdio.h> - -#include "../cpu.h" -#include "../mem.c" -#include "../error.c" -#include "../avltree.c" - -#if CONFIG_MEM_USE_PHYS -#include "../phys.c" -#endif /* CONFIG_MEM_USE_PHYS */ - -#define NTHREADS 4 -#define TEST_TIME 60 -#define OBJSPERLOOP 100 - -struct result { - unsigned long allocs; - unsigned long frees; -} __aligned(CPU_L1_SIZE); - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static struct mem_cache *obj_cache; -static volatile int work; -static struct result results[NTHREADS]; - -static void * -run(void *arg) -{ - struct obj *objs[OBJSPERLOOP]; - struct result *result; - int i, id; - - result = arg; - id = result - results; - - mem_print("started"); - - while (work) { - for (i = 0; i < OBJSPERLOOP; i++) { - objs[i] = mem_cache_alloc(obj_cache); - result->allocs++; - } - - for (i = 0; i < OBJSPERLOOP; i++) { - mem_cache_free(obj_cache, objs[i]); - result->frees++; - } - } - - return NULL; -} - -int -main(int argc, char *argv[]) -{ - pthread_t threads[NTHREADS]; - unsigned long ops; - int i; - - (void)argc; - (void)argv; - - mem_setup(); - - mem_info(); - - mem_print("Selected cache line size: %u", CPU_L1_SIZE); - mem_print("sizeof(pthread_mutex_t): %zu", sizeof(pthread_mutex_t)); - mem_print("sizeof(struct mem_cpu_pool): %zu", sizeof(struct mem_cpu_pool)); - mem_print("sizeof(union mem_bufctl): %zu", sizeof(union mem_bufctl)); - mem_print("sizeof(struct mem_buftag): %zu", sizeof(struct mem_buftag)); - mem_print("sizeof(struct mem_slab): %zu", sizeof(struct mem_slab)); - mem_print("sizeof(struct mem_cache): %zu", sizeof(struct mem_cache)); - mem_print("sizeof(struct obj): %zu", sizeof(struct obj)); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, 0); - - memset(results, 0, sizeof(results)); - work = 1; - - for (i = 0; i < NTHREADS; i++) - pthread_create(&threads[i], NULL, run, &results[i]); - - sleep(TEST_TIME); - work = 0; - - for (i = 0; i < NTHREADS; i++) - pthread_join(threads[i], NULL); - - ops = 0; - - for (i = 0; i < NTHREADS; i++) - ops += results[i].allocs + results[i].frees; - - mem_info(); - mem_cache_info(obj_cache); - mem_print("total: %lu ops in %d secs", ops, TEST_TIME); - - mem_cache_destroy(obj_cache); - - return 0; -} diff --git a/test/test_mem_cache_double_free.c b/test/test_mem_cache_double_free.c deleted file mode 100644 index de356a3..0000000 --- a/test/test_mem_cache_double_free.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - - printf("trying normal alloc+free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - mem_cache_info(obj_cache); - - printf("trying alloc+double free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_invalid_free.c b/test/test_mem_cache_invalid_free.c deleted file mode 100644 index 767ee6b..0000000 --- a/test/test_mem_cache_invalid_free.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - - printf("trying normal alloc+free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - mem_cache_info(obj_cache); - - printf("trying alloc+invalid free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, (char *)obj + 1); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_write_beyond.c b/test/test_mem_cache_write_beyond.c deleted file mode 100644 index 05bea2e..0000000 --- a/test/test_mem_cache_write_beyond.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static void -obj_print(struct obj *obj, size_t size) -{ - unsigned char *ptr, *end; - - printf("buffer content: "); - - for (ptr = (unsigned char *)obj, end = ptr + size; ptr < end; ptr++) - printf("%02x", *ptr); - - printf("\n"); -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - size_t buf_size; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - mem_cache_info(obj_cache); - - printf("doing normal alloc:\n"); - obj = mem_cache_alloc(obj_cache); - - printf("writing beyond object boundary:\n"); - strcpy(obj->name, "invalid write 000000"); - buf_size = P2ROUND(sizeof(*obj), 8); - obj_print(obj, buf_size + (sizeof(unsigned long) * 2)); - - printf("doing normal free:\n"); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_write_buftag.c b/test/test_mem_cache_write_buftag.c deleted file mode 100644 index 539d399..0000000 --- a/test/test_mem_cache_write_buftag.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static void -obj_print(struct obj *obj, size_t size) -{ - unsigned char *ptr, *end; - - printf("buffer content: "); - - for (ptr = (unsigned char *)obj, end = ptr + size; ptr < end; ptr++) - printf("%02x", *ptr); - - printf("\n"); -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - unsigned long *ptr; - size_t buf_size; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - mem_cache_info(obj_cache); - - printf("doing normal alloc:\n"); - obj = mem_cache_alloc(obj_cache); - - printf("writing garbage in buftag:\n"); - buf_size = P2ROUND(sizeof(*obj), 8); - ptr = (void *)obj + buf_size + sizeof(unsigned long); - *ptr = 0xed0000a1; - obj_print(obj, buf_size + (sizeof(unsigned long) * 2)); - - printf("doing normal free:\n"); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_write_free.c b/test/test_mem_cache_write_free.c deleted file mode 100644 index fd1c9db..0000000 --- a/test/test_mem_cache_write_free.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 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 <sched.h> - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static void -obj_print(struct obj *obj) -{ - unsigned char *ptr, *end; - - printf("buffer content: "); - - for (ptr = (unsigned char *)obj, end = ptr + sizeof(*obj); - ptr < end; - ptr++) - printf("%02x", *ptr); - - printf("\n"); -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - cpu_set_t cpu_set; - - (void)argc; - (void)argv; - - printf("binding to CPU 0\n"); - CPU_ZERO(&cpu_set); - CPU_SET(0, &cpu_set); - sched_setaffinity(0, sizeof(cpu_set), &cpu_set); - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - - printf("doing normal alloc+free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - mem_cache_info(obj_cache); - - obj_print(obj); - - printf("doing write on free object:\n"); - memset((void *)obj + 3, 0x89, 5); - - obj_print(obj); - - printf("trying normal alloc+free on same CPU (should fail):\n"); - - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_offbyone.c b/test/test_mem_offbyone.c deleted file mode 100644 index b9e7d75..0000000 --- a/test/test_mem_offbyone.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <string.h> - -#include "../mem.h" - -#define BUFSIZE 200 - -int -main(int argc, char *argv[]) -{ - char *s; - - (void)argc; - (void)argv; - - mem_setup(); - - printf("allocating memory:\n"); - s = mem_alloc(BUFSIZE); - - if (s == NULL) { - fprintf(stderr, "unable to allocate memory\n"); - return 1; - } - - printf("writing beyond end of buffer:\n"); - memset(s, 'a', BUFSIZE + 1); - - printf("releasing buffer, should fail if CONFIG_MEM_VERIFY defined\n"); - mem_free(s, BUFSIZE); - - return 0; -} diff --git a/test/test_phys.c b/test/test_phys.c deleted file mode 100644 index 5ded8d5..0000000 --- a/test/test_phys.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2011 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stddef.h> -#include <string.h> - -#include "../error.c" -#include "../phys.c" - -int -main(int argc, char *argv[]) -{ - struct phys_page *page; - - (void)argc; - (void)argv; - - phys_setup(); - - phys_info(); - printf("sizeof(struct phys_cpu_pool) = %zu\n", - sizeof(struct phys_cpu_pool)); - printf("sizeof(struct phys_free_list) = %zu\n", - sizeof(struct phys_free_list)); - printf("sizeof(struct phys_page): %zu\n", sizeof(struct phys_page)); - printf("sizeof(struct phys_seg): %zu\n", sizeof(struct phys_seg)); - printf("allocating two pages\n"); - page = phys_alloc_pages(PAGE_SIZE * 2); - - if (page == NULL) { - fprintf(stderr, "unable to allocate memory\n"); - return 1; - } - - phys_info(); - - printf("freeing the allocated pages\n"); - phys_free_pages(page, PAGE_SIZE * 2); - phys_info(); - - return 0; -} diff --git a/test/test_xprintf.c b/test/test_xprintf.c deleted file mode 100644 index ff157b8..0000000 --- a/test/test_xprintf.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <stdarg.h> -#include <string.h> - -#include "../macros.h" -#include "../xprintf.h" - -#define TEST_PRINTF(format, ...) \ -MACRO_BEGIN \ - char stra[256], strb[256]; \ - int la, lb; \ - la = snprintf(stra, 256, format, ## __VA_ARGS__); \ - printf(" printf: %s", stra); \ - lb = xsnprintf(strb, 256, format, ## __VA_ARGS__); \ - xprintf("xprintf: %s", stra); \ - assert(la == lb); \ - assert(strcmp(stra, strb) == 0); \ -MACRO_END - -int -main(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - -#define FORMAT "%c" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 'a'); -#undef FORMAT - -#define FORMAT "%8c" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 'a'); -#undef FORMAT - -#define FORMAT "%-8c" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 'a'); -#undef FORMAT - -#define FORMAT "%.s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%.3s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%4.3s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%-4.3s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%3.4s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%-3.4s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%#o" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%#x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%#X" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%-8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%-08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%0-8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "% d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%+d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%+ d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%12d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%*d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 12, 123); -#undef FORMAT - -#define FORMAT "%.12d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%.012d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%.*d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 12, 123); -#undef FORMAT - -#define FORMAT "%.d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%.*d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -12, 123); -#undef FORMAT - -#define FORMAT "%.4d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%5.4d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%4.5d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%.0d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%.0o" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%.0x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%1.0d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%08.0d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%.8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%.80d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%-80d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%80d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%80.40d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%-+80.40d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%+x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%#x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%#o" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%p" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "123"); -#undef FORMAT - -#define FORMAT "%#lx" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0xdeadbeefL); -#undef FORMAT - -#define FORMAT "%zd" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, (size_t)-123); -#undef FORMAT - -#define FORMAT "%#llx" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0xdeadbeefbadcafeLL); -#undef FORMAT - -#define FORMAT "%llo" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0xffffffffffffffffLL); -#undef FORMAT - -#define FORMAT "%%" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT); -#undef FORMAT - -#define FORMAT "%y" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT); -#undef FORMAT - - char stra[10], strb[10]; - int la, lb; - la = snprintf(stra, sizeof(stra), "%s", "123456789a"); - printf(" printf: %s\n", stra); - lb = xsnprintf(strb, sizeof(strb), "%s", "123456789a"); - xprintf("xprintf: %s\n", stra); - assert(la == lb); - assert(strncmp(stra, strb, 10) == 0); - -#define FORMAT "12%n3%#08x4%n5" - int lc, ld; - sprintf(stra, FORMAT, &la, 123, &lb); - printf(" printf: la: %d, lb: %d\n", la, lb); - xsprintf(strb, FORMAT, &lc, 123, &ld); - xprintf("xprintf: lc: %d, ld: %d\n", lc, ld); - assert(la == lc); - assert(lb == ld); -#undef FORMAT - - la = snprintf(NULL, 0, "%s", "123"); - printf(" printf: %d\n", la); - lb = xsnprintf(NULL, 0, "%s", "123"); - xprintf("xprintf: %d\n", lb); - assert(la == lb); - - return 0; -} diff --git a/xprintf.c b/xprintf.c deleted file mode 100644 index 7efb786..0000000 --- a/xprintf.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <limits.h> -#include <stdarg.h> -#include <stddef.h> -#include <unistd.h> -#include <pthread.h> - -#include "xprintf.h" - -/* - * Formatting flags. - * - * FORMAT_LOWER must be 0x20 as it is OR'd with digits, eg. - * '0': 0x30 | 0x20 => 0x30 ('0') - * 'A': 0x41 | 0x20 => 0x61 ('a') - */ -#define FORMAT_ALT_FORM 0x01 /* "Alternate form" */ -#define FORMAT_ZERO_PAD 0x02 /* Zero padding on the left */ -#define FORMAT_LEFT_JUSTIFY 0x04 /* Align text on the left */ -#define FORMAT_BLANK 0x08 /* Blank space before positive number */ -#define FORMAT_SIGN 0x10 /* Always place a sign (either + or -) */ -#define FORMAT_LOWER 0x20 /* To lowercase (for %x) */ -#define FORMAT_CONV_SIGNED 0x40 /* Format specifies signed conversion */ - -enum { - MODIFIER_NONE, - MODIFIER_CHAR, - MODIFIER_SHORT, - MODIFIER_LONG, - MODIFIER_LONGLONG, - MODIFIER_PTR, /* Used only for %p */ - MODIFIER_SIZE, - MODIFIER_PTRDIFF -}; - -enum { - SPECIFIER_INVALID, - SPECIFIER_INT, - SPECIFIER_CHAR, - SPECIFIER_STR, - SPECIFIER_NRCHARS, - SPECIFIER_PERCENT -}; - -/* - * Size for the temporary number buffer. The minimum base is 8 so 3 bits - * are consumed per digit. Add one to round up. The conversion algorithm - * doesn't use the null byte. - */ -#define MAX_NUM_SIZE (((sizeof(unsigned long long) * CHAR_BIT) / 3) + 1) - -/* - * Special size for xvsnprintf(), used by xsprintf()/xvsprintf() when the - * buffer size is unknown. - */ -#define XPRINT_NOLIMIT ((size_t)-1) - -/* - * Size of the static buffer used by xprintf()/xvprintf(). - */ -#define XPRINT_BUFSIZE 1024 - -static char xprint_buffer[XPRINT_BUFSIZE]; -static pthread_mutex_t xprint_mutex = PTHREAD_MUTEX_INITIALIZER; - -static const char digits[] = "0123456789ABCDEF"; - -static inline char * -xputchar(char *str, char *end, char c) -{ - if (str < end) - *str = c; - - str++; - - return str; -} - -static inline int -xisdigit(char c) -{ - return (c >= '0') && (c <= '9'); -} - -int -xprintf(const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = xvprintf(format, ap); - va_end(ap); - - return length; -} - -int -xvprintf(const char *format, va_list ap) -{ - size_t size; - int length; - - pthread_mutex_lock(&xprint_mutex); - length = xvsnprintf(xprint_buffer, sizeof(xprint_buffer), format, ap); - size = ((unsigned int)length >= sizeof(xprint_buffer)) - ? sizeof(xprint_buffer) - 1 - : (unsigned int)length; - fwrite(xprint_buffer, 1, size, stdout); - pthread_mutex_unlock(&xprint_mutex); - - return length; -} - -int -xsprintf(char *str, const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = xvsprintf(str, format, ap); - va_end(ap); - - return length; -} - -int -xvsprintf(char *str, const char *format, va_list ap) -{ - return xvsnprintf(str, XPRINT_NOLIMIT, format, ap); -} - -int -xsnprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = xvsnprintf(str, size, format, ap); - va_end(ap); - - return length; -} - -int -xvsnprintf(char *str, size_t size, const char *format, va_list ap) -{ - unsigned long long n; - int i, len, found, flags, width, precision, modifier, specifier, shift; - unsigned char r, base, mask; - char c, *s, *start, *end, sign, tmp[MAX_NUM_SIZE]; - - start = str; - - if (size == 0) - end = NULL; - else if (size == XPRINT_NOLIMIT) - end = (char *)-1; - else - end = start + size - 1; - - while ((c = *format) != '\0') { - if (c != '%') { - str = xputchar(str, end, c); - format++; - continue; - } - - /* Flags */ - - found = 1; - flags = 0; - - do { - format++; - c = *format; - - switch (c) { - case '#': - flags |= FORMAT_ALT_FORM; - break; - case '0': - flags |= FORMAT_ZERO_PAD; - break; - case '-': - flags |= FORMAT_LEFT_JUSTIFY; - break; - case ' ': - flags |= FORMAT_BLANK; - break; - case '+': - flags |= FORMAT_SIGN; - break; - default: - found = 0; - break; - } - } while (found); - - /* Width */ - - if (xisdigit(c)) { - width = 0; - - while (xisdigit(c)) { - width = width * 10 + (c - '0'); - format++; - c = *format; - } - } else if (c == '*') { - width = va_arg(ap, int); - - if (width < 0) { - flags |= FORMAT_LEFT_JUSTIFY; - width = -width; - } - - format++; - c = *format; - } else { - width = 0; - } - - /* Precision */ - - if (c == '.') { - format++; - c = *format; - - if (xisdigit(c)) { - precision = 0; - - while (xisdigit(c)) { - precision = precision * 10 + (c - '0'); - format++; - c = *format; - } - } else if (c == '*') { - precision = va_arg(ap, int); - - if (precision < 0) - precision = 0; - - format++; - c = *format; - } else { - precision = 0; - } - } else { - /* precision is >= 0 only if explicit */ - precision = -1; - } - - /* Length modifier */ - - switch (c) { - case 'h': - case 'l': - format++; - - if (c == *format) { - modifier = (c == 'h') ? MODIFIER_CHAR : MODIFIER_LONGLONG; - goto skip_modifier; - } else { - modifier = (c == 'h') ? MODIFIER_SHORT : MODIFIER_LONG; - c = *format; - } - - break; - case 'z': - modifier = MODIFIER_SIZE; - goto skip_modifier; - case 't': - modifier = MODIFIER_PTRDIFF; -skip_modifier: - format++; - c = *format; - break; - default: - modifier = MODIFIER_NONE; - break; - } - - /* Specifier */ - - switch (c) { - case 'd': - case 'i': - flags |= FORMAT_CONV_SIGNED; - case 'u': - base = 10; - goto integer; - case 'o': - base = 8; - goto integer; - case 'p': - flags |= FORMAT_ALT_FORM; - modifier = MODIFIER_PTR; - case 'x': - flags |= FORMAT_LOWER; - case 'X': - base = 16; -integer: - specifier = SPECIFIER_INT; - break; - case 'c': - specifier = SPECIFIER_CHAR; - break; - case 's': - specifier = SPECIFIER_STR; - break; - case 'n': - specifier = SPECIFIER_NRCHARS; - break; - case '%': - specifier = SPECIFIER_PERCENT; - break; - default: - specifier = SPECIFIER_INVALID; - break; - } - - /* Output */ - - switch (specifier) { - case SPECIFIER_INT: - switch (modifier) { - case MODIFIER_CHAR: - if (flags & FORMAT_CONV_SIGNED) - n = (signed char)va_arg(ap, int); - else - n = (unsigned char)va_arg(ap, int); - break; - case MODIFIER_SHORT: - if (flags & FORMAT_CONV_SIGNED) - n = (short)va_arg(ap, int); - else - n = (unsigned short)va_arg(ap, int); - break; - case MODIFIER_LONG: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, long); - else - n = va_arg(ap, unsigned long); - break; - case MODIFIER_LONGLONG: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, long long); - else - n = va_arg(ap, unsigned long long); - break; - case MODIFIER_PTR: - n = (unsigned long)va_arg(ap, void *); - break; - case MODIFIER_SIZE: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, ssize_t); - else - n = va_arg(ap, size_t); - break; - case MODIFIER_PTRDIFF: - n = va_arg(ap, ptrdiff_t); - break; - default: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, int); - else - n = va_arg(ap, unsigned int); - break; - } - - if ((flags & FORMAT_LEFT_JUSTIFY) || (precision >= 0)) - flags &= ~FORMAT_ZERO_PAD; - - sign = 0; - - if (flags & FORMAT_ALT_FORM) { - /* '0' for octal */ - width--; - - /* '0x' or '0X' for hexadecimal */ - if (base == 16) - width--; - } else if (flags & FORMAT_CONV_SIGNED) { - if ((long long)n < 0) { - sign = '-'; - width--; - n = -(long long)n; - } else if (flags & FORMAT_SIGN) { - /* FORMAT_SIGN must precede FORMAT_BLANK. */ - sign = '+'; - width--; - } else if (flags & FORMAT_BLANK) { - sign = ' '; - width--; - } - } - - /* Conversion, in reverse order */ - - i = 0; - - if (n == 0) { - if (precision != 0) - tmp[i++] = '0'; - } else if (base == 10) { - /* - * Try to avoid 64 bits operations if the processor doesn't - * support them. Note that even when using modulus and - * division operators close to each other, the compiler may - * forge two calls to __udivdi3() and __umoddi3() instead of - * one to __udivmoddi3(), whereas processor instructions are - * generally correctly used once, giving both the remainder - * and the quotient, through plain or reciprocal division. - */ -#ifndef __LP64__ - if (modifier == MODIFIER_LONGLONG) { -#endif /* __LP64__ */ - do { - r = n % 10; - n /= 10; - tmp[i++] = digits[r]; - } while (n != 0); -#ifndef __LP64__ - } else { - unsigned long m; - - m = (unsigned long)n; - - do { - r = m % 10; - m /= 10; - tmp[i++] = digits[r]; - } while (m != 0); - } -#endif /* __LP64__ */ - } else { - mask = base - 1; - shift = (base == 8) ? 3 : 4; - - do { - r = (unsigned char)n & mask; - n >>= shift; - tmp[i++] = digits[r] | (flags & FORMAT_LOWER); - } while (n != 0); - } - - if (i > precision) - precision = i; - - width -= precision; - - if (!(flags & (FORMAT_LEFT_JUSTIFY | FORMAT_ZERO_PAD))) - while (width-- > 0) - str = xputchar(str, end, ' '); - - if (flags & FORMAT_ALT_FORM) { - str = xputchar(str, end, '0'); - - if (base == 16) - str = xputchar(str, end, 'X' | (flags & FORMAT_LOWER)); - } else if (sign) { - str = xputchar(str, end, sign); - } - - if (!(flags & FORMAT_LEFT_JUSTIFY)) { - c = (flags & FORMAT_ZERO_PAD) ? '0' : ' '; - - while (width-- > 0) - str = xputchar(str, end, c); - } - - while (i < precision--) - str = xputchar(str, end, '0'); - - while (i-- > 0) - str = xputchar(str, end, tmp[i]); - - while (width-- > 0) - str = xputchar(str, end, ' '); - - break; - case SPECIFIER_CHAR: - c = (unsigned char)va_arg(ap, int); - - if (!(flags & FORMAT_LEFT_JUSTIFY)) - while (--width > 0) - str = xputchar(str, end, ' '); - - str = xputchar(str, end, c); - - while (--width > 0) - str = xputchar(str, end, ' '); - - break; - case SPECIFIER_STR: - s = va_arg(ap, char *); - - if (s == NULL) - s = "(null)"; - - len = 0; - - for (len = 0; s[len] != '\0'; len++) - if (len == precision) - break; - - if (!(flags & FORMAT_LEFT_JUSTIFY)) - while (len < width--) - str = xputchar(str, end, ' '); - - for (i = 0; i < len; i++) { - str = xputchar(str, end, *s); - s++; - } - - while (len < width--) - str = xputchar(str, end, ' '); - - break; - case SPECIFIER_NRCHARS: - if (modifier == MODIFIER_CHAR) { - signed char *ptr = va_arg(ap, signed char *); - *ptr = str - start; - } else if (modifier == MODIFIER_SHORT) { - short *ptr = va_arg(ap, short *); - *ptr = str - start; - } else if (modifier == MODIFIER_LONG) { - long *ptr = va_arg(ap, long *); - *ptr = str - start; - } else if (modifier == MODIFIER_LONGLONG) { - long long *ptr = va_arg(ap, long long *); - *ptr = str - start; - } else if (modifier == MODIFIER_SIZE) { - ssize_t *ptr = va_arg(ap, ssize_t *); - *ptr = str - start; - } else if (modifier == MODIFIER_PTRDIFF) { - ptrdiff_t *ptr = va_arg(ap, ptrdiff_t *); - *ptr = str - start; - } else { - int *ptr = va_arg(ap, int *); - *ptr = str - start; - } - - break; - case SPECIFIER_PERCENT: - case SPECIFIER_INVALID: - str = xputchar(str, end, '%'); - break; - default: - break; - } - - if (specifier != SPECIFIER_INVALID) - format++; - } - - if (str < end) - *str = '\0'; - else if (end != NULL) - *end = '\0'; - - return str - start; -} diff --git a/xprintf.h b/xprintf.h deleted file mode 100644 index 7634a9f..0000000 --- a/xprintf.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * 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 ``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 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. - * - * - * Formatted output functions. - * - * The functions provided by this module implement a subset of the C99 - * printf() like functions, mostly centered around character, string, and - * integer conversions. - * - * The supported specifiers are: d i o u x X c s p n % - * The supported length modifiers are: hh h l ll z t - * - * The xprintf() and xvprintf() functions internally use a statically - * allocated buffer. Although they are thread-safe, they won't produce - * output larger than 1 KiB. - */ - -#ifndef _XPRINTF_H -#define _XPRINTF_H - -#include <stdarg.h> - -#include "macros.h" - -int xprintf(const char *format, ...) __format_printf(1, 2); -int xvprintf(const char *format, va_list ap) __format_printf(1, 0); - -int xsprintf(char *str, const char *format, ...) __format_printf(2, 3); -int xvsprintf(char *str, const char *format, va_list ap) __format_printf(2, 0); - -int xsnprintf(char *str, size_t size, const char *format, ...) - __format_printf(3, 4); -int xvsnprintf(char *str, size_t size, const char *format, va_list ap) - __format_printf(3, 0); - -#endif /* _XPRINTF_H */ |