
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>

/**
 * To simulate the use of a different allocator, my_malloc()
 * places a sentinel before the block returned to the user.
 * This makes it possible to recognize blocks allocated by my_alloc()
 * when g_free() or g_realloc() is called.
 *
 * If  g_mem_set_vtable() is called before the first allocation,
 * all works well.
 * When g_mem_set_vtable() is called after the first mallocs,
 * things will go wrong when g_free() of g_realloc() is called with
 * such old blocks.
 *
 * Since  gobject_init_ctor()  makes indirectly calls g_malloc_n()
 * before main(), the a call to g_free() with such data cause a crash.
 * 
 * The case is detected here by a call to g_free() without the sentinel.
 *
 * This demo uses  g_intern_string()  because mallocs for gobject_init_ctor()
 * where used for the hash tables used by g_intern_static_string().
 *
 *** This program works file when linked with glib without gobject.
 *
 *** Simply adding gobject to --libs of pkg-config breaks the program.
 * see bug-report. 
 */
 
typedef guint64 Sentinel;
#define SENTINEL 0x123454321
 
static gpointer
my_malloc (gsize n_bytes)
{
    Sentinel *p_sentinel = malloc (n_bytes + sizeof(Sentinel));
    *p_sentinel = SENTINEL;
    return p_sentinel + 1;  // return memory after the sentinel.
}

static gpointer
my_realloc (gpointer mem, gsize n_bytes)
{
    Sentinel *p_sentinel = mem;
    if (p_sentinel != NULL) {
	p_sentinel--;  // point sentinel before old user data.
	if (*p_sentinel != SENTINEL) {
            fprintf (stderr, G_STRLOC
                     " %s ERROR:  No sentinel Things will go wrong now!\n", __func__);
        }
    }
    p_sentinel = realloc (p_sentinel, n_bytes + sizeof(Sentinel));
    *p_sentinel++ = SENTINEL;
    return p_sentinel;
}

void
my_free (gpointer mem)
{
    if (mem) {
        Sentinel *p_sentinel = mem;
        p_sentinel--;   // point sentinel before user data.
        if (*p_sentinel != SENTINEL)
            fprintf (stderr, G_STRLOC " %s ERROR:  No sentinel. "
                    "Things will go wrong now!\n", __func__);
        free (p_sentinel);
    }
}

static GMemVTable my_g_mem_vtable =
    { .malloc	   = my_malloc,
      .realloc     = my_realloc,
      .free	   = my_free,
      .calloc	   = NULL,
      .try_malloc  = NULL,
      .try_realloc = NULL
    };


gint
main (gint argc, gchar **argv)
{
    int i;
    g_mem_set_vtable (&my_g_mem_vtable);
    fprintf (stderr, "Using glib version %d.%d\n",
            glib_major_version, glib_minor_version);
    
    g_free ( g_malloc (1000));
    g_free ( g_malloc (10000));
    fprintf (stderr, G_STRLOC " Things are still all right now.\n");
    
    for (i = 0; i < 100; i++) {
        gchar *s = g_strdup_printf ("%d", i);
        // One of these g_intern_string() calls will cause the extension
        // of the hash table used by the intern mechanism which was
        // initialized with g_malloc before main() during initialization
        // of gobject_init_ctor().
        g_intern_string (s);
        g_free (s);
    }
    fprintf (stderr, G_STRLOC " Everything works file when "
            "gobject is not linked, or when gobject is an old version.\n");
    return 0;
}


