Impl Dict and DictIter in c11

This commit is contained in:
方而静 2024-06-13 14:09:45 +08:00
parent 59537c9bd0
commit d871d91adb
10 changed files with 495 additions and 263 deletions

View File

@ -74,9 +74,9 @@ struct Generator {
struct DictItemsIter {
PyVar ref;
int i;
pkpy_DictIter it;
DictItemsIter(PyVar ref) : ref(ref) { i = PK_OBJ_GET(Dict, ref)._head_idx; }
DictItemsIter(PyVar ref) : ref(ref) { it = PK_OBJ_GET(Dict, ref).iter(); }
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }

View File

@ -0,0 +1,113 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include "pocketpy/objects/pyvar.h"
#include "pocketpy/common/vector.h"
typedef struct {
unsigned int _version; /** used internelly to detect iterator invalidation */
int count; /** number of elements in the dictionary */
c11_vector _entries; /** contains `pkpy_DictEntry` (hidden type) */
int _htcap; /** capacity of the hashtable, always a power of 2 */
void* _hashtable; /** contains indecies, can be `u8`, `u16` or `u32` according to size*/
} pkpy_Dict;
typedef struct {
const pkpy_Dict* _dict;
unsigned int _version;
int _index;
} pkpy_DictIter;
/**
* @brief `pkpy_Dict` constructor
* @param self `pkpy_Dict` instance
*/
void pkpy_Dict__ctor(pkpy_Dict* self);
/**
* @brief `pkpy_Dict` destructor
* @param self `pkpy_Dict` instance
*/
void pkpy_Dict__dtor(pkpy_Dict* self);
/**
* @brief Copy a `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @return a new `pkpy_Dict` instance, must be destructed by the caller
*/
pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self);
/**
* @brief Set a key-value pair into the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to set
* @param val value to set
* @return `true` if the key is newly added, `false` if the key already exists
*/
bool pkpy_Dict__set(pkpy_Dict* self, void* vm, pkpy_Var key, pkpy_Var val);
/**
* @brief Check if a key exists in the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to check
* @return `true` if the key exists, `false` otherwise
*/
bool pkpy_Dict__contains(const pkpy_Dict* self, void* vm, pkpy_Var key);
/**
* @brief Remove a key from the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to remove
* @return `true` if the key was found and removed, `false` if the key doesn't exist
*/
bool pkpy_Dict__del(pkpy_Dict* self, void* vm, pkpy_Var key);
/**
* @brief Try to get a value from the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to get
* @return the value associated with the key, `NULL` if the key doesn't exist
*/
const pkpy_Var* pkpy_Dict__try_get(const pkpy_Dict* self, void* vm, pkpy_Var key);
/**
* @brief Update the `pkpy_Dict` with another one
* @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param other `pkpy_Dict` instance to update with
*/
void pkpy_Dict__update(pkpy_Dict* self, void *vm, const pkpy_Dict* other);
/**
* @brief Clear the `pkpy_Dict`
* @param self `pkpy_Dict` instance
*/
void pkpy_Dict__clear(pkpy_Dict* self);
/**
* @brief Iterate over the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @return an iterator over the `pkpy_Dict`
*/
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict* self);
/**
* @brief Iterate over the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @param key key will be filled with the current key, can be `NULL` if not needed
* @param value value will be filled with the current value, can be `NULL` if not needed
* @return `true` if the iteration is still valid, `false` otherwise
*/
bool pkpy_DictIter__next(pkpy_DictIter* self, pkpy_Var* key, pkpy_Var* value);
#ifdef __cplusplus
}
#endif

View File

