sl@0: /* GLIB - Library of useful routines for C programming sl@0: * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald sl@0: * sl@0: * This library is free software; you can redistribute it and/or sl@0: * modify it under the terms of the GNU Lesser General Public sl@0: * License as published by the Free Software Foundation; either sl@0: * version 2 of the License, or (at your option) any later version. sl@0: * sl@0: * This library is distributed in the hope that it will be useful, sl@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of sl@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU sl@0: * Lesser General Public License for more details. sl@0: * sl@0: * You should have received a copy of the GNU Lesser General Public sl@0: * License along with this library; if not, write to the sl@0: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, sl@0: * Boston, MA 02111-1307, USA. sl@0: */ sl@0: sl@0: /* sl@0: * Modified by the GLib Team and others 1997-2000. See the AUTHORS sl@0: * file for a list of people on the GLib Team. See the ChangeLog sl@0: * files for a list of changes. These files are distributed with sl@0: * GLib at ftp://ftp.gtk.org/pub/gtk/. sl@0: */ sl@0: sl@0: /* sl@0: * MT safe sl@0: */ sl@0: sl@0: #include "config.h" sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: #include "glib.h" sl@0: sl@0: /* notes on macros: sl@0: * if ENABLE_GC_FRIENDLY is defined, freed memory should be 0-wiped. sl@0: */ sl@0: sl@0: #define MEM_PROFILE_TABLE_SIZE 4096 sl@0: sl@0: #define MEM_AREA_SIZE 4L sl@0: sl@0: static guint mem_chunk_recursion = 0; sl@0: # define MEM_CHUNK_ROUTINE_COUNT() (mem_chunk_recursion) sl@0: # define ENTER_MEM_CHUNK_ROUTINE() (mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () + 1) sl@0: # define LEAVE_MEM_CHUNK_ROUTINE() (mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () - 1) sl@0: sl@0: /* --- old memchunk prototypes --- */ sl@0: void old_mem_chunks_init (void); sl@0: GMemChunk* old_mem_chunk_new (const gchar *name, sl@0: gint atom_size, sl@0: gulong area_size, sl@0: gint type); sl@0: void old_mem_chunk_destroy (GMemChunk *mem_chunk); sl@0: gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk); sl@0: gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_free (GMemChunk *mem_chunk, sl@0: gpointer mem); sl@0: void old_mem_chunk_clean (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_reset (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_print (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_info (void); sl@0: sl@0: sl@0: /* --- MemChunks --- */ sl@0: #ifndef G_ALLOC_AND_FREE sl@0: typedef struct _GAllocator GAllocator; sl@0: typedef struct _GMemChunk GMemChunk; sl@0: #define G_ALLOC_ONLY 1 sl@0: #define G_ALLOC_AND_FREE 2 sl@0: #endif sl@0: sl@0: typedef struct _GFreeAtom GFreeAtom; sl@0: typedef struct _GMemArea GMemArea; sl@0: sl@0: struct _GFreeAtom sl@0: { sl@0: GFreeAtom *next; sl@0: }; sl@0: sl@0: struct _GMemArea sl@0: { sl@0: GMemArea *next; /* the next mem area */ sl@0: GMemArea *prev; /* the previous mem area */ sl@0: gulong index; /* the current index into the "mem" array */ sl@0: gulong free; /* the number of free bytes in this mem area */ sl@0: gulong allocated; /* the number of atoms allocated from this area */ sl@0: gulong mark; /* is this mem area marked for deletion */ sl@0: gchar mem[MEM_AREA_SIZE]; /* the mem array from which atoms get allocated sl@0: * the actual size of this array is determined by sl@0: * the mem chunk "area_size". ANSI says that it sl@0: * must be declared to be the maximum size it sl@0: * can possibly be (even though the actual size sl@0: * may be less). sl@0: */ sl@0: }; sl@0: sl@0: struct _GMemChunk sl@0: { sl@0: const gchar *name; /* name of this MemChunk...used for debugging output */ sl@0: gint type; /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */ sl@0: gint num_mem_areas; /* the number of memory areas */ sl@0: gint num_marked_areas; /* the number of areas marked for deletion */ sl@0: guint atom_size; /* the size of an atom */ sl@0: gulong area_size; /* the size of a memory area */ sl@0: GMemArea *mem_area; /* the current memory area */ sl@0: GMemArea *mem_areas; /* a list of all the mem areas owned by this chunk */ sl@0: GMemArea *free_mem_area; /* the free area...which is about to be destroyed */ sl@0: GFreeAtom *free_atoms; /* the free atoms list */ sl@0: GTree *mem_tree; /* tree of mem areas sorted by memory address */ sl@0: GMemChunk *next; /* pointer to the next chunk */ sl@0: GMemChunk *prev; /* pointer to the previous chunk */ sl@0: }; sl@0: sl@0: sl@0: static gulong old_mem_chunk_compute_size (gulong size, sl@0: gulong min_size) G_GNUC_CONST; sl@0: static gint old_mem_chunk_area_compare (GMemArea *a, sl@0: GMemArea *b); sl@0: static gint old_mem_chunk_area_search (GMemArea *a, sl@0: gchar *addr); sl@0: sl@0: /* here we can't use StaticMutexes, as they depend upon a working sl@0: * g_malloc, the same holds true for StaticPrivate sl@0: */ sl@0: static GMutex *mem_chunks_lock = NULL; sl@0: static GMemChunk *mem_chunks = NULL; sl@0: sl@0: void sl@0: old_mem_chunks_init (void) sl@0: { sl@0: mem_chunks_lock = g_mutex_new (); sl@0: } sl@0: sl@0: GMemChunk* sl@0: old_mem_chunk_new (const gchar *name, sl@0: gint atom_size, sl@0: gulong area_size, sl@0: gint type) sl@0: { sl@0: GMemChunk *mem_chunk; sl@0: gulong rarea_size; sl@0: sl@0: g_return_val_if_fail (atom_size > 0, NULL); sl@0: g_return_val_if_fail (area_size >= atom_size, NULL); sl@0: sl@0: ENTER_MEM_CHUNK_ROUTINE (); sl@0: sl@0: area_size = (area_size + atom_size - 1) / atom_size; sl@0: area_size *= atom_size; sl@0: sl@0: mem_chunk = g_new (GMemChunk, 1); sl@0: mem_chunk->name = name; sl@0: mem_chunk->type = type; sl@0: mem_chunk->num_mem_areas = 0; sl@0: mem_chunk->num_marked_areas = 0; sl@0: mem_chunk->mem_area = NULL; sl@0: mem_chunk->free_mem_area = NULL; sl@0: mem_chunk->free_atoms = NULL; sl@0: mem_chunk->mem_tree = NULL; sl@0: mem_chunk->mem_areas = NULL; sl@0: mem_chunk->atom_size = atom_size; sl@0: sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare); sl@0: sl@0: if (mem_chunk->atom_size % G_MEM_ALIGN) sl@0: mem_chunk->atom_size += G_MEM_ALIGN - (mem_chunk->atom_size % G_MEM_ALIGN); sl@0: sl@0: rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE; sl@0: rarea_size = old_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE); sl@0: mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE); sl@0: sl@0: g_mutex_lock (mem_chunks_lock); sl@0: mem_chunk->next = mem_chunks; sl@0: mem_chunk->prev = NULL; sl@0: if (mem_chunks) sl@0: mem_chunks->prev = mem_chunk; sl@0: mem_chunks = mem_chunk; sl@0: g_mutex_unlock (mem_chunks_lock); sl@0: sl@0: LEAVE_MEM_CHUNK_ROUTINE (); sl@0: sl@0: return mem_chunk; sl@0: } sl@0: sl@0: void sl@0: old_mem_chunk_destroy (GMemChunk *mem_chunk) sl@0: { sl@0: GMemArea *mem_areas; sl@0: GMemArea *temp_area; sl@0: sl@0: g_return_if_fail (mem_chunk != NULL); sl@0: sl@0: ENTER_MEM_CHUNK_ROUTINE (); sl@0: sl@0: mem_areas = mem_chunk->mem_areas; sl@0: while (mem_areas) sl@0: { sl@0: temp_area = mem_areas; sl@0: mem_areas = mem_areas->next; sl@0: g_free (temp_area); sl@0: } sl@0: sl@0: g_mutex_lock (mem_chunks_lock); sl@0: if (mem_chunk->next) sl@0: mem_chunk->next->prev = mem_chunk->prev; sl@0: if (mem_chunk->prev) sl@0: mem_chunk->prev->next = mem_chunk->next; sl@0: sl@0: if (mem_chunk == mem_chunks) sl@0: mem_chunks = mem_chunks->next; sl@0: g_mutex_unlock (mem_chunks_lock); sl@0: sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: g_tree_destroy (mem_chunk->mem_tree); sl@0: sl@0: g_free (mem_chunk); sl@0: sl@0: LEAVE_MEM_CHUNK_ROUTINE (); sl@0: } sl@0: sl@0: gpointer sl@0: old_mem_chunk_alloc (GMemChunk *mem_chunk) sl@0: { sl@0: GMemArea *temp_area; sl@0: gpointer mem; sl@0: sl@0: ENTER_MEM_CHUNK_ROUTINE (); sl@0: sl@0: g_return_val_if_fail (mem_chunk != NULL, NULL); sl@0: sl@0: while (mem_chunk->free_atoms) sl@0: { sl@0: /* Get the first piece of memory on the "free_atoms" list. sl@0: * We can go ahead and destroy the list node we used to keep sl@0: * track of it with and to update the "free_atoms" list to sl@0: * point to its next element. sl@0: */ sl@0: mem = mem_chunk->free_atoms; sl@0: mem_chunk->free_atoms = mem_chunk->free_atoms->next; sl@0: sl@0: /* Determine which area this piece of memory is allocated from */ sl@0: temp_area = g_tree_search (mem_chunk->mem_tree, sl@0: (GCompareFunc) old_mem_chunk_area_search, sl@0: mem); sl@0: sl@0: /* If the area has been marked, then it is being destroyed. sl@0: * (ie marked to be destroyed). sl@0: * We check to see if all of the segments on the free list that sl@0: * reference this area have been removed. This occurs when sl@0: * the ammount of free memory is less than the allocatable size. sl@0: * If the chunk should be freed, then we place it in the "free_mem_area". sl@0: * This is so we make sure not to free the mem area here and then sl@0: * allocate it again a few lines down. sl@0: * If we don't allocate a chunk a few lines down then the "free_mem_area" sl@0: * will be freed. sl@0: * If there is already a "free_mem_area" then we'll just free this mem area. sl@0: */ sl@0: if (temp_area->mark) sl@0: { sl@0: /* Update the "free" memory available in that area */ sl@0: temp_area->free += mem_chunk->atom_size; sl@0: sl@0: if (temp_area->free == mem_chunk->area_size) sl@0: { sl@0: if (temp_area == mem_chunk->mem_area) sl@0: mem_chunk->mem_area = NULL; sl@0: sl@0: if (mem_chunk->free_mem_area) sl@0: { sl@0: mem_chunk->num_mem_areas -= 1; sl@0: sl@0: if (temp_area->next) sl@0: temp_area->next->prev = temp_area->prev; sl@0: if (temp_area->prev) sl@0: temp_area->prev->next = temp_area->next; sl@0: if (temp_area == mem_chunk->mem_areas) sl@0: mem_chunk->mem_areas = mem_chunk->mem_areas->next; sl@0: sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: g_tree_remove (mem_chunk->mem_tree, temp_area); sl@0: g_free (temp_area); sl@0: } sl@0: else sl@0: mem_chunk->free_mem_area = temp_area; sl@0: sl@0: mem_chunk->num_marked_areas -= 1; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: /* Update the number of allocated atoms count. sl@0: */ sl@0: temp_area->allocated += 1; sl@0: sl@0: /* The area wasn't marked...return the memory sl@0: */ sl@0: goto outa_here; sl@0: } sl@0: } sl@0: sl@0: /* If there isn't a current mem area or the current mem area is out of space sl@0: * then allocate a new mem area. We'll first check and see if we can use sl@0: * the "free_mem_area". Otherwise we'll just malloc the mem area. sl@0: */ sl@0: if ((!mem_chunk->mem_area) || sl@0: ((mem_chunk->mem_area->index + mem_chunk->atom_size) > mem_chunk->area_size)) sl@0: { sl@0: if (mem_chunk->free_mem_area) sl@0: { sl@0: mem_chunk->mem_area = mem_chunk->free_mem_area; sl@0: mem_chunk->free_mem_area = NULL; sl@0: } sl@0: else sl@0: { sl@0: #ifdef ENABLE_GC_FRIENDLY sl@0: mem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) - sl@0: MEM_AREA_SIZE + sl@0: mem_chunk->area_size); sl@0: #else /* !ENABLE_GC_FRIENDLY */ sl@0: mem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) - sl@0: MEM_AREA_SIZE + sl@0: mem_chunk->area_size); sl@0: #endif /* ENABLE_GC_FRIENDLY */ sl@0: sl@0: mem_chunk->num_mem_areas += 1; sl@0: mem_chunk->mem_area->next = mem_chunk->mem_areas; sl@0: mem_chunk->mem_area->prev = NULL; sl@0: sl@0: if (mem_chunk->mem_areas) sl@0: mem_chunk->mem_areas->prev = mem_chunk->mem_area; sl@0: mem_chunk->mem_areas = mem_chunk->mem_area; sl@0: sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: g_tree_insert (mem_chunk->mem_tree, mem_chunk->mem_area, mem_chunk->mem_area); sl@0: } sl@0: sl@0: mem_chunk->mem_area->index = 0; sl@0: mem_chunk->mem_area->free = mem_chunk->area_size; sl@0: mem_chunk->mem_area->allocated = 0; sl@0: mem_chunk->mem_area->mark = 0; sl@0: } sl@0: sl@0: /* Get the memory and modify the state variables appropriately. sl@0: */ sl@0: mem = (gpointer) &mem_chunk->mem_area->mem[mem_chunk->mem_area->index]; sl@0: mem_chunk->mem_area->index += mem_chunk->atom_size; sl@0: mem_chunk->mem_area->free -= mem_chunk->atom_size; sl@0: mem_chunk->mem_area->allocated += 1; sl@0: sl@0: outa_here: sl@0: sl@0: LEAVE_MEM_CHUNK_ROUTINE (); sl@0: sl@0: return mem; sl@0: } sl@0: sl@0: gpointer sl@0: old_mem_chunk_alloc0 (GMemChunk *mem_chunk) sl@0: { sl@0: gpointer mem; sl@0: sl@0: mem = old_mem_chunk_alloc (mem_chunk); sl@0: if (mem) sl@0: { sl@0: memset (mem, 0, mem_chunk->atom_size); sl@0: } sl@0: sl@0: return mem; sl@0: } sl@0: sl@0: void sl@0: old_mem_chunk_free (GMemChunk *mem_chunk, sl@0: gpointer mem) sl@0: { sl@0: GMemArea *temp_area; sl@0: GFreeAtom *free_atom; sl@0: sl@0: g_return_if_fail (mem_chunk != NULL); sl@0: g_return_if_fail (mem != NULL); sl@0: sl@0: ENTER_MEM_CHUNK_ROUTINE (); sl@0: sl@0: #ifdef ENABLE_GC_FRIENDLY sl@0: memset (mem, 0, mem_chunk->atom_size); sl@0: #endif /* ENABLE_GC_FRIENDLY */ sl@0: sl@0: /* Don't do anything if this is an ALLOC_ONLY chunk sl@0: */ sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: { sl@0: /* Place the memory on the "free_atoms" list sl@0: */ sl@0: free_atom = (GFreeAtom*) mem; sl@0: free_atom->next = mem_chunk->free_atoms; sl@0: mem_chunk->free_atoms = free_atom; sl@0: sl@0: temp_area = g_tree_search (mem_chunk->mem_tree, sl@0: (GCompareFunc) old_mem_chunk_area_search, sl@0: mem); sl@0: sl@0: temp_area->allocated -= 1; sl@0: sl@0: if (temp_area->allocated == 0) sl@0: { sl@0: temp_area->mark = 1; sl@0: mem_chunk->num_marked_areas += 1; sl@0: } sl@0: } sl@0: sl@0: LEAVE_MEM_CHUNK_ROUTINE (); sl@0: } sl@0: sl@0: /* This doesn't free the free_area if there is one */ sl@0: void sl@0: old_mem_chunk_clean (GMemChunk *mem_chunk) sl@0: { sl@0: GMemArea *mem_area; sl@0: GFreeAtom *prev_free_atom; sl@0: GFreeAtom *temp_free_atom; sl@0: gpointer mem; sl@0: sl@0: g_return_if_fail (mem_chunk != NULL); sl@0: sl@0: ENTER_MEM_CHUNK_ROUTINE (); sl@0: sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: { sl@0: prev_free_atom = NULL; sl@0: temp_free_atom = mem_chunk->free_atoms; sl@0: sl@0: while (temp_free_atom) sl@0: { sl@0: mem = (gpointer) temp_free_atom; sl@0: sl@0: mem_area = g_tree_search (mem_chunk->mem_tree, sl@0: (GCompareFunc) old_mem_chunk_area_search, sl@0: mem); sl@0: sl@0: /* If this mem area is marked for destruction then delete the sl@0: * area and list node and decrement the free mem. sl@0: */ sl@0: if (mem_area->mark) sl@0: { sl@0: if (prev_free_atom) sl@0: prev_free_atom->next = temp_free_atom->next; sl@0: else sl@0: mem_chunk->free_atoms = temp_free_atom->next; sl@0: temp_free_atom = temp_free_atom->next; sl@0: sl@0: mem_area->free += mem_chunk->atom_size; sl@0: if (mem_area->free == mem_chunk->area_size) sl@0: { sl@0: mem_chunk->num_mem_areas -= 1; sl@0: mem_chunk->num_marked_areas -= 1; sl@0: sl@0: if (mem_area->next) sl@0: mem_area->next->prev = mem_area->prev; sl@0: if (mem_area->prev) sl@0: mem_area->prev->next = mem_area->next; sl@0: if (mem_area == mem_chunk->mem_areas) sl@0: mem_chunk->mem_areas = mem_chunk->mem_areas->next; sl@0: if (mem_area == mem_chunk->mem_area) sl@0: mem_chunk->mem_area = NULL; sl@0: sl@0: if (mem_chunk->type == G_ALLOC_AND_FREE) sl@0: g_tree_remove (mem_chunk->mem_tree, mem_area); sl@0: g_free (mem_area); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: prev_free_atom = temp_free_atom; sl@0: temp_free_atom = temp_free_atom->next; sl@0: } sl@0: } sl@0: } sl@0: LEAVE_MEM_CHUNK_ROUTINE (); sl@0: } sl@0: sl@0: void sl@0: old_mem_chunk_reset (GMemChunk *mem_chunk) sl@0: { sl@0: GMemArea *mem_areas; sl@0: GMemArea *temp_area; sl@0: sl@0: g_return_if_fail (mem_chunk != NULL); sl@0: sl@0: ENTER_MEM_CHUNK_ROUTINE (); sl@0: sl@0: mem_areas = mem_chunk->mem_areas; sl@0: mem_chunk->num_mem_areas = 0; sl@0: mem_chunk->mem_areas = NULL; sl@0: mem_chunk->mem_area = NULL; sl@0: sl@0: while (mem_areas) sl@0: { sl@0: temp_area = mem_areas; sl@0: mem_areas = mem_areas->next; sl@0: g_free (temp_area); sl@0: } sl@0: sl@0: mem_chunk->free_atoms = NULL; sl@0: sl@0: if (mem_chunk->mem_tree) sl@0: { sl@0: g_tree_destroy (mem_chunk->mem_tree); sl@0: mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare); sl@0: } sl@0: sl@0: LEAVE_MEM_CHUNK_ROUTINE (); sl@0: } sl@0: sl@0: void sl@0: old_mem_chunk_print (GMemChunk *mem_chunk) sl@0: { sl@0: GMemArea *mem_areas; sl@0: gulong mem; sl@0: sl@0: g_return_if_fail (mem_chunk != NULL); sl@0: sl@0: mem_areas = mem_chunk->mem_areas; sl@0: mem = 0; sl@0: sl@0: while (mem_areas) sl@0: { sl@0: mem += mem_chunk->area_size - mem_areas->free; sl@0: mem_areas = mem_areas->next; sl@0: } sl@0: sl@0: g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, sl@0: "%s: %ld bytes using %d mem areas", sl@0: mem_chunk->name, mem, mem_chunk->num_mem_areas); sl@0: } sl@0: sl@0: void sl@0: old_mem_chunk_info (void) sl@0: { sl@0: GMemChunk *mem_chunk; sl@0: gint count; sl@0: sl@0: count = 0; sl@0: g_mutex_lock (mem_chunks_lock); sl@0: mem_chunk = mem_chunks; sl@0: while (mem_chunk) sl@0: { sl@0: count += 1; sl@0: mem_chunk = mem_chunk->next; sl@0: } sl@0: g_mutex_unlock (mem_chunks_lock); sl@0: sl@0: g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%d mem chunks", count); sl@0: sl@0: g_mutex_lock (mem_chunks_lock); sl@0: mem_chunk = mem_chunks; sl@0: g_mutex_unlock (mem_chunks_lock); sl@0: sl@0: while (mem_chunk) sl@0: { sl@0: old_mem_chunk_print ((GMemChunk*) mem_chunk); sl@0: mem_chunk = mem_chunk->next; sl@0: } sl@0: } sl@0: sl@0: static gulong sl@0: old_mem_chunk_compute_size (gulong size, sl@0: gulong min_size) sl@0: { sl@0: gulong power_of_2; sl@0: gulong lower, upper; sl@0: sl@0: power_of_2 = 16; sl@0: while (power_of_2 < size) sl@0: power_of_2 <<= 1; sl@0: sl@0: lower = power_of_2 >> 1; sl@0: upper = power_of_2; sl@0: sl@0: if (size - lower < upper - size && lower >= min_size) sl@0: return lower; sl@0: else sl@0: return upper; sl@0: } sl@0: sl@0: static gint sl@0: old_mem_chunk_area_compare (GMemArea *a, sl@0: GMemArea *b) sl@0: { sl@0: if (a->mem > b->mem) sl@0: return 1; sl@0: else if (a->mem < b->mem) sl@0: return -1; sl@0: return 0; sl@0: } sl@0: sl@0: static gint sl@0: old_mem_chunk_area_search (GMemArea *a, sl@0: gchar *addr) sl@0: { sl@0: if (a->mem <= addr) sl@0: { sl@0: if (addr < &a->mem[a->index]) sl@0: return 0; sl@0: return 1; sl@0: } sl@0: return -1; sl@0: }