|
#define _POSIX_C_SOURCE 200809L
|
|
#include "runtime.h"
|
|
#include "../utils/safe_alloc.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
static inline uint32_t _rava_field_hash(const char *name) {
|
|
uint32_t hash = 5381;
|
|
while (*name) {
|
|
hash = ((hash << 5) + hash) ^ (uint32_t)*name++;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
RavaObject_t* rava_object_create(const char *class_name) {
|
|
RavaObject_t *obj = calloc(1, sizeof(RavaObject_t));
|
|
if (!obj) return NULL;
|
|
obj->class_name = strdup(class_name);
|
|
obj->field_capacity = 8;
|
|
obj->field_names = calloc(obj->field_capacity, sizeof(char*));
|
|
obj->field_values = calloc(obj->field_capacity, sizeof(RavaValue_t));
|
|
if (!obj->field_names || !obj->field_values) {
|
|
free(obj->field_names);
|
|
free(obj->field_values);
|
|
free(obj->class_name);
|
|
free(obj);
|
|
return NULL;
|
|
}
|
|
obj->field_count = 0;
|
|
for (int i = 0; i < RAVA_OBJECT_HASH_SIZE; i++) {
|
|
obj->field_hash[i] = -1;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
void rava_object_destroy(RavaObject_t *obj) {
|
|
if (!obj) return;
|
|
free(obj->class_name);
|
|
for (size_t i = 0; i < obj->field_count; i++) {
|
|
free(obj->field_names[i]);
|
|
}
|
|
free(obj->field_names);
|
|
free(obj->field_values);
|
|
free(obj);
|
|
}
|
|
|
|
void rava_object_set_field(RavaObject_t *obj, const char *name, RavaValue_t value) {
|
|
if (!obj || !name) return;
|
|
|
|
uint32_t hash = _rava_field_hash(name);
|
|
|
|
for (size_t probe = 0; probe < RAVA_OBJECT_HASH_SIZE; probe++) {
|
|
uint32_t idx = (hash + probe) & (RAVA_OBJECT_HASH_SIZE - 1);
|
|
int field_idx = obj->field_hash[idx];
|
|
|
|
if (field_idx == -1) {
|
|
if (obj->field_count >= obj->field_capacity) {
|
|
size_t new_cap = obj->field_capacity * 2;
|
|
char **new_names = rava_safe_realloc(obj->field_names, new_cap * sizeof(char*));
|
|
RavaValue_t *new_values = rava_safe_realloc(obj->field_values, new_cap * sizeof(RavaValue_t));
|
|
if (!new_names || !new_values) return;
|
|
obj->field_names = new_names;
|
|
obj->field_values = new_values;
|
|
obj->field_capacity = new_cap;
|
|
}
|
|
obj->field_names[obj->field_count] = strdup(name);
|
|
obj->field_values[obj->field_count] = value;
|
|
obj->field_hash[idx] = (int)obj->field_count;
|
|
obj->field_count++;
|
|
return;
|
|
}
|
|
|
|
if ((size_t)field_idx < obj->field_count &&
|
|
strcmp(obj->field_names[field_idx], name) == 0) {
|
|
obj->field_values[field_idx] = value;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
RavaValue_t rava_object_get_field(RavaObject_t *obj, const char *name) {
|
|
if (!obj || !name) return rava_value_null();
|
|
|
|
uint32_t hash = _rava_field_hash(name);
|
|
|
|
for (size_t probe = 0; probe < RAVA_OBJECT_HASH_SIZE; probe++) {
|
|
uint32_t idx = (hash + probe) & (RAVA_OBJECT_HASH_SIZE - 1);
|
|
int field_idx = obj->field_hash[idx];
|
|
|
|
if (field_idx == -1) {
|
|
return rava_value_null();
|
|
}
|
|
|
|
if ((size_t)field_idx < obj->field_count &&
|
|
strcmp(obj->field_names[field_idx], name) == 0) {
|
|
return obj->field_values[field_idx];
|
|
}
|
|
}
|
|
|
|
return rava_value_null();
|
|
}
|
|
|
|
int rava_object_get_field_index(RavaObject_t *obj, const char *name) {
|
|
if (!obj || !name) return -1;
|
|
|
|
uint32_t hash = _rava_field_hash(name);
|
|
|
|
for (size_t probe = 0; probe < RAVA_OBJECT_HASH_SIZE; probe++) {
|
|
uint32_t idx = (hash + probe) & (RAVA_OBJECT_HASH_SIZE - 1);
|
|
int field_idx = obj->field_hash[idx];
|
|
|
|
if (field_idx == -1) return -1;
|
|
|
|
if ((size_t)field_idx < obj->field_count &&
|
|
strcmp(obj->field_names[field_idx], name) == 0) {
|
|
return field_idx;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
RavaValue_t rava_object_get_field_by_index(RavaObject_t *obj, int index) {
|
|
if (!obj || index < 0 || (size_t)index >= obj->field_count) {
|
|
return rava_value_null();
|
|
}
|
|
return obj->field_values[index];
|
|
}
|
|
|
|
void rava_object_set_field_by_index(RavaObject_t *obj, int index, RavaValue_t value) {
|
|
if (!obj || index < 0 || (size_t)index >= obj->field_count) return;
|
|
obj->field_values[index] = value;
|
|
}
|
|
|
|
int32_t rava_object_hashcode(RavaValue_t value) {
|
|
switch (value.type) {
|
|
case RAVA_VAL_NULL:
|
|
return 0;
|
|
case RAVA_VAL_INT:
|
|
return value.data.int_val;
|
|
case RAVA_VAL_LONG: {
|
|
int64_t v = value.data.long_val;
|
|
return (int32_t)(v ^ (v >> 32));
|
|
}
|
|
case RAVA_VAL_FLOAT: {
|
|
union { float f; int32_t i; } u;
|
|
u.f = value.data.float_val;
|
|
return u.i;
|
|
}
|
|
case RAVA_VAL_DOUBLE: {
|
|
union { double d; int64_t l; } u;
|
|
u.d = value.data.double_val;
|
|
return (int32_t)(u.l ^ (u.l >> 32));
|
|
}
|
|
case RAVA_VAL_BOOLEAN:
|
|
return value.data.bool_val ? 1231 : 1237;
|
|
case RAVA_VAL_CHAR:
|
|
return (int32_t)value.data.char_val;
|
|
case RAVA_VAL_STRING: {
|
|
if (!value.data.string_val) return 0;
|
|
int32_t hash = 0;
|
|
for (const char *p = value.data.string_val; *p; p++) {
|
|
hash = 31 * hash + (int32_t)(unsigned char)*p;
|
|
}
|
|
return hash;
|
|
}
|
|
case RAVA_VAL_OBJECT:
|
|
case RAVA_VAL_ARRAY:
|
|
case RAVA_VAL_ARRAYLIST:
|
|
case RAVA_VAL_HASHMAP:
|
|
return (int32_t)((uintptr_t)value.data.object_val >> 3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool rava_object_equals(RavaValue_t a, RavaValue_t b) {
|
|
if (a.type == RAVA_VAL_NULL && b.type == RAVA_VAL_NULL) {
|
|
return true;
|
|
}
|
|
if (a.type == RAVA_VAL_NULL || b.type == RAVA_VAL_NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING) {
|
|
if (!a.data.string_val && !b.data.string_val) return true;
|
|
if (!a.data.string_val || !b.data.string_val) return false;
|
|
return strcmp(a.data.string_val, b.data.string_val) == 0;
|
|
}
|
|
|
|
if (a.type != b.type) {
|
|
return false;
|
|
}
|
|
|
|
switch (a.type) {
|
|
case RAVA_VAL_INT:
|
|
return a.data.int_val == b.data.int_val;
|
|
case RAVA_VAL_LONG:
|
|
return a.data.long_val == b.data.long_val;
|
|
case RAVA_VAL_FLOAT:
|
|
return a.data.float_val == b.data.float_val;
|
|
case RAVA_VAL_DOUBLE:
|
|
return a.data.double_val == b.data.double_val;
|
|
case RAVA_VAL_BOOLEAN:
|
|
return a.data.bool_val == b.data.bool_val;
|
|
case RAVA_VAL_CHAR:
|
|
return a.data.char_val == b.data.char_val;
|
|
case RAVA_VAL_OBJECT:
|
|
case RAVA_VAL_ARRAY:
|
|
case RAVA_VAL_ARRAYLIST:
|
|
case RAVA_VAL_HASHMAP:
|
|
return a.data.object_val == b.data.object_val;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char* rava_object_tostring(RavaValue_t value) {
|
|
char *buffer = malloc(128);
|
|
if (!buffer) return NULL;
|
|
|
|
switch (value.type) {
|
|
case RAVA_VAL_NULL:
|
|
strcpy(buffer, "null");
|
|
break;
|
|
case RAVA_VAL_INT:
|
|
snprintf(buffer, 128, "%d", value.data.int_val);
|
|
break;
|
|
case RAVA_VAL_LONG:
|
|
snprintf(buffer, 128, "%ld", value.data.long_val);
|
|
break;
|
|
case RAVA_VAL_FLOAT:
|
|
snprintf(buffer, 128, "%g", (double)value.data.float_val);
|
|
break;
|
|
case RAVA_VAL_DOUBLE:
|
|
snprintf(buffer, 128, "%g", value.data.double_val);
|
|
break;
|
|
case RAVA_VAL_BOOLEAN:
|
|
strcpy(buffer, value.data.bool_val ? "true" : "false");
|
|
break;
|
|
case RAVA_VAL_CHAR:
|
|
snprintf(buffer, 128, "%c", value.data.char_val);
|
|
break;
|
|
case RAVA_VAL_STRING:
|
|
free(buffer);
|
|
return value.data.string_val ? strdup(value.data.string_val) : strdup("null");
|
|
case RAVA_VAL_OBJECT:
|
|
if (!value.data.object_val) {
|
|
strcpy(buffer, "null");
|
|
} else {
|
|
snprintf(buffer, 128, "%s@%x",
|
|
value.data.object_val->class_name,
|
|
(unsigned int)((uintptr_t)value.data.object_val >> 3));
|
|
}
|
|
break;
|
|
case RAVA_VAL_ARRAY:
|
|
if (!value.data.array_val) {
|
|
strcpy(buffer, "null");
|
|
} else {
|
|
snprintf(buffer, 128, "[array@%x]",
|
|
(unsigned int)((uintptr_t)value.data.array_val >> 3));
|
|
}
|
|
break;
|
|
case RAVA_VAL_ARRAYLIST:
|
|
snprintf(buffer, 128, "ArrayList@%x",
|
|
(unsigned int)((uintptr_t)value.data.arraylist_val >> 3));
|
|
break;
|
|
case RAVA_VAL_HASHMAP:
|
|
snprintf(buffer, 128, "HashMap@%x",
|
|
(unsigned int)((uintptr_t)value.data.hashmap_val >> 3));
|
|
break;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
const char* rava_object_getclass(RavaValue_t value) {
|
|
switch (value.type) {
|
|
case RAVA_VAL_NULL:
|
|
return "null";
|
|
case RAVA_VAL_INT:
|
|
return "Integer";
|
|
case RAVA_VAL_LONG:
|
|
return "Long";
|
|
case RAVA_VAL_FLOAT:
|
|
return "Float";
|
|
case RAVA_VAL_DOUBLE:
|
|
return "Double";
|
|
case RAVA_VAL_BOOLEAN:
|
|
return "Boolean";
|
|
case RAVA_VAL_CHAR:
|
|
return "Character";
|
|
case RAVA_VAL_STRING:
|
|
return "String";
|
|
case RAVA_VAL_OBJECT:
|
|
if (value.data.object_val) {
|
|
return value.data.object_val->class_name;
|
|
}
|
|
return "null";
|
|
case RAVA_VAL_ARRAY:
|
|
return "Array";
|
|
case RAVA_VAL_ARRAYLIST:
|
|
return "ArrayList";
|
|
case RAVA_VAL_HASHMAP:
|
|
return "HashMap";
|
|
}
|
|
return "Object";
|
|
}
|