Browse Source

Implement allocation checking in the unit tests

master
Riyyi 5 days ago
parent
commit
ba0d496a3c
  1. 3
      CMakeLists.txt
  2. 9
      README.org
  3. 18
      src/10-4-decrement-and-free/main.c
  4. 17
      src/10-4-decrement-and-free/snekobject.c
  5. 34
      src/10-5-vectors/main.c
  6. 15
      src/10-5-vectors/snekobject.c
  7. 38
      src/10-6-arrays/main.c
  8. 17
      src/10-6-arrays/snekobject.c
  9. 3
      src/10-7-refcounting-review/main.c
  10. 6
      src/7-6-generic-swap/exercise.c
  11. 6
      src/7-6-generic-swap/main.c
  12. 14
      src/8-1-low-level-stack/main.c
  13. 7
      src/8-1-low-level-stack/snekstack.c
  14. 22
      src/8-2-stack-push/main.c
  15. 13
      src/8-2-stack-push/snekstack.c
  16. 26
      src/8-3-stack-pop/main.c
  17. 9
      src/8-3-stack-pop/snekstack.c
  18. 8
      src/8-4-stack-free/main.c
  19. 13
      src/8-4-stack-free/snekstack.c
  20. 13
      src/9-3-float/main.c
  21. 5
      src/9-3-float/snekobject.c
  22. 12
      src/9-4-string/main.c
  23. 11
      src/9-4-string/snekobject.c
  24. 19
      src/9-5-vector3/main.c
  25. 24
      src/9-5-vector3/snekobject.c
  26. 13
      src/9-6-arrays/main.c
  27. 19
      src/9-6-arrays/snekobject.c
  28. 33
      src/9-7-get-and-set/main.c
  29. 19
      src/9-7-get-and-set/snekobject.c
  30. 33
      src/9-8-length/main.c
  31. 19
      src/9-8-length/snekobject.c
  32. 127
      src/bootlib.c
  33. 16
      src/bootlib.h
  34. 5
      src/munit.h

3
CMakeLists.txt

@ -33,13 +33,14 @@ foreach(LESSON ${LESSONS})
if(NOT LESSON_SOURCES) if(NOT LESSON_SOURCES)
continue() continue()
endif() endif()
list(APPEND LESSON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/bootlib.c)
# Get last directory name of the path to use as exe # Get last directory name of the path to use as exe
get_filename_component(EXE_NAME ${LESSON} NAME) get_filename_component(EXE_NAME ${LESSON} NAME)
add_executable(${EXE_NAME} ${LESSON_SOURCES}) add_executable(${EXE_NAME} ${LESSON_SOURCES})
target_include_directories(${EXE_NAME} PRIVATE target_include_directories(${EXE_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/${LESSON}) ${CMAKE_CURRENT_SOURCE_DIR}/${LESSON})
target_link_libraries(${EXE_NAME} munit) target_link_libraries(${EXE_NAME} munit)

9
README.org

@ -13,11 +13,14 @@ The first the 3 chapters of the course can be done interactively on the website.
[[./bootdev-c.png]] [[./bootdev-c.png]]
If you're reading this with the intention of completing the exercises yourself, 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 you can find each exercise is in its own directory inside of the =src= directory.
with the files from the exercise. Alternatively, just remove the file the Remove all the files that are not called =main.c= and recreate them from the
exercise is asking you to modify as you go. exercises on the boot.dev website as you go through them, so you won't get spoiled.
Note: Note:
- In order to verify allocations in the unit tests, please use the wrapper
functions =boot_malloc=, =boot_calloc=, =boot_realloc= and =boot_free= from
=boot_lib.h= instead of the =stdlib.h= allocation functions
- In chapter 6 exercise 6, macOS malloc will almost never return a NULL pointer - In chapter 6 exercise 6, macOS malloc will almost never return a NULL pointer
due to virtual memory overcommit, so you should check for due to virtual memory overcommit, so you should check for
~size == 1024 * 1024 * 100)~ instead of ~array == NULL~ ~size == 1024 * 1024 * 100)~ instead of ~array == NULL~

18
src/10-4-decrement-and-free/main.c

