mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
Compare commits
13 Commits
bf8ec59dc5
...
0a44220f66
Author | SHA1 | Date | |
---|---|---|---|
|
0a44220f66 | ||
|
d5bd9359d9 | ||
|
ddd99b121f | ||
|
f9f74b7b12 | ||
|
9b8f706010 | ||
|
78caa5602a | ||
|
eb52965aef | ||
|
a1cdfb6739 | ||
|
1fe211d728 | ||
|
94d8a40653 | ||
|
05e0432b4e | ||
|
caf7505dc2 | ||
|
be2aae493a |
@ -61,3 +61,15 @@
|
||||
#else
|
||||
#define PK_DEPRECATED
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG
|
||||
#if defined(__GNUC__)
|
||||
#define PK_INLINE __attribute__((always_inline)) inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define PK_INLINE __forceinline
|
||||
#else
|
||||
#define PK_INLINE inline
|
||||
#endif
|
||||
#else
|
||||
#define PK_INLINE
|
||||
#endif
|
||||
|
11
include/pocketpy/interpreter/bindings.h
Normal file
11
include/pocketpy/interpreter/bindings.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/pocketpy.h"
|
||||
|
||||
bool generator__next__(int argc, py_Ref argv);
|
||||
bool array2d_like_iterator__next__(int argc, py_Ref argv);
|
||||
bool list_iterator__next__(int argc, py_Ref argv);
|
||||
bool tuple_iterator__next__(int argc, py_Ref argv);
|
||||
bool dict_items__next__(int argc, py_Ref argv);
|
||||
bool range_iterator__next__(int argc, py_Ref argv);
|
||||
bool str_iterator__next__(int argc, py_Ref argv);
|
@ -3,22 +3,18 @@
|
||||
#include "pocketpy/common/vector.h"
|
||||
#include "pocketpy/objects/base.h"
|
||||
|
||||
#define PK_DICT_MAX_COLLISION 4
|
||||
|
||||
typedef struct {
|
||||
uint64_t hash;
|
||||
py_TValue key;
|
||||
py_TValue val;
|
||||
} DictEntry;
|
||||
|
||||
typedef struct {
|
||||
int _[PK_DICT_MAX_COLLISION];
|
||||
} DictIndex;
|
||||
|
||||
typedef struct {
|
||||
int length;
|
||||
uint32_t capacity;
|
||||
DictIndex* indices;
|
||||
uint32_t null_index_value;
|
||||
bool index_is_short;
|
||||
void* indices;
|
||||
c11_vector /*T=DictEntry*/ entries;
|
||||
} Dict;
|
||||
|
||||
|
@ -92,7 +92,6 @@ bool pk__parse_int_slice(py_Ref slice,
|
||||
bool pk__normalize_index(int* index, int length);
|
||||
|
||||
bool pk__object_new(int argc, py_Ref argv);
|
||||
py_TypeInfo* pk_typeinfo(py_Type type);
|
||||
|
||||
bool pk_wrapper__self(int argc, py_Ref argv);
|
||||
|
||||
|
@ -124,3 +124,4 @@ bool METHOD(contains)(NAME* self, K key) {
|
||||
#undef less
|
||||
#undef partial_less
|
||||
#undef equal
|
||||
#undef hash
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,7 @@ void Generator__dtor(Generator* ud) {
|
||||
if(ud->frame) Frame__delete(ud->frame);
|
||||
}
|
||||
|
||||
static bool generator__next__(int argc, py_Ref argv) {
|
||||
bool generator__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Generator* ud = py_touserdata(argv);
|
||||
py_StackRef p0 = py_peek(0);
|
||||
|
@ -33,25 +33,26 @@ void ManagedHeap__dtor(ManagedHeap* self) {
|
||||
void ManagedHeap__collect_if_needed(ManagedHeap* self) {
|
||||
if(!self->gc_enabled) return;
|
||||
if(self->gc_counter < self->gc_threshold) return;
|
||||
self->gc_counter = 0;
|
||||
int freed = ManagedHeap__collect(self);
|
||||
// adjust `gc_threshold` based on `freed_ma`
|
||||
self->freed_ma[0] = self->freed_ma[1];
|
||||
self->freed_ma[1] = self->freed_ma[2];
|
||||
self->freed_ma[2] = freed;
|
||||
int avg_freed = (self->freed_ma[0] + self->freed_ma[1] + self->freed_ma[2]) / 3;
|
||||
const int upper = PK_GC_MIN_THRESHOLD * 8;
|
||||
const int upper = PK_GC_MIN_THRESHOLD * 16;
|
||||
const int lower = PK_GC_MIN_THRESHOLD / 2;
|
||||
float free_ratio = (float)avg_freed / self->gc_threshold;
|
||||
int new_threshold = self->gc_threshold * (1 / free_ratio);
|
||||
int new_threshold = self->gc_threshold * (1.5f / free_ratio);
|
||||
// printf("gc_threshold=%d, avg_freed=%d, new_threshold=%d\n", self->gc_threshold, avg_freed,
|
||||
// new_threshold);
|
||||
self->gc_threshold = c11__min(c11__max(new_threshold, lower), upper);
|
||||
}
|
||||
|
||||
int ManagedHeap__collect(ManagedHeap* self) {
|
||||
self->gc_counter = 0;
|
||||
ManagedHeap__mark(self);
|
||||
int freed = ManagedHeap__sweep(self);
|
||||
// printf("GC: collected %d objects\n", freed);
|
||||
return freed;
|
||||
}
|
||||
|
||||
|
@ -10,17 +10,17 @@ py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
py_ItemRef py_tpfindname(py_Type type, py_Name name) {
|
||||
PK_INLINE py_ItemRef py_tpfindname(py_Type type, py_Name name) {
|
||||
py_TypeInfo* ti = pk_typeinfo(type);
|
||||
return pk_tpfindname(ti, name);
|
||||
}
|
||||
|
||||
py_Ref py_tpfindmagic(py_Type t, py_Name name) {
|
||||
PK_INLINE py_Ref py_tpfindmagic(py_Type t, py_Name name) {
|
||||
// assert(py_ismagicname(name));
|
||||
return py_tpfindname(t, name);
|
||||
}
|
||||
|
||||
py_Type py_tpbase(py_Type t) {
|
||||
PK_INLINE py_Type py_tpbase(py_Type t) {
|
||||
assert(t);
|
||||
py_TypeInfo* ti = pk_typeinfo(t);
|
||||
return ti->base;
|
||||
@ -44,7 +44,7 @@ const char* py_tpname(py_Type type) {
|
||||
return py_name2str(name);
|
||||
}
|
||||
|
||||
py_TypeInfo* pk_typeinfo(py_Type type) {
|
||||
PK_INLINE py_TypeInfo* pk_typeinfo(py_Type type) {
|
||||
#ifndef NDEBUG
|
||||
int length = pk_current_vm->types.length;
|
||||
if(type < 0 || type >= length) {
|
||||
|
@ -123,5 +123,8 @@ py_Ref py_name2ref(py_Name name) {
|
||||
void PyObject__dtor(PyObject* self) {
|
||||
py_Dtor dtor = c11__getitem(TypePointer, &pk_current_vm->types, self->type).dtor;
|
||||
if(dtor) dtor(PyObject__userdata(self));
|
||||
if(self->slots == -1) NameDict__dtor(PyObject__dict(self));
|
||||
if(self->slots == -1) {
|
||||
NameDict* dict = PyObject__dict(self);
|
||||
NameDict__dtor(dict);
|
||||
}
|
||||
}
|
@ -800,7 +800,7 @@ static void register_array2d_like(py_Ref mod) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool array2d_like_iterator__next__(int argc, py_Ref argv) {
|
||||
bool array2d_like_iterator__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
c11_array2d_like_iterator* self = py_touserdata(argv);
|
||||
if(self->j >= self->array->n_rows) return StopIteration();
|
||||
|
@ -6,19 +6,29 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HASH_PROBE_0(__k, ok, i) \
|
||||
ok = false; \
|
||||
i = (uintptr_t)(__k)&self->mask; \
|
||||
do { \
|
||||
if(self->items[i].key == (__k)) { \
|
||||
ok = true; \
|
||||
break; \
|
||||
} \
|
||||
if(self->items[i].key == NULL) break; \
|
||||
i = (5 * i + 1) & self->mask; \
|
||||
} while(true);
|
||||
|
||||
#define HASH_PROBE_1(__k, ok, i) \
|
||||
ok = false; \
|
||||
i = (uintptr_t)(__k) & self->mask; \
|
||||
i = (uintptr_t)(__k)&self->mask; \
|
||||
while(self->items[i].key != NULL) { \
|
||||
if(self->items[i].key == (__k)) { \
|
||||
ok = true; \
|
||||
break; \
|
||||
} \
|
||||
i = (i + 1) & self->mask; \
|
||||
i = (5 * i + 1) & self->mask; \
|
||||
}
|
||||
|
||||
#define HASH_PROBE_0 HASH_PROBE_1
|
||||
|
||||
static void NameDict__set_capacity_and_alloc_items(NameDict* self, int val) {
|
||||
self->capacity = val;
|
||||
self->critical_size = val * self->load_factor;
|
||||
@ -101,18 +111,24 @@ bool NameDict__del(NameDict* self, py_Name key) {
|
||||
self->items[i].key = NULL;
|
||||
self->items[i].value = *py_NIL();
|
||||
self->length--;
|
||||
// tidy
|
||||
uintptr_t pre_z = i;
|
||||
uintptr_t z = (i + 1) & self->mask;
|
||||
while(self->items[z].key != NULL) {
|
||||
uintptr_t h = (uintptr_t)self->items[z].key & self->mask;
|
||||
if(h != i) break;
|
||||
// std::swap(_items[pre_z], _items[z]);
|
||||
NameDict_KV tmp = self->items[pre_z];
|
||||
self->items[pre_z] = self->items[z];
|
||||
self->items[z] = tmp;
|
||||
pre_z = z;
|
||||
z = (z + 1) & self->mask;
|
||||
/* tidy */
|
||||
uint32_t posToRemove = i;
|
||||
uint32_t posToShift = posToRemove;
|
||||
while(true) {
|
||||
posToShift = (5 * posToShift + 1) & self->mask;
|
||||
if(self->items[posToShift].key == NULL) break;
|
||||
uintptr_t hash_z = (uintptr_t)self->items[posToShift].key;
|
||||
uintptr_t insertPos = hash_z & self->mask;
|
||||
bool cond1 = insertPos <= posToRemove;
|
||||
bool cond2 = posToRemove <= posToShift;
|
||||
if((cond1 && cond2) ||
|
||||
// chain wrapped around capacity
|
||||
(posToShift < insertPos && (cond1 || cond2))) {
|
||||
NameDict_KV tmp = self->items[posToRemove];
|
||||
self->items[posToRemove] = self->items[posToShift];
|
||||
self->items[posToShift] = tmp;
|
||||
posToRemove = posToShift;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2,14 +2,16 @@
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include <assert.h>
|
||||
|
||||
void* PyObject__userdata(PyObject* self) { return self->flex + PK_OBJ_SLOTS_SIZE(self->slots); }
|
||||
PK_INLINE void* PyObject__userdata(PyObject* self) {
|
||||
return self->flex + PK_OBJ_SLOTS_SIZE(self->slots);
|
||||
}
|
||||
|
||||
NameDict* PyObject__dict(PyObject* self) {
|
||||
PK_INLINE NameDict* PyObject__dict(PyObject* self) {
|
||||
assert(self->slots == -1);
|
||||
return (NameDict*)(self->flex);
|
||||
}
|
||||
|
||||
py_TValue* PyObject__slots(PyObject* self) {
|
||||
PK_INLINE py_TValue* PyObject__slots(PyObject* self) {
|
||||
assert(self->slots >= 0);
|
||||
return (py_TValue*)(self->flex);
|
||||
}
|
@ -178,7 +178,7 @@ bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
|
||||
return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR;
|
||||
}
|
||||
|
||||
py_Ref py_retval() { return &pk_current_vm->last_retval; }
|
||||
PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; }
|
||||
|
||||
bool py_pushmethod(py_Name name) {
|
||||
bool ok = pk_loadmethod(py_peek(-1), name);
|
||||
|
@ -57,7 +57,7 @@ bool pk_arraycontains(py_Ref self, py_Ref val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool list_iterator__next__(int argc, py_Ref argv) {
|
||||
bool list_iterator__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
list_iterator* ud = py_touserdata(argv);
|
||||
if(ud->index < ud->vec->length) {
|
||||
@ -69,7 +69,7 @@ static bool list_iterator__next__(int argc, py_Ref argv) {
|
||||
return StopIteration();
|
||||
}
|
||||
|
||||
static bool tuple_iterator__next__(int argc, py_Ref argv) {
|
||||
bool tuple_iterator__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
tuple_iterator* ud = py_touserdata(argv);
|
||||
if(ud->index < ud->length) {
|
||||
|
@ -5,6 +5,16 @@
|
||||
#include "pocketpy/interpreter/types.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
|
||||
typedef struct {
|
||||
Dict* dict; // weakref for slot 0
|
||||
Dict dict_backup;
|
||||
DictEntry* curr;
|
||||
DictEntry* end;
|
||||
int mode; // 0: keys, 1: values, 2: items
|
||||
} DictIterator;
|
||||
|
||||
#define Dict__step(x) ((x) < mask ? (x) + 1 : 0)
|
||||
|
||||
static uint32_t Dict__next_cap(uint32_t cap) {
|
||||
switch(cap) {
|
||||
case 7: return 17;
|
||||
@ -51,19 +61,36 @@ static uint32_t Dict__next_cap(uint32_t cap) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
DictEntry* curr;
|
||||
DictEntry* end;
|
||||
int mode; // 0: keys, 1: values, 2: items
|
||||
} DictIterator;
|
||||
static uint64_t Dict__hash_2nd(uint64_t key) {
|
||||
// https://gist.github.com/badboy/6267743
|
||||
key = (~key) + (key << 21); // key = (key << 21) - key - 1
|
||||
key = key ^ (key >> 24);
|
||||
key = (key + (key << 3)) + (key << 8); // key * 265
|
||||
key = key ^ (key >> 14);
|
||||
key = (key + (key << 2)) + (key << 4); // key * 21
|
||||
key = key ^ (key >> 28);
|
||||
key = key + (key << 31);
|
||||
return key;
|
||||
}
|
||||
|
||||
static void Dict__ctor(Dict* self, uint32_t capacity, int entries_capacity) {
|
||||
self->length = 0;
|
||||
self->capacity = capacity;
|
||||
self->indices = PK_MALLOC(self->capacity * sizeof(DictIndex));
|
||||
memset(self->indices, -1, self->capacity * sizeof(DictIndex));
|
||||
|
||||
size_t indices_size;
|
||||
if(self->capacity < UINT16_MAX) {
|
||||
self->index_is_short = true;
|
||||
indices_size = self->capacity * sizeof(uint16_t);
|
||||
self->null_index_value = UINT16_MAX;
|
||||
} else {
|
||||
self->index_is_short = false;
|
||||
indices_size = self->capacity * sizeof(uint32_t);
|
||||
self->null_index_value = UINT32_MAX;
|
||||
}
|
||||
|
||||
self->indices = PK_MALLOC(indices_size);
|
||||
memset(self->indices, -1, indices_size);
|
||||
|
||||
c11_vector__ctor(&self->entries, sizeof(DictEntry));
|
||||
c11_vector__reserve(&self->entries, entries_capacity);
|
||||
}
|
||||
@ -75,70 +102,118 @@ static void Dict__dtor(Dict* self) {
|
||||
c11_vector__dtor(&self->entries);
|
||||
}
|
||||
|
||||
static bool Dict__try_get(Dict* self, py_TValue* key, DictEntry** out) {
|
||||
py_i64 hash;
|
||||
if(!py_hash(key, &hash)) return false;
|
||||
int idx = (uint64_t)hash % self->capacity;
|
||||
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
|
||||
int idx2 = self->indices[idx]._[i];
|
||||
if(idx2 == -1) continue;
|
||||
static uint32_t Dict__get_index(Dict* self, uint32_t index) {
|
||||
if(self->index_is_short) {
|
||||
uint16_t* indices = self->indices;
|
||||
return indices[index];
|
||||
} else {
|
||||
uint32_t* indices = self->indices;
|
||||
return indices[index];
|
||||
}
|
||||
}
|
||||
|
||||
static void Dict__swap_null_index(Dict* self, uint32_t pre_z, uint32_t z) {
|
||||
if(self->index_is_short) {
|
||||
uint16_t* indices = self->indices;
|
||||
assert(indices[pre_z] == UINT16_MAX);
|
||||
indices[pre_z] = indices[z];
|
||||
indices[z] = UINT16_MAX;
|
||||
} else {
|
||||
uint32_t* indices = self->indices;
|
||||
assert(indices[pre_z] == UINT32_MAX);
|
||||
indices[pre_z] = indices[z];
|
||||
indices[z] = UINT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static void Dict__set_index(Dict* self, uint32_t index, uint32_t value) {
|
||||
if(self->index_is_short) {
|
||||
uint16_t* indices = self->indices;
|
||||
indices[index] = (uint16_t)value;
|
||||
} else {
|
||||
uint32_t* indices = self->indices;
|
||||
indices[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static bool Dict__probe(Dict* self,
|
||||
py_TValue* key,
|
||||
uint64_t* p_hash,
|
||||
uint32_t* p_idx,
|
||||
DictEntry** p_entry) {
|
||||
py_i64 h_user;
|
||||
if(!py_hash(key, &h_user)) return false;
|
||||
if(py_isstr(key)) {
|
||||
*p_hash = (uint64_t)h_user;
|
||||
} else {
|
||||
*p_hash = Dict__hash_2nd((uint64_t)h_user);
|
||||
}
|
||||
uint32_t mask = self->capacity - 1;
|
||||
uint32_t idx = (*p_hash) % self->capacity;
|
||||
while(true) {
|
||||
uint32_t idx2 = Dict__get_index(self, idx);
|
||||
if(idx2 == self->null_index_value) break;
|
||||
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
|
||||
if(entry->hash == (uint64_t)hash) {
|
||||
if(entry->hash == (*p_hash)) {
|
||||
int res = py_equal(&entry->key, key);
|
||||
if(res == 1) {
|
||||
*out = entry;
|
||||
*p_idx = idx;
|
||||
*p_entry = entry;
|
||||
return true;
|
||||
}
|
||||
if(res == -1) return false; // error
|
||||
}
|
||||
// try next index
|
||||
idx = Dict__step(idx);
|
||||
}
|
||||
*out = NULL;
|
||||
// not found
|
||||
*p_idx = idx;
|
||||
*p_entry = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Dict__try_get(Dict* self, py_TValue* key, DictEntry** out) {
|
||||
uint64_t hash;
|
||||
uint32_t idx;
|
||||
return Dict__probe(self, key, &hash, &idx, out);
|
||||
}
|
||||
|
||||
static void Dict__clear(Dict* self) {
|
||||
memset(self->indices, -1, self->capacity * sizeof(DictIndex));
|
||||
size_t indices_size = self->index_is_short ? self->capacity * sizeof(uint16_t)
|
||||
: self->capacity * sizeof(uint32_t);
|
||||
memset(self->indices, -1, indices_size);
|
||||
c11_vector__clear(&self->entries);
|
||||
self->length = 0;
|
||||
}
|
||||
|
||||
static void Dict__rehash_2x(Dict* self) {
|
||||
Dict old_dict = *self;
|
||||
uint32_t new_capacity = self->capacity;
|
||||
|
||||
__RETRY:
|
||||
// use next capacity
|
||||
new_capacity = Dict__next_cap(new_capacity);
|
||||
uint32_t new_capacity = Dict__next_cap(old_dict.capacity);
|
||||
uint32_t mask = new_capacity - 1;
|
||||
// create a new dict with new capacity
|
||||
Dict__ctor(self, new_capacity, old_dict.entries.capacity);
|
||||
// move entries from old dict to new dict
|
||||
for(int i = 0; i < old_dict.entries.length; i++) {
|
||||
DictEntry* old_entry = c11__at(DictEntry, &old_dict.entries, i);
|
||||
if(py_isnil(&old_entry->key)) continue;
|
||||
int idx = old_entry->hash % new_capacity;
|
||||
bool success = false;
|
||||
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
|
||||
int idx2 = self->indices[idx]._[i];
|
||||
if(idx2 == -1) {
|
||||
// insert new entry (empty slot)
|
||||
if(py_isnil(&old_entry->key)) continue; // skip deleted
|
||||
uint32_t idx = old_entry->hash % new_capacity;
|
||||
while(true) {
|
||||
uint32_t idx2 = Dict__get_index(self, idx);
|
||||
if(idx2 == self->null_index_value) {
|
||||
c11_vector__push(DictEntry, &self->entries, *old_entry);
|
||||
self->indices[idx]._[i] = self->entries.length - 1;
|
||||
Dict__set_index(self, idx, self->entries.length - 1);
|
||||
self->length++;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!success) {
|
||||
Dict__dtor(self);
|
||||
goto __RETRY;
|
||||
// try next index
|
||||
idx = Dict__step(idx);
|
||||
}
|
||||
}
|
||||
// done
|
||||
Dict__dtor(&old_dict);
|
||||
}
|
||||
|
||||
static void Dict__compact_entries(Dict* self) {
|
||||
int* mappings = PK_MALLOC(self->entries.length * sizeof(int));
|
||||
uint32_t* mappings = PK_MALLOC(self->entries.length * sizeof(uint32_t));
|
||||
|
||||
int n = 0;
|
||||
for(int i = 0; i < self->entries.length; i++) {
|
||||
@ -153,96 +228,97 @@ static void Dict__compact_entries(Dict* self) {
|
||||
}
|
||||
self->entries.length = n;
|
||||
// update indices
|
||||
for(uint32_t i = 0; i < self->capacity; i++) {
|
||||
for(int j = 0; j < PK_DICT_MAX_COLLISION; j++) {
|
||||
int idx = self->indices[i]._[j];
|
||||
if(idx == -1) continue;
|
||||
self->indices[i]._[j] = mappings[idx];
|
||||
}
|
||||
for(uint32_t idx = 0; idx < self->capacity; idx++) {
|
||||
uint32_t idx2 = Dict__get_index(self, idx);
|
||||
if(idx2 == self->null_index_value) continue;
|
||||
Dict__set_index(self, idx, mappings[idx2]);
|
||||
}
|
||||
PK_FREE(mappings);
|
||||
}
|
||||
|
||||
static bool Dict__set(Dict* self, py_TValue* key, py_TValue* val) {
|
||||
py_i64 hash;
|
||||
if(!py_hash(key, &hash)) return false;
|
||||
int idx = (uint64_t)hash % self->capacity;
|
||||
int bad_hash_count = 0;
|
||||
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
|
||||
int idx2 = self->indices[idx]._[i];
|
||||
if(idx2 == -1) {
|
||||
// insert new entry
|
||||
DictEntry* new_entry = c11_vector__emplace(&self->entries);
|
||||
new_entry->hash = (uint64_t)hash;
|
||||
new_entry->key = *key;
|
||||
new_entry->val = *val;
|
||||
self->indices[idx]._[i] = self->entries.length - 1;
|
||||
self->length++;
|
||||
return true;
|
||||
}
|
||||
uint64_t hash;
|
||||
uint32_t idx;
|
||||
DictEntry* entry;
|
||||
if(!Dict__probe(self, key, &hash, &idx, &entry)) return false;
|
||||
if(entry) {
|
||||
// update existing entry
|
||||
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
|
||||
// check if they have the same hash
|
||||
if(entry->hash == (uint64_t)hash) {
|
||||
// check if they are equal
|
||||
int res = py_equal(&entry->key, key);
|
||||
if(res == 1) {
|
||||
entry->val = *val;
|
||||
return true;
|
||||
}
|
||||
if(res == -1) return false; // error
|
||||
// res == 0
|
||||
bad_hash_count++;
|
||||
}
|
||||
entry->val = *val;
|
||||
return true;
|
||||
}
|
||||
// no empty slot found
|
||||
if(bad_hash_count == PK_DICT_MAX_COLLISION) {
|
||||
// all `PK_DICT_MAX_COLLISION` slots have the same hash but different keys
|
||||
// we are unable to solve this collision via rehashing
|
||||
return RuntimeError("dict: %d/%d/%d: maximum collision reached (hash=%i)",
|
||||
self->entries.length,
|
||||
self->entries.capacity,
|
||||
self->capacity,
|
||||
hash);
|
||||
}
|
||||
|
||||
if(self->capacity >= (uint32_t)self->entries.length * 10) {
|
||||
return RuntimeError("dict: %d/%d/%d: minimum load factor reached",
|
||||
self->entries.length,
|
||||
self->entries.capacity,
|
||||
self->capacity);
|
||||
}
|
||||
Dict__rehash_2x(self);
|
||||
return Dict__set(self, key, val);
|
||||
// insert new entry
|
||||
DictEntry* new_entry = c11_vector__emplace(&self->entries);
|
||||
new_entry->hash = hash;
|
||||
new_entry->key = *key;
|
||||
new_entry->val = *val;
|
||||
Dict__set_index(self, idx, self->entries.length - 1);
|
||||
self->length++;
|
||||
// check if we need to rehash
|
||||
float load_factor = (float)self->length / self->capacity;
|
||||
if(load_factor > (self->index_is_short ? 0.3f : 0.4f)) Dict__rehash_2x(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Delete an entry from the dict.
|
||||
/// -1: error, 0: not found, 1: found and deleted
|
||||
static int Dict__pop(Dict* self, py_Ref key) {
|
||||
py_i64 hash;
|
||||
if(!py_hash(key, &hash)) return -1;
|
||||
int idx = (uint64_t)hash % self->capacity;
|
||||
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
|
||||
int idx2 = self->indices[idx]._[i];
|
||||
if(idx2 == -1) continue;
|
||||
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
|
||||
if(entry->hash == (uint64_t)hash) {
|
||||
int res = py_equal(&entry->key, key);
|
||||
if(res == 1) {
|
||||
*py_retval() = entry->val;
|
||||
py_newnil(&entry->key);
|
||||
self->indices[idx]._[i] = -1;
|
||||
self->length--;
|
||||
if(self->length < self->entries.length / 2) Dict__compact_entries(self);
|
||||
return 1;
|
||||
}
|
||||
if(res == -1) return -1; // error
|
||||
// Dict__log_index(self, "before pop");
|
||||
uint64_t hash;
|
||||
uint32_t idx;
|
||||
DictEntry* entry;
|
||||
if(!Dict__probe(self, key, &hash, &idx, &entry)) return -1;
|
||||
if(!entry) return 0; // not found
|
||||
|
||||
// found the entry, delete and return it
|
||||
py_assign(py_retval(), &entry->val);
|
||||
Dict__set_index(self, idx, self->null_index_value);
|
||||
py_newnil(&entry->key);
|
||||
py_newnil(&entry->val);
|
||||
self->length--;
|
||||
|
||||
/* tidy */
|
||||
// https://github.com/OpenHFT/Chronicle-Map/blob/820573a68471509ffc1b0584454f4a67c0be1b84/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java#L156
|
||||
uint32_t mask = self->capacity - 1;
|
||||
uint32_t posToRemove = idx;
|
||||
uint32_t posToShift = posToRemove;
|
||||
// int probe_count = 0;
|
||||
// int swap_count = 0;
|
||||
while(true) {
|
||||
posToShift = Dict__step(posToShift);
|
||||
uint32_t idx_z = Dict__get_index(self, posToShift);
|
||||
if(idx_z == self->null_index_value) break;
|
||||
uint64_t hash_z = c11__at(DictEntry, &self->entries, idx_z)->hash;
|
||||
uint32_t insertPos = (uint64_t)hash_z % self->capacity;
|
||||
// the following condition essentially means circular permutations
|
||||
// of three (r = posToRemove, s = posToShift, i = insertPos)
|
||||
// positions are accepted:
|
||||
// [...i..r...s.] or
|
||||
// [...r..s...i.] or
|
||||
// [...s..i...r.]
|
||||
bool cond1 = insertPos <= posToRemove;
|
||||
bool cond2 = posToRemove <= posToShift;
|
||||
if((cond1 && cond2) ||
|
||||
// chain wrapped around capacity
|
||||
(posToShift < insertPos && (cond1 || cond2))) {
|
||||
Dict__swap_null_index(self, posToRemove, posToShift);
|
||||
posToRemove = posToShift;
|
||||
// swap_count++;
|
||||
}
|
||||
// probe_count++;
|
||||
}
|
||||
return 0;
|
||||
// printf("Dict__pop: probe_count=%d, swap_count=%d\n", probe_count, swap_count);
|
||||
// compact entries if necessary
|
||||
if(self->entries.length > 16 && (self->length < self->entries.length >> 1)) {
|
||||
Dict__compact_entries(self); // compact entries
|
||||
}
|
||||
// Dict__log_index(self, "after pop");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void DictIterator__ctor(DictIterator* self, Dict* dict, int mode) {
|
||||
assert(mode >= 0 && mode <= 2);
|
||||
self->dict = dict;
|
||||
self->dict_backup = *dict; // backup the dict
|
||||
self->curr = dict->entries.data;
|
||||
self->end = self->curr + dict->entries.length;
|
||||
self->mode = mode;
|
||||
@ -257,18 +333,22 @@ static DictEntry* DictIterator__next(DictIterator* self) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool DictIterator__modified(DictIterator* self) {
|
||||
return memcmp(self->dict, &self->dict_backup, sizeof(Dict)) != 0;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
static bool dict__new__(int argc, py_Ref argv) {
|
||||
py_Type cls = py_totype(argv);
|
||||
int slots = cls == tp_dict ? 0 : -1;
|
||||
Dict* ud = py_newobject(py_retval(), cls, slots, sizeof(Dict));
|
||||
Dict__ctor(ud, 7, 8);
|
||||
Dict__ctor(ud, 17, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
void py_newdict(py_OutRef out) {
|
||||
Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict));
|
||||
Dict__ctor(ud, 7, 8);
|
||||
Dict__ctor(ud, 17, 4);
|
||||
}
|
||||
|
||||
static bool dict__init__(int argc, py_Ref argv) {
|
||||
@ -426,12 +506,17 @@ static bool dict_copy(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Dict* self = py_touserdata(argv);
|
||||
Dict* new_dict = py_newobject(py_retval(), tp_dict, 0, sizeof(Dict));
|
||||
new_dict->capacity = self->capacity;
|
||||
new_dict->length = self->length;
|
||||
new_dict->capacity = self->capacity;
|
||||
new_dict->null_index_value = self->null_index_value;
|
||||
new_dict->index_is_short = self->index_is_short;
|
||||
// copy entries
|
||||
new_dict->entries = c11_vector__copy(&self->entries);
|
||||
// copy indices
|
||||
new_dict->indices = PK_MALLOC(new_dict->capacity * sizeof(DictIndex));
|
||||
memcpy(new_dict->indices, self->indices, new_dict->capacity * sizeof(DictIndex));
|
||||
size_t indices_size = self->index_is_short ? self->capacity * sizeof(uint16_t)
|
||||
: self->capacity * sizeof(uint32_t);
|
||||
new_dict->indices = PK_MALLOC(indices_size);
|
||||
memcpy(new_dict->indices, self->indices, indices_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -525,9 +610,10 @@ py_Type pk_dict__register() {
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
static bool dict_items__next__(int argc, py_Ref argv) {
|
||||
bool dict_items__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
DictIterator* iter = py_touserdata(py_arg(0));
|
||||
if(DictIterator__modified(iter)) return RuntimeError("dictionary modified during iteration");
|
||||
DictEntry* entry = (DictIterator__next(iter));
|
||||
if(entry) {
|
||||
switch(iter->mode) {
|
||||
@ -641,4 +727,4 @@ bool py_dict_apply(py_Ref self, bool (*f)(py_Ref, py_Ref, void*), void* ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef PK_DICT_MAX_COLLISION
|
||||
#undef Dict__step
|
@ -245,24 +245,6 @@ static bool float__repr__(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
union c11_8bytes {
|
||||
py_i64 _i64;
|
||||
py_f64 _f64;
|
||||
|
||||
union {
|
||||
uint32_t upper;
|
||||
uint32_t lower;
|
||||
} bits;
|
||||
};
|
||||
|
||||
static py_i64 c11_8bytes__hash(union c11_8bytes u) {
|
||||
// https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
|
||||
const uint32_t C = 2654435761;
|
||||
u.bits.upper *= C;
|
||||
u.bits.lower *= C;
|
||||
return u._i64;
|
||||
}
|
||||
|
||||
static bool int__hash__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
py_assign(py_retval(), argv);
|
||||
@ -272,8 +254,9 @@ static bool int__hash__(int argc, py_Ref argv) {
|
||||
static bool float__hash__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
py_f64 val = py_tofloat(&argv[0]);
|
||||
union c11_8bytes u = {._f64 = val};
|
||||
py_newint(py_retval(), c11_8bytes__hash(u));
|
||||
py_i64 h_user;
|
||||
memcpy(&h_user, &val, sizeof(py_f64));
|
||||
py_newint(py_retval(), h_user);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "pocketpy/interpreter/typeinfo.h"
|
||||
#include "pocketpy/interpreter/bindings.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
#include "pocketpy/objects/base.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
@ -77,12 +78,39 @@ bool py_iter(py_Ref val) {
|
||||
|
||||
int py_next(py_Ref val) {
|
||||
VM* vm = pk_current_vm;
|
||||
py_Ref tmp = py_tpfindmagic(val->type, __next__);
|
||||
if(!tmp) {
|
||||
TypeError("'%t' object is not an iterator", val->type);
|
||||
return -1;
|
||||
|
||||
switch(val->type) {
|
||||
case tp_generator:
|
||||
if(generator__next__(1, val)) return 1;
|
||||
break;
|
||||
case tp_array2d_like_iterator:
|
||||
if(array2d_like_iterator__next__(1, val)) return 1;
|
||||
break;
|
||||
case tp_list_iterator:
|
||||
if(list_iterator__next__(1, val)) return 1;
|
||||
break;
|
||||
case tp_tuple_iterator:
|
||||
if(tuple_iterator__next__(1, val)) return 1;
|
||||
break;
|
||||
case tp_dict_iterator:
|
||||
if(dict_items__next__(1, val)) return 1;
|
||||
break;
|
||||
case tp_range_iterator:
|
||||
if(range_iterator__next__(1, val)) return 1;
|
||||
break;
|
||||
case tp_str_iterator:
|
||||
if(str_iterator__next__(1, val)) return 1;
|
||||
break;
|
||||
default: {
|
||||
py_Ref tmp = py_tpfindmagic(val->type, __next__);
|
||||
if(!tmp) {
|
||||
TypeError("'%t' object is not an iterator", val->type);
|
||||
return -1;
|
||||
}
|
||||
if(py_call(tmp, 1, val)) return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(py_call(tmp, 1, val)) return 1;
|
||||
if(vm->curr_exception.type == tp_StopIteration) {
|
||||
vm->last_retval = vm->curr_exception;
|
||||
py_clearexc(NULL);
|
||||
|
@ -68,9 +68,9 @@ static bool range_iterator__new__(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool range_iterator__next__(int argc, py_Ref argv) {
|
||||
bool range_iterator__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
RangeIterator* ud = py_touserdata(py_arg(0));
|
||||
RangeIterator* ud = py_touserdata(argv);
|
||||
if(ud->range.step > 0) {
|
||||
if(ud->current >= ud->range.stop) return StopIteration();
|
||||
} else {
|
||||
|
@ -513,7 +513,7 @@ py_Type pk_str__register() {
|
||||
return type;
|
||||
}
|
||||
|
||||
static bool str_iterator__next__(int argc, py_Ref argv) {
|
||||
bool str_iterator__next__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
int* ud = py_touserdata(&argv[0]);
|
||||
int size;
|
||||
|
@ -8,12 +8,12 @@ py_Ref py_getreg(int i) { return pk_current_vm->reg + i; }
|
||||
|
||||
void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; }
|
||||
|
||||
py_Ref py_getdict(py_Ref self, py_Name name) {
|
||||
PK_INLINE py_Ref py_getdict(py_Ref self, py_Name name) {
|
||||
assert(self && self->is_ptr);
|
||||
return NameDict__try_get(PyObject__dict(self->_obj), name);
|
||||
}
|
||||
|
||||
void py_setdict(py_Ref self, py_Name name, py_Ref val) {
|
||||
PK_INLINE void py_setdict(py_Ref self, py_Name name, py_Ref val) {
|
||||
assert(self && self->is_ptr);
|
||||
NameDict__set(PyObject__dict(self->_obj), name, val);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "pocketpy/objects/object.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
|
||||
bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
|
||||
PK_INLINE bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
|
||||
|
||||
bool py_checktype(py_Ref self, py_Type type) {
|
||||
if(self->type == type) return true;
|
||||
|
@ -115,30 +115,7 @@ assert a.pop(1) == 2
|
||||
|
||||
assert a.pop(1, None) is None
|
||||
|
||||
n = 2 ** 17
|
||||
a = {}
|
||||
for i in range(n):
|
||||
a[str(i)] = i
|
||||
|
||||
for i in range(n):
|
||||
y = a[str(i)]
|
||||
|
||||
for i in range(n):
|
||||
del a[str(i)]
|
||||
|
||||
# namedict delete test
|
||||
# class A: pass
|
||||
# a = A()
|
||||
# b = ['0', '1']
|
||||
|
||||
# for i in range(len(data)):
|
||||
# z = data[i]
|
||||
# setattr(a, str(z), i)
|
||||
# b.append(z)
|
||||
# if i % 3 == 0:
|
||||
# y = b.pop()
|
||||
# delattr(a, y)
|
||||
|
||||
# test getitem
|
||||
d = {}
|
||||
for i in range(-1000, 1000):
|
||||
d[i] = i
|
||||
@ -155,3 +132,37 @@ assert list(d) == ['1', 222, '333']
|
||||
assert list(d.keys()) == ['1', 222, '333']
|
||||
assert list(d.values()) == [1, 2, 3]
|
||||
assert list(d.items()) == [('1', 1), (222, 2), ('333', 3)]
|
||||
|
||||
# test del
|
||||
n = 2 ** 17
|
||||
a = {}
|
||||
for i in range(n):
|
||||
a[str(i)] = i
|
||||
for i in range(n):
|
||||
del a[str(i)]
|
||||
assert len(a) == 0
|
||||
|
||||
# test del with int keys
|
||||
if 0:
|
||||
n = 2 ** 17
|
||||
a = {}
|
||||
for i in range(n):
|
||||
a[i] = i
|
||||
for i in range(n):
|
||||
del a[i]
|
||||
assert len(a) == 0
|
||||
|
||||
#######################
|
||||
|
||||
# namedict delete test
|
||||
class A: pass
|
||||
a = A()
|
||||
b = ['0', '1']
|
||||
|
||||
for i in range(len(data)):
|
||||
z = data[i]
|
||||
setattr(a, str(z), i)
|
||||
b.append(z)
|
||||
if i % 3 == 0:
|
||||
y = b.pop()
|
||||
delattr(a, y)
|
@ -103,9 +103,4 @@ class A:
|
||||
|
||||
bad_dict = {A(): 1, A(): 2, A(): 3, A(): 4}
|
||||
assert len(bad_dict) == 4
|
||||
try:
|
||||
bad_dict[A()] = 5 # error
|
||||
exit(1)
|
||||
except RuntimeError as e:
|
||||
assert 'maximum collision reached' in str(e)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user