Version 7.0
Copyright © 2021 Lowell D. Thomas
APG
… an ABNF Parser Generator
memory.c
Go to the documentation of this file.
1 /* *************************************************************************************
2  Copyright (c) 2021, Lowell D. Thomas
3  All rights reserved.
4 
5  This file is part of APG Version 7.0.
6  APG Version 7.0 may be used under the terms of the BSD 2-Clause License.
7 
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions are met:
10 
11  1. Redistributions of source code must retain the above copyright notice, this
12  list of conditions and the following disclaimer.
13 
14  2. Redistributions in binary form must reproduce the above copyright notice,
15  this list of conditions and the following disclaimer in the documentation
16  and/or other materials provided with the distribution.
17 
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 * *************************************************************************************/
30 #include "./lib.h"
31 #include <stdarg.h>
32 
52 typedef struct mem_cell_tag {
53  struct mem_cell_tag* spPrev;
54  struct mem_cell_tag* spNext;
57 } mem_cell;
58 
59 
60 #if defined APG_MEM_STATS
61 
68 typedef struct {
69  const void* vpValidate;
72  mem_cell* spActiveList;
74 }mem;
75 
80 
84 #define STATS_ALLOC(s, i) vStatsAlloc((s), (i))
85 
88 #define STATS_FREE(s, i) vStatsFree((s), (i))
89 
92 #define STATS_REALLOC(s, i, o) vStatsRealloc((s), (i), (o))
93 static void vStatsAlloc(mem_stats* spStats, mem_cell* spIn);
94 static void vStatsFree(mem_stats* spStats, mem_cell* spIn);
95 static void vStatsRealloc(mem_stats* spStats, mem_cell* spIn, mem_cell* spOut);
96 #else
97 typedef struct {
98  const void* vpValidate;
99  exception* spException;
100  aint uiActiveCellCount; // number of cells on the active list
101  mem_cell* spActiveList; // pointer to the first (and last) cell in a circularly, doubly linked list
102 } mem;
103 #define STATS_ALLOC(s, i)
104 #define STATS_FREE(s, i)
105 #define STATS_REALLOC(s, i, o)
106 #endif
107 
109 static const void *s_vpMagicNumber = (void*)"memory";
110 static const char* s_cpMemory = "memory allocation error";
111 static void vActivePush(mem* spCtx, mem_cell* spCellIn);
112 static void vActivePop(mem* spCtx, mem_cell* spCellIn);
113 
121 void* vpMemCtor(exception* spException) {
122  if(!bExValidate(spException)){
123  vExContext();
124  }
125  mem* spCtx = NULL;
126  spCtx = (mem*) malloc(sizeof(mem));
127  if (!spCtx) {
128  XTHROW(spException, "malloc failure");
129  }
130  memset((void*) spCtx, 0, sizeof(mem));
131  spCtx->spException = spException;
132  spCtx->vpValidate = s_vpMagicNumber;
133  return (void*) spCtx;
134 }
135 
141 void vMemDtor(void* vpCtx) {
142  mem* spCtx = (mem*) vpCtx;
143  if (vpCtx) {
144  // if the context pointer is non-NULL it must be a valid memory object
145  if (spCtx->vpValidate == s_vpMagicNumber) {
146  vMemClear(vpCtx);
147  memset(vpCtx, 0, sizeof(mem));
148  free(vpCtx);
149  }else{
150  vExContext();
151  }
152  }
153 }
154 
155 
160 abool bMemValidate(void* vpCtx){
161  mem* spCtx = (mem*) vpCtx;
162  if (vpCtx && (spCtx->vpValidate == s_vpMagicNumber)) {
163  return APG_TRUE;
164  }
165  return APG_FALSE;
166 }
167 
174 exception* spMemException(void* vpCtx){
175  mem* spCtx = (mem*) vpCtx;
176  if (vpCtx && (spCtx->vpValidate == s_vpMagicNumber)) {
177  return spCtx->spException;
178  }else{
179  vExContext();
180  }
181  return NULL;
182 }
183 
196 void* vpMemAlloc(void* vpCtx, aint uiBytes) {
197  mem* spCtx = (mem*) vpCtx;
198  if (!vpCtx || (spCtx->vpValidate != s_vpMagicNumber)) {
199  vExContext();
200  return NULL;
201  }
202  mem_cell* spCell;
203  spCell = (mem_cell*) malloc(uiBytes + sizeof(mem_cell));
204  if (spCell) {
205  spCell->uiSize = uiBytes;
206 
207  // push it on the active list
208  vActivePush(spCtx, spCell);
209  STATS_ALLOC(&spCtx->sStats, spCell);
210 
211  // success - return pointer to user data, not the actual heap block
212  return (void*) (spCell + 1);
213  }
214  // malloc failed
215  XTHROW(spCtx->spException, s_cpMemory);
216  return NULL;
217 }
226 void vMemFree(void* vpCtx, const void* vpData) {
227  mem* spCtx = (mem*) vpCtx;
228  if (vpCtx && spCtx->vpValidate == s_vpMagicNumber) {
229  if (vpData) {
230  mem* spCtx = (mem*) vpCtx;
231  mem_cell* spCell;
232  spCell = (mem_cell*) vpData;
233  --spCell; // this backs off from the user data to the actual heap allocation address
234 
235  // validate the data (must be a valid cell)
236  aint ui;
237  mem_cell* spThis = spCtx->spActiveList;
238  for(ui = 0; ui < spCtx->uiActiveCellCount; ui++){
239  if(spThis == spCell){
240  goto found;
241  }
242  spThis = spThis->spNext;
243  }
244  XTHROW(spCtx->spException, "attempt to free an unallocated memory address");
245 
246  // pop from active list
247  found:;
248  STATS_FREE(&spCtx->sStats, spCell);
249  vActivePop(spCtx, spCell);
250  }
251  }else{
252  vExContext();
253  }
254 }
268 void* vpMemRealloc(void* vpCtx, const void* vpData, aint uiBytes) {
269  mem* spCtx = (mem*) vpCtx;
270  if (vpCtx && spCtx->vpValidate == s_vpMagicNumber) {
271  mem_cell* spOldCell;
272  mem_cell* spNewCell;
273  void* vpDst;
274  void* vpSrc;
275  aint uiCopy;
276  if(!vpData){
277  XTHROW(spCtx->spException, "data pointer cannot be NULL");
278  }
279  if(!uiBytes){
280  XTHROW(spCtx->spException, "byte-size for re-allocation cannot be 0");
281  }
282  spOldCell = (mem_cell*) vpData;
283  --spOldCell;
284 
285  // validate the data (must be a valid cell)
286  aint ui;
287  mem_cell* spThis = spCtx->spActiveList;
288  for(ui = 0; ui < spCtx->uiActiveCellCount; ui++){
289  if(spThis == spOldCell){
290  goto found;
291  }
292  spThis = spThis->spNext;
293  }
294  XTHROW(spCtx->spException, "attempt to re-allocate an unallocated memory address");
295 
296  found:;
297  if(spOldCell->uiSize == uiBytes){
298  // no need to reallocate
299  return (void*)(spOldCell + 1);
300  }
301 
302  // get the new allocation
303  spNewCell = (mem_cell*) malloc(uiBytes + sizeof(mem_cell));
304  if (spNewCell) {
305  spNewCell->spNext = spOldCell->spNext;
306  spNewCell->spPrev = spOldCell->spPrev;
307  spOldCell->spNext->spPrev = (struct mem_cell_tag*) spNewCell;
308  spOldCell->spPrev->spNext = (struct mem_cell_tag*) spNewCell;
309  spNewCell->uiSeq = spOldCell->uiSeq;
310  spNewCell->uiSize = uiBytes;
311 
312  // copy the data from old allocation to new
313  uiCopy = uiBytes < spOldCell->uiSize ? uiBytes : spOldCell->uiSize;
314  vpDst = (void*) (spNewCell + 1);
315  vpSrc = (void*) (spOldCell + 1);
316  memcpy(vpDst, vpSrc, uiCopy);
317  if(spCtx->spActiveList == spOldCell){
318  // deleting the first cell, reassign the active list
319  spCtx->spActiveList = spNewCell;
320  }
321 
322  // free the old data
323  STATS_REALLOC(&spCtx->sStats, spOldCell, spNewCell);
324  free((void*) spOldCell);
325  return (void*) (spNewCell + 1);
326  }
327  // malloc failed
328  XTHROW(spCtx->spException, s_cpMemory);
329  }else{
330  vExContext();
331  }
332  return NULL;
333 }
334 
342 aint uiMemCount(void* vpCtx) {
343  mem* spCtx = (mem*) vpCtx;
344  if (vpCtx && spCtx->vpValidate == s_vpMagicNumber) {
345  mem_cell* spLast;
346  if (spCtx->spActiveList) {
347  spLast = (mem_cell*) spCtx->spActiveList->spPrev;
348  return (spLast->uiSeq + 1);
349  }
350  return (aint) 0;
351  }else{
352  vExContext();
353  }
354  return (aint) 0;
355 }
356 
361 void vMemClear(void* vpCtx) {
362  mem* spCtx = (mem*) vpCtx;
363  if (vpCtx && (spCtx->vpValidate == s_vpMagicNumber)) {
364  mem_cell* spLast;
365  while (spCtx->spActiveList) {
366  spLast = (mem_cell*) spCtx->spActiveList->spPrev;
367  STATS_FREE(&spCtx->sStats, spLast);
368  vActivePop(spCtx, spLast);
369  }
370  }else{
371  vExContext();
372  }
373 }
374 
380 static void vActivePush(mem* spCtx, mem_cell* spCellIn) {
381  struct mem_cell_tag* spLast;
382  struct mem_cell_tag* spFirst;
383  struct mem_cell_tag* spCell = (struct mem_cell_tag*) spCellIn;
384 
385  // sequence number roll over is a fatal error
386  // link the cell
387  switch (spCtx->uiActiveCellCount) {
388  case 0:
389  spCtx->spActiveList = spCell;
390  spCell->spNext = spCell;
391  spCell->spPrev = spCell;
392  spCell->uiSeq = 0;
393  break;
394  case 1:
395  spLast = spCtx->spActiveList;
396  spLast->spNext = spCell;
397  spLast->spPrev = spCell;
398  spCell->spNext = spLast;
399  spCell->spPrev = spLast;
400  spCell->uiSeq = spLast->uiSeq + 1;
401  break;
402  default:
403  spFirst = spCtx->spActiveList;
404  spLast = spFirst->spPrev;
405  spFirst->spPrev = spCell;
406  spLast->spNext = spCell;
407  spCell->spNext = spFirst;
408  spCell->spPrev = spLast;
409  spCell->uiSeq = spLast->uiSeq + 1;
410  break;
411  }
412 
413  ++spCtx->uiActiveCellCount;
414 }
415 
423 static void vActivePop(mem* spCtx, mem_cell* spCellIn) {
424  struct mem_cell_tag* spPrev;
425  struct mem_cell_tag* spNext;
426  struct mem_cell_tag* spCell = (struct mem_cell_tag*) spCellIn;
427  if (spCtx->spActiveList == spCell) {
428  // update the pointer to the first cell
429  if (spCtx->uiActiveCellCount == 1) {
430  spCtx->spActiveList = NULL;
431  } else {
432  spCtx->spActiveList = spCtx->spActiveList->spNext;
433  }
434  }
435 
436  // remove the cell from the active list
437  spPrev = spCell->spPrev;
438  spNext = spCell->spNext;
439  spPrev->spNext = spCell->spNext;
440  spNext->spPrev = spCell->spPrev;
441 
442  --spCtx->uiActiveCellCount;
443 
444  // free the data
445  free((void*) spCell);
446 }
447 
448 #if defined APG_MEM_STATS
449 
456 void vMemStats(void* vpCtx, mem_stats* spStats) {
457  mem* spCtx = (mem*) vpCtx;
458  if (vpCtx && spCtx->vpValidate == s_vpMagicNumber) {
459  if (spStats) {
460  mem* spMemCtx = (mem*) vpCtx;
461  memcpy((void*) spStats, (void*) &spMemCtx->sStats, sizeof(mem_stats));
462  }
463  }
464 }
465 
466 /* static helper functions */
472 static void vStatsAlloc(mem_stats* spStats, mem_cell* spIn) {
473  spStats->uiAllocations += 1;
474  spStats->uiCells += 1;
475  spStats->uiHeapBytes += spIn->uiSize + sizeof(mem_cell);
476  if (spStats->uiCells > spStats->uiMaxCells) {
477  spStats->uiMaxCells = spStats->uiCells;
478  }
479  if (spStats->uiHeapBytes > spStats->uiMaxHeapBytes) {
480  spStats->uiMaxHeapBytes = spStats->uiHeapBytes;
481  }
482 }
483 
489 static void vStatsFree(mem_stats* spStats, mem_cell* spIn) {
490  spStats->uiFrees += 1;
491  spStats->uiCells -= 1;
492  spStats->uiHeapBytes -= spIn->uiSize + sizeof(mem_cell);
493 }
500 static void vStatsRealloc(mem_stats* spStats, mem_cell* spIn, mem_cell* spOut) {
501  spStats->uiReAllocations += 1;
502  if(spOut->uiSize > spIn->uiSize){
503  spStats->uiHeapBytes += spOut->uiSize - spIn->uiSize;
504  }else{
505  spStats->uiHeapBytes -= spIn->uiSize - spOut->uiSize;
506  }
507  if (spStats->uiHeapBytes > spStats->uiMaxHeapBytes) {
508  spStats->uiMaxHeapBytes = spStats->uiHeapBytes;
509  }
510 }
511 #else
512 
518 void vMemStats(void* vpCtx, mem_stats* spStats) {
519  if (spStats) {
520  memset((void*) spStats, 0, sizeof(mem_stats));
521  }
522 }
523 #endif
lib.h
This header "#include"s all publid lib headers and other standard headers needed by most objects.
mem_stats::uiMaxCells
aint uiMaxCells
Definition: memory.h:50
mem::spActiveList
mem_cell * spActiveList
pointer to the first (and last) cell in a circularly, doubly linked list
Definition: memory.c:72
vMemDtor
void vMemDtor(void *vpCtx)
Destroys a Memory component. Frees all memory allocated.
Definition: memory.c:141
mem::sStats
mem_stats sStats
memory statistics
Definition: memory.c:73
mem::vpValidate
const void * vpValidate
validation handle
Definition: memory.c:69
mem
For internal use only. The memory object's context. Opaque to user applications.
Definition: memory.c:68
mem_cell_tag::uiSeq
aint uiSeq
The sequence number of this cell.
Definition: memory.c:56
mem_stats::uiCells
aint uiCells
Definition: memory.h:49
vMemClear
void vMemClear(void *vpCtx)
Frees all memory allocations.
Definition: memory.c:361
mem_stats::uiReAllocations
aint uiReAllocations
Definition: memory.h:47
vExContext
void vExContext()
Handles bad context pointers.
Definition: exception.c:126
STATS_REALLOC
#define STATS_REALLOC(s, i, o)
Called by the memory object to count memory re-allocations.
Definition: memory.c:92
uiMemCount
aint uiMemCount(void *vpCtx)
Returns the number of memory allocations.
Definition: memory.c:342
mem_stats::uiMaxHeapBytes
aint uiMaxHeapBytes
Definition: memory.h:52
STATS_ALLOC
#define STATS_ALLOC(s, i)
Called by the memory object to count memory allocations.
Definition: memory.c:84
STATS_FREE
#define STATS_FREE(s, i)
Called by the memory object to count memory frees.
Definition: memory.c:88
mem_stats
Available to the user for display of memory statistics.
Definition: memory.h:45
vpMemRealloc
void * vpMemRealloc(void *vpCtx, const void *vpData, aint uiBytes)
Re-allocates memory previously allocated with vpMemAlloc().
Definition: memory.c:268
XTHROW
#define XTHROW(ctx, msg)
Exception throw macro.
Definition: exception.h:67
mem_cell_tag::spPrev
struct mem_cell_tag * spPrev
pointer to the previous cell
Definition: memory.c:53
aint
uint_fast32_t aint
The APG parser's unsigned integer type.
Definition: apg.h:79
spMemException
exception * spMemException(void *vpCtx)
Get a pointer to this memory objects's exception handler.
Definition: memory.c:174
vpMemAlloc
void * vpMemAlloc(void *vpCtx, aint uiBytes)
Allocates memory.
Definition: memory.c:196
mem_stats::uiHeapBytes
aint uiHeapBytes
Definition: memory.h:51
exception
A structure to describe the type and location of a caught exception.
Definition: exception.h:47
vMemFree
void vMemFree(void *vpCtx, const void *vpData)
Free memory previously allocated with vpMemAlloc().
Definition: memory.c:226
mem::uiActiveCellCount
aint uiActiveCellCount
number of cells on the active list
Definition: memory.c:71
bExValidate
abool bExValidate(exception *spException)
Test an exception structure for validity.
Definition: exception.c:70
exception::vpValidate
const void * vpValidate
Used by the memory object to validate the exception structure.
Definition: exception.h:48
vpMemCtor
void * vpMemCtor(exception *spException)
Construct a memory component.
Definition: memory.c:121
mem::spException
exception * spException
Pointer to the exception struct. NULL if none.
Definition: memory.c:70
APG_TRUE
#define APG_TRUE
Definition: apg.h:291
mem_stats::uiFrees
aint uiFrees
Definition: memory.h:48
bMemValidate
abool bMemValidate(void *vpCtx)
Validates a memory context.
Definition: memory.c:160
vMemStats
void vMemStats(void *vpCtx, mem_stats *spStats)
Returns a copy of the Memory component's current statistics.
Definition: memory.c:456
abool
uint8_t abool
abool is the APG bool type.
Definition: apg.h:140
mem_cell_tag::spNext
struct mem_cell_tag * spNext
pointer to the next cell
Definition: memory.c:54
mem_stats::uiAllocations
aint uiAllocations
Definition: memory.h:46
mem_cell_tag::uiSize
aint uiSize
The usable size, in bytes, of this memory allocation.
Definition: memory.c:55
mem_cell_tag
A circularly-linked list structure.
Definition: memory.c:52
APG_FALSE
#define APG_FALSE
Definition: apg.h:292
APG Version 7.0 is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.