@ -2,63 +2,95 @@
#include "pocketpy/objects/base.hpp"
#include "pocketpy/objects/tuplelist.hpp"
#include "pocketpy/objects/dict.h"
namespace pkpy {
struct Dict {
struct Item {
PyVar first;
PyVar second;
int prev;
int next;
};
struct Dict : private pkpy_Dict {
Dict() {
pkpy_Dict__ctor(this);
}
constexpr static int __Capacity = 8;
constexpr static float __LoadFactor = 0.67f;
Dict(Dict&& other) {
std::memcpy(this, &other, sizeof(Dict));
pkpy_Dict__ctor(&other);
}
int _capacity;
int _mask;
int _size;
int _critical_size;
int _head_idx; // for order preserving
int _tail_idx; // for order preserving
Item* _items;
Dict(const Dict& other) {
// OPTIMIZEME: reduce copy
auto clone = pkpy_Dict__copy(&other);
std::memcpy(this, &clone, sizeof(Dict));
}
Dict();
Dict(Dict&& other);
Dict(const Dict& other);
Dict& operator= (const Dict&) = delete;
Dict& operator= (Dict&&) = delete;
int size() const { return _size; }
int size() const { return count; }
void _probe_0(VM* vm, PyVar key, bool& ok, int& i) const;
void _probe_1(VM* vm, PyVar key, bool& ok, int& i) const;
void set(VM* vm, PyVar key, PyVar val) {
pkpy_Dict__set(this, vm, *reinterpret_cast<::pkpy_Var*>(&key), *reinterpret_cast<::pkpy_Var*>(&val));
}
void set(VM* vm, PyVar key, PyVar val);
void _rehash(VM* vm);
PyVar try_get(VM* vm, PyVar key) const {
auto res = pkpy_Dict__try_get(this, vm, *reinterpret_cast<::pkpy_Var*>(&key));
if (!res) return nullptr;
return *reinterpret_cast<PyVar*>(&res);
}
PyVar try_get(VM* vm, PyVar key) const;
bool contains(VM* vm, PyVar key) const {
return pkpy_Dict__contains(this, vm, *reinterpret_cast<::pkpy_Var*>(&key));
}
bool contains(VM* vm, PyVar key) const;
bool del(VM* vm, PyVar key);
void update(VM* vm, const Dict& other);
bool del(VM* vm, PyVar key) {
return pkpy_Dict__del(this, vm, *reinterpret_cast<::pkpy_Var*>(&key));
}
void update(VM* vm, const Dict& other) {
pkpy_Dict__update(this, vm, &other);
}
template <typename __Func>
void apply(__Func f) const {
int i = _head_idx;
while(i != -1) {
f(_items[i].first, _items[i].second);
i = _items[i].next;
pkpy_DictIter it = iter();
PyVar key, val;
while(pkpy_DictIter__next(&it, reinterpret_cast<::pkpy_Var*>(&key), reinterpret_cast<::pkpy_Var*>(&val))) {
f(key, val);
}
}
Tuple keys() const;
Tuple values() const;
void clear();
~Dict();
Tuple keys() const {
Tuple res(count);
pkpy_DictIter it = iter();
PyVar key, val;
int i = 0;
while(pkpy_DictIter__next(&it, reinterpret_cast<::pkpy_Var*>(&key), reinterpret_cast<::pkpy_Var*>(&val))) {
res[i++] = key;
}
return res;
}
void __alloc_items();
Tuple values() const {
Tuple res(count);
pkpy_DictIter it = iter();
PyVar key, val;
int i = 0;
while(pkpy_DictIter__next(&it, reinterpret_cast<::pkpy_Var*>(&key), reinterpret_cast<::pkpy_Var*>(&val))) {
res[i++] = val;
}
return res;
}
pkpy_DictIter iter() const {
return pkpy_Dict__iter(this);
}
void clear() {
pkpy_Dict__clear(this);
}
~Dict() {
pkpy_Dict__dtor(this);
}
void _gc_mark(VM*) const;
};

View File

@ -0,0 +1,54 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
/**
* @brief A python value in pocketpy.
*/
typedef struct {
// TODO: implement
union {
int type;
char buf[16];
};
} pkpy_Var;
/**
* @brief Check if the pkpy_Var is null.
* @param self The variable to check.
* @return True if the variable is null, false otherwise.
*/
#define pkpy_Var__is_null(self) ((self)->type == 0)
/**
* @brief Set the variable to null.
* @param self The variable to set.
*/
#define pkpy_Var__set_null(self) do { (self)->type = 0; } while(0)
/**
* @brief Check if two pkpy_Vars are equal, respects to __eq__ method.
* @param vm The virtual machine.
* @param a The first pkpy_Var.
* @param b The second pkpy_Var.
* @return True if the pkpy_Vars are equal, false otherwise.
*/
bool pkpy_Var__eq__(void *vm, pkpy_Var a, pkpy_Var b);
/**
* @brief Get the hash of the pkpy_Var, respects to __hash__ method.
* @param vm The virtual machine.
* @param a The pkpy_Var to hash.
* @return The hash of the pkpy_Var.
*/
int64_t pkpy_Var__hash__(void *vm, pkpy_Var a);
#ifdef __cplusplus
}
#endif

View File

@ -117,12 +117,13 @@ void DictItemsIter::_register(VM* vm, PyObject* mod, PyObject* type) {
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
DictItemsIter& self = _CAST(DictItemsIter&, _0);
Dict& d = PK_OBJ_GET(Dict, self.ref);
if(self.i == -1) return 0;
vm->s_data.push(d._items[self.i].first);
vm->s_data.push(d._items[self.i].second);
self.i = d._items[self.i].next;
PyVar key, val;
if (pkpy_DictIter__next(&self.it, reinterpret_cast<::pkpy_Var*>(&key), reinterpret_cast<::pkpy_Var*>(&val))) {
vm->s_data.push(key);
vm->s_data.push(val);
return 2;
}
return 0;
});
}

View File

@ -1628,37 +1628,6 @@ BIND_BINARY_SPECIAL(__xor__)
#undef BIND_BINARY_SPECIAL
void Dict::_probe_0(VM* vm, PyVar key, bool& ok, int& i) const {
ok = false;
i64 hash = vm->py_hash(key);
i = hash & _mask;
for(int j = 0; j < _capacity; j++) {
if(_items[i].first != nullptr) {
if(vm->py_eq(_items[i].first, key)) {
ok = true;
break;
}
} else {
if(_items[i].second == nullptr) break;
}
// https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
i = ((5 * i) + 1) & _mask;
}
}
void Dict::_probe_1(VM* vm, PyVar key, bool& ok, int& i) const {
ok = false;
i = vm->py_hash(key) & _mask;
while(_items[i].first != nullptr) {
if(vm->py_eq(_items[i].first, key)) {
ok = true;
break;
}
// https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
i = ((5 * i) + 1) & _mask;
}
}
#if PK_ENABLE_PROFILER
void NextBreakpoint::_step(VM* vm) {
int curr_callstack_size = vm->callstack.size();

228
src/objects/dict.c Normal file
View File

@ -0,0 +1,228 @@
#include "pocketpy/objects/dict.h"
#include "pocketpy/common/utils.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
struct pkpy_DictEntry {
int64_t hash;
pkpy_Var key;
pkpy_Var val;
};
inline static int pkpy_Dict__idx_size(const pkpy_Dict* self) {
if(self->count < 255) return 1;
if(self->count < 65535) return 2;
return 4;
}
inline static int pkpy_Dict__idx_null(const pkpy_Dict* self) {
if(self->count < 255) return 255;
if(self->count < 65535) return 65535;
return 4294967295;
}
inline static int pkpy_Dict__ht_byte_size(const pkpy_Dict* self) { return self->_htcap * pkpy_Dict__idx_size(self); }
void pkpy_Dict__ctor(pkpy_Dict* self) {
self->_version = 0;
self->count = 0;
c11_vector__ctor(&self->_entries, sizeof(struct pkpy_DictEntry));
self->_htcap = 16;
self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
}
void pkpy_Dict__dtor(pkpy_Dict* self) {
c11_vector__dtor(&self->_entries);
free(self->_hashtable);
}
pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self) {
int ht_size = pkpy_Dict__ht_byte_size(self);
void* ht_clone = malloc(ht_size);
memcpy(ht_clone, self->_hashtable, ht_size);
return (pkpy_Dict){._version = 0,
.count = self->count,
._entries = c11_vector__copy(&self->_entries),
._htcap = self->_htcap,
._hashtable = ht_clone};
}
static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
int sz = pkpy_Dict__idx_size(self);
switch(sz) {
case 1: return ((uint8_t*)self->_hashtable)[h];
case 2: return ((uint16_t*)self->_hashtable)[h];
case 4: return ((uint32_t*)self->_hashtable)[h];
default: PK_UNREACHABLE();
}
}
static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) {
int sz = pkpy_Dict__idx_size(self);
switch(sz) {
case 1: ((uint8_t*)self->_hashtable)[h] = v; break;
case 2: ((uint16_t*)self->_hashtable)[h] = v; break;
case 4: ((uint32_t*)self->_hashtable)[h] = v; break;
default: PK_UNREACHABLE();
}
}
static int pkpy_Dict__probe(const pkpy_Dict* self, void* vm, pkpy_Var key, int64_t hash) {
const int null = pkpy_Dict__idx_null(self);
const int mask = self->_htcap - 1;
for(int h = hash & mask;; h = (h + 1) & mask) {
int idx = pkpy_Dict__htget(self, h);
if(idx == null) return h;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
if(pkpy_Var__is_null(&entry->key)) return h;
if(entry->hash == hash && pkpy_Var__eq__(vm, entry->key, key)) return h;
}
PK_UNREACHABLE();
}
static void pkpy_Dict__extendht(pkpy_Dict* self, void* vm) {
self->_version += 1;
free(self->_hashtable);
self->_htcap *= 2;
void* new_ht = malloc(pkpy_Dict__ht_byte_size(self));
memset(new_ht, 0xff, pkpy_Dict__ht_byte_size(self));
for(int i = 0; i < self->_entries.count; i++) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i);
if(pkpy_Var__is_null(&entry->key)) continue;
int h = pkpy_Dict__probe(self, vm, entry->key, entry->hash);
pkpy_Dict__htset(self, h, i);
}
}
static bool pkpy_Dict__refactor(pkpy_Dict* self, void* vm) {
int deleted_slots = self->_entries.count - self->count;
if(deleted_slots < self->_entries.count * 0.25) return false;
// shrink
self->_version += 1;
free(self->_hashtable);
while(self->_htcap * 0.375 > self->count && self->_htcap >= 32)
self->_htcap /= 2;
self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
c11_vector new_entries;
c11_vector__ctor(&new_entries, sizeof(struct pkpy_DictEntry));
for(int i = 0; i < self->_entries.count; i++) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i);
if(pkpy_Var__is_null(&entry->key)) continue;
int j = new_entries.count;
c11_vector__push(struct pkpy_DictEntry, &new_entries, *entry);
pkpy_Dict__htset(self, pkpy_Dict__probe(self, vm, entry->key, entry->hash), j);
}
c11_vector__dtor(&self->_entries);
self->_entries = new_entries;
return true;
}
bool pkpy_Dict__set(pkpy_Dict* self, void* vm, pkpy_Var key, pkpy_Var val) {
int hash = pkpy_Var__hash__(vm, key);
int h = pkpy_Dict__probe(self, vm, key, hash);
int idx = pkpy_Dict__htget(self, h);
if(idx == pkpy_Dict__idx_null(self)) {
self->_version += 1;
idx = self->_entries.count;
c11_vector__push(struct pkpy_DictEntry,
&self->_entries,
((struct pkpy_DictEntry){
.hash = hash,
.key = key,
.val = val,
}));
pkpy_Dict__htset(self, h, idx);
if(self->count >= self->_htcap * 0.75) pkpy_Dict__extendht(self, vm);
return true;
}
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
assert(entry->hash == hash && pkpy_Var__eq__(vm, entry->key, key));
entry->val = val;
return false;
}
bool pkpy_Dict__contains(const pkpy_Dict* self, void* vm, pkpy_Var key) {
int hash = pkpy_Var__hash__(vm, key);
int h = pkpy_Dict__probe(self, vm, key, hash);
int idx = pkpy_Dict__htget(self, h);
if(idx == pkpy_Dict__idx_null(self)) return false;
return true;
}
bool pkpy_Dict__del(pkpy_Dict* self, void* vm, pkpy_Var key) {
int hash = pkpy_Var__hash__(vm, key);
int h = pkpy_Dict__probe(self, vm, key, hash);
int idx = pkpy_Dict__htget(self, h), null = pkpy_Dict__idx_null(self);
if(idx == null) return false;
self->_version += 1;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
assert(entry->hash == hash && pkpy_Var__eq__(vm, entry->key, key));
pkpy_Var__set_null(&entry->key);
pkpy_Dict__htset(self, h, null);
pkpy_Dict__refactor(self, vm);
return true;
}
const pkpy_Var *pkpy_Dict__try_get(const pkpy_Dict* self, void* vm, pkpy_Var key) {
int hash = pkpy_Var__hash__(vm, key);
int h = pkpy_Dict__probe(self, vm, key, hash);
int idx = pkpy_Dict__htget(self, h);
if(idx == pkpy_Dict__idx_null(self)) return NULL;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
assert(entry->hash == hash && pkpy_Var__eq__(vm, entry->key, key));
return &entry->val;
}
void pkpy_Dict__update(pkpy_Dict *self, void *vm, const pkpy_Dict *other) {
for(int i = 0; i < other->_entries.count; i++) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &other->_entries, i);
if(pkpy_Var__is_null(&entry->key)) continue;
pkpy_Dict__set(self, vm, entry->key, entry->val);
}
}
void pkpy_Dict__clear(pkpy_Dict *self) {
int v = self->_version;
pkpy_Dict__dtor(self);
pkpy_Dict__ctor(self);
self->_version = v + 1;
}
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict *self) {
return (pkpy_DictIter){
._dict = self,
._index = 0,
._version = self->_version,
};
}
bool pkpy_DictIter__next(pkpy_DictIter *self, pkpy_Var *key, pkpy_Var *val) {
if(self->_version != self->_dict->_version) return false;
if(self->_index >= self->_dict->_entries.count) return false;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_dict->_entries, self->_index);
assert(!pkpy_Var__is_null(&entry->key));
if (key) *key = entry->key;
if (val) *val = entry->val;
while (self->_index < self->_dict->_entries.count) {
self->_index++;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_dict->_entries, self->_index);
if(!pkpy_Var__is_null(&entry->key)) break;
}
return true;
}

