Custom Memory Allocation Example

#include <stdio.h>

#include "pure.h"

#include <windows.h>

 

// Forward declaration

void func_that_uses_custom_alloc();

 

int main()

{

 func_that_uses_custom_alloc();

 sreturn 0;

}

 

// Size of a virtual memory region that will contain blocks not managed by

// the windows heap

#define REGIONSIZE 0x10000

 

// Pointer to the virtual memory region itself

void *lpRegion;

 

// Structure forming an ordered list of allocated memory blocks

typedef struct Block

{

 unsigned int base;

 unsigned int limit;

 struct Block *next;

} Block;

 

Block * FirstBlock;

 

// A really basic allocator

void *custom_alloc(size_t size)

{

 void *retVal = NULL;

 static unsigned int allocCount;

 Block *NewBlock, *ThisBlock, *NextBlock;

 unsigned int lastLimit = (unsigned int) lpRegion;

 unsigned int newBase;

 

 if (!allocCount)

 {

  FirstBlock = NULL;

 }

 

 if (FirstBlock)

 {

  // Find the first space big enough for the new block.

  ThisBlock = FirstBlock;

  NextBlock = ThisBlock->next;

  lastLimit = ((unsigned int) lpRegion) - 1;

  newBase = 0;

 

  while (ThisBlock)

  {

   if (ThisBlock->base - lastLimit > size)

   {

    // Fit the new block in before this one.

    newBase = lastLimit + 1;

    break;

   }

   else if (!NextBlock)

   {

    // See if the new block will fit at the end.

    if (size < (((unsigned int) lpRegion) +

      REGIONSIZE - ThisBlock->limit))

    {

     newBase = ThisBlock->limit + 1;

    }

    break;

   }

   lastLimit = ThisBlock->limit;

   ThisBlock = NextBlock;

   NextBlock = ThisBlock->next;

  }

 }

 else if (size < ((unsigned int) lpRegion) + REGIONSIZE)

 {

  newBase = (unsigned int) lpRegion;

  NextBlock = NULL;

 }

 

 if (newBase)

 {

  // Remember the base and extent of the new block.

  NewBlock = (Block *) malloc(sizeof(Block));

  NewBlock->base = newBase;

  NewBlock->limit = newBase + size - 1;

  NewBlock->next = NextBlock;

 

  if (!FirstBlock)

  {

   // The list was empty.

   FirstBlock = NewBlock;

  }

  else if (NewBlock->base < FirstBlock->base)

  {

   // The new block appears at the head of the list.

   FirstBlock = NewBlock;

   FirstBlock->next = ThisBlock;

  }

  else

  {

   // The new block belongs anywhere else in the list.

   ThisBlock->next = NewBlock;

  }

 

  allocCount++;

  retVal = (void *) newBase;

 }

 

 return retVal;

}

 

 

// A really basic deallocator

void custom_dealloc(void *address)

