diff --git a/README.org b/README.org index c9361f8..81903fb 100644 --- a/README.org +++ b/README.org @@ -42,67 +42,77 @@ Successful output of all the tests: #+BEGIN_SRC sh $ make run 12:25PM [ 3%] Built target munit -[ 8%] Built target 8-6-multiple-types -[ 11%] Built target 4-1-enums -[ 14%] Built target 4-2-non-default-values -[ 17%] Built target 4-3-switch-case -[ 20%] Built target 4-4-sizeof-enum -[ 24%] Built target 5-1-union -[ 26%] Built target 5-2-memory-layout -[ 29%] Built target 5-3-5-4-union-size -[ 32%] Built target 5-5-helper-fields -[ 34%] Built target 6-1-the-stack -[ 37%] Built target 6-2-why-a-stack -[ 39%] Built target 6-3-stack-overflow -[ 42%] Built target 6-4-pointers-to-the-stack -[ 46%] Built target 6-5-the-heap -[ 50%] Built target 6-6-malloc -[ 53%] Built target 6-7-free -[ 56%] Built target 6-8-big-endian-little-endian -[ 60%] Built target 7-1-pointer-pointers -[ 64%] Built target 7-2-array-of-pointers -[ 67%] Built target 7-3-void-pointers -[ 71%] Built target 7-4-swapping-integers -[ 75%] Built target 7-5-swapping-strings -[ 79%] Built target 7-6-generic-swap -[ 83%] Built target 8-1-low-level-stack -[ 87%] Built target 8-2-stack-push -[ 91%] Built target 8-3-stack-pop -[ 94%] Built target 8-4-stack-free -[100%] Built target 8-5-dangerous-push -Running test suite with seed 0x039ca6b1... -colors/are_defined [ OK ] [ 0.00000100 / 0.00000200 CPU ] -colors/are_defined_correctly [ OK ] [ 0.00000100 / 0.00000100 CPU ] +[ 6%] Built target 9-8-length +[ 7%] Built target 4-1-enums +[ 9%] Built target 4-2-non-default-values +[ 12%] Built target 4-3-switch-case +[ 14%] Built target 4-4-sizeof-enum +[ 17%] Built target 5-1-union +[ 19%] Built target 5-2-memory-layout +[ 21%] Built target 5-3-5-4-union-size +[ 23%] Built target 5-5-helper-fields +[ 25%] Built target 6-1-the-stack +[ 27%] Built target 6-2-why-a-stack +[ 29%] Built target 6-3-stack-overflow +[ 31%] Built target 6-4-pointers-to-the-stack +[ 34%] Built target 6-5-the-heap +[ 37%] Built target 6-6-malloc +[ 40%] Built target 6-7-free +[ 42%] Built target 6-8-big-endian-little-endian +[ 45%] Built target 7-1-pointer-pointers +[ 48%] Built target 7-2-array-of-pointers +[ 51%] Built target 7-3-void-pointers +[ 54%] Built target 7-4-swapping-integers +[ 57%] Built target 7-5-swapping-strings +[ 60%] Built target 7-6-generic-swap +[ 63%] Built target 8-1-low-level-stack +[ 66%] Built target 8-2-stack-push +[ 69%] Built target 8-3-stack-pop +[ 72%] Built target 8-4-stack-free +[ 76%] Built target 8-5-dangerous-push +[ 80%] Built target 8-6-multiple-types +[ 82%] Built target 9-1-snek-objects +[ 85%] Built target 9-2-integer +[ 88%] Built target 9-3-float +[ 91%] Built target 9-4-string +[ 94%] Built target 9-5-vector3 +[ 97%] Built target 9-6-arrays +[100%] Built target 9-7-get-and-set +Running test suite with seed 0xa13b83c4... +colors/are_defined [ OK ] [ 0.00000100 / 0.00000300 CPU ] +colors/are_defined_correctly [ OK ] [ 0.00000100 / 0.00000200 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xbf68f4fa... -colors/defined [ OK ] [ 0.00000100 / 0.00000100 CPU ] -colors/defined_vscode [ OK ] [ 0.00000100 / 0.00000000 CPU ] +Running test suite with seed 0x9c11f043... +colors/defined [ OK ] [ 0.00000000 / 0.00000000 CPU ] +colors/defined_vscode [ OK ] [ 0.00000000 / 0.00000000 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0x40b17fef... -http/switch_enum [ OK ] [ 0.00000100 / 0.00000200 CPU ] -http/switch_enum_default [ OK ] [ 0.00000100 / 0.00000000 CPU ] +Running test suite with seed 0xdac57c79... +http/switch_enum [ OK ] [ 0.00000100 / 0.00000100 CPU ] +http/switch_enum_default [ OK ] [ 0.00000100 / 0.00000100 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. The size of BigNumbers is 8 bytes The size of HttpErrorCode is 4 bytes -Running test suite with seed 0xde548fa9... -format/integer [ OK ] [ 0.00000200 / 0.00000200 CPU ] +Running test suite with seed 0x85251cb1... +format/integer [ OK ] [ 0.00000100 / 0.00000100 CPU ] format/string [ OK ] [ 0.00000100 / 0.00000100 CPU ] format/integer_nvim [ OK ] [ 0.00000100 / 0.00000200 CPU ] -format/string_nvim [ OK ] [ 0.00000200 / 0.00000200 CPU ] +format/string_nvim [ OK ] [ 0.00000100 / 0.00000100 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. value (set): -420 err (unset): 4294966876 value (unset): -1 err (set): 4294967295 -Running test suite with seed 0xa7197940... -PacketHeader/test_packet_header_size [ OK ] [ 0.00000100 / 0.00000200 CPU ] +Running test suite with seed 0xb2bd8586... +PacketHeader/test_packet_header_size [ OK ] [ 0.00000100 / 0.00000000 CPU ] PacketHeader/test_tcp_header_fields [ OK ] [ 0.00000100 / 0.00000100 CPU ] -PacketHeader/test_field_raw_size [ OK ] [ 0.00000000 / 0.00000100 CPU ] -PacketHeader/test_field_to_raw_consistency[ OK ] [ 0.00000100 / 0.00000100 CPU ] +PacketHeader/test_field_raw_size [ OK ] [ 0.00000100 / 0.00000100 CPU ] +PacketHeader/test_field_to_raw_consistency[ OK ] [ 0.00000100 / 0.00000200 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. ---------------------------------- Stack pointer offset: 0 bytes +--------------------------------- +Stack pointer offset: 0 bytes --------------------------------- Dark mode? + --------------------------------- Stack pointer offset: 0 bytes --------------------------------- @@ -112,95 +122,142 @@ More like... Stack pointer offset: 0 bytes --------------------------------- dark roast. -Running test suite with seed 0x7dcfad3b... -/example/compare [ OK ] [ 0.00000100 / 0.00000000 CPU ] -/example/rand [ OK ] [ 0.00000100 / 0.00000000 CPU ] + +Running test suite with seed 0x5ed24c16... +/example/compare [ OK ] [ 0.00000100 / 0.00000200 CPU ] +/example/rand [ OK ] [ 0.00000200 / 0.00000100 CPU ] /example/parameters foo=one, bar=red [ OK ] [ 0.00000200 / 0.00000200 CPU ] - foo=one, bar=green [ OK ] [ 0.00000200 / 0.00000300 CPU ] + foo=one, bar=green [ OK ] [ 0.00000200 / 0.00000200 CPU ] foo=one, bar=blue [ OK ] [ 0.00000200 / 0.00000200 CPU ] - foo=two, bar=red [ OK ] [ 0.00000100 / 0.00000200 CPU ] - foo=two, bar=green [ OK ] [ 0.00000200 / 0.00000200 CPU ] foo=two, bar=blue [ OK ] [ 0.00000200 / 0.00000200 CPU ] - foo=three, bar=red [ OK ] [ 0.00000100 / 0.00000200 CPU ] - foo=three, bar=green [ OK ] [ 0.00000200 / 0.00000100 CPU ] - foo=three, bar=blue [ OK ] [ 0.00000100 / 0.00000200 CPU ] + foo=two, bar=red [ OK ] [ 0.00000200 / 0.00000100 CPU ] + foo=two, bar=green [ OK ] [ 0.00000200 / 0.00000200 CPU ] + foo=two, bar=blue [ OK ] [ 0.00000200 / 0.00000200 CPU ] + foo=three, bar=red [ OK ] [ 0.00000200 / 0.00000200 CPU ] + foo=three, bar=green [ OK ] [ 0.00000200 / 0.00000200 CPU ] + foo=three, bar=blue [ OK ] [ 0.00000100 / 0.00000000 CPU ] 11 of 11 (100%) tests successful, 0 (0%) test skipped. Size of pool: 10240 Initial string: snek c1: 10, 20 c2: 30, 40 -c3: 50, 60 Running test suite with seed 0x53c7e720... -get_full_greeting/test_basic_greeting[ OK ] [ 0.00000200 / 0.00000300 CPU ] -get_full_greeting/test_short_buffer [ OK ] [ 0.00000200 / 0.00000200 CPU ] +c3: 50, 60 +Running test suite with seed 0x08ecbbe7... +get_full_greeting/test_basic_greeting[ OK ] [ 0.00000200 / 0.00000200 CPU ] +get_full_greeting/test_short_buffer [ OK ] [ 0.00000100 / 0.00000200 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xee42ee28... -allocate_scalar_array/test_allocate_scalar_array_size[ OK ] [ 0.00000100 / 0.00000200 CPU ] +Running test suite with seed 0xd953a05a... +allocate_scalar_array/test_allocate_scalar_array_size[ OK ] [ 0.00000100 / 0.00000100 CPU ] allocate_scalar_array/test_allocate_scalar_array_values[ OK ] [ 0.00000200 / 0.00000100 CPU ] -allocate_scalar_array/test_allocate_scalar_array_zero_multiplier[ OK ] [ 0.00000200 / 0.00000100 CPU ] +allocate_scalar_array/test_allocate_scalar_array_zero_multiplier[ OK ] [ 0.00000200 / 0.00000200 CPU ] allocate_scalar_array/test_allocate_too_much[ OK ] [ 0.00001000 / 0.00000900 CPU ] -4 of 4 (100%) tests successful, 0 (0%) test skipped. Allocated 500 lists -Running test suite with seed 0x9d78388e... +4 of 4 (100%) tests successful, 0 (0%) test skipped. +Allocated 500 lists +Running test suite with seed 0x026c49ea... /example/compare [ OK ] [ 0.00000100 / 0.00000200 CPU ] /example/rand [ OK ] [ 0.00000100 / 0.00000200 CPU ] /example/parameters - foo=one, bar=red [ OK ] [ 0.00000200 / 0.00000200 CPU ] - foo=one, bar=green [ OK ] [ 0.00000100 / 0.00000200 CPU ] - foo=one, bar=blue [ OK ] [ 0.00000200 / 0.00000100 CPU ] - foo=two, bar=red [ OK ] [ 0.00000200 / 0.00000200 CPU ] - foo=two, bar=green [ OK ] [ 0.00000200 / 0.00000000 CPU ] foo=two, bar=blue [ OK ] [ 0.00000200 / 0.00000100 CPU ] - foo=three, bar=red [ OK ] [ 0.00000100 / 0.00000100 CPU ] - foo=three, bar=green [ OK ] [ 0.00000200 / 0.00000200 CPU ] + foo=one, bar=red [ OK ] [ 0.00000200 / 0.00000100 CPU ] + foo=one, bar=green [ OK ] [ 0.00000100 / 0.00000100 CPU ] + foo=one, bar=blue [ OK ] [ 0.00000100 / 0.00000200 CPU ] + foo=two, bar=red [ OK ] [ 0.00000100 / 0.00000200 CPU ] + foo=two, bar=green [ OK ] [ 0.00000100 / 0.00000200 CPU ] + foo=two, bar=blue [ OK ] [ 0.00000200 / 0.00000100 CPU ] + foo=three, bar=red [ OK ] [ 0.00000200 / 0.00000100 CPU ] + foo=three, bar=green [ OK ] [ 0.00000200 / 0.00000100 CPU ] foo=three, bar=blue [ OK ] [ 0.00000200 / 0.00000200 CPU ] 11 of 11 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0x07fdd526... -allocate_list/create [ OK ] [ 0.00000100 / 0.00000000 CPU ] -allocate_list/overwrite [ OK ] [ 0.00000100 / 0.00000100 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xf26a30e1... -create_token_pointer_array/test_create_token_pointer_array_single[ OK ] [ 0.00000200 / 0.00000200 CPU ] -create_token_pointer_array/test_create_token_pointer_array_multiple[ OK ] [ 0.00000300 / 0.00000200 CPU ] -create_token_pointer_array/test_create_token_pointer_array_memory_allocation[ OK ] [ 0.00000200 / 0.00000300 CPU ] +Running test suite with seed 0xbb38daad... +allocate_list/create [ OK ] [ 0.00000100 / 0.00000200 CPU ] +allocate_list/overwrite [ OK ] [ 0.00000100 / 0.00000000 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x33807c18... +create_token_pointer_array/test_create_token_pointer_array_single[ OK ] [ 0.00000100 / 0.00000200 CPU ] +create_token_pointer_array/test_create_token_pointer_array_multiple[ OK ] [ 0.00000200 / 0.00000100 CPU ] +create_token_pointer_array/test_create_token_pointer_array_memory_allocation[ OK ] [ 0.00000200 / 0.00000200 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0x4c18af41... +Running test suite with seed 0x8614a0d1... snek_zero_out/test_zero_out_integer [ OK ] [ 0.00000100 / 0.00000100 CPU ] -snek_zero_out/test_zero_out_float [ OK ] [ 0.00000100 / 0.00000200 CPU ] -snek_zero_out/test_zero_out_bool [ OK ] [ 0.00000100 / 0.00000000 CPU ] snek_zero_out/test_zero_out_nonzero_values[ OK ] [ 0.00000100 / 0.00000200 CPU ] +snek_zero_out/test_zero_out_float [ OK ] [ 0.00000100 / 0.00000000 CPU ] +snek_zero_out/test_zero_out_bool [ OK ] [ 0.00000000 / 0.00000000 CPU ] +snek_zero_out/test_zero_out_nonzero_values[ OK ] [ 0.00000100 / 0.00000000 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xb0750720... -void-pointer/swap_ints [ OK ] [ 0.00000100 / 0.00000100 CPU ] -void-pointer/swap_ints_same [ OK ] [ 0.00000100 / 0.00000100 CPU ] +Running test suite with seed 0x2f6f82cf... +void-pointer/swap_ints [ OK ] [ 0.00000000 / 0.00000000 CPU ] +void-pointer/swap_ints_same [ OK ] [ 0.00000100 / 0.00000200 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xc10a0ca0... +Running test suite with seed 0x62099957... void-pointer/swap_str [ OK ] [ 0.00000100 / 0.00000100 CPU ] void-pointer/test_swap_str_long [ OK ] [ 0.00000100 / 0.00000100 CPU ] -2 of 2 (100%) tests successful, 0 (0%) test skipped. Running test suite with seed 0xa27e09af... -swap/generic_ints [ OK ] [ 0.00000200 / 0.00000200 CPU ] -swap/generic_strings [ OK ] [ 0.00000200 / 0.00000100 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x56ef9f75... +swap/generic_ints [ OK ] [ 0.00000100 / 0.00000200 CPU ] +swap/generic_strings [ OK ] [ 0.00000100 / 0.00000100 CPU ] swap/generic_struct [ OK ] [ 0.00000200 / 0.00000200 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xd1be93bf... -snekstack/create_stack_small [ OK ] [ 0.00000100 / 0.00000000 CPU ] -snekstack/create_stack_large [ OK ] [ 0.00000100 / 0.00000200 CPU ] -2 of 2 (100%) tests successful, 0 (0%) test skipped. Running test suite with seed 0x92735306... -snekstack/create_stack [ OK ] [ 0.00000200 / 0.00000100 CPU ] +Running test suite with seed 0xdf070ee5... +snekstack/create_stack_small [ OK ] [ 0.00000200 / 0.00000200 CPU ] +snekstack/create_stack_large [ OK ] [ 0.00000100 / 0.00000100 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xa0778dac... +snekstack/create_stack [ OK ] [ 0.00000100 / 0.00000200 CPU ] snekstack/push_stack [ OK ] [ 0.00000100 / 0.00000200 CPU ] -snekstack/push_double_capacity [ OK ] [ 0.00000200 / 0.00000200 CPU ] +snekstack/push_double_capacity [ OK ] [ 0.00000300 / 0.00000300 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xd641eade... +Running test suite with seed 0x1df97623... snekstack/create_stack [ OK ] [ 0.00000200 / 0.00000200 CPU ] -snekstack/push_stack [ OK ] [ 0.00000200 / 0.00000200 CPU ] -snekstack/pop_stack [ OK ] [ 0.00000200 / 0.00000100 CPU ] +snekstack/push_stack [ OK ] [ 0.00000200 / 0.00000300 CPU ] +snekstack/pop_stack [ OK ] [ 0.00000100 / 0.00000100 CPU ] snekstack/pop_stack_empty [ OK ] [ 0.00000200 / 0.00000100 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xd1d1136d... -snekstack/create_stack [ OK ] [ 0.00000200 / 0.00000100 CPU ] +Running test suite with seed 0xc67f74fb... +snekstack/create_stack [ OK ] [ 0.00000100 / 0.00000100 CPU ] snekstack/push_stack [ OK ] [ 0.00000200 / 0.00000200 CPU ] -snekstack/pop_stack [ OK ] [ 0.00000200 / 0.00000200 CPU ] +snekstack/pop_stack [ OK ] [ 0.00000100 / 0.00000200 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0x5493a18d... -snekstack/heterogenous_stack [ OK ] [ 0.00000100 / 0.00000200 CPU ] +Running test suite with seed 0xb607698d... +snekstack/heterogenous_stack [ OK ] [ 0.00000200 / 0.00000100 CPU ] +1 of 1 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x4804e928... +snekstack/multiple_types_stack [ OK ] [ 0.00000200 / 0.00000300 CPU ] 1 of 1 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0x0dc3c3f9... -snekstack/multiple_types_stack [ OK ] [ 0.00000300 / 0.00000200 CPU ] +Running test suite with seed 0xa5a077a3... +object-integer-def/integer_constant [ OK ] [ 0.00000000 / 0.00000100 CPU ] +object-integer-def/integer_obj [ OK ] [ 0.00000100 / 0.00000100 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x0a7224ef... +object-integer/positive [ OK ] [ 0.00000100 / 0.00000200 CPU ] +object-integer/zero [ OK ] [ 0.00000100 / 0.00000100 CPU ] +object-integer/negative [ OK ] [ 0.00000100 / 0.00000100 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x748c3598... +object-float/positive [ OK ] [ 0.00000100 / 0.00000200 CPU ] +object-float/zero [ OK ] [ 0.00000100 / 0.00000100 CPU ] +object-float/negative [ OK ] [ 0.00000100 / 0.00000100 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x4632a6a6... +object-string/copies_value [ OK ] [ 0.00000200 / 0.00000200 CPU ] 1 of 1 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x386735e2... +object-vector/returns_null [ OK ] [ 0.00000100 / 0.00000200 CPU ] +object-vector/multiple_objects [ OK ] [ 0.00000200 / 0.00000200 CPU ] +object-vector/same_object [ OK ] [ 0.00000100 / 0.00000200 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x4e6badf5... +object-array/empty [ OK ] [ 0.00000100 / 0.00000100 CPU ] +object-array/calloc [ OK ] [ 0.00000100 / 0.00000100 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xe9625367... +object-array/set_and_get [ OK ] [ 0.00000200 / 0.00000200 CPU ] +object-array/set_outside [ OK ] [ 0.00000200 / 0.00000300 CPU ] +object-array/get_outside [ OK ] [ 0.00000200 / 0.00000200 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xa7022b54... +object-length/integer [ OK ] [ 0.00000200 / 0.00000100 CPU ] +object-length/float [ OK ] [ 0.00000200 / 0.00000200 CPU ] +object-length/string [ OK ] [ 0.00000200 / 0.00000200 CPU ] +object-length/vector [ OK ] [ 0.00000200 / 0.00000200 CPU ] +object-length/array [ OK ] [ 0.00000200 / 0.00000100 CPU ] +5 of 5 (100%) tests successful, 0 (0%) test skipped. [100%] Built target run #+END_SRC diff --git a/src/10-1-garbage-collector/main.c b/src/10-1-garbage-collector/main.c new file mode 100644 index 0000000..f1531ae --- /dev/null +++ b/src/10-1-garbage-collector/main.c @@ -0,0 +1,3 @@ + +// Q: Which language makes use of a garbage collector (automatic memory management)? +// A: Go diff --git a/src/10-2-refcounting/main.c b/src/10-2-refcounting/main.c new file mode 100644 index 0000000..9037f23 --- /dev/null +++ b/src/10-2-refcounting/main.c @@ -0,0 +1,30 @@ +#include +#include + +// #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); +} diff --git a/src/10-2-refcounting/snekobject.c b/src/10-2-refcounting/snekobject.c new file mode 100644 index 0000000..32c3a03 --- /dev/null +++ b/src/10-2-refcounting/snekobject.c @@ -0,0 +1,91 @@ +#include +#include + +#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; +} diff --git a/src/10-2-refcounting/snekobject.h b/src/10-2-refcounting/snekobject.h new file mode 100644 index 0000000..acb686f --- /dev/null +++ b/src/10-2-refcounting/snekobject.h @@ -0,0 +1,47 @@ +#include +#include + +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); diff --git a/src/10-3-increment/main.c b/src/10-3-increment/main.c new file mode 100644 index 0000000..b5fead1 --- /dev/null +++ b/src/10-3-increment/main.c @@ -0,0 +1,48 @@ +#include +#include + +// #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); +} diff --git a/src/10-3-increment/snekobject.c b/src/10-3-increment/snekobject.c new file mode 100644 index 0000000..8eb04e0 --- /dev/null +++ b/src/10-3-increment/snekobject.c @@ -0,0 +1,102 @@ +#include "assert.h" +#include +#include +#include + +#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; +} diff --git a/src/10-3-increment/snekobject.h b/src/10-3-increment/snekobject.h new file mode 100644 index 0000000..fd693b1 --- /dev/null +++ b/src/10-3-increment/snekobject.h @@ -0,0 +1,49 @@ +#include +#include + +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); diff --git a/src/10-4-decrement-and-free/main.c b/src/10-4-decrement-and-free/main.c new file mode 100644 index 0000000..a70f91f --- /dev/null +++ b/src/10-4-decrement-and-free/main.c @@ -0,0 +1,82 @@ +#include +#include + +// #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); +} diff --git a/src/10-4-decrement-and-free/snekobject.c b/src/10-4-decrement-and-free/snekobject.c new file mode 100644 index 0000000..8258606 --- /dev/null +++ b/src/10-4-decrement-and-free/snekobject.c @@ -0,0 +1,123 @@ +#include "assert.h" +#include +#include +#include + +#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; +} diff --git a/src/10-4-decrement-and-free/snekobject.h b/src/10-4-decrement-and-free/snekobject.h new file mode 100644 index 0000000..a89fbd7 --- /dev/null +++ b/src/10-4-decrement-and-free/snekobject.h @@ -0,0 +1,51 @@ +#include +#include + +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); diff --git a/src/10-5-vectors/main.c b/src/10-5-vectors/main.c new file mode 100644 index 0000000..bd48034 --- /dev/null +++ b/src/10-5-vectors/main.c @@ -0,0 +1,128 @@ +#include +#include + +// #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); +} diff --git a/src/10-5-vectors/snekobject.c b/src/10-5-vectors/snekobject.c new file mode 100644 index 0000000..eb73b56 --- /dev/null +++ b/src/10-5-vectors/snekobject.c @@ -0,0 +1,140 @@ +#include "assert.h" +#include +#include +#include + +#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; +} diff --git a/src/10-5-vectors/snekobject.h b/src/10-5-vectors/snekobject.h new file mode 100644 index 0000000..a89fbd7 --- /dev/null +++ b/src/10-5-vectors/snekobject.h @@ -0,0 +1,51 @@ +#include +#include + +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); diff --git a/src/10-6-arrays/main.c b/src/10-6-arrays/main.c new file mode 100644 index 0000000..b460b39 --- /dev/null +++ b/src/10-6-arrays/main.c @@ -0,0 +1,154 @@ +#include +#include + +// #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); +} diff --git a/src/10-6-arrays/snekobject.c b/src/10-6-arrays/snekobject.c new file mode 100644 index 0000000..c2871a5 --- /dev/null +++ b/src/10-6-arrays/snekobject.c @@ -0,0 +1,181 @@ +#include "assert.h" +#include +#include +#include + +#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; +} diff --git a/src/10-6-arrays/snekobject.h b/src/10-6-arrays/snekobject.h new file mode 100644 index 0000000..a89fbd7 --- /dev/null +++ b/src/10-6-arrays/snekobject.h @@ -0,0 +1,51 @@ +#include +#include + +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); diff --git a/src/10-7-refcounting-review/main.c b/src/10-7-refcounting-review/main.c new file mode 100644 index 0000000..c753e7c --- /dev/null +++ b/src/10-7-refcounting-review/main.c @@ -0,0 +1,5 @@ + +// Q: Why doesn't the snek_add function need to take care of +// garbage collecting the resulting object? + +// A: diff --git a/src/9-6-arrays/main.c b/src/9-6-arrays/main.c new file mode 100644 index 0000000..2a00bc8 --- /dev/null +++ b/src/9-6-arrays/main.c @@ -0,0 +1,38 @@ +#include + +#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); +} diff --git a/src/9-6-arrays/snekobject.c b/src/9-6-arrays/snekobject.c new file mode 100644 index 0000000..2252514 --- /dev/null +++ b/src/9-6-arrays/snekobject.c @@ -0,0 +1,81 @@ +#include +#include + +#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; +} diff --git a/src/9-6-arrays/snekobject.h b/src/9-6-arrays/snekobject.h new file mode 100644 index 0000000..37b4813 --- /dev/null +++ b/src/9-6-arrays/snekobject.h @@ -0,0 +1,41 @@ +#include + +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); diff --git a/src/9-7-get-and-set/main.c b/src/9-7-get-and-set/main.c new file mode 100644 index 0000000..3d4a9cc --- /dev/null +++ b/src/9-7-get-and-set/main.c @@ -0,0 +1,83 @@ +#include + +#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); +} diff --git a/src/9-7-get-and-set/snekobject.c b/src/9-7-get-and-set/snekobject.c new file mode 100644 index 0000000..3463e2d --- /dev/null +++ b/src/9-7-get-and-set/snekobject.c @@ -0,0 +1,101 @@ +#include +#include +#include + +#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; +} diff --git a/src/9-7-get-and-set/snekobject.h b/src/9-7-get-and-set/snekobject.h new file mode 100644 index 0000000..1928241 --- /dev/null +++ b/src/9-7-get-and-set/snekobject.h @@ -0,0 +1,45 @@ +#include +#include + +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); diff --git a/src/9-8-length/main.c b/src/9-8-length/main.c new file mode 100644 index 0000000..9518a1d --- /dev/null +++ b/src/9-8-length/main.c @@ -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); +} diff --git a/src/9-8-length/snekobject.c b/src/9-8-length/snekobject.c new file mode 100644 index 0000000..48f4e6a --- /dev/null +++ b/src/9-8-length/snekobject.c @@ -0,0 +1,127 @@ +#include +#include + +#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; +} diff --git a/src/9-8-length/snekobject.h b/src/9-8-length/snekobject.h new file mode 100644 index 0000000..d742e63 --- /dev/null +++ b/src/9-8-length/snekobject.h @@ -0,0 +1,46 @@ +#include +#include + +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); diff --git a/src/munit.h b/src/munit.h index 97307c3..f9c94f0 100644 --- a/src/munit.h +++ b/src/munit.h @@ -1,3 +1,4 @@ +#define MUNIT_ENABLE_ASSERT_ALIASES #include "../vendor/munit/munit.h" // TJ seems to have added a bunch of macros to the default munit.h, @@ -99,6 +100,9 @@ #undef assert_null #define assert_null(PTR, MSG) munit_assert_null(PTR, MSG) +#undef assert_not_null +#define assert_not_null(PTR, MSG) munit_assert_not_null(PTR, MSG) + // ptr #undef munit_assert_ptr_not_equal @@ -108,6 +112,10 @@ #undef assert_ptr #define assert_ptr(A, OP, B, MSG) munit_assert_ptr(A, OP, B) +#undef assert_ptr_null +#define assert_ptr_null(PTR, MSG) munit_assert_ptr_null(PTR) +// https://github.com/nemequ/munit/pull/73/files + #undef assert_ptr_not_null #define assert_ptr_not_null(PTR, MSG) munit_assert_not_null(PTR, MSG)