View File

@ -1,180 +0,0 @@
#include "pocketpy/objects/dict.hpp"
namespace pkpy {
Dict::Dict() :
_capacity(__Capacity), _mask(__Capacity - 1), _size(0), _critical_size(__Capacity * __LoadFactor + 0.5f),
_head_idx(-1), _tail_idx(-1) {
__alloc_items();
}
void Dict::__alloc_items() {
_items = (Item*)std::malloc(_capacity * sizeof(Item));
for(int i = 0; i < _capacity; i++) {
_items[i].first = nullptr;
_items[i].second = nullptr;
_items[i].prev = -1;
_items[i].next = -1;
}
}
Dict::Dict(Dict&& other) {
_capacity = other._capacity;
_mask = other._mask;
_size = other._size;
_critical_size = other._critical_size;
_head_idx = other._head_idx;
_tail_idx = other._tail_idx;
_items = other._items;
other._items = nullptr;
}
Dict::Dict(const Dict& other) {
_capacity = other._capacity;
_mask = other._mask;
_size = other._size;
_critical_size = other._critical_size;
_head_idx = other._head_idx;
_tail_idx = other._tail_idx;
// copy items
_items = (Item*)std::malloc(_capacity * sizeof(Item));
std::memcpy(_items, other._items, _capacity * sizeof(Item));
}
void Dict::set(VM* vm, PyVar key, PyVar val) {
// do possible rehash
if(_size + 1 > _critical_size) _rehash(vm);
bool ok;
int i;
_probe_1(vm, key, ok, i);
if(!ok) {
_size++;
_items[i].first = key;
// append to tail
if(_size == 0 + 1) {
_head_idx = i;
_tail_idx = i;
} else {
_items[i].prev = _tail_idx;
_items[_tail_idx].next = i;
_tail_idx = i;
}
}
_items[i].second = val;
}
void Dict::_rehash(VM* vm) {
Item* old_items = _items;
int old_head_idx = _head_idx;
_capacity *= 4;
_mask = _capacity - 1;
_size = 0;
_critical_size = _capacity * __LoadFactor + 0.5f;
_head_idx = -1;
_tail_idx = -1;
__alloc_items();
// copy old items to new dict
int i = old_head_idx;
while(i != -1) {
set(vm, old_items[i].first, old_items[i].second);
i = old_items[i].next;
}
std::free(old_items);
}
PyVar Dict::try_get(VM* vm, PyVar key) const {
bool ok;
int i;
_probe_0(vm, key, ok, i);
if(!ok) return nullptr;
return _items[i].second;
}
bool Dict::contains(VM* vm, PyVar key) const {
bool ok;
int i;
_probe_0(vm, key, ok, i);
return ok;
}
bool Dict::del(VM* vm, PyVar key) {
bool ok;
int i;
_probe_0(vm, key, ok, i);
if(!ok) return false;
_items[i].first = nullptr;
// _items[i].second = PY_DELETED_SLOT; // do not change .second if it is not NULL, it means the slot is occupied by
// a deleted item
_size--;
if(_size == 0) {
_head_idx = -1;
_tail_idx = -1;
} else {
if(_head_idx == i) {
_head_idx = _items[i].next;
_items[_head_idx].prev = -1;
} else if(_tail_idx == i) {
_tail_idx = _items[i].prev;
_items[_tail_idx].next = -1;
} else {
_items[_items[i].prev].next = _items[i].next;
_items[_items[i].next].prev = _items[i].prev;
}
}
_items[i].prev = -1;
_items[i].next = -1;
return true;
}
void Dict::update(VM* vm, const Dict& other) {
other.apply([&](PyVar k, PyVar v) {
set(vm, k, v);
});
}
Tuple Dict::keys() const {
Tuple t(_size);
int i = _head_idx;
int j = 0;
while(i != -1) {
t[j++] = _items[i].first;
i = _items[i].next;
}
assert(j == _size);
return t;
}
Tuple Dict::values() const {
Tuple t(_size);
int i = _head_idx;
int j = 0;
while(i != -1) {
t[j++] = _items[i].second;
i = _items[i].next;
}
assert(j == _size);
return t;
}
void Dict::clear() {
_size = 0;
_head_idx = -1;
_tail_idx = -1;
for(int i = 0; i < _capacity; i++) {
_items[i].first = nullptr;
_items[i].second = nullptr;
_items[i].prev = -1;
_items[i].next = -1;
}
}
Dict::~Dict() {
if(_items) std::free(_items);
}
} // namespace pkpy