{

 Block *ThisBlock, *LastBlock;

 

 if ((unsigned int) address < ((unsigned int) lpRegion) ||

  (unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)

 {

  printf("Bad dealloc at ", (unsigned int) address);

 }

 

 if (FirstBlock && FirstBlock->base)

 {

  // Walk through the blocks, starting at the head of the list.

  ThisBlock = FirstBlock;

 

  while (ThisBlock)

  {

   if (ThisBlock->base == (unsigned int) address)

   {

    if (ThisBlock == FirstBlock)

    {

     // Free the first block.

     FirstBlock = ThisBlock->next;

    }

    else

    {

     // Free this one.

     LastBlock->next = ThisBlock->next;

    }

    free(ThisBlock);

    break;

   }

 

   LastBlock = ThisBlock;

   ThisBlock = ThisBlock->next;

  }

 }

}

 

 

// A really basic reallocator

void * custom_realloc(void *address, size_t size)

{

 void *retVal = NULL;

 Block *NewBlock, *OldBlock, *LastOldBlock;

 unsigned int newBase;

 size_t bytesToCopy;

 

 if ((unsigned int) address < ((unsigned int) lpRegion) ||

  (unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)

 {

  printf("Bad realloc at ", (unsigned int) address);

 }

 

 if (FirstBlock && FirstBlock->base)

  {

  // Find the specified old block in the list.

  OldBlock = FirstBlock;

 

  while (OldBlock)

  {

   if (OldBlock->base == (unsigned int) address)

    {

     break;

    }

 

   LastOldBlock = OldBlock;

   OldBlock = OldBlock->next;

  }

 

  if (OldBlock)

  {

   // Allocate the new block (this may update OldBlock->next).

   newBase = (unsigned int) custom_alloc(size);

 

   // Copy as many bytes as we have in the smaller of these blocks.

   bytesToCopy = OldBlock->limit - OldBlock->base + 1;

   if (bytesToCopy > size)

   {

    bytesToCopy = size;

   }

 

   memcpy((void *) newBase, (void *) OldBlock->base, bytesToCopy);

 

   // Clean up the old block.

   if (OldBlock == FirstBlock)

   {

    OldBlock = OldBlock->next; // Free the first block.

   }

   else

   {

    LastOldBlock->next = OldBlock->next; // Free this one.

   }

 

   free(OldBlock);

   retVal = (void *) newBase;

  }

 }

 

 reurn retVal;

}

 

 

// A really basic "get block size" routine

size_t custom_getsize(void *address)

{

 int retVal = 0;

 Block *ThisBlock;

 

 if ((unsigned int) address < ((unsigned int) lpRegion) ||

  (unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)

 {

  printf("Bad getsize at ", (unsigned int) address);

 }

 

 if (FirstBlock && FirstBlock->base)

 {

  // Get the size of the specified block in the list.

  ThisBlock = FirstBlock;

 

  while (ThisBlock)

  {

   if (ThisBlock->base == (unsigned int) address)

   {

    retVal = ThisBlock->limit - ThisBlock->base + 1;

    break;

   }

 

   ThisBlock = ThisBlock->next;

  }

 }

 

 return retVal;

}

 

 

// Returns non-zero if the address is in a block, zero otherwise.

int custom_didalloc(void *address)

{

 int retVal = 0;

 Block *ThisBlock;

 

 if ((unsigned int) address < ((unsigned int) lpRegion) ||

  (unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)

 {

  printf("Bad didalloc at ", (unsigned int) address);

 }

 

 if (FirstBlock && FirstBlock->base)

 {

  // Get the size of the specified block in the list.

  ThisBlock = FirstBlock;

 

  while (ThisBlock)

  {

   if (ThisBlock->base <= (unsigned int) address &&

    ThisBlock->base + ThisBlock->limit >= (unsigned int) address)

   {

    retVal = (unsigned int) address - ThisBlock->base;

    break;

   }

 

   ThisBlock = ThisBlock->next;

  }

 }

 

 return retVal;

}

 

 

// Not implemented

void custom_compact(void)

{

 return;

}

 

 

// The following functions are used to "wrap" the above custom memory

// allocation routines when Purify is running. They are written to be

// understandable. In production code, the PurifyPre.../Post... API calls

// could be placed in the custom memory allocation routines themselves.

// When Purify is running, these calls adjust for red zones and enable

// Purify to track the allocated blocks for error reporting purposes.

// The Purify API calls do not adjust the passed-in and returned values

// unless Purify is running.

//

void *Allocate(size_t userSize)

{

 size_t adjustedSize = PurifyPreAlloc(userSize, /* dwFlags = */ 0);

 void * lpBlock = (char *) custom_alloc(adjustedSize);

 return PurifyPostAlloc(lpBlock, /* dwFlags = */ 0);

}

 

 

void *Reallocate(void *userAddress, size_t userSize)

{

 void *adjustedAddress = userAddress;

 size_t adjustedSize = PurifyPreRealloc(userAddress, userSize, (void **) &adjustedAddress, /* dwFlags = */ 0);

 adjustedAddress = (char *) custom_realloc(adjustedAddress, adjustedSize);

 return (void *) PurifyPostRealloc(adjustedAddress, /* dwFlags = */ 0);

}

 

 

void Deallocate(void *userAddress)

{

 void *adjustedAddress = PurifyPreFree(userAddress, /* dwFlags = */ 0);

 custom_dealloc(adjustedAddress);

 PurifyPostFree(/* dwFlags = */ 0);

 return;

}

 

 

size_t GetBlockSize(void *userAddress)

{

 void *adjustedAddress = PurifyPreGetSize(userAddress, /* dwFlags = */ 0);

 size_t adjustedSize = custom_getsize(adjustedAddress);

 return PurifyPostGetSize(adjustedSize, /* dwFlags = */ 0);

}

 

 

int DidAlloc(void *userAddress)

{

 void *adjustedAddress = PurifyPreDidAlloc(userAddress, /* dwFlags = */ 0);

 int fActual = custom_didalloc(adjustedAddress);

 return PurifyPostDidAlloc(adjustedAddress, fActual, /* dwFlags = */ 0);

}

 

 

void Compact(void)

{

 PurifyPreHeapMinimize();

 custom_compact();

 PurifyPostHeapMinimize();

}

 

 

// This is an example function that uses the above Purify-aware custom memory

// allocation routines.

//

void func_that_uses_custom_alloc()

{

 char *lpvBlock, *lpvTrueBlock;

 size_t vBlockSize, vBlockSavedSize;

 int indicator;

 

 lpRegion = VirtualAlloc(/* lpAddress = */ NULL, REGIONSIZE, MEM_COMMIT, PAGE_READWRITE);

 

 // Allocate a block (note that Purify will add red zones).

 vBlockSize = 0x20;

 lpvBlock = (char *) Allocate(vBlockSize);

 

 // Leak that first block and create a second one.

 lpvBlock = (char *) Allocate(vBlockSize);

 

 // Initialize part of the second block.

 if (DidAlloc(lpvBlock))

 {

  lpvBlock[0] = 't'; lpvBlock[1] = 'e'; lpvBlock[2] = 's'; lpvBlock[3] = 't';

 }

 PurifyDescribe(lpvBlock);

 PurifyWhatColors(lpvBlock, 8);

 

 // Enlarge it.

 vBlockSize = vBlockSavedSize = 0x180;

 lpvBlock = (char *) Reallocate(lpvBlock, vBlockSize);

 

 // Check its size.

 vBlockSize = GetBlockSize(lpvBlock);

 if (vBlockSize != vBlockSavedSize)

 {

  printf("Block size mismatch for block at ", (unsigned int) lpvBlock);

 }

 PurifyWhatColors(lpvBlock, 8);

 

 // Deallocate it.

 Deallocate(lpvBlock);

 

 // Check for leaks.

 indicator = PurifyNewLeaks(); // Report a leak of 0x20 bytes.

}

(C) Copyright IBM Corporation 1992, 2010.