diff --git a/memory_pool.c b/memory_pool.c index d4b69b8..97d91ab 100644 --- a/memory_pool.c +++ b/memory_pool.c @@ -2,4 +2,143 @@ // Created by epagris on 2022.10.20.. // +#include #include "memory_pool.h" +#include "utils.h" + +MP *mp_init(uint8_t *p, uint32_t size) { + ASSERT_BAD_ALIGN(p); // check for alignment + MP *pool = (MP *) p; // fill in properties + pool->p = p + sizeof(MP); // compute beginning of the allocatable area + pool->poolSize = size; // save total pool size + pool->blockRegistry = (MPAllocRecord *) ALIGN((p + size - sizeof(MPAllocRecord)), uint32_t); // determine block registry address + pool->freeSpace = ((uint8_t *) pool->blockRegistry) - pool->p - sizeof(MPAllocRecord); // calculate free space size + + // place sentry element + pool->blockRegistry[-1].addrStart = 0; + pool->blockRegistry[-1].size = 0; + pool->blockRegistry[-1].type = MPRT_SENTRY; + + // place element record holding info on the single, undivided, unallocated block + pool->blockRegistry[0].addrStart = pool->p; + pool->blockRegistry[0].size = pool->freeSpace; + pool->blockRegistry[0].type = MPRT_FREE; + + pool->blockRecCnt = 2; // sentry and free block + + return pool; +} + +uint8_t *mp_alloc(MP *mp, uint32_t size) { + // make the allocation from the beginning of the smallest suitable (large enough) + // contiguous block + + // round size to make it divisible by 4 + size = ALIGN(size, uint32_t); + + // badness = area left free on the candidate free block after allocating the requested block + MPAllocRecord *bestBlock = NULL; + uint32_t leastBadness = ~0; + MPAllocRecord *recIter = mp->blockRegistry; // allocation record + while (recIter->type != MPRT_SENTRY && leastBadness > 0) { // at badness = 0 just break, since it's a perfectly fitting block + // look for suitable free block + if (recIter->type == MPRT_FREE && recIter->size > size) { + uint32_t iterBadness = recIter->size - size; + if (iterBadness < leastBadness) { // calculate badness + bestBlock = recIter; + leastBadness = recIter->size - size; + } + } + recIter--; // step to next block + } + + // allocate block + uint8_t * ptr = NULL; + if (leastBadness == 0) { // just change block registry type if block perfectly fits + bestBlock->type = MPRT_ALLOCATED; + ptr = bestBlock->addrStart; + } else { // if there are some bytes left between allocated blocks + // shift the registry below best block + MPAllocRecord *rec = mp->blockRegistry - (mp->blockRecCnt + 1); // bottom of the registry + while (rec != bestBlock) { + *(rec) = *(rec + 1); + rec++; + } + + // store information on allocated block + MPAllocRecord *allocated = bestBlock - 1; + allocated->type = MPRT_ALLOCATED; + allocated->size = size; + allocated->addrStart = bestBlock->addrStart; + ptr = allocated->addrStart; + + // shrink the remaining free block size + bestBlock->size -= size; + bestBlock->addrStart += size; + + // decrease free size with the increase in the registry size + mp->freeSpace -= sizeof(MPAllocRecord); + + // increase record count + mp->blockRecCnt++; + } + + // decrease free space size with the allocated block size + mp->freeSpace -= size; + + return ptr; +} + +// join adjacent free blocks +static void mp_join_free_blocks(MP *mp) { + MPAllocRecord *recIter = mp->blockRegistry; + while (recIter->type != MPRT_SENTRY) { + if (recIter->type == MPRT_FREE && (recIter - 1)->type == MPRT_FREE) { // if two adjacent free blocks have been found... + // join the blocks + recIter->size += (recIter - 1)->size; + recIter->addrStart = (recIter - 1)->addrStart; + + // shift block below joined blocks one upper + MPAllocRecord *joinIter = (recIter - 1); + while (joinIter->type != MPRT_SENTRY) { + *joinIter = *(joinIter - 1); + joinIter--; + } + mp->freeSpace += sizeof(MPAllocRecord); + } else { + recIter--; + } + } +} + +void mp_free(MP *mp, const uint8_t *p) { + // look for registry record + bool success = false; + MPAllocRecord *recIter = mp->blockRegistry; + while (recIter->type != MPRT_SENTRY) { + if ((recIter->type == MPRT_ALLOCATED) && + ((recIter->addrStart <= p) && ((recIter->addrStart + recIter->size) > p))) { // ...block found + recIter->type = MPRT_FREE; + success = true; + break; + } + recIter--; + } + + if (success) { + mp_join_free_blocks(mp); + } +} + +void mp_report(MP *mp) { + INFO("# TYPE BEGIN SIZE\n"); + MPAllocRecord *recIter = mp->blockRegistry; + uint32_t bi = 0; + while (recIter->type != MPRT_SENTRY) { + INFO("%05u %s %p %u\n", bi, recIter->type == MPRT_ALLOCATED ? "ALLOC " : "FREE ", + recIter->addrStart, recIter->size); + recIter--; + bi++; + } + INFO("\n"); +} \ No newline at end of file diff --git a/memory_pool.h b/memory_pool.h index 33e44c2..80a939e 100644 --- a/memory_pool.h +++ b/memory_pool.h @@ -5,56 +5,75 @@ #include +/** + * Record type + */ +typedef enum { + MPRT_FREE = 0, + MPRT_ALLOCATED = 1, + MPRT_SENTRY = ~0 +} MPRecordtype; + +/** + * Record in the allocation registry. + */ +typedef struct { + uint8_t * addrStart; ///< Starting address of allocated memory block. + uint32_t size; ///< Size of allocated block. + MPRecordtype type; ///< Type of block +} MPAllocRecord; + /** * Memory pool state structure. */ typedef struct { uint8_t * p; ///< Pointer to contiguous memory block the pool operates on. - uint32_t totalSize; ///< Total size of the memory pool. - uint32_t blockSize; ///< Single block size. - uint8_t * blockRegistry; ///< Bitfield array in which block allocations are maintained. - uint32_t totalBlocks; ///< Total number of blocks. - uint32_t freeBlocks; ///< Number of free blocks. -} MemoryPool; + uint32_t poolSize; ///< Total size of the memory pool. + MPAllocRecord * blockRegistry; ///< Array in which block allocations are maintained. + uint32_t blockRecCnt; ///< Number of block records (including sentry!) + uint32_t freeSpace; ///< Free memory size. +} MP; /** - * Header inserted at the beginning of each block when allocated. Blocks can - * be forward-chained with this structure used as singly-linked list elements. - */ -typedef struct MemoryPoolBlockHeader_ { - struct MemoryPoolBlockHeader_ * next; ///< Next block in the chain. Zero if chain's last block. - uint8_t * block; ///< Pointer to the data block. -} MemoryPoolBlockHeader; - -/** - * Block chain header placed after the first memory block's header. - * Contains fast-accessable information about the full chain. - */ -typedef struct MemoryPoolBlockChainHead_ { - uint32_t totalChainSize; ///< Total memory size allocated in the full chain. - uint16_t chainCntr; ///< Number of chain elements linked together. - uint16_t _spaceholder; -} MemoryPoolBlockChainHead; - -/** - * Initialize a new memory pool. MemoryPool object is allocated at the beginning of the given + * Initialize a new memory pool. MP object is allocated at the beginning of the given * area (p). + * + * Memory structure: + * + * p -> ------------- + * MP object + * ------------------ + * allocatable area + * ------------------ + * block registry + * p + size -> ------ + * * @param p memory block the pool is based on - * @param blockSize allocation block size - * @param blockCnt number of available blocks - * @return Pointer to the MemoryPool object handling the newly allocated memory pool, or NULL + * @param size pool size + * @return Pointer to the MP object handling the newly allocated memory pool, or NULL * on failure. */ -MemoryPool * mp_init(uint8_t * p, uint32_t blockSize, uint32_t blockCnt); +MP * mp_init(uint8_t * p, uint32_t size); /** * Allocate a memory block chain from memory pool. * @param mp memory pool the allocation is made from * @param size requested memory size - * @return pointer to head of allocated memory block chain or NULL on failure + * @return beginning of the allocated area or NULL on failure */ -MemoryPoolBlockHeader * mp_alloc(MemoryPool * mp, uint32_t size); +uint8_t * mp_alloc(MP * mp, uint32_t size); -void mp_free(MemoryPool * mp, MemoryPoolBlockHeader ) +/** + * Release a chain of memory blocks. + * @param mp Memory block on which the allocation happened earlier. + * @param mpbh Beginning of allocated memory block to free. + */ +void mp_free(MP * mp, const uint8_t * p); + +/** + * Create report on memory allocations. + * @param mp Memory pool + */ +void mp_report(MP * mp); #endif //ETHERLIB_MEMORY_POOL_H diff --git a/packet_input.h b/packet_input.h index 3d4bd8d..e73619d 100644 --- a/packet_input.h +++ b/packet_input.h @@ -1,6 +1,6 @@ #ifndef ETHERLIB_PACKET_INPUT_H #define ETHERLIB_PACKET_INPUT_H -void input_packet() +void input_packet(); #endif //ETHERLIB_PACKET_INPUT_H diff --git a/packet_registry.h b/packet_registry.h index 0bec413..c10862c 100644 --- a/packet_registry.h +++ b/packet_registry.h @@ -40,12 +40,15 @@ typedef int (*PcktProcFn)(const uint8_t *pHdr, uint32_t size, uint8_t *pProps); * @struct PcktTypeDesc * Pckt type descriptor. Pckt parsers can be registered * using PcktTypeDesc assignments. - * */ typedef struct { - uint16_t type; ///< Type identification the packet (unique!) + uint16_t type; ///< Type identification of the packet 'class' (unique!) uint16_t containerType; ///< Type of container packet packet (e.g.: IPv4 in case of UDP) PcktProcFn procFun; ///< Pckt processing function } PcktTypeDesc; +// --------------------- + + + #endif //ETHERLIB_PACKET_REGISTRY_H \ No newline at end of file diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..c5de145 --- /dev/null +++ b/utils.c @@ -0,0 +1,5 @@ +// +// Created by epagris on 2022.10.31.. +// + +#include "utils.h" diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..b6fb7b6 --- /dev/null +++ b/utils.h @@ -0,0 +1,16 @@ +// +// Created by epagris on 2022.10.31.. +// + +#ifndef ETHERLIB_UTILS_H +#define ETHERLIB_UTILS_H + +#include + +#define ERROR(...) printf(__VA_ARGS__) +#define INFO(...) printf(__VA_ARGS__) + +#define ASSERT_BAD_ALIGN(p) if ((size_t)(p) & 0b11) ERROR("Bad memory alignment in function '%s' in file '%s' on line %d!\n", __func__, __FILE__, __LINE__) +#define ALIGN(p,t) (((size_t)(p) + (sizeof(t) - 1)) & ~(sizeof(t) - 1)) + +#endif //ETHERLIB_UTILS_H