*
* 设计取舍 (为什么这样一个结构最"对标 PHP"):
* PHP 的 array 同时是 list 与 map, 且保留插入顺序。这是它"一把梭"写业务
* 的核心。我们用一个统一结构实现: 整数键(0..n-1 自动追加) 与 字符串键共存,
* 底层是哈希表(开放链地址) + 插入顺序数组, 二者共享同一批 ArrEntry 节点。
*
* 按引用语义共享(同 Lua table / Python list): 赋值把一个数组的指针交给变量,
* 多个变量可指向同一个数组并看到彼此的修改。数据全部分配在 arena, 随运行时
* 销毁, 运行期不单独释放——刻意避免为教学过程引入 GC 复杂度。
*/
#include "cvm_core.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static unsigned long long arr_fnv(const char *s) {
unsigned long long h = 1469598103934665603ULL;
while (*s) { h ^= (unsigned char)(*s++); h *= 1099511628211ULL; }
return h;
}
static unsigned long long arr_ihash(int64_t k) {
unsigned long long h = 1469598103934665603ULL;
unsigned char *p = (unsigned char *)&k;
for (int i = 0; i < (int)sizeof(int64_t); ++i) { h ^= p[i]; h *= 1099511628211ULL; }
return h;
}
static int bucket_of(int cap, int is_str, const char *skey, int64_t ikey) {
unsigned long long h = is_str ? arr_fnv(skey) : arr_ihash(ikey);
return (int)(h % (unsigned long long)cap);
}
static void arr_grow_order(Arena *a, Arr *arr) {
if (arr->count < arr->order_cap) return;
int nc = arr->order_cap ? arr->order_cap * 2 : 8;
ArrEntry **no = (ArrEntry **)arena_alloc(a, (size_t)nc * sizeof(ArrEntry *));
if (arr->count) memcpy(no, arr->order, (size_t)arr->count * sizeof(ArrEntry *));
arr->order = no;
arr->order_cap = nc;
}
static void arr_grow_buckets(Arena *a, Arr *arr) {
int nc = arr->cap * 2;
ArrEntry **nb = (ArrEntry **)arena_alloc(a, (size_t)nc * sizeof(ArrEntry *));
memset(nb, 0, (size_t)nc * sizeof(ArrEntry *));
for (int i = 0; i < arr->count; ++i) {
ArrEntry *e = arr->order[i];
int b = bucket_of(nc, e->is_str, e->skey, e->ikey);
e->hnext = nb[b];
nb[b] = e;
}
arr->buckets = nb;
arr->cap = nc;
}
void arr_init(Arena *a, Arr *arr) {
arr->cap = 8;
arr->count = 0;
arr->buckets = (ArrEntry **)arena_alloc(a, (size_t)arr->cap * sizeof(ArrEntry *));
memset(arr->buckets, 0, (size_t)arr->cap * sizeof(ArrEntry *));
arr->order = NULL;
arr->order_cap = 0;
arr->next_ikey = 0;
}
ArrEntry *arr_find(int is_str, const char *skey, int64_t ikey, const Arr *arr) {
int b = bucket_of(arr->cap, is_str, skey, ikey);
for (ArrEntry *e = arr->buckets[b]; e; e = e->hnext) {
if (is_str) {
if (e->is_str && strcmp(e->skey, skey) == 0) return e;
} else {
if (!e->is_str && e->ikey == ikey) return e;
}
}
return NULL;
}
static int key_of(Value key, int *is_str, const char **skey, int64_t *ikey) {
if (key.type == VAL_INT) { *is_str = 0; *ikey = key.as.i; *skey = NULL; return 1; }
if (key.type == VAL_NUM) { *is_str = 0; *ikey = (int64_t)key.as.num; *skey = NULL; return 1; }
if (key.type == VAL_STR) { *is_str = 1; *skey = key.as.str; *ikey = 0; return 1; }
if (key.type == VAL_BOOL) { *is_str = 0; *ikey = key.as.boolean ? 1 : 0; *skey = NULL; return 1; }
return 0;
}
ArrEntry *arr_lookup(Runtime *rt, Arr *arr, Value key) {
int is_str; const char *sk; int64_t ik;
if (!key_of(key, &is_str, &sk, &ik)) {
snprintf(rt->errbuf, sizeof(rt->errbuf), "数组索引必须是数字、字符串或布尔");
rt->has_error = 1;
return NULL;
}
return arr_find(is_str, sk, ik, arr);
}
void arr_set(Arena *a, Arr *arr, int is_str, const char *skey, int64_t ikey, Value v) {
ArrEntry *e = arr_find(is_str, skey, ikey, arr);
if (e) { e->value = v; return; }
if (arr->count + 1 > arr->cap) arr_grow_buckets(a, arr);
e = (ArrEntry *)arena_alloc(a, sizeof(ArrEntry));
e->is_str = is_str;
e->skey = is_str ? (char *)skey : NULL;
e->ikey = is_str ? 0 : ikey;
e->value = v;
int b = bucket_of(arr->cap, is_str, skey, ikey);
e->hnext = arr->buckets[b];
arr->buckets[b] = e;
arr_grow_order(a, arr);
arr->order[arr->count++] = e;
if (!is_str && ikey >= arr->next_ikey) arr->next_ikey = ikey + 1;
}
void arr_append(Arena *a, Arr *arr, Value v) {
arr_set(a, arr, 0, NULL, arr->next_ikey, v);
}
void arr_set_from_key(Runtime *rt, Arr *arr, Value key, Value v) {
int is_str; const char *sk; int64_t ik;
if (!key_of(key, &is_str, &sk, &ik)) {
snprintf(rt->errbuf, sizeof(rt->errbuf), "数组键必须是数字、字符串或布尔");
rt->has_error = 1;
return;
}
arr_set(&rt->arena, arr, is_str, sk, ik, v);
}
int arr_del(Arr *arr, Value key) {
int is_str; const char *sk; int64_t ik;
if (!key_of(key, &is_str, &sk, &ik)) return 0;
int b = bucket_of(arr->cap, is_str, sk, ik);
ArrEntry *prev = NULL, *e = arr->buckets[b];
while (e) {
int hit = is_str ? (e->is_str && strcmp(e->skey, sk) == 0)
: (!e->is_str && e->ikey == ik);
if (hit) {
if (prev) prev->hnext = e->hnext;
else arr->buckets[b] = e->hnext;
for (int i = 0; i < arr->count; ++i)
if (arr->order[i] == e) {
memmove(&arr->order[i], &arr->order[i + 1],
(size_t)(arr->count - i - 1) * sizeof(ArrEntry *));
break;
}
arr->count--;
return 1;
}
prev = e; e = e->hnext;
}
return 0;
}
int arr_len(const Arr *arr) { return arr->count; }
ArrEntry *arr_at(const Arr *arr, int idx) {
if (idx < 0 || idx >= arr->count) return NULL;
return arr->order[idx];
}
Arr *arr_copy(Arena *a, const Arr *src) {
Arr *d = (Arr *)arena_alloc(a, sizeof(Arr));
arr_init(a, d);
for (int i = 0; i < src->count; ++i) {
ArrEntry *e = src->order[i];
arr_set(a, d, e->is_str, e->skey, e->ikey, e->value);
}
return d;
}
Arr *arr_keys(Arena *a, const Arr *src) {
Arr *d = (Arr *)arena_alloc(a, sizeof(Arr));
arr_init(a, d);
for (int i = 0; i < src->count; ++i) {
ArrEntry *e = src->order[i];
Value k = e->is_str ? val_str(e->skey) : val_int(e->ikey);
arr_append(a, d, k);
}
return d;
}
Arr *arr_values(Arena *a, const Arr *src) {
Arr *d = (Arr *)arena_alloc(a, sizeof(Arr));
arr_init(a, d);
for (int i = 0; i < src->count; ++i)
arr_append(a, d, src->order[i]->value);
return d;
}