/** GSWMultiKeyDictionary.m - <title>GSWeb: Class GSWMultiKeyDictionary</title>

   Copyright (C) 1999-2004 Free Software Foundation, Inc.
  
   Written by:	Manuel Guesdon <mguesdon@orange-concept.com>
   Date: 	Mar 1999

   Partially copied From GSIMap.h 
   	written by Richard Frith-Macdonald <richard@brainstorm.co.uk> 
	based on code written by Albin L. Jones <Albin.L.Jones@Dartmouth.EDU>
   
   $Revision: 21054 $
   $Date: 2005-04-05 10:54:24 -0500 (Tue, 05 Apr 2005) $

   This file is part of the GNUstep Web Library.
   
   <license>
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   </license>
**/

#include "config.h"

RCS_ID("$Id: GSWMultiKeyDictionary.m 21054 2005-04-05 15:54:24Z mguesdon $")

#include "GSWeb.h"

#include <limits.h>

#include <GNUstepBase/GSObjCRuntime.h>

#define DEFAULT_DICTIONARY_CAPACITY 32

// Copied from NSDate.m. We should find a better solution....
static NSTimeInterval GSWTimeNow(void)
{
#if !defined(__MINGW__)
  NSTimeInterval interval;
  struct timeval tp;

  gettimeofday (&tp, NULL);
  interval = -NSTimeIntervalSince1970;
  interval += tp.tv_sec;
  interval += (double)tp.tv_usec / 1000000.0;
  return interval;
#else
  SYSTEMTIME sys_time;
  NSTimeInterval t;
#if 0
  NSCalendarDate *d;

  // Get the system time
  GetLocalTime(&sys_time);

  // Use an NSCalendar object to make it easier
  d = [NSCalendarDate alloc];
  [d initWithYear: sys_time.wYear
     month: sys_time.wMonth
     day: sys_time.wDay
     hour: sys_time.wHour
     minute: sys_time.wMinute
     second: sys_time.wSecond
     timeZone: [NSTimeZone localTimeZone]];
  t = otherTime(d);
  RELEASE(d);
#else
  /*
   * Get current GMT time, convert to NSTimeInterval since reference date,
   */
  GetSystemTime(&sys_time);
  t = GSTime(sys_time.wDay, sys_time.wMonth, sys_time.wYear, sys_time.wHour,
    sys_time.wMinute, sys_time.wSecond, sys_time.wMilliseconds); 
#endif
  return t;
#endif /* __MINGW__ */
}

typedef struct _GSWMapTable GSWMapTable_t;
typedef struct _GSWMapBase GSWMapBase_t;
typedef struct _GSWMapBucket GSWMapBucket_t;
typedef struct _GSWMapNode GSWMapNode_t;
typedef struct _GSWCacheMapNode GSWCacheMapNode_t;

typedef GSWMapTable_t *GSWMapTable;
typedef GSWMapBase_t *GSWMapBase;
typedef GSWMapBucket_t *GSWMapBucket;
typedef GSWMapNode_t *GSWMapNode;
typedef GSWCacheMapNode_t *GSWCacheMapNode;

struct	_GSWMapNode {
  GSWMapNode	nextInBucket;	/* Linked list of bucket.	*/
  id		key;
  id		value;
  GSWMapTable   subMap;		/* SubMap */
};

struct	_GSWCacheMapNode {
  // Should have same first members as _GSWMapNode
  GSWMapNode	nextInBucket;	/* Linked list of bucket.	*/
  id		key;
  id		value;
  GSWMapTable   subMap;		/* SubMap */

  NSTimeInterval firstAccessTS;
  NSTimeInterval lastAccessTS;
  NSTimeInterval cacheDuration;
  unsigned int flags;
};

struct	_GSWMapBucket {
  size_t	nodeCount;	/* Number of nodes in bucket.	*/
  GSWMapNode	firstNode;	/* The linked list of nodes.	*/
};

struct	_GSWMapBase {
  NSZone	*zone;
  unsigned int	nodeSize;	/* Size of a node */
  GSWMapTable	firstTable;

  GSWMapTable	freeTables;	/* List of unused tables.	*/
  size_t	tableChunkCount;	/* Number of chunks in array.	*/
  GSWMapTable	*tableChunks;	/* Chunks of allocated memory.	*/

  GSWMapNode	freeNodes;	/* List of unused nodes.	*/
  size_t	nodeChunkCount;	/* Number of chunks in array.	*/
  GSWMapNode	*nodeChunks;	/* Chunks of allocated memory.	*/
};

struct	_GSWMapTable {
  GSWMapBase	base;
  GSWMapTable   nextTable;
  size_t	nodeCount;	/* Number of nodes in map.	*/
  size_t	bucketCount;	/* Number of buckets in map.	*/
  GSWMapBucket	buckets;	/* Array of buckets.		*/
};

typedef struct	_GSWMapEnumerator {
  GSWMapTable	map;		/* the map being enumerated.	*/
  GSWMapNode	node;		/* The next node to use.	*/
  size_t	bucket;		/* The next bucket to use.	*/
} *_GSIE;

#ifdef	GSI_MAP_ENUMERATOR
typedef GSI_MAP_ENUMERATOR	GSWMapEnumerator_t;
#else
typedef struct _GSWMapEnumerator GSWMapEnumerator_t;
#endif
typedef GSWMapEnumerator_t	*GSWMapEnumerator;

static GSWMapBucket GSWMapPickBucket(unsigned hash, 
                                     GSWMapBucket buckets, 
                                     size_t bucketCount)
{
  return buckets + hash % bucketCount;
}

static GSWMapBucket GSWMapBucketForKey(GSWMapTable map, id key)
{
  /*
    NSDebugFLLog(@"GSWMultiKeyDictionary",
    @"map=%p key=%@",
    map,key);
  */
  return GSWMapPickBucket([key hash],
                          map->buckets, 
                          map->bucketCount);
}

//--------------------------------------------------------------------
static void GSWMapCleanMap(GSWMapTable map);
//--------------------------------------------------------------------
static void GSWMapEmptyMap(GSWMapTable map);



//--------------------------------------------------------------------
static void GSWMapLinkNodeIntoBucket(GSWMapBucket bucket, 
                                     GSWMapNode node)
{
  node->nextInBucket = bucket->firstNode;
  bucket->firstNode = node;
}

//--------------------------------------------------------------------
static void GSWMapUnlinkNodeFromBucket(GSWMapBucket bucket, 
                                       GSWMapNode node)
{
  if (node == bucket->firstNode)
    {
      bucket->firstNode = node->nextInBucket;
    }
  else
    {
      GSWMapNode	tmp = bucket->firstNode;

      while (tmp->nextInBucket != node)
	{
	  tmp = tmp->nextInBucket;
	}
      tmp->nextInBucket = node->nextInBucket;
    }
  node->nextInBucket = 0;
}

//--------------------------------------------------------------------
static void GSWMapAddNodeToBucket(GSWMapBucket bucket, 
                                  GSWMapNode node)
{
  GSWMapLinkNodeIntoBucket(bucket, node);
  bucket->nodeCount += 1;
}

//--------------------------------------------------------------------
static void GSWMapAddNodeToMap(GSWMapTable map, 
                               GSWMapNode node)
{
  GSWMapBucket	bucket;

  bucket = GSWMapBucketForKey(map, node->key);
  GSWMapAddNodeToBucket(bucket, node);
  map->nodeCount++;
}

//--------------------------------------------------------------------
static void GSWMapRemoveNodeFromBucket(GSWMapBucket bucket, 
                                       GSWMapNode node)
{
  bucket->nodeCount--;
  GSWMapUnlinkNodeFromBucket(bucket, node);
}

//--------------------------------------------------------------------
static void GSWMapRemoveNodeFromMap(GSWMapTable map, 
                                    GSWMapBucket bkt, 
                                    GSWMapNode node)
{
  map->nodeCount--;
  GSWMapRemoveNodeFromBucket(bkt, node);
}

//--------------------------------------------------------------------
static void GSWMapRemangleBuckets(GSWMapTable map,
                                  GSWMapBucket old_buckets, 
                                  size_t old_bucketCount,
                                  GSWMapBucket new_buckets, 
                                  size_t new_bucketCount)
{
  while (old_bucketCount-- > 0)
    {
      GSWMapNode	node;

      while ((node = old_buckets->firstNode) != 0)
	{
	  GSWMapBucket	bkt;

	  GSWMapRemoveNodeFromBucket(old_buckets, node);
	  bkt = GSWMapPickBucket([node->key hash],
                                 new_buckets, 
                                 new_bucketCount);
	  GSWMapAddNodeToBucket(bkt, node);
	}
      old_buckets++;
    }
}

//--------------------------------------------------------------------
static void GSWMapMoreNodes(GSWMapTable map, unsigned required)
{
  GSWMapNode	*newArray = NULL;
  size_t	arraySize = (map->base->nodeChunkCount+1)*sizeof(GSWMapNode);

  /*
    NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p required=%u",
               map,required);
  */
#if	GS_WITH_GC == 1
  /*
   * Our nodes may be allocated from the atomic zone - but we don't want
   * them freed - so we must keep the array of pointers to memory chunks in
   * the default zone
   */
  if (map->base->zone == GSAtomicMallocZone())
    {
      newArray = (GSWMapNode*)NSZoneMalloc(NSDefaultMallocZone(), arraySize);
    }
  else
#endif
  newArray = (GSWMapNode*)NSZoneMalloc(map->base->zone, arraySize);
  if (newArray)
    {
      void*		newNodes = NULL;
      size_t		chunkCount;
      size_t		chunkSize;

      memcpy(newArray, map->base->nodeChunks, (map->base->nodeChunkCount)*sizeof(GSWMapNode));
      if (map->base->nodeChunks)
	{
	  NSZoneFree(map->base->zone, map->base->nodeChunks);
	}
      map->base->nodeChunks = newArray;

      if (required == 0)
	{
	  if (map->base->nodeChunkCount == 0)
	    {
	      chunkCount = map->bucketCount > 1 ? map->bucketCount : 2;
	    }
	  else
	    {
	      chunkCount = ((map->nodeCount>>2)+1)<<1;
	    }
	}
      else
	{
	  chunkCount = required;
	}
      /*
      NSDebugFLLog(@"GSWMultiKeyDictionary",
                   @"map=%p map->base=%p required=%u nodeSize=%u sizeof(GSWMapNode_t)=%u",
                   map,map->base,required,map->base->nodeSize,sizeof(GSWMapNode_t));
      */
      NSCAssert2(map->base->nodeSize>=sizeof(GSWMapNode_t),
                 @"Bad node size: %u < %u",
                 map->base->nodeSize,
                 sizeof(GSWMapNode_t));
      chunkSize = chunkCount * map->base->nodeSize;
      newNodes = NSZoneMalloc(map->base->zone, chunkSize);
      if (newNodes)
	{
          memset(newNodes,0,chunkSize);//I HATE unitialized memory !
	  map->base->nodeChunks[map->base->nodeChunkCount++] = newNodes;
          chunkCount--;
	  ((GSWMapNode)(newNodes+(chunkCount*map->base->nodeSize)))->nextInBucket = map->base->freeNodes;
	  while (chunkCount--)
	    {
	      ((GSWMapNode)(newNodes+(chunkCount*map->base->nodeSize)))->nextInBucket = 
                (GSWMapNode)(newNodes+((chunkCount+1)*map->base->nodeSize));
              /*
              NSDebugFLLog(@"GSWMultiKeyDictionary",
                           @"newNodes[chunkCount].nextInBucket node=%p",
                           (GSWMapNode)(newNodes+(chunkCount*map->base->nodeSize)));
              */
	    }
	  map->base->freeNodes = newNodes;
          /*
          NSDebugFLLog(@"GSWMultiKeyDictionary",
                       @"map->base->freeNodes=%p",
                       map->base->freeNodes);
          */
	}
    }
}

//--------------------------------------------------------------------
static void GSWMapResize(GSWMapTable map, size_t new_capacity)
{
  GSWMapBucket	new_buckets = NULL;
  size_t	size = 1;
  size_t	old = 1;

  /*
   *	Find next size up in the fibonacci series
   */
  while (size < new_capacity)
    {
      size_t	tmp = old;

      old = size;
      size += tmp;
    }
  /*
   *	Avoid 8 - since hash functions frequently generate uneven distributions
   *	around powers of two - we don't want lots of keys falling into a single
   *	bucket.
   */
  if (size == 8)
    {
      size++;
    }

  /*
   *	Make a new set of buckets for this map
   */
  new_buckets = (GSWMapBucket)NSZoneCalloc(map->base->zone, size,
                                           sizeof(GSWMapBucket_t));
  if (new_buckets != 0)
    {
      GSWMapRemangleBuckets(map, 
                            map->buckets, 
                            map->bucketCount, new_buckets,
                            size);

      if (map->buckets != 0)
	{
	  NSZoneFree(map->base->zone, map->buckets);
	}
      map->buckets = new_buckets;
      map->bucketCount = size;
    };
}

