Files
FAT-Allocator/allocator/testallocator/benchmarks/kmeans/simple.h
2025-04-25 16:32:38 +01:00

178 lines
8.0 KiB
C

// /* Copyright (C) 2023. Shivashish Das. Licensed under the MIT License.*/
// #include <stdint.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <string.h>
// #include <time.h>
// // source: https://www.reddit.com/r/C_Programming/comments/1bt8dyz/github_dasshivamalloc_a_simple_memory_allocator/
// // #include "alloc.h"
// #ifndef _MSC_VER
// #include <sys/mman.h>
// #endif
// /* This is a simple memory allocator meant for use in single threaded applications.
// First we get memory from the system allocator which is defined by the pool size
// Larger the pool size, more is the amount you can allocate before running out of memory.
// On linux, mmap() is used for memory allocation while on windows good old calloc() is used as the system allocator
// Some basic definitions:
// Block - A memory region always of size 16 bytes. This is the basic unit of allocation
// All allocations are made in multiples of blocks. If any allocation request is not a multiple of 16 bytes, we return memory of a size
// that is the closest multiple to 16 and greater than the user requested size.
// Metadata blocks - For every allocation we allocate two extra blocks. These two blocks hold data about the allocation itself
// and serve to prevent buffer overflows too. Check the comment in alloc() to find out more. For example suppose that if the user asks for 80 bytes
// i.e 80 / 16 = 5 blocks, we will allocate 7 blocks but the pointer passed to the user will point to the seconf block so the user is unable to access
// these blocks. They do sound like a waste of some bytes but help provide protection from buffer overflows
// Posioning - This means that user code has overflown the buffer it was allocated. All memory allocated by alloc() is now invalid
// However this may also be caused by the user simply passing an invalid pointer to us i.e a pointer allocated by some other allocator etc.
// In this case the user can clear the poisoned state by calling clear_posion() but be absolutely sure as a user about this before doing so
// */
// static uint8_t* mem = 0;
// static uint8_t* bitmap = 0;
// static uint64_t blocks = 0;
// static uint8_t poison = 0;
// // Always call this before anything else.
// void alloc_init(uint64_t pool) {
// if (pool % 16 != 0) {
// int rem = pool % 16;
// pool += (16 - rem);
// }
// #ifndef _MSC_VER
// mem = mmap(0, pool, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
// if (mem == MAP_FAILED) {
// fprintf(stderr, "alloc_init(): could not allocate heap\n");
// perror("mmap");
// return;
// }
// #else
// mem = calloc(pool, 1);
// if (!mem) {
// fprintf(stderr, "alloc_init(): could not allocate heap\n");
// exit(1);
// }
// #endif
// // Each bitmap entry can represent 8 blocks and each block is 16 bytes
// // So space representable in one uint8_t is 16 * 8 = 128 bytes
// uint64_t sz = pool / 128;
// if (sz == 0)
// sz = 1; // allocate at least one to keep track of small pools
// #ifndef _MSC_VER
// bitmap = mmap(0, sz , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
// if (bitmap == MAP_FAILED) {
// fprintf(stderr, "alloc_init(): could not allocate bitmap");
// munmap(mem, pool);
// }
// #else
// bitmap = calloc(sz, 1);
// if (!bitmap) {
// fprintf(stderr, "alloc_init(): could not allocate bitmap\n");
// exit(1);
// }
// #endif
// // Zero the entire bitmap
// memset(bitmap, 0, sz);
// blocks = pool / 16;
// }
// #define IS_FREE(blkid) (bitmap[blkid / 8] & (((uint8_t)1) << blkid)) == 0
// #define MARK(blkid) (bitmap[blkid / 8] ^= ((((uint8_t)1) << blkid) - 1))
// // Allocate sz bytes of memory. Caution: May allocate upto 15 bytes more than sz
// void* alloc(uint64_t sz) {
// if (sz % 16 != 0) {
// int rem = sz % 16;
// sz += (16 - rem);
// }
// // Allocate two extra blocks
// // First will be allocated at just behind the first user accessible block
// // This block will have the number of blocks allocated and a randomly generated magic number each 8 bytes long
// // The last block has the "magic number" present in the first block
// // If this magic number gets modified then when free() tries to free the memory
// // Buffer overruns will be caught and this allocator gets poisoned i.e it can no longer allocate memory
// // This is because all blocks are laid out sequentially and if the user overruns the blocks allocated
// // Then the user may have overwritten the contents of other blocks and it is not possible to estimate the damage caused
// // and data corrupted. All pointers to blocks allocated immediately become invalid and free() posions the allocator
// // This helps catch buffer overflows early on
// uint64_t blk = (sz / 16) + 2;
// // if we are posioned, all allocation requests will fail
// if (poison)
// return 0;
// // Loop through the entire bitmap. If a free block is found, check if there are at least blk free blocks after it.
// // If such a contigious group of blocks is found, take appropriate actions and return to user
// // Otherwise we have ran out of memory so inform the user about it
// for (uint64_t i = 0; i < blocks; i++) {
// if (IS_FREE(i)) {
// // Check for contigious free blocks
// for (uint64_t j = i; j < (i + blk); j++) {
// if (!IS_FREE(j))
// goto next;
// }
// // Mark all free blocks
// for (uint64_t j = i; j < (i + blk + 1); j++) {
// MARK(j);
// }
// uint64_t* ptr = mem + (i * 16);
// *ptr = blk;
// // I needed a number which was large enough to occupy 8 bytes so rand() is not enough as in most cases RAND_MAX is only USHORT_MAX
// // Instead use time() which returns a 64 bit value and is almost guaranteed to be unique on every call to alloc()
// uint64_t magic = time(0);
// *(ptr + 1) = magic;
// // Store a magic number in the last block. For the reason see free_mem()
// ptr = mem + (i * 16) + ((blk - 1) * 16);
// *ptr = magic;
// *(ptr + 1) = magic;
// // Return the user a pointer which points to the region just above our metadata block
// return mem + ((i + 1) * 16);
// }
// next:
// }
// fprintf(stderr, "Pool has been exhausted...Cannot allocate more memory");
// return 0;
// }
// // Frees memory allocated by alloc()
// void free_mem(void* data) {
// // First get the number of blocks allocated and magic from the metadata block (i.e the block right behind what alloc() returned)
// uint64_t* ptr = data;
// ptr -= 2;
// uint64_t blk = *ptr;
// ptr++;
// uint64_t magic = *ptr;
// // The magic is stored in the last block of the allocation
// // Compare the two magic values
// // If they are equal, this memory block was allocated by us and we can free this
// // Otherwise the buffer has been overflown which has overwritten the magic number or this was not allocated by alloc() and is not ours to deal with
// ptr = data + (blk - 2) * 16;
// if (magic != *ptr) {
// // If the buffer has overflown then mark this allocator posioned.
// // You may change the poison back to 0 in your code but be careful and do this only if you know that the buffer was not overrun
// fprintf(stderr, "Invalid pointer or buffer overrun detected..Poisoning ourself");
// poison = 1;
// return;
// }
// uint64_t offset = ((uint8_t*) data) - mem;
// offset -= 16;
// offset /= 16;
// // Clear all bits representing this block so next call to alloc() can use this
// for (uint64_t j = offset; j < offset + blk + 1; j++) {
// MARK(j);
// }
// }
// // Do not call this unless you are absolutely sure about the cause of poisoning
// void clear_posion() {
// poison = 0;
// }