table.c
Go to the documentation of this file.
1 
21 #include <string.h>
22 #include <kuroko/kuroko.h>
23 #include <kuroko/object.h>
24 #include <kuroko/value.h>
25 #include <kuroko/memory.h>
26 #include <kuroko/table.h>
27 #include <kuroko/vm.h>
28 #include <kuroko/threads.h>
29 #include <kuroko/util.h>
30 
31 #define TABLE_MAX_LOAD 3 / 4
32 
33 void krk_initTable(KrkTable * table) {
34  table->count = 0;
35  table->capacity = 0;
36  table->used = 0;
37  table->entries = NULL;
38  table->indexes = NULL;
39 }
40 
41 void krk_freeTable(KrkTable * table) {
42  KRK_FREE_ARRAY(KrkTableEntry, table->entries, table->capacity);
43  KRK_FREE_ARRAY(ssize_t, table->indexes, table->capacity);
44  krk_initTable(table);
45 }
46 
47 inline int krk_hashValue(KrkValue value, uint32_t *hashOut) {
48  switch (KRK_VAL_TYPE(value)) {
49  case KRK_VAL_BOOLEAN:
50  case KRK_VAL_INTEGER:
51  case KRK_VAL_NONE:
52  case KRK_VAL_HANDLER:
53  case KRK_VAL_KWARGS:
54  *hashOut = (uint32_t)AS_INTEGER(value);
55  return 0;
56  case KRK_VAL_OBJECT:
57  if (AS_OBJECT(value)->flags & KRK_OBJ_FLAGS_VALID_HASH) {
58  *hashOut = AS_OBJECT(value)->hash;
59  return 0;
60  }
61  break;
62  default:
63 #ifndef KRK_NO_FLOAT
64  *hashOut = (uint32_t)AS_FLOATING(value);
65  return 0;
66 #else
67  break;
68 #endif
69  }
70  KrkClass * type = krk_getType(value);
71  if (type && type->_hash) {
72  krk_push(value);
73  KrkValue result = krk_callDirect(type->_hash, 1);
74  if (!IS_INTEGER(result)) goto _unhashable;
75  *hashOut = (uint32_t)AS_INTEGER(result);
76  return 0;
77  }
78  if (IS_CLASS(value)) {
79  *hashOut = (uint32_t)((int)(intptr_t)AS_OBJECT(value));
80  return 0;
81  }
82 _unhashable:
84  krk_runtimeError(vm.exceptions->typeError, "unhashable type: '%T'", value);
85  return 1;
86 }
87 
88 static inline ssize_t krk_tableIndexKeyC(const KrkTableEntry * entries, const ssize_t * indexes, size_t capacity, KrkValue key, int (*comparator)(KrkValue,KrkValue)) {
89  uint32_t index;
90  if (krk_hashValue(key, &index)) return -1;
91  index &= (capacity - 1);
92 
93  ssize_t tombstone = -1;
94  for (;;) {
95  if (indexes[index] == -1) {
96  return tombstone != -1 ? tombstone : index;
97  } else if (indexes[index] == -2) {
98  if (tombstone == index) return tombstone;
99  if (tombstone == -1) tombstone = index;
100  } else if (comparator(entries[indexes[index]].key, key)) {
101  return index;
102  }
103  index = (index + 1) & (capacity - 1);
104  }
105 }
106 
107 static ssize_t krk_tableIndexKey(const KrkTableEntry * entries, const ssize_t * indexes, size_t capacity, KrkValue key) {
108  return krk_tableIndexKeyC(entries,indexes,capacity,key,krk_valuesSameOrEqual);
109 }
110 
111 static ssize_t krk_tableIndexKeyExact(const KrkTableEntry * entries, const ssize_t * indexes, size_t capacity, KrkValue key) {
112  return krk_tableIndexKeyC(entries,indexes,capacity,key,krk_valuesSame);
113 }
114 
115 void krk_tableAdjustCapacity(KrkTable * table, size_t capacity) {
116  KrkTableEntry * nentries = KRK_ALLOCATE(KrkTableEntry, capacity);
117  ssize_t * nindexes = KRK_ALLOCATE(ssize_t, capacity);
118  for (size_t i = 0; i < capacity; ++i) {
119  nindexes[i] = -1;
120  nentries[i].key = KWARGS_VAL(0);
121  nentries[i].value = KWARGS_VAL(0);
122  }
123 
124  /* Fill in used entries */
125  const KrkTableEntry * e = table->entries;
126  for (size_t i = 0; i < table->count; ++i) {
127  while (IS_KWARGS(e->key)) e++;
128  memcpy(&nentries[i], e, sizeof(KrkTableEntry));
129  ssize_t indexkey = krk_tableIndexKeyExact(nentries,nindexes,capacity, e->key);
130  nindexes[indexkey] = i;
131  e++;
132  }
133 
134  /* Swap before freeing */
135  KrkTableEntry * oldEntries = table->entries;
136  table->entries = nentries;
137  KRK_FREE_ARRAY(KrkTableEntry, oldEntries, table->capacity);
138 
139  ssize_t * oldIndexes = table->indexes;
140  table->indexes = nindexes;
141  KRK_FREE_ARRAY(ssize_t, oldIndexes, table->capacity);
142 
143  /* Update table with new capacity and used count */
144  table->capacity = capacity;
145  table->used = table->count;
146 }
147 
148 int krk_tableSet(KrkTable * table, KrkValue key, KrkValue value) {
149  if (table->used + 1 > table->capacity * TABLE_MAX_LOAD) {
150  size_t capacity = KRK_GROW_CAPACITY(table->capacity);
151  krk_tableAdjustCapacity(table, capacity);
152  }
153 
154  ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
155  if (index < 0) return 0;
156  KrkTableEntry * entry;
157  int isNew = table->indexes[index] < 0;
158  if (isNew) {
159  table->indexes[index] = table->used;
160  entry = &table->entries[table->used];
161  entry->key = key;
162  table->used++;
163  table->count++;
164  } else {
165  entry = &table->entries[table->indexes[index]];
166  }
167  entry->value = value;
168  return isNew;
169 }
170 
171 int krk_tableSetExact(KrkTable * table, KrkValue key, KrkValue value) {
172  if (table->used + 1 > table->capacity * TABLE_MAX_LOAD) {
173  size_t capacity = KRK_GROW_CAPACITY(table->capacity);
174  krk_tableAdjustCapacity(table, capacity);
175  }
176 
177  ssize_t index = krk_tableIndexKeyExact(table->entries, table->indexes, table->capacity, key);
178  if (index < 0) return 0;
179  KrkTableEntry * entry;
180  int isNew = table->indexes[index] < 0;
181  if (isNew) {
182  table->indexes[index] = table->used;
183  entry = &table->entries[table->used];
184  entry->key = key;
185  table->used++;
186  table->count++;
187  } else {
188  entry = &table->entries[table->indexes[index]];
189  }
190  entry->value = value;
191  return isNew;
192 }
193 
194 int krk_tableSetIfExists(KrkTable * table, KrkValue key, KrkValue value) {
195  if (table->count == 0) return 0;
196  ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
197  if (index < 0 || table->indexes[index] < 0) return 0;
198  table->entries[table->indexes[index]].value = value;
199  return 1;
200 }
201 
202 void krk_tableAddAll(KrkTable * from, KrkTable * to) {
203  for (size_t i = 0; i < from->capacity; ++i) {
204  KrkTableEntry * entry = &from->entries[i];
205  if (!IS_KWARGS(entry->key)) {
206  krk_tableSet(to, entry->key, entry->value);
207  }
208  }
209 }
210 
211 int krk_tableGet(KrkTable * table, KrkValue key, KrkValue * value) {
212  if (table->count == 0) return 0;
213  ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
214  if (index < 0 || table->indexes[index] < 0) return 0;
215  *value = table->entries[table->indexes[index]].value;
216  return 1;
217 }
218 
219 int krk_tableGet_fast(KrkTable * table, KrkString * str, KrkValue * value) {
220  if (table->count == 0) return 0;
221  uint32_t index = str->obj.hash & (table->capacity-1);
222 
223  ssize_t tombstone = -1;
224  for (;;) {
225  if (table->indexes[index] == -1) {
226  return 0;
227  } else if (table->indexes[index] == -2) {
228  if (tombstone == index) return 0;
229  if (tombstone == -1) tombstone = index;
230  } else if (krk_valuesSame(table->entries[table->indexes[index]].key, OBJECT_VAL(str))) {
231  *value = table->entries[table->indexes[index]].value;
232  return 1;
233  }
234  index = (index + 1) & (table->capacity - 1);
235  }
236 }
237 
238 int krk_tableDelete(KrkTable * table, KrkValue key) {
239  if (table->count == 0) return 0;
240  ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
241  if (index < 0 || table->indexes[index] < 0) return 0;
242  table->count--;
243  table->entries[table->indexes[index]].key = KWARGS_VAL(0);
244  table->entries[table->indexes[index]].value = KWARGS_VAL(0);
245  table->indexes[index] = -2;
246  return 1;
247 }
248 
250  if (table->count == 0) return 0;
251  ssize_t index = krk_tableIndexKeyExact(table->entries, table->indexes, table->capacity, key);
252  if (index < 0 || table->indexes[index] < 0) return 0;
253  table->count--;
254  table->entries[table->indexes[index]].key = KWARGS_VAL(0);
255  table->entries[table->indexes[index]].value = KWARGS_VAL(0);
256  table->indexes[index] = -2;
257  return 1;
258 }
259 
260 KrkString * krk_tableFindString(KrkTable * table, const char * chars, size_t length, uint32_t hash) {
261  if (table->count == 0) return NULL;
262  uint32_t index = hash & (table->capacity - 1);
263 
264  ssize_t tombstone = -1;
265  for (;;) {
266  if (table->indexes[index] == -1) {
267  return NULL;
268  } else if (table->indexes[index] == -2) {
269  if (tombstone == index) return NULL;
270  if (tombstone == -1) tombstone = index;
271  } else if (AS_STRING(table->entries[table->indexes[index]].key)->length == length &&
272  AS_OBJECT(table->entries[table->indexes[index]].key)->hash == hash &&
273  memcmp(AS_STRING(table->entries[table->indexes[index]].key)->chars, chars, length) == 0) {
274  return AS_STRING(table->entries[table->indexes[index]].key);
275  }
276  index = (index + 1) & (table->capacity - 1);
277  }
278 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
Top-level header with configuration macros.
Functions for dealing with garbage collection and memory allocation.
Struct definitions for core object types.
Type object.
Definition: object.h:215
KrkObj * _hash
__hash__ Called when an instance is a key in a dict or an entry in a set
Definition: object.h:245
uint32_t hash
Cached hash value for table keys.
Definition: object.h:44
Immutable sequence of Unicode codepoints.
Definition: object.h:93
KrkObj obj
Base.
Definition: object.h:94
One (key,value) pair in a table.
Definition: table.h:20
Simple hash table of arbitrary keys to values.
Definition: table.h:28
void krk_initTable(KrkTable *table)
Initialize a hash table.
Definition: table.c:33
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
Definition: table.c:211
int krk_tableDeleteExact(KrkTable *table, KrkValue key)
Remove a key from a hash table, with identity lookup.
Definition: table.c:249
int krk_tableDelete(KrkTable *table, KrkValue key)
Remove a key from a hash table.
Definition: table.c:238
int krk_tableSet(KrkTable *table, KrkValue key, KrkValue value)
Assign a value to a key in a table.
Definition: table.c:148
void krk_tableAdjustCapacity(KrkTable *table, size_t capacity)
Preset the size of a table.
Definition: table.c:115
KrkTableEntry * entries
Definition: table.h:32
struct KrkString * krk_tableFindString(KrkTable *table, const char *chars, size_t length, uint32_t hash)
Find a character sequence in the string interning table.
Definition: table.c:260
void krk_tableAddAll(KrkTable *from, KrkTable *to)
Add all key-value pairs from 'from' into 'to'.
Definition: table.c:202
void krk_freeTable(KrkTable *table)
Release resources associated with a hash table.
Definition: table.c:41
int krk_tableGet_fast(KrkTable *table, struct KrkString *str, KrkValue *value)
Obtain the value associated with a string key in a table.
Definition: table.c:219
size_t count
Definition: table.h:29
size_t capacity
Definition: table.h:30
int krk_tableSetIfExists(KrkTable *table, KrkValue key, KrkValue value)
Update the value of a table entry only if it is found.
Definition: table.c:194
ssize_t * indexes
Definition: table.h:33
size_t used
Definition: table.h:31
KrkValue currentException
Definition: vm.h:164
Stack reference or primative value.
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:240
static int krk_valuesSame(KrkValue a, KrkValue b)
Compare two values by identity.
Definition: value.h:141
int krk_valuesSameOrEqual(KrkValue a, KrkValue b)
Compare two values by identity, then by equality.
Definition: value.c:96
int krk_hashValue(KrkValue value, uint32_t *hashOut)
Calculate the hash for a value.
Definition: table.c:47
Implementation of a generic hash table.
Convience header for providing atomic operations to threads.
Utilities for creating native bindings.
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
Definition: vm.c:740