//--------------------------------------------------------------------
static void GSWMapRightSizeMap(GSWMapTable map, size_t capacity)
{
  /* FIXME: Now, this is a guess, based solely on my intuition.  If anyone
   * knows of a better ratio (or other test, for that matter) and can
   * provide evidence of its goodness, please get in touch with me, Albin
   * L. Jones <Albin.L.Jones@Dartmouth.EDU>. */

  if (3 * capacity >= 4 * map->bucketCount)
    {
      GSWMapResize(map, (3 * capacity)/4 + 1);
    }
}


//--------------------------------------------------------------------
static void GSWMapMoreTables(GSWMapBase base, unsigned required)
{
  GSWMapTable	*newArray = NULL;
  size_t	arraySize = (base->tableChunkCount+1)*sizeof(GSWMapTable);

#if	GS_WITH_GC == 1
  /*
   * Our nodes may be allocated from the atomic zone - but we don't want
   * them freed - so we must keep the array of pointers to memory chunks in
   * the default zone
   */
  if (base->zone == GSAtomicMallocZone())
    {
      newArray = (GSWMapTable*)NSZoneMalloc(NSDefaultMallocZone(), arraySize);
    }
  else
#endif
  newArray = (GSWMapTable*)NSZoneMalloc(base->zone, arraySize);
  if (newArray)
    {
      GSWMapTable	newTables = NULL;
      size_t		chunkCount;
      size_t		chunkSize;

      memcpy(newArray, base->tableChunks, (base->tableChunkCount)*sizeof(GSWMapTable));

      if (base->tableChunks)
	{
	  NSZoneFree(base->zone, base->tableChunks);
	}
      base->tableChunks = newArray;

      if (required == 0)
	{
          chunkCount = 10;
	}
      else
	{
	  chunkCount = required;
	}

      chunkSize = chunkCount * sizeof(GSWMapTable_t);
      newTables = (GSWMapTable)NSZoneMalloc(base->zone, chunkSize);

      if (newTables)
	{
          memset(newTables,0,chunkSize);//I HATE unitialized memory !
	  base->tableChunks[base->tableChunkCount++] = newTables;
	  newTables[--chunkCount].nextTable = base->freeTables;
	  while (chunkCount--)
	    {
	      newTables[chunkCount].nextTable = &newTables[chunkCount+1];
	    }
	  base->freeTables = newTables;
	}
    }
}


//--------------------------------------------------------------------
static GSWMapNode GSWMapNewNode(GSWMapTable map, id key, id value)
{
  GSWMapNode	node = map->base->freeNodes;
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p base=%p key='%@' value=%p node=%p",
               map,map->base,key,value,node);
  */
  if (!node)
    {
      GSWMapMoreNodes(map, 0);
      node = map->base->freeNodes;
      /*
      NSDebugFLLog(@"GSWMultiKeyDictionary",
                   @"map=%p base=%p key='%@' value=%p node=%p",
                   map,map->base,key,value,node);
      */
      if (!node)
        return NULL;
    };
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p base=%p key='%@' value=%p node=%p",
               map,map->base,key,value,node);

  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p base=%p key='%@' value=%p node=%p node->nextInBucket=%p",
               map,map->base,key,value,node,node->nextInBucket);
  */
  map->base->freeNodes = node->nextInBucket;

  ASSIGN(node->key,key);
  ASSIGN(node->value,value);
  node->nextInBucket = NULL;
  node->subMap = NULL;

  return node;
}

//--------------------------------------------------------------------
static GSWMapTable GSWMapNewTableWithCapacity(GSWMapBase base,size_t capacity)
{
  GSWMapTable	table = base->freeTables;

  if (!table)
    {
      GSWMapMoreTables(base, 0);
      table = base->freeTables;
      if (!table)
        return NULL;
    }

  base->freeTables = table->nextTable;
  table->nextTable = NULL;
  table->base=base;

  table->nodeCount = 0;
  table->bucketCount = 0;
  table->buckets = NULL;
  GSWMapRightSizeMap(table, capacity);
  GSWMapMoreNodes(table, capacity);

  return table;
}

//--------------------------------------------------------------------
static void GSWMapFreeNode(GSWMapTable map, GSWMapNode node)
{
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p base=%p node=%p",
               map,map->base,node);
  */
  DESTROY(node->key);
  DESTROY(node->value);
  if (node->subMap)
    {
      GSWMapEmptyMap(node->subMap);
      node->subMap=NULL;
    };

  node->nextInBucket = map->base->freeNodes;
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p base=%p node->nextInBucket node=%p",
               map,map->base,node->nextInBucket);
  */
  map->base->freeNodes = node;
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p base=%p map->base->freeNodes node=%p",
               map,map->base, map->base->freeNodes);
  */
}

//--------------------------------------------------------------------
static void GSWMapFreeTable(GSWMapTable table)
{
  table->nextTable = table->base->freeTables;
  table->base->freeTables = table;
}

//--------------------------------------------------------------------
static GSWMapNode GSWMapNodeForKeyInBucket(GSWMapTable map, 
                                           GSWMapBucket bucket, 
                                           id key)
{
  GSWMapNode	node = bucket->firstNode;

  while (node && ![node->key isEqual:key])
    node = node->nextInBucket;
  return node;
}

//--------------------------------------------------------------------
static GSWMapNode GSWMapNodeForKey(GSWMapTable map, id key)
{
  GSWMapBucket	bucket = NULL;
  GSWMapNode	node = NULL;
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"map=%p key=%@",
               map,key);
  */
  if (map->nodeCount>0)
    {
      bucket = GSWMapBucketForKey(map, key);
      node = GSWMapNodeForKeyInBucket(map, bucket, key);
    };
  return node;
}

//--------------------------------------------------------------------
/** Enumerating **/

