mirror of
https://github.com/opencv/opencv.git
synced 2024-12-29 20:48:39 +08:00
604 lines
18 KiB
C
604 lines
18 KiB
C
|
/**********************************************************************
|
||
|
*
|
||
|
* Name: tif_hash_set.c
|
||
|
* Purpose: Hash set functions.
|
||
|
* Author: Even Rouault, <even dot rouault at spatialys.com>
|
||
|
*
|
||
|
**********************************************************************
|
||
|
* Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included
|
||
|
* in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
* DEALINGS IN THE SOFTWARE.
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "tif_config.h"
|
||
|
|
||
|
#include "tif_hash_set.h"
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
/** List element structure. */
|
||
|
typedef struct _TIFFList TIFFList;
|
||
|
|
||
|
/** List element structure. */
|
||
|
struct _TIFFList
|
||
|
{
|
||
|
/*! Pointer to the data object. Should be allocated and freed by the
|
||
|
* caller.
|
||
|
* */
|
||
|
void *pData;
|
||
|
/*! Pointer to the next element in list. NULL, if current element is the
|
||
|
* last one.
|
||
|
*/
|
||
|
struct _TIFFList *psNext;
|
||
|
};
|
||
|
|
||
|
struct _TIFFHashSet
|
||
|
{
|
||
|
TIFFHashSetHashFunc fnHashFunc;
|
||
|
TIFFHashSetEqualFunc fnEqualFunc;
|
||
|
TIFFHashSetFreeEltFunc fnFreeEltFunc;
|
||
|
TIFFList **tabList;
|
||
|
int nSize;
|
||
|
int nIndiceAllocatedSize;
|
||
|
int nAllocatedSize;
|
||
|
TIFFList *psRecyclingList;
|
||
|
int nRecyclingListSize;
|
||
|
bool bRehash;
|
||
|
#ifdef HASH_DEBUG
|
||
|
int nCollisions;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static const int anPrimes[] = {
|
||
|
53, 97, 193, 389, 769, 1543, 3079,
|
||
|
6151, 12289, 24593, 49157, 98317, 196613, 393241,
|
||
|
786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653,
|
||
|
100663319, 201326611, 402653189, 805306457, 1610612741};
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetHashPointer() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Hash function for an arbitrary pointer
|
||
|
*
|
||
|
* @param elt the arbitrary pointer to hash
|
||
|
*
|
||
|
* @return the hash value of the pointer
|
||
|
*/
|
||
|
|
||
|
static unsigned long TIFFHashSetHashPointer(const void *elt)
|
||
|
{
|
||
|
return (unsigned long)(uintptr_t)((void *)(elt));
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetEqualPointer() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Equality function for arbitrary pointers
|
||
|
*
|
||
|
* @param elt1 the first arbitrary pointer to compare
|
||
|
* @param elt2 the second arbitrary pointer to compare
|
||
|
*
|
||
|
* @return true if the pointers are equal
|
||
|
*/
|
||
|
|
||
|
static bool TIFFHashSetEqualPointer(const void *elt1, const void *elt2)
|
||
|
{
|
||
|
return elt1 == elt2;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetNew() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Creates a new hash set
|
||
|
*
|
||
|
* The hash function must return a hash value for the elements to insert.
|
||
|
* If fnHashFunc is NULL, TIFFHashSetHashPointer will be used.
|
||
|
*
|
||
|
* The equal function must return if two elements are equal.
|
||
|
* If fnEqualFunc is NULL, TIFFHashSetEqualPointer will be used.
|
||
|
*
|
||
|
* The free function is used to free elements inserted in the hash set,
|
||
|
* when the hash set is destroyed, when elements are removed or replaced.
|
||
|
* If fnFreeEltFunc is NULL, elements inserted into the hash set will not be
|
||
|
* freed.
|
||
|
*
|
||
|
* @param fnHashFunc hash function. May be NULL.
|
||
|
* @param fnEqualFunc equal function. May be NULL.
|
||
|
* @param fnFreeEltFunc element free function. May be NULL.
|
||
|
*
|
||
|
* @return a new hash set
|
||
|
*/
|
||
|
|
||
|
TIFFHashSet *TIFFHashSetNew(TIFFHashSetHashFunc fnHashFunc,
|
||
|
TIFFHashSetEqualFunc fnEqualFunc,
|
||
|
TIFFHashSetFreeEltFunc fnFreeEltFunc)
|
||
|
{
|
||
|
TIFFHashSet *set = (TIFFHashSet *)malloc(sizeof(TIFFHashSet));
|
||
|
if (set == NULL)
|
||
|
return NULL;
|
||
|
set->fnHashFunc = fnHashFunc ? fnHashFunc : TIFFHashSetHashPointer;
|
||
|
set->fnEqualFunc = fnEqualFunc ? fnEqualFunc : TIFFHashSetEqualPointer;
|
||
|
set->fnFreeEltFunc = fnFreeEltFunc;
|
||
|
set->nSize = 0;
|
||
|
set->tabList = (TIFFList **)(calloc(sizeof(TIFFList *), 53));
|
||
|
if (set->tabList == NULL)
|
||
|
{
|
||
|
free(set);
|
||
|
return NULL;
|
||
|
}
|
||
|
set->nIndiceAllocatedSize = 0;
|
||
|
set->nAllocatedSize = 53;
|
||
|
set->psRecyclingList = NULL;
|
||
|
set->nRecyclingListSize = 0;
|
||
|
set->bRehash = false;
|
||
|
#ifdef HASH_DEBUG
|
||
|
set->nCollisions = 0;
|
||
|
#endif
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetSize() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Returns the number of elements inserted in the hash set
|
||
|
*
|
||
|
* Note: this is not the internal size of the hash set
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
*
|
||
|
* @return the number of elements in the hash set
|
||
|
*/
|
||
|
|
||
|
int TIFFHashSetSize(const TIFFHashSet *set)
|
||
|
{
|
||
|
assert(set != NULL);
|
||
|
return set->nSize;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetGetNewListElt() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
static TIFFList *TIFFHashSetGetNewListElt(TIFFHashSet *set)
|
||
|
{
|
||
|
if (set->psRecyclingList)
|
||
|
{
|
||
|
TIFFList *psRet = set->psRecyclingList;
|
||
|
psRet->pData = NULL;
|
||
|
set->nRecyclingListSize--;
|
||
|
set->psRecyclingList = psRet->psNext;
|
||
|
return psRet;
|
||
|
}
|
||
|
|
||
|
return (TIFFList *)malloc(sizeof(TIFFList));
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetReturnListElt() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
static void TIFFHashSetReturnListElt(TIFFHashSet *set, TIFFList *psList)
|
||
|
{
|
||
|
if (set->nRecyclingListSize < 128)
|
||
|
{
|
||
|
psList->psNext = set->psRecyclingList;
|
||
|
set->psRecyclingList = psList;
|
||
|
set->nRecyclingListSize++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free(psList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetClearInternal() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
static void TIFFHashSetClearInternal(TIFFHashSet *set, bool bFinalize)
|
||
|
{
|
||
|
assert(set != NULL);
|
||
|
for (int i = 0; i < set->nAllocatedSize; i++)
|
||
|
{
|
||
|
TIFFList *cur = set->tabList[i];
|
||
|
while (cur)
|
||
|
{
|
||
|
if (set->fnFreeEltFunc)
|
||
|
set->fnFreeEltFunc(cur->pData);
|
||
|
TIFFList *psNext = cur->psNext;
|
||
|
if (bFinalize)
|
||
|
free(cur);
|
||
|
else
|
||
|
TIFFHashSetReturnListElt(set, cur);
|
||
|
cur = psNext;
|
||
|
}
|
||
|
set->tabList[i] = NULL;
|
||
|
}
|
||
|
set->bRehash = false;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFListDestroy() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Destroy a list. Caller responsible for freeing data objects contained in
|
||
|
* list elements.
|
||
|
*
|
||
|
* @param psList pointer to list head.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static void TIFFListDestroy(TIFFList *psList)
|
||
|
{
|
||
|
TIFFList *psCurrent = psList;
|
||
|
|
||
|
while (psCurrent)
|
||
|
{
|
||
|
TIFFList *const psNext = psCurrent->psNext;
|
||
|
free(psCurrent);
|
||
|
psCurrent = psNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetDestroy() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Destroys an allocated hash set.
|
||
|
*
|
||
|
* This function also frees the elements if a free function was
|
||
|
* provided at the creation of the hash set.
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
*/
|
||
|
|
||
|
void TIFFHashSetDestroy(TIFFHashSet *set)
|
||
|
{
|
||
|
if (set)
|
||
|
{
|
||
|
TIFFHashSetClearInternal(set, true);
|
||
|
free(set->tabList);
|
||
|
TIFFListDestroy(set->psRecyclingList);
|
||
|
free(set);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef notused
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetClear() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Clear all elements from a hash set.
|
||
|
*
|
||
|
* This function also frees the elements if a free function was
|
||
|
* provided at the creation of the hash set.
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
*/
|
||
|
|
||
|
void TIFFHashSetClear(TIFFHashSet *set)
|
||
|
{
|
||
|
TIFFHashSetClearInternal(set, false);
|
||
|
set->nIndiceAllocatedSize = 0;
|
||
|
set->nAllocatedSize = 53;
|
||
|
#ifdef HASH_DEBUG
|
||
|
set->nCollisions = 0;
|
||
|
#endif
|
||
|
set->nSize = 0;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetForeach() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Walk through the hash set and runs the provided function on all the
|
||
|
* elements
|
||
|
*
|
||
|
* This function is provided the user_data argument of TIFFHashSetForeach.
|
||
|
* It must return true to go on the walk through the hash set, or FALSE to
|
||
|
* make it stop.
|
||
|
*
|
||
|
* Note : the structure of the hash set must *NOT* be modified during the
|
||
|
* walk.
|
||
|
*
|
||
|
* @param set the hash set.
|
||
|
* @param fnIterFunc the function called on each element.
|
||
|
* @param user_data the user data provided to the function.
|
||
|
*/
|
||
|
|
||
|
void TIFFHashSetForeach(TIFFHashSet *set, TIFFHashSetIterEltFunc fnIterFunc,
|
||
|
void *user_data)
|
||
|
{
|
||
|
assert(set != NULL);
|
||
|
if (!fnIterFunc)
|
||
|
return;
|
||
|
|
||
|
for (int i = 0; i < set->nAllocatedSize; i++)
|
||
|
{
|
||
|
TIFFList *cur = set->tabList[i];
|
||
|
while (cur)
|
||
|
{
|
||
|
if (!fnIterFunc(cur->pData, user_data))
|
||
|
return;
|
||
|
|
||
|
cur = cur->psNext;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetRehash() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
static bool TIFFHashSetRehash(TIFFHashSet *set)
|
||
|
{
|
||
|
int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize];
|
||
|
TIFFList **newTabList =
|
||
|
(TIFFList **)(calloc(sizeof(TIFFList *), nNewAllocatedSize));
|
||
|
if (newTabList == NULL)
|
||
|
return false;
|
||
|
#ifdef HASH_DEBUG
|
||
|
TIFFDebug("TIFFHASH",
|
||
|
"hashSet=%p, nSize=%d, nCollisions=%d, "
|
||
|
"fCollisionRate=%.02f",
|
||
|
set, set->nSize, set->nCollisions,
|
||
|
set->nCollisions * 100.0 / set->nSize);
|
||
|
set->nCollisions = 0;
|
||
|
#endif
|
||
|
for (int i = 0; i < set->nAllocatedSize; i++)
|
||
|
{
|
||
|
TIFFList *cur = set->tabList[i];
|
||
|
while (cur)
|
||
|
{
|
||
|
const unsigned long nNewHashVal =
|
||
|
set->fnHashFunc(cur->pData) % nNewAllocatedSize;
|
||
|
#ifdef HASH_DEBUG
|
||
|
if (newTabList[nNewHashVal])
|
||
|
set->nCollisions++;
|
||
|
#endif
|
||
|
TIFFList *psNext = cur->psNext;
|
||
|
cur->psNext = newTabList[nNewHashVal];
|
||
|
newTabList[nNewHashVal] = cur;
|
||
|
cur = psNext;
|
||
|
}
|
||
|
}
|
||
|
free(set->tabList);
|
||
|
set->tabList = newTabList;
|
||
|
set->nAllocatedSize = nNewAllocatedSize;
|
||
|
set->bRehash = false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetFindPtr() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
static void **TIFFHashSetFindPtr(TIFFHashSet *set, const void *elt)
|
||
|
{
|
||
|
const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
|
||
|
TIFFList *cur = set->tabList[nHashVal];
|
||
|
while (cur)
|
||
|
{
|
||
|
if (set->fnEqualFunc(cur->pData, elt))
|
||
|
return &cur->pData;
|
||
|
cur = cur->psNext;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetInsert() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Inserts an element into a hash set.
|
||
|
*
|
||
|
* If the element was already inserted in the hash set, the previous
|
||
|
* element is replaced by the new element. If a free function was provided,
|
||
|
* it is used to free the previously inserted element
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
* @param elt the new element to insert in the hash set
|
||
|
*
|
||
|
* @return true if success. If false is returned, elt has not been inserted,
|
||
|
* but TIFFHashSetInsert() will have run the free function if provided.
|
||
|
*/
|
||
|
|
||
|
bool TIFFHashSetInsert(TIFFHashSet *set, void *elt)
|
||
|
{
|
||
|
assert(set != NULL);
|
||
|
void **pElt = TIFFHashSetFindPtr(set, elt);
|
||
|
if (pElt)
|
||
|
{
|
||
|
if (set->fnFreeEltFunc)
|
||
|
set->fnFreeEltFunc(*pElt);
|
||
|
|
||
|
*pElt = elt;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (set->nSize >= 2 * set->nAllocatedSize / 3 ||
|
||
|
(set->bRehash && set->nIndiceAllocatedSize > 0 &&
|
||
|
set->nSize <= set->nAllocatedSize / 2))
|
||
|
{
|
||
|
set->nIndiceAllocatedSize++;
|
||
|
if (!TIFFHashSetRehash(set))
|
||
|
{
|
||
|
set->nIndiceAllocatedSize--;
|
||
|
if (set->fnFreeEltFunc)
|
||
|
set->fnFreeEltFunc(elt);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
|
||
|
#ifdef HASH_DEBUG
|
||
|
if (set->tabList[nHashVal])
|
||
|
set->nCollisions++;
|
||
|
#endif
|
||
|
|
||
|
TIFFList *new_elt = TIFFHashSetGetNewListElt(set);
|
||
|
if (new_elt == NULL)
|
||
|
{
|
||
|
if (set->fnFreeEltFunc)
|
||
|
set->fnFreeEltFunc(elt);
|
||
|
return false;
|
||
|
}
|
||
|
new_elt->pData = elt;
|
||
|
new_elt->psNext = set->tabList[nHashVal];
|
||
|
set->tabList[nHashVal] = new_elt;
|
||
|
set->nSize++;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetLookup() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Returns the element found in the hash set corresponding to the element to
|
||
|
* look up The element must not be modified.
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
* @param elt the element to look up in the hash set
|
||
|
*
|
||
|
* @return the element found in the hash set or NULL
|
||
|
*/
|
||
|
|
||
|
void *TIFFHashSetLookup(TIFFHashSet *set, const void *elt)
|
||
|
{
|
||
|
assert(set != NULL);
|
||
|
void **pElt = TIFFHashSetFindPtr(set, elt);
|
||
|
if (pElt)
|
||
|
return *pElt;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetRemoveInternal() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
static bool TIFFHashSetRemoveInternal(TIFFHashSet *set, const void *elt,
|
||
|
bool bDeferRehash)
|
||
|
{
|
||
|
assert(set != NULL);
|
||
|
if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2)
|
||
|
{
|
||
|
set->nIndiceAllocatedSize--;
|
||
|
if (bDeferRehash)
|
||
|
set->bRehash = true;
|
||
|
else
|
||
|
{
|
||
|
if (!TIFFHashSetRehash(set))
|
||
|
{
|
||
|
set->nIndiceAllocatedSize++;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int nHashVal = (int)(set->fnHashFunc(elt) % set->nAllocatedSize);
|
||
|
TIFFList *cur = set->tabList[nHashVal];
|
||
|
TIFFList *prev = NULL;
|
||
|
while (cur)
|
||
|
{
|
||
|
if (set->fnEqualFunc(cur->pData, elt))
|
||
|
{
|
||
|
if (prev)
|
||
|
prev->psNext = cur->psNext;
|
||
|
else
|
||
|
set->tabList[nHashVal] = cur->psNext;
|
||
|
|
||
|
if (set->fnFreeEltFunc)
|
||
|
set->fnFreeEltFunc(cur->pData);
|
||
|
|
||
|
TIFFHashSetReturnListElt(set, cur);
|
||
|
#ifdef HASH_DEBUG
|
||
|
if (set->tabList[nHashVal])
|
||
|
set->nCollisions--;
|
||
|
#endif
|
||
|
set->nSize--;
|
||
|
return true;
|
||
|
}
|
||
|
prev = cur;
|
||
|
cur = cur->psNext;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetRemove() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Removes an element from a hash set
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
* @param elt the new element to remove from the hash set
|
||
|
*
|
||
|
* @return true if the element was in the hash set
|
||
|
*/
|
||
|
|
||
|
bool TIFFHashSetRemove(TIFFHashSet *set, const void *elt)
|
||
|
{
|
||
|
return TIFFHashSetRemoveInternal(set, elt, false);
|
||
|
}
|
||
|
|
||
|
#ifdef notused
|
||
|
/************************************************************************/
|
||
|
/* TIFFHashSetRemoveDeferRehash() */
|
||
|
/************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Removes an element from a hash set.
|
||
|
*
|
||
|
* This will defer potential rehashing of the set to later calls to
|
||
|
* TIFFHashSetInsert() or TIFFHashSetRemove().
|
||
|
*
|
||
|
* @param set the hash set
|
||
|
* @param elt the new element to remove from the hash set
|
||
|
*
|
||
|
* @return true if the element was in the hash set
|
||
|
*/
|
||
|
|
||
|
bool TIFFHashSetRemoveDeferRehash(TIFFHashSet *set, const void *elt)
|
||
|
{
|
||
|
return TIFFHashSetRemoveInternal(set, elt, true);
|
||
|
}
|
||
|
#endif
|