LCOV - code coverage report
Current view: top level - src/container - stack.c (source / functions) Hit Total Coverage
Test: passgen-test.info Lines: 91 91 100.0 %
Date: 2024-06-14 06:05:32 Functions: 10 10 100.0 %

          Line data    Source code
       1             : #include "passgen/container/stack.h"
       2             : #include "passgen/assert.h"
       3             : #include "passgen/config.h"
       4             : #include <stdlib.h>
       5             : #include <string.h>
       6             : 
       7             : static const size_t passgen_stack_bin_size = 32;
       8             : static const size_t passgen_stack_cap_init = 4;
       9             : 
      10      103164 : static size_t passgen_stack_bins(const passgen_stack *stack) {
      11      103164 :     return (stack->len + stack->bin_size - 1) / stack->bin_size;
      12             : }
      13             : 
      14      170470 : static size_t passgen_stack_bin(const passgen_stack *stack, size_t pos) {
      15      170470 :     return pos / stack->bin_size;
      16             : }
      17             : 
      18      227418 : static size_t passgen_stack_bin_count(const passgen_stack *stack, size_t len) {
      19             :     (void) stack;
      20      227418 :     size_t power = 1;
      21      367181 :     while(power < len) {
      22      139763 :         power *= 2;
      23             :     }
      24      227418 :     return power;
      25             : }
      26             : 
      27             : // Initialize this stack.
      28      103164 : void passgen_stack_init(passgen_stack *stack, size_t element_size) {
      29      103164 :     memset(stack, 0, sizeof(*stack));
      30      103164 :     stack->element_size = element_size;
      31      103164 :     stack->bin_size = passgen_stack_bin_size;
      32      103164 : }
      33             : 
      34             : // Delete the memory for this stack.
      35      103164 : void passgen_stack_free(passgen_stack *stack) {
      36      103164 :     size_t bins = passgen_stack_bins(stack);
      37      202994 :     for(size_t i = 0; i < bins; i++) {
      38       99830 :         free(stack->data[i]);
      39             :     }
      40             : 
      41      103164 :     if(stack->len) {
      42       99803 :         free(stack->data);
      43             :     }
      44             : 
      45      103164 :     PASSGEN_CLEAR(stack);
      46      103164 : }
      47             : 
      48             : // Execute a function for every item on the stack.
      49           3 : void passgen_stack_foreach(
      50             :     const passgen_stack *stack,
      51             :     void (*func)(void *element)) {
      52           3 :     size_t full_bins = stack->len / stack->bin_size;
      53          10 :     for(size_t i = 0; i < full_bins; i++) {
      54           7 :         void *bin = stack->data[i];
      55         231 :         for(size_t j = 0; j < stack->bin_size; j++) {
      56         224 :             func(bin + j * stack->element_size);
      57             :         }
      58             :     }
      59           3 :     size_t last_bin_items = stack->len % stack->bin_size;
      60          33 :     for(size_t i = 0; i < last_bin_items; i++) {
      61          30 :         void *bin = stack->data[full_bins];
      62          30 :         func(bin + i * stack->element_size);
      63             :     }
      64           3 : }
      65             : 
      66             : // Push a new value onto this stack. If a non-NULL pointer is passed, it is
      67             : // copied to the new value. A pointer to the value on the stack is returned.
      68      169070 : void *passgen_stack_push(passgen_stack *stack, void *value) {
      69      169070 :     size_t new_bin = passgen_stack_bin(stack, stack->len);
      70             : 
      71             :     // initially allocate data
      72      169070 :     if(!new_bin) {
      73      168048 :         if(!stack->len) {
      74       99921 :             stack->data = calloc(1, sizeof(void *));
      75       99921 :             stack->data[0] =
      76       99921 :                 calloc(passgen_stack_cap_init, stack->element_size);
      77             :         }
      78      168048 :         if(passgen_stack_bin_count(stack, stack->len) == stack->len) {
      79       57426 :             size_t new_size = passgen_stack_bin_count(stack, stack->len + 1);
      80       57426 :             stack->data[0] =
      81       57426 :                 realloc(stack->data[0], new_size * stack->element_size);
      82             :         }
      83             :     } else {
      84        1022 :         size_t max_bin = passgen_stack_bin(stack, stack->len - 1);
      85             : 
      86             :         // do we need to increase the bins?
      87        1022 :         if(new_bin != max_bin) {
      88          33 :             size_t current_bins = passgen_stack_bin_count(stack, new_bin);
      89          33 :             size_t needed_bins = passgen_stack_bin_count(stack, new_bin + 1);
      90          33 :             if(current_bins != needed_bins) {
      91             :                 void **new_data =
      92          16 :                     realloc(stack->data, needed_bins * sizeof(void *));
      93          16 :                 passgen_assert(new_data);
      94          16 :                 stack->data = new_data;
      95             :             }
      96          33 :             stack->data[new_bin] = calloc(stack->bin_size, stack->element_size);
      97             :         }
      98             :     }
      99             : 
     100      169070 :     size_t offset = stack->len % stack->bin_size;
     101      169070 :     void *data = stack->data[new_bin] + offset * stack->element_size;
     102             : 
     103      169070 :     stack->len += 1;
     104      169070 :     if(value) {
     105        1245 :         memcpy(data, value, stack->element_size);
     106             :     }
     107             : 
     108      169070 :     return data;
     109             : }
     110             : 
     111             : // Get the nth element on the stack. If the position is invalid, returns NULL.
     112     1022788 : void *passgen_stack_get(const passgen_stack *stack, size_t pos) {
     113     1022788 :     if(stack->len <= pos) {
     114         758 :         return NULL;
     115             :     }
     116             : 
     117     1022030 :     size_t bin = pos / stack->bin_size;
     118     1022030 :     size_t offset = pos % stack->bin_size;
     119     1022030 :     return stack->data[bin] + offset * stack->element_size;
     120             : }
     121             : 
     122             : // Pop the largest element off the stack. If a non-NULL pointer is passed,
     123             : // the element's memory is copied into that value, and the passed pointer
     124             : // is returned. If the stack is empty, a NULL pointer is returned.
     125        1247 : void *passgen_stack_pop(passgen_stack *stack, void *element) {
     126        1247 :     if(!stack->len) {
     127           1 :         return NULL;
     128             :     }
     129             : 
     130        1246 :     if(element) {
     131         254 :         void *item = passgen_stack_top(stack);
     132         254 :         memcpy(element, item, stack->element_size);
     133             :     }
     134             : 
     135        1246 :     if(stack->len == 1) {
     136         118 :         free(stack->data[0]);
     137         118 :         stack->data[0] = NULL;
     138         118 :         free(stack->data);
     139         118 :         stack->data = NULL;
     140        1128 :     } else if(stack->len <= stack->bin_size) {
     141         939 :         size_t entry_size = passgen_stack_bin_count(stack, stack->len);
     142         939 :         size_t new_size = passgen_stack_bin_count(stack, stack->len - 1);
     143         939 :         if(entry_size != new_size) {
     144         883 :             stack->data[0] =
     145         883 :                 realloc(stack->data[0], new_size * stack->element_size);
     146             :         }
     147             :     } else {
     148         189 :         size_t max_bin = passgen_stack_bin(stack, stack->len - 1);
     149         189 :         size_t new_bin = passgen_stack_bin(stack, stack->len - 2);
     150         189 :         if(max_bin != new_bin) {
     151           6 :             free(stack->data[max_bin]);
     152           6 :             stack->data[max_bin] = NULL;
     153             :         }
     154             :     }
     155             : 
     156        1246 :     stack->len -= 1;
     157             : 
     158        1246 :     return element;
     159             : }
     160             : 
     161             : // Returns the topmost element of the stack, or NULL if the stack is empty.
     162      912578 : void *passgen_stack_top(const passgen_stack *stack) {
     163      912578 :     return passgen_stack_get(stack, stack->len > 0 ? stack->len - 1 : 0);
     164             : }

Generated by: LCOV version 1.14