/* IMPORTANT WARNING: Enumerators have a wonderous property.
 * Once a node has been returned by `GSWMapEnumeratorNextNode()', it may be
 * removed from the map without effecting the rest of the current
 * enumeration. */

/* EXTREMELY IMPORTANT WARNING: The purpose of this warning is point
 * out that, various (i.e., many) functions currently depend on
 * the behaviour outlined above.  So be prepared for some serious
 * breakage when you go fudging around with these things. */

/**
 * Create an return an enumerator for the specified map.<br />
 * You must call GSWMapEndEnumerator() when you have finished
 * with the enumerator.<br />
 * <strong>WARNING</strong> You should not alter a map while an enumeration
 * is in progress.  The results of doing so are reasonably unpredictable.
 * <br />Remember, DON'T MESS WITH A MAP WHILE YOU'RE ENUMERATING IT.
 */
static GSWMapEnumerator_t GSWMapEnumeratorForMap(GSWMapTable map)
{
  GSWMapEnumerator_t	enumerator;

  enumerator.map = map;
  enumerator.node = 0;
  enumerator.bucket = 0;
  /*
   * Locate next bucket and node to be returned.
   */
  while (enumerator.bucket < map->bucketCount)
    {
      enumerator.node = map->buckets[enumerator.bucket].firstNode;
      if (enumerator.node != 0)
	{
	  break;	// Got first node, and recorded its bucket.
	}
      enumerator.bucket++;
    }

  return enumerator;
}

//--------------------------------------------------------------------
/**
 * Tidies up after map enumeration ... effectively destroys the enumerator.
 */
static void GSWMapEndEnumerator(GSWMapEnumerator enumerator)
{
  ((_GSIE)enumerator)->map = 0;
  ((_GSIE)enumerator)->node = 0;
  ((_GSIE)enumerator)->bucket = 0;
}

//--------------------------------------------------------------------
/**
 * Returns the bucket from which the next node in the enumeration will
 * come.  Once the next node has been enumerated, you can use the
 * bucket and node to remove the node from the map using the
 * GSWMapRemoveNodeFromMap() function.
 */
static GSWMapBucket GSWMapEnumeratorBucket(GSWMapEnumerator enumerator)
{
  if (((_GSIE)enumerator)->node != 0)
    {
      GSWMapTable	map = ((_GSIE)enumerator)->map;

      return &((map->buckets)[((_GSIE)enumerator)->bucket]);
    }
  return 0;
}

//--------------------------------------------------------------------
/**
 * Returns the next node in the map, or a nul pointer if at the end.
 */
static GSWMapNode GSWMapEnumeratorNextNode(GSWMapEnumerator enumerator)
{
  GSWMapNode	node = ((_GSIE)enumerator)->node;

  if (node != 0)
    {
      GSWMapNode	next = node->nextInBucket;

      if (next == 0)
	{
	  GSWMapTable	map = ((_GSIE)enumerator)->map;
	  size_t	bucketCount = map->bucketCount;
	  size_t	bucket = ((_GSIE)enumerator)->bucket;

	  while (next == 0 && ++bucket < bucketCount)
	    {
	      next = (map->buckets[bucket]).firstNode;
	    }
	  ((_GSIE)enumerator)->bucket = bucket;
	}
      ((_GSIE)enumerator)->node = next;
    }
  return node;
}

//--------------------------------------------------------------------
static GSWMapNode GSWMapAddPair(GSWMapTable map, id key, id value)
{
  GSWMapNode node = NULL;
  /*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"key='%@' value=%p",
               key,value);
  */
  node = GSWMapNewNode(map, key, value);
/*
  NSDebugFLLog(@"GSWMultiKeyDictionary",
               @"key='%@' value=%p node=%p",
               key,value,node);
*/
  if (node)
    {
      GSWMapRightSizeMap(map, map->nodeCount);
      GSWMapAddNodeToMap(map, node);
    };
  return node;
}

//--------------------------------------------------------------------
static void GSWMapRemoveKey(GSWMapTable map, id key)
{
  GSWMapBucket	bucket = GSWMapBucketForKey(map, key);
  GSWMapNode	node = NULL;
  
  node = GSWMapNodeForKeyInBucket(map, bucket, key);
  if (node)
    {
      if (node->subMap)
        {
          //Only release Value
          DESTROY(node->value);
        }
      else
        {
          // Remove Node
          GSWMapRemoveNodeFromMap(map, bucket, node);
          GSWMapFreeNode(map, node);
        };
    }
}

//--------------------------------------------------------------------
static void GSWMapMakeObjectsPerformSelector(GSWMapTable map,
                                             SEL selector)
{
  GSWMapEnumerator_t	enumerator = GSWMapEnumeratorForMap(map);
  GSWMapNode	node = GSWMapEnumeratorNextNode(&enumerator);

  while (node)
    {
      [node->value performSelector:selector];
      if (node->subMap)
        GSWMapMakeObjectsPerformSelector(node->subMap,selector);
      node = GSWMapEnumeratorNextNode(&enumerator);
    }
  GSWMapEndEnumerator(&enumerator);
};

//--------------------------------------------------------------------
static void GSWMapMakeObjectsPerformSelectorWithObject(GSWMapTable map,
                                                         SEL selector,
                                                         id object)
{
  GSWMapEnumerator_t	enumerator = GSWMapEnumeratorForMap(map);
  GSWMapNode	node = GSWMapEnumeratorNextNode(&enumerator);

  while (node)
    {
      [node->value performSelector:selector
           withObject:object];
      if (node->subMap)
        GSWMapMakeObjectsPerformSelectorWithObject(node->subMap,
                                                   selector,
                                                   object);
      node = GSWMapEnumeratorNextNode(&enumerator);
    }
  GSWMapEndEnumerator(&enumerator);
};

//--------------------------------------------------------------------
static void GSWMapMakeObjectsPerformSelectorWith2Objects(GSWMapTable map,
                                                         SEL selector,
                                                         id object1,
                                                         id object2)
{
  GSWMapEnumerator_t	enumerator = GSWMapEnumeratorForMap(map);
  GSWMapNode	node = GSWMapEnumeratorNextNode(&enumerator);

  while (node)
    {
      [node->value performSelector:selector
           withObject:object1
           withObject:object2];
      if (node->subMap)
        GSWMapMakeObjectsPerformSelectorWith2Objects(node->subMap,
                                                     selector,
                                                     object1,
                                                     object2);
      node = GSWMapEnumeratorNextNode(&enumerator);
    }
  GSWMapEndEnumerator(&enumerator);
};

