/****************************************************************************
* *
* * This file is part of GAP , a system for computational discrete algebra .
* *
* * Copyright of GAP belongs to its developers , whose names are too numerous
* * to list here . Please refer to the COPYRIGHT file for details .
* *
* * SPDX - License - Identifier : GPL - 2 . 0 - or - later
* *
* * This file contains the GAP interface for thread primitives .
*/
#include "objset.h"
#include "bool.h"
#include "error.h"
#include "fibhash.h"
#include "gaputils.h"
#include "gvars.h"
#include "io.h"
#include "lists.h"
#include "modules.h"
#include "plist.h"
#include "saveload.h"
#ifdef HPCGAP
#include "hpc/traverse.h"
#endif
static Obj TYPE_OBJSET;
static Obj TYPE_OBJMAP;
static Obj TypeObjSet(Obj obj)
{
return TYPE_OBJSET;
}
static Obj TypeObjMap(Obj obj)
{
return TYPE_OBJMAP;
}
static inline BOOL IS_OBJSET(Obj obj)
{
UInt tnum = TNUM_OBJ(obj);
return tnum == T_OBJSET || tnum == T_OBJSET + IMMUTABLE;
}
static inline BOOL IS_OBJMAP(Obj obj)
{
UInt tnum = TNUM_OBJ(obj);
return tnum == T_OBJMAP || tnum == T_OBJMAP + IMMUTABLE;
}
/** Object sets and maps --------------------
*
* Object sets and maps are hash tables where identity is determined
* according to the IsIdenticalObj ( ) relation . They are primarily intended
* for code that needs to traverse object structures in order to remember if
* an object has already been seen by the traversal algorithm . They can also
* be used as sparse lists with integer keys and as proper sets and maps for
* short integers and small finite field elements .
*
* Object sets and object maps consist of four header words describing the
* map layout , followed by a list of entries containing the actual set / map
* data . The size of that list is always a power of 2 .
*
* The first header word contains the size of the data list , the second
* header word contains the base 2 logarithm of that size , the third word
* contains the number of entries actually in use , and the fourth word
* contains the number of deleted but not reused entries .
*
* Entries in the data list comprise either a single GAP object for sets or a
* ( key , value ) pair of GAP objects for maps .
*
* Unused entries contain a null pointer ; deleted entries for sets and the
* keys of deleted entries for sets contain the special boolean value
* ` Undefined ` . Values of deleted entries contain a null pointer .
*/
enum {
DEFAULT_OBJSET_BITS = 2 ,
DEFAULT_OBJSET_SIZE = (1 << DEFAULT_OBJSET_BITS),
};
static inline UInt * ADDR_WORD(Obj obj)
{
return ((UInt *)(ADDR_OBJ(obj)));
}
static inline const UInt * CONST_ADDR_WORD(Obj obj)
{
return ((const UInt *)(CONST_ADDR_OBJ(obj)));
}
static inline Obj READ_SLOT(Obj container, int slot)
{
#ifdef GAP_KERNEL_DEBUG
GAP_ASSERT(slot >= 0 );
UInt size = CONST_ADDR_WORD(container)[OBJSET_SIZE];
if (IS_OBJMAP(container))
size *= 2 ;
GAP_ASSERT(slot < size);
GAP_ASSERT((OBJSET_HDRSIZE + size)*sizeof (Obj) == SIZE_BAG(container));
#endif
return CONST_ADDR_OBJ(container)[OBJSET_HDRSIZE + slot];
}
static inline void WRITE_SLOT(Obj container, int slot, Obj elm)
{
#ifdef GAP_KERNEL_DEBUG
GAP_ASSERT(slot >= 0 );
UInt size = CONST_ADDR_WORD(container)[OBJSET_SIZE];
if (IS_OBJMAP(container))
size *= 2 ;
GAP_ASSERT(slot < size);
GAP_ASSERT((OBJSET_HDRSIZE + size)*sizeof (Obj) == SIZE_BAG(container));
#endif
ADDR_OBJ(container)[OBJSET_HDRSIZE + slot] = elm;
}
/**
* Functions to print object maps and sets
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static void PrintObjSet(Obj set)
{
UInt i, size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
Int comma = 0 ;
Pr("OBJ_SET([ " , 0 , 0 );
for (i=0 ; i < size; i++) {
Obj obj = READ_SLOT(set, i);
if (obj && obj != Undefined) {
if (comma) {
Pr(", " , 0 , 0 );
} else {
comma = 1 ;
}
PrintObj(obj);
}
}
Pr(" ])" , 0 , 0 );
}
static void PrintObjMap(Obj map)
{
UInt i, size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
Int comma = 0 ;
Pr("OBJ_MAP([ " , 0 , 0 );
for (i=0 ; i < size; i++) {
Obj obj = READ_SLOT(map, i * 2 );
if (obj && obj != Undefined) {
if (comma) {
Pr(", " , 0 , 0 );
} else {
comma = 1 ;
}
PrintObj(obj);
Pr(", " , 0 , 0 );
PrintObj(READ_SLOT(map, i * 2 + 1 ));
}
}
Pr(" ])" , 0 , 0 );
}
/**
* Garbage collector support for object maps and sets
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* These functions are not yet implemented .
*/
static void MarkObjSet(Obj obj, void * ref)
{
UInt size = CONST_ADDR_WORD(obj)[OBJSET_SIZE];
MarkArrayOfBags( CONST_ADDR_OBJ(obj) + OBJSET_HDRSIZE, size, ref );
}
static void MarkObjMap(Obj obj, void * ref)
{
UInt size = CONST_ADDR_WORD(obj)[OBJSET_SIZE];
MarkArrayOfBags( CONST_ADDR_OBJ(obj) + OBJSET_HDRSIZE, 2 * size, ref );
}
/**
* The primary hash function
* - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Hashing is done using Fibonacci hashing ( Knuth ) modulo the
* size of the table .
*/
static inline UInt ObjHash(Obj set, Obj obj)
{
return FibHash((UInt) obj, CONST_ADDR_WORD(set)[OBJSET_BITS]);
}
/**
* ` NewObjSet ( ) `
* - - - - - - - - - - - - -
*
* Create and return a new object set .
*/
Obj NewObjSet(void ) {
Obj result = NewBag(T_OBJSET,
(OBJSET_HDRSIZE+DEFAULT_OBJSET_SIZE)*sizeof (Bag));
ADDR_WORD(result)[OBJSET_SIZE] = DEFAULT_OBJSET_SIZE;
ADDR_WORD(result)[OBJSET_BITS] = DEFAULT_OBJSET_BITS;
ADDR_WORD(result)[OBJSET_USED] = 0 ;
ADDR_WORD(result)[OBJSET_DIRTY] = 0 ;
return result;
}
/**
* ` CheckObjSetForCleanUp ( ) `
* - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Determine if there is an excess number of deleted entries in ` set ` and
* compact the set if necessary . The additional parameter ` expand ` can be
* set to a non - zero value to reserve space for that many additional entries
* that will be inserted right after compaction .
*/
static void ResizeObjSet(Obj set, UInt bits);
static void CheckObjSetForCleanUp(Obj set, UInt expand)
{
UInt size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
UInt bits = CONST_ADDR_WORD(set)[OBJSET_BITS];
UInt used = CONST_ADDR_WORD(set)[OBJSET_USED] + expand;
UInt dirty = CONST_ADDR_WORD(set)[OBJSET_DIRTY];
if (used * 3 >= size * 2 )
ResizeObjSet(set, bits+1 );
else if (dirty && (dirty >= used || (dirty + used) * 3 >= size * 2 ))
ResizeObjSet(set, bits);
}
/**
* ` FindObjSet ( ) `
* - - - - - - - - - - - - - -
*
* Locate ` obj ` within ` set ` . Return - 1 if ` obj ` was not found , otherwise
* return the position within the data , starting with zero for the first
* entry .
*/
Int FindObjSet(Obj set, Obj obj) {
GAP_ASSERT(IS_OBJSET(set));
UInt size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
UInt hash = ObjHash(set, obj);
GAP_ASSERT(hash < size);
for (;;) {
Obj current;
current = READ_SLOT(set, hash);
if (!current)
return -1 ;
if (current == obj)
return (Int ) hash;
hash++;
if (hash >= size)
hash = 0 ;
}
}
/**
* ` AddObjSetNew ( ) `
* - - - - - - - - - - - - - - - -
*
* Add ` obj ` to ` set ` .
*
* Precondition : ` set ` must not already contain ` obj ` .
*
* This function does not call CHANGED_BAG , so the caller
* should do so , unless loading a workspace .
*/
static void AddObjSetNew(Obj set, Obj obj)
{
UInt size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
UInt hash = ObjHash(set, obj);
GAP_ASSERT(TNUM_OBJ(set) == T_OBJSET);
GAP_ASSERT(hash < size);
for (;;) {
Obj current;
current = READ_SLOT(set, hash);
if (!current) {
WRITE_SLOT(set, hash, obj);
ADDR_WORD(set)[OBJSET_USED]++;
return ;
}
if (current == Undefined) {
WRITE_SLOT(set, hash, obj);
ADDR_WORD(set)[OBJSET_USED]++;
GAP_ASSERT(CONST_ADDR_WORD(set)[OBJSET_DIRTY] >= 1 );
ADDR_WORD(set)[OBJSET_DIRTY]--;
return ;
}
hash++;
if (hash >= size)
hash = 0 ;
}
}
/**
* ` AddObjSet ( ) `
* - - - - - - - - - - - - -
*
* This function adds ` obj ` to ` set ` if the set doesn ' t contain it already .
*/
void AddObjSet(Obj set, Obj obj) {
GAP_ASSERT(TNUM_OBJ(set) == T_OBJSET);
if (FindObjSet(set, obj) >= 0 )
return ;
CheckObjSetForCleanUp(set, 1 );
AddObjSetNew(set, obj);
CHANGED_BAG(set);
}
/**
* ` RemoveObjSet ( ) `
* - - - - - - - - - - - - - - - -
*
* This function removes ` obj ` from ` set ` unless the set doesn ' t contain it .
*/
void RemoveObjSet(Obj set, Obj obj) {
GAP_ASSERT(TNUM_OBJ(set) == T_OBJSET);
Int pos = FindObjSet(set, obj);
if (pos >= 0 ) {
WRITE_SLOT(set, pos, Undefined);
ADDR_WORD(set)[OBJSET_USED]--;
ADDR_WORD(set)[OBJSET_DIRTY]++;
CHANGED_BAG(set);
CheckObjSetForCleanUp(set, 0 );
}
}
/**
* ` ClearObjSet ( ) `
* - - - - - - - - - - - - - - -
*
* This function removes all objects from ` set ` .
*/
void ClearObjSet(Obj set) {
GAP_ASSERT(TNUM_OBJ(set) == T_OBJSET);
Obj new = NewObjSet();
SwapMasterPoint(set, new );
}
/**
* ` ObjSetValues ( ) `
* - - - - - - - - - - - - - - -
*
* This function returns the elements of the set as a list .
*/
Obj ObjSetValues(Obj set) {
GAP_ASSERT(IS_OBJSET(set));
UInt len = CONST_ADDR_WORD(set)[OBJSET_USED];
UInt size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
UInt p, i;
Obj result = NEW_PLIST(T_PLIST, len);
SET_LEN_PLIST(result, len);
for (i=0 , p=1 ; i < size; i++) {
Obj el = READ_SLOT(set, i);
if (el && el != Undefined) {
SET_ELM_PLIST(result, p, el);
p++;
}
}
GAP_ASSERT(p == len + 1 );
CHANGED_BAG(result);
return result;
}
/**
* ` ResizeObjSet ( ) `
* - - - - - - - - - - - - - - - -
*
* This function resizes ` set ` to have room for ` 2 ^ bits ` entries .
*
* Precondition : the number of entries in ` set ` must be less than
* ` 2 ^ bits ` . There must be at least one free entry remaining .
*/
static void ResizeObjSet(Obj set, UInt bits)
{
UInt i, new_size = (1 << bits);
Int size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
Obj new = NewBag(T_OBJSET, (OBJSET_HDRSIZE+new_size)*sizeof (Bag));
GAP_ASSERT(TNUM_OBJ(set) == T_OBJSET);
GAP_ASSERT(new_size >= size);
ADDR_WORD(new )[OBJSET_SIZE] = new_size;
ADDR_WORD(new )[OBJSET_BITS] = bits;
ADDR_WORD(new )[OBJSET_USED] = 0 ;
ADDR_WORD(new )[OBJSET_DIRTY] = 0 ;
for (i = OBJSET_HDRSIZE + size - 1 ; i >= OBJSET_HDRSIZE; i--) {
Obj obj = CONST_ADDR_OBJ(set)[i];
if (obj && obj != Undefined) {
AddObjSetNew(new , obj);
}
}
SwapMasterPoint(set, new );
}
#ifdef GAP_ENABLE_SAVELOAD
static void SaveObjSet(Obj set)
{
UInt size = CONST_ADDR_WORD(set)[OBJSET_SIZE];
UInt bits = CONST_ADDR_WORD(set)[OBJSET_BITS];
UInt used = CONST_ADDR_WORD(set)[OBJSET_USED];
SaveUInt(size);
SaveUInt(bits);
SaveUInt(used);
for (UInt i = 0 ; i < size; i++) {
Obj val = READ_SLOT(set, i);
if (!val || val == Undefined)
continue ;
SaveSubObj(val);
GAP_ASSERT(used-- > 0 );
}
GAP_ASSERT(used == 0 );
}
static void LoadObjSet(Obj set)
{
UInt size = LoadUInt();
UInt bits = LoadUInt();
UInt used = LoadUInt();
ADDR_WORD(set)[OBJSET_SIZE] = size;
ADDR_WORD(set)[OBJSET_BITS] = bits;
ADDR_WORD(set)[OBJSET_USED] = 0 ;
ADDR_WORD(set)[OBJSET_DIRTY] = 0 ;
for (UInt i = 1 ; i <= used; i++) {
Obj val = LoadSubObj();
AddObjSetNew(set, val);
// No CHANGED_BAG required while loading a workspace
}
}
#endif
#ifdef USE_THREADSAFE_COPYING
#ifndef WARD_ENABLED
static void TraverseObjSet(TraversalState * traversal, Obj obj)
{
UInt i, len = CONST_ADDR_WORD(obj)[OBJSET_SIZE];
for (i = 0 ; i < len; i++) {
Obj item = READ_SLOT(obj, i);
if (item && item != Undefined)
QueueForTraversal(traversal, item);
}
}
static void CopyObjSet(TraversalState * traversal, Obj copy, Obj original)
{
UInt i, len = CONST_ADDR_WORD(original)[OBJSET_SIZE];
for (i = 0 ; i < len; i++) {
Obj item = READ_SLOT(original, i);
WRITE_SLOT(copy, i, ReplaceByCopy(traversal, item));
}
}
#endif // WARD_ENABLED
#endif
/**
* ` NewObjMap ( ) `
* - - - - - - - - - - - - -
*
* Create a new object map .
*/
Obj NewObjMap(void )
{
Obj result = NewBag(T_OBJMAP, (OBJSET_HDRSIZE+2 *DEFAULT_OBJSET_SIZE)*sizeof (Bag));
ADDR_WORD(result)[OBJSET_SIZE] = DEFAULT_OBJSET_SIZE;
ADDR_WORD(result)[OBJSET_BITS] = DEFAULT_OBJSET_BITS;
ADDR_WORD(result)[OBJSET_USED] = 0 ;
ADDR_WORD(result)[OBJSET_DIRTY] = 0 ;
return result;
}
/**
* ` CheckObjMapForCleanUp ( ) `
* - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Determine if there is an excess number of deleted entries in ` map ` and
* compact the map if necessary . The additional parameter ` expand ` can be
* set to a non - zero value to reserve space for that many additional entries
* that will be inserted right after compaction .
*/
static void ResizeObjMap(Obj map, UInt bits);
static void CheckObjMapForCleanUp(Obj map, UInt expand)
{
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
UInt bits = CONST_ADDR_WORD(map)[OBJSET_BITS];
UInt used = CONST_ADDR_WORD(map)[OBJSET_USED] + expand;
UInt dirty = CONST_ADDR_WORD(map)[OBJSET_DIRTY];
if (used * 3 >= size * 2 )
ResizeObjMap(map, bits+1 );
else if (dirty && (dirty >= used || (dirty + used) * 3 >= size * 2 ))
ResizeObjMap(map, bits);
}
/**
* ` FindObjMap ( ) `
* - - - - - - - - - - - - - -
*
* Locate the data entry with key ` obj ` within ` map ` . Return - 1 if such an
* entry was not found , otherwise return the position within the data ,
* starting with zero for the first entry .
*/
Int FindObjMap(Obj map, Obj obj) {
GAP_ASSERT(IS_OBJMAP(map));
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
UInt hash = ObjHash(map, obj);
for (;;) {
Obj current = READ_SLOT(map, hash*2 );
if (!current)
return -1 ;
if (current == obj)
return (Int ) hash;
hash++;
if (hash >= size)
hash = 0 ;
}
}
/**
* ` LookupObjMap ( ) `
* - - - - - - - - - - - - - - - -
*
* Locate the data entry with key ` obj ` within ` map ` . Return a null pointer
* if such an entry was not found , otherwise return the corresponding value .
*/
Obj LookupObjMap(Obj map, Obj obj) {
Int index = FindObjMap(map, obj);
if (index < 0 )
return (Obj) 0 ;
return READ_SLOT(map, index*2 +1 );
}
/**
* ` AddObjMapNew ( ) `
* - - - - - - - - - - - - - - - -
*
* Add an entry ` ( key , value ) ` to ` map ` .
*
* Precondition : No other entry with key ` key ` exists within ` map ` .
*
* This function does not call CHANGED_BAG , so the caller
* should do so , unless loading a workspace .
*/
static void AddObjMapNew(Obj map, Obj key, Obj value)
{
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
UInt hash = ObjHash(map, key);
for (;;) {
Obj current;
current = READ_SLOT(map, hash * 2 );
if (!current) {
WRITE_SLOT(map, hash*2 , key);
WRITE_SLOT(map, hash*2 +1 , value);
ADDR_WORD(map)[OBJSET_USED]++;
return ;
}
if (current == Undefined) {
WRITE_SLOT(map, hash*2 , key);
WRITE_SLOT(map, hash*2 +1 , value);
ADDR_WORD(map)[OBJSET_USED]++;
ADDR_WORD(map)[OBJSET_DIRTY]--;
return ;
}
hash++;
if (hash >= size)
hash = 0 ;
}
}
/**
* ` AddObjMap ( ) `
* - - - - - - - - - - - - -
*
* Add a data entry ` ( key , value ) ` to ` map ` . If ` map ` already contains an
* entry with that key , its value will be replaced .
*/
void AddObjMap(Obj map, Obj key, Obj value) {
GAP_ASSERT(TNUM_OBJ(map) == T_OBJMAP);
Int pos;
pos = FindObjMap(map, key);
if (pos >= 0 ) {
WRITE_SLOT(map, pos*2 +1 , value);
CHANGED_BAG(map);
return ;
}
CheckObjMapForCleanUp(map, 1 );
AddObjMapNew(map, key, value);
CHANGED_BAG(map);
}
/**
* ` RemoveObjMap ( ) `
* - - - - - - - - - - - - - - - -
*
* Remove the data entry with key ` key ` from ` map ` if such an entry exists .
*/
void RemoveObjMap(Obj map, Obj key) {
GAP_ASSERT(TNUM_OBJ(map) == T_OBJMAP);
Int pos = FindObjMap(map, key);
if (pos >= 0 ) {
WRITE_SLOT(map, pos*2 , Undefined);
WRITE_SLOT(map, pos*2 +1 , (Obj) 0 );
ADDR_WORD(map)[OBJSET_USED]--;
ADDR_WORD(map)[OBJSET_DIRTY]++;
CHANGED_BAG(map);
CheckObjMapForCleanUp(map, 0 );
}
}
/**
* ` ClearObjMap ( ) `
* - - - - - - - - - - - - - - -
*
* Remove all data entries from ` map ` .
*/
void ClearObjMap(Obj map) {
GAP_ASSERT(TNUM_OBJ(map) == T_OBJMAP);
Obj new = NewObjMap();
SwapMasterPoint(map, new );
}
/**
* ` ObjMapValues ( ) `
* - - - - - - - - - - - - - - -
*
* This function returns all values from the map .
*/
Obj ObjMapValues(Obj map)
{
GAP_ASSERT(IS_OBJMAP(map));
UInt len = CONST_ADDR_WORD(map)[OBJSET_USED];
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
UInt p, i;
Obj result = NEW_PLIST(T_PLIST, len);
SET_LEN_PLIST(result, len);
for (i=0 , p=1 ; i < size; i++) {
Obj el = READ_SLOT(map, 2 *i+1 );
if (el && el != Undefined) {
SET_ELM_PLIST(result, p, el);
p++;
}
}
GAP_ASSERT(p == len + 1 );
CHANGED_BAG(result);
return result;
}
/**
* ` ObjMapKeys ( ) `
* - - - - - - - - - - - - - - -
*
* This function returns all keys from the map .
*/
Obj ObjMapKeys(Obj map)
{
GAP_ASSERT(IS_OBJMAP(map));
UInt len = CONST_ADDR_WORD(map)[OBJSET_USED];
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
UInt p, i;
Obj result = NEW_PLIST(T_PLIST, len);
SET_LEN_PLIST(result, len);
for (i=0 , p=1 ; i < size; i++) {
Obj el = READ_SLOT(map, 2 *i);
if (el && el != Undefined) {
SET_ELM_PLIST(result, p, el);
p++;
}
}
GAP_ASSERT(p == len + 1 );
CHANGED_BAG(result);
return result;
}
/**
* ` ResizeObjMap ( ) `
* - - - - - - - - - - - - - - - -
*
* Resizes ` map ` so that it contains ` 2 ^ bits ` entries .
*
* Precondition : The number of entries in ` map ` must be less than ` 2 ^ bits ` .
*/
static void ResizeObjMap(Obj map, UInt bits)
{
UInt i, new_size = (1 << bits);
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
GAP_ASSERT(new_size >= size);
Obj new = NewBag(T_OBJMAP, (OBJSET_HDRSIZE+2 *new_size)*sizeof (Bag));
ADDR_WORD(new )[OBJSET_SIZE] = new_size;
ADDR_WORD(new )[OBJSET_BITS] = bits;
ADDR_WORD(new )[OBJSET_USED] = 0 ;
ADDR_WORD(new )[OBJSET_DIRTY] = 0 ;
for (i = 0 ; i < size; i++) {
Obj obj = READ_SLOT(map, i*2 );
if (obj && obj != Undefined) {
AddObjMapNew(new , obj, READ_SLOT(map, i * 2 + 1 ));
}
}
SwapMasterPoint(map, new );
}
#ifdef GAP_ENABLE_SAVELOAD
static void SaveObjMap(Obj map)
{
UInt size = CONST_ADDR_WORD(map)[OBJSET_SIZE];
UInt bits = CONST_ADDR_WORD(map)[OBJSET_BITS];
UInt used = CONST_ADDR_WORD(map)[OBJSET_USED];
SaveUInt(size);
SaveUInt(bits);
SaveUInt(used);
for (UInt i = 0 ; i < size; i++) {
Obj key = READ_SLOT(map, 2 * i);
Obj val = READ_SLOT(map, 2 * i + 1 );
if (!key || key == Undefined)
continue ;
SaveSubObj(key);
SaveSubObj(val);
GAP_ASSERT(used-- > 0 );
}
GAP_ASSERT(used == 0 );
}
static void LoadObjMap(Obj map)
{
UInt size = LoadUInt();
UInt bits = LoadUInt();
UInt used = LoadUInt();
ADDR_WORD(map)[OBJSET_SIZE] = size;
ADDR_WORD(map)[OBJSET_BITS] = bits;
ADDR_WORD(map)[OBJSET_USED] = 0 ;
ADDR_WORD(map)[OBJSET_DIRTY] = 0 ;
for (UInt i = 1 ; i <= used; i++) {
Obj key = LoadSubObj();
Obj val = LoadSubObj();
AddObjMapNew(map, key, val);
// No CHANGED_BAG required while loading a workspace
}
}
#endif
#ifdef USE_THREADSAFE_COPYING
#ifndef WARD_ENABLED
static void TraverseObjMap(TraversalState * traversal, Obj obj)
{
UInt i, len = CONST_ADDR_WORD(obj)[OBJSET_SIZE];
for (i = 0 ; i < len; i++) {
Obj key = READ_SLOT(obj, 2 * i);
Obj val = READ_SLOT(obj, 2 * i + 1 );
if (key && key != Undefined) {
QueueForTraversal(traversal, key);
QueueForTraversal(traversal, val);
}
}
}
static void CopyObjMap(TraversalState * traversal, Obj copy, Obj original)
{
UInt i, len = CONST_ADDR_WORD(original)[OBJSET_SIZE];
for (i = 0 ; i < len; i++) {
Obj key = READ_SLOT(original, 2 * i);
Obj val = READ_SLOT(original, 2 * i + 1 );
WRITE_SLOT(copy, 2 * i, ReplaceByCopy(traversal, key));
WRITE_SLOT(copy, 2 * i + 1 , ReplaceByCopy(traversal, val));
}
}
#endif
#endif
/**
* ` FuncOBJ_SET ( ) `
* - - - - - - - - - - - - - - -
*
* GAP function to create a new object set .
*
* It takes an optional argument that must be a list containing the elements
* of the new set . If no argument is provided , an empty set is created .
*/
static Obj FuncOBJ_SET(Obj self, Obj arg)
{
Obj result;
Obj list;
UInt i, len;
switch (LEN_PLIST(arg)) {
case 0 :
return NewObjSet();
case 1 :
list = ELM_PLIST(arg, 1 );
if (!IS_LIST(list))
ErrorQuit("OBJ_SET: Argument must be a list" , 0 , 0 );
result = NewObjSet();
len = LEN_LIST(list);
for (i = 1 ; i <= len; i++) {
Obj obj = ELM_LIST(list, i);
if (obj)
AddObjSet(result, obj);
}
CHANGED_BAG(result);
return result;
default :
ErrorQuit("OBJ_SET: Too many arguments" , 0 , 0 );
return (Obj) 0 ; // flow control hint
}
}
/**
* ` FuncADD_OBJ_SET ( ) `
* - - - - - - - - - - - - - - - - - - -
*
* GAP function to add ` obj ` to ` set ` .
*/
static Obj FuncADD_OBJ_SET(Obj self, Obj set, Obj obj)
{
RequireArgumentCondition(SELF_NAME, set, TNUM_OBJ(set) == T_OBJSET,
"must be a mutable object set" );
AddObjSet(set, obj);
return 0 ;
}
/**
* ` FuncREMOVE_OBJ_SET ( ) `
* - - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to remove ` obj ` from ` set ` .
*/
static Obj FuncREMOVE_OBJ_SET(Obj self, Obj set, Obj obj)
{
RequireArgumentCondition(SELF_NAME, set, TNUM_OBJ(set) == T_OBJSET,
"must be a mutable object set" );
RemoveObjSet(set, obj);
return 0 ;
}
/**
* ` FuncFIND_OBJ_SET ( ) `
* - - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to test if ` obj ` is contained in ` set ` . Returns ` true ` or
* ` false ` .
*/
static Obj FuncFIND_OBJ_SET(Obj self, Obj set, Obj obj)
{
RequireArgumentCondition(SELF_NAME, set, IS_OBJSET(set),
"must be an object set" );
Int pos = FindObjSet(set, obj);
return pos >= 0 ? True : False ;
}
/**
* ` FuncCLEAR_OBJ_SET ( ) `
* - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to remove all objects from ` set ` .
*/
static Obj FuncCLEAR_OBJ_SET(Obj self, Obj set)
{
RequireArgumentCondition(SELF_NAME, set, TNUM_OBJ(set) == T_OBJSET,
"must be a mutable object set" );
ClearObjSet(set);
return 0 ;
}
/**
* ` FuncOBJ_SET_VALUES ( ) `
* - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to return values in set as a list .
*/
static Obj FuncOBJ_SET_VALUES(Obj self, Obj set)
{
RequireArgumentCondition(SELF_NAME, set, IS_OBJSET(set),
"must be an object set" );
return ObjSetValues(set);
}
/**
* ` FuncOBJ_MAP ( ) `
* - - - - - - - - - - - - - - -
*
* GAP function to create a new object map .
*
* It takes an optional argument that must be a list containing the
* keys and values for the new map . Keys and values must alternate and
* there must be an equal number of keys and values . If no argument is
* provided , an empty map is created .
*/
static Obj FuncOBJ_MAP(Obj self, Obj arg)
{
Obj result;
Obj list;
UInt i, len;
switch (LEN_PLIST(arg)) {
case 0 :
return NewObjMap();
case 1 :
list = ELM_PLIST(arg, 1 );
if (!IS_LIST(list) || LEN_LIST(list) % 2 != 0 )
ErrorQuit("OBJ_MAP: Argument must be a list with even length" , 0 , 0 );
result = NewObjMap();
len = LEN_LIST(list);
for (i = 1 ; i <= len; i += 2 ) {
Obj key = ELM_LIST(list, i);
Obj value = ELM_LIST(list, i+1 );
if (key && value)
AddObjMap(result, key, value);
}
return result;
default :
ErrorQuit("OBJ_MAP: Too many arguments" , 0 , 0 );
return (Obj) 0 ; // flow control hint
}
}
/**
* ` FuncADD_OBJ_MAP ( ) `
* - - - - - - - - - - - - - - - - - - -
*
* GAP function to add a ( key , value ) pair to an object map .
*/
static Obj FuncADD_OBJ_MAP(Obj self, Obj map, Obj key, Obj value)
{
RequireArgumentCondition(SELF_NAME, map, TNUM_OBJ(map) == T_OBJMAP,
"must be a mutable object map" );
AddObjMap(map, key, value);
return 0 ;
}
/**
* ` FuncFIND_OBJ_MAP ( ) `
* - - - - - - - - - - - - - - - - - - - -
*
* GAP function to locate an entry with key ` key ` within ` map ` . The
* function returns the corresponding value if found and ` defvalue `
* otherwise .
*/
static Obj FuncFIND_OBJ_MAP(Obj self, Obj map, Obj key, Obj defvalue)
{
RequireArgumentCondition(SELF_NAME, map, IS_OBJMAP(map),
"must be an object map" );
Int pos = FindObjMap(map, key);
if (pos < 0 )
return defvalue;
return READ_SLOT(map, 2 * pos + 1 );
}
/**
* ` FuncCONTAINS_OBJ_MAP ( ) `
* - - - - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to locate an entry with key ` key ` within ` map ` . The
* function returns true if such an entry exists , false otherwise .
*/
static Obj FuncCONTAINS_OBJ_MAP(Obj self, Obj map, Obj key)
{
RequireArgumentCondition(SELF_NAME, map, IS_OBJMAP(map),
"must be an object map" );
Int pos = FindObjMap(map, key);
return pos >= 0 ? True : False ;
}
/**
* ` FuncREMOVE_OBJ_MAP ( ) `
* - - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to remove the entry with key ` key ` from ` map ` if it
* exists .
*/
static Obj FuncREMOVE_OBJ_MAP(Obj self, Obj map, Obj key)
{
RequireArgumentCondition(SELF_NAME, map, TNUM_OBJ(map) == T_OBJMAP,
"must be a mutable object map" );
RemoveObjMap(map, key);
return 0 ;
}
/**
* ` FuncCLEAR_OBJ_MAP ( ) `
* - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to remove all objects from ` map ` .
*/
static Obj FuncCLEAR_OBJ_MAP(Obj self, Obj map)
{
RequireArgumentCondition(SELF_NAME, map, TNUM_OBJ(map) == T_OBJMAP,
"must be a mutable object map" );
ClearObjMap(map);
return 0 ;
}
/**
* ` FuncOBJ_MAP_VALUES ( ) `
* - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to return values in set as a list .
*/
static Obj FuncOBJ_MAP_VALUES(Obj self, Obj map)
{
RequireArgumentCondition(SELF_NAME, map, IS_OBJMAP(map),
"must be an object map" );
return ObjMapValues(map);
}
/**
* ` FuncOBJ_MAP_KEYS ( ) `
* - - - - - - - - - - - - - - - - - - - - -
*
* GAP function to return keys in set as a list .
*/
static Obj FuncOBJ_MAP_KEYS(Obj self, Obj map)
{
RequireArgumentCondition(SELF_NAME, map, IS_OBJMAP(map),
"must be an object map" );
return ObjMapKeys(map);
}
/****************************************************************************
* *
* F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
*/
/****************************************************************************
* *
* V BagNames . . . . . . . . . . . . . . . . . . . . . . . list of bag names
*/
static StructBagNames BagNames[] = {
{ T_OBJSET , "object set" },
{ T_OBJSET+IMMUTABLE, "immutable object set" },
{ T_OBJMAP , "object map" },
{ T_OBJMAP+IMMUTABLE, "immutable object map" },
{ -1 , "" }
};
/****************************************************************************
* *
* V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
*/
static StructGVarFunc GVarFuncs[] = {
GVAR_FUNC_XARGS(OBJ_SET, -1 , "[list]" ),
GVAR_FUNC_2ARGS(ADD_OBJ_SET, set, obj),
GVAR_FUNC_2ARGS(REMOVE_OBJ_SET, set, obj),
GVAR_FUNC_2ARGS(FIND_OBJ_SET, set, obj),
GVAR_FUNC_1ARGS(CLEAR_OBJ_SET, set),
GVAR_FUNC_1ARGS(OBJ_SET_VALUES, set),
GVAR_FUNC_XARGS(OBJ_MAP, -1 , "[list]" ),
GVAR_FUNC_3ARGS(ADD_OBJ_MAP, map, key, value),
GVAR_FUNC_2ARGS(REMOVE_OBJ_MAP, map, obj),
GVAR_FUNC_3ARGS(FIND_OBJ_MAP, map, obj, default ),
GVAR_FUNC_2ARGS(CONTAINS_OBJ_MAP, map, obj),
GVAR_FUNC_1ARGS(CLEAR_OBJ_MAP, map),
GVAR_FUNC_1ARGS(OBJ_MAP_VALUES, map),
GVAR_FUNC_1ARGS(OBJ_MAP_KEYS, map),
{ 0 , 0 , 0 , 0 , 0 }
};
/****************************************************************************
* *
* F InitKernel ( < module > ) . . . . . . . . initialise kernel data structures
*/
static Int InitKernel (
StructInitInfo * module )
{
// set the bag type names (for error messages and debugging)
InitBagNamesFromTable( BagNames );
// install kind functions
TypeObjFuncs[T_OBJSET ] = TypeObjSet;
TypeObjFuncs[T_OBJSET+IMMUTABLE] = TypeObjSet;
TypeObjFuncs[T_OBJMAP ] = TypeObjMap;
TypeObjFuncs[T_OBJMAP+IMMUTABLE] = TypeObjMap;
// install global variables
InitCopyGVar("TYPE_OBJSET" , &TYPE_OBJSET);
InitCopyGVar("TYPE_OBJMAP" , &TYPE_OBJMAP);
// install mark functions
InitMarkFuncBags(T_OBJSET , MarkObjSet);
InitMarkFuncBags(T_OBJSET+IMMUTABLE, MarkObjSet);
InitMarkFuncBags(T_OBJMAP , MarkObjMap);
InitMarkFuncBags(T_OBJMAP+IMMUTABLE, MarkObjMap);
// install print functions
PrintObjFuncs[ T_OBJSET ] = PrintObjSet;
PrintObjFuncs[ T_OBJSET+IMMUTABLE ] = PrintObjSet;
PrintObjFuncs[ T_OBJMAP ] = PrintObjMap;
PrintObjFuncs[ T_OBJMAP+IMMUTABLE ] = PrintObjMap;
#ifdef USE_THREADSAFE_COPYING
SetTraversalMethod(T_OBJSET, TRAVERSE_BY_FUNCTION, TraverseObjSet, CopyObjSet);
SetTraversalMethod(T_OBJMAP, TRAVERSE_BY_FUNCTION, TraverseObjMap, CopyObjMap);
#endif
#ifdef GAP_ENABLE_SAVELOAD
// Install saving functions
SaveObjFuncs[ T_OBJSET ] = SaveObjSet;
SaveObjFuncs[ T_OBJSET +IMMUTABLE ] = SaveObjSet;
SaveObjFuncs[ T_OBJMAP ] = SaveObjMap;
SaveObjFuncs[ T_OBJMAP +IMMUTABLE ] = SaveObjMap;
LoadObjFuncs[ T_OBJSET ] = LoadObjSet;
LoadObjFuncs[ T_OBJSET +IMMUTABLE ] = LoadObjSet;
LoadObjFuncs[ T_OBJMAP ] = LoadObjMap;
LoadObjFuncs[ T_OBJMAP +IMMUTABLE ] = LoadObjMap;
#endif
// init filters and functions
InitHdlrFuncsFromTable( GVarFuncs );
return 0 ;
}
/****************************************************************************
* *
* F InitLibrary ( < module > ) . . . . . . . initialise library data structures
*/
static Int InitLibrary (
StructInitInfo * module )
{
// init filters and functions
InitGVarFuncsFromTable( GVarFuncs );
return 0 ;
}
/****************************************************************************
* *
* F InitInfoObjSet ( ) . . . . . . . . . . . . . . . table of init functions
*/
static StructInitInfo module = {
// init struct using C99 designated initializers; for a full list of
// fields, please refer to the definition of StructInitInfo
.type = MODULE_BUILTIN,
.name = "objset" ,
.initKernel = InitKernel,
.initLibrary = InitLibrary,
};
StructInitInfo * InitInfoObjSets ( void )
{
return &module;
}
Messung V0.5 in Prozent C=96 H=85 G=90
¤ Dauer der Verarbeitung: 0.54 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland