28 changed files with 2139 additions and 102 deletions
@ -0,0 +1,3 @@ |
|||||||
|
|
||||||
|
// Q: Which language makes use of a garbage collector (automatic memory management)?
|
||||||
|
// A: Go
|
@ -0,0 +1,30 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
// #include "bootlib.h"
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_int_has_refcount, { |
||||||
|
snek_object_t *obj = new_snek_integer(10); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_float_has_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(42.0); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("test_int_has_refcount", test_int_has_refcount), |
||||||
|
munit_test("test_float_has_refcount", test_float_has_refcount), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("refcount", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
snek_object_t *_new_snek_object() { |
||||||
|
snek_object_t *obj = (snek_object_t *)calloc(1, sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) return NULL; |
||||||
|
obj->refcount = 1; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
size_t refcount; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
||||||
|
int snek_length(snek_object_t *obj); |
@ -0,0 +1,48 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
// #include "bootlib.h"
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_inc_refcount, { |
||||||
|
snek_object_t *obj = new_snek_integer(10); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_inc_refcount_more, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
refcount_inc(obj); |
||||||
|
refcount_inc(obj); |
||||||
|
refcount_inc(obj); |
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 6, "Refcount should be incremented to 6"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_null_obj, { |
||||||
|
refcount_inc(NULL); |
||||||
|
assert(1); |
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/test_inc_refcount", test_inc_refcount), |
||||||
|
munit_test("/test_inc_refcount_more", test_inc_refcount_more), |
||||||
|
munit_test("/test_null_obj", test_null_obj), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("refcount", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
#include "assert.h" |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj) { |
||||||
|
if (obj == NULL) return; |
||||||
|
obj->refcount++; |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
snek_object_t *_new_snek_object() { |
||||||
|
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount = 1; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
size_t refcount; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
||||||
|
int snek_length(snek_object_t *obj); |
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj); |
@ -0,0 +1,82 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
// #include "bootlib.h"
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_int_has_refcount, { |
||||||
|
snek_object_t *obj = new_snek_integer(10); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_inc_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_dec_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
|
||||||
|
// assert(!boot_is_freed(obj));
|
||||||
|
|
||||||
|
// Object is still alive, so we will free manually.
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_refcount_free_is_called, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
// assert(boot_is_freed(obj));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_allocated_string_is_freed, { |
||||||
|
snek_object_t *obj = new_snek_string("Hello @wagslane!"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
// assert(boot_is_freed(obj));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/has_refcount", test_int_has_refcount), |
||||||
|
munit_test("/inc_refcount", test_inc_refcount), |
||||||
|
munit_test("/dec_refcount", test_dec_refcount), |
||||||
|
munit_test("/free_refcount", test_refcount_free_is_called), |
||||||
|
munit_test("/string_freed", test_allocated_string_is_freed), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("refcount", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,123 @@ |
|||||||
|
#include "assert.h" |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
void refcount_dec(snek_object_t *obj) { |
||||||
|
if (obj == NULL) return; |
||||||
|
obj->refcount--; |
||||||
|
if (obj->refcount <= 0) refcount_free(obj); |
||||||
|
} |
||||||
|
|
||||||
|
void refcount_free(snek_object_t *obj) { |
||||||
|
if (obj == NULL) return; |
||||||
|
if (obj->kind == INTEGER || obj->kind == FLOAT) { |
||||||
|
free(obj); |
||||||
|
} |
||||||
|
if (obj->kind == STRING) { |
||||||
|
free(obj->data.v_string); |
||||||
|
free(obj); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj) { |
||||||
|
if (obj == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount++; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *_new_snek_object() { |
||||||
|
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount = 1; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
size_t refcount; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
||||||
|
int snek_length(snek_object_t *obj); |
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj); |
||||||
|
void refcount_dec(snek_object_t *obj); |
||||||
|
void refcount_free(snek_object_t *obj); |
@ -0,0 +1,128 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
// #include "bootlib.h"
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_vector3_refcounting, { |
||||||
|
snek_object_t *foo = new_snek_integer(1); |
||||||
|
snek_object_t *bar = new_snek_integer(2); |
||||||
|
snek_object_t *baz = new_snek_integer(3); |
||||||
|
|
||||||
|
snek_object_t *vec = new_snek_vector3(foo, bar, baz); |
||||||
|
assert_int(foo->refcount, ==, 2, "foo is now referenced by vec"); |
||||||
|
assert_int(bar->refcount, ==, 2, "bar is now referenced by vec"); |
||||||
|
assert_int(baz->refcount, ==, 2, "baz is now referenced by vec"); |
||||||
|
|
||||||
|
// `foo` is stil referenced in the `vec`, so it should not be freed.
|
||||||
|
refcount_dec(foo); |
||||||
|
// assert(!boot_is_freed(foo));
|
||||||
|
|
||||||
|
refcount_dec(vec); |
||||||
|
// assert(boot_is_freed(foo));
|
||||||
|
|
||||||
|
// These are still alive, they have the original reference still.
|
||||||
|
// assert(!boot_is_freed(bar));
|
||||||
|
// assert(!boot_is_freed(baz));
|
||||||
|
|
||||||
|
// Decrement the last reference to the objects, so they will be freed.
|
||||||
|
refcount_dec(bar); |
||||||
|
refcount_dec(baz); |
||||||
|
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_vector3_refcounting_same, { |
||||||
|
snek_object_t *foo = new_snek_integer(1); |
||||||
|
|
||||||
|
snek_object_t *vec = new_snek_vector3(foo, foo, foo); |
||||||
|
assert_int(foo->refcount, ==, 4, "foo is now referenced by vec x3"); |
||||||
|
|
||||||
|
// `foo` is stil referenced in the `vec`, so it should not be freed.
|
||||||
|
refcount_dec(foo); |
||||||
|
// assert(!boot_is_freed(foo));
|
||||||
|
|
||||||
|
refcount_dec(vec); |
||||||
|
// assert(boot_is_freed(foo));
|
||||||
|
// assert(boot_is_freed(vec));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_int_has_refcount, { |
||||||
|
snek_object_t *obj = new_snek_integer(10); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_inc_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_dec_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
|
||||||
|
// assert(!boot_is_freed(obj));
|
||||||
|
|
||||||
|
// Object is still alive, so we will free manually.
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_refcount_free_is_called, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
// assert(boot_is_freed(obj));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_allocated_string_is_freed, { |
||||||
|
snek_object_t *obj = new_snek_string("Hello @wagslane!"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
// assert(boot_is_freed(obj));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/has_refcount", test_int_has_refcount), |
||||||
|
munit_test("/inc_refcount", test_inc_refcount), |
||||||
|
munit_test("/dec_refcount", test_dec_refcount), |
||||||
|
munit_test("/free_refcount", test_refcount_free_is_called), |
||||||
|
munit_test("/string_freed", test_allocated_string_is_freed), |
||||||
|
munit_test("/vector3", test_vector3_refcounting), |
||||||
|
munit_test("/vector3-same", test_vector3_refcounting_same), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("refcount", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
#include "assert.h" |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
snek_object_t *_new_snek_object(); |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z) |
||||||
|
{ |
||||||
|
if (x == NULL || y == NULL || z == NULL) return NULL; |
||||||
|
|
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
refcount_inc(x); |
||||||
|
refcount_inc(y); |
||||||
|
refcount_inc(z); |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
void refcount_free(snek_object_t *obj) |
||||||
|
{ |
||||||
|
switch (obj->kind) { |
||||||
|
case INTEGER: |
||||||
|
case FLOAT: |
||||||
|
break; |
||||||
|
case STRING: |
||||||
|
free(obj->data.v_string); |
||||||
|
break; |
||||||
|
case VECTOR3: { |
||||||
|
refcount_dec(obj->data.v_vector3.x); |
||||||
|
refcount_dec(obj->data.v_vector3.y); |
||||||
|
refcount_dec(obj->data.v_vector3.z); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
assert(false); |
||||||
|
} |
||||||
|
|
||||||
|
free(obj); |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj) { |
||||||
|
if (obj == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount++; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void refcount_dec(snek_object_t *obj) { |
||||||
|
if (obj == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
obj->refcount--; |
||||||
|
if (obj->refcount == 0) { |
||||||
|
return refcount_free(obj); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *_new_snek_object() { |
||||||
|
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount = 1; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
size_t refcount; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
||||||
|
int snek_length(snek_object_t *obj); |
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj); |
||||||
|
void refcount_dec(snek_object_t *obj); |
||||||
|
void refcount_free(snek_object_t *obj); |
@ -0,0 +1,154 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
// #include "bootlib.h"
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_array_set, { |
||||||
|
snek_object_t *foo = new_snek_integer(1); |
||||||
|
|
||||||
|
snek_object_t *array = new_snek_array(1); |
||||||
|
snek_array_set(array, 0, foo); |
||||||
|
assert_int(foo->refcount, ==, 2, "foo is now referenced by array"); |
||||||
|
|
||||||
|
// assert(!boot_is_freed(foo));
|
||||||
|
|
||||||
|
refcount_dec(foo); |
||||||
|
refcount_dec(array); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_array_free, { |
||||||
|
snek_object_t *foo = new_snek_integer(1); |
||||||
|
snek_object_t *bar = new_snek_integer(2); |
||||||
|
snek_object_t *baz = new_snek_integer(3); |
||||||
|
|
||||||
|
snek_object_t *array = new_snek_array(2); |
||||||
|
snek_array_set(array, 0, foo); |
||||||
|
snek_array_set(array, 1, bar); |
||||||
|
assert_int(foo->refcount, ==, 2, "foo is now referenced by array"); |
||||||
|
assert_int(bar->refcount, ==, 2, "bar is now referenced by array"); |
||||||
|
assert_int(baz->refcount, ==, 1, "baz is not yet referenced by array"); |
||||||
|
|
||||||
|
// `foo` is stil referenced in the `array`, so it should not be freed.
|
||||||
|
refcount_dec(foo); |
||||||
|
// assert(!boot_is_freed(foo));
|
||||||
|
|
||||||
|
// Overwrite index 0, which is `foo`, with `baz`.
|
||||||
|
// Now `foo` is not referenced by `array`, so it should be freed.
|
||||||
|
snek_array_set(array, 0, baz); |
||||||
|
// assert(boot_is_freed(foo));
|
||||||
|
|
||||||
|
refcount_dec(bar); |
||||||
|
refcount_dec(baz); |
||||||
|
refcount_dec(array); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_vector3_refcounting, { |
||||||
|
snek_object_t *foo = new_snek_integer(1); |
||||||
|
snek_object_t *bar = new_snek_integer(2); |
||||||
|
snek_object_t *baz = new_snek_integer(3); |
||||||
|
|
||||||
|
snek_object_t *vec = new_snek_vector3(foo, bar, baz); |
||||||
|
assert_int(foo->refcount, ==, 2, "foo is now referenced by vec"); |
||||||
|
assert_int(bar->refcount, ==, 2, "bar is now referenced by vec"); |
||||||
|
assert_int(baz->refcount, ==, 2, "baz is now referenced by vec"); |
||||||
|
|
||||||
|
// `foo` is stil referenced in the `vec`, so it should not be freed.
|
||||||
|
refcount_dec(foo); |
||||||
|
// assert(!boot_is_freed(foo));
|
||||||
|
|
||||||
|
refcount_dec(vec); |
||||||
|
// assert(boot_is_freed(foo));
|
||||||
|
|
||||||
|
// These are still alive, they have the original reference still.
|
||||||
|
// assert(!boot_is_freed(bar));
|
||||||
|
// assert(!boot_is_freed(baz));
|
||||||
|
|
||||||
|
// Decrement the last reference to the objects, so they will be freed.
|
||||||
|
refcount_dec(bar); |
||||||
|
refcount_dec(baz); |
||||||
|
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_int_has_refcount, { |
||||||
|
snek_object_t *obj = new_snek_integer(10); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_inc_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_dec_refcount, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
|
||||||
|
// assert(!boot_is_freed(obj));
|
||||||
|
|
||||||
|
// Object is still alive, so we will free manually.
|
||||||
|
free(obj); |
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_refcount_free_is_called, { |
||||||
|
snek_object_t *obj = new_snek_float(4.20); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
// assert(boot_is_freed(obj));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_allocated_string_is_freed, { |
||||||
|
snek_object_t *obj = new_snek_string("Hello @wagslane!"); |
||||||
|
|
||||||
|
refcount_inc(obj); |
||||||
|
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); |
||||||
|
assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str"); |
||||||
|
|
||||||
|
refcount_dec(obj); |
||||||
|
// assert(boot_is_freed(obj));
|
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/array_set", test_array_set), |
||||||
|
munit_test("/array_free", test_array_free), |
||||||
|
munit_test("/has_refcount", test_int_has_refcount), |
||||||
|
munit_test("/inc_refcount", test_inc_refcount), |
||||||
|
munit_test("/dec_refcount", test_dec_refcount), |
||||||
|
munit_test("/free_refcount", test_refcount_free_is_called), |
||||||
|
munit_test("/string_freed", test_allocated_string_is_freed), |
||||||
|
munit_test("/vector3", test_vector3_refcounting), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("refcount", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,181 @@ |
|||||||
|
#include "assert.h" |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value) |
||||||
|
{ |
||||||
|
if (snek_obj == NULL || value == NULL) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (snek_obj->kind != ARRAY) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (index >= snek_obj->data.v_array.size) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (snek_obj->data.v_array.elements[index] != NULL) { |
||||||
|
refcount_dec(snek_obj->data.v_array.elements[index]); |
||||||
|
} |
||||||
|
snek_obj->data.v_array.elements[index] = value; |
||||||
|
refcount_inc(value); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void refcount_free(snek_object_t *obj) |
||||||
|
{ |
||||||
|
switch (obj->kind) { |
||||||
|
case INTEGER: |
||||||
|
case FLOAT: |
||||||
|
break; |
||||||
|
case STRING: |
||||||
|
free(obj->data.v_string); |
||||||
|
break; |
||||||
|
case VECTOR3: { |
||||||
|
snek_vector_t vec = obj->data.v_vector3; |
||||||
|
refcount_dec(vec.x); |
||||||
|
refcount_dec(vec.y); |
||||||
|
refcount_dec(vec.z); |
||||||
|
break; |
||||||
|
} |
||||||
|
case ARRAY: { |
||||||
|
for (int i = 0; i < obj->data.v_array.size; i++) { |
||||||
|
refcount_dec(obj->data.v_array.elements[i]); |
||||||
|
} |
||||||
|
free(obj->data.v_array.elements); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
assert(false); |
||||||
|
} |
||||||
|
free(obj); |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index) { |
||||||
|
if (snek_obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (snek_obj->kind != ARRAY) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (index >= snek_obj->data.v_array.size) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
return snek_obj->data.v_array.elements[index]; |
||||||
|
} |
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj) { |
||||||
|
if (obj == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount++; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void refcount_dec(snek_object_t *obj) { |
||||||
|
if (obj == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
obj->refcount--; |
||||||
|
if (obj->refcount == 0) { |
||||||
|
return refcount_free(obj); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *_new_snek_object() { |
||||||
|
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->refcount = 1; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
snek_object_t *obj = _new_snek_object(); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
refcount_inc(x); |
||||||
|
refcount_inc(y); |
||||||
|
refcount_inc(z); |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
size_t refcount; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
||||||
|
int snek_length(snek_object_t *obj); |
||||||
|
|
||||||
|
void refcount_inc(snek_object_t *obj); |
||||||
|
void refcount_dec(snek_object_t *obj); |
||||||
|
void refcount_free(snek_object_t *obj); |
@ -0,0 +1,5 @@ |
|||||||
|
|
||||||
|
// Q: Why doesn't the snek_add function need to take care of
|
||||||
|
// garbage collecting the resulting object?
|
||||||
|
|
||||||
|
// A:
|
@ -0,0 +1,38 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_create_empty_array, { |
||||||
|
snek_object_t *obj = new_snek_array(2); |
||||||
|
|
||||||
|
assert_int(obj->kind, ==, ARRAY, "Must set type to ARRAY"); |
||||||
|
assert_int(obj->data.v_array.size, ==, 2, "Must set size to 2"); |
||||||
|
|
||||||
|
free(obj->data.v_array.elements); |
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_used_calloc, { |
||||||
|
snek_object_t *obj = new_snek_array(2); |
||||||
|
|
||||||
|
assert_ptr_null(obj->data.v_array.elements[0], "Should use calloc"); |
||||||
|
assert_ptr_null(obj->data.v_array.elements[1], "Should use calloc"); |
||||||
|
|
||||||
|
free(obj->data.v_array.elements); |
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/empty", test_create_empty_array), |
||||||
|
munit_test("/calloc", test_used_calloc), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("object-array", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = (snek_object_t *)malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) return NULL; |
||||||
|
|
||||||
|
snek_object_t **arr = (snek_object_t **)calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (arr == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){ .size = size, .elements = arr }; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
snek_object_t *new_snek_array(size_t size); |
@ -0,0 +1,83 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_array, { |
||||||
|
// Create an array
|
||||||
|
snek_object_t *obj = new_snek_array(2); |
||||||
|
|
||||||
|
// Create some objects to put in the array
|
||||||
|
// Can store objects, even though they have different types (just like Python)
|
||||||
|
snek_object_t *first = new_snek_string("First"); |
||||||
|
snek_object_t *second = new_snek_integer(3); |
||||||
|
|
||||||
|
assert(snek_array_set(obj, 0, first)); |
||||||
|
assert(snek_array_set(obj, 1, second)); |
||||||
|
|
||||||
|
snek_object_t *retrieved_first = snek_array_get(obj, 0); |
||||||
|
assert_not_null(retrieved_first, "Should find the first object"); |
||||||
|
assert_int(retrieved_first->kind, ==, STRING, "Should be a string"); |
||||||
|
assert_ptr(first, ==, retrieved_first, "Should be the same object"); |
||||||
|
|
||||||
|
snek_object_t *retrieved_second = snek_array_get(obj, 1); |
||||||
|
assert_not_null(retrieved_second, "Should find the second object"); |
||||||
|
assert_int(retrieved_second->kind, ==, INTEGER, "Should be a integer"); |
||||||
|
assert_ptr(second, ==, retrieved_second, "Should be the same object"); |
||||||
|
|
||||||
|
free(first->data.v_string); |
||||||
|
free(first); |
||||||
|
free(second); |
||||||
|
free(obj->data.v_array.elements); |
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_set_outside_bounds, { |
||||||
|
// Create an array
|
||||||
|
snek_object_t *obj = new_snek_array(2); |
||||||
|
|
||||||
|
snek_object_t *outside = new_snek_string("First"); |
||||||
|
|
||||||
|
// Inside of bound
|
||||||
|
assert(snek_array_set(obj, 1, outside)); |
||||||
|
|
||||||
|
// Outside of bound
|
||||||
|
assert_false(snek_array_set(obj, 100, outside)); |
||||||
|
|
||||||
|
// Free memory
|
||||||
|
free(outside->data.v_string); |
||||||
|
free(outside); |
||||||
|
free(obj->data.v_array.elements); |
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_get_outside_bounds, { |
||||||
|
// Create an array
|
||||||
|
snek_object_t *obj = new_snek_array(1); |
||||||
|
snek_object_t *first = new_snek_string("First"); |
||||||
|
assert(snek_array_set(obj, 0, first)); |
||||||
|
|
||||||
|
// Outside of bound
|
||||||
|
assert_null(snek_array_get(obj, 1), "Should not access outside the array"); |
||||||
|
|
||||||
|
free(first->data.v_string); |
||||||
|
free(first); |
||||||
|
free(obj->data.v_array.elements); |
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/set_and_get", test_array), |
||||||
|
munit_test("/set_outside", test_set_outside_bounds), |
||||||
|
munit_test("/get_outside", test_get_outside_bounds), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("object-array", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value) { |
||||||
|
if (snek_obj == NULL || value == NULL) return false; |
||||||
|
if (snek_obj->kind != ARRAY) return false; |
||||||
|
if (snek_obj->data.v_array.size <= index) return false; |
||||||
|
|
||||||
|
snek_obj->data.v_array.elements[index] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index) { |
||||||
|
if (snek_obj == NULL) return NULL; |
||||||
|
if (snek_obj->kind != ARRAY) return NULL; |
||||||
|
if (snek_obj->data.v_array.size <= index) return NULL; |
||||||
|
|
||||||
|
return snek_obj->data.v_array.elements[index]; |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
@ -0,0 +1,74 @@ |
|||||||
|
#include "munit.h" |
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
munit_case(RUN, test_integer, { |
||||||
|
snek_object_t *obj = new_snek_integer(42); |
||||||
|
assert_int(snek_length(obj), ==, 1, "Integers are length 1"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_float, { |
||||||
|
snek_object_t *obj = new_snek_float(3.14); |
||||||
|
assert_int(snek_length(obj), ==, 1, "Floats are length 1"); |
||||||
|
|
||||||
|
free(obj); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(RUN, test_string, { |
||||||
|
snek_object_t *shorter = new_snek_string("hello"); |
||||||
|
assert_int(snek_length(shorter), ==, 5, "Should return based on input"); |
||||||
|
|
||||||
|
snek_object_t *longer = new_snek_string("hello, world"); |
||||||
|
assert_int( |
||||||
|
snek_length(longer), ==, strlen("hello, world"), "Should use strlen" |
||||||
|
); |
||||||
|
|
||||||
|
free(shorter->data.v_string); |
||||||
|
free(shorter); |
||||||
|
free(longer->data.v_string); |
||||||
|
free(longer); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_vector3, { |
||||||
|
snek_object_t *i = new_snek_integer(1); |
||||||
|
snek_object_t *vec = new_snek_vector3(i, i, i); |
||||||
|
assert_int(snek_length(vec), ==, 3, "Vec3 always has length 3"); |
||||||
|
|
||||||
|
free(i); |
||||||
|
free(vec); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
munit_case(SUBMIT, test_array, { |
||||||
|
snek_object_t *i = new_snek_integer(1); |
||||||
|
snek_object_t *arr = new_snek_array(4); |
||||||
|
assert(snek_array_set(arr, 0, i)); |
||||||
|
assert(snek_array_set(arr, 1, i)); |
||||||
|
assert(snek_array_set(arr, 2, i)); |
||||||
|
|
||||||
|
assert_int(snek_length(arr), ==, 4, "Length of array should be its size"); |
||||||
|
|
||||||
|
free(i); |
||||||
|
free(arr->data.v_array.elements); |
||||||
|
free(arr); |
||||||
|
// assert(boot_all_freed());
|
||||||
|
}); |
||||||
|
|
||||||
|
int main() { |
||||||
|
MunitTest tests[] = { |
||||||
|
munit_test("/integer", test_integer), |
||||||
|
munit_test("/float", test_float), |
||||||
|
munit_test("/string", test_string), |
||||||
|
munit_test("/vector", test_vector3), |
||||||
|
munit_test("/array", test_array), |
||||||
|
munit_null_test, |
||||||
|
}; |
||||||
|
|
||||||
|
MunitSuite suite = munit_suite("object-length", tests); |
||||||
|
|
||||||
|
return munit_suite_main(&suite, NULL, 0, NULL); |
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "snekobject.h" |
||||||
|
|
||||||
|
int snek_length(snek_object_t *obj) { |
||||||
|
if (obj == NULL) return -1; |
||||||
|
if (obj->kind == INTEGER || obj->kind == FLOAT) return 1; |
||||||
|
if (obj->kind == STRING) return strlen(obj->data.v_string); |
||||||
|
if (obj->kind == VECTOR3) return 3; |
||||||
|
if (obj->kind == ARRAY) return obj->data.v_array.size; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
// don't touch below this line
|
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); |
||||||
|
if (elements == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = ARRAY; |
||||||
|
obj->data.v_array = (snek_array_t){.size = size, .elements = elements}; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
bool snek_array_set(snek_object_t *array, size_t index, snek_object_t *value) { |
||||||
|
if (array == NULL || value == NULL) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (array->kind != ARRAY) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (index >= array->data.v_array.size) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Set the value directly now (already checked size constraint)
|
||||||
|
array->data.v_array.elements[index] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *snek_array_get(snek_object_t *array, size_t index) { |
||||||
|
if (array == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (array->kind != ARRAY) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (index >= array->data.v_array.size) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// Get the value directly now (already checked size constraint)
|
||||||
|
return array->data.v_array.elements[index]; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_vector3( |
||||||
|
snek_object_t *x, snek_object_t *y, snek_object_t *z |
||||||
|
) { |
||||||
|
if (x == NULL || y == NULL || z == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = VECTOR3; |
||||||
|
obj->data.v_vector3 = (snek_vector_t){.x = x, .y = y, .z = z}; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = INTEGER; |
||||||
|
obj->data.v_int = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_float(float value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
obj->kind = FLOAT; |
||||||
|
obj->data.v_float = value; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
snek_object_t *new_snek_string(char *value) { |
||||||
|
snek_object_t *obj = malloc(sizeof(snek_object_t)); |
||||||
|
if (obj == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int len = strlen(value); |
||||||
|
char *dst = malloc(len + 1); |
||||||
|
if (dst == NULL) { |
||||||
|
free(obj); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(dst, value); |
||||||
|
|
||||||
|
obj->kind = STRING; |
||||||
|
obj->data.v_string = dst; |
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct SnekObject snek_object_t; |
||||||
|
|
||||||
|
typedef enum SnekObjectKind { |
||||||
|
INTEGER, |
||||||
|
FLOAT, |
||||||
|
STRING, |
||||||
|
VECTOR3, |
||||||
|
ARRAY, |
||||||
|
} snek_object_kind_t; |
||||||
|
|
||||||
|
typedef struct SnekVector { |
||||||
|
snek_object_t *x; |
||||||
|
snek_object_t *y; |
||||||
|
snek_object_t *z; |
||||||
|
} snek_vector_t; |
||||||
|
|
||||||
|
typedef struct SnekArray { |
||||||
|
size_t size; |
||||||
|
snek_object_t **elements; |
||||||
|
} snek_array_t; |
||||||
|
|
||||||
|
typedef union SnekObjectData { |
||||||
|
int v_int; |
||||||
|
float v_float; |
||||||
|
char* v_string; |
||||||
|
snek_vector_t v_vector3; |
||||||
|
snek_array_t v_array; |
||||||
|
} snek_object_data_t; |
||||||
|
|
||||||
|
typedef struct SnekObject { |
||||||
|
snek_object_kind_t kind; |
||||||
|
snek_object_data_t data; |
||||||
|
} snek_object_t; |
||||||
|
|
||||||
|
snek_object_t *new_snek_integer(int value); |
||||||
|
snek_object_t *new_snek_float(float value); |
||||||
|
snek_object_t *new_snek_string(char *value); |
||||||
|
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); |
||||||
|
|
||||||
|
snek_object_t *new_snek_array(size_t size); |
||||||
|
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value); |
||||||
|
snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index); |
||||||
|
int snek_length(snek_object_t *obj); |
Loading…
Reference in new issue