//--------------------------------------------------------------------
static void GSWMapAddAllValuesIntoArray(GSWMapTable map,
                                        NSMutableArray* array)
{
  GSWMapEnumerator_t	enumerator = GSWMapEnumeratorForMap(map);
  GSWMapNode	node = GSWMapEnumeratorNextNode(&enumerator);

  while (node)
    {
      if (node->value)
        [array addObject:node->value];
      if (node->subMap)
        GSWMapAddAllValuesIntoArray(node->subMap,
                                    array);
      node = GSWMapEnumeratorNextNode(&enumerator);
    }
  GSWMapEndEnumerator(&enumerator);
};

//--------------------------------------------------------------------
static void GSWMapCleanMap(GSWMapTable map)
{
  if (map->nodeCount > 0)
    {
      GSWMapBucket	bucket = map->buckets;
      unsigned int	i;
      GSWMapNode	startNode = 0;
      GSWMapNode	prevNode = 0;
      GSWMapNode	node = NULL;
      
      map->nodeCount = 0;
      for (i = 0; i < map->bucketCount; i++)
	{
	  node = bucket->firstNode;
	  if (prevNode)
	    {
	      prevNode->nextInBucket = node;
	    }
	  else
	    {
	      startNode = node;
	    }
	  while(node)
	    {
	      DESTROY(node->key);	  
	      DESTROY(node->value);
              if (node->subMap)
                {
                  GSWMapEmptyMap(node->subMap);
                  node->subMap=NULL;
                };
                  
	      prevNode = node;
	      node = node->nextInBucket;
	    }
	  bucket->nodeCount = 0;
	  bucket->firstNode = NULL;
	  bucket++;
	}
      
      prevNode->nextInBucket = map->base->freeNodes;
      map->base->freeNodes = startNode;
    }
}

//Really remove all (before deallocation)
//--------------------------------------------------------------------
static void GSWMapEmptyMap(GSWMapTable map)
{
  GSWMapCleanMap(map);

  if (map->buckets)
    {
      NSZoneFree(map->base->zone, map->buckets);
      map->buckets = NULL;
      map->bucketCount = 0;
    }
  GSWMapFreeTable(map);
}

//--------------------------------------------------------------------
static void GSWMapCleanBase(GSWMapBase base)
{
  GSWMapCleanMap(base->firstTable);
};

//Really remove all (before deallocation)
//--------------------------------------------------------------------
static void GSWMapEmptyBase(GSWMapBase base)
{
  unsigned int	i=0;

  GSWMapEmptyMap(base->firstTable);
  base->firstTable=NULL;

  if (base->nodeChunks != 0)
    {
      for (i = 0; i < base->nodeChunkCount; i++)
	{
	  NSZoneFree(base->zone, base->nodeChunks[i]);
	}
      base->nodeChunkCount = 0;
      NSZoneFree(base->zone, base->nodeChunks);
      base->nodeChunks = NULL;
    }
  base->freeNodes = NULL;

  if (base->tableChunks != 0)
    {
      for (i = 0; i < base->tableChunkCount; i++)
	{
	  NSZoneFree(base->zone, base->tableChunks[i]);
	}
      base->tableChunkCount = 0;
      NSZoneFree(base->zone, base->tableChunks);
      base->tableChunks = NULL;
    }
  base->freeTables = NULL;
  base->zone = NULL;
}


//--------------------------------------------------------------------
static void GSWMapBaseInitWithZoneAndCapacity(GSWMapBase base, 
                                              NSZone *zone, 
                                              size_t capacity)
{
  base->zone = zone;

  base->nodeChunks = NULL;
  base->freeNodes = NULL;
  base->nodeChunkCount = 0;

  base->tableChunks = NULL;
  base->freeTables = NULL;
  base->tableChunkCount = 0;

  base->firstTable=GSWMapNewTableWithCapacity(base,capacity);
}


//==============================================================================
@interface GSWMultiKeyDictionaryObjectEnumerator : NSEnumerator
{
  GSWMultiKeyDictionary* _dictionary;
  NSMutableArray* _objects;
}
@end

//==============================================================================
@interface GSWMultiKeyDictionary(Private)
+(id)dictionaryWithNodeSize:(unsigned int)nodeSize;
-(id)initWithNodeSize:(unsigned int)nodeSize;
-(id)initWithNodeSize:(unsigned int)nodeSize
             capacity:(unsigned int)capacity;

-(void)setObject:(id)object
         forKeys:(id*)keys
           count:(unsigned)count
     returnsNode:(GSWMapNode*)nodePtr;

-(GSWMapNode)nodeForKey:(id)key
                andKeys:(va_list)nextKeys;

-(GSWMapNode)nodeForKeys:(id*)keys
                   count:(unsigned)count;
@end

//==============================================================================
@implementation GSWMultiKeyDictionary : NSObject

//------------------------------------------------------------------------------
+(id)dictionaryWithNodeSize:(unsigned int)nodeSize
{
  NSCAssert2(nodeSize>=sizeof(GSWMapNode_t),
             @"Bad node size: %u < %u",
             nodeSize,
             sizeof(GSWMapNode_t));
  return [[self alloc]initWithNodeSize:nodeSize];
};

//------------------------------------------------------------------------------
+(id)dictionary
{
  return [self dictionaryWithNodeSize:sizeof(GSWMapNode_t)];
};

//------------------------------------------------------------------------------
-(id)initWithNodeSize:(unsigned int)nodeSize
{
  LOGObjectFnStart();
  NSCAssert2(nodeSize>=sizeof(GSWMapNode_t),
             @"Bad node size: %u < %u",
             nodeSize,
             sizeof(GSWMapNode_t));
  self=[self initWithNodeSize:nodeSize
             capacity:DEFAULT_DICTIONARY_CAPACITY];
  LOGObjectFnStop();
  return self;
};

//------------------------------------------------------------------------------
-(id)init
{
  LOGObjectFnStart();
  self=[self initWithNodeSize:sizeof(GSWMapNode_t)];
  LOGObjectFnStop();
  return self;
};

