Compare commits

..

6 Commits

Author SHA1 Message Date
blueloveTH
3786abccdd Update _generated.c 2024-12-14 17:16:29 +08:00
blueloveTH
2986c6268f fix type anno 2024-12-14 17:16:15 +08:00
blueloveTH
6b332dbfbb add pkl support for array2d 2024-12-14 17:08:59 +08:00
blueloveTH
d0546c16da ... 2024-12-14 16:06:58 +08:00
blueloveTH
4583b57d12 improve pickle with memo 2024-12-14 16:03:03 +08:00
blueloveTH
5a783d81f5 ... 2024-12-14 14:55:51 +08:00
12 changed files with 266 additions and 65 deletions

View File

@ -20,3 +20,11 @@
#define equal(a, b) (c11_sv__cmp((a), (b)) == 0)
#include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__HEADER
#define SMALLMAP_T__HEADER
#define K void*
#define V int
#define NAME c11_smallmap_p2i
#include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__HEADER

View File

@ -0,0 +1,21 @@
#pragma once
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h"
typedef struct c11_array2d {
py_TValue* data; // slots
int n_cols;
int n_rows;
int numel;
} c11_array2d;
typedef struct c11_array2d_iterator {
c11_array2d* array;
int index;
} c11_array2d_iterator;
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows);

View File

@ -610,12 +610,20 @@ PK_API int py_dict_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN;
PK_API bool py_dict_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
/// -1: error, 0: not found, 1: found (and deleted)
PK_API int py_dict_delitem(py_Ref self, py_Ref key) PY_RAISE;
/// -1: error, 0: not found, 1: found
PK_API int py_dict_getitem_by_str(py_Ref self, const char* key) PY_RAISE PY_RETURN;
/// -1: error, 0: not found, 1: found
PK_API int py_dict_getitem_by_int(py_Ref self, py_i64 key) PY_RAISE PY_RETURN;
/// true: success, false: error
PK_API bool py_dict_setitem_by_str(py_Ref self, const char* key, py_Ref val) PY_RAISE;
/// true: success, false: error
PK_API bool py_dict_setitem_by_int(py_Ref self, py_i64 key, py_Ref val) PY_RAISE;
/// -1: error, 0: not found, 1: found (and deleted)
PK_API int py_dict_delitem_by_str(py_Ref self, const char* key) PY_RAISE;
/// -1: error, 0: not found, 1: found (and deleted)
PK_API int py_dict_delitem_by_int(py_Ref self, py_i64 key) PY_RAISE;
/// true: success, false: error
PK_API bool
py_dict_apply(py_Ref self, bool (*f)(py_Ref key, py_Ref val, void* ctx), void* ctx) PY_RAISE;

View File

@ -35,7 +35,7 @@ class deque(Generic[T]):
_capacity: int
def __init__(self, iterable: Iterable[T] = None):
self._data = [None] * 8 # initial capacity
self._data = [None] * 8 # type: ignore
self._head = 0
self._tail = 0
self._capacity = len(self._data)
@ -98,7 +98,7 @@ class deque(Generic[T]):
def clear(self):
i = self._head
while i != self._tail:
self._data[i] = None
self._data[i] = None # type: ignore
i = (i + 1) % self._capacity
self._head = 0
self._tail = 0

View File

@ -9,12 +9,12 @@ class timedelta:
def __repr__(self):
return f"datetime.timedelta(days={self.days}, seconds={self.seconds})"
def __eq__(self, other: 'timedelta') -> bool:
def __eq__(self, other) -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return (self.days, self.seconds) == (other.days, other.seconds)
def __ne__(self, other: 'timedelta') -> bool:
def __ne__(self, other) -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return (self.days, self.seconds) != (other.days, other.seconds)
@ -40,10 +40,10 @@ class date:
return op(self.month, other.month)
return op(self.day, other.day)
def __eq__(self, other: 'date') -> bool:
def __eq__(self, other) -> bool:
return self.__cmp(other, operator.eq)
def __ne__(self, other: 'date') -> bool:
def __ne__(self, other) -> bool:
return self.__cmp(other, operator.ne)
def __lt__(self, other: 'date') -> bool:

File diff suppressed because one or more lines are too long

View File

@ -16,3 +16,11 @@
#define equal(a, b) (c11_sv__cmp((a), (b)) == 0)
#include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__SOURCE
#define SMALLMAP_T__SOURCE
#define K void*
#define V int
#define NAME c11_smallmap_p2i
#include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__SOURCE

View File

@ -1,20 +1,4 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h"
typedef struct c11_array2d {
py_TValue* data; // slots
int n_cols;
int n_rows;
int numel;
} c11_array2d;
typedef struct c11_array2d_iterator {
c11_array2d* array;
int index;
} c11_array2d_iterator;
#include "pocketpy/interpreter/array2d.h"
static bool py_array2d_is_valid(c11_array2d* self, int col, int row) {
return col >= 0 && col < self->n_cols && row >= 0 && row < self->n_rows;
@ -28,7 +12,7 @@ static void py_array2d__set(c11_array2d* self, int col, int row, py_Ref value) {
self->data[row * self->n_cols + col] = *value;
}
static c11_array2d* py_array2d(py_OutRef out, int n_cols, int n_rows) {
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows) {
int numel = n_cols * n_rows;
c11_array2d* ud = py_newobject(out, tp_array2d, numel, sizeof(c11_array2d));
ud->data = py_getslot(out, 0);
@ -49,7 +33,7 @@ static bool array2d__new__(int argc, py_Ref argv) {
int n_rows = argv[2]._i64;
int numel = n_cols * n_rows;
if(n_cols <= 0 || n_rows <= 0) return ValueError("array2d() expected positive dimensions");
c11_array2d* ud = py_array2d(py_pushtmp(), n_cols, n_rows);
c11_array2d* ud = py_newarray2d(py_pushtmp(), n_cols, n_rows);
// setup initial values
if(py_callable(default_)) {
for(int j = 0; j < n_rows; j++) {
@ -191,7 +175,7 @@ static bool array2d_any(int argc, py_Ref argv) {
static bool array2d__eq__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_array2d* self = py_touserdata(argv);
c11_array2d* res = py_array2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
if(py_istype(py_arg(1), tp_array2d)) {
c11_array2d* other = py_touserdata(py_arg(1));
if(!_array2d_check_same_shape(self, other)) return false;
@ -268,7 +252,7 @@ static bool array2d_map(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_array2d* self = py_touserdata(argv);
py_Ref f = py_arg(1);
c11_array2d* res = py_array2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int i = 0; i < self->numel; i++) {
bool ok = py_call(f, 1, self->data + i);
if(!ok) return false;
@ -283,7 +267,7 @@ static bool array2d_copy(int argc, py_Ref argv) {
// def copy(self) -> 'array2d': ...
PY_CHECK_ARGC(1);
c11_array2d* self = py_touserdata(argv);
c11_array2d* res = py_array2d(py_retval(), self->n_cols, self->n_rows);
c11_array2d* res = py_newarray2d(py_retval(), self->n_cols, self->n_rows);
memcpy(res->data, self->data, self->numel * sizeof(py_TValue));
return true;
}
@ -356,7 +340,7 @@ static bool array2d_fromlist_STATIC(int argc, py_Ref argv) {
return ValueError("fromlist() expected a list of lists with the same length");
}
}
c11_array2d* res = py_array2d(py_retval(), n_cols, n_rows);
c11_array2d* res = py_newarray2d(py_retval(), n_cols, n_rows);
for(int j = 0; j < n_rows; j++) {
py_Ref row_j = py_list_getitem(argv, j);
for(int i = 0; i < n_cols; i++) {
@ -452,7 +436,7 @@ static bool array2d_get_bounding_rect(int argc, py_Ref argv) {
static bool array2d_count_neighbors(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
c11_array2d* self = py_touserdata(argv);
c11_array2d* res = py_array2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
py_Ref value = py_arg(1);
const char* neighborhood = py_tostr(py_arg(2));
@ -556,7 +540,7 @@ static bool array2d__getitem__(int argc, py_Ref argv) {
return _array2d_IndexError(self, col, row);
} else if(py_istype(x, tp_slice) && py_istype(y, tp_slice)) {
HANDLE_SLICE();
c11_array2d* res = py_array2d(py_retval(), slice_width, slice_height);
c11_array2d* res = py_newarray2d(py_retval(), slice_width, slice_height);
for(int j = start_row; j < stop_row; j++) {
for(int i = start_col; i < stop_col; i++) {
py_array2d__set(res, i - start_col, j - start_row, py_array2d__get(self, i, j));
@ -660,7 +644,7 @@ static bool array2d_convolve(int argc, py_Ref argv) {
int ksize_half = ksize / 2;
if(!_array2d_check_all_type(self, tp_int)) return false;
if(!_array2d_check_all_type(kernel, tp_int)) return false;
c11_array2d* res = py_array2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_i64 sum = 0;

View File

@ -3,12 +3,16 @@
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/interpreter/array2d.h"
#include <stdint.h>
typedef enum {
// clang-format off
PKL_NONE,
PKL_MEMO_GET,
PKL_MEMO_SET,
PKL_NONE, PKL_ELLIPSIS,
PKL_INT_0, PKL_INT_1, PKL_INT_2, PKL_INT_3, PKL_INT_4, PKL_INT_5, PKL_INT_6, PKL_INT_7,
PKL_INT_8, PKL_INT_9, PKL_INT_10, PKL_INT_11, PKL_INT_12, PKL_INT_13, PKL_INT_14, PKL_INT_15,
PKL_INT8, PKL_INT16, PKL_INT32, PKL_INT64,
PKL_FLOAT32, PKL_FLOAT64,
PKL_TRUE, PKL_FALSE,
@ -19,24 +23,39 @@ typedef enum {
PKL_VEC2, PKL_VEC3,
PKL_VEC2I, PKL_VEC3I,
PKL_TYPE,
PKL_ARRAY2D,
PKL_EOF,
// clang-format on
} PickleOp;
typedef struct {
c11_smallmap_p2i memo;
c11_vector /*T=char*/ codes;
} PickleObject;
static void PickleObject__ctor(PickleObject* self) { c11_vector__ctor(&self->codes, sizeof(char)); }
typedef struct {
uint16_t memo_length;
} PickleObjectHeader;
static void PickleObject__dtor(PickleObject* self) { c11_vector__dtor(&self->codes); }
static void PickleObject__ctor(PickleObject* self) {
c11_smallmap_p2i__ctor(&self->memo);
c11_vector__ctor(&self->codes, sizeof(char));
}
static void PickleObject__py_submit(PickleObject* self, py_OutRef out) {
int size;
unsigned char* data = c11_vector__submit(&self->codes, &size);
unsigned char* out_data = py_newbytes(out, size);
memcpy(out_data, data, size);
free(data);
static void PickleObject__dtor(PickleObject* self) {
c11_smallmap_p2i__dtor(&self->memo);
c11_vector__dtor(&self->codes);
}
static bool PickleObject__py_submit(PickleObject* self, py_OutRef out) {
unsigned char* data = self->codes.data;
PickleObjectHeader* p =
(PickleObjectHeader*)py_newbytes(out, sizeof(PickleObjectHeader) + self->codes.length);
if(self->memo.length >= UINT16_MAX) c11__abort("PickleObject__py_submit(): memo overflow");
p->memo_length = (uint16_t)self->memo.length;
memcpy(p + 1, data, self->codes.length);
PickleObject__dtor(self);
return true;
}
static void PickleObject__write_bytes(PickleObject* buf, const void* data, int size) {
@ -48,6 +67,10 @@ static void pkl__emit_op(PickleObject* buf, PickleOp op) {
}
static void pkl__emit_int(PickleObject* buf, py_i64 val) {
if(val >= 0 && val <= 15) {
pkl__emit_op(buf, PKL_INT_0 + val);
return;
}
if((int8_t)val == val) {
pkl__emit_op(buf, PKL_INT8);
PickleObject__write_bytes(buf, &val, 1);
@ -73,6 +96,24 @@ static py_i64 pkl__read_int(const unsigned char** p) {
PickleOp op = (PickleOp) * *p;
(*p)++;
switch(op) {
// clang-format off
case PKL_INT_0: return 0;
case PKL_INT_1: return 1;
case PKL_INT_2: return 2;
case PKL_INT_3: return 3;
case PKL_INT_4: return 4;
case PKL_INT_5: return 5;
case PKL_INT_6: return 6;
case PKL_INT_7: return 7;
case PKL_INT_8: return 8;
case PKL_INT_9: return 9;
case PKL_INT_10: return 10;
case PKL_INT_11: return 11;
case PKL_INT_12: return 12;
case PKL_INT_13: return 13;
case PKL_INT_14: return 14;
case PKL_INT_15: return 15;
// clang-format on
case PKL_INT8: {
int8_t val;
UNALIGNED_READ(&val, *p);
@ -144,15 +185,28 @@ static bool pickle__write_dict_kv(py_Ref k, py_Ref v, void* ctx) {
}
static bool pickle__write_object(PickleObject* buf, py_TValue* obj) {
if(obj->is_ptr) {
void* memo_key = obj->_obj;
int index = c11_smallmap_p2i__get(&buf->memo, memo_key, -1);
if(index != -1) {
pkl__emit_op(buf, PKL_MEMO_GET);
pkl__emit_int(buf, index);
return true;
}
}
switch(obj->type) {
case tp_NoneType: {
pkl__emit_op(buf, PKL_NONE);
return true;
break;
}
case tp_ellipsis: {
pkl__emit_op(buf, PKL_ELLIPSIS);
break;
}
case tp_int: {
py_i64 val = obj->_i64;
pkl__emit_int(buf, val);
return true;
break;
}
case tp_float: {
py_f64 val = obj->_f64;
@ -164,19 +218,19 @@ static bool pickle__write_object(PickleObject* buf, py_TValue* obj) {
pkl__emit_op(buf, PKL_FLOAT64);
PickleObject__write_bytes(buf, &val, 8);
}
return true;
break;
}
case tp_bool: {
bool val = obj->_bool;
pkl__emit_op(buf, val ? PKL_TRUE : PKL_FALSE);
return true;
break;
}
case tp_str: {
pkl__emit_op(buf, PKL_STRING);
c11_sv sv = py_tosv(obj);
pkl__emit_int(buf, sv.size);
PickleObject__write_bytes(buf, sv.data, sv.size);
return true;
break;
}
case tp_bytes: {
pkl__emit_op(buf, PKL_BYTES);
@ -184,39 +238,44 @@ static bool pickle__write_object(PickleObject* buf, py_TValue* obj) {
unsigned char* data = py_tobytes(obj, &size);
pkl__emit_int(buf, size);
PickleObject__write_bytes(buf, data, size);
return true;
break;
}
case tp_list: {
return pickle__write_array(buf, PKL_BUILD_LIST, py_list_data(obj), py_list_len(obj));
bool ok = pickle__write_array(buf, PKL_BUILD_LIST, py_list_data(obj), py_list_len(obj));
if(!ok) return false;
break;
}
case tp_tuple: {
return pickle__write_array(buf, PKL_BUILD_TUPLE, py_tuple_data(obj), py_tuple_len(obj));
bool ok =
pickle__write_array(buf, PKL_BUILD_TUPLE, py_tuple_data(obj), py_tuple_len(obj));
if(!ok) return false;
break;
}
case tp_dict: {
bool ok = py_dict_apply(obj, pickle__write_dict_kv, (void*)buf);
if(!ok) return false;
pkl__emit_op(buf, PKL_BUILD_DICT);
pkl__emit_int(buf, py_dict_len(obj));
return true;
break;
}
case tp_vec2: {
c11_vec2 val = py_tovec2(obj);
pkl__emit_op(buf, PKL_VEC2);
PickleObject__write_bytes(buf, &val, sizeof(c11_vec2));
return true;
break;
}
case tp_vec3: {
c11_vec3 val = py_tovec3(obj);
pkl__emit_op(buf, PKL_VEC3);
PickleObject__write_bytes(buf, &val, sizeof(c11_vec3));
return true;
break;
}
case tp_vec2i: {
c11_vec2i val = py_tovec2i(obj);
pkl__emit_op(buf, PKL_VEC2I);
pkl__emit_int(buf, val.x);
pkl__emit_int(buf, val.y);
return true;
break;
}
case tp_vec3i: {
c11_vec3i val = py_tovec3i(obj);
@ -224,7 +283,7 @@ static bool pickle__write_object(PickleObject* buf, py_TValue* obj) {
pkl__emit_int(buf, val.x);
pkl__emit_int(buf, val.y);
pkl__emit_int(buf, val.z);
return true;
break;
}
case tp_type: {
pkl__emit_op(buf, PKL_TYPE);
@ -239,10 +298,32 @@ static bool pickle__write_object(PickleObject* buf, py_TValue* obj) {
// include '\0'
PickleObject__write_bytes(buf, path->data, path->size + 1);
c11_string__delete(path);
return true;
break;
}
case tp_array2d: {
c11_array2d* arr = py_touserdata(obj);
for(int i = 0; i < arr->numel; i++) {
if(arr->data[i].is_ptr)
return TypeError(
"'array2d' object is not picklable because it contains heap-allocated objects");
}
pkl__emit_op(buf, PKL_ARRAY2D);
pkl__emit_int(buf, arr->n_cols);
pkl__emit_int(buf, arr->n_rows);
// TODO: fix type index which is not stable
PickleObject__write_bytes(buf, arr->data, arr->numel * sizeof(py_TValue));
break;
}
default: return TypeError("'%t' object is not picklable", obj->type);
}
if(obj->is_ptr) {
void* memo_key = obj->_obj;
int index = buf->memo.length;
c11_smallmap_p2i__set(&buf->memo, memo_key, index);
pkl__emit_op(buf, PKL_MEMO_SET);
pkl__emit_int(buf, index);
}
return true;
}
bool py_pickle_dumps(py_Ref val) {
@ -254,21 +335,48 @@ bool py_pickle_dumps(py_Ref val) {
return false;
}
pkl__emit_op(&buf, PKL_EOF);
PickleObject__py_submit(&buf, py_retval());
return true;
return PickleObject__py_submit(&buf, py_retval());
}
bool py_pickle_loads(const unsigned char* data, int size) {
PickleObjectHeader* header = (PickleObjectHeader*)data;
const unsigned char* p = (const unsigned char*)(header + 1);
py_StackRef p0 = py_peek(0);
const unsigned char* p = data;
py_StackRef memo = py_pushtmp();
py_newtuple(memo, header->memo_length);
while(true) {
PickleOp op = (PickleOp)*p;
p++;
switch(op) {
case PKL_MEMO_GET: {
int index = pkl__read_int(&p);
py_Ref val = py_tuple_getitem(memo, index);
assert(!py_isnil(val));
py_push(val);
break;
}
case PKL_MEMO_SET: {
int index = pkl__read_int(&p);
py_tuple_setitem(memo, index, py_peek(-1));
break;
}
case PKL_NONE: {
py_pushnone();
break;
}
case PKL_ELLIPSIS: {
py_newellipsis(py_pushtmp());
break;
}
// clang-format off
case PKL_INT_0: case PKL_INT_1: case PKL_INT_2: case PKL_INT_3:
case PKL_INT_4: case PKL_INT_5: case PKL_INT_6: case PKL_INT_7:
case PKL_INT_8: case PKL_INT_9: case PKL_INT_10: case PKL_INT_11:
case PKL_INT_12: case PKL_INT_13: case PKL_INT_14: case PKL_INT_15: {
py_newint(py_pushtmp(), op - PKL_INT_0);
break;
}
// clang-format on
case PKL_INT8: {
int8_t val;
UNALIGNED_READ(&val, p);
@ -410,10 +518,20 @@ bool py_pickle_loads(const unsigned char* data, int size) {
py_push(py_tpobject(t));
break;
}
case PKL_ARRAY2D: {
int n_cols = pkl__read_int(&p);
int n_rows = pkl__read_int(&p);
c11_array2d* arr = py_newarray2d(py_pushtmp(), n_cols, n_rows);
int total_size = arr->numel * sizeof(py_TValue);
memcpy(arr->data, p, total_size);
p += total_size;
break;
}
case PKL_EOF: {
if(py_peek(0) - p0 != 1) { return ValueError("invalid pickle data"); }
py_assign(py_retval(), p0);
py_pop();
// [memo, obj]
if(py_peek(0) - p0 != 2) return ValueError("invalid pickle data");
py_assign(py_retval(), py_peek(-1));
py_shrink(2);
return true;
}
default: c11__unreachable();

View File

@ -316,7 +316,10 @@ static bool builtins_print(int argc, py_Ref argv) {
c11_sbuf__ctor(&buf);
for(int i = 0; i < length; i++) {
if(i > 0) c11_sbuf__write_sv(&buf, sep);
if(!py_str(&args[i])) return false;
if(!py_str(&args[i])) {
c11_sbuf__dtor(&buf);
return false;
}
c11_sbuf__write_sv(&buf, py_tosv(py_retval()));
}
c11_sbuf__write_sv(&buf, end);

View File

@ -632,6 +632,24 @@ int py_dict_delitem_by_str(py_Ref self, const char* key) {
return res;
}
int py_dict_getitem_by_int(py_Ref self, py_i64 key) {
py_TValue tmp;
py_newint(&tmp, key);
return py_dict_getitem(self, &tmp);
}
bool py_dict_setitem_by_int(py_Ref self, py_i64 key, py_Ref val) {
py_TValue tmp;
py_newint(&tmp, key);
return py_dict_setitem(self, &tmp, val);
}
int py_dict_delitem_by_int(py_Ref self, py_i64 key) {
py_TValue tmp;
py_newint(&tmp, key);
return py_dict_delitem(self, &tmp);
}
int py_dict_len(py_Ref self) {
assert(py_isdict(self));
Dict* ud = py_touserdata(self);

View File

@ -7,8 +7,10 @@ def test(data): # type: ignore
o = pkl.loads(b)
print(o)
assert data == o
return o
test(None) # PKL_NONE
test(...) # PKL_ELLIPSIS
test(1) # PKL_INT8
test(277) # PKL_INT16
test(-66666) # PKL_INT32
@ -29,6 +31,20 @@ test(vec3i(1, 2, 3)) # PKL_VEC3I
test(vec3i) # PKL_TYPE
print('-'*50)
from array2d import array2d
a = array2d[int].fromlist([
[1, 2, 3],
[4, 5, 6]
])
a_encoded = pkl.dumps(a)
print(a_encoded)
a_decoded = pkl.loads(a_encoded)
assert isinstance(a_decoded, array2d)
assert a_decoded.width == 3 and a_decoded.height == 2
assert (a == a_decoded).all()
print(a_decoded)
test([1, 2, 3]) # PKL_LIST
test((1, 2, 3)) # PKL_TUPLE
test({1: 2, 3: 4}) # PKL_DICT
@ -39,6 +55,23 @@ test([1, '2', 3.0, True])
test([1, '2', True, {'key': 4}])
test([1, '2', 3.0, True, {'k1': 4, 'k2': [b'xxxx']}])
# test memo
a = [1, 2, 3, 4, 5, 6, 745]
b = [a] * 10
c = test(b)
assert b == c
assert b is not c
assert c[0] is c[1] and c[1] is c[2]
s1 = 'hello'
s2 = 'world'
a = [s1, s2] * 10
b = test(a)
assert b == a
assert b is not a
assert b[0] is b[2]
assert b[1] is b[3]
exit()
from pickle import dumps, loads, _wrap, _unwrap