17
src/objects/pyvar.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "pocketpy/objects/base.hpp"
#include "pocketpy/objects/pyvar.h"
#include "pocketpy/interpreter/vm.hpp"
extern "C" {
bool pkpy_Var__eq__(void *vm_, pkpy_Var a, pkpy_Var b) {
auto vm = static_cast<pkpy::VM *>(vm_);
return vm->py_eq(*reinterpret_cast<pkpy::PyVar*>(&a), *reinterpret_cast<pkpy::PyVar*>(&b));
}
int64_t pkpy_Var__hash__(void *vm_, pkpy_Var a) {
auto vm = static_cast<pkpy::VM *>(vm_);
return vm->py_hash(*reinterpret_cast<pkpy::PyVar*>(&a));
}
}

View File

@ -1493,12 +1493,10 @@ void __init_builtins(VM* _vm) {
if(!vm->isinstance(_1, vm->tp_dict)) return vm->NotImplemented;
Dict& other = _CAST(Dict&, _1);
if(self.size() != other.size()) return vm->False;
for(int i = 0; i < self._capacity; i++) {
auto item = self._items[i];
if(item.first == nullptr) continue;
PyVar value = other.try_get(vm, item.first);
if(value == nullptr) return vm->False;
if(!vm->py_eq(item.second, value)) return vm->False;
pkpy_DictIter it = self.iter();
PyVar key, val;
while(pkpy_DictIter__next(&it, reinterpret_cast<::pkpy_Var*>(&key), reinterpret_cast<::pkpy_Var*>(&val))) {
if(!vm->py_eq(val, other.try_get(vm, key))) return vm->False;
}
return vm->True;
});