//------------------------------------------------------------------------------
-(id)initWithNodeSize:(unsigned int)nodeSize
             capacity:(unsigned int)capacity
{
  LOGObjectFnStart();
  NSCAssert2(nodeSize>=sizeof(GSWMapNode_t),
             @"Bad node size: %u < %u",
             nodeSize,
             sizeof(GSWMapNode_t));
  if ((self=[super init]))
    {
      _mapBase = (GSWMapBase)NSZoneMalloc([self zone],sizeof(GSWMapBase_t));
      ((GSWMapBase)_mapBase)->nodeSize=nodeSize;
      /*
      NSDebugMLLog(@"GSWMultiKeyDictionary",
                   @"self=%p class=%@ base=%p nodeSize=%u sizeof(GSWMapNode_t)=%u",
                   self,[self class],_mapBase,nodeSize,sizeof(GSWMapNode_t));
      */
      GSWMapBaseInitWithZoneAndCapacity((GSWMapBase)_mapBase, [self zone],
					capacity);
    };
  LOGObjectFnStop();
  return self;
};

//------------------------------------------------------------------------------
-(id)initWithCapacity:(unsigned int)capacity
{
  LOGObjectFnStart();
  [self initWithNodeSize:sizeof(GSWMapNode_t)
        capacity:capacity];
  LOGObjectFnStop();
  return self;
};

//------------------------------------------------------------------------------
- (void) dealloc
{
  if (_mapBase)
    {
      GSWMapEmptyBase((GSWMapBase)_mapBase);
      NSZoneFree([self zone],_mapBase);
    };
  [super dealloc];
}

//------------------------------------------------------------------------------
-(NSString*)description
{
  NSString* descr=nil;
  //TODO
  descr=[NSString stringWithFormat:@"<%s %p>",
				  object_get_class_name(self),
				  (void*)self];
  return descr;
};

//------------------------------------------------------------------------------
-(void)setObject:(id)object
         forKeys:(id)key,...
{
  GS_USEIDLIST(key,[self setObject:object 
                         forKeys:__objects
                         count: __count
                         returnsNode:NULL]); 
};

//------------------------------------------------------------------------------
-(void)setObject:(id)object
    forKeysArray:(NSArray*)keysArray
{
  int keysCount=[keysArray count];
  if (keysCount==0)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to add empty keys array to multi-keys dictionary. Object: %@",
                   object];
    }
  else if (object == nil)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to add nil value to multi-keys dictionary"];
    }
  else
    {
      id keys[keysCount];
      [keysArray getObjects:keys];

      [self setObject:object
            forKeys:keys
            count:keysCount
            returnsNode:NULL];
    };
};

//----------------------------------------------------------------------------------------
-(void)setObject:(id)object
         forKeys:(id*)keys
           count:(unsigned)count
{
  [self setObject:object
        forKeys:keys
        count:count
        returnsNode:NULL];
}

//----------------------------------------------------------------------------------------
-(void)setObject:(id)object
         forKeys:(id*)keys
           count:(unsigned)count
     returnsNode:(GSWMapNode*)nodePtr
{
  if (count==0)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to add object to multi-keys dictionary with no key. Object: %@",
                   object];
    }
  else if (*keys == nil)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to add nil key to multi-keys dictionary. Object: %@",
                   object];
    }
  else if (object == nil)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to add nil value to multi-keys dictionary. First key: %@",
                   *keys];
    }
  else
    {
      int i=0;
      GSWMapTable	currentMap=((GSWMapBase)_mapBase)->firstTable;
      
      for(i=0;i<count;i++)
        {
          id key=keys[i];
          BOOL isNextKey=(i<(count-1) && keys[i+1]);
          GSWMapNode node  = GSWMapNodeForKey(currentMap,key);
          /*
          NSDebugMLLog(@"GSWMultiKeyDictionary",
                       @"key='%@' node=%p node->value=%p isNextKey=%d",
                       key,node,(node ? node->value : NULL),isNextKey);
          */
          if (node)
            {
              if (isNextKey)
                {
                  if (!node->subMap)
                    node->subMap=GSWMapNewTableWithCapacity(currentMap->base,DEFAULT_DICTIONARY_CAPACITY);                
                  currentMap=node->subMap;
                }
              else
                {
                  ASSIGN(node->value,object);
                  if (nodePtr)
                    *nodePtr=node;
                };
            }
          else
            {
              if (isNextKey)
                {
                  node=GSWMapAddPair(currentMap,key,nil);
                  node->subMap=GSWMapNewTableWithCapacity(currentMap->base,DEFAULT_DICTIONARY_CAPACITY);
                  currentMap=node->subMap;
                }
              else
                {
                  GSWMapNode aNode=GSWMapAddPair(currentMap,key,object);
                  if (nodePtr)
                    *nodePtr=aNode;
                };
            };
        };
    };
}

//------------------------------------------------------------------------------
-(id)objectForKeys:(id)key,...
{
  id object=nil;
  /*
  NSDebugMLLog(@"GSWMultiKeyDictionary",
               @"self=%p class=%@ key=%@",
               self,[self class],key);
  */
  GS_USEIDLIST(key,object = [self objectForKeys:__objects
                                  count: __count]); 
  return object;
};

//------------------------------------------------------------------------------
-(id)objectForKeys:(id*)keys
             count:(unsigned)count
{
  id object=nil;

  GSWMapNode node=[self nodeForKeys:keys
                        count:count];

  if (node)
    object=node->value;

  return object;
};

//----------------------------------------------------------------------------------------
-(id)objectForKeysArray:(NSArray*)keysArray
{
  id object=nil;
  int keysCount=[keysArray count];
  if (keysCount>0)
    {
      id keys[keysCount];
      [keysArray getObjects:keys];
      object=[self objectForKeys:keys
                   count:keysCount];
    };
  return object;
}

