diff --git a/README.org b/README.org new file mode 100644 index 0000000..3f33061 --- /dev/null +++ b/README.org @@ -0,0 +1,207 @@ +#+TITLE: Learn Memory Management in C +#+AUTHOR: Riyyi +#+LANGUAGE: en +#+OPTIONS: toc:nil + +Completed course from +[[https://www.boot.dev/courses/learn-memory-management-c][boot.dev]]. + +The exercises from the course have been worked out in this repo. +This is my way of having the "certificate" for free. + +If you're reading this with the intention of completing the exercises yourself, +just remove the directories from the =src= directory and recreate the directory +with the files from the exercise. Alternatively, just remove the file the +exercise is asking you to modify as you go. + +Note: +- In chapter 6 exercise 6, macOS malloc will almost never return a NULL pointer + due to virtual memory overcommit, so you should check for + =size \=\= 1024 * 1024 * 100)= instead of =array \=\= NULL= +- In chapter 8, I renamed =stack_t= to =my_stack_t= due to =std= naming conflict + on macOS + +** Build instructions + +Compiling and running all the tests can be done with the following commands: + +#+BEGIN_SRC sh +$ mkdir build +$ cd build +$ cmake .. +$ make run +#+END_SRC + +** Output + +The first the 3 chapters of the course can be done interactively on the website. + +[[./bootdev-c.png]] + +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 ] +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 ] +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 ] +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 ] +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 ] +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 ] +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 ] +4 of 4 (100%) tests successful, 0 (0%) test skipped. +--------------------------------- Stack pointer offset: 0 bytes +--------------------------------- +Dark mode? +--------------------------------- +Stack pointer offset: 0 bytes +--------------------------------- +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 ] +/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=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 ] +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 ] +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 ] +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_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... +/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=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 ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x4c18af41... +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 ] +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 ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xc10a0ca0... +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 ] +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 ] +snekstack/push_stack [ OK ] [ 0.00000100 / 0.00000200 CPU ] +snekstack/push_double_capacity [ OK ] [ 0.00000200 / 0.00000200 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xd641eade... +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/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 ] +snekstack/push_stack [ OK ] [ 0.00000200 / 0.00000200 CPU ] +snekstack/pop_stack [ OK ] [ 0.00000200 / 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 ] +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 ] +1 of 1 (100%) tests successful, 0 (0%) test skipped. +[100%] Built target run +#+END_SRC diff --git a/bootdev-c.png b/bootdev-c.png new file mode 100644 index 0000000..56c8022 Binary files /dev/null and b/bootdev-c.png differ diff --git a/src/8-1-low-level-stack/main.c b/src/8-1-low-level-stack/main.c new file mode 100644 index 0000000..39bff9f --- /dev/null +++ b/src/8-1-low-level-stack/main.c @@ -0,0 +1,39 @@ +// #include "bootlib.h" +#include "munit.h" +#include "snekstack.h" + +munit_case(RUN, create_stack_small, { + my_stack_t *s = stack_new(3); + assert_int(s->capacity, ==, 3, "Sets capacity to 3"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + free(s->data); + free(s); + + // assert(boot_all_freed()); +}); + +munit_case(SUBMIT, create_stack_large, { + my_stack_t *s = stack_new(100); + assert_int(s->capacity, ==, 100, "Sets capacity to 100"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + free(s->data); + free(s); + + // assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/create_stack_small", create_stack_small), + munit_test("/create_stack_large", create_stack_large), + munit_null_test, + }; + + MunitSuite suite = munit_suite("snekstack", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/8-1-low-level-stack/snekstack.c b/src/8-1-low-level-stack/snekstack.c new file mode 100644 index 0000000..d9f3e17 --- /dev/null +++ b/src/8-1-low-level-stack/snekstack.c @@ -0,0 +1,18 @@ +#include + +#include "snekstack.h" + +my_stack_t *stack_new(size_t capacity) +{ + my_stack_t *stack = (my_stack_t *)malloc(sizeof(my_stack_t)); + if (stack == NULL) return NULL; + stack->count = 0; + stack->capacity = capacity; + stack->data = malloc(sizeof(void*) * capacity); + if (stack->data == NULL) { + free(stack); + return NULL; + } + + return stack; +} diff --git a/src/8-1-low-level-stack/snekstack.h b/src/8-1-low-level-stack/snekstack.h new file mode 100644 index 0000000..cc3a5e3 --- /dev/null +++ b/src/8-1-low-level-stack/snekstack.h @@ -0,0 +1,9 @@ +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; // NOTE: renamed due to std naming conflict on macOS + +my_stack_t *stack_new(size_t capacity); diff --git a/src/8-2-stack-push/main.c b/src/8-2-stack-push/main.c new file mode 100644 index 0000000..e9a3673 --- /dev/null +++ b/src/8-2-stack-push/main.c @@ -0,0 +1,88 @@ +// #include "bootlib.h" +#include "munit.h" +#include "snekstack.h" + +munit_case(RUN, create_stack, { + my_stack_t *s = stack_new(10); + assert_int(s->capacity, ==, 10, "Sets capacity to 10"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + +munit_case(RUN, push_stack, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int a = 1; + + stack_push(s, &a); + stack_push(s, &a); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 2, "2 elements in the stack"); + assert_ptr_equal(s->data[0], &a, "element inserted into stack"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + +munit_case(SUBMIT, push_double_capacity, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int a = 1; + + stack_push(s, &a); + stack_push(s, &a); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 2, "2 elements in the stack"); + + stack_push(s, &a); + assert_int(s->capacity, ==, 4, "Capacity is doubled"); + assert_int(s->count, ==, 3, "3 elements in the stack"); + + // Should reallocate memory. + // assert_int_equal(boot_realloc_count(), 1, "Must reallocate memory for stack"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + + + +int main() { + MunitTest tests[] = { + munit_test("/create_stack", create_stack), + munit_test("/push_stack", push_stack), + munit_test("/push_double_capacity", push_double_capacity), + munit_null_test, + }; + + MunitSuite suite = munit_suite("snekstack", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/8-2-stack-push/snekstack.c b/src/8-2-stack-push/snekstack.c new file mode 100644 index 0000000..89d5618 --- /dev/null +++ b/src/8-2-stack-push/snekstack.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +#include "snekstack.h" + +void stack_push(my_stack_t *stack, void *obj) { + + if (stack->count >= stack->capacity) { + void **data = realloc(stack->data, sizeof(void *) * stack->capacity * 2); + if (data == NULL) return; + stack->data = data; + stack->capacity *= 2; + } + + stack->data[stack->count] = obj; + stack->count++; +} + +// don't touch below this line + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + free(stack); + return NULL; + } + + return stack; +} diff --git a/src/8-2-stack-push/snekstack.h b/src/8-2-stack-push/snekstack.h new file mode 100644 index 0000000..642657d --- /dev/null +++ b/src/8-2-stack-push/snekstack.h @@ -0,0 +1,10 @@ +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; // NOTE: renamed due to std naming conflict on macOS + +my_stack_t *stack_new(size_t capacity); +void stack_push(my_stack_t *stack, void *obj); diff --git a/src/8-3-stack-pop/main.c b/src/8-3-stack-pop/main.c new file mode 100644 index 0000000..80e24c1 --- /dev/null +++ b/src/8-3-stack-pop/main.c @@ -0,0 +1,121 @@ +// #include "bootlib.h" +#include "munit.h" +#include "snekstack.h" + +munit_case(RUN, pop_stack, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int one = 1; + int two = 2; + int three = 3; + + stack_push(s, &one); + stack_push(s, &two); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 2, "2 elements in the stack"); + + stack_push(s, &three); + assert_int(s->capacity, ==, 4, "Capacity is doubled"); + assert_int(s->count, ==, 3, "3 elements in the stack"); + + int *popped = stack_pop(s); + assert_int(*popped, ==, three, "Should pop the last element"); + + popped = stack_pop(s); + assert_int(*popped, ==, two, "Should pop the last element"); + + popped = stack_pop(s); + assert_int(*popped, ==, one, "Should pop the only remaining element"); + + popped = stack_pop(s); + assert_null(popped, "No remaining elements"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + +munit_case(SUBMIT, pop_stack_empty, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int *popped = stack_pop(s); + assert_null(popped, "Should return null when popping an empty stack"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + +munit_case(RUN, push_stack, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int a = 1; + + stack_push(s, &a); + stack_push(s, &a); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 2, "2 elements in the stack"); + + stack_push(s, &a); + assert_int(s->capacity, ==, 4, "Capacity is doubled"); + assert_int(s->count, ==, 3, "3 elements in the stack"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + +munit_case(RUN, create_stack, { + my_stack_t *s = stack_new(10); + assert_int(s->capacity, ==, 10, "Sets capacity to 10"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + // Clean up our allocated data. + free(s->data); + free(s); + + // Should be nothing left that is allocated. + // assert(boot_all_freed()); +}); + + +int main() { + MunitTest tests[] = { + munit_test("/create_stack", create_stack), + munit_test("/push_stack", push_stack), + munit_test("/pop_stack", pop_stack), + munit_test("/pop_stack_empty", pop_stack_empty), + munit_null_test, + }; + + MunitSuite suite = munit_suite("snekstack", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/8-3-stack-pop/snekstack.c b/src/8-3-stack-pop/snekstack.c new file mode 100644 index 0000000..5180f43 --- /dev/null +++ b/src/8-3-stack-pop/snekstack.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "snekstack.h" + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) return NULL; + stack->count--; + return stack->data[stack->count]; +} + +// don't touch below this line' + +void stack_push(my_stack_t *stack, void *obj) { + if (stack->count == stack->capacity) { + stack->capacity *= 2; + void **temp = realloc(stack->data, stack->capacity * sizeof(void *)); + if (temp == NULL) { + stack->capacity /= 2; + + exit(1); + } + stack->data = temp; + } + stack->data[stack->count] = obj; + stack->count++; + return; +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + free(stack); + return NULL; + } + + return stack; +} diff --git a/src/8-3-stack-pop/snekstack.h b/src/8-3-stack-pop/snekstack.h new file mode 100644 index 0000000..8ea6c74 --- /dev/null +++ b/src/8-3-stack-pop/snekstack.h @@ -0,0 +1,11 @@ +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; // NOTE: renamed due to std naming conflict on macOS + +my_stack_t *stack_new(size_t capacity); +void stack_push(my_stack_t *stack, void *obj); +void *stack_pop(my_stack_t *stack); diff --git a/src/8-4-stack-free/main.c b/src/8-4-stack-free/main.c new file mode 100644 index 0000000..5c076f7 --- /dev/null +++ b/src/8-4-stack-free/main.c @@ -0,0 +1,88 @@ +// #include "bootlib.h" +#include "munit.h" +#include "snekstack.h" + +munit_case(RUN, pop_stack, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int one = 1; + int two = 2; + int three = 3; + + stack_push(s, &one); + stack_push(s, &two); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 2, "2 elements in the stack"); + + stack_push(s, &three); + assert_int(s->capacity, ==, 4, "Capacity is doubled"); + assert_int(s->count, ==, 3, "3 elements in the stack"); + + int *popped = stack_pop(s); + assert_int(*popped, ==, three, "Should pop the last element"); + + popped = stack_pop(s); + assert_int(*popped, ==, two, "Should pop the last element"); + + popped = stack_pop(s); + assert_int(*popped, ==, one, "Should pop the only remaining element"); + + popped = stack_pop(s); + assert_null(popped, "No remaining elements"); + + stack_free(s); + // assert(boot_all_freed()); +}); + +munit_case(RUN, push_stack, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + int a = 1; + + stack_push(s, &a); + stack_push(s, &a); + + assert_int(s->capacity, ==, 2, "Sets capacity to 2"); + assert_int(s->count, ==, 2, "2 elements in the stack"); + + stack_push(s, &a); + assert_int(s->capacity, ==, 4, "Capacity is doubled"); + assert_int(s->count, ==, 3, "3 elements in the stack"); + + stack_free(s); + // assert(boot_all_freed()); +}); + +munit_case(RUN, create_stack, { + my_stack_t *s = stack_new(10); + assert_int(s->capacity, ==, 10, "Sets capacity to 10"); + assert_int(s->count, ==, 0, "No elements in the stack yet"); + assert_ptr_not_null(s->data, "Allocates the stack data"); + + stack_free(s); + // assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/create_stack", create_stack), + munit_test("/push_stack", push_stack), + munit_test("/pop_stack", pop_stack), + munit_null_test, + }; + + MunitSuite suite = munit_suite("snekstack", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/8-4-stack-free/snekstack.c b/src/8-4-stack-free/snekstack.c new file mode 100644 index 0000000..310cebf --- /dev/null +++ b/src/8-4-stack-free/snekstack.c @@ -0,0 +1,59 @@ +#include +#include +#include + +#include "snekstack.h" + +void stack_free(my_stack_t *stack) { + if (stack == NULL) return; + + if (stack->data != NULL) { + free(stack->data); + stack->data = NULL; + } + + free(stack); +} + +// don't touch below this line + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_push(my_stack_t *stack, void *obj) { + if (stack->count == stack->capacity) { + stack->capacity *= 2; + void **temp = realloc(stack->data, stack->capacity * sizeof(void *)); + if (temp == NULL) { + stack->capacity /= 2; + exit(1); + } + stack->data = temp; + } + stack->data[stack->count] = obj; + stack->count++; + return; +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + free(stack); + return NULL; + } + + return stack; +} diff --git a/src/8-4-stack-free/snekstack.h b/src/8-4-stack-free/snekstack.h new file mode 100644 index 0000000..765024a --- /dev/null +++ b/src/8-4-stack-free/snekstack.h @@ -0,0 +1,12 @@ +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; // NOTE: renamed due to std naming conflict on macOS + +my_stack_t *stack_new(size_t capacity); +void stack_push(my_stack_t *stack, void *obj); +void *stack_pop(my_stack_t *stack); +void stack_free(my_stack_t *stack); diff --git a/src/8-5-dangerous-push/exercise.c b/src/8-5-dangerous-push/exercise.c new file mode 100644 index 0000000..b597727 --- /dev/null +++ b/src/8-5-dangerous-push/exercise.c @@ -0,0 +1,10 @@ +#include "snekstack.h" +#include "stdlib.h" + +void scary_double_push(my_stack_t *s) { + stack_push(s, (void *)1337); + int *number = malloc(sizeof(int)); + if (number == NULL) return; + *number = 1024; + stack_push(s, number); +} diff --git a/src/8-5-dangerous-push/exercise.h b/src/8-5-dangerous-push/exercise.h new file mode 100644 index 0000000..c05fd0f --- /dev/null +++ b/src/8-5-dangerous-push/exercise.h @@ -0,0 +1,3 @@ +#include "snekstack.h" + +void scary_double_push(my_stack_t *s); diff --git a/src/8-5-dangerous-push/main.c b/src/8-5-dangerous-push/main.c new file mode 100644 index 0000000..e008791 --- /dev/null +++ b/src/8-5-dangerous-push/main.c @@ -0,0 +1,33 @@ +#include "exercise.h" + +// #include "bootlib.h" +#include "munit.h" +#include "snekstack.h" + +munit_case(RUN, heterogenous_stack, { + my_stack_t *s = stack_new(2); + assert_ptr_not_null(s, "Must allocate a new stack"); + + scary_double_push(s); + assert_int(s->count, ==, 2, "Should have two items in the stack"); + + int value = (int)s->data[0]; + assert_int(value, ==, 1337, "Zero item should be 1337"); + + int *pointer = s->data[1]; + assert_int(*pointer, ==, 1024, "Top item should be 1024"); + + free(pointer); + stack_free(s); +}); + +int main() { + MunitTest tests[] = { + munit_test("/heterogenous_stack", heterogenous_stack), + munit_null_test, + }; + + MunitSuite suite = munit_suite("snekstack", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/8-5-dangerous-push/snekstack.c b/src/8-5-dangerous-push/snekstack.c new file mode 100644 index 0000000..9d2cc49 --- /dev/null +++ b/src/8-5-dangerous-push/snekstack.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "snekstack.h" + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + free(stack->data); + } + + free(stack); +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_push(my_stack_t *stack, void *obj) { + if (stack->count == stack->capacity) { + stack->capacity *= 2; + void **temp = realloc(stack->data, stack->capacity * sizeof(void *)); + if (temp == NULL) { + stack->capacity /= 2; + exit(1); + } + stack->data = temp; + } + stack->data[stack->count] = obj; + stack->count++; + return; +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + free(stack); + return NULL; + } + + return stack; +} diff --git a/src/8-5-dangerous-push/snekstack.h b/src/8-5-dangerous-push/snekstack.h new file mode 100644 index 0000000..a337adc --- /dev/null +++ b/src/8-5-dangerous-push/snekstack.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; // NOTE: renamed due to std naming conflict on macOS + +my_stack_t *stack_new(size_t capacity); +void stack_push(my_stack_t *stack, void *obj); +void *stack_pop(my_stack_t *stack); +void stack_free(my_stack_t *stack); diff --git a/src/8-6-multiple-types/exercise.c b/src/8-6-multiple-types/exercise.c new file mode 100644 index 0000000..f15111b --- /dev/null +++ b/src/8-6-multiple-types/exercise.c @@ -0,0 +1,20 @@ +#include +#include + +#include "snekstack.h" + +void stack_push_multiple_types(my_stack_t *s) +{ + float *pi = (float *)malloc(sizeof(float)); + if (pi == NULL) return; + *pi = 3.14; + + stack_push(s, pi); + + const char *text = "Sneklang is blazingly slow!"; + char *str = (char *)malloc(sizeof(char) * strlen(text)); + if (str == NULL) return; + strcpy(str, text); + + stack_push(s, str); +} diff --git a/src/8-6-multiple-types/exercise.h b/src/8-6-multiple-types/exercise.h new file mode 100644 index 0000000..26c6330 --- /dev/null +++ b/src/8-6-multiple-types/exercise.h @@ -0,0 +1,3 @@ +#include "snekstack.h" + +void stack_push_multiple_types(my_stack_t *s); diff --git a/src/8-6-multiple-types/main.c b/src/8-6-multiple-types/main.c new file mode 100644 index 0000000..6e3c3e9 --- /dev/null +++ b/src/8-6-multiple-types/main.c @@ -0,0 +1,34 @@ +#include "exercise.h" + +// #include "bootlib.h" +#include "munit.h" +#include "snekstack.h" + +munit_case(RUN, multiple_types_stack, { + my_stack_t *s = stack_new(4); + assert_ptr_not_null(s, "Must allocate a new stack"); + + stack_push_multiple_types(s); + assert_int(s->count, ==, 2, "Should have two items in the stack"); + + float *f = s->data[0]; + assert_float(*f, ==, 3.14, "Float is equal"); + + char *string = s->data[1]; + assert_string_equal(string, "Sneklang is blazingly slow!", "char* is equal"); + + free(f); + free(string); + stack_free(s); +}); + +int main() { + MunitTest tests[] = { + munit_test("/multiple_types_stack", multiple_types_stack), + munit_null_test, + }; + + MunitSuite suite = munit_suite("snekstack", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/8-6-multiple-types/snekstack.c b/src/8-6-multiple-types/snekstack.c new file mode 100644 index 0000000..9d2cc49 --- /dev/null +++ b/src/8-6-multiple-types/snekstack.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "snekstack.h" + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + free(stack->data); + } + + free(stack); +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_push(my_stack_t *stack, void *obj) { + if (stack->count == stack->capacity) { + stack->capacity *= 2; + void **temp = realloc(stack->data, stack->capacity * sizeof(void *)); + if (temp == NULL) { + stack->capacity /= 2; + exit(1); + } + stack->data = temp; + } + stack->data[stack->count] = obj; + stack->count++; + return; +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + free(stack); + return NULL; + } + + return stack; +} diff --git a/src/8-6-multiple-types/snekstack.h b/src/8-6-multiple-types/snekstack.h new file mode 100644 index 0000000..a337adc --- /dev/null +++ b/src/8-6-multiple-types/snekstack.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; // NOTE: renamed due to std naming conflict on macOS + +my_stack_t *stack_new(size_t capacity); +void stack_push(my_stack_t *stack, void *obj); +void *stack_pop(my_stack_t *stack); +void stack_free(my_stack_t *stack); diff --git a/src/munit.h b/src/munit.h index d6822d8..e2389c0 100644 --- a/src/munit.h +++ b/src/munit.h @@ -43,6 +43,10 @@ #define assert_uint(A, OP, B, MSG) \ munit_assert_uint(A, OP, B); +#undef assert_float +#define assert_float(A, OP, B, MSG) \ + munit_assert_float(A, OP, B, MSG); + #undef assert_double #define assert_double(A, OP, B, MSG) \ munit_assert_double(A, OP, B); @@ -92,11 +96,17 @@ #define munit_assert_not_null(PTR, MSG) \ munit_assert_ptr(PTR, !=, NULL) -// ptr +#undef assert_null +#define assert_null(PTR, MSG) munit_assert_null(PTR, MSG) -#undef assert_ptr_not_null -#define assert_ptr_not_null(PTR, MSG) munit_assert_not_null(PTR, MSG) +// ptr #undef munit_assert_ptr_not_equal #define munit_assert_ptr_not_equal(A, B, MSG) \ munit_assert_ptr(A, !=, B) + +#undef assert_ptr_not_null +#define assert_ptr_not_null(PTR, MSG) munit_assert_not_null(PTR, MSG) + +#undef assert_ptr_equal +#define assert_ptr_equal(A, B, MSG) munit_assert_ptr_equal(A, B)