@ -1,7 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -9,7 +9,7 @@ munit_case(RUN, test_int_has_refcount, {
snek_object_t *obj = new_snek_integer(10); snek_object_t *obj = new_snek_integer(10);
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation");
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_inc_refcount, { munit_case(RUN, test_inc_refcount, {
@ -19,7 +19,7 @@ munit_case(RUN, test_inc_refcount, {
refcount_inc(obj); refcount_inc(obj);
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); assert_int(obj->refcount, ==, 2, "Refcount should be incremented");
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_dec_refcount, { munit_case(RUN, test_dec_refcount, {
@ -31,10 +31,10 @@ munit_case(RUN, test_dec_refcount, {
refcount_dec(obj); refcount_dec(obj);
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); assert_int(obj->refcount, ==, 1, "Refcount should be decremented");
// assert(!boot_is_freed(obj)); assert(!boot_is_freed(obj));
// Object is still alive, so we will free manually. // Object is still alive, so we will free manually.
free(obj); boot_free(obj);
}); });
munit_case(SUBMIT, test_refcount_free_is_called, { munit_case(SUBMIT, test_refcount_free_is_called, {
@ -47,8 +47,8 @@ munit_case(SUBMIT, test_refcount_free_is_called, {
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); assert_int(obj->refcount, ==, 1, "Refcount should be decremented");
refcount_dec(obj); refcount_dec(obj);
// assert(boot_is_freed(obj)); assert(boot_is_freed(obj));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_allocated_string_is_freed, { munit_case(SUBMIT, test_allocated_string_is_freed, {
@ -62,8 +62,8 @@ munit_case(SUBMIT, test_allocated_string_is_freed, {
assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str"); assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str");
refcount_dec(obj); refcount_dec(obj);
// assert(boot_is_freed(obj)); assert(boot_is_freed(obj));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

17
src/10-4-decrement-and-free/snekobject.c

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
void refcount_dec(snek_object_t *obj) { void refcount_dec(snek_object_t *obj) {
@ -14,11 +15,11 @@ void refcount_dec(snek_object_t *obj) {
void refcount_free(snek_object_t *obj) { void refcount_free(snek_object_t *obj) {
if (obj == NULL) return; if (obj == NULL) return;
if (obj->kind == INTEGER || obj->kind == FLOAT) { if (obj->kind == INTEGER || obj->kind == FLOAT) {
free(obj); boot_free(obj);
} }
if (obj->kind == STRING) { if (obj->kind == STRING) {
free(obj->data.v_string); boot_free(obj->data.v_string);
free(obj); boot_free(obj);
} }
} }
@ -34,7 +35,7 @@ void refcount_inc(snek_object_t *obj) {
} }
snek_object_t *_new_snek_object() { snek_object_t *_new_snek_object() {
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -50,9 +51,9 @@ snek_object_t *new_snek_array(size_t size) {
return NULL; return NULL;
} }
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *));
if (elements == NULL) { if (elements == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -109,9 +110,9 @@ snek_object_t *new_snek_string(char *value) {
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

34
src/10-5-vectors/main.c

@ -1,7 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -17,20 +17,20 @@ munit_case(RUN, test_vector3_refcounting, {
// `foo` is stil referenced in the `vec`, so it should not be freed. // `foo` is stil referenced in the `vec`, so it should not be freed.
refcount_dec(foo); refcount_dec(foo);
// assert(!boot_is_freed(foo)); assert(!boot_is_freed(foo));
refcount_dec(vec); refcount_dec(vec);
// assert(boot_is_freed(foo)); assert(boot_is_freed(foo));
// These are still alive, they have the original reference still. // These are still alive, they have the original reference still.
// assert(!boot_is_freed(bar)); assert(!boot_is_freed(bar));
// assert(!boot_is_freed(baz)); assert(!boot_is_freed(baz));
// Decrement the last reference to the objects, so they will be freed. // Decrement the last reference to the objects, so they will be freed.
refcount_dec(bar); refcount_dec(bar);
refcount_dec(baz); refcount_dec(baz);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_vector3_refcounting_same, { munit_case(SUBMIT, test_vector3_refcounting_same, {
@ -41,19 +41,19 @@ munit_case(SUBMIT, test_vector3_refcounting_same, {
// `foo` is stil referenced in the `vec`, so it should not be freed. // `foo` is stil referenced in the `vec`, so it should not be freed.
refcount_dec(foo); refcount_dec(foo);
// assert(!boot_is_freed(foo)); assert(!boot_is_freed(foo));
refcount_dec(vec); refcount_dec(vec);
// assert(boot_is_freed(foo)); assert(boot_is_freed(foo));
// assert(boot_is_freed(vec)); assert(boot_is_freed(vec));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_int_has_refcount, { munit_case(RUN, test_int_has_refcount, {
snek_object_t *obj = new_snek_integer(10); snek_object_t *obj = new_snek_integer(10);
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation");
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_inc_refcount, { munit_case(RUN, test_inc_refcount, {
@ -63,7 +63,7 @@ munit_case(RUN, test_inc_refcount, {
refcount_inc(obj); refcount_inc(obj);
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); assert_int(obj->refcount, ==, 2, "Refcount should be incremented");
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_dec_refcount, { munit_case(RUN, test_dec_refcount, {
@ -78,7 +78,7 @@ munit_case(RUN, test_dec_refcount, {
// assert(!boot_is_freed(obj)); // assert(!boot_is_freed(obj));
// Object is still alive, so we will free manually. // Object is still alive, so we will free manually.
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_refcount_free_is_called, { munit_case(RUN, test_refcount_free_is_called, {
@ -91,8 +91,8 @@ munit_case(RUN, test_refcount_free_is_called, {
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); assert_int(obj->refcount, ==, 1, "Refcount should be decremented");
refcount_dec(obj); refcount_dec(obj);
// assert(boot_is_freed(obj)); assert(boot_is_freed(obj));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_allocated_string_is_freed, { munit_case(RUN, test_allocated_string_is_freed, {
@ -106,8 +106,8 @@ munit_case(RUN, test_allocated_string_is_freed, {
assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str"); assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str");
refcount_dec(obj); refcount_dec(obj);
// assert(boot_is_freed(obj)); assert(boot_is_freed(obj));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

15
src/10-5-vectors/snekobject.c

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
snek_object_t *_new_snek_object(); snek_object_t *_new_snek_object();
@ -31,7 +32,7 @@ void refcount_free(snek_object_t *obj)
case FLOAT: case FLOAT:
break; break;
case STRING: case STRING:
free(obj->data.v_string); boot_free(obj->data.v_string);
break; break;
case VECTOR3: { case VECTOR3: {
refcount_dec(obj->data.v_vector3.x); refcount_dec(obj->data.v_vector3.x);
@ -43,7 +44,7 @@ void refcount_free(snek_object_t *obj)
assert(false); assert(false);
} }
free(obj); boot_free(obj);
} }
// don't touch below this line // don't touch below this line
@ -69,7 +70,7 @@ void refcount_dec(snek_object_t *obj) {
} }
snek_object_t *_new_snek_object() { snek_object_t *_new_snek_object() {
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -85,9 +86,9 @@ snek_object_t *new_snek_array(size_t size) {
return NULL; return NULL;
} }
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *));
if (elements == NULL) { if (elements == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -126,9 +127,9 @@ snek_object_t *new_snek_string(char *value) {
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

38
src/10-6-arrays/main.c

@ -1,7 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -12,11 +12,11 @@ munit_case(RUN, test_array_set, {
snek_array_set(array, 0, foo); snek_array_set(array, 0, foo);
assert_int(foo->refcount, ==, 2, "foo is now referenced by array"); assert_int(foo->refcount, ==, 2, "foo is now referenced by array");
// assert(!boot_is_freed(foo)); assert(!boot_is_freed(foo));
refcount_dec(foo); refcount_dec(foo);
refcount_dec(array); refcount_dec(array);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_array_free, { munit_case(SUBMIT, test_array_free, {
@ -33,17 +33,17 @@ munit_case(SUBMIT, test_array_free, {
// `foo` is stil referenced in the `array`, so it should not be freed. // `foo` is stil referenced in the `array`, so it should not be freed.
refcount_dec(foo); refcount_dec(foo);
// assert(!boot_is_freed(foo)); assert(!boot_is_freed(foo));
// Overwrite index 0, which is `foo`, with `baz`. // Overwrite index 0, which is `foo`, with `baz`.
// Now `foo` is not referenced by `array`, so it should be freed. // Now `foo` is not referenced by `array`, so it should be freed.
snek_array_set(array, 0, baz); snek_array_set(array, 0, baz);
// assert(boot_is_freed(foo)); assert(boot_is_freed(foo));
refcount_dec(bar); refcount_dec(bar);
refcount_dec(baz); refcount_dec(baz);
refcount_dec(array); refcount_dec(array);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_vector3_refcounting, { munit_case(RUN, test_vector3_refcounting, {
@ -58,27 +58,27 @@ munit_case(RUN, test_vector3_refcounting, {
// `foo` is stil referenced in the `vec`, so it should not be freed. // `foo` is stil referenced in the `vec`, so it should not be freed.
refcount_dec(foo); refcount_dec(foo);
// assert(!boot_is_freed(foo)); assert(!boot_is_freed(foo));
refcount_dec(vec); refcount_dec(vec);
// assert(boot_is_freed(foo)); assert(boot_is_freed(foo));
// These are still alive, they have the original reference still. // These are still alive, they have the original reference still.
// assert(!boot_is_freed(bar)); assert(!boot_is_freed(bar));
// assert(!boot_is_freed(baz)); assert(!boot_is_freed(baz));
// Decrement the last reference to the objects, so they will be freed. // Decrement the last reference to the objects, so they will be freed.
refcount_dec(bar); refcount_dec(bar);
refcount_dec(baz); refcount_dec(baz);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_int_has_refcount, { munit_case(RUN, test_int_has_refcount, {
snek_object_t *obj = new_snek_integer(10); snek_object_t *obj = new_snek_integer(10);
assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation"); assert_int(obj->refcount, ==, 1, "Refcount should be 1 on creation");
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_inc_refcount, { munit_case(RUN, test_inc_refcount, {
@ -88,7 +88,7 @@ munit_case(RUN, test_inc_refcount, {
refcount_inc(obj); refcount_inc(obj);
assert_int(obj->refcount, ==, 2, "Refcount should be incremented"); assert_int(obj->refcount, ==, 2, "Refcount should be incremented");
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_dec_refcount, { munit_case(RUN, test_dec_refcount, {
@ -100,10 +100,10 @@ munit_case(RUN, test_dec_refcount, {
refcount_dec(obj); refcount_dec(obj);
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); assert_int(obj->refcount, ==, 1, "Refcount should be decremented");
// assert(!boot_is_freed(obj)); assert(!boot_is_freed(obj));
// Object is still alive, so we will free manually. // Object is still alive, so we will free manually.
free(obj); boot_free(obj);
}); });
munit_case(RUN, test_refcount_free_is_called, { munit_case(RUN, test_refcount_free_is_called, {
@ -116,8 +116,8 @@ munit_case(RUN, test_refcount_free_is_called, {
assert_int(obj->refcount, ==, 1, "Refcount should be decremented"); assert_int(obj->refcount, ==, 1, "Refcount should be decremented");
refcount_dec(obj); refcount_dec(obj);
// assert(boot_is_freed(obj)); assert(boot_is_freed(obj));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_allocated_string_is_freed, { munit_case(RUN, test_allocated_string_is_freed, {
@ -131,8 +131,8 @@ munit_case(RUN, test_allocated_string_is_freed, {
assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str"); assert_string_equal(obj->data.v_string, "Hello @wagslane!", "references str");
refcount_dec(obj); refcount_dec(obj);
// assert(boot_is_freed(obj)); assert(boot_is_freed(obj));
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

17
src/10-6-arrays/snekobject.c

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value) bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value)
@ -31,7 +32,7 @@ void refcount_free(snek_object_t *obj)
case FLOAT: case FLOAT:
break; break;
case STRING: case STRING:
free(obj->data.v_string); boot_free(obj->data.v_string);
break; break;
case VECTOR3: { case VECTOR3: {
snek_vector_t vec = obj->data.v_vector3; snek_vector_t vec = obj->data.v_vector3;
@ -44,13 +45,13 @@ void refcount_free(snek_object_t *obj)
for (int i = 0; i < obj->data.v_array.size; i++) { for (int i = 0; i < obj->data.v_array.size; i++) {
refcount_dec(obj->data.v_array.elements[i]); refcount_dec(obj->data.v_array.elements[i]);
} }
free(obj->data.v_array.elements); boot_free(obj->data.v_array.elements);
break; break;
} }
default: default:
assert(false); assert(false);
} }
free(obj); boot_free(obj);
} }
// don't touch below this line // don't touch below this line
@ -92,7 +93,7 @@ void refcount_dec(snek_object_t *obj) {
} }
snek_object_t *_new_snek_object() { snek_object_t *_new_snek_object() {
snek_object_t *obj = calloc(1, sizeof(snek_object_t)); snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -108,9 +109,9 @@ snek_object_t *new_snek_array(size_t size) {
return NULL; return NULL;
} }
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *));
if (elements == NULL) { if (elements == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -149,9 +150,9 @@ snek_object_t *new_snek_string(char *value) {
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

3
src/10-7-refcounting-review/main.c

@ -2,4 +2,5 @@
// Q: Why doesn't the snek_add function need to take care of // Q: Why doesn't the snek_add function need to take care of
// garbage collecting the resulting object? // garbage collecting the resulting object?
// A: // A: Because we've coded the GC logic to happen automatically whenever a
// reference count hits 0

6
src/7-6-generic-swap/exercise.c

@ -1,13 +1,15 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
void swap(void *vp1, void *vp2, size_t size) { void swap(void *vp1, void *vp2, size_t size) {
if (vp1 == vp2) return; if (vp1 == vp2) return;
void *tmp = malloc(size); void *tmp = boot_malloc(size);
if (tmp == NULL) return; if (tmp == NULL) return;
memcpy(tmp, vp1, size); memcpy(tmp, vp1, size);
memcpy(vp1, vp2, size); memcpy(vp1, vp2, size);
memcpy(vp2, tmp, size); memcpy(vp2, tmp, size);
free(tmp); boot_free(tmp);
} }

6
src/7-6-generic-swap/main.c

@ -1,4 +1,4 @@
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "exercise.h" #include "exercise.h"
@ -18,7 +18,7 @@ munit_case(RUN, test_generic_ints, {
assert_int(i1, ==, 5678, "i1 should be i2's original value"); assert_int(i1, ==, 5678, "i1 should be i2's original value");
assert_int(i2, ==, 1234, "i2 should be i1's original value"); assert_int(i2, ==, 1234, "i2 should be i1's original value");
// assert_true(boot_all_freed()); assert_true(boot_all_freed());
}); });
munit_case(RUN, test_generic_strings, { munit_case(RUN, test_generic_strings, {
@ -28,7 +28,7 @@ munit_case(RUN, test_generic_strings, {
swap(&s1, &s2, sizeof(char *)); swap(&s1, &s2, sizeof(char *));
assert_string_equal(s1, "adam", "s1 should be s2's original value"); assert_string_equal(s1, "adam", "s1 should be s2's original value");
assert_string_equal(s2, "dax", "s2 should be s1's original value"); assert_string_equal(s2, "dax", "s2 should be s1's original value");
// assert_true(boot_all_freed()); assert_true(boot_all_freed());
}); });
munit_case(SUBMIT, test_generic_structs, { munit_case(SUBMIT, test_generic_structs, {

14
src/8-1-low-level-stack/main.c

@ -1,4 +1,4 @@
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekstack.h" #include "snekstack.h"
@ -8,10 +8,10 @@ munit_case(RUN, create_stack_small, {
assert_int(s->count, ==, 0, "No elements in the stack yet"); assert_int(s->count, ==, 0, "No elements in the stack yet");
assert_ptr_not_null(s->data, "Allocates the stack data"); assert_ptr_not_null(s->data, "Allocates the stack data");
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, create_stack_large, { munit_case(SUBMIT, create_stack_large, {
@ -20,10 +20,10 @@ munit_case(SUBMIT, create_stack_large, {
assert_int(s->count, ==, 0, "No elements in the stack yet"); assert_int(s->count, ==, 0, "No elements in the stack yet");
assert_ptr_not_null(s->data, "Allocates the stack data"); assert_ptr_not_null(s->data, "Allocates the stack data");
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

7
src/8-1-low-level-stack/snekstack.c

@ -1,16 +1,17 @@
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "snekstack.h" #include "snekstack.h"
my_stack_t *stack_new(size_t capacity) my_stack_t *stack_new(size_t capacity)
{ {
my_stack_t *stack = (my_stack_t *)malloc(sizeof(my_stack_t)); my_stack_t *stack = (my_stack_t *)boot_malloc(sizeof(my_stack_t));
if (stack == NULL) return NULL; if (stack == NULL) return NULL;
stack->count = 0; stack->count = 0;
stack->capacity = capacity; stack->capacity = capacity;
stack->data = malloc(sizeof(void*) * capacity); stack->data = boot_malloc(sizeof(void*) * capacity);
if (stack->data == NULL) { if (stack->data == NULL) {
free(stack); boot_free(stack);
return NULL; return NULL;
} }

22
src/8-2-stack-push/main.c

@ -1,4 +1,4 @@
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekstack.h" #include "snekstack.h"
@ -9,11 +9,11 @@ munit_case(RUN, create_stack, {
assert_ptr_not_null(s->data, "Allocates the stack data"); assert_ptr_not_null(s->data, "Allocates the stack data");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, push_stack, { munit_case(RUN, push_stack, {
@ -34,11 +34,11 @@ munit_case(RUN, push_stack, {
assert_ptr_equal(s->data[0], &a, "element inserted into stack"); assert_ptr_equal(s->data[0], &a, "element inserted into stack");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, push_double_capacity, { munit_case(SUBMIT, push_double_capacity, {
@ -62,14 +62,14 @@ munit_case(SUBMIT, push_double_capacity, {
assert_int(s->count, ==, 3, "3 elements in the stack"); assert_int(s->count, ==, 3, "3 elements in the stack");
// Should reallocate memory. // Should reallocate memory.
// assert_int_equal(boot_realloc_count(), 1, "Must reallocate memory for stack"); assert_int_equal(boot_realloc_count(), 1, "Must reallocate memory for stack");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });

13
src/8-2-stack-push/snekstack.c

@ -3,12 +3,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekstack.h" #include "snekstack.h"
void stack_push(my_stack_t *stack, void *obj) { void stack_push(my_stack_t *stack, void *obj)
{
if (stack->count >= stack->capacity) { if (stack->count >= stack->capacity) {
void **data = realloc(stack->data, sizeof(void *) * stack->capacity * 2); void **data = boot_realloc(stack->data, sizeof(void *) * stack->capacity * 2);
if (data == NULL) return; if (data == NULL) return;
stack->data = data; stack->data = data;
stack->capacity *= 2; stack->capacity *= 2;
@ -21,16 +22,16 @@ void stack_push(my_stack_t *stack, void *obj) {
// don't touch below this line // don't touch below this line
my_stack_t *stack_new(size_t capacity) { my_stack_t *stack_new(size_t capacity) {
my_stack_t *stack = malloc(sizeof(my_stack_t)); my_stack_t *stack = boot_malloc(sizeof(my_stack_t));
if (stack == NULL) { if (stack == NULL) {
return NULL; return NULL;
} }
stack->count = 0; stack->count = 0;
stack->capacity = capacity; stack->capacity = capacity;
stack->data = malloc(stack->capacity * sizeof(void *)); stack->data = boot_malloc(stack->capacity * sizeof(void *));
if (stack->data == NULL) { if (stack->data == NULL) {
free(stack); boot_free(stack);
return NULL; return NULL;
} }

26
src/8-3-stack-pop/main.c

@ -1,4 +1,4 @@
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekstack.h" #include "snekstack.h"
@ -37,11 +37,11 @@ munit_case(RUN, pop_stack, {
assert_null(popped, "No remaining elements"); assert_null(popped, "No remaining elements");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, pop_stack_empty, { munit_case(SUBMIT, pop_stack_empty, {
@ -56,11 +56,11 @@ munit_case(SUBMIT, pop_stack_empty, {
assert_null(popped, "Should return null when popping an empty stack"); assert_null(popped, "Should return null when popping an empty stack");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, push_stack, { munit_case(RUN, push_stack, {
@ -84,11 +84,11 @@ munit_case(RUN, push_stack, {
assert_int(s->count, ==, 3, "3 elements in the stack"); assert_int(s->count, ==, 3, "3 elements in the stack");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, create_stack, { munit_case(RUN, create_stack, {
@ -98,11 +98,11 @@ munit_case(RUN, create_stack, {
assert_ptr_not_null(s->data, "Allocates the stack data"); assert_ptr_not_null(s->data, "Allocates the stack data");
// Clean up our allocated data. // Clean up our allocated data.
free(s->data); boot_free(s->data);
free(s); boot_free(s);
// Should be nothing left that is allocated. // Should be nothing left that is allocated.
// assert(boot_all_freed()); assert(boot_all_freed());
}); });

9
src/8-3-stack-pop/snekstack.c

@ -2,6 +2,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "snekstack.h" #include "snekstack.h"
void *stack_pop(my_stack_t *stack) { void *stack_pop(my_stack_t *stack) {
@ -15,7 +16,7 @@ void *stack_pop(my_stack_t *stack) {
void stack_push(my_stack_t *stack, void *obj) { void stack_push(my_stack_t *stack, void *obj) {
if (stack->count == stack->capacity) { if (stack->count == stack->capacity) {
stack->capacity *= 2; stack->capacity *= 2;
void **temp = realloc(stack->data, stack->capacity * sizeof(void *)); void **temp = boot_realloc(stack->data, stack->capacity * sizeof(void *));
if (temp == NULL) { if (temp == NULL) {
stack->capacity /= 2; stack->capacity /= 2;
@ -29,16 +30,16 @@ void stack_push(my_stack_t *stack, void *obj) {
} }
my_stack_t *stack_new(size_t capacity) { my_stack_t *stack_new(size_t capacity) {
my_stack_t *stack = malloc(sizeof(my_stack_t)); my_stack_t *stack = boot_malloc(sizeof(my_stack_t));
if (stack == NULL) { if (stack == NULL) {
return NULL; return NULL;
} }
stack->count = 0; stack->count = 0;
stack->capacity = capacity; stack->capacity = capacity;
stack->data = malloc(stack->capacity * sizeof(void *)); stack->data = boot_malloc(stack->capacity * sizeof(void *));
if (stack->data == NULL) { if (stack->data == NULL) {
free(stack); boot_free(stack);
return NULL; return NULL;
} }

8
src/8-4-stack-free/main.c

@ -1,4 +1,4 @@
// #include "bootlib.h" #include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekstack.h" #include "snekstack.h"
@ -37,7 +37,7 @@ munit_case(RUN, pop_stack, {
assert_null(popped, "No remaining elements"); assert_null(popped, "No remaining elements");
stack_free(s); stack_free(s);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, push_stack, { munit_case(RUN, push_stack, {
@ -61,7 +61,7 @@ munit_case(RUN, push_stack, {
assert_int(s->count, ==, 3, "3 elements in the stack"); assert_int(s->count, ==, 3, "3 elements in the stack");
stack_free(s); stack_free(s);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, create_stack, { munit_case(RUN, create_stack, {
@ -71,7 +71,7 @@ munit_case(RUN, create_stack, {
assert_ptr_not_null(s->data, "Allocates the stack data"); assert_ptr_not_null(s->data, "Allocates the stack data");
stack_free(s); stack_free(s);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

13
src/8-4-stack-free/snekstack.c

@ -2,17 +2,18 @@
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "snekstack.h" #include "snekstack.h"
void stack_free(my_stack_t *stack) { void stack_free(my_stack_t *stack) {
if (stack == NULL) return; if (stack == NULL) return;
if (stack->data != NULL) { if (stack->data != NULL) {
free(stack->data); boot_free(stack->data);
stack->data = NULL; stack->data = NULL;
} }
free(stack); boot_free(stack);
} }
// don't touch below this line // don't touch below this line
@ -29,7 +30,7 @@ void *stack_pop(my_stack_t *stack) {
void stack_push(my_stack_t *stack, void *obj) { void stack_push(my_stack_t *stack, void *obj) {
if (stack->count == stack->capacity) { if (stack->count == stack->capacity) {
stack->capacity *= 2; stack->capacity *= 2;
void **temp = realloc(stack->data, stack->capacity * sizeof(void *)); void **temp = boot_realloc(stack->data, stack->capacity * sizeof(void *));
if (temp == NULL) { if (temp == NULL) {
stack->capacity /= 2; stack->capacity /= 2;
exit(1); exit(1);
@ -42,16 +43,16 @@ void stack_push(my_stack_t *stack, void *obj) {
} }
my_stack_t *stack_new(size_t capacity) { my_stack_t *stack_new(size_t capacity) {
my_stack_t *stack = malloc(sizeof(my_stack_t)); my_stack_t *stack = boot_malloc(sizeof(my_stack_t));
if (stack == NULL) { if (stack == NULL) {
return NULL; return NULL;
} }
stack->count = 0; stack->count = 0;
stack->capacity = capacity; stack->capacity = capacity;
stack->data = malloc(stack->capacity * sizeof(void *)); stack->data = boot_malloc(stack->capacity * sizeof(void *));
if (stack->data == NULL) { if (stack->data == NULL) {
free(stack); boot_free(stack);
return NULL; return NULL;
} }

13
src/9-3-float/main.c

@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -7,8 +8,8 @@ munit_case(RUN, test_positive, {
snek_object_t *obj = new_snek_float(42); snek_object_t *obj = new_snek_float(42);
assert_float(obj->data.v_float, ==, 42, "Must accept positive values"); assert_float(obj->data.v_float, ==, 42, "Must accept positive values");
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_zero, { munit_case(SUBMIT, test_zero, {
@ -17,8 +18,8 @@ munit_case(SUBMIT, test_zero, {
assert_float(obj->kind, ==, FLOAT, "Must set type to FLOAT"); assert_float(obj->kind, ==, FLOAT, "Must set type to FLOAT");
assert_float(obj->data.v_float, ==, 0.0, "Must accept 0.0"); assert_float(obj->data.v_float, ==, 0.0, "Must accept 0.0");
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_negative, { munit_case(SUBMIT, test_negative, {
@ -27,8 +28,8 @@ munit_case(SUBMIT, test_negative, {
assert_float(obj->kind, ==, FLOAT, "Must set type to FLOAT"); assert_float(obj->kind, ==, FLOAT, "Must set type to FLOAT");
assert_float(obj->data.v_float, ==, -5.0, "Must accept negative numbers"); assert_float(obj->data.v_float, ==, -5.0, "Must accept negative numbers");
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

5
src/9-3-float/snekobject.c

@ -1,9 +1,10 @@
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
snek_object_t *new_snek_float(float value) { snek_object_t *new_snek_float(float value) {
snek_object_t *obj = (snek_object_t *)malloc(sizeof(snek_object_t)); snek_object_t *obj = (snek_object_t *)boot_malloc(sizeof(snek_object_t));
if (obj == NULL) return NULL; if (obj == NULL) return NULL;
obj->kind = FLOAT; obj->kind = FLOAT;
obj->data.v_float = value; obj->data.v_float = value;
@ -13,7 +14,7 @@ snek_object_t *new_snek_float(float value) {
// don't touch below this line // don't touch below this line
snek_object_t *new_snek_integer(int value) { snek_object_t *new_snek_integer(int value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }

12
src/9-4-string/main.c

@ -1,5 +1,7 @@
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -21,12 +23,14 @@ munit_case(RUN, test_str_copied, {
); );
// Should allocate memory for the string with null terminator. // Should allocate memory for the string with null terminator.
// assert_int_equal(boot_alloc_size(), 22, "Must allocate memory for string"); bool is_32_bit = sizeof(void *) == 4;
int allocated = 14 + (is_32_bit ? 8 : 16); // pointers are larger on x64
assert_int_equal(boot_alloc_size(), allocated, "Must allocate memory for string");
// Free the string, and then free the object. // Free the string, and then free the object.
free(obj->data.v_string); boot_free(obj->data.v_string);
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

11
src/9-4-string/snekobject.c

@ -2,16 +2,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
snek_object_t *new_snek_string(char *value) { snek_object_t *new_snek_string(char *value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = (snek_object_t *)boot_malloc(sizeof(snek_object_t));
if (obj == NULL) return NULL; if (obj == NULL) return NULL;
size_t size = strlen(value); size_t size = strlen(value);
char *str = (char *)malloc(sizeof(char) * size + 1); char *str = (char *)boot_malloc(sizeof(char) * size + 1);
if (str == NULL) { if (str == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -24,7 +25,7 @@ snek_object_t *new_snek_string(char *value) {
// don't touch below this line // don't touch below this line
snek_object_t *new_snek_integer(int value) { snek_object_t *new_snek_integer(int value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -35,7 +36,7 @@ snek_object_t *new_snek_integer(int value) {
} }
snek_object_t *new_snek_float(float value) { snek_object_t *new_snek_float(float value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }

19
src/9-5-vector3/main.c

@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -8,7 +9,7 @@ munit_case(RUN, test_returns_null, {
assert_null(vec, "Should return null when input is null"); assert_null(vec, "Should return null when input is null");
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
@ -31,11 +32,11 @@ munit_case(RUN, test_vec_multiple_objects, {
assert_int(vec->data.v_vector3.z->data.v_int, ==, 3, "should have correct z"); assert_int(vec->data.v_vector3.z->data.v_int, ==, 3, "should have correct z");
// Free all of our objects. // Free all of our objects.
free(x); boot_free(x);
free(y); boot_free(y);
free(z); boot_free(z);
free(vec); boot_free(vec);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_vec_same_object, { munit_case(SUBMIT, test_vec_same_object, {
@ -62,9 +63,9 @@ munit_case(SUBMIT, test_vec_same_object, {
assert_int(vec->data.v_vector3.z->data.v_int, ==, 2, "should have correct z"); assert_int(vec->data.v_vector3.z->data.v_int, ==, 2, "should have correct z");
// Free all of our objects. // Free all of our objects.
free(i); boot_free(i);
free(vec); boot_free(vec);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

24
src/9-5-vector3/snekobject.c

@ -1,33 +1,25 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z) 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; if (x == NULL || y == NULL || z == NULL) return NULL;
snek_object_t *obj = (snek_object_t *)malloc(sizeof(snek_object_t)); snek_object_t *obj = (snek_object_t *)boot_malloc(sizeof(snek_object_t));
if (obj == NULL) return NULL; if (obj == NULL) return NULL;
snek_vector_t *vec = (snek_vector_t *)malloc(sizeof(snek_vector_t));
if (vec == NULL) {
free(obj);
return NULL;
}
vec->x = x;
vec->y = y;
vec->z = z;
obj->kind = VECTOR3; obj->kind = VECTOR3;
obj->data.v_vector3 = *vec; obj->data.v_vector3 = (snek_vector_t){ .x = x, .y = y, .z = z };
return obj; return obj;
} }
// don't touch below this line // don't touch below this line
snek_object_t *new_snek_integer(int value) { snek_object_t *new_snek_integer(int value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -38,7 +30,7 @@ snek_object_t *new_snek_integer(int value) {
} }
snek_object_t *new_snek_float(float value) { snek_object_t *new_snek_float(float value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -49,15 +41,15 @@ snek_object_t *new_snek_float(float value) {
} }
snek_object_t *new_snek_string(char *value) { snek_object_t *new_snek_string(char *value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

13
src/9-6-arrays/main.c

@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -9,9 +10,9 @@ munit_case(RUN, test_create_empty_array, {
assert_int(obj->kind, ==, ARRAY, "Must set type to ARRAY"); assert_int(obj->kind, ==, ARRAY, "Must set type to ARRAY");
assert_int(obj->data.v_array.size, ==, 2, "Must set size to 2"); assert_int(obj->data.v_array.size, ==, 2, "Must set size to 2");
free(obj->data.v_array.elements); boot_free(obj->data.v_array.elements);
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_used_calloc, { munit_case(SUBMIT, test_used_calloc, {
@ -20,9 +21,9 @@ munit_case(SUBMIT, test_used_calloc, {
assert_ptr_null(obj->data.v_array.elements[0], "Should use calloc"); assert_ptr_null(obj->data.v_array.elements[0], "Should use calloc");
assert_ptr_null(obj->data.v_array.elements[1], "Should use calloc"); assert_ptr_null(obj->data.v_array.elements[1], "Should use calloc");
free(obj->data.v_array.elements); boot_free(obj->data.v_array.elements);
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

19
src/9-6-arrays/snekobject.c

@ -1,15 +1,16 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
snek_object_t *new_snek_array(size_t size) { snek_object_t *new_snek_array(size_t size) {
snek_object_t *obj = (snek_object_t *)malloc(sizeof(snek_object_t)); snek_object_t *obj = (snek_object_t *)boot_malloc(sizeof(snek_object_t));
if (obj == NULL) return NULL; if (obj == NULL) return NULL;
snek_object_t **arr = (snek_object_t **)calloc(size, sizeof(snek_object_t *)); snek_object_t **arr = (snek_object_t **)boot_calloc(size, sizeof(snek_object_t *));
if (arr == NULL) { if (arr == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -27,7 +28,7 @@ snek_object_t *new_snek_vector3(
return NULL; return NULL;
} }
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -39,7 +40,7 @@ snek_object_t *new_snek_vector3(
} }
snek_object_t *new_snek_integer(int value) { snek_object_t *new_snek_integer(int value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -50,7 +51,7 @@ snek_object_t *new_snek_integer(int value) {
} }
snek_object_t *new_snek_float(float value) { snek_object_t *new_snek_float(float value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -61,15 +62,15 @@ snek_object_t *new_snek_float(float value) {
} }
snek_object_t *new_snek_string(char *value) { snek_object_t *new_snek_string(char *value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

33
src/9-7-get-and-set/main.c

@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -25,12 +26,12 @@ munit_case(RUN, test_array, {
assert_int(retrieved_second->kind, ==, INTEGER, "Should be a integer"); assert_int(retrieved_second->kind, ==, INTEGER, "Should be a integer");
assert_ptr(second, ==, retrieved_second, "Should be the same object"); assert_ptr(second, ==, retrieved_second, "Should be the same object");
free(first->data.v_string); boot_free(first->data.v_string);
free(first); boot_free(first);
free(second); boot_free(second);
free(obj->data.v_array.elements); boot_free(obj->data.v_array.elements);
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_set_outside_bounds, { munit_case(RUN, test_set_outside_bounds, {
@ -46,11 +47,11 @@ munit_case(RUN, test_set_outside_bounds, {
assert_false(snek_array_set(obj, 100, outside)); assert_false(snek_array_set(obj, 100, outside));
// Free memory // Free memory
free(outside->data.v_string); boot_free(outside->data.v_string);
free(outside); boot_free(outside);
free(obj->data.v_array.elements); boot_free(obj->data.v_array.elements);
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_get_outside_bounds, { munit_case(SUBMIT, test_get_outside_bounds, {
@ -62,11 +63,11 @@ munit_case(SUBMIT, test_get_outside_bounds, {
// Outside of bound // Outside of bound
assert_null(snek_array_get(obj, 1), "Should not access outside the array"); assert_null(snek_array_get(obj, 1), "Should not access outside the array");
free(first->data.v_string); boot_free(first->data.v_string);
free(first); boot_free(first);
free(obj->data.v_array.elements); boot_free(obj->data.v_array.elements);
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

19
src/9-7-get-and-set/snekobject.c

@ -2,6 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bootlib.h"
#include "snekobject.h" #include "snekobject.h"
bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value) { bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value) {
@ -24,14 +25,14 @@ snek_object_t *snek_array_get(snek_object_t *snek_obj, size_t index) {
// don't touch below this line // don't touch below this line
snek_object_t *new_snek_array(size_t size) { snek_object_t *new_snek_array(size_t size) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *));
if (elements == NULL) { if (elements == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -47,7 +48,7 @@ snek_object_t *new_snek_vector3(
return NULL; return NULL;
} }
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -59,7 +60,7 @@ snek_object_t *new_snek_vector3(
} }
snek_object_t *new_snek_integer(int value) { snek_object_t *new_snek_integer(int value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -70,7 +71,7 @@ snek_object_t *new_snek_integer(int value) {
} }
snek_object_t *new_snek_float(float value) { snek_object_t *new_snek_float(float value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -81,15 +82,15 @@ snek_object_t *new_snek_float(float value) {
} }
snek_object_t *new_snek_string(char *value) { snek_object_t *new_snek_string(char *value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

33
src/9-8-length/main.c

@ -1,3 +1,4 @@
#include "bootlib.h"
#include "munit.h" #include "munit.h"
#include "snekobject.h" #include "snekobject.h"
@ -5,16 +6,16 @@ munit_case(RUN, test_integer, {
snek_object_t *obj = new_snek_integer(42); snek_object_t *obj = new_snek_integer(42);
assert_int(snek_length(obj), ==, 1, "Integers are length 1"); assert_int(snek_length(obj), ==, 1, "Integers are length 1");
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_float, { munit_case(RUN, test_float, {
snek_object_t *obj = new_snek_float(3.14); snek_object_t *obj = new_snek_float(3.14);
assert_int(snek_length(obj), ==, 1, "Floats are length 1"); assert_int(snek_length(obj), ==, 1, "Floats are length 1");
free(obj); boot_free(obj);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(RUN, test_string, { munit_case(RUN, test_string, {
@ -26,11 +27,11 @@ munit_case(RUN, test_string, {
snek_length(longer), ==, strlen("hello, world"), "Should use strlen" snek_length(longer), ==, strlen("hello, world"), "Should use strlen"
); );
free(shorter->data.v_string); boot_free(shorter->data.v_string);
free(shorter); boot_free(shorter);
free(longer->data.v_string); boot_free(longer->data.v_string);
free(longer); boot_free(longer);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_vector3, { munit_case(SUBMIT, test_vector3, {
@ -38,9 +39,9 @@ munit_case(SUBMIT, test_vector3, {
snek_object_t *vec = new_snek_vector3(i, i, i); snek_object_t *vec = new_snek_vector3(i, i, i);
assert_int(snek_length(vec), ==, 3, "Vec3 always has length 3"); assert_int(snek_length(vec), ==, 3, "Vec3 always has length 3");
free(i); boot_free(i);
free(vec); boot_free(vec);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
munit_case(SUBMIT, test_array, { munit_case(SUBMIT, test_array, {
@ -52,10 +53,10 @@ munit_case(SUBMIT, test_array, {
assert_int(snek_length(arr), ==, 4, "Length of array should be its size"); assert_int(snek_length(arr), ==, 4, "Length of array should be its size");
free(i); boot_free(i);
free(arr->data.v_array.elements); boot_free(arr->data.v_array.elements);
free(arr); boot_free(arr);
// assert(boot_all_freed()); assert(boot_all_freed());
}); });
int main() { int main() {

19
src/9-8-length/snekobject.c

@ -1,3 +1,4 @@
#include "bootlib.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -15,14 +16,14 @@ int snek_length(snek_object_t *obj) {
// don't touch below this line // don't touch below this line
snek_object_t *new_snek_array(size_t size) { snek_object_t *new_snek_array(size_t size) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
snek_object_t **elements = calloc(size, sizeof(snek_object_t *)); snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *));
if (elements == NULL) { if (elements == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }
@ -73,7 +74,7 @@ snek_object_t *new_snek_vector3(
return NULL; return NULL;
} }
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -85,7 +86,7 @@ snek_object_t *new_snek_vector3(
} }
snek_object_t *new_snek_integer(int value) { snek_object_t *new_snek_integer(int value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -96,7 +97,7 @@ snek_object_t *new_snek_integer(int value) {
} }
snek_object_t *new_snek_float(float value) { snek_object_t *new_snek_float(float value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
@ -107,15 +108,15 @@ snek_object_t *new_snek_float(float value) {
} }
snek_object_t *new_snek_string(char *value) { snek_object_t *new_snek_string(char *value) {
snek_object_t *obj = malloc(sizeof(snek_object_t)); snek_object_t *obj = boot_malloc(sizeof(snek_object_t));
if (obj == NULL) { if (obj == NULL) {
return NULL; return NULL;
} }
int len = strlen(value); int len = strlen(value);
char *dst = malloc(len + 1); char *dst = boot_malloc(len + 1);
if (dst == NULL) { if (dst == NULL) {
free(obj); boot_free(obj);
return NULL; return NULL;
} }

127
src/bootlib.c

@ -0,0 +1,127 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "bootlib.h"
typedef struct Allocation {
void* ptr;
size_t size;
bool freed;
struct Allocation *next;
} Allocation;
static Allocation *allocations = NULL; // backwards linked list
static int allocated_count = 0;
static int reallocated_count = 0;
static int total_size = 0;
void *create_allocation(void *allocated, size_t size);
Allocation *find_allocation(void *ptr);
void *boot_malloc(size_t size)
{
void *allocated = malloc(size);
if (allocated == NULL) return NULL;
return create_allocation(allocated, size);
}
void *boot_calloc (size_t num, size_t size)
{
void *allocated = calloc(num, size);
if (allocated == NULL) return NULL;
return create_allocation(allocated, num * size);
}
void *boot_realloc(void* ptr, size_t size)
{
if (ptr == NULL) return NULL;
void *allocated = realloc(ptr, size);
if (allocated == NULL) return NULL;
reallocated_count++;
Allocation *allocation = find_allocation(ptr);
if (allocated == ptr) { // realloc resized the existing ptr's memory block
allocation->size = size;
total_size += size - allocation->size;
return allocated;
}
allocation->freed = true;
allocated_count--;
total_size -= allocation->size;
return create_allocation(allocated, size);
}
void boot_free (void *ptr)
{
if (ptr == NULL) return;
free(ptr);
Allocation *allocation = find_allocation(ptr);
allocation->freed = true;
allocated_count--;
total_size -= allocation->size;
}
// -----------------------------------------
bool boot_all_freed()
{
return allocated_count == 0;
}
bool boot_is_freed(void *ptr)
{
return find_allocation(ptr)->freed;
}
int boot_realloc_count()
{
return reallocated_count;
}
int boot_alloc_size()
{
return total_size;
}
// -----------------------------------------
void *create_allocation(void *allocated, size_t size)
{
if (allocated == NULL) return NULL;
total_size += size;
Allocation *allocation = (Allocation *)malloc(sizeof(Allocation));
if (allocation == NULL) return NULL;
allocation->ptr = allocated;
allocation->size = size;
allocation->freed = false;
allocation->next = allocations;
allocations = allocation;
allocated_count++;
return allocated;
}
Allocation *find_allocation(void *ptr)
{
if (allocations == NULL) return NULL;
Allocation *current = allocations;
while (true) {
if (current == NULL) return NULL;
if (current->ptr == ptr) return current;
current = current->next;
}
return NULL;
}

16
src/bootlib.h

@ -0,0 +1,16 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
void *boot_malloc(size_t size);
void *boot_calloc (size_t num, size_t size);
void *boot_realloc(void* ptr, size_t size);
void boot_free (void *ptr);
// -----------------------------------------
bool boot_all_freed();
bool boot_is_freed(void *ptr);
int boot_realloc_count();
int boot_alloc_size();

5
src/munit.h

@ -1,3 +1,5 @@
#pragma once
#define MUNIT_ENABLE_ASSERT_ALIASES #define MUNIT_ENABLE_ASSERT_ALIASES
#include "../vendor/munit/munit.h" #include "../vendor/munit/munit.h"
@ -40,6 +42,9 @@
#define assert_int(A, OP, B, MSG) \ #define assert_int(A, OP, B, MSG) \
munit_assert_int(A, OP, B, MSG); munit_assert_int(A, OP, B, MSG);
#define assert_int_equal(A, B, MSG) \
munit_assert_int(A, ==, B, MSG);
#undef assert_uint #undef assert_uint
#define assert_uint(A, OP, B, MSG) \ #define assert_uint(A, OP, B, MSG) \
munit_assert_uint(A, OP, B); munit_assert_uint(A, OP, B);

Loading…
Cancel
Save