//----------------------------------------------------------------------------------------
-(GSWMapNode)nodeForKeys:(id*)keys
                   count:(unsigned)count
{
  int i=0;
  GSWMapNode finalNode=NULL;
  GSWMapTable	currentMap=((GSWMapBase)_mapBase)->firstTable;
  for(i=0;i<count;i++)
    {
      id key=keys[i];
      BOOL isNextKey=(i<(count-1) && keys[i+1]);
      GSWMapNode node  = NULL;

      /*
      NSDebugMLLog(@"GSWMultiKeyDictionary",
                   @"key[%d]='%@' isNextKey=%d",
                   i,key,isNextKey);

      NSDebugMLLog(@"GSWMultiKeyDictionary",
                   @"key[%d]=%p isNextKey=%d",
                   i,key,isNextKey);
      */
      node  = GSWMapNodeForKey(currentMap,key);
      /*
      NSDebugMLLog(@"GSWMultiKeyDictionary",
                   @"key[%d]='%@' node=%p node->value=%p isNextKey=%d",
                   i,key,node,(node ? node->value : NULL),isNextKey);
      */
      if (node)
	{
          if (isNextKey)
            {
              currentMap=node->subMap;
              if (!currentMap)
                break;
            }
          else
            finalNode=node;
	}
      else
        break;
    };
/*
  NSDebugMLLog(@"GSWMultiKeyDictionary",@"Object node %sfound: %p",
               (finalNode ? "" : "not "),finalNode);
*/
  return finalNode;
}

//------------------------------------------------------------------------------
-(void)removeAllObjects
{
  GSWMapCleanBase((GSWMapBase)_mapBase);
};

//------------------------------------------------------------------------------
-(void)removeObjectForKeys:(id)key,...
{
  GS_USEIDLIST(key,[self removeObjectForKeys:__objects
                         count: __count]); 
};

//------------------------------------------------------------------------------
-(void)removeObjectForKeysArray:(NSArray*)keysArray
{
  int keysCount=[keysArray count];
  if (keysCount>0)
    {
      id keys[keysCount];
      [keysArray getObjects:keys];
      [self removeObjectForKeys:keys
            count:keysCount];
    };
};

//----------------------------------------------------------------------------------------
-(void)removeObjectForKeys:(id*)keys
                     count:(unsigned)count
{
  int i=0;
  GSWMapTable	currentMap=((GSWMapBase)_mapBase)->firstTable;
  for(i=0;i<count;i++)
    {
      id key=keys[i];
      BOOL isNextKey=(i<(count-1) && keys[i+1]);
      GSWMapNode node  = GSWMapNodeForKey(currentMap,key);
      /*
      NSDebugMLLog(@"GSWMultiKeyDictionary",
                   @"key='%@' node=%p node->value=%p isNextKey=%d",
                   key,node,(node ? node->value : NULL),isNextKey);
*/
      if (node)
	{
          if (isNextKey)
            {
              currentMap=node->subMap;
              if (!currentMap)
                break;
            }
          else
            {
              GSWMapRemoveKey(currentMap,key);
            };
	}
      else
        break;
    };
}

//------------------------------------------------------------------------------
-(void)removeAllSubObjectsForKeys:(id)key,...
{
  GS_USEIDLIST(key,[self removeAllSubObjectsForKeys:__objects
                         count: __count]); 
};

//------------------------------------------------------------------------------
-(void)removeAllSubObjectsForKeys:(id*)keys
                            count:(unsigned)count
{
  GSWMapNode node=[self nodeForKeys:keys
                        count:count];

  if (node)
    {
      if (node->subMap)
        GSWMapCleanMap(node->subMap);
      [self removeObjectForKeys:keys
            count:count];
    };
}

//------------------------------------------------------------------------------
-(void)removeAllSubObjectsForKeysArray:(NSArray*)keysArray
{
  int keysCount=[keysArray count];
  if (keysCount>0)
    {
      id keys[keysCount];
      [keysArray getObjects:keys];
      [self removeAllSubObjectsForKeys:keys
            count:keysCount];
    };
}

//------------------------------------------------------------------------------
-(void)makeObjectsPerformSelector:(SEL)selector
{
  GSWMapMakeObjectsPerformSelector(((GSWMapBase)_mapBase)->firstTable,selector);
};

//------------------------------------------------------------------------------
-(void)makeObjectsPerformSelector:(SEL)selector
                       withObject:(id)object
{
  GSWMapMakeObjectsPerformSelectorWithObject(((GSWMapBase)_mapBase)->firstTable,selector,object);
};

//------------------------------------------------------------------------------
-(void)makeObjectsPerformSelector:(SEL)selector
                       withObject:(id)object1
                       withObject:(id)object2
{
  GSWMapMakeObjectsPerformSelectorWith2Objects(((GSWMapBase)_mapBase)->firstTable,
                                               selector,
                                               object1,
                                               object2);
};

//------------------------------------------------------------------------------
- (NSEnumerator*) objectEnumerator
{
  return AUTORELEASE([[GSWMultiKeyDictionaryObjectEnumerator 
                        allocWithZone:NSDefaultMallocZone()] 
                       initWithDictionary: (NSDictionary*)self]);
}

//------------------------------------------------------------------------------
-(NSArray*)allValues
{
  NSMutableArray* objects=(NSMutableArray*)[NSMutableArray array];
  GSWMapAddAllValuesIntoArray(((GSWMapBase)_mapBase)->firstTable,
                              objects);
  return objects;
};

//------------------------------------------------------------------------------
-(NSArray*)allSubValuesForKeys:(id)key,...
{
  NSArray* values=nil;
  /*
  NSDebugMLLog(@"GSWMultiKeyDictionary",
               @"self=%p class=%@ key=%@",
               self,[self class],key);
  */
  GS_USEIDLIST(key,values = [self allSubValuesForKeys:__objects
                                  count: __count]); 
  return values;
};

//------------------------------------------------------------------------------
-(NSArray*)allSubValuesForKeys:(id*)keys
                     count:(unsigned)count
{
  NSMutableArray* objects=nil;
  GSWMapNode node=[self nodeForKeys:keys
                        count:count];
  if (node)
    {
      NSMutableArray* objects=(NSMutableArray*)[NSMutableArray array];
      if (node->value)
        [objects addObject:node->value];
      if (node->subMap)
        GSWMapAddAllValuesIntoArray(node->subMap,
                                    objects);
    };
  return objects;
}

//------------------------------------------------------------------------------
-(NSArray*)allSubValuesForKeysArray:(NSArray*)keysArray
{
  NSArray* objects=nil;
  int keysCount=[keysArray count];
  if (keysCount>0)
    {
      id keys[keysCount];
      [keysArray getObjects:keys];
      objects=[self allSubValuesForKeys:keys
                    count:keysCount];
    };
  return objects;
}
@end

