[Summary]

One final addition to the ZString class - sentinels. One common programming error is buffer over-run, writing past the end of an allocated array, and possibly overwriting a following item. We can catch this common programming error by allocating an extra byte on both ends of the string buffer and placing known values (sentinels) in each byte. The sentinels are checked when the ZString instance is freed. This is especially important when using a free list as any normal heap validity tests on deallocation are not exercised.

Only the “debug” version of the code makes use of sentinels.

ZString.cpp

#include <stdlib.h>
#include <string.h>
#include "ZString.h"

#ifndef ASSERT
#include <assert.h>
#define ASSERT(X) assert(X)
#endif

//
//  Out-of-line string methods and free list maintenance.
//

enum {
    STRING_BUFFER_SIZE  = 256,
    BOB_SENTINEL        = 0x66,
    EOB_SENTINEL        = 0x99
};

static void* g_pFreeList;

void ZString::recycle()
{
#ifdef _DEBUG
    ASSERT(BOB_SENTINEL == (255&*(sBuffer-1)));
    ASSERT(EOB_SENTINEL == (255&*(sBuffer+cbBufferMax)));
#endif
    if (STRING_BUFFER_SIZE != cbBufferMax) {
        delete (sBuffer-1);
        sBuffer = 0;
        return;
    }
    *((void**)sBuffer) = g_pFreeList;
    g_pFreeList = sBuffer;
    sBuffer = 0;
    cbBufferMax = 0;
}

void ZString::upsizeTo(int n)
{
    // Allocate oversize strings.
    if (STRING_BUFFER_SIZE <= n) {
        // round up to a quanta
        n = ((n + STRING_BUFFER_SIZE + 1) / STRING_BUFFER_SIZE) * STRING_BUFFER_SIZE;
#ifdef _DEBUG
        char* p = new char[n+2];
        p[0]    = (char) BOB_SENTINEL;
        p[1+n]  = (char) EOB_SENTINEL;
        ++p;
#else
        char* p = new char[n];
#endif
        ::strcpy(p,sBuffer);
        recycle();
        sBuffer = p;
        cbBufferMax = n;
        return;
    }
    cbBufferMax = STRING_BUFFER_SIZE;
    // Grab a buffer from the free list if present (the usual case).
    if (g_pFreeList) {
        sBuffer = (char*) g_pFreeList;
        g_pFreeList = *((void**)g_pFreeList);
    } else {
        // Allocate a stock-sized buffer.
#ifdef _DEBUG
        sBuffer = new char[2+STRING_BUFFER_SIZE];
        *sBuffer++                      = (char) BOB_SENTINEL;
        *(sBuffer+STRING_BUFFER_SIZE)   = (char) EOB_SENTINEL;
#else
        sBuffer = new char[STRING_BUFFER_SIZE];
#endif
    }
    *sBuffer = 0;
}