├── VERSION ├── .gitignore ├── sm_calloc.c ├── sm_realloc.c ├── sm_zalloc.c ├── sm_realloc_move.c ├── sm_alloc_valid.c ├── sm_szalloc.c ├── sm_free.c ├── sm_hash.c ├── Makefile ├── COPYRIGHT ├── sm_malloc_stats.c ├── smalloc_i.h ├── sm_util.c ├── sm_pool.c ├── sm_malloc.c ├── smalloc.h ├── sm_realloc_i.c ├── smalloc_test_so.c ├── README ├── smalloc.3 └── smalloc.3.txt /VERSION: -------------------------------------------------------------------------------- 1 | 43 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _* 2 | *.swp 3 | *.o 4 | *.a 5 | *.so 6 | *.patch 7 | *.diff 8 | tags 9 | -------------------------------------------------------------------------------- /sm_calloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | void *sm_calloc_pool(struct smalloc_pool *spool, size_t x, size_t y) 10 | { 11 | return sm_zalloc_pool(spool, x * y); 12 | } 13 | 14 | void *sm_calloc(size_t x, size_t y) 15 | { 16 | return sm_calloc_pool(&smalloc_curr_pool, x, y); 17 | } 18 | -------------------------------------------------------------------------------- /sm_realloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | void *sm_realloc_pool(struct smalloc_pool *spool, void *p, size_t n) 10 | { 11 | return sm_realloc_pool_i(spool, p, n, 0); 12 | } 13 | 14 | void *sm_realloc(void *p, size_t n) 15 | { 16 | return sm_realloc_pool_i(&smalloc_curr_pool, p, n, 0); 17 | } 18 | -------------------------------------------------------------------------------- /sm_zalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | void *sm_zalloc_pool(struct smalloc_pool *spool, size_t n) 10 | { 11 | void *r = sm_malloc_pool(spool, n); 12 | if (r) memset(r, 0, n); 13 | return r; 14 | } 15 | 16 | void *sm_zalloc(size_t n) 17 | { 18 | return sm_zalloc_pool(&smalloc_curr_pool, n); 19 | } 20 | -------------------------------------------------------------------------------- /sm_realloc_move.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | void *sm_realloc_move_pool(struct smalloc_pool *spool, void *p, size_t n) 10 | { 11 | return sm_realloc_pool_i(spool, p, n, 1); 12 | } 13 | 14 | void *sm_realloc_move(void *p, size_t n) 15 | { 16 | return sm_realloc_pool_i(&smalloc_curr_pool, p, n, 1); 17 | } 18 | -------------------------------------------------------------------------------- /sm_alloc_valid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | int sm_alloc_valid_pool(struct smalloc_pool *spool, const void *p) 10 | { 11 | struct smalloc_hdr *shdr; 12 | 13 | if (!smalloc_verify_pool(spool)) { 14 | errno = EINVAL; 15 | return 0; 16 | } 17 | 18 | if (!p) return 0; 19 | 20 | shdr = USER_TO_HEADER(p); 21 | if (smalloc_is_alloc(spool, shdr)) return 1; 22 | return 0; 23 | } 24 | 25 | int sm_alloc_valid(const void *p) 26 | { 27 | return sm_alloc_valid_pool(&smalloc_curr_pool, p); 28 | } 29 | -------------------------------------------------------------------------------- /sm_szalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | size_t sm_szalloc_pool(struct smalloc_pool *spool, const void *p) 10 | { 11 | struct smalloc_hdr *shdr; 12 | 13 | if (!smalloc_verify_pool(spool)) { 14 | errno = EINVAL; 15 | return ((size_t)-1); 16 | } 17 | 18 | if (!p) return 0; 19 | 20 | shdr = USER_TO_HEADER(p); 21 | if (smalloc_is_alloc(spool, shdr)) return shdr->usz; 22 | smalloc_UB(spool, p); 23 | return 0; 24 | } 25 | 26 | size_t sm_szalloc(const void *p) 27 | { 28 | return sm_szalloc_pool(&smalloc_curr_pool, p); 29 | } 30 | -------------------------------------------------------------------------------- /sm_free.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | void sm_free_pool(struct smalloc_pool *spool, void *p) 10 | { 11 | struct smalloc_hdr *shdr; 12 | char *s; 13 | 14 | if (!smalloc_verify_pool(spool)) { 15 | errno = EINVAL; 16 | return; 17 | } 18 | 19 | if (!p) return; 20 | 21 | shdr = USER_TO_HEADER(p); 22 | if (smalloc_is_alloc(spool, shdr)) { 23 | if (spool->do_zero) memset(p, 0, shdr->rsz); 24 | s = CHAR_PTR(p); 25 | s += shdr->usz; 26 | memset(s, 0, HEADER_SZ); 27 | if (spool->do_zero) memset(s+HEADER_SZ, 0, shdr->rsz - shdr->usz); 28 | memset(shdr, 0, HEADER_SZ); 29 | return; 30 | } 31 | 32 | smalloc_UB(spool, p); 33 | return; 34 | } 35 | 36 | void sm_free(void *p) 37 | { 38 | sm_free_pool(&smalloc_curr_pool, p); 39 | } 40 | -------------------------------------------------------------------------------- /sm_hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | /* An adopted Jenkins one-at-a-time hash */ 10 | #define UIHOP(x, s) do { \ 11 | hash += (x >> s) & 0xff;\ 12 | hash += hash << 10; \ 13 | hash ^= hash >> 6; \ 14 | } while (0) 15 | uintptr_t smalloc_uinthash(uintptr_t x) 16 | { 17 | uintptr_t hash = 0; 18 | 19 | UIHOP(x, 0); 20 | UIHOP(x, 8); 21 | UIHOP(x, 16); 22 | UIHOP(x, 24); 23 | 24 | hash += hash << 3; 25 | hash ^= hash >> 11; 26 | hash += hash << 15; 27 | 28 | return hash; 29 | } 30 | #undef UIHOP 31 | 32 | uintptr_t smalloc_mktag(struct smalloc_hdr *shdr) 33 | { 34 | uintptr_t r = smalloc_uinthash(PTR_UINT(shdr)); 35 | r += shdr->rsz; 36 | r = smalloc_uinthash(r); 37 | r += shdr->usz; 38 | r = smalloc_uinthash(r); 39 | return r; 40 | } 41 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This file is a part of SMalloc. 2 | # SMalloc is MIT licensed. 3 | # Copyright (c) 2017 Andrey Rys. 4 | 5 | SRCS = $(wildcard *.c) 6 | LIB_OBJS = $(filter-out smalloc_test_so.o, $(SRCS:.c=.o)) 7 | TEST_OBJS = smalloc_test_so.o 8 | override CFLAGS += -Wall -fPIC 9 | 10 | ifneq (,$(DEBUG)) 11 | override CFLAGS+=-O0 -g 12 | else 13 | override CFLAGS+=-O2 14 | endif 15 | 16 | all: $(LIB_OBJS) libsmalloc.a 17 | 18 | %.o: %.c 19 | $(CROSS_COMPILE)$(CC) $(CFLAGS) -I. -c -o $@ $< 20 | 21 | libsmalloc.a: $(LIB_OBJS) 22 | $(CROSS_COMPILE)$(AR) cru $@ *.o 23 | 24 | smalloc_test_so.so: $(TEST_OBJS) 25 | @$(MAKE) libsmalloc.a 26 | $(CROSS_COMPILE)$(CC) $(CFLAGS) $< -shared -o $@ libsmalloc.a 27 | @echo Now you can test it with LD_PRELOAD=./$@ and see it works for conformant apps. 28 | 29 | docs: smalloc.3 30 | mandoc -Tascii smalloc.3 >smalloc.3.txt 31 | 32 | clean: 33 | rm -f *.a *.so *.o 34 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright © 2017 Andrey Rys. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /sm_malloc_stats.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | int sm_malloc_stats_pool(struct smalloc_pool *spool, size_t *total, size_t *user, size_t *free, int *nr_blocks) 10 | { 11 | struct smalloc_hdr *shdr, *basehdr; 12 | int r = 0; 13 | 14 | if (!smalloc_verify_pool(spool)) { 15 | errno = EINVAL; 16 | return -1; 17 | } 18 | 19 | if (!total && !user && !free && !nr_blocks) return 0; 20 | 21 | if (total) *total = 0; 22 | if (user) *user = 0; 23 | if (free) *free = 0; 24 | if (nr_blocks) *nr_blocks = 0; 25 | 26 | shdr = basehdr = (struct smalloc_hdr *)spool->pool; 27 | while (CHAR_PTR(shdr)-CHAR_PTR(basehdr) < spool->pool_size) { 28 | if (smalloc_is_alloc(spool, shdr)) { 29 | if (total) *total += HEADER_SZ + shdr->rsz + HEADER_SZ; 30 | if (user) *user += shdr->usz; 31 | if (nr_blocks) *nr_blocks += 1; 32 | r = 1; 33 | } 34 | 35 | shdr++; 36 | } 37 | 38 | *free = spool->pool_size - *total; 39 | 40 | return r; 41 | } 42 | 43 | int sm_malloc_stats(size_t *total, size_t *user, size_t *free, int *nr_blocks) 44 | { 45 | return sm_malloc_stats_pool(&smalloc_curr_pool, total, user, free, nr_blocks); 46 | } 47 | -------------------------------------------------------------------------------- /smalloc_i.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #ifndef _SMALLOC_I_H 8 | #define _SMALLOC_I_H 9 | 10 | #include "smalloc.h" 11 | #include 12 | #include 13 | #include 14 | 15 | struct smalloc_hdr { 16 | size_t rsz; /* real allocated size with overhead (if any) */ 17 | size_t usz; /* exact user size as reported by s_szalloc */ 18 | uintptr_t tag; /* sum of all the above, hashed value */ 19 | }; 20 | 21 | #define HEADER_SZ (sizeof(struct smalloc_hdr)) 22 | #define MIN_POOL_SZ (HEADER_SZ*20) 23 | 24 | #define VOID_PTR(p) ((void *)p) 25 | #define CHAR_PTR(p) ((char *)p) 26 | #define PTR_UINT(p) ((uintptr_t)VOID_PTR(p)) 27 | #define HEADER_PTR(p) ((struct smalloc_hdr *)p) 28 | #define USER_TO_HEADER(p) (HEADER_PTR((CHAR_PTR(p)-HEADER_SZ))) 29 | #define HEADER_TO_USER(p) (VOID_PTR((CHAR_PTR(p)+HEADER_SZ))) 30 | 31 | extern smalloc_ub_handler smalloc_UB; 32 | 33 | uintptr_t smalloc_uinthash(uintptr_t x); 34 | uintptr_t smalloc_mktag(struct smalloc_hdr *shdr); 35 | int smalloc_verify_pool(struct smalloc_pool *spool); 36 | int smalloc_is_alloc(struct smalloc_pool *spool, struct smalloc_hdr *shdr); 37 | 38 | void *sm_realloc_pool_i(struct smalloc_pool *spool, void *p, size_t n, int nomove); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /sm_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | static int smalloc_check_bounds(struct smalloc_pool *spool, struct smalloc_hdr *shdr) 10 | { 11 | if (!spool) return 0; 12 | if (CHAR_PTR(shdr) >= CHAR_PTR(spool->pool) 13 | && CHAR_PTR(shdr) <= (CHAR_PTR(spool->pool)+spool->pool_size)) 14 | return 1; 15 | return 0; 16 | } 17 | 18 | static int smalloc_valid_tag(struct smalloc_hdr *shdr) 19 | { 20 | char *s; 21 | uintptr_t r = smalloc_mktag(shdr); 22 | size_t x; 23 | 24 | if (shdr->tag == r) { 25 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 26 | s += shdr->usz; 27 | for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) { 28 | r = smalloc_uinthash(r); 29 | if (memcmp(s+x, &r, sizeof(uintptr_t)) != 0) return 0; 30 | } 31 | s += x; x = 0; 32 | while (x < shdr->rsz - shdr->usz) { 33 | if (s[x] != '\xFF') return 0; 34 | x++; 35 | } 36 | return 1; 37 | } 38 | return 0; 39 | } 40 | 41 | static void smalloc_do_crash(struct smalloc_pool *spool, const void *p) 42 | { 43 | char *c = NULL; 44 | *c = 'X'; 45 | } 46 | 47 | smalloc_ub_handler smalloc_UB = smalloc_do_crash; 48 | 49 | void sm_set_ub_handler(smalloc_ub_handler handler) 50 | { 51 | if (!handler) smalloc_UB = smalloc_do_crash; 52 | else smalloc_UB = handler; 53 | } 54 | 55 | int smalloc_is_alloc(struct smalloc_pool *spool, struct smalloc_hdr *shdr) 56 | { 57 | if (!smalloc_check_bounds(spool, shdr)) return 0; 58 | if (shdr->rsz == 0) return 0; 59 | if (shdr->rsz > SIZE_MAX) return 0; 60 | if (shdr->usz > SIZE_MAX) return 0; 61 | if (shdr->usz > shdr->rsz) return 0; 62 | if (shdr->rsz % HEADER_SZ) return 0; 63 | if (!smalloc_valid_tag(shdr)) return 0; 64 | return 1; 65 | } 66 | -------------------------------------------------------------------------------- /sm_pool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | struct smalloc_pool smalloc_curr_pool; 10 | 11 | int smalloc_verify_pool(struct smalloc_pool *spool) 12 | { 13 | if (!spool->pool || !spool->pool_size) return 0; 14 | if (spool->pool_size % HEADER_SZ) return 0; 15 | return 1; 16 | } 17 | 18 | int sm_align_pool(struct smalloc_pool *spool) 19 | { 20 | size_t x; 21 | 22 | if (smalloc_verify_pool(spool)) return 1; 23 | 24 | x = spool->pool_size % HEADER_SZ; 25 | if (x) spool->pool_size -= x; 26 | if (spool->pool_size <= MIN_POOL_SZ) { 27 | errno = ENOSPC; 28 | return 0; 29 | } 30 | 31 | return 1; 32 | } 33 | 34 | int sm_set_pool(struct smalloc_pool *spool, void *new_pool, size_t new_pool_size, int do_zero, smalloc_oom_handler oom_handler) 35 | { 36 | if (!spool) { 37 | errno = EINVAL; 38 | return 0; 39 | } 40 | 41 | if (!new_pool || !new_pool_size) { 42 | if (smalloc_verify_pool(spool)) { 43 | if (spool->do_zero) memset(spool->pool, 0, spool->pool_size); 44 | memset(spool, 0, sizeof(struct smalloc_pool)); 45 | return 1; 46 | } 47 | 48 | errno = EINVAL; 49 | return 0; 50 | } 51 | 52 | spool->pool = new_pool; 53 | spool->pool_size = new_pool_size; 54 | spool->oomfn = oom_handler; 55 | if (!sm_align_pool(spool)) return 0; 56 | 57 | if (do_zero) { 58 | spool->do_zero = do_zero; 59 | memset(spool->pool, 0, spool->pool_size); 60 | } 61 | 62 | return 1; 63 | } 64 | 65 | int sm_set_default_pool(void *new_pool, size_t new_pool_size, int do_zero, smalloc_oom_handler oom_handler) 66 | { 67 | return sm_set_pool(&smalloc_curr_pool, new_pool, new_pool_size, do_zero, oom_handler); 68 | } 69 | 70 | int sm_release_pool(struct smalloc_pool *spool) 71 | { 72 | return sm_set_pool(spool, NULL, 0, 0, NULL); 73 | } 74 | 75 | int sm_release_default_pool(void) 76 | { 77 | return sm_release_pool(&smalloc_curr_pool); 78 | } 79 | -------------------------------------------------------------------------------- /sm_malloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | void *sm_malloc_pool(struct smalloc_pool *spool, size_t n) 10 | { 11 | struct smalloc_hdr *basehdr, *shdr, *dhdr; 12 | char *s; 13 | int found; 14 | size_t x; 15 | 16 | again: if (!smalloc_verify_pool(spool)) { 17 | errno = EINVAL; 18 | return NULL; 19 | } 20 | 21 | if (n == 0) n++; /* return a block successfully */ 22 | if (n > SIZE_MAX 23 | || n > (spool->pool_size - HEADER_SZ)) goto oom; 24 | 25 | shdr = basehdr = (struct smalloc_hdr *)spool->pool; 26 | while (CHAR_PTR(shdr)-CHAR_PTR(basehdr) < spool->pool_size) { 27 | /* 28 | * Already allocated block. 29 | * Skip it by jumping over it. 30 | */ 31 | if (smalloc_is_alloc(spool, shdr)) { 32 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 33 | s += shdr->rsz + HEADER_SZ; 34 | shdr = HEADER_PTR(s); 35 | continue; 36 | } 37 | /* 38 | * Free blocks ahead! 39 | * Do a second search over them to find out if they're 40 | * really large enough to fit the new allocation. 41 | */ 42 | else { 43 | dhdr = shdr; found = 0; 44 | while (CHAR_PTR(dhdr)-CHAR_PTR(basehdr) < spool->pool_size) { 45 | /* pre calculate free block size */ 46 | x = CHAR_PTR(dhdr)-CHAR_PTR(shdr); 47 | /* 48 | * ugh, found next allocated block. 49 | * skip this candidate then. 50 | */ 51 | if (smalloc_is_alloc(spool, dhdr)) 52 | goto allocblock; 53 | /* 54 | * did not see allocated block yet, 55 | * but this free block is of enough size 56 | * - finally, use it. 57 | */ 58 | if (n + HEADER_SZ <= x) { 59 | x -= HEADER_SZ; 60 | found = 1; 61 | goto outfound; 62 | } 63 | dhdr++; 64 | } 65 | 66 | outfound: if (found) { 67 | uintptr_t tag; 68 | /* allocate and return this block */ 69 | shdr->rsz = x; 70 | shdr->usz = n; 71 | shdr->tag = tag = smalloc_mktag(shdr); 72 | if (spool->do_zero) memset(HEADER_TO_USER(shdr), 0, shdr->rsz); 73 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 74 | s += shdr->usz; 75 | for (x = 0; 76 | x < sizeof(struct smalloc_hdr); 77 | x += sizeof(uintptr_t)) { 78 | tag = smalloc_uinthash(tag); 79 | memcpy(s+x, &tag, sizeof(uintptr_t)); 80 | } 81 | memset(s+x, 0xff, shdr->rsz - shdr->usz); 82 | return HEADER_TO_USER(shdr); 83 | } 84 | 85 | /* continue first search for next free block */ 86 | allocblock: shdr = dhdr; 87 | continue; 88 | } 89 | 90 | shdr++; 91 | } 92 | 93 | oom: if (spool->oomfn) { 94 | x = spool->oomfn(spool, n); 95 | if (x > spool->pool_size) { 96 | spool->pool_size = x; 97 | if (sm_align_pool(spool)) goto again; 98 | } 99 | } 100 | 101 | errno = ENOMEM; 102 | return NULL; 103 | } 104 | 105 | void *sm_malloc(size_t n) 106 | { 107 | return sm_malloc_pool(&smalloc_curr_pool, n); 108 | } 109 | -------------------------------------------------------------------------------- /smalloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SMalloc -- a *static* memory allocator. 3 | * 4 | * See README for a complete description. 5 | * 6 | * SMalloc is MIT licensed. 7 | * Copyright (c) 2017 Andrey Rys. 8 | * Written during Aug2017. 9 | */ 10 | 11 | #ifndef _SMALLOC_H 12 | #define _SMALLOC_H 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #include 19 | #include 20 | 21 | struct smalloc_pool; 22 | 23 | typedef size_t (*smalloc_oom_handler)(struct smalloc_pool *, size_t); 24 | 25 | /* describes static pool, if you're going to use multiple pools at same time */ 26 | struct smalloc_pool { 27 | void *pool; /* pointer to your pool */ 28 | size_t pool_size; /* it's size. Must be aligned with sm_align_pool. */ 29 | int do_zero; /* zero pool before use and all the new allocations from it. */ 30 | smalloc_oom_handler oomfn; /* this will be called, if non-NULL, on OOM condition in pool */ 31 | }; 32 | 33 | /* a default one which is initialised with sm_set_default_pool. */ 34 | extern struct smalloc_pool smalloc_curr_pool; 35 | 36 | /* undefined behavior handler is called on typical malloc UB situations */ 37 | typedef void (*smalloc_ub_handler)(struct smalloc_pool *, const void *); 38 | 39 | void sm_set_ub_handler(smalloc_ub_handler); 40 | 41 | int sm_align_pool(struct smalloc_pool *); 42 | int sm_set_pool(struct smalloc_pool *, void *, size_t, int, smalloc_oom_handler); 43 | int sm_set_default_pool(void *, size_t, int, smalloc_oom_handler); 44 | int sm_release_pool(struct smalloc_pool *); 45 | int sm_release_default_pool(void); 46 | 47 | /* Use these with multiple pools which you control */ 48 | 49 | void *sm_malloc_pool(struct smalloc_pool *, size_t); 50 | void *sm_zalloc_pool(struct smalloc_pool *, size_t); 51 | void sm_free_pool(struct smalloc_pool *, void *); 52 | 53 | void *sm_realloc_pool(struct smalloc_pool *, void *, size_t); 54 | void *sm_realloc_move_pool(struct smalloc_pool *, void *, size_t); 55 | void *sm_calloc_pool(struct smalloc_pool *, size_t, size_t); 56 | 57 | int sm_alloc_valid_pool(struct smalloc_pool *spool, const void *p); 58 | 59 | size_t sm_szalloc_pool(struct smalloc_pool *, const void *); 60 | int sm_malloc_stats_pool(struct smalloc_pool *, size_t *, size_t *, size_t *, int *); 61 | 62 | /* Use these when you use just default smalloc_curr_pool pool */ 63 | 64 | void *sm_malloc(size_t); 65 | void *sm_zalloc(size_t); /* guarantee zero memory allocation */ 66 | void sm_free(void *); 67 | 68 | void *sm_realloc(void *, size_t); 69 | void *sm_realloc_move(void *, size_t); 70 | void *sm_calloc(size_t, size_t); /* calls zalloc internally */ 71 | 72 | int sm_alloc_valid(const void *p); /* verify pointer without intentional crash */ 73 | 74 | size_t sm_szalloc(const void *); /* get size of allocation */ 75 | /* 76 | * get stats: total used, user used, total free, nr. of allocated blocks. 77 | * any of pointers maybe set to NULL, but at least one must be non NULL. 78 | */ 79 | int sm_malloc_stats(size_t *, size_t *, size_t *, int *); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /sm_realloc_i.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | */ 6 | 7 | #include "smalloc_i.h" 8 | 9 | /* 10 | * Please do NOT use this function directly or rely on it's presence. 11 | * It may go away in future SMalloc versions, or it's calling 12 | * signature may change. It is internal function, hence "_i" suffix. 13 | */ 14 | void *sm_realloc_pool_i(struct smalloc_pool *spool, void *p, size_t n, int nomove) 15 | { 16 | struct smalloc_hdr *basehdr, *shdr, *dhdr; 17 | void *r; 18 | char *s; 19 | int found; 20 | size_t rsz, usz, x; 21 | uintptr_t tag; 22 | 23 | if (!smalloc_verify_pool(spool)) { 24 | errno = EINVAL; 25 | return NULL; 26 | } 27 | 28 | if (!p) return sm_malloc_pool(spool, n); 29 | if (!n && p) { 30 | sm_free_pool(spool, p); 31 | return NULL; 32 | } 33 | 34 | /* determine user size */ 35 | shdr = USER_TO_HEADER(p); 36 | if (!smalloc_is_alloc(spool, shdr)) smalloc_UB(spool, p); 37 | usz = shdr->usz; 38 | rsz = shdr->rsz; 39 | 40 | /* newsize is lesser than allocated - truncate */ 41 | if (n <= usz) { 42 | if (spool->do_zero) memset((char *)p + n, 0, shdr->rsz - n); 43 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 44 | s += usz; 45 | memset(s, 0, HEADER_SZ); 46 | if (spool->do_zero) memset(s+HEADER_SZ, 0, rsz - usz); 47 | shdr->rsz = (n%HEADER_SZ)?(((n/HEADER_SZ)+1)*HEADER_SZ):n; 48 | shdr->usz = n; 49 | shdr->tag = tag = smalloc_mktag(shdr); 50 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 51 | s += shdr->usz; 52 | for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) { 53 | tag = smalloc_uinthash(tag); 54 | memcpy(s+x, &tag, sizeof(uintptr_t)); 55 | } 56 | memset(s+x, 0xff, shdr->rsz - shdr->usz); 57 | return p; 58 | } 59 | 60 | /* newsize is bigger than allocated, but there is free room - modify */ 61 | if (n > usz && n <= rsz) { 62 | if (spool->do_zero) { 63 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 64 | s += usz; 65 | memset(s, 0, HEADER_SZ); 66 | } 67 | shdr->usz = n; 68 | shdr->tag = tag = smalloc_mktag(shdr); 69 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 70 | s += shdr->usz; 71 | for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) { 72 | tag = smalloc_uinthash(tag); 73 | memcpy(s+x, &tag, sizeof(uintptr_t)); 74 | } 75 | memset(s+x, 0xff, shdr->rsz - shdr->usz); 76 | return p; 77 | } 78 | 79 | /* newsize is bigger, larger than rsz but there are free blocks beyond - extend */ 80 | basehdr = (struct smalloc_hdr *)spool->pool; dhdr = shdr+(rsz/HEADER_SZ); found = 0; 81 | while (CHAR_PTR(dhdr)-CHAR_PTR(basehdr) < spool->pool_size) { 82 | x = CHAR_PTR(dhdr)-CHAR_PTR(shdr); 83 | if (smalloc_is_alloc(spool, dhdr)) 84 | goto allocblock; 85 | if (n + HEADER_SZ <= x) { 86 | x -= HEADER_SZ; 87 | found = 1; 88 | goto outfound; 89 | } 90 | dhdr++; 91 | } 92 | 93 | outfound: 94 | /* write new numbers of same allocation */ 95 | if (found) { 96 | if (spool->do_zero) { 97 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 98 | s += usz; 99 | memset(s, 0, HEADER_SZ); 100 | memset(s+HEADER_SZ, 0, rsz - usz); 101 | } 102 | shdr->rsz = x; 103 | shdr->usz = n; 104 | shdr->tag = tag = smalloc_mktag(shdr); 105 | s = CHAR_PTR(HEADER_TO_USER(shdr)); 106 | s += shdr->usz; 107 | for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) { 108 | tag = smalloc_uinthash(tag); 109 | memcpy(s+x, &tag, sizeof(uintptr_t)); 110 | } 111 | memset(s+x, 0xff, shdr->rsz - shdr->usz); 112 | return p; 113 | } 114 | 115 | allocblock: 116 | /* newsize is bigger than allocated and no free space - move */ 117 | if (nomove) { 118 | /* fail if user asked */ 119 | errno = ERANGE; 120 | return NULL; 121 | } 122 | r = sm_malloc_pool(spool, n); 123 | if (!r) return NULL; 124 | memcpy(r, p, usz); 125 | sm_free_pool(spool, p); 126 | 127 | return r; 128 | } 129 | -------------------------------------------------------------------------------- /smalloc_test_so.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of SMalloc. 3 | * SMalloc is MIT licensed. 4 | * Copyright (c) 2017 Andrey Rys. 5 | * 6 | * This is an example program which is actually 7 | * a DSO overriding std. malloc functions. 8 | * It shows how to allocate new memory for a 9 | * task with mmap and use it as a dynamically 10 | * extendable heap space, which is placed at 11 | * almost random base address at each startup. 12 | * 13 | * On each OOM situation new page(s) are requested. 14 | * These pages are never released during runtime but 15 | * only at program exit, and the whole space is used 16 | * as a "scratch space". 17 | * 18 | * This is a bright example of how SMalloc can be 19 | * used as a generic system memory allocator. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "smalloc.h" 35 | 36 | #ifndef PAGE_SIZE 37 | #define PAGE_SIZE 4096 38 | #endif 39 | 40 | static long sc_page_size; 41 | 42 | /* base pointer and size of allocated pool */ 43 | static char *xpool; 44 | static size_t xpool_n; 45 | 46 | static int smalloc_initialised; 47 | 48 | /* atexit call: wipe all the data out of pool */ 49 | static void exit_smalloc(void) 50 | { 51 | if (smalloc_initialised) { 52 | sm_release_default_pool(); 53 | memset(xpool, 0, xpool_n); 54 | munmap(xpool, xpool_n); 55 | smalloc_initialised = 0; 56 | } 57 | } 58 | 59 | static void xerror(int x, const char *fmt, ...) 60 | { 61 | va_list ap; 62 | 63 | va_start(ap, fmt); 64 | vfprintf(stderr, fmt, ap); 65 | if (errno) fprintf(stderr, " (%s)\n", strerror(errno)); 66 | else fprintf(stderr, "\n"); 67 | va_end(ap); 68 | 69 | exit_smalloc(); 70 | _exit(x); 71 | } 72 | 73 | static void xgetrandom(void *buf, size_t size) 74 | { 75 | char *ubuf = buf; 76 | int fd = -1; 77 | size_t rd; 78 | int x; 79 | 80 | /* Most common and probably available on every Nix, */ 81 | fd = open("/dev/urandom", O_RDONLY); 82 | /* OpenBSD arc4 */ 83 | if (fd == -1) fd = open("/dev/arandom", O_RDONLY); 84 | /* OpenBSD simple urandom */ 85 | if (fd == -1) fd = open("/dev/prandom", O_RDONLY); 86 | /* OpenBSD srandom, blocking! */ 87 | if (fd == -1) fd = open("/dev/srandom", O_RDONLY); 88 | /* Most common blocking. */ 89 | if (fd == -1) fd = open("/dev/random", O_RDONLY); 90 | /* Very bad, is this a crippled chroot? */ 91 | if (fd == -1) xerror(2, "urandom is required"); 92 | 93 | x = 0; 94 | _again: rd = read(fd, ubuf, size); 95 | /* I want full random block, and there is no EOF can be! */ 96 | if (rd < size) { 97 | if (x >= 100) xerror(2, "urandom always returns less bytes! (rd = %zu)", rd); 98 | x++; 99 | ubuf += rd; 100 | size -= rd; 101 | goto _again; 102 | } 103 | 104 | close(fd); 105 | } 106 | 107 | static void *getrndbase(void) 108 | { 109 | uintptr_t r; 110 | xgetrandom(&r, sizeof(uintptr_t)); 111 | r &= ~(sc_page_size-1); 112 | #if UINTPTR_MAX == UINT64_MAX 113 | r &= 0xffffffffff; 114 | #endif 115 | return (void *)r; 116 | } 117 | 118 | static void xpool_ub(struct smalloc_pool *spool, const void *offender) 119 | { 120 | errno = 0; 121 | xerror(5, "%p: address is not from %p-%p range!", offender, xpool, xpool+spool->pool_size); 122 | } 123 | 124 | /* called each time we ran out of memory, in hope to get more */ 125 | static size_t xpool_oom(struct smalloc_pool *spool, size_t n) 126 | { 127 | void *t; 128 | size_t newsz; 129 | 130 | newsz = (n / sc_page_size) * sc_page_size; 131 | if (n % sc_page_size) newsz += sc_page_size; 132 | if (newsz == 0) newsz += sc_page_size; 133 | 134 | /* get new page(s) */ 135 | t = mmap(xpool+xpool_n, newsz, 136 | PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 137 | /* failed to get? */ 138 | if (t == MAP_FAILED) return 0; 139 | /* !MAP_FIXED and there are existing pages on the road already? */ 140 | if (t != xpool+xpool_n) { 141 | munmap(t, newsz); 142 | return 0; 143 | } 144 | 145 | /* success! Return new pool size */ 146 | xpool_n += newsz; 147 | return xpool_n; 148 | } 149 | 150 | /* single time init call: setup default pool descriptor */ 151 | static void init_smalloc(void) 152 | { 153 | if (!smalloc_initialised) { 154 | void *p; 155 | sc_page_size = sysconf(_SC_PAGE_SIZE); 156 | if (sc_page_size == 0) sc_page_size = PAGE_SIZE; 157 | sm_set_ub_handler(xpool_ub); 158 | _again: p = getrndbase(); /* get random base pointer */ 159 | /* allocate initial base page */ 160 | xpool = mmap(p, sc_page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 161 | if (xpool == MAP_FAILED 162 | || xpool != p) { 163 | /* try again several times */ 164 | if (xpool != p && xpool != MAP_FAILED) munmap(p, sc_page_size); 165 | smalloc_initialised++; 166 | if (smalloc_initialised > 10) xerror(3, "failed to map page at base = %p", p); 167 | goto _again; 168 | } 169 | /* initial pool size == PAGE_SIZE */ 170 | xpool_n = sc_page_size; 171 | 172 | /* setup SMalloc to use this pool */ 173 | if (!sm_set_default_pool(xpool, xpool_n, 0, xpool_oom)) 174 | xerror(4, "sm_set_default_pool failed!"); 175 | 176 | /* register atexit cleanup call */ 177 | atexit(exit_smalloc); 178 | 179 | /* well done! */ 180 | smalloc_initialised = 1; 181 | } 182 | } 183 | 184 | /* our replacement memory management functions */ 185 | 186 | void *malloc(size_t n) 187 | { 188 | if (!smalloc_initialised) init_smalloc(); 189 | /* return sm_zalloc(n); */ 190 | return sm_malloc(n); 191 | } 192 | 193 | void free(void *p) 194 | { 195 | if (!smalloc_initialised) init_smalloc(); 196 | sm_free(p); 197 | } 198 | 199 | void *realloc(void *p, size_t n) 200 | { 201 | if (!smalloc_initialised) init_smalloc(); 202 | return sm_realloc(p, n); 203 | } 204 | 205 | void *calloc(size_t y, size_t x) 206 | { 207 | if (!smalloc_initialised) init_smalloc(); 208 | return sm_calloc(y, x); 209 | } 210 | 211 | char *strdup(const char *s) 212 | { 213 | size_t n = strlen(s); 214 | char *r = sm_zalloc(n+1); 215 | if (!r) return NULL; 216 | memcpy(r, s, n); 217 | return r; 218 | } 219 | 220 | char *strndup(const char *s, size_t n) 221 | { 222 | size_t x = strnlen(s, n); 223 | char *r = sm_zalloc(x+1); 224 | if (!r) return NULL; 225 | memcpy(r, s, x); 226 | return r; 227 | } 228 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | WARNING: THIS CODE IS NOT SUPPORTED ANYMORE. 2 | If you considering using it, be aware that the code is not going to be supported. 3 | 4 | This code will be left as is. No changes or further 5 | development are planned. I planned some features to 6 | be implemented in past, but this code itself is not 7 | optimized for performance, so I abandoned it. 8 | 9 | I can recommend you to try out mimalloc instead, which 10 | does exactly what you want: allocation from multiple 11 | custom heaps, automatic zeroing, security features, highly 12 | tunable for specific task. It's not embedded-friendly tho. 13 | So if your code must run inside MCU, it's not what you want. 14 | 15 | SMalloc -- a *static* memory allocator. 16 | 17 | SMalloc allows you to use an arbitrary memory array, which is allocated 18 | inside your program as, say 19 | 20 | static char my_memory[10240]; /* 10kb memory block */ 21 | 22 | , dynamically - that is, allocate objects of fixed length from it. 23 | 24 | Thus, it's like you usually do: 25 | 26 | ptr = malloc(strlen(str)+1); 27 | if (!ptr) { ... error handling ... } 28 | ... do something with ptr ... 29 | free(ptr); 30 | 31 | , but space for "ptr" will be allocated from _your_ my_memory[]. 32 | 33 | SMalloc has more useful features rather than usual memory allocators available: 34 | 35 | - Obviously, you can erase your static memory block at any time, for example, 36 | when you will need to wipe out some sensitive data out just before program termination, 37 | - SMalloc allows you to use pools of any types: static storage, 38 | -or- obtained objects from host malloc / mmap / brk etc. 39 | - SMalloc allows you to obtain clean zeroed objects, 40 | - SMalloc allows you to request an exact size of (valid) memory block you obtained, 41 | - SMalloc allows you to manage _multiple! memory arrays_ at same time, 42 | - SMalloc allows you to check pointer validity before use, 43 | - SMalloc tries to reuse the pool memory efficiently, because pool size is always fixed, 44 | - SMalloc can recurse into itself, thus you can allocate a pool from existing pool and use 45 | it separately, just fill the pool structure, align it and pass it to *_pool() calls then, 46 | - SMalloc will crash your program on unwanted memory behavior just like any other sane 47 | heap memory allocator, but it permits you to set your own crash handler, and you can 48 | report more about bad memory event (with pointers to current pool and offending pointer), 49 | or even completely avoid the crash or wipe out memory block / whatever you wish! 50 | - Per pool OOM handlers allow you to grow pools if they run out of free space 51 | (if possible), or just report OOM condition gracefully. 52 | 53 | SMalloc still will not permit you to do these things however: 54 | 55 | - Automatic error handlers on "Out of memory" conditions, 56 | - Playing nice with double free / header / memory corruptions, 57 | - Shooting in your foot without serious wounds after. 58 | 59 | ## But why? 60 | 61 | SMalloc is a design decision for my own long term project - access(8). 62 | This program had a silly static memory allocator in past, which used large and small static arrays 63 | of fixed lengths and this approach was really simple, but too memory wasteful of course. 64 | Because super is not so large and does not do much of large memory allocations I seriously 65 | thought about brk() style memory allocation, that is - just have a large memory pool and shift 66 | an allocation pointer among it until it will not run out of memory of course. But large string 67 | allocations and requirement of almost arbitrary string length handling made this idea inadequate. 68 | 69 | Time passed, I felt a need for the allocator in my other (security) projects. 70 | So I decided finally to sit and write one, even if it will take a month to write. 71 | The working prototype however worked after less than two hours of coding :-) 72 | 73 | Answering a generic "Why?" question is simple: because almost nobody did that in the past. 74 | 75 | Current memory allocators, both supplied with your system and separate 76 | libraries rely on these two (or more) things: 77 | - process data segment which is enlarged with brk(), 78 | - pages allocated with mmap when extra large allocations are requested or brk() returns -ENOMEM. 79 | Usually if one or another fails with -ENOMEM, you have no options to recover other than to free 80 | some existing allocations or to wait for condition to dissolve (which may or may not happen). 81 | Worse, the -ENOMEM condition can appear to be completely unawaited, almost randomly. 82 | 83 | The target of this library is to have a preallocated memory since the program start: if program 84 | did started, it will have this memory already allocated and unreclaimable, always available. 85 | The only problem was to use it as a big memory pool and allocate smaller objects from it instead 86 | of opaque size, discontinous heap memory provided by host malloc. 87 | 88 | SMalloc also strives to be very simple to understand for beginners who learn C language. 89 | 90 | That's why such library should exist. 91 | 92 | ## Who may need it? 93 | 94 | SMalloc maybe useful for you, if you need to: 95 | 96 | - manage objects from preallocated static storage (primary target), 97 | - organise memory management inside an embedded environment, 98 | - embed ready to use memory allocator into your OS kernel project, 99 | - manage multiple heaps (pools) simultaneously, 100 | - learn how a simple memory allocator can work. 101 | 102 | ## Implementation details 103 | 104 | SMalloc search strategy is simple pointer-size or start-length two stage search. 105 | First stage searches for any allocated blocks. 106 | Second stage searches for blocks beyond found free space. 107 | 108 | SMalloc is a very simple allocator. It has no any additional protective features, 109 | nor any speedup optimisations. It is NOT suitable for general usage, but only for small projects 110 | which require small amounts of allocated objects and small pools. 111 | 112 | It's header consists of three numbers: 113 | - Real size of allocation, 114 | - Pure user size of allocation, 115 | - Magic "tag" value, which is a hash of current *header* address, the rsize and usize values above. 116 | The header is written prior to user data. The "tag" is required to distinguish a genuine header 117 | out of user data and to guarantee that user is not such lucky to forge it. 118 | Real size indicates full block size with any header-wide overhead. 119 | Real size does not include header size. 120 | Pure user size is the value passed by user during allocation. 121 | User size is usually lesser than real size. 122 | 123 | "Block" is a memory area with header and user data. 124 | Free memory can contain anything. The invalid header is considered as free memory during search. 125 | 126 | Searches are done by shifting a header-wide pointer across the pool. 127 | Allocated block is found by testing each possible header for validity. 128 | During primary search allocated blocks are jumped over by their real size number. 129 | If free space is found, a secondary search is started for possible end of free space 130 | and next allocated block. If no such is found and size marker exceeded user requested size, 131 | the free space is turned into block with header and pointer to user data is returned. 132 | If during search an allocated block is found prior to user size is hit, then secondary search 133 | is aborted, and primary search is resumed. Return of user data aborts primary search obviously. 134 | 135 | SMalloc preventively crashes the whole program (by default) in these conditions: 136 | - Header corruption (possibly by previous "too far" overwrite of user memory) 137 | - Double free (previous allocation with erased header after normal free) 138 | - Wild pointer (including pointer into pool, but no *valid* header was found for it) 139 | Those three are normal "Undefined behavior" conditions as with any other normal memory 140 | operations (both dynamic and static memory), so crash in these situations is justified and desirable. 141 | However user can reassign fatal error handler to it's own function so crashes can be disabled. 142 | 143 | SMalloc cannot work properly with relocated pool itself. The address of allocated objects is 144 | encoded into header into tag field and cannot be mass reassigned easily. 145 | There will be no support for that. 146 | 147 | ## Conclusion 148 | 149 | I hope SMalloc will find it's way into many projects outside of the camp it was developed for. 150 | Possible area to use it is an embedded world or other small projects like author's access(8). 151 | It may fill the gap or remain mostly unknown, but I hope it will be less buggy in future :-) 152 | 153 | SMalloc was written by Andrey "ElectroRys" Rys during Aug2017. 154 | Contact: rys@lynxlynx.ru; https://gitlab.com/electrorys 155 | 156 | ## Licensing 157 | 158 | SMalloc is MIT licensed: Copyright (c) 2017 Andrey Rys. All rights reserved. 159 | 160 | By using it you absolutely, in sane mind, accept that this code can kill your dog, 161 | terrorise your mom and finally shock you with 200VDC @ 10mA. 162 | Although, obviously, it will not do that and cannot do, but just to warn you of possibility. 163 | I do not know, maybe your embedded Arduino will fail with memory allocation and then will 164 | turn it's brains insane and send a signal through optocoupler driver to a power MOSFET, 165 | which will lead this power to you. Anything then can happen :-) 166 | 167 | For full reuse conditions see COPYRIGHT file. 168 | -------------------------------------------------------------------------------- /smalloc.3: -------------------------------------------------------------------------------- 1 | .Dd 19Aug2017 2 | .Dt SMALLOC 3 3 | .Os R2 4 | 5 | .Sh NAME 6 | .Nm smalloc 7 | : sm_malloc, sm_zalloc, sm_free, sm_realloc, sm_realloc_move, sm_calloc, sm_szalloc 8 | .Nd allocate, manage, resize, query size and free dynamic memory which is allocated from user pointed static memory area; 9 | 10 | sm_alloc_valid - query object validity; 11 | 12 | sm_malloc_stats - get attached pool statistics; 13 | 14 | sm_set_default_pool, sm_release_default_pool - attach and release pool; 15 | 16 | sm_set_ub_handler - set global 17 | .Nm 18 | undefined behavior handler. 19 | 20 | .Sh SYNOPSIS 21 | .In errno.h 22 | .In smalloc.h 23 | .Fn "void *sm_malloc" "size_t n" 24 | .Fn "void *sm_zalloc" "size_t n" 25 | .Fn "void sm_free" "void *p" 26 | .Fn "void *sm_realloc" "void *p" "size_t n" 27 | .Fn "void *sm_realloc_move" "void *p" "size_t n" 28 | .Fn "void *sm_calloc" "size_t y" "size_t x" 29 | .Fn "size_t sm_szalloc" "void *p" 30 | .Fn "int sm_alloc_valid" "void *p" 31 | .Fn "int sm_malloc_stats" "size_t *total" "size_t *user" "size_t *free" "int *nr_obj" 32 | .Fn "int sm_set_default_pool" "void *pool" "size_t pool_size" "int do_zero" "smalloc_oom_handler oom_handler_fn" 33 | .Fn "int sm_release_default_pool" "void" 34 | .Fn "void sm_set_ub_handler" "smalloc_ub_handler ub_handler" 35 | 36 | .Sh DESCRIPTION 37 | .Nm 38 | is a portable and simple memory management package which is intended to be used especially with user provided memory regions. It is like a normal 39 | .Xr malloc 3 40 | provided by any modern system today (and you should expect conventional behavior), but it extends it by allowing the user to specify memory area, 41 | .Em a custom heap , 42 | in which all the allocations will be stored. 43 | 44 | .Sy sm_malloc, sm_zalloc, sm_calloc 45 | allocate memory. 46 | .Sy sm_zalloc 47 | and 48 | .Sy sm_calloc 49 | guarantee zero-fill of newly created object. 50 | .Sy sm_malloc 51 | may return object containing garbage (usually, if pool is static storage, it contains zeroes after program start, but after extensive usage it will contain garbage). 52 | 53 | .Sy sm_realloc 54 | change already allocated object size, but also can be used to allocate and free memory too. 55 | 56 | .Sy sm_realloc_move 57 | works like 58 | .Sy sm_realloc , 59 | but fails if physical reallocation (move) of the object is required. 60 | 61 | .Sy sm_free 62 | deallocates 63 | .Nm 64 | allocated memory. 65 | 66 | .Sy sm_szalloc 67 | queries a 68 | .Em valid 69 | .Nm 70 | memory block size. 71 | 72 | .Sy sm_alloc_valid 73 | tests if a pointer belongs to valid 74 | .Nm 75 | object within the pool. 76 | 77 | .Sy sm_malloc_stats 78 | accept four pointers to numbers where it stores current pool state: 79 | .Fa *total 80 | accepts total used bytes in pool: user data with any overhead added by 81 | .Nm , 82 | .Fa *user 83 | accepts total user bytes with any user overhead, 84 | .Fa *free 85 | accepts total free bytes still available, and 86 | .Fa *nr_obj 87 | accepts number of already allocated objects within the pool. 88 | 89 | .Sy sm_set_default_pool 90 | takes 91 | .Fa *pool 92 | pool of 93 | .Fa pool_size 94 | size and registers it as a global default pool. 95 | Nonzero 96 | .Fa do_zero 97 | instructs 98 | .Nm 99 | to zero-fill pool before use, 100 | .Em and also zero any newly allocated objects before returning them, and zero any to be freed objects upon to returning them back to the pool. 101 | If 102 | .Fa do_zero 103 | is 0, then only 104 | .Sy sm_zalloc 105 | and 106 | .Sy sm_calloc 107 | zero-fill object before returning them to caller, but 108 | .Sy sm_malloc 109 | will return object possibly containing garbage. 110 | .Fa oom_handler_fn 111 | can be either 112 | .Em NULL 113 | or a pointer to OOM handler function (see 114 | .Sx ERROR HANDLERS 115 | for a description). In case of 116 | .Em NULL , 117 | no OOM handler is called at all on an OOM condition. 118 | 119 | .Sy sm_release_default_pool 120 | deregisters current pool and zero-fills it (erases) if 121 | .Fa do_zero 122 | argument to 123 | .Sy sm_set_default_pool 124 | was nonzero. All further calls to any allocation or freeing functions will fail without registered pool. 125 | 126 | .Sy sm_set_ub_handler 127 | sets global undefined behavior handler. It's description is given in 128 | .Sx ERROR HANDLERS 129 | section. 130 | If 131 | .Em NULL 132 | is passed as 133 | .Fa ub_handler , 134 | then internal UB handler is reset to 135 | .Nm 136 | default one: crashing the program. 137 | 138 | .Sh RETURN VALUE 139 | .Sy sm_malloc, sm_zalloc, sm_calloc 140 | return a pointer to newly created object on success. The data it poins to can be used only up to 141 | .Fa n 142 | argument passed to them (or 143 | .Fa y * x 144 | in case of 145 | .Sy sm_calloc ) 146 | If 147 | .Fa n 148 | is 0, these functions return a pointer to newly created object 149 | .Em which content should be never accessed . 150 | . 151 | They return 152 | .Em NULL 153 | on failure to allocate memory and set 154 | .Va errno 155 | to 156 | .Em ENOMEM . 157 | 158 | .Sy sm_realloc 159 | returns a pointer to object which size was adjusted. 160 | .Em The object address may differ from passed in address . 161 | If 162 | .Fa p 163 | is 164 | .Em NULL , 165 | then the call is equivalent to 166 | .Fn "sm_malloc" "n" . 167 | If 168 | .Fa p 169 | is a pointer to existing object and 170 | .Fa n 171 | is 0, then the call is equivalent to 172 | .Fn "sm_free" "p" . 173 | On failure to relocate or size change, it will return 174 | .Em NULL 175 | and set 176 | .Va errno 177 | to 178 | .Em ENOMEM . 179 | 180 | .Sy sm_realloc_move 181 | works exactly as 182 | .Sy sm_realloc , 183 | but fails if physical reallocation (move) of the object is required. In such case, 184 | .Em NULL 185 | is returned and 186 | .Va errno 187 | is set to 188 | .Em ERANGE . 189 | Original object of question is not touched, it's size is not changed and it can be used as before. 190 | 191 | .Sy sm_free 192 | does not return a value, but may change 193 | .Va errno 194 | in cases described in 195 | .Sx NOTES 196 | section. 197 | 198 | .Sy sm_szalloc 199 | return an exact object size of object pointed to by 200 | .Fa p 201 | (the argument 202 | .Fa n 203 | passed to any of: 204 | .Sy sm_malloc, sm_zalloc, sm_realloc, sm_realloc_move 205 | and 206 | .Fa y * x 207 | result of 208 | .Sy sm_calloc ) 209 | . 210 | .Em This is the only permitted area that the caller may use. 211 | For 212 | .Em NULL 213 | as argument, 214 | .Sy sm_szalloc 215 | returns 0. 216 | For unique object of 0 size created with 217 | .Fn "sm_malloc" "0" 218 | (or equivalent), the return value is 1, 219 | .Em but this may be changed in future . 220 | 221 | .Sy sm_alloc_valid 222 | returns 1 if object pointed to by 223 | .Fa p 224 | is valid reference, 0 otherwise. It does not permit to differentiate between multiple pools. 225 | 226 | .Sy sm_malloc_stats 227 | return 1 when the pool contains at least one object, thus numbers stored are not zeroes, 0 if no objects are in pool or 228 | .Em all arguments are NULLs , 229 | or -1 on any other error described in 230 | .Sx NOTES 231 | section. 232 | 233 | .Sy sm_set_default_pool 234 | returns 1 on success (pool was registered), 0 if pool is very small to use. In this situation, an 235 | .Va errno 236 | will be also set to 237 | .Em ENOSPC . 238 | 239 | .Sy sm_release_default_pool 240 | returns 1 on success (an existing pool was successfully deregistered), 0 otherwise, with 241 | .Va errno 242 | set to 243 | .Em EINVAL . 244 | 245 | .Sy sm_set_ub_handler 246 | always succeeds and does not return any value. 247 | 248 | .Sh NOTES 249 | If pool was never registered, or recently was deregistered with 250 | .Sy sm_release_default_pool , 251 | then all memory management functions will fail by returning their error values: 252 | .Em NULL 253 | or 254 | .Em 0 255 | or 256 | .Em -1 257 | or 258 | .Em (size_t)-1 , 259 | or 260 | .Em (void) 261 | and 262 | .Va errno 263 | will be set to 264 | .Em EINVAL . 265 | 266 | All functions working on existing objects which take pointers to them, except 267 | .Sy sm_alloc_valid , 268 | will check the pointer to be a valid reference to existing object belonging to registered pool. 269 | If an invalid pointer is catched, then 270 | .Nm 271 | calls an 272 | .Em undefined behavior 273 | handler. The default 274 | .Nm 275 | embedded UB handler is set to crash the program to bring programmer's attention as early as possible. This handler can be overriden with 276 | .Sy sm_set_ub_handler 277 | for a lifetime of program until next call to this function. 278 | .Sy sm_alloc_valid 279 | does not call UB handler in case of invalid pointer reference: it was specially designed to answer the question: 280 | .Dq Is this pointer a valid object reference? 281 | 282 | One can implement a classic but more precise malloc on top of 283 | .Nm 284 | by using 285 | .Xr brk 2 286 | as a custom heap and extending it on each OOM handler call. 287 | 288 | .Sh ERROR HANDLERS 289 | .Sy smalloc_oom_handler 290 | Out Of Memory handler is defined as follows: 291 | .Bd -literal -offset 8n 292 | typedef size_t (*smalloc_oom_handler)(struct smalloc_pool *, size_t); 293 | size_t oom_handler(struct smalloc_pool *spool, size_t failed_alloc_req); 294 | .Ed 295 | 296 | It takes a pool descriptor 297 | .Fa *spool 298 | (see 299 | .Sx MULTIPLE POOLS 300 | section) 301 | and 302 | .Fa failed_alloc_req , 303 | which is size of object that failed to be created (the 304 | .Fa n 305 | argument to allocation functions). 306 | The task of OOM handler is either to report an abnormal condition, possibly (and often) with program abortion or other way to exit, or to extend the pool, if possible (if pool is static, but resizeable). 307 | In case of refuse to extend, but without abortion, the handler must return 0. Otherwise handler must return a 308 | .Em new size of pool after successful extension . 309 | 310 | .Em IMPORTANT! The pool CANNOT BE RELOCATED IF IT CONTAINS ALLOCATED OBJECTS 311 | with functions such as 312 | .Xr realloc 3 . 313 | Relocation of pool will lead to bad references to the objects stored inside pointers across your program! You must ensure that pool will never be relocated once used when resizing the pool. 314 | Returning a size lesser than current pool size will not lead to extension of pool, the effect will be the same as if handler would return 0. Returned size may or may not be not aligned: the function will align the new size automatically. 315 | 316 | .Sy smalloc_ub_handler 317 | Undefined Behavior handler is defined as follows: 318 | .Bd -literal -offset 8n 319 | typedef void (*smalloc_ub_handler)(struct smalloc_pool *, const void *); 320 | void ub_handler(struct smalloc_pool *spool, const void *offender); 321 | .Ed 322 | 323 | It takes a pool descriptor 324 | .Fa *spool 325 | (see 326 | .Sx MULTIPLE POOLS 327 | section) 328 | and 329 | .Fa *offender 330 | pointer which is an exact pointer value that caused an UB exception. 331 | The task of UB handler is to report the condition as fast as possible and abort the program. An UB handler can 332 | .Em ignore 333 | abnormal condition, but it is highly discouraged. 334 | Default UB handler embedded into 335 | .Nm 336 | itself is to cause program crash by writing to NULL pointer. It does not report condition somewhere just not to depend on libc's stdio package (or something other, possibly platform specific). 337 | 338 | .Sh MULTIPLE POOLS 339 | .Nm 340 | supports using multiple pools in parallel (but not in multithreaded environment however). There are versions of described functions above which names end with 341 | .Sq _pool 342 | suffix and have prepended their first argument as 343 | .Fa "struct smalloc_pool *" , 344 | which is a pool descriptor of this format: 345 | .Bd -literal -offset 8n 346 | struct smalloc_pool { 347 | void *pool; 348 | size_t pool_size; 349 | int do_zero; 350 | smalloc_oom_handler oomfn; 351 | }; 352 | .Ed 353 | 354 | Manual fill of the structure is 355 | .Em NOT RECOMMENDED , 356 | it is best to use a pool aware 357 | .Sy sm_set_pool 358 | function, which is just the 359 | .Sy sm_set_default_pool 360 | variant with 361 | .Fa "struct smalloc_pool *" 362 | as it's first argument. 363 | 364 | Releasing such a pool is done with 365 | .Sy sm_release_pool , 366 | which takes 367 | .Fa "struct smalloc_pool *" 368 | as it's only single argument. 369 | 370 | Memory behind these descriptors is not allocated by 371 | .Nm , 372 | it is task of the caller to store pool descriptors somewhere. 373 | 374 | Then caller may turn normal functions into pool versions, for example: 375 | .Fn "sm_realloc" "void *p" "size_t n" 376 | turns into 377 | .Fn "sm_realloc_pool" "struct smalloc_pool *spool" "void *p" "size_t n" , 378 | and so on. 379 | 380 | There is a 381 | .Sy sm_align_pool 382 | function, which takes a pool descriptor and adjusts it's 383 | .Va pool_size 384 | member to a value best fit for a 385 | .Nm . 386 | This function is provided only for manual fill of the pool descriptor. 387 | .Em Unaligned pool descriptors will be rejected 388 | by 389 | .Nm 390 | and 391 | .Va errno 392 | will be set to 393 | .Em EINVAL 394 | in such cases. 395 | 396 | .Sy smalloc_curr_pool 397 | symbol points to global pool descriptor which is used by 398 | .Sy sm_set_default_pool 399 | and 400 | .Sy sm_release_default_pool , 401 | as well as by 402 | .Sq non-pool 403 | functions. 404 | 405 | .Sh FILES 406 | See 407 | .Pa smalloc.h , 408 | .Pa smalloc_test_so.c , 409 | and source code. 410 | 411 | .Sh EXAMPLE 412 | This is the minimal example of how to use the library: 413 | .Bd -literal -offset 8n 414 | #include 415 | 416 | static char my_pool[16384]; 417 | 418 | int main(void) 419 | { 420 | char *s, *d; 421 | size_t n; 422 | 423 | if (!sm_set_default_pool(my_pool, sizeof(my_pool), 0, NULL)) return 1; 424 | 425 | s = sm_malloc(40); 426 | if (s) { 427 | n = sm_szalloc(s); 428 | memset(s, 'X', n); 429 | } 430 | d = sm_malloc(700); 431 | if (d) memset(d, 'Y', sm_szalloc(d)); 432 | s = sm_realloc(s, n+30); 433 | if (s) memset(s+n, 'x', sm_szalloc(s)-n); 434 | d = sm_realloc(d, 14000); 435 | if (d) memset(d, 'y', sm_szalloc(d)); 436 | 437 | sm_free(s); 438 | sm_free(d); 439 | 440 | sm_release_default_pool(); 441 | 442 | return 0; 443 | } 444 | .Ed 445 | 446 | .Sh BUGS 447 | Returned objects may or may not be aligned to be used for any kind of variable. However it places objects exactly so at least integers and pointers can be placed and used without harm within them. 448 | 449 | Allocations lesser than 12 bytes on 32 bit systems (typ.) are not so efficient: the object header takes 12 bytes and minimum overhead size is also 12 bytes. So per each, for example, 4 byte request there will be a 20 byte of overhead. On 64 bit systems it's even worse, things usually double. 450 | 451 | True multithreading with locking was not implemented and is not currently a planned task. 452 | 453 | Unlike highly promoted Linux's behavior about always succeeding 454 | .Sy malloc , 455 | the memory in 456 | .Nm 457 | is managed directly by programmer. 458 | 459 | .Sh CONFORMING TO 460 | .Sy sm_malloc, sm_calloc, sm_realloc 461 | and 462 | .Sy sm_free 463 | are fully compatible with usual 464 | .Sy malloc, calloc, realloc 465 | and 466 | .Sy free . 467 | Their behavior on normal/failed situations is same (or should be same - report a bug if not). 468 | Programmer should not bother about UB because good program does not invoke UB. 469 | 470 | .Sy sm_zalloc, sm_szalloc, sm_realloc_move 471 | and 472 | .Sy sm_alloc_valid 473 | are 474 | .Nm 475 | extensions. They're not implemented in other malloc type packages, thus their usage is not portable. 476 | 477 | .Sh AUTHORS 478 | .Nm 479 | was written in spare time by 480 | .An Andrey Rys Aq Mt rys@lynxlynx.ru 481 | This library is available at 482 | .Lk https://github.com/electrorys/smalloc/ . 483 | 484 | The code, unlike any other programs written by Rys is MIT licensed: 485 | .Em Copyright (c) 2017 Andrey Rys . 486 | See 487 | .Em COPYRIGHT 488 | file in the source distribution for complete terms. 489 | 490 | .Sh SEE ALSO 491 | 492 | .Xr malloc 3 , 493 | .Xr calloc 3 , 494 | .Xr free 3 , 495 | .Xr realloc 3 . 496 | -------------------------------------------------------------------------------- /smalloc.3.txt: -------------------------------------------------------------------------------- 1 | SMALLOC(3) Library Functions Manual SMALLOC(3) 2 | 3 | 4 | 5 | NNAAMMEE 6 | ssmmaalllloocc : sm_malloc, sm_zalloc, sm_free, sm_realloc, sm_realloc_move, 7 | sm_calloc, sm_szalloc - allocate, manage, resize, query size and free 8 | dynamic memory which is allocated from user pointed static memory area; 9 | 10 | sm_alloc_valid - query object validity; 11 | 12 | sm_malloc_stats - get attached pool statistics; 13 | 14 | sm_set_default_pool, sm_release_default_pool - attach and release pool; 15 | 16 | sm_set_ub_handler - set global ssmmaalllloocc undefined behavior handler. 17 | 18 | 19 | SSYYNNOOPPSSIISS 20 | ##iinncclluuddee <> 21 | ##iinncclluuddee <> 22 | 23 | vvooiidd **ssmm__mmaalllloocc(_s_i_z_e___t _n); 24 | 25 | vvooiidd **ssmm__zzaalllloocc(_s_i_z_e___t _n); 26 | 27 | vvooiidd ssmm__ffrreeee(_v_o_i_d _*_p); 28 | 29 | vvooiidd **ssmm__rreeaalllloocc(_v_o_i_d _*_p, _s_i_z_e___t _n); 30 | 31 | vvooiidd **ssmm__rreeaalllloocc__mmoovvee(_v_o_i_d _*_p, _s_i_z_e___t _n); 32 | 33 | vvooiidd **ssmm__ccaalllloocc(_s_i_z_e___t _y, _s_i_z_e___t _x); 34 | 35 | ssiizzee__tt ssmm__sszzaalllloocc(_v_o_i_d _*_p); 36 | 37 | iinntt ssmm__aalllloocc__vvaalliidd(_v_o_i_d _*_p); 38 | 39 | iinntt ssmm__mmaalllloocc__ssttaattss(_s_i_z_e___t _*_t_o_t_a_l, _s_i_z_e___t _*_u_s_e_r, _s_i_z_e___t _*_f_r_e_e, 40 | _i_n_t _*_n_r___o_b_j); 41 | 42 | iinntt ssmm__sseett__ddeeffaauulltt__ppooooll(_v_o_i_d _*_p_o_o_l, _s_i_z_e___t _p_o_o_l___s_i_z_e, _i_n_t _d_o___z_e_r_o, 43 | _s_m_a_l_l_o_c___o_o_m___h_a_n_d_l_e_r _o_o_m___h_a_n_d_l_e_r___f_n); 44 | 45 | iinntt ssmm__rreelleeaassee__ddeeffaauulltt__ppooooll(_v_o_i_d); 46 | 47 | vvooiidd ssmm__sseett__uubb__hhaannddlleerr(_s_m_a_l_l_o_c___u_b___h_a_n_d_l_e_r _u_b___h_a_n_d_l_e_r); 48 | 49 | 50 | DDEESSCCRRIIPPTTIIOONN 51 | ssmmaalllloocc is a portable and simple memory management package which is 52 | intended to be used especially with user provided memory regions. It is 53 | like a normal malloc(3) provided by any modern system today (and you 54 | should expect conventional behavior), but it extends it by allowing the 55 | user to specify memory area, _a _c_u_s_t_o_m _h_e_a_p, in which all the allocations 56 | will be stored. 57 | 58 | ssmm__mmaalllloocc,, ssmm__zzaalllloocc,, ssmm__ccaalllloocc allocate memory. ssmm__zzaalllloocc and ssmm__ccaalllloocc 59 | guarantee zero-fill of newly created object. ssmm__mmaalllloocc may return object 60 | containing garbage (usually, if pool is static storage, it contains 61 | zeroes after program start, but after extensive usage it will contain 62 | garbage). 63 | 64 | ssmm__rreeaalllloocc change already allocated object size, but also can be used to 65 | allocate and free memory too. 66 | 67 | ssmm__rreeaalllloocc__mmoovvee works like ssmm__rreeaalllloocc, but fails if physical reallocation 68 | (move) of the object is required. 69 | 70 | ssmm__ffrreeee deallocates ssmmaalllloocc allocated memory. 71 | 72 | ssmm__sszzaalllloocc queries a _v_a_l_i_d ssmmaalllloocc memory block size. 73 | 74 | ssmm__aalllloocc__vvaalliidd tests if a pointer belongs to valid ssmmaalllloocc object within 75 | the pool. 76 | 77 | ssmm__mmaalllloocc__ssttaattss accept four pointers to numbers where it stores current 78 | pool state: _*_t_o_t_a_l accepts total used bytes in pool: user data with any 79 | overhead added by ssmmaalllloocc, _*_u_s_e_r accepts total user bytes with any user 80 | overhead, _*_f_r_e_e accepts total free bytes still available, and _*_n_r___o_b_j 81 | accepts number of already allocated objects within the pool. 82 | 83 | ssmm__sseett__ddeeffaauulltt__ppooooll takes _*_p_o_o_l pool of _p_o_o_l___s_i_z_e size and registers it 84 | as a global default pool. Nonzero _d_o___z_e_r_o instructs ssmmaalllloocc to zero-fill 85 | pool before use, _a_n_d _a_l_s_o _z_e_r_o _a_n_y _n_e_w_l_y _a_l_l_o_c_a_t_e_d _o_b_j_e_c_t_s _b_e_f_o_r_e 86 | _r_e_t_u_r_n_i_n_g _t_h_e_m_, _a_n_d _z_e_r_o _a_n_y _t_o _b_e _f_r_e_e_d _o_b_j_e_c_t_s _u_p_o_n _t_o _r_e_t_u_r_n_i_n_g _t_h_e_m 87 | _b_a_c_k _t_o _t_h_e _p_o_o_l_. If _d_o___z_e_r_o is 0, then only ssmm__zzaalllloocc and ssmm__ccaalllloocc 88 | zero-fill object before returning them to caller, but ssmm__mmaalllloocc will 89 | return object possibly containing garbage. _o_o_m___h_a_n_d_l_e_r___f_n can be either 90 | _N_U_L_L or a pointer to OOM handler function (see _E_R_R_O_R _H_A_N_D_L_E_R_S for a 91 | description). In case of _N_U_L_L, no OOM handler is called at all on an OOM 92 | condition. 93 | 94 | ssmm__rreelleeaassee__ddeeffaauulltt__ppooooll deregisters current pool and zero-fills it 95 | (erases) if _d_o___z_e_r_o argument to ssmm__sseett__ddeeffaauulltt__ppooooll was nonzero. All 96 | further calls to any allocation or freeing functions will fail without 97 | registered pool. 98 | 99 | ssmm__sseett__uubb__hhaannddlleerr sets global undefined behavior handler. It's 100 | description is given in _E_R_R_O_R _H_A_N_D_L_E_R_S section. If _N_U_L_L is passed as 101 | _u_b___h_a_n_d_l_e_r, then internal UB handler is reset to ssmmaalllloocc default one: 102 | crashing the program. 103 | 104 | 105 | RREETTUURRNN VVAALLUUEE 106 | ssmm__mmaalllloocc,, ssmm__zzaalllloocc,, ssmm__ccaalllloocc return a pointer to newly created object 107 | on success. The data it poins to can be used only up to _n argument passed 108 | to them (or _y _* _x in case of ssmm__ccaalllloocc) If _n is 0, these functions return 109 | a pointer to newly created object _w_h_i_c_h _c_o_n_t_e_n_t _s_h_o_u_l_d _b_e _n_e_v_e_r _a_c_c_e_s_s_e_d. 110 | They return _N_U_L_L on failure to allocate memory and set _e_r_r_n_o to _E_N_O_M_E_M. 111 | 112 | ssmm__rreeaalllloocc returns a pointer to object which size was adjusted. _T_h_e 113 | _o_b_j_e_c_t _a_d_d_r_e_s_s _m_a_y _d_i_f_f_e_r _f_r_o_m _p_a_s_s_e_d _i_n _a_d_d_r_e_s_s. If _p is _N_U_L_L, then the 114 | call is equivalent to ssmm__mmaalllloocc(_n). If _p is a pointer to existing object 115 | and _n is 0, then the call is equivalent to ssmm__ffrreeee(_p). On failure to 116 | relocate or size change, it will return _N_U_L_L and set _e_r_r_n_o to _E_N_O_M_E_M. 117 | 118 | ssmm__rreeaalllloocc__mmoovvee works exactly as ssmm__rreeaalllloocc, but fails if physical 119 | reallocation (move) of the object is required. In such case, _N_U_L_L is 120 | returned and _e_r_r_n_o is set to _E_R_A_N_G_E. Original object of question is not 121 | touched, it's size is not changed and it can be used as before. 122 | 123 | ssmm__ffrreeee does not return a value, but may change _e_r_r_n_o in cases described 124 | in _N_O_T_E_S section. 125 | 126 | ssmm__sszzaalllloocc return an exact object size of object pointed to by _p (the 127 | argument _n passed to any of: ssmm__mmaalllloocc,, ssmm__zzaalllloocc,, ssmm__rreeaalllloocc,, 128 | ssmm__rreeaalllloocc__mmoovvee and _y _* _x result of ssmm__ccaalllloocc) _T_h_i_s _i_s _t_h_e _o_n_l_y _p_e_r_m_i_t_t_e_d 129 | _a_r_e_a _t_h_a_t _t_h_e _c_a_l_l_e_r _m_a_y _u_s_e_. For _N_U_L_L as argument, ssmm__sszzaalllloocc returns 0. 130 | For unique object of 0 size created with ssmm__mmaalllloocc(_0) (or equivalent), 131 | the return value is 1, _b_u_t _t_h_i_s _m_a_y _b_e _c_h_a_n_g_e_d _i_n _f_u_t_u_r_e. 132 | 133 | ssmm__aalllloocc__vvaalliidd returns 1 if object pointed to by _p is valid reference, 0 134 | otherwise. It does not permit to differentiate between multiple pools. 135 | 136 | ssmm__mmaalllloocc__ssttaattss return 1 when the pool contains at least one object, thus 137 | numbers stored are not zeroes, 0 if no objects are in pool or _a_l_l 138 | _a_r_g_u_m_e_n_t_s _a_r_e _N_U_L_L_s, or -1 on any other error described in _N_O_T_E_S section. 139 | 140 | ssmm__sseett__ddeeffaauulltt__ppooooll returns 1 on success (pool was registered), 0 if pool 141 | is very small to use. In this situation, an _e_r_r_n_o will be also set to 142 | _E_N_O_S_P_C. 143 | 144 | ssmm__rreelleeaassee__ddeeffaauulltt__ppooooll returns 1 on success (an existing pool was 145 | successfully deregistered), 0 otherwise, with _e_r_r_n_o set to _E_I_N_V_A_L. 146 | 147 | ssmm__sseett__uubb__hhaannddlleerr always succeeds and does not return any value. 148 | 149 | 150 | NNOOTTEESS 151 | If pool was never registered, or recently was deregistered with 152 | ssmm__rreelleeaassee__ddeeffaauulltt__ppooooll, then all memory management functions will fail 153 | by returning their error values: _N_U_L_L or _0 or _-_1 or _(_s_i_z_e___t_)_-_1, or _(_v_o_i_d_) 154 | and _e_r_r_n_o will be set to _E_I_N_V_A_L. 155 | 156 | All functions working on existing objects which take pointers to them, 157 | except ssmm__aalllloocc__vvaalliidd, will check the pointer to be a valid reference to 158 | existing object belonging to registered pool. If an invalid pointer is 159 | catched, then ssmmaalllloocc calls an _u_n_d_e_f_i_n_e_d _b_e_h_a_v_i_o_r handler. The default 160 | ssmmaalllloocc embedded UB handler is set to crash the program to bring 161 | programmer's attention as early as possible. This handler can be 162 | overriden with ssmm__sseett__uubb__hhaannddlleerr for a lifetime of program until next 163 | call to this function. ssmm__aalllloocc__vvaalliidd does not call UB handler in case 164 | of invalid pointer reference: it was specially designed to answer the 165 | question: "Is this pointer a valid object reference?" 166 | 167 | One can implement a classic but more precise malloc on top of ssmmaalllloocc by 168 | using brk(2) as a custom heap and extending it on each OOM handler call. 169 | 170 | 171 | EERRRROORR HHAANNDDLLEERRSS 172 | ssmmaalllloocc__oooomm__hhaannddlleerr Out Of Memory handler is defined as follows: 173 | 174 | typedef size_t (*smalloc_oom_handler)(struct smalloc_pool *, size_t); 175 | size_t oom_handler(struct smalloc_pool *spool, size_t failed_alloc_req); 176 | 177 | It takes a pool descriptor _*_s_p_o_o_l (see _M_U_L_T_I_P_L_E _P_O_O_L_S section) and 178 | _f_a_i_l_e_d___a_l_l_o_c___r_e_q, which is size of object that failed to be created (the 179 | _n argument to allocation functions). The task of OOM handler is either 180 | to report an abnormal condition, possibly (and often) with program 181 | abortion or other way to exit, or to extend the pool, if possible (if 182 | pool is static, but resizeable). In case of refuse to extend, but 183 | without abortion, the handler must return 0. Otherwise handler must 184 | return a _n_e_w _s_i_z_e _o_f _p_o_o_l _a_f_t_e_r _s_u_c_c_e_s_s_f_u_l _e_x_t_e_n_s_i_o_n. 185 | 186 | _I_M_P_O_R_T_A_N_T_! _T_h_e _p_o_o_l _C_A_N_N_O_T _B_E _R_E_L_O_C_A_T_E_D _I_F _I_T _C_O_N_T_A_I_N_S _A_L_L_O_C_A_T_E_D _O_B_J_E_C_T_S 187 | with functions such as realloc(3). Relocation of pool will lead to bad 188 | references to the objects stored inside pointers across your program! You 189 | must ensure that pool will never be relocated once used when resizing the 190 | pool. Returning a size lesser than current pool size will not lead to 191 | extension of pool, the effect will be the same as if handler would return 192 | 0. Returned size may or may not be not aligned: the function will align 193 | the new size automatically. 194 | 195 | ssmmaalllloocc__uubb__hhaannddlleerr Undefined Behavior handler is defined as follows: 196 | 197 | typedef void (*smalloc_ub_handler)(struct smalloc_pool *, const void *); 198 | void ub_handler(struct smalloc_pool *spool, const void *offender); 199 | 200 | It takes a pool descriptor _*_s_p_o_o_l (see _M_U_L_T_I_P_L_E _P_O_O_L_S section) and 201 | _*_o_f_f_e_n_d_e_r pointer which is an exact pointer value that caused an UB 202 | exception. The task of UB handler is to report the condition as fast as 203 | possible and abort the program. An UB handler can _i_g_n_o_r_e abnormal 204 | condition, but it is highly discouraged. Default UB handler embedded 205 | into ssmmaalllloocc itself is to cause program crash by writing to NULL pointer. 206 | It does not report condition somewhere just not to depend on libc's stdio 207 | package (or something other, possibly platform specific). 208 | 209 | 210 | MMUULLTTIIPPLLEE PPOOOOLLSS 211 | ssmmaalllloocc supports using multiple pools in parallel (but not in 212 | multithreaded environment however). There are versions of described 213 | functions above which names end with `_pool' suffix and have prepended 214 | their first argument as _s_t_r_u_c_t _s_m_a_l_l_o_c___p_o_o_l _*, which is a pool descriptor 215 | of this format: 216 | 217 | struct smalloc_pool { 218 | void *pool; 219 | size_t pool_size; 220 | int do_zero; 221 | smalloc_oom_handler oomfn; 222 | }; 223 | 224 | Manual fill of the structure is _N_O_T _R_E_C_O_M_M_E_N_D_E_D, it is best to use a pool 225 | aware ssmm__sseett__ppooooll function, which is just the ssmm__sseett__ddeeffaauulltt__ppooooll variant 226 | with _s_t_r_u_c_t _s_m_a_l_l_o_c___p_o_o_l _* as it's first argument. 227 | 228 | Releasing such a pool is done with ssmm__rreelleeaassee__ppooooll, which takes _s_t_r_u_c_t 229 | _s_m_a_l_l_o_c___p_o_o_l _* as it's only single argument. 230 | 231 | Memory behind these descriptors is not allocated by ssmmaalllloocc, it is task 232 | of the caller to store pool descriptors somewhere. 233 | 234 | Then caller may turn normal functions into pool versions, for example: 235 | ssmm__rreeaalllloocc(_v_o_i_d _*_p, _s_i_z_e___t _n) turns into ssmm__rreeaalllloocc__ppooooll(_s_t_r_u_c_t 236 | _s_m_a_l_l_o_c___p_o_o_l _*_s_p_o_o_l, _v_o_i_d _*_p, _s_i_z_e___t _n), and so on. 237 | 238 | There is a ssmm__aalliiggnn__ppooooll function, which takes a pool descriptor and 239 | adjusts it's _p_o_o_l___s_i_z_e member to a value best fit for a ssmmaalllloocc. This 240 | function is provided only for manual fill of the pool descriptor. 241 | _U_n_a_l_i_g_n_e_d _p_o_o_l _d_e_s_c_r_i_p_t_o_r_s _w_i_l_l _b_e _r_e_j_e_c_t_e_d by ssmmaalllloocc and _e_r_r_n_o will be 242 | set to _E_I_N_V_A_L in such cases. 243 | 244 | ssmmaalllloocc__ccuurrrr__ppooooll symbol points to global pool descriptor which is used 245 | by ssmm__sseett__ddeeffaauulltt__ppooooll and ssmm__rreelleeaassee__ddeeffaauulltt__ppooooll, as well as by 246 | `non-pool' functions. 247 | 248 | 249 | FFIILLEESS 250 | See _s_m_a_l_l_o_c_._h, _s_m_a_l_l_o_c___t_e_s_t___s_o_._c, and source code. 251 | 252 | 253 | EEXXAAMMPPLLEE 254 | This is the minimal example of how to use the library: 255 | 256 | #include 257 | 258 | static char my_pool[16384]; 259 | 260 | int main(void) 261 | { 262 | char *s, *d; 263 | size_t n; 264 | 265 | if (!sm_set_default_pool(my_pool, sizeof(my_pool), 0, NULL)) return 1; 266 | 267 | s = sm_malloc(40); 268 | if (s) { 269 | n = sm_szalloc(s); 270 | memset(s, 'X', n); 271 | } 272 | d = sm_malloc(700); 273 | if (d) memset(d, 'Y', sm_szalloc(d)); 274 | s = sm_realloc(s, n+30); 275 | if (s) memset(s+n, 'x', sm_szalloc(s)-n); 276 | d = sm_realloc(d, 14000); 277 | if (d) memset(d, 'y', sm_szalloc(d)); 278 | 279 | sm_free(s); 280 | sm_free(d); 281 | 282 | sm_release_default_pool(); 283 | 284 | return 0; 285 | } 286 | 287 | 288 | BBUUGGSS 289 | Returned objects may or may not be aligned to be used for any kind of 290 | variable. However it places objects exactly so at least integers and 291 | pointers can be placed and used without harm within them. 292 | 293 | Allocations lesser than 12 bytes on 32 bit systems (typ.) are not so 294 | efficient: the object header takes 12 bytes and minimum overhead size is 295 | also 12 bytes. So per each, for example, 4 byte request there will be a 296 | 20 byte of overhead. On 64 bit systems it's even worse, things usually 297 | double. 298 | 299 | True multithreading with locking was not implemented and is not currently 300 | a planned task. 301 | 302 | Unlike highly promoted Linux's behavior about always succeeding mmaalllloocc, 303 | the memory in ssmmaalllloocc is managed directly by programmer. 304 | 305 | 306 | CCOONNFFOORRMMIINNGG TTOO 307 | ssmm__mmaalllloocc,, ssmm__ccaalllloocc,, ssmm__rreeaalllloocc and ssmm__ffrreeee are fully compatible with 308 | usual mmaalllloocc,, ccaalllloocc,, rreeaalllloocc and ffrreeee. Their behavior on normal/failed 309 | situations is same (or should be same - report a bug if not). Programmer 310 | should not bother about UB because good program does not invoke UB. 311 | 312 | ssmm__zzaalllloocc,, ssmm__sszzaalllloocc,, ssmm__rreeaalllloocc__mmoovvee and ssmm__aalllloocc__vvaalliidd are ssmmaalllloocc 313 | extensions. They're not implemented in other malloc type packages, thus 314 | their usage is not portable. 315 | 316 | 317 | AAUUTTHHOORRSS 318 | ssmmaalllloocc was written in spare time by Andrey Rys <_r_y_s_@_l_y_n_x_l_y_n_x_._r_u> This 319 | library is available at hhttttppss::////ggiitthhuubb..ccoomm//eelleeccttrroorryyss//ssmmaalllloocc//. 320 | 321 | The code, unlike any other programs written by Rys is MIT licensed: 322 | _C_o_p_y_r_i_g_h_t _(_c_) _2_0_1_7 _A_n_d_r_e_y _R_y_s _<_r_y_s_@_l_y_n_x_l_y_n_x_._r_u_>. See _C_O_P_Y_R_I_G_H_T file in 323 | the source distribution for complete terms. 324 | 325 | 326 | SSEEEE AALLSSOO 327 | malloc(3), calloc(3), free(3), realloc(3). 328 | 329 | R2 19Aug2017 R2 330 | --------------------------------------------------------------------------------