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