//==============================================================================
@implementation GSWMultiKeyDictionaryObjectEnumerator

//------------------------------------------------------------------------------
-(id)initWithDictionary:(GSWMultiKeyDictionary*)d
{
  if ((self=[super init]))
    {
      ASSIGN(_dictionary,((GSWMultiKeyDictionary*)d));
      ASSIGN(_objects,[_dictionary allValues]);
    };
  return self;
}

//------------------------------------------------------------------------------
-(void)dealloc
{
  DESTROY(_dictionary);
  DESTROY(_objects);
  [super dealloc];
}

//------------------------------------------------------------------------------
-(id)nextObject
{
  id object=nil;
  if ([_objects count]>0)
    {
      object=[_objects lastObject];
      AUTORELEASE(RETAIN(object));
      [_objects removeLastObject];
    };
  return object;
}

@end

//==============================================================================
@implementation GSWCache

//------------------------------------------------------------------------------
static BOOL isNodeExpired(GSWCacheMapNode node)
{
  if ((node->flags & GSWCacheFlags_expiresOnFirstAccess)==GSWCacheFlags_expiresOnFirstAccess)
    {
      return (node->cacheDuration>0 && (GSWTimeNow()-node->firstAccessTS)>node->cacheDuration);
    }
  else
    {
      return (node->cacheDuration>0 && (GSWTimeNow()-node->lastAccessTS)>node->cacheDuration);
    };
};

//--------------------------------------------------------------------
static void removeExpiredNodes(GSWMapTable map)
{
  GSWMapEnumerator_t	enumerator = GSWMapEnumeratorForMap(map);
  GSWMapBucket  bucket=GSWMapEnumeratorBucket(&enumerator);
  GSWCacheMapNode	node = (GSWCacheMapNode)GSWMapEnumeratorNextNode(&enumerator);
  while (node)
    {
      if (isNodeExpired(node))
        {
          if (node->subMap && node->subMap->nodeCount>0)
            {
              //Only release Value
              DESTROY(node->value);
            }
          else
            {
              // Remove Node
              GSWMapRemoveNodeFromMap(map,bucket, (GSWMapNode)node);
              GSWMapFreeNode(map, (GSWMapNode)node);
            };
        };
      if (node->subMap)
        {
          removeExpiredNodes(node->subMap);
          if (node->subMap->nodeCount==0)
            {
              GSWMapEmptyMap(node->subMap);
              node->subMap=NULL;
            };
        };
      bucket=GSWMapEnumeratorBucket(&enumerator);
      node = (GSWCacheMapNode)GSWMapEnumeratorNextNode(&enumerator);
    }
  GSWMapEndEnumerator(&enumerator);
};

//------------------------------------------------------------------------------
- (id)init
{
  if ((self = [self initWithDefaultDuration:-1 //never expires
                    defaultFlags:0]))
    {
    };
  return self;
}

//------------------------------------------------------------------------------
- (id)initWithDefaultDuration:(NSTimeInterval)defaultDuration
                 defaultFlags:(unsigned int)defaultFlags
{
  if ((self = [self initWithNodeSize:sizeof(GSWCacheMapNode_t)]))
    {
      _defaultDuration=defaultDuration;
      _defaultFlags=defaultFlags;
    };
  return self;
}

//------------------------------------------------------------------------------
- (void) dealloc
{
  [super dealloc];
}

//------------------------------------------------------------------------------
-(NSString*)description
{
  return [super description];  
};

//------------------------------------------------------------------------------
+(GSWCache*)cache
{
  return [[[GSWCache alloc] init] autorelease];
};

//------------------------------------------------------------------------------
+(GSWCache*)cacheWithDefaultDuration:(NSTimeInterval)defaultDuration
                        defaultFlags:(unsigned int)defaultFlags
{
  return [[[GSWCache alloc] initWithDefaultDuration:defaultDuration
                               defaultFlags:defaultFlags] autorelease];
};

//----------------------------------------------------------------------------------------
-(void)deleteExpiredEntries
{
  removeExpiredNodes(((GSWMapBase)_mapBase)->firstTable);
};

//------------------------------------------------------------------------------
-(GSWMapNode)nodeForKeys:(id*)keys
                   count:(unsigned)count
{
  GSWCacheMapNode node=(GSWCacheMapNode)[super nodeForKeys:keys
                                               count:count];
  if (node)
    {
      if (isNodeExpired(node))
        {
          NSDebugMLog(@"Node for object %p EXPIRED",node->value);
          [self removeObjectForKeys:keys
                count:count];
        }
      else
        {
          node->lastAccessTS=GSWTimeNow();
        };      
    };
  return (GSWMapNode)node;
};

//------------------------------------------------------------------------------
-(void)setObject:(id)object
         forKeys:(id*)keys
           count:(unsigned)count
     returnsNode:(GSWMapNode*)nodePtr
{
  GSWCacheMapNode aNode=NULL;
  [super setObject:object
         forKeys:keys
         count:count
         returnsNode:(GSWMapNode*)&aNode];
  if (aNode)
    {
      aNode->firstAccessTS=GSWTimeNow();
      aNode->lastAccessTS=aNode->firstAccessTS;
      aNode->cacheDuration=_defaultDuration;
      aNode->flags=_defaultFlags;
    };
  if (nodePtr)
    *nodePtr=(GSWMapNode)aNode;
}

//----------------------------------------------------------------------------------------
-(void)setObject:(id)object
    withDuration:(NSTimeInterval)duration
          forKey:(id)key
{
  [self setObject:object
        withDuration:duration
        forKeys:&key
        count:1];
}

//----------------------------------------------------------------------------------------
-(void)setObject:(id)object
    withDuration:(NSTimeInterval)duration
         forKeys:(id*)keys
           count:(unsigned)count
{
  GSWCacheMapNode aNode=NULL;
  [self setObject:object
        forKeys:keys
        count:count
        returnsNode:(GSWMapNode*)&aNode];
  if (aNode)
    {
      aNode->cacheDuration=duration;
    };
};

//----------------------------------------------------------------------------------------
-(void)setObject:(id)object
    withDuration:(NSTimeInterval)duration
         forKeys:(id)key,...
{
  GS_USEIDLIST(key,[self setObject:object 
                         withDuration:duration
                         forKeys:__objects
                         count: __count]); 
};


@end

