From 780996458b70dcc32b9c1ce0efd4757046f1aa2d Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sat, 6 Sep 2025 23:31:03 +0200 Subject: [PATCH] Add the last exercises --- README.org | 442 +++++++++++------- src/{4-1-enums => 04-1-enums}/color.h | 0 src/{4-1-enums => 04-1-enums}/main.c | 0 .../color.h | 0 .../main.c | 0 .../http.c | 0 .../http.h | 0 .../main.c | 0 .../main.c | 0 src/{5-1-union => 05-1-union}/exercise.c | 0 src/{5-1-union => 05-1-union}/exercise.h | 0 src/{5-1-union => 05-1-union}/main.c | 0 .../main.c | 0 .../main.c | 0 .../exercise.h | 0 .../main.c | 0 .../exercise.h | 0 src/{6-1-the-stack => 06-1-the-stack}/main.c | 0 .../main.c | 0 .../main.c | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 src/{6-5-the-heap => 06-5-the-heap}/main.c | 0 src/{6-6-malloc => 06-6-malloc}/exercise.c | 0 src/{6-6-malloc => 06-6-malloc}/exercise.h | 0 src/{6-6-malloc => 06-6-malloc}/main.c | 0 src/{6-7-free => 06-7-free}/exercise.c | 0 src/{6-7-free => 06-7-free}/exercise.h | 0 src/{6-7-free => 06-7-free}/main.c | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../main.c | 0 .../snekstack.c | 0 .../snekstack.h | 0 .../main.c | 0 .../snekstack.c | 0 .../snekstack.h | 0 src/{8-3-stack-pop => 08-3-stack-pop}/main.c | 0 .../snekstack.c | 0 .../snekstack.h | 0 .../main.c | 0 .../snekstack.c | 0 .../snekstack.h | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../snekstack.c | 0 .../snekstack.h | 0 .../exercise.c | 0 .../exercise.h | 0 .../main.c | 0 .../snekstack.c | 0 .../snekstack.h | 0 .../main.c | 0 .../snekobject.h | 0 src/{9-2-integer => 09-2-integer}/main.c | 0 .../snekobject.c | 0 .../snekobject.h | 0 src/{9-3-float => 09-3-float}/main.c | 0 src/{9-3-float => 09-3-float}/snekobject.c | 0 src/{9-3-float => 09-3-float}/snekobject.h | 0 src/{9-4-string => 09-4-string}/main.c | 0 src/{9-4-string => 09-4-string}/snekobject.c | 0 src/{9-4-string => 09-4-string}/snekobject.h | 0 src/{9-5-vector3 => 09-5-vector3}/main.c | 0 .../snekobject.c | 0 .../snekobject.h | 0 src/{9-6-arrays => 09-6-arrays}/main.c | 0 src/{9-6-arrays => 09-6-arrays}/snekobject.c | 0 src/{9-6-arrays => 09-6-arrays}/snekobject.h | 0 .../main.c | 0 .../snekobject.c | 0 .../snekobject.h | 0 src/{9-8-length => 09-8-length}/main.c | 0 src/{9-8-length => 09-8-length}/snekobject.c | 0 src/{9-8-length => 09-8-length}/snekobject.h | 0 src/11-01-handling-cycles/main.c | 30 ++ src/11-01-handling-cycles/snekobject.c | 179 +++++++ src/11-01-handling-cycles/snekobject.h | 52 +++ src/11-02-pros-and-cons/main.c | 31 ++ src/11-02-pros-and-cons/snekobject.h | 38 ++ src/11-02-pros-and-cons/stack.c | 78 ++++ src/11-02-pros-and-cons/stack.h | 16 + src/11-02-pros-and-cons/vm.c | 19 + src/11-02-pros-and-cons/vm.h | 9 + src/11-03-stack-frames/main.c | 43 ++ src/11-03-stack-frames/snekobject.h | 40 ++ src/11-03-stack-frames/stack.c | 78 ++++ src/11-03-stack-frames/stack.h | 16 + src/11-03-stack-frames/vm.c | 49 ++ src/11-03-stack-frames/vm.h | 18 + src/11-04-tracking-objects/main.c | 46 ++ src/11-04-tracking-objects/sneknew.c | 98 ++++ src/11-04-tracking-objects/sneknew.h | 11 + src/11-04-tracking-objects/snekobject.h | 41 ++ src/11-04-tracking-objects/stack.c | 82 ++++ src/11-04-tracking-objects/stack.h | 17 + src/11-04-tracking-objects/vm.c | 50 ++ src/11-04-tracking-objects/vm.h | 21 + src/11-05-free/main.c | 35 ++ src/11-05-free/sneknew.c | 95 ++++ src/11-05-free/sneknew.h | 11 + src/11-05-free/snekobject.c | 21 + src/11-05-free/snekobject.h | 43 ++ src/11-05-free/stack.c | 81 ++++ src/11-05-free/stack.h | 17 + src/11-05-free/vm.c | 53 +++ src/11-05-free/vm.h | 21 + src/11-06-frame-references/main.c | 50 ++ src/11-06-frame-references/sneknew.c | 95 ++++ src/11-06-frame-references/sneknew.h | 11 + src/11-06-frame-references/snekobject.c | 21 + src/11-06-frame-references/snekobject.h | 43 ++ src/11-06-frame-references/stack.c | 81 ++++ src/11-06-frame-references/stack.h | 17 + src/11-06-frame-references/vm.c | 57 +++ src/11-06-frame-references/vm.h | 23 + src/11-07-mark-and-sweep/main.c | 39 ++ src/11-07-mark-and-sweep/sneknew.c | 96 ++++ src/11-07-mark-and-sweep/sneknew.h | 11 + src/11-07-mark-and-sweep/snekobject.c | 21 + src/11-07-mark-and-sweep/snekobject.h | 43 ++ src/11-07-mark-and-sweep/stack.c | 81 ++++ src/11-07-mark-and-sweep/stack.h | 17 + src/11-07-mark-and-sweep/vm.c | 57 +++ src/11-07-mark-and-sweep/vm.h | 23 + src/11-08-mark/main.c | 65 +++ src/11-08-mark/sneknew.c | 96 ++++ src/11-08-mark/sneknew.h | 11 + src/11-08-mark/snekobject.c | 21 + src/11-08-mark/snekobject.h | 43 ++ src/11-08-mark/stack.c | 81 ++++ src/11-08-mark/stack.h | 17 + src/11-08-mark/vm.c | 69 +++ src/11-08-mark/vm.h | 37 ++ src/11-09-trace/main.c | 131 ++++++ src/11-09-trace/sneknew.c | 96 ++++ src/11-09-trace/sneknew.h | 11 + src/11-09-trace/snekobject.c | 143 ++++++ src/11-09-trace/snekobject.h | 46 ++ src/11-09-trace/stack.c | 81 ++++ src/11-09-trace/stack.h | 17 + src/11-09-trace/vm.c | 123 +++++ src/11-09-trace/vm.h | 41 ++ src/11-10-sweep/main.c | 99 ++++ src/11-10-sweep/sneknew.c | 96 ++++ src/11-10-sweep/sneknew.h | 11 + src/11-10-sweep/snekobject.c | 143 ++++++ src/11-10-sweep/snekobject.h | 46 ++ src/11-10-sweep/stack.c | 81 ++++ src/11-10-sweep/stack.h | 17 + src/11-10-sweep/vm.c | 152 ++++++ src/11-10-sweep/vm.h | 47 ++ src/6-8-big-endian-little-endian/.DS_Store | Bin 6148 -> 0 bytes src/bootlib.c | 2 +- 173 files changed, 4329 insertions(+), 161 deletions(-) rename src/{4-1-enums => 04-1-enums}/color.h (100%) rename src/{4-1-enums => 04-1-enums}/main.c (100%) rename src/{4-2-non-default-values => 04-2-non-default-values}/color.h (100%) rename src/{4-2-non-default-values => 04-2-non-default-values}/main.c (100%) rename src/{4-3-switch-case => 04-3-switch-case}/http.c (100%) rename src/{4-3-switch-case => 04-3-switch-case}/http.h (100%) rename src/{4-3-switch-case => 04-3-switch-case}/main.c (100%) rename src/{4-4-sizeof-enum => 04-4-sizeof-enum}/main.c (100%) rename src/{5-1-union => 05-1-union}/exercise.c (100%) rename src/{5-1-union => 05-1-union}/exercise.h (100%) rename src/{5-1-union => 05-1-union}/main.c (100%) rename src/{5-2-memory-layout => 05-2-memory-layout}/main.c (100%) rename src/{5-3-5-4-union-size => 05-3-5-4-union-size}/main.c (100%) rename src/{5-5-helper-fields => 05-5-helper-fields}/exercise.h (100%) rename src/{5-5-helper-fields => 05-5-helper-fields}/main.c (100%) rename src/{6-1-the-stack => 06-1-the-stack}/exercise.h (100%) rename src/{6-1-the-stack => 06-1-the-stack}/main.c (100%) rename src/{6-2-why-a-stack => 06-2-why-a-stack}/main.c (100%) rename src/{6-3-stack-overflow => 06-3-stack-overflow}/main.c (100%) rename src/{6-4-pointers-to-the-stack => 06-4-pointers-to-the-stack}/main.c (100%) rename src/{6-5-the-heap => 06-5-the-heap}/exercise.c (100%) rename src/{6-5-the-heap => 06-5-the-heap}/exercise.h (100%) rename src/{6-5-the-heap => 06-5-the-heap}/main.c (100%) rename src/{6-6-malloc => 06-6-malloc}/exercise.c (100%) rename src/{6-6-malloc => 06-6-malloc}/exercise.h (100%) rename src/{6-6-malloc => 06-6-malloc}/main.c (100%) rename src/{6-7-free => 06-7-free}/exercise.c (100%) rename src/{6-7-free => 06-7-free}/exercise.h (100%) rename src/{6-7-free => 06-7-free}/main.c (100%) rename src/{6-8-big-endian-little-endian => 06-8-big-endian-little-endian}/main.c (100%) rename src/{7-1-pointer-pointers => 07-1-pointer-pointers}/exercise.c (100%) rename src/{7-1-pointer-pointers => 07-1-pointer-pointers}/exercise.h (100%) rename src/{7-1-pointer-pointers => 07-1-pointer-pointers}/main.c (100%) rename src/{7-2-array-of-pointers => 07-2-array-of-pointers}/exercise.c (100%) rename src/{7-2-array-of-pointers => 07-2-array-of-pointers}/exercise.h (100%) rename src/{7-2-array-of-pointers => 07-2-array-of-pointers}/main.c (100%) rename src/{7-3-void-pointers => 07-3-void-pointers}/exercise.c (100%) rename src/{7-3-void-pointers => 07-3-void-pointers}/exercise.h (100%) rename src/{7-3-void-pointers => 07-3-void-pointers}/main.c (100%) rename src/{7-4-swapping-integers => 07-4-swapping-integers}/exercise.c (100%) rename src/{7-4-swapping-integers => 07-4-swapping-integers}/exercise.h (100%) rename src/{7-4-swapping-integers => 07-4-swapping-integers}/main.c (100%) rename src/{7-5-swapping-strings => 07-5-swapping-strings}/exercise.c (100%) rename src/{7-5-swapping-strings => 07-5-swapping-strings}/exercise.h (100%) rename src/{7-5-swapping-strings => 07-5-swapping-strings}/main.c (100%) rename src/{7-6-generic-swap => 07-6-generic-swap}/exercise.c (100%) rename src/{7-6-generic-swap => 07-6-generic-swap}/exercise.h (100%) rename src/{7-6-generic-swap => 07-6-generic-swap}/main.c (100%) rename src/{8-1-low-level-stack => 08-1-low-level-stack}/main.c (100%) rename src/{8-1-low-level-stack => 08-1-low-level-stack}/snekstack.c (100%) rename src/{8-1-low-level-stack => 08-1-low-level-stack}/snekstack.h (100%) rename src/{8-2-stack-push => 08-2-stack-push}/main.c (100%) rename src/{8-2-stack-push => 08-2-stack-push}/snekstack.c (100%) rename src/{8-2-stack-push => 08-2-stack-push}/snekstack.h (100%) rename src/{8-3-stack-pop => 08-3-stack-pop}/main.c (100%) rename src/{8-3-stack-pop => 08-3-stack-pop}/snekstack.c (100%) rename src/{8-3-stack-pop => 08-3-stack-pop}/snekstack.h (100%) rename src/{8-4-stack-free => 08-4-stack-free}/main.c (100%) rename src/{8-4-stack-free => 08-4-stack-free}/snekstack.c (100%) rename src/{8-4-stack-free => 08-4-stack-free}/snekstack.h (100%) rename src/{8-5-dangerous-push => 08-5-dangerous-push}/exercise.c (100%) rename src/{8-5-dangerous-push => 08-5-dangerous-push}/exercise.h (100%) rename src/{8-5-dangerous-push => 08-5-dangerous-push}/main.c (100%) rename src/{8-5-dangerous-push => 08-5-dangerous-push}/snekstack.c (100%) rename src/{8-5-dangerous-push => 08-5-dangerous-push}/snekstack.h (100%) rename src/{8-6-multiple-types => 08-6-multiple-types}/exercise.c (100%) rename src/{8-6-multiple-types => 08-6-multiple-types}/exercise.h (100%) rename src/{8-6-multiple-types => 08-6-multiple-types}/main.c (100%) rename src/{8-6-multiple-types => 08-6-multiple-types}/snekstack.c (100%) rename src/{8-6-multiple-types => 08-6-multiple-types}/snekstack.h (100%) rename src/{9-1-snek-objects => 09-1-snek-objects}/main.c (100%) rename src/{9-1-snek-objects => 09-1-snek-objects}/snekobject.h (100%) rename src/{9-2-integer => 09-2-integer}/main.c (100%) rename src/{9-2-integer => 09-2-integer}/snekobject.c (100%) rename src/{9-2-integer => 09-2-integer}/snekobject.h (100%) rename src/{9-3-float => 09-3-float}/main.c (100%) rename src/{9-3-float => 09-3-float}/snekobject.c (100%) rename src/{9-3-float => 09-3-float}/snekobject.h (100%) rename src/{9-4-string => 09-4-string}/main.c (100%) rename src/{9-4-string => 09-4-string}/snekobject.c (100%) rename src/{9-4-string => 09-4-string}/snekobject.h (100%) rename src/{9-5-vector3 => 09-5-vector3}/main.c (100%) rename src/{9-5-vector3 => 09-5-vector3}/snekobject.c (100%) rename src/{9-5-vector3 => 09-5-vector3}/snekobject.h (100%) rename src/{9-6-arrays => 09-6-arrays}/main.c (100%) rename src/{9-6-arrays => 09-6-arrays}/snekobject.c (100%) rename src/{9-6-arrays => 09-6-arrays}/snekobject.h (100%) rename src/{9-7-get-and-set => 09-7-get-and-set}/main.c (100%) rename src/{9-7-get-and-set => 09-7-get-and-set}/snekobject.c (100%) rename src/{9-7-get-and-set => 09-7-get-and-set}/snekobject.h (100%) rename src/{9-8-length => 09-8-length}/main.c (100%) rename src/{9-8-length => 09-8-length}/snekobject.c (100%) rename src/{9-8-length => 09-8-length}/snekobject.h (100%) create mode 100644 src/11-01-handling-cycles/main.c create mode 100644 src/11-01-handling-cycles/snekobject.c create mode 100644 src/11-01-handling-cycles/snekobject.h create mode 100644 src/11-02-pros-and-cons/main.c create mode 100644 src/11-02-pros-and-cons/snekobject.h create mode 100644 src/11-02-pros-and-cons/stack.c create mode 100644 src/11-02-pros-and-cons/stack.h create mode 100644 src/11-02-pros-and-cons/vm.c create mode 100644 src/11-02-pros-and-cons/vm.h create mode 100644 src/11-03-stack-frames/main.c create mode 100644 src/11-03-stack-frames/snekobject.h create mode 100644 src/11-03-stack-frames/stack.c create mode 100644 src/11-03-stack-frames/stack.h create mode 100644 src/11-03-stack-frames/vm.c create mode 100644 src/11-03-stack-frames/vm.h create mode 100644 src/11-04-tracking-objects/main.c create mode 100644 src/11-04-tracking-objects/sneknew.c create mode 100644 src/11-04-tracking-objects/sneknew.h create mode 100644 src/11-04-tracking-objects/snekobject.h create mode 100644 src/11-04-tracking-objects/stack.c create mode 100644 src/11-04-tracking-objects/stack.h create mode 100644 src/11-04-tracking-objects/vm.c create mode 100644 src/11-04-tracking-objects/vm.h create mode 100644 src/11-05-free/main.c create mode 100644 src/11-05-free/sneknew.c create mode 100644 src/11-05-free/sneknew.h create mode 100644 src/11-05-free/snekobject.c create mode 100644 src/11-05-free/snekobject.h create mode 100644 src/11-05-free/stack.c create mode 100644 src/11-05-free/stack.h create mode 100644 src/11-05-free/vm.c create mode 100644 src/11-05-free/vm.h create mode 100644 src/11-06-frame-references/main.c create mode 100644 src/11-06-frame-references/sneknew.c create mode 100644 src/11-06-frame-references/sneknew.h create mode 100644 src/11-06-frame-references/snekobject.c create mode 100644 src/11-06-frame-references/snekobject.h create mode 100644 src/11-06-frame-references/stack.c create mode 100644 src/11-06-frame-references/stack.h create mode 100644 src/11-06-frame-references/vm.c create mode 100644 src/11-06-frame-references/vm.h create mode 100644 src/11-07-mark-and-sweep/main.c create mode 100644 src/11-07-mark-and-sweep/sneknew.c create mode 100644 src/11-07-mark-and-sweep/sneknew.h create mode 100644 src/11-07-mark-and-sweep/snekobject.c create mode 100644 src/11-07-mark-and-sweep/snekobject.h create mode 100644 src/11-07-mark-and-sweep/stack.c create mode 100644 src/11-07-mark-and-sweep/stack.h create mode 100644 src/11-07-mark-and-sweep/vm.c create mode 100644 src/11-07-mark-and-sweep/vm.h create mode 100644 src/11-08-mark/main.c create mode 100644 src/11-08-mark/sneknew.c create mode 100644 src/11-08-mark/sneknew.h create mode 100644 src/11-08-mark/snekobject.c create mode 100644 src/11-08-mark/snekobject.h create mode 100644 src/11-08-mark/stack.c create mode 100644 src/11-08-mark/stack.h create mode 100644 src/11-08-mark/vm.c create mode 100644 src/11-08-mark/vm.h create mode 100644 src/11-09-trace/main.c create mode 100644 src/11-09-trace/sneknew.c create mode 100644 src/11-09-trace/sneknew.h create mode 100644 src/11-09-trace/snekobject.c create mode 100644 src/11-09-trace/snekobject.h create mode 100644 src/11-09-trace/stack.c create mode 100644 src/11-09-trace/stack.h create mode 100644 src/11-09-trace/vm.c create mode 100644 src/11-09-trace/vm.h create mode 100644 src/11-10-sweep/main.c create mode 100644 src/11-10-sweep/sneknew.c create mode 100644 src/11-10-sweep/sneknew.h create mode 100644 src/11-10-sweep/snekobject.c create mode 100644 src/11-10-sweep/snekobject.h create mode 100644 src/11-10-sweep/stack.c create mode 100644 src/11-10-sweep/stack.h create mode 100644 src/11-10-sweep/vm.c create mode 100644 src/11-10-sweep/vm.h delete mode 100644 src/6-8-big-endian-little-endian/.DS_Store diff --git a/README.org b/README.org index dd333a1..825c015 100644 --- a/README.org +++ b/README.org @@ -6,25 +6,25 @@ 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. +The exercises from the course have been worked out in this repository. +This is my way of obtaining the "certificate" for free. The first the 3 chapters of the course can be done interactively on the website. [[./bootdev-c.png]] If you're reading this with the intention of completing the exercises yourself, you can find each exercise is in its own directory inside of the =src= directory. -Remove all the files that are not called =main.c= and recreate them from the -exercises on the boot.dev website as you go through them, so you won't get spoiled. +Remove all the files that each exercise mentions you should modify, as you go +through them. Then recreate the files from the exercise, so you won't get spoiled. -Note: +Notes: - 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 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 a =std= naming +- In chapter 8 and 11, I renamed ~stack_t~ to ~my_stack_t~ due to a =std= naming conflict on macOS ** Build instructions @@ -44,72 +44,89 @@ Successful output of all the tests: #+BEGIN_SRC sh $ make run 12:25PM -[ 3%] Built target munit -[ 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 ] +[ 2%] Built target munit +[ 5%] Built target 11-10-sweep +[ 6%] Built target 04-1-enums +[ 7%] Built target 04-2-non-default-values +[ 9%] Built target 04-3-switch-case +[ 10%] Built target 04-4-sizeof-enum +[ 12%] Built target 05-1-union +[ 13%] Built target 05-2-memory-layout +[ 15%] Built target 05-3-5-4-union-size +[ 16%] Built target 05-5-helper-fields +[ 17%] Built target 06-1-the-stack +[ 19%] Built target 06-2-why-a-stack +[ 20%] Built target 06-3-stack-overflow +[ 21%] Built target 06-4-pointers-to-the-stack +[ 23%] Built target 06-5-the-heap +[ 25%] Built target 06-6-malloc +[ 27%] Built target 06-7-free +[ 28%] Built target 06-8-big-endian-little-endian +[ 30%] Built target 07-1-pointer-pointers +[ 32%] Built target 07-2-array-of-pointers +[ 33%] Built target 07-3-void-pointers +[ 35%] Built target 07-4-swapping-integers +[ 37%] Built target 07-5-swapping-strings +[ 39%] Built target 07-6-generic-swap +[ 41%] Built target 08-1-low-level-stack +[ 42%] Built target 08-2-stack-push +[ 44%] Built target 08-3-stack-pop +[ 46%] Built target 08-4-stack-free +[ 48%] Built target 08-5-dangerous-push +[ 50%] Built target 08-6-multiple-types +[ 52%] Built target 09-1-snek-objects +[ 53%] Built target 09-2-integer +[ 55%] Built target 09-3-float +[ 57%] Built target 09-4-string +[ 59%] Built target 09-5-vector3 +[ 61%] Built target 09-6-arrays +[ 62%] Built target 09-7-get-and-set +[ 64%] Built target 09-8-length +[ 65%] Built target 10-1-garbage-collector +[ 67%] Built target 10-2-refcounting +[ 69%] Built target 10-3-increment +[ 71%] Built target 10-4-decrement-and-free +[ 73%] Built target 10-5-vectors +[ 74%] Built target 10-6-arrays +[ 76%] Built target 10-7-refcounting-review +[ 77%] Built target 11-01-handling-cycles +[ 80%] Built target 11-02-pros-and-cons +[ 82%] Built target 11-03-stack-frames +[ 85%] Built target 11-04-tracking-objects +[ 88%] Built target 11-05-free +[ 91%] Built target 11-06-frame-references +[ 94%] Built target 11-07-mark-and-sweep +[ 97%] Built target 11-08-mark +[100%] Built target 11-09-trace +Running test suite with seed 0x3d418efb... +colors/are_defined [ OK ] [ 0.00000100 / 0.00000100 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 0x9c11f043... -colors/defined [ OK ] [ 0.00000000 / 0.00000000 CPU ] -colors/defined_vscode [ OK ] [ 0.00000000 / 0.00000000 CPU ] +Running test suite with seed 0x1447c343... +colors/defined [ OK ] [ 0.00000000 / 0.00000100 CPU ] +colors/defined_vscode [ OK ] [ 0.00000100 / 0.00000200 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xdac57c79... +Running test suite with seed 0xba550ddb... http/switch_enum [ OK ] [ 0.00000100 / 0.00000100 CPU ] -http/switch_enum_default [ OK ] [ 0.00000100 / 0.00000100 CPU ] +http/switch_enum_default [ OK ] [ 0.00000100 / 0.00000200 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 0x85251cb1... -format/integer [ OK ] [ 0.00000100 / 0.00000100 CPU ] +Running test suite with seed 0xba8a2b8d... +format/integer [ OK ] [ 0.00000200 / 0.00000300 CPU ] format/string [ OK ] [ 0.00000100 / 0.00000100 CPU ] -format/integer_nvim [ OK ] [ 0.00000100 / 0.00000200 CPU ] -format/string_nvim [ OK ] [ 0.00000100 / 0.00000100 CPU ] +format/integer_nvim [ OK ] [ 0.00000200 / 0.00000200 CPU ] +format/string_nvim [ OK ] [ 0.00000100 / 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 0xb2bd8586... -PacketHeader/test_packet_header_size [ OK ] [ 0.00000100 / 0.00000000 CPU ] +Running test suite with seed 0xa0f30fc8... +PacketHeader/test_packet_header_size [ OK ] [ 0.00000100 / 0.00000100 CPU ] PacketHeader/test_tcp_header_fields [ 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 ] +PacketHeader/test_field_raw_size [ OK ] [ 0.00000100 / 0.00000000 CPU ] +PacketHeader/test_field_to_raw_consistency[ OK ] [ 0.00000100 / 0.00000000 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. --------------------------------- Stack pointer offset: 0 bytes @@ -126,141 +143,246 @@ Stack pointer offset: 0 bytes --------------------------------- dark roast. -Running test suite with seed 0x5ed24c16... -/example/compare [ OK ] [ 0.00000100 / 0.00000200 CPU ] -/example/rand [ OK ] [ 0.00000200 / 0.00000100 CPU ] +Running test suite with seed 0xbd38a5fa... +/example/compare [ OK ] [ 0.00000600 / 0.00000500 CPU ] +/example/rand [ OK ] [ 0.00000300 / 0.00000300 CPU ] /example/parameters - foo=one, bar=red [ OK ] [ 0.00000200 / 0.00000200 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.00000200 / 0.00000100 CPU ] + foo=one, bar=red [ OK ] [ 0.00000300 / 0.00000300 CPU ] + foo=one, bar=green [ OK ] [ 0.00000300 / 0.00000300 CPU ] + foo=one, bar=blue [ OK ] [ 0.00000200 / 0.00000300 CPU ] + foo=two, bar=red [ OK ] [ 0.00000300 / 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.00000200 / 0.00000200 CPU ] - foo=three, bar=green [ OK ] [ 0.00000200 / 0.00000200 CPU ] - foo=three, bar=blue [ OK ] [ 0.00000100 / 0.00000000 CPU ] + foo=three, bar=red [ OK ] [ 0.00000100 / 0.00000200 CPU ] + foo=three, bar=green [ OK ] [ 0.00000300 / 0.00000200 CPU ] + foo=three, bar=blue [ OK ] [ 0.00000200 / 0.00000300 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 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 ] +Running test suite with seed 0x4e7eb250... +get_full_greeting/test_basic_greeting[ OK ] [ 0.00000500 / 0.00000500 CPU ] +get_full_greeting/test_short_buffer [ OK ] [ 0.00000400 / 0.00000400 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -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.00000200 CPU ] -allocate_scalar_array/test_allocate_too_much[ OK ] [ 0.00001000 / 0.00000900 CPU ] +Running test suite with seed 0x98ffe6c3... +allocate_scalar_array/test_allocate_scalar_array_size[ OK ] [ 0.00000600 / 0.00000600 CPU ] +allocate_scalar_array/test_allocate_scalar_array_values[ OK ] [ 0.00000300 / 0.00000400 CPU ] +allocate_scalar_array/test_allocate_scalar_array_zero_multiplier[ OK ] [ 0.00000500 / 0.00000500 CPU ] +allocate_scalar_array/test_allocate_too_much[ OK ] [ 0.00001800 / 0.00001800 CPU ] 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 ] +Running test suite with seed 0xd142494f... +/example/compare [ OK ] [ 0.00000600 / 0.00000500 CPU ] +/example/rand [ OK ] [ 0.00000500 / 0.00000400 CPU ] /example/parameters - 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 ] + foo=one, bar=red [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=one, bar=green [ OK ] [ 0.00000600 / 0.00000700 CPU ] + foo=one, bar=blue [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=two, bar=red [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=two, bar=green [ OK ] [ 0.00000400 / 0.00000300 CPU ] + foo=two, bar=blue [ OK ] [ 0.00000500 / 0.00000600 CPU ] + foo=three, bar=red [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=three, bar=green [ OK ] [ 0.00000600 / 0.00000500 CPU ] + foo=three, bar=blue [ OK ] [ 0.00001100 / 0.00001100 CPU ] 11 of 11 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xbb38daad... -allocate_list/create [ OK ] [ 0.00000100 / 0.00000200 CPU ] -allocate_list/overwrite [ OK ] [ 0.00000100 / 0.00000000 CPU ] +Running test suite with seed 0x3b94faee... +allocate_list/create [ OK ] [ 0.00000500 / 0.00000600 CPU ] +allocate_list/overwrite [ OK ] [ 0.00000300 / 0.00000300 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 ] +Running test suite with seed 0x30623318... +create_token_pointer_array/test_create_token_pointer_array_single[ OK ] [ 0.00000600 / 0.00000500 CPU ] +create_token_pointer_array/test_create_token_pointer_array_multiple[ OK ] [ 0.00000600 / 0.00000700 CPU ] +create_token_pointer_array/test_create_token_pointer_array_memory_allocation[ OK ] [ 0.00000600 / 0.00000600 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -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.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 ] +Running test suite with seed 0xdb4dc3bb... +snek_zero_out/test_zero_out_integer [ OK ] [ 0.00000500 / 0.00000500 CPU ] +snek_zero_out/test_zero_out_float [ OK ] [ 0.00000300 / 0.00000300 CPU ] +snek_zero_out/test_zero_out_bool [ OK ] [ 0.00000300 / 0.00000300 CPU ] +snek_zero_out/test_zero_out_nonzero_values[ OK ] [ 0.00000400 / 0.00000400 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. -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 ] +Running test suite with seed 0x1e23c270... +void-pointer/swap_ints [ OK ] [ 0.00000500 / 0.00000400 CPU ] +void-pointer/swap_ints_same [ OK ] [ 0.00000300 / 0.00000300 CPU ] 2 of 2 (100%) tests successful, 0 (0%) test skipped. -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 ] +Running test suite with seed 0x343e18c2... +void-pointer/swap_str [ OK ] [ 0.00000400 / 0.00000500 CPU ] +void-pointer/test_swap_str_long [ OK ] [ 0.00000400 / 0.00000400 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 ] +Running test suite with seed 0x349b3cc2... +swap/generic_ints [ OK ] [ 0.00000500 / 0.00000400 CPU ] +swap/generic_strings [ OK ] [ 0.00000500 / 0.00000500 CPU ] +swap/generic_struct [ OK ] [ 0.00000600 / 0.00000600 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -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 ] +Running test suite with seed 0xcc7f57e3... +snekstack/create_stack_small [ OK ] [ 0.00000600 / 0.00000600 CPU ] +snekstack/create_stack_large [ OK ] [ 0.00000500 / 0.00000500 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.00000300 / 0.00000300 CPU ] +Running test suite with seed 0xb54c2f05... +snekstack/create_stack [ OK ] [ 0.00000500 / 0.00000500 CPU ] +snekstack/push_stack [ OK ] [ 0.00000700 / 0.00000600 CPU ] +snekstack/push_double_capacity [ OK ] [ 0.00000900 / 0.00000900 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0x1df97623... -snekstack/create_stack [ OK ] [ 0.00000200 / 0.00000200 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 ] +Running test suite with seed 0x986e20e8... +snekstack/create_stack [ OK ] [ 0.00000500 / 0.00000500 CPU ] +snekstack/push_stack [ OK ] [ 0.00000600 / 0.00000600 CPU ] +snekstack/pop_stack [ OK ] [ 0.00000600 / 0.00000700 CPU ] +snekstack/pop_stack_empty [ OK ] [ 0.00000600 / 0.00000600 CPU ] 4 of 4 (100%) tests successful, 0 (0%) test skipped. -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.00000100 / 0.00000200 CPU ] +Running test suite with seed 0xf45ae006... +snekstack/create_stack [ OK ] [ 0.00000700 / 0.00000600 CPU ] +snekstack/push_stack [ OK ] [ 0.00000600 / 0.00000500 CPU ] +snekstack/pop_stack [ OK ] [ 0.00000700 / 0.00000700 CPU ] 3 of 3 (100%) tests successful, 0 (0%) test skipped. -Running test suite with seed 0xb607698d... -snekstack/heterogenous_stack [ OK ] [ 0.00000200 / 0.00000100 CPU ] +Running test suite with seed 0xf83a103b... +snekstack/heterogenous_stack [ OK ] [ 0.00000600 / 0.00000500 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 ] +Running test suite with seed 0x53a94d13... +snekstack/multiple_types_stack [ OK ] [ 0.00000800 / 0.00000800 CPU ] 1 of 1 (100%) tests successful, 0 (0%) test skipped. -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 ] +Running test suite with seed 0xd977a296... +object-integer-def/integer_constant [ OK ] [ 0.00000400 / 0.00000400 CPU ] +object-integer-def/integer_obj [ OK ] [ 0.00000500 / 0.00000500 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 ] +Running test suite with seed 0x9850a5b5... +object-integer/positive [ OK ] [ 0.00000600 / 0.00000500 CPU ] +object-integer/zero [ OK ] [ 0.00000400 / 0.00000300 CPU ] +object-integer/negative [ OK ] [ 0.00000400 / 0.00000300 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 ] +Running test suite with seed 0x06edb0c3... +object-float/positive [ OK ] [ 0.00000600 / 0.00000400 CPU ] +object-float/zero [ OK ] [ 0.00002200 / 0.00001700 CPU ] +object-float/negative [ OK ] [ 0.00000300 / 0.00000300 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 ] +Running test suite with seed 0x846fed7d... +object-string/copies_value [ OK ] [ 0.00000600 / 0.00000500 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 ] +Running test suite with seed 0x53cf4c00... +object-vector/returns_null [ OK ] [ 0.00000600 / 0.00000600 CPU ] +object-vector/multiple_objects [ OK ] [ 0.00000700 / 0.00000700 CPU ] +object-vector/same_object [ OK ] [ 0.00000500 / 0.00000500 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 ] +Running test suite with seed 0x58fd8016... +object-array/empty [ OK ] [ 0.00000500 / 0.00000500 CPU ] +object-array/calloc [ OK ] [ 0.00000300 / 0.00000400 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 ] +Running test suite with seed 0x7f5412f2... +object-array/set_and_get [ OK ] [ 0.00000500 / 0.00000500 CPU ] +object-array/set_outside [ OK ] [ 0.00000600 / 0.00000600 CPU ] +object-array/get_outside [ OK ] [ 0.00000500 / 0.00000400 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 ] +Running test suite with seed 0xa5a5383d... +object-length/integer [ OK ] [ 0.00000600 / 0.00000600 CPU ] +object-length/float [ OK ] [ 0.00000600 / 0.00000600 CPU ] +object-length/string [ OK ] [ 0.00000600 / 0.00000600 CPU ] +object-length/vector [ OK ] [ 0.00000400 / 0.00000300 CPU ] +object-length/array [ OK ] [ 0.00000500 / 0.00000500 CPU ] 5 of 5 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x2e6f75bf... +/example/compare [ OK ] [ 0.00000600 / 0.00000400 CPU ] +/example/rand [ OK ] [ 0.00000500 / 0.00000500 CPU ] +/example/parameters + foo=one, bar=red [ OK ] [ 0.00000500 / 0.00000500 CPU ] + foo=one, bar=green [ OK ] [ 0.00000500 / 0.00000600 CPU ] + foo=one, bar=blue [ OK ] [ 0.00000900 / 0.00000800 CPU ] + foo=two, bar=red [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=two, bar=green [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=two, bar=blue [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=three, bar=red [ OK ] [ 0.00000300 / 0.00000300 CPU ] + foo=three, bar=green [ OK ] [ 0.00000300 / 0.00000400 CPU ] + foo=three, bar=blue [ OK ] [ 0.00000400 / 0.00000300 CPU ] +11 of 11 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xdf462866... +refcounttest_int_has_refcount [ OK ] [ 0.00000500 / 0.00000400 CPU ] +refcounttest_float_has_refcount [ OK ] [ 0.00000500 / 0.00000400 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x3b906f5c... +refcount/test_inc_refcount [ OK ] [ 0.00000600 / 0.00000600 CPU ] +refcount/test_inc_refcount_more [ OK ] [ 0.00000400 / 0.00000400 CPU ] +refcount/test_null_obj [ OK ] [ 0.00000500 / 0.00000500 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x6b51ea68... +refcount/has_refcount [ OK ] [ 0.00000600 / 0.00000600 CPU ] +refcount/inc_refcount [ OK ] [ 0.00000400 / 0.00000300 CPU ] +refcount/dec_refcount [ OK ] [ 0.00000500 / 0.00000500 CPU ] +refcount/free_refcount [ OK ] [ 0.00000400 / 0.00000500 CPU ] +refcount/string_freed [ OK ] [ 0.00000500 / 0.00000600 CPU ] +5 of 5 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xfcdf2928... +refcount/has_refcount [ OK ] [ 0.00000600 / 0.00000500 CPU ] +refcount/inc_refcount [ OK ] [ 0.00000500 / 0.00000500 CPU ] +refcount/dec_refcount [ OK ] [ 0.00000500 / 0.00000500 CPU ] +refcount/free_refcount [ OK ] [ 0.00000300 / 0.00000300 CPU ] +refcount/string_freed [ OK ] [ 0.00000300 / 0.00000300 CPU ] +refcount/vector3 [ OK ] [ 0.00000400 / 0.00000400 CPU ] +refcount/vector3-same [ OK ] [ 0.00000300 / 0.00000200 CPU ] +7 of 7 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x2df34e21... +refcount/array_set [ OK ] [ 0.00000800 / 0.00000800 CPU ] +refcount/array_free [ OK ] [ 0.00000600 / 0.00000700 CPU ] +refcount/has_refcount [ OK ] [ 0.00000300 / 0.00000300 CPU ] +refcount/inc_refcount [ OK ] [ 0.00000400 / 0.00000400 CPU ] +refcount/dec_refcount [ OK ] [ 0.00000400 / 0.00000300 CPU ] +refcount/free_refcount [ OK ] [ 0.00000500 / 0.00000500 CPU ] +refcount/string_freed [ OK ] [ 0.00000500 / 0.00000500 CPU ] +refcount/vector3 [ OK ] [ 0.00000400 / 0.00000400 CPU ] +8 of 8 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x91892c1e... +/example/compare [ OK ] [ 0.00000500 / 0.00000500 CPU ] +/example/rand [ OK ] [ 0.00000600 / 0.00000500 CPU ] +/example/parameters + foo=one, bar=red [ OK ] [ 0.00000500 / 0.00000400 CPU ] + foo=one, bar=green [ OK ] [ 0.00000500 / 0.00000500 CPU ] + foo=one, bar=blue [ OK ] [ 0.00001100 / 0.00001200 CPU ] + foo=two, bar=red [ OK ] [ 0.00000500 / 0.00000600 CPU ] + foo=two, bar=green [ OK ] [ 0.00000600 / 0.00000600 CPU ] + foo=two, bar=blue [ OK ] [ 0.00000300 / 0.00000400 CPU ] + foo=three, bar=red [ OK ] [ 0.00000300 / 0.00000400 CPU ] + foo=three, bar=green [ OK ] [ 0.00000400 / 0.00000400 CPU ] + foo=three, bar=blue [ OK ] [ 0.00000400 / 0.00000300 CPU ] +11 of 11 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x0dddaf2c... +refcount/correctly_free [ OK ] [ 0.00000700 / 0.00000700 CPU ] +1 of 1 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x435b0c66... +mark-and-sweep/vm [ OK ] [ 0.00000600 / 0.00000700 CPU ] +mark-and-sweep/vm [ OK ] [ 0.00001400 / 0.00000700 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x85af19f7... +mark-and-sweep/test_vm_new [ OK ] [ 0.00000800 / 0.00000700 CPU ] +mark-and-sweep/test_vm_new_frame [ OK ] [ 0.00000700 / 0.00000600 CPU ] +mark-and-sweep/test_frames_are_freed [ OK ] [ 0.00000600 / 0.00000600 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x1cceaa41... +mark-and-sweep/test_vm_new [ OK ] [ 0.00000800 / 0.00000700 CPU ] +mark-and-sweep/test_frames_are_freed [ OK ] [ 0.00000700 / 0.00000700 CPU ] +mark-and-sweep/test_new_object [ OK ] [ 0.00000700 / 0.00000600 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xc169d14b... +mark-and-sweep/test_reference_object [ OK ] [ 0.00000700 / 0.00000700 CPU ] +mark-and-sweep/test_frames_are_freed [ OK ] [ 0.00000700 / 0.00000700 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xac37539a... +mark-and-sweep/test_one_ref [ OK ] [ 0.00000900 / 0.00000900 CPU ] +mark-and-sweep/test_multi_ref [ OK ] [ 0.00000800 / 0.00000800 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0xaa6df145... +mark-and-sweep/test_field_exists [ OK ] [ 0.00000900 / 0.00000800 CPU ] +mark-and-sweep/test_marked_is_false [ OK ] [ 0.00000600 / 0.00000500 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x22d87982... +mark-and-sweep/test_single_frame [ OK ] [ 0.00000900 / 0.00000900 CPU ] +mark-and-sweep/test_multi_frame [ OK ] [ 0.00001000 / 0.00001100 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x147d09b4... +mark-and-sweep/test_trace_vector [ OK ] [ 0.00000900 / 0.00000900 CPU ] +mark-and-sweep/test_trace_array [ OK ] [ 0.00001200 / 0.00001300 CPU ] +mark-and-sweep/test_trace_nested [ OK ] [ 0.00001700 / 0.00001700 CPU ] +3 of 3 (100%) tests successful, 0 (0%) test skipped. +Running test suite with seed 0x93c96b1a... +mark-and-sweep/test_simple [ OK ] [ 0.00000900 / 0.00000800 CPU ] +mark-and-sweep/test_full [ OK ] [ 0.00001900 / 0.00002000 CPU ] +2 of 2 (100%) tests successful, 0 (0%) test skipped. [100%] Built target run #+END_SRC diff --git a/src/4-1-enums/color.h b/src/04-1-enums/color.h similarity index 100% rename from src/4-1-enums/color.h rename to src/04-1-enums/color.h diff --git a/src/4-1-enums/main.c b/src/04-1-enums/main.c similarity index 100% rename from src/4-1-enums/main.c rename to src/04-1-enums/main.c diff --git a/src/4-2-non-default-values/color.h b/src/04-2-non-default-values/color.h similarity index 100% rename from src/4-2-non-default-values/color.h rename to src/04-2-non-default-values/color.h diff --git a/src/4-2-non-default-values/main.c b/src/04-2-non-default-values/main.c similarity index 100% rename from src/4-2-non-default-values/main.c rename to src/04-2-non-default-values/main.c diff --git a/src/4-3-switch-case/http.c b/src/04-3-switch-case/http.c similarity index 100% rename from src/4-3-switch-case/http.c rename to src/04-3-switch-case/http.c diff --git a/src/4-3-switch-case/http.h b/src/04-3-switch-case/http.h similarity index 100% rename from src/4-3-switch-case/http.h rename to src/04-3-switch-case/http.h diff --git a/src/4-3-switch-case/main.c b/src/04-3-switch-case/main.c similarity index 100% rename from src/4-3-switch-case/main.c rename to src/04-3-switch-case/main.c diff --git a/src/4-4-sizeof-enum/main.c b/src/04-4-sizeof-enum/main.c similarity index 100% rename from src/4-4-sizeof-enum/main.c rename to src/04-4-sizeof-enum/main.c diff --git a/src/5-1-union/exercise.c b/src/05-1-union/exercise.c similarity index 100% rename from src/5-1-union/exercise.c rename to src/05-1-union/exercise.c diff --git a/src/5-1-union/exercise.h b/src/05-1-union/exercise.h similarity index 100% rename from src/5-1-union/exercise.h rename to src/05-1-union/exercise.h diff --git a/src/5-1-union/main.c b/src/05-1-union/main.c similarity index 100% rename from src/5-1-union/main.c rename to src/05-1-union/main.c diff --git a/src/5-2-memory-layout/main.c b/src/05-2-memory-layout/main.c similarity index 100% rename from src/5-2-memory-layout/main.c rename to src/05-2-memory-layout/main.c diff --git a/src/5-3-5-4-union-size/main.c b/src/05-3-5-4-union-size/main.c similarity index 100% rename from src/5-3-5-4-union-size/main.c rename to src/05-3-5-4-union-size/main.c diff --git a/src/5-5-helper-fields/exercise.h b/src/05-5-helper-fields/exercise.h similarity index 100% rename from src/5-5-helper-fields/exercise.h rename to src/05-5-helper-fields/exercise.h diff --git a/src/5-5-helper-fields/main.c b/src/05-5-helper-fields/main.c similarity index 100% rename from src/5-5-helper-fields/main.c rename to src/05-5-helper-fields/main.c diff --git a/src/6-1-the-stack/exercise.h b/src/06-1-the-stack/exercise.h similarity index 100% rename from src/6-1-the-stack/exercise.h rename to src/06-1-the-stack/exercise.h diff --git a/src/6-1-the-stack/main.c b/src/06-1-the-stack/main.c similarity index 100% rename from src/6-1-the-stack/main.c rename to src/06-1-the-stack/main.c diff --git a/src/6-2-why-a-stack/main.c b/src/06-2-why-a-stack/main.c similarity index 100% rename from src/6-2-why-a-stack/main.c rename to src/06-2-why-a-stack/main.c diff --git a/src/6-3-stack-overflow/main.c b/src/06-3-stack-overflow/main.c similarity index 100% rename from src/6-3-stack-overflow/main.c rename to src/06-3-stack-overflow/main.c diff --git a/src/6-4-pointers-to-the-stack/main.c b/src/06-4-pointers-to-the-stack/main.c similarity index 100% rename from src/6-4-pointers-to-the-stack/main.c rename to src/06-4-pointers-to-the-stack/main.c diff --git a/src/6-5-the-heap/exercise.c b/src/06-5-the-heap/exercise.c similarity index 100% rename from src/6-5-the-heap/exercise.c rename to src/06-5-the-heap/exercise.c diff --git a/src/6-5-the-heap/exercise.h b/src/06-5-the-heap/exercise.h similarity index 100% rename from src/6-5-the-heap/exercise.h rename to src/06-5-the-heap/exercise.h diff --git a/src/6-5-the-heap/main.c b/src/06-5-the-heap/main.c similarity index 100% rename from src/6-5-the-heap/main.c rename to src/06-5-the-heap/main.c diff --git a/src/6-6-malloc/exercise.c b/src/06-6-malloc/exercise.c similarity index 100% rename from src/6-6-malloc/exercise.c rename to src/06-6-malloc/exercise.c diff --git a/src/6-6-malloc/exercise.h b/src/06-6-malloc/exercise.h similarity index 100% rename from src/6-6-malloc/exercise.h rename to src/06-6-malloc/exercise.h diff --git a/src/6-6-malloc/main.c b/src/06-6-malloc/main.c similarity index 100% rename from src/6-6-malloc/main.c rename to src/06-6-malloc/main.c diff --git a/src/6-7-free/exercise.c b/src/06-7-free/exercise.c similarity index 100% rename from src/6-7-free/exercise.c rename to src/06-7-free/exercise.c diff --git a/src/6-7-free/exercise.h b/src/06-7-free/exercise.h similarity index 100% rename from src/6-7-free/exercise.h rename to src/06-7-free/exercise.h diff --git a/src/6-7-free/main.c b/src/06-7-free/main.c similarity index 100% rename from src/6-7-free/main.c rename to src/06-7-free/main.c diff --git a/src/6-8-big-endian-little-endian/main.c b/src/06-8-big-endian-little-endian/main.c similarity index 100% rename from src/6-8-big-endian-little-endian/main.c rename to src/06-8-big-endian-little-endian/main.c diff --git a/src/7-1-pointer-pointers/exercise.c b/src/07-1-pointer-pointers/exercise.c similarity index 100% rename from src/7-1-pointer-pointers/exercise.c rename to src/07-1-pointer-pointers/exercise.c diff --git a/src/7-1-pointer-pointers/exercise.h b/src/07-1-pointer-pointers/exercise.h similarity index 100% rename from src/7-1-pointer-pointers/exercise.h rename to src/07-1-pointer-pointers/exercise.h diff --git a/src/7-1-pointer-pointers/main.c b/src/07-1-pointer-pointers/main.c similarity index 100% rename from src/7-1-pointer-pointers/main.c rename to src/07-1-pointer-pointers/main.c diff --git a/src/7-2-array-of-pointers/exercise.c b/src/07-2-array-of-pointers/exercise.c similarity index 100% rename from src/7-2-array-of-pointers/exercise.c rename to src/07-2-array-of-pointers/exercise.c diff --git a/src/7-2-array-of-pointers/exercise.h b/src/07-2-array-of-pointers/exercise.h similarity index 100% rename from src/7-2-array-of-pointers/exercise.h rename to src/07-2-array-of-pointers/exercise.h diff --git a/src/7-2-array-of-pointers/main.c b/src/07-2-array-of-pointers/main.c similarity index 100% rename from src/7-2-array-of-pointers/main.c rename to src/07-2-array-of-pointers/main.c diff --git a/src/7-3-void-pointers/exercise.c b/src/07-3-void-pointers/exercise.c similarity index 100% rename from src/7-3-void-pointers/exercise.c rename to src/07-3-void-pointers/exercise.c diff --git a/src/7-3-void-pointers/exercise.h b/src/07-3-void-pointers/exercise.h similarity index 100% rename from src/7-3-void-pointers/exercise.h rename to src/07-3-void-pointers/exercise.h diff --git a/src/7-3-void-pointers/main.c b/src/07-3-void-pointers/main.c similarity index 100% rename from src/7-3-void-pointers/main.c rename to src/07-3-void-pointers/main.c diff --git a/src/7-4-swapping-integers/exercise.c b/src/07-4-swapping-integers/exercise.c similarity index 100% rename from src/7-4-swapping-integers/exercise.c rename to src/07-4-swapping-integers/exercise.c diff --git a/src/7-4-swapping-integers/exercise.h b/src/07-4-swapping-integers/exercise.h similarity index 100% rename from src/7-4-swapping-integers/exercise.h rename to src/07-4-swapping-integers/exercise.h diff --git a/src/7-4-swapping-integers/main.c b/src/07-4-swapping-integers/main.c similarity index 100% rename from src/7-4-swapping-integers/main.c rename to src/07-4-swapping-integers/main.c diff --git a/src/7-5-swapping-strings/exercise.c b/src/07-5-swapping-strings/exercise.c similarity index 100% rename from src/7-5-swapping-strings/exercise.c rename to src/07-5-swapping-strings/exercise.c diff --git a/src/7-5-swapping-strings/exercise.h b/src/07-5-swapping-strings/exercise.h similarity index 100% rename from src/7-5-swapping-strings/exercise.h rename to src/07-5-swapping-strings/exercise.h diff --git a/src/7-5-swapping-strings/main.c b/src/07-5-swapping-strings/main.c similarity index 100% rename from src/7-5-swapping-strings/main.c rename to src/07-5-swapping-strings/main.c diff --git a/src/7-6-generic-swap/exercise.c b/src/07-6-generic-swap/exercise.c similarity index 100% rename from src/7-6-generic-swap/exercise.c rename to src/07-6-generic-swap/exercise.c diff --git a/src/7-6-generic-swap/exercise.h b/src/07-6-generic-swap/exercise.h similarity index 100% rename from src/7-6-generic-swap/exercise.h rename to src/07-6-generic-swap/exercise.h diff --git a/src/7-6-generic-swap/main.c b/src/07-6-generic-swap/main.c similarity index 100% rename from src/7-6-generic-swap/main.c rename to src/07-6-generic-swap/main.c diff --git a/src/8-1-low-level-stack/main.c b/src/08-1-low-level-stack/main.c similarity index 100% rename from src/8-1-low-level-stack/main.c rename to src/08-1-low-level-stack/main.c diff --git a/src/8-1-low-level-stack/snekstack.c b/src/08-1-low-level-stack/snekstack.c similarity index 100% rename from src/8-1-low-level-stack/snekstack.c rename to src/08-1-low-level-stack/snekstack.c diff --git a/src/8-1-low-level-stack/snekstack.h b/src/08-1-low-level-stack/snekstack.h similarity index 100% rename from src/8-1-low-level-stack/snekstack.h rename to src/08-1-low-level-stack/snekstack.h diff --git a/src/8-2-stack-push/main.c b/src/08-2-stack-push/main.c similarity index 100% rename from src/8-2-stack-push/main.c rename to src/08-2-stack-push/main.c diff --git a/src/8-2-stack-push/snekstack.c b/src/08-2-stack-push/snekstack.c similarity index 100% rename from src/8-2-stack-push/snekstack.c rename to src/08-2-stack-push/snekstack.c diff --git a/src/8-2-stack-push/snekstack.h b/src/08-2-stack-push/snekstack.h similarity index 100% rename from src/8-2-stack-push/snekstack.h rename to src/08-2-stack-push/snekstack.h diff --git a/src/8-3-stack-pop/main.c b/src/08-3-stack-pop/main.c similarity index 100% rename from src/8-3-stack-pop/main.c rename to src/08-3-stack-pop/main.c diff --git a/src/8-3-stack-pop/snekstack.c b/src/08-3-stack-pop/snekstack.c similarity index 100% rename from src/8-3-stack-pop/snekstack.c rename to src/08-3-stack-pop/snekstack.c diff --git a/src/8-3-stack-pop/snekstack.h b/src/08-3-stack-pop/snekstack.h similarity index 100% rename from src/8-3-stack-pop/snekstack.h rename to src/08-3-stack-pop/snekstack.h diff --git a/src/8-4-stack-free/main.c b/src/08-4-stack-free/main.c similarity index 100% rename from src/8-4-stack-free/main.c rename to src/08-4-stack-free/main.c diff --git a/src/8-4-stack-free/snekstack.c b/src/08-4-stack-free/snekstack.c similarity index 100% rename from src/8-4-stack-free/snekstack.c rename to src/08-4-stack-free/snekstack.c diff --git a/src/8-4-stack-free/snekstack.h b/src/08-4-stack-free/snekstack.h similarity index 100% rename from src/8-4-stack-free/snekstack.h rename to src/08-4-stack-free/snekstack.h diff --git a/src/8-5-dangerous-push/exercise.c b/src/08-5-dangerous-push/exercise.c similarity index 100% rename from src/8-5-dangerous-push/exercise.c rename to src/08-5-dangerous-push/exercise.c diff --git a/src/8-5-dangerous-push/exercise.h b/src/08-5-dangerous-push/exercise.h similarity index 100% rename from src/8-5-dangerous-push/exercise.h rename to src/08-5-dangerous-push/exercise.h diff --git a/src/8-5-dangerous-push/main.c b/src/08-5-dangerous-push/main.c similarity index 100% rename from src/8-5-dangerous-push/main.c rename to src/08-5-dangerous-push/main.c diff --git a/src/8-5-dangerous-push/snekstack.c b/src/08-5-dangerous-push/snekstack.c similarity index 100% rename from src/8-5-dangerous-push/snekstack.c rename to src/08-5-dangerous-push/snekstack.c diff --git a/src/8-5-dangerous-push/snekstack.h b/src/08-5-dangerous-push/snekstack.h similarity index 100% rename from src/8-5-dangerous-push/snekstack.h rename to src/08-5-dangerous-push/snekstack.h diff --git a/src/8-6-multiple-types/exercise.c b/src/08-6-multiple-types/exercise.c similarity index 100% rename from src/8-6-multiple-types/exercise.c rename to src/08-6-multiple-types/exercise.c diff --git a/src/8-6-multiple-types/exercise.h b/src/08-6-multiple-types/exercise.h similarity index 100% rename from src/8-6-multiple-types/exercise.h rename to src/08-6-multiple-types/exercise.h diff --git a/src/8-6-multiple-types/main.c b/src/08-6-multiple-types/main.c similarity index 100% rename from src/8-6-multiple-types/main.c rename to src/08-6-multiple-types/main.c diff --git a/src/8-6-multiple-types/snekstack.c b/src/08-6-multiple-types/snekstack.c similarity index 100% rename from src/8-6-multiple-types/snekstack.c rename to src/08-6-multiple-types/snekstack.c diff --git a/src/8-6-multiple-types/snekstack.h b/src/08-6-multiple-types/snekstack.h similarity index 100% rename from src/8-6-multiple-types/snekstack.h rename to src/08-6-multiple-types/snekstack.h diff --git a/src/9-1-snek-objects/main.c b/src/09-1-snek-objects/main.c similarity index 100% rename from src/9-1-snek-objects/main.c rename to src/09-1-snek-objects/main.c diff --git a/src/9-1-snek-objects/snekobject.h b/src/09-1-snek-objects/snekobject.h similarity index 100% rename from src/9-1-snek-objects/snekobject.h rename to src/09-1-snek-objects/snekobject.h diff --git a/src/9-2-integer/main.c b/src/09-2-integer/main.c similarity index 100% rename from src/9-2-integer/main.c rename to src/09-2-integer/main.c diff --git a/src/9-2-integer/snekobject.c b/src/09-2-integer/snekobject.c similarity index 100% rename from src/9-2-integer/snekobject.c rename to src/09-2-integer/snekobject.c diff --git a/src/9-2-integer/snekobject.h b/src/09-2-integer/snekobject.h similarity index 100% rename from src/9-2-integer/snekobject.h rename to src/09-2-integer/snekobject.h diff --git a/src/9-3-float/main.c b/src/09-3-float/main.c similarity index 100% rename from src/9-3-float/main.c rename to src/09-3-float/main.c diff --git a/src/9-3-float/snekobject.c b/src/09-3-float/snekobject.c similarity index 100% rename from src/9-3-float/snekobject.c rename to src/09-3-float/snekobject.c diff --git a/src/9-3-float/snekobject.h b/src/09-3-float/snekobject.h similarity index 100% rename from src/9-3-float/snekobject.h rename to src/09-3-float/snekobject.h diff --git a/src/9-4-string/main.c b/src/09-4-string/main.c similarity index 100% rename from src/9-4-string/main.c rename to src/09-4-string/main.c diff --git a/src/9-4-string/snekobject.c b/src/09-4-string/snekobject.c similarity index 100% rename from src/9-4-string/snekobject.c rename to src/09-4-string/snekobject.c diff --git a/src/9-4-string/snekobject.h b/src/09-4-string/snekobject.h similarity index 100% rename from src/9-4-string/snekobject.h rename to src/09-4-string/snekobject.h diff --git a/src/9-5-vector3/main.c b/src/09-5-vector3/main.c similarity index 100% rename from src/9-5-vector3/main.c rename to src/09-5-vector3/main.c diff --git a/src/9-5-vector3/snekobject.c b/src/09-5-vector3/snekobject.c similarity index 100% rename from src/9-5-vector3/snekobject.c rename to src/09-5-vector3/snekobject.c diff --git a/src/9-5-vector3/snekobject.h b/src/09-5-vector3/snekobject.h similarity index 100% rename from src/9-5-vector3/snekobject.h rename to src/09-5-vector3/snekobject.h diff --git a/src/9-6-arrays/main.c b/src/09-6-arrays/main.c similarity index 100% rename from src/9-6-arrays/main.c rename to src/09-6-arrays/main.c diff --git a/src/9-6-arrays/snekobject.c b/src/09-6-arrays/snekobject.c similarity index 100% rename from src/9-6-arrays/snekobject.c rename to src/09-6-arrays/snekobject.c diff --git a/src/9-6-arrays/snekobject.h b/src/09-6-arrays/snekobject.h similarity index 100% rename from src/9-6-arrays/snekobject.h rename to src/09-6-arrays/snekobject.h diff --git a/src/9-7-get-and-set/main.c b/src/09-7-get-and-set/main.c similarity index 100% rename from src/9-7-get-and-set/main.c rename to src/09-7-get-and-set/main.c diff --git a/src/9-7-get-and-set/snekobject.c b/src/09-7-get-and-set/snekobject.c similarity index 100% rename from src/9-7-get-and-set/snekobject.c rename to src/09-7-get-and-set/snekobject.c diff --git a/src/9-7-get-and-set/snekobject.h b/src/09-7-get-and-set/snekobject.h similarity index 100% rename from src/9-7-get-and-set/snekobject.h rename to src/09-7-get-and-set/snekobject.h diff --git a/src/9-8-length/main.c b/src/09-8-length/main.c similarity index 100% rename from src/9-8-length/main.c rename to src/09-8-length/main.c diff --git a/src/9-8-length/snekobject.c b/src/09-8-length/snekobject.c similarity index 100% rename from src/9-8-length/snekobject.c rename to src/09-8-length/snekobject.c diff --git a/src/9-8-length/snekobject.h b/src/09-8-length/snekobject.h similarity index 100% rename from src/9-8-length/snekobject.h rename to src/09-8-length/snekobject.h diff --git a/src/11-01-handling-cycles/main.c b/src/11-01-handling-cycles/main.c new file mode 100644 index 0000000..e1c4a1d --- /dev/null +++ b/src/11-01-handling-cycles/main.c @@ -0,0 +1,30 @@ +#include +#include + +#include "munit.h" +#include "snekobject.h" + +munit_case(RUN, correctly_free, { + snek_object_t *first = new_snek_array(1); + snek_object_t *second = new_snek_array(1); + // refcounts: first = 1, second = 1 + snek_array_set(first, 0, second); + // refcounts: first = 1, second = 2 + snek_array_set(second, 0, first); + // refcounts: first = 2, second = 2 + refcount_dec(first); + refcount_dec(second); + assert_int(first->refcount, ==, 1, "Refcount first should be 1"); + assert_int(second->refcount, ==, 1, "Refcount second should be 1"); +}); + +int main() { + MunitTest tests[] = { + munit_test("/correctly_free", correctly_free), + munit_null_test, + }; + + MunitSuite suite = munit_suite("refcount", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-01-handling-cycles/snekobject.c b/src/11-01-handling-cycles/snekobject.c new file mode 100644 index 0000000..6e34461 --- /dev/null +++ b/src/11-01-handling-cycles/snekobject.c @@ -0,0 +1,179 @@ +#include "assert.h" +#include +#include +#include + +#include "bootlib.h" +#include "snekobject.h" + +bool snek_array_set(snek_object_t *snek_obj, size_t index, snek_object_t *value) { + if (snek_obj == NULL || value == NULL) { + return false; + } + if (snek_obj->kind != ARRAY) { + return false; + } + if (index >= snek_obj->data.v_array.size) { + return false; + } + refcount_inc(value); + 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; + return true; +} + +void refcount_boot_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_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: { + snek_array_t array = obj->data.v_array; + for (size_t i = 0; i < array.size; i++) { + refcount_dec(array.elements[i]); + } + boot_free(array.elements); + break; + } + default: + assert(false); + } + boot_free(obj); +} + +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_boot_free(obj); + } + return; +} + +snek_object_t *_new_snek_object() { + snek_object_t *obj = boot_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 = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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 = boot_malloc(len + 1); + if (dst == NULL) { + boot_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/11-01-handling-cycles/snekobject.h b/src/11-01-handling-cycles/snekobject.h new file mode 100644 index 0000000..e6e1a10 --- /dev/null +++ b/src/11-01-handling-cycles/snekobject.h @@ -0,0 +1,52 @@ +#include +#include + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + int refcount; + 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); + +void refcount_inc(snek_object_t *obj); +void refcount_dec(snek_object_t *obj); +void refcount_free(snek_object_t *obj); + +bool snek_array_set(snek_object_t *array, 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/11-02-pros-and-cons/main.c b/src/11-02-pros-and-cons/main.c new file mode 100644 index 0000000..3fce313 --- /dev/null +++ b/src/11-02-pros-and-cons/main.c @@ -0,0 +1,31 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "vm.h" + +munit_case(RUN, test_vm_new, { + vm_t *vm = vm_new(); + assert_int(vm->frames->capacity, ==, 8, "frames should have capacity 8"); + assert_int(vm->objects->capacity, ==, 8, "objects should have capacity 8"); + vm_free(vm); +}); + +munit_case(SUBMIT, test_vm_new_free, { + vm_t *vm = vm_new(); + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/vm", test_vm_new), + munit_test("/vm", test_vm_new_free), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-02-pros-and-cons/snekobject.h b/src/11-02-pros-and-cons/snekobject.h new file mode 100644 index 0000000..c7094bc --- /dev/null +++ b/src/11-02-pros-and-cons/snekobject.h @@ -0,0 +1,38 @@ +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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; diff --git a/src/11-02-pros-and-cons/stack.c b/src/11-02-pros-and-cons/stack.c new file mode 100644 index 0000000..a81c7c2 --- /dev/null +++ b/src/11-02-pros-and-cons/stack.c @@ -0,0 +1,78 @@ +#include + +#include "bootlib.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-02-pros-and-cons/stack.h b/src/11-02-pros-and-cons/stack.h new file mode 100644 index 0000000..80890ab --- /dev/null +++ b/src/11-02-pros-and-cons/stack.h @@ -0,0 +1,16 @@ +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-02-pros-and-cons/vm.c b/src/11-02-pros-and-cons/vm.c new file mode 100644 index 0000000..b58d08f --- /dev/null +++ b/src/11-02-pros-and-cons/vm.c @@ -0,0 +1,19 @@ +#include "bootlib.h" +#include "vm.h" + +vm_t *vm_new() { + vm_t * vm = (vm_t *)boot_malloc(sizeof(vm_t)); + if (vm == NULL) return NULL; + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + if (vm == NULL) return; + + stack_free(vm->frames); + stack_free(vm->objects); + boot_free(vm); +} diff --git a/src/11-02-pros-and-cons/vm.h b/src/11-02-pros-and-cons/vm.h new file mode 100644 index 0000000..126815e --- /dev/null +++ b/src/11-02-pros-and-cons/vm.h @@ -0,0 +1,9 @@ +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +vm_t *vm_new(); +void vm_free(vm_t *vm); diff --git a/src/11-03-stack-frames/main.c b/src/11-03-stack-frames/main.c new file mode 100644 index 0000000..4b1d40a --- /dev/null +++ b/src/11-03-stack-frames/main.c @@ -0,0 +1,43 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "vm.h" + +munit_case(RUN, test_vm_new, { + vm_t *vm = vm_new(); + vm_new_frame(vm); + assert_int(vm->frames->count, ==, 1, "frame was pushed"); + vm_free(vm); +}); + +munit_case(RUN, test_vm_new_frame, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + assert_ptr(frame->references, !=, NULL, "frame->references must be allocated"); + assert_int(frame->references->count, ==, 0, "references stack should start empty"); + assert(frame->references->capacity > 0); // references stack must have capacity > 0 + assert_ptr(frame->references->data, !=, NULL, "references stack backing array must be allocated"); + vm_free(vm); +}); + +munit_case(SUBMIT, test_frames_are_freed, { + vm_t *vm = vm_new(); + vm_new_frame(vm); + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_vm_new", test_vm_new), + munit_test("/test_vm_new_frame", test_vm_new_frame), + munit_test("/test_frames_are_freed", test_frames_are_freed), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-03-stack-frames/snekobject.h b/src/11-03-stack-frames/snekobject.h new file mode 100644 index 0000000..c41b9a4 --- /dev/null +++ b/src/11-03-stack-frames/snekobject.h @@ -0,0 +1,40 @@ +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; diff --git a/src/11-03-stack-frames/stack.c b/src/11-03-stack-frames/stack.c new file mode 100644 index 0000000..a81c7c2 --- /dev/null +++ b/src/11-03-stack-frames/stack.c @@ -0,0 +1,78 @@ +#include + +#include "bootlib.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-03-stack-frames/stack.h b/src/11-03-stack-frames/stack.h new file mode 100644 index 0000000..80890ab --- /dev/null +++ b/src/11-03-stack-frames/stack.h @@ -0,0 +1,16 @@ +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-03-stack-frames/vm.c b/src/11-03-stack-frames/vm.c new file mode 100644 index 0000000..86f9b8b --- /dev/null +++ b/src/11-03-stack-frames/vm.c @@ -0,0 +1,49 @@ +#include "bootlib.h" +#include "vm.h" + +void vm_frame_push(vm_t *vm, frame_t *frame) { + if (vm == NULL || frame == NULL) return; + + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + if (vm == NULL) return NULL; + + frame_t *frame = (frame_t *)boot_malloc(sizeof(frame_t)); + if (frame == NULL) return NULL; + + frame->references = stack_new(8); + vm_frame_push(vm, frame); + + return frame; +} + +void frame_free(frame_t *frame) { + if (frame == NULL) return; + + stack_free(frame->references); + boot_free(frame); +} + +// don't touch below this line + +vm_t *vm_new() { + vm_t *vm = malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + stack_free(vm->objects); + free(vm); +} diff --git a/src/11-03-stack-frames/vm.h b/src/11-03-stack-frames/vm.h new file mode 100644 index 0000000..a17f345 --- /dev/null +++ b/src/11-03-stack-frames/vm.h @@ -0,0 +1,18 @@ +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +vm_t *vm_new(); +void vm_free(vm_t *vm); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); diff --git a/src/11-04-tracking-objects/main.c b/src/11-04-tracking-objects/main.c new file mode 100644 index 0000000..df25efd --- /dev/null +++ b/src/11-04-tracking-objects/main.c @@ -0,0 +1,46 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "snekobject.h" +#include "vm.h" + +munit_case(RUN, test_new_object, { + vm_t *vm = vm_new(); + snek_object_t *obj = new_snek_integer(vm, 5); + assert_int(obj->kind, ==, INTEGER, "kind must be INTEGER"); + assert_ptr_equal(vm->objects->data[0], obj, "object must be tracked"); + boot_free(obj); + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(RUN, test_vm_new, { + vm_t *vm = vm_new(); + assert_ptr_not_null(vm->frames, "frames must not be NULL"); + assert_ptr_not_null(vm->objects, "objects must not be NULL"); + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(RUN, test_frames_are_freed, { + vm_t *vm = vm_new(); + vm_new_frame(vm); + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_vm_new", test_vm_new), + munit_test("/test_frames_are_freed", test_frames_are_freed), + munit_test("/test_new_object", test_new_object), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-04-tracking-objects/sneknew.c b/src/11-04-tracking-objects/sneknew.c new file mode 100644 index 0000000..5b4eb9e --- /dev/null +++ b/src/11-04-tracking-objects/sneknew.c @@ -0,0 +1,98 @@ +#include +#include + +#include "bootlib.h" +#include "sneknew.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) +{ + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) return NULL; + + vm_track_object(vm, obj); + + return obj; +} + +// don't touch below this line + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-04-tracking-objects/sneknew.h b/src/11-04-tracking-objects/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-04-tracking-objects/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-04-tracking-objects/snekobject.h b/src/11-04-tracking-objects/snekobject.h new file mode 100644 index 0000000..aac7126 --- /dev/null +++ b/src/11-04-tracking-objects/snekobject.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; diff --git a/src/11-04-tracking-objects/stack.c b/src/11-04-tracking-objects/stack.c new file mode 100644 index 0000000..3df6bc0 --- /dev/null +++ b/src/11-04-tracking-objects/stack.c @@ -0,0 +1,82 @@ + +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-04-tracking-objects/stack.h b/src/11-04-tracking-objects/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-04-tracking-objects/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-04-tracking-objects/vm.c b/src/11-04-tracking-objects/vm.c new file mode 100644 index 0000000..84766b6 --- /dev/null +++ b/src/11-04-tracking-objects/vm.c @@ -0,0 +1,50 @@ +#include "bootlib.h" +#include "vm.h" +#include "snekobject.h" +#include "stack.h" + +void vm_track_object(vm_t *vm, snek_object_t *obj) +{ + if (vm == NULL || obj == NULL) return; + + stack_push(vm->objects, obj); +} + +// don't touch below this line + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + stack_free(vm->objects); + boot_free(vm); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} diff --git a/src/11-04-tracking-objects/vm.h b/src/11-04-tracking-objects/vm.h new file mode 100644 index 0000000..03d15f7 --- /dev/null +++ b/src/11-04-tracking-objects/vm.h @@ -0,0 +1,21 @@ +#pragma once +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); diff --git a/src/11-05-free/main.c b/src/11-05-free/main.c new file mode 100644 index 0000000..ee8c0f5 --- /dev/null +++ b/src/11-05-free/main.c @@ -0,0 +1,35 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "vm.h" + +munit_case(RUN, test_reference_object, { + vm_t *vm = vm_new(); + new_snek_integer(vm, 5); + new_snek_string(vm, "hello"); + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_frames_are_freed, { + vm_t *vm = vm_new(); + vm_new_frame(vm); + vm_new_frame(vm); + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_reference_object", test_reference_object), + munit_test("/test_frames_are_freed", test_frames_are_freed), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-05-free/sneknew.c b/src/11-05-free/sneknew.c new file mode 100644 index 0000000..6957efa --- /dev/null +++ b/src/11-05-free/sneknew.c @@ -0,0 +1,95 @@ +#include "sneknew.h" +#include +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) { + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + vm_track_object(vm, obj); + return obj; +} + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-05-free/sneknew.h b/src/11-05-free/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-05-free/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-05-free/snekobject.c b/src/11-05-free/snekobject.c new file mode 100644 index 0000000..c1663ef --- /dev/null +++ b/src/11-05-free/snekobject.c @@ -0,0 +1,21 @@ +#include "bootlib.h" +#include "snekobject.h" + +void snek_object_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_free(obj->data.v_string); + break; + case VECTOR3: { + break; + } + case ARRAY: { + boot_free(obj->data.v_array.elements); + break; + } + } + boot_free(obj); +} diff --git a/src/11-05-free/snekobject.h b/src/11-05-free/snekobject.h new file mode 100644 index 0000000..cdbfc9e --- /dev/null +++ b/src/11-05-free/snekobject.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +void snek_object_free(snek_object_t *obj); + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; diff --git a/src/11-05-free/stack.c b/src/11-05-free/stack.c new file mode 100644 index 0000000..dd6c936 --- /dev/null +++ b/src/11-05-free/stack.c @@ -0,0 +1,81 @@ +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-05-free/stack.h b/src/11-05-free/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-05-free/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-05-free/vm.c b/src/11-05-free/vm.c new file mode 100644 index 0000000..b374d56 --- /dev/null +++ b/src/11-05-free/vm.c @@ -0,0 +1,53 @@ +#include "bootlib.h" +#include "vm.h" +#include "stack.h" + +void vm_free(vm_t *vm) { + if (vm == NULL) return; + + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + + for (int i = 0; i < vm->objects->count; i++) { + snek_object_free(vm->objects->data[i]); + } + stack_free(vm->objects); + + boot_free(vm); +} + +// don't touch below this line + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_track_object(vm_t *vm, snek_object_t *obj) { + stack_push(vm->objects, obj); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} diff --git a/src/11-05-free/vm.h b/src/11-05-free/vm.h new file mode 100644 index 0000000..03d15f7 --- /dev/null +++ b/src/11-05-free/vm.h @@ -0,0 +1,21 @@ +#pragma once +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); diff --git a/src/11-06-frame-references/main.c b/src/11-06-frame-references/main.c new file mode 100644 index 0000000..2096178 --- /dev/null +++ b/src/11-06-frame-references/main.c @@ -0,0 +1,50 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "vm.h" + +munit_case(RUN, test_one_ref, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + + snek_object_t *lanes_wpm = new_snek_integer(vm, 9); + frame_reference_object(frame, lanes_wpm); + + assert_int(frame->references->count, ==, 1, "Only one reference"); + assert_ptr_equal(lanes_wpm, frame->references->data[0], "Refs lanes_wpm"); + + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_multi_ref, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + + snek_object_t *lanes_wpm = new_snek_integer(vm, 9); + snek_object_t *teej_wpm = new_snek_integer(vm, 160); + frame_reference_object(frame, lanes_wpm); + frame_reference_object(frame, teej_wpm); + + assert_int(frame->references->count, ==, 2, "Two references"); + assert_ptr_equal(lanes_wpm, frame->references->data[0], "Refs lanes_wpm"); + assert_ptr_equal(teej_wpm, frame->references->data[1], "Refs teej_wpm"); + + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_one_ref", test_one_ref), + munit_test("/test_multi_ref", test_multi_ref), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-06-frame-references/sneknew.c b/src/11-06-frame-references/sneknew.c new file mode 100644 index 0000000..6957efa --- /dev/null +++ b/src/11-06-frame-references/sneknew.c @@ -0,0 +1,95 @@ +#include "sneknew.h" +#include +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) { + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + vm_track_object(vm, obj); + return obj; +} + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-06-frame-references/sneknew.h b/src/11-06-frame-references/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-06-frame-references/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-06-frame-references/snekobject.c b/src/11-06-frame-references/snekobject.c new file mode 100644 index 0000000..c1663ef --- /dev/null +++ b/src/11-06-frame-references/snekobject.c @@ -0,0 +1,21 @@ +#include "bootlib.h" +#include "snekobject.h" + +void snek_object_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_free(obj->data.v_string); + break; + case VECTOR3: { + break; + } + case ARRAY: { + boot_free(obj->data.v_array.elements); + break; + } + } + boot_free(obj); +} diff --git a/src/11-06-frame-references/snekobject.h b/src/11-06-frame-references/snekobject.h new file mode 100644 index 0000000..cdbfc9e --- /dev/null +++ b/src/11-06-frame-references/snekobject.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +void snek_object_free(snek_object_t *obj); + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; diff --git a/src/11-06-frame-references/stack.c b/src/11-06-frame-references/stack.c new file mode 100644 index 0000000..dd6c936 --- /dev/null +++ b/src/11-06-frame-references/stack.c @@ -0,0 +1,81 @@ +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-06-frame-references/stack.h b/src/11-06-frame-references/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-06-frame-references/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-06-frame-references/vm.c b/src/11-06-frame-references/vm.c new file mode 100644 index 0000000..0a8efc3 --- /dev/null +++ b/src/11-06-frame-references/vm.c @@ -0,0 +1,57 @@ +#include "bootlib.h" +#include "vm.h" +#include "snekobject.h" +#include "stack.h" + +void frame_reference_object(frame_t *frame, snek_object_t *obj) { + if (frame == NULL || obj == NULL) return; + + stack_push(frame->references, obj); +} + +// don't touch below this line + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + for (int i = 0; i < vm->objects->count; i++) { + snek_object_free(vm->objects->data[i]); + } + stack_free(vm->objects); + + boot_free(vm); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} + +void vm_track_object(vm_t *vm, snek_object_t *obj) { + stack_push(vm->objects, obj); +} diff --git a/src/11-06-frame-references/vm.h b/src/11-06-frame-references/vm.h new file mode 100644 index 0000000..fc0e84a --- /dev/null +++ b/src/11-06-frame-references/vm.h @@ -0,0 +1,23 @@ +#pragma once +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); + +void frame_reference_object(frame_t *frame, snek_object_t *obj); diff --git a/src/11-07-mark-and-sweep/main.c b/src/11-07-mark-and-sweep/main.c new file mode 100644 index 0000000..9035ae9 --- /dev/null +++ b/src/11-07-mark-and-sweep/main.c @@ -0,0 +1,39 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "vm.h" + +munit_case(RUN, test_field_exists, { + vm_t *vm = vm_new(); + snek_object_t *lane_courses = new_snek_integer(vm, 20); + snek_object_t *teej_courses = new_snek_integer(vm, 1); + lane_courses->is_marked; + teej_courses->is_marked; + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_marked_is_false, { + vm_t *vm = vm_new(); + snek_object_t *lane_courses = new_snek_integer(vm, 20); + snek_object_t *teej_courses = new_snek_integer(vm, 1); + assert_false(lane_courses->is_marked); + assert_false(teej_courses->is_marked); + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_field_exists", test_field_exists), + munit_test("/test_marked_is_false", test_marked_is_false), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-07-mark-and-sweep/sneknew.c b/src/11-07-mark-and-sweep/sneknew.c new file mode 100644 index 0000000..a2294f5 --- /dev/null +++ b/src/11-07-mark-and-sweep/sneknew.c @@ -0,0 +1,96 @@ +#include "sneknew.h" +#include +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) { + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + obj->is_marked = false; + vm_track_object(vm, obj); + return obj; +} + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-07-mark-and-sweep/sneknew.h b/src/11-07-mark-and-sweep/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-07-mark-and-sweep/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-07-mark-and-sweep/snekobject.c b/src/11-07-mark-and-sweep/snekobject.c new file mode 100644 index 0000000..c1663ef --- /dev/null +++ b/src/11-07-mark-and-sweep/snekobject.c @@ -0,0 +1,21 @@ +#include "bootlib.h" +#include "snekobject.h" + +void snek_object_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_free(obj->data.v_string); + break; + case VECTOR3: { + break; + } + case ARRAY: { + boot_free(obj->data.v_array.elements); + break; + } + } + boot_free(obj); +} diff --git a/src/11-07-mark-and-sweep/snekobject.h b/src/11-07-mark-and-sweep/snekobject.h new file mode 100644 index 0000000..cdbfc9e --- /dev/null +++ b/src/11-07-mark-and-sweep/snekobject.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +void snek_object_free(snek_object_t *obj); + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; diff --git a/src/11-07-mark-and-sweep/stack.c b/src/11-07-mark-and-sweep/stack.c new file mode 100644 index 0000000..dd6c936 --- /dev/null +++ b/src/11-07-mark-and-sweep/stack.c @@ -0,0 +1,81 @@ +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-07-mark-and-sweep/stack.h b/src/11-07-mark-and-sweep/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-07-mark-and-sweep/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-07-mark-and-sweep/vm.c b/src/11-07-mark-and-sweep/vm.c new file mode 100644 index 0000000..0a8efc3 --- /dev/null +++ b/src/11-07-mark-and-sweep/vm.c @@ -0,0 +1,57 @@ +#include "bootlib.h" +#include "vm.h" +#include "snekobject.h" +#include "stack.h" + +void frame_reference_object(frame_t *frame, snek_object_t *obj) { + if (frame == NULL || obj == NULL) return; + + stack_push(frame->references, obj); +} + +// don't touch below this line + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + for (int i = 0; i < vm->objects->count; i++) { + snek_object_free(vm->objects->data[i]); + } + stack_free(vm->objects); + + boot_free(vm); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} + +void vm_track_object(vm_t *vm, snek_object_t *obj) { + stack_push(vm->objects, obj); +} diff --git a/src/11-07-mark-and-sweep/vm.h b/src/11-07-mark-and-sweep/vm.h new file mode 100644 index 0000000..fc0e84a --- /dev/null +++ b/src/11-07-mark-and-sweep/vm.h @@ -0,0 +1,23 @@ +#pragma once +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); + +void frame_reference_object(frame_t *frame, snek_object_t *obj); diff --git a/src/11-08-mark/main.c b/src/11-08-mark/main.c new file mode 100644 index 0000000..efce0c2 --- /dev/null +++ b/src/11-08-mark/main.c @@ -0,0 +1,65 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "vm.h" + +munit_case(RUN, test_single_frame, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + + snek_object_t *teej_skill = new_snek_integer(vm, 420); + snek_object_t *lane_skill = new_snek_string(vm, "issues"); + + mark(vm); + // should not be marked because not in frame + assert_false(teej_skill->is_marked); + assert_false(lane_skill->is_marked); + + frame_reference_object(frame, teej_skill); + frame_reference_object(frame, lane_skill); + + // after adding and marking, should be marked + mark(vm); + assert_true(teej_skill->is_marked); + assert_true(lane_skill->is_marked); + + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_multi_frame, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + frame_t *frame2 = vm_new_frame(vm); + + snek_object_t *teej_skill = new_snek_integer(vm, 420); + snek_object_t *lane_skill = new_snek_string(vm, "issues"); + snek_object_t *prime_skill = new_snek_string(vm, "infinite"); + + frame_reference_object(frame, teej_skill); + frame_reference_object(frame, lane_skill); + frame_reference_object(frame2, prime_skill); + mark(vm); + + assert_true(teej_skill->is_marked); + assert_true(lane_skill->is_marked); + assert_true(prime_skill->is_marked); + vm_free(vm); + assert(boot_all_freed()); +}); + + +int main() { + MunitTest tests[] = { + munit_test("/test_single_frame", test_single_frame), + munit_test("/test_multi_frame", test_multi_frame), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-08-mark/sneknew.c b/src/11-08-mark/sneknew.c new file mode 100644 index 0000000..a2294f5 --- /dev/null +++ b/src/11-08-mark/sneknew.c @@ -0,0 +1,96 @@ +#include "sneknew.h" +#include +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) { + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + obj->is_marked = false; + vm_track_object(vm, obj); + return obj; +} + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-08-mark/sneknew.h b/src/11-08-mark/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-08-mark/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-08-mark/snekobject.c b/src/11-08-mark/snekobject.c new file mode 100644 index 0000000..c1663ef --- /dev/null +++ b/src/11-08-mark/snekobject.c @@ -0,0 +1,21 @@ +#include "bootlib.h" +#include "snekobject.h" + +void snek_object_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_free(obj->data.v_string); + break; + case VECTOR3: { + break; + } + case ARRAY: { + boot_free(obj->data.v_array.elements); + break; + } + } + boot_free(obj); +} diff --git a/src/11-08-mark/snekobject.h b/src/11-08-mark/snekobject.h new file mode 100644 index 0000000..cbafafb --- /dev/null +++ b/src/11-08-mark/snekobject.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +void snek_object_free(snek_object_t *obj); diff --git a/src/11-08-mark/stack.c b/src/11-08-mark/stack.c new file mode 100644 index 0000000..dd6c936 --- /dev/null +++ b/src/11-08-mark/stack.c @@ -0,0 +1,81 @@ +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-08-mark/stack.h b/src/11-08-mark/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-08-mark/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-08-mark/vm.c b/src/11-08-mark/vm.c new file mode 100644 index 0000000..600ee63 --- /dev/null +++ b/src/11-08-mark/vm.c @@ -0,0 +1,69 @@ +#include "bootlib.h" +#include "vm.h" +#include "snekobject.h" +#include "stack.h" + +void mark(vm_t *vm) { + // Set mark on any object directly referenced by any stack frame + for (int i = 0; i < vm->frames->count; i++) { + frame_t *frame = (frame_t *)vm->frames->data[i]; + for (int j = 0; j < frame->references->count; j++) { + snek_object_t *obj = (snek_object_t *)frame->references->data[j]; + obj->is_marked = true; + } + } +} + +// don't touch below this line + +void frame_reference_object(frame_t *frame, snek_object_t *obj) { + stack_push(frame->references, obj); +} + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + // Free the stack frames, and then their container + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + + // Free the objects, and then their container + for (int i = 0; i < vm->objects->count; i++) { + snek_object_free(vm->objects->data[i]); + } + stack_free(vm->objects); + + boot_free(vm); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} + +void vm_track_object(vm_t *vm, snek_object_t *obj) { + stack_push(vm->objects, obj); +} diff --git a/src/11-08-mark/vm.h b/src/11-08-mark/vm.h new file mode 100644 index 0000000..0725e60 --- /dev/null +++ b/src/11-08-mark/vm.h @@ -0,0 +1,37 @@ +#pragma once + +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +/// Our main functions for garbage collection. +void mark(vm_t *vm); +void trace(vm_t *vm); +void sweep(vm_t *vm); + +/// This is the function that gets called to actually do the garbage collection, +/// but is just composed of `mark`, `trace`, and `sweep`. +/// +/// Don't worry, it's not going to delete your code (hopefully!) +void vm_collect_garbage(vm_t *vm); + +/// Already implemented +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); + +// Marks the object as referenced in the current stack frame. +void frame_reference_object(frame_t *frame, snek_object_t *obj); diff --git a/src/11-09-trace/main.c b/src/11-09-trace/main.c new file mode 100644 index 0000000..1608df4 --- /dev/null +++ b/src/11-09-trace/main.c @@ -0,0 +1,131 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "snekobject.h" +#include "vm.h" + +munit_case(RUN, test_trace_vector, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + + snek_object_t *x = new_snek_integer(vm, 5); + snek_object_t *y = new_snek_integer(vm, 5); + snek_object_t *z = new_snek_integer(vm, 5); + snek_object_t *vector = new_snek_vector3(vm, x, y, z); + + // nothing is marked + assert_false(x->is_marked); + assert_false(y->is_marked); + assert_false(z->is_marked); + assert_false(vector->is_marked); + + // After referencing and marking, the + // vector should be marked, but not the contents + frame_reference_object(frame, vector); + mark(vm); + assert_true(vector->is_marked); + assert_false(x->is_marked); + assert_false(y->is_marked); + assert_false(z->is_marked); + + // After tracing, the contents should be marked + trace(vm); + assert_true(vector->is_marked); + assert_true(x->is_marked); + assert_true(y->is_marked); + assert_true(z->is_marked); + + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_trace_array, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + + snek_object_t *devs = new_snek_array(vm, 2); + snek_object_t *lane = new_snek_string(vm, "Lane"); + snek_object_t *teej = new_snek_string(vm, "Teej"); + snek_array_set(devs, 0, lane); + snek_array_set(devs, 1, teej); + + // nothing is marked + assert_false(devs->is_marked); + assert_false(lane->is_marked); + assert_false(teej->is_marked); + + // After referencing and marking, the + // array should be marked, but not the contents + frame_reference_object(frame, devs); + mark(vm); + assert_true(devs->is_marked); + assert_false(lane->is_marked); + assert_false(teej->is_marked); + + // After tracing, the contents should be marked + trace(vm); + assert_true(devs->is_marked); + assert_true(lane->is_marked); + assert_true(teej->is_marked); + + vm_free(vm); + assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_trace_nested, { + vm_t *vm = vm_new(); + frame_t *frame = vm_new_frame(vm); + + snek_object_t *bootdevs = new_snek_array(vm, 2); + snek_object_t *lane = new_snek_string(vm, "Lane"); + snek_object_t *hunter = new_snek_string(vm, "Hunter"); + snek_array_set(bootdevs, 0, lane); + snek_array_set(bootdevs, 1, hunter); + + snek_object_t *terminaldevs = new_snek_array(vm, 4); + snek_object_t *prime = new_snek_string(vm, "Prime"); + snek_object_t *teej = new_snek_string(vm, "Teej"); + snek_object_t *dax = new_snek_string(vm, "Dax"); + snek_object_t *adam = new_snek_string(vm, "Adam"); + snek_array_set(terminaldevs, 0, prime); + snek_array_set(terminaldevs, 1, teej); + snek_array_set(terminaldevs, 2, dax); + snek_array_set(terminaldevs, 3, adam); + + snek_object_t *alldevs = new_snek_array(vm, 2); + snek_array_set(alldevs, 0, bootdevs); + snek_array_set(alldevs, 1, terminaldevs); + + frame_reference_object(frame, alldevs); + mark(vm); + trace(vm); + + assert_true(bootdevs->is_marked); + assert_true(lane->is_marked); + assert_true(hunter->is_marked); + assert_true(terminaldevs->is_marked); + assert_true(prime->is_marked); + assert_true(teej->is_marked); + assert_true(dax->is_marked); + assert_true(adam->is_marked); + assert_true(alldevs->is_marked); + + vm_free(vm); + assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_trace_vector", test_trace_vector), + munit_test("/test_trace_array", test_trace_array), + munit_test("/test_trace_nested", test_trace_nested), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-09-trace/sneknew.c b/src/11-09-trace/sneknew.c new file mode 100644 index 0000000..a2294f5 --- /dev/null +++ b/src/11-09-trace/sneknew.c @@ -0,0 +1,96 @@ +#include "sneknew.h" +#include +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) { + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + obj->is_marked = false; + vm_track_object(vm, obj); + return obj; +} + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-09-trace/sneknew.h b/src/11-09-trace/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-09-trace/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-09-trace/snekobject.c b/src/11-09-trace/snekobject.c new file mode 100644 index 0000000..44ef6fa --- /dev/null +++ b/src/11-09-trace/snekobject.c @@ -0,0 +1,143 @@ +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "sneknew.h" + +void snek_object_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_free(obj->data.v_string); + break; + case VECTOR3: { + break; + } + case ARRAY: { + boot_free(obj->data.v_array.elements); + break; + } + } + boot_free(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; + } + + // No need to change refcounts, we will find the garbage values + // later through mark-and-sweep. + + // 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 *snek_add(vm_t *vm, snek_object_t *a, snek_object_t *b) { + if (a == NULL || b == NULL) { + return NULL; + } + + switch (a->kind) { + case INTEGER: + switch (b->kind) { + case INTEGER: + return new_snek_integer(vm, a->data.v_int + b->data.v_int); + case FLOAT: + return new_snek_float(vm, (float)a->data.v_int + b->data.v_float); + default: + return NULL; + } + case FLOAT: + switch (b->kind) { + case FLOAT: + return new_snek_float(vm, a->data.v_float + b->data.v_float); + default: + return snek_add(vm, b, a); + } + case STRING: + switch (b->kind) { + case STRING: { + int a_len = strlen(a->data.v_string); + int b_len = strlen(b->data.v_string); + int len = a_len + b_len + 1; + char *dst = malloc(len * sizeof(char)); + dst[0] = '\0'; + + strcat(dst, a->data.v_string); + strcat(dst, b->data.v_string); + + snek_object_t *obj = new_snek_string(vm, dst); + boot_free(dst); + + return obj; + } + default: + return NULL; + } + case VECTOR3: + switch (b->kind) { + case VECTOR3: + return new_snek_vector3( + vm, + snek_add(vm, a->data.v_vector3.x, b->data.v_vector3.x), + snek_add(vm, a->data.v_vector3.y, b->data.v_vector3.y), + snek_add(vm, a->data.v_vector3.z, b->data.v_vector3.z) + ); + default: + return NULL; + } + case ARRAY: + switch (b->kind) { + case ARRAY: { + size_t a_len = a->data.v_array.size; + size_t b_len = b->data.v_array.size; + size_t length = a_len + b_len; + + snek_object_t *array = new_snek_array(vm, length); + + for (int i = 0; i < a_len; i++) { + snek_array_set(array, i, snek_array_get(a, i)); + } + + for (int i = 0; i < b_len; i++) { + snek_array_set(array, i + a_len, snek_array_get(b, i)); + } + + return array; + } + default: + return NULL; + } + default: + return NULL; + } +} diff --git a/src/11-09-trace/snekobject.h b/src/11-09-trace/snekobject.h new file mode 100644 index 0000000..fa70f55 --- /dev/null +++ b/src/11-09-trace/snekobject.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +void snek_object_free(snek_object_t *obj); + +bool snek_array_set(snek_object_t *array, size_t index, snek_object_t *value); +snek_object_t *snek_array_get(snek_object_t *array, size_t index); diff --git a/src/11-09-trace/stack.c b/src/11-09-trace/stack.c new file mode 100644 index 0000000..dd6c936 --- /dev/null +++ b/src/11-09-trace/stack.c @@ -0,0 +1,81 @@ +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-09-trace/stack.h b/src/11-09-trace/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-09-trace/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-09-trace/vm.c b/src/11-09-trace/vm.c new file mode 100644 index 0000000..aca5ed3 --- /dev/null +++ b/src/11-09-trace/vm.c @@ -0,0 +1,123 @@ +#include "bootlib.h" +#include "vm.h" +#include "stack.h" + +void trace(vm_t *vm) { + if (vm == NULL) return; + + my_stack_t *gray_objects = stack_new(8); + if (gray_objects == NULL) return; + + for (int i = 0; i < vm->objects->count; i++) { + snek_object_t *obj = (snek_object_t *)vm->objects->data[i]; + if (obj->is_marked) { + stack_push(gray_objects, obj); + } + } + + while (true) { + snek_object_t *obj = (snek_object_t *)stack_pop(gray_objects); + if ( obj == NULL) break; + trace_blacken_object(gray_objects, obj); + } + stack_free(gray_objects); +} + +void trace_blacken_object(my_stack_t *gray_objects, snek_object_t *obj) { + if (gray_objects == NULL || obj == NULL) return; + + switch (obj->kind) { + case INTEGER: + case FLOAT: + case STRING: + break; + case VECTOR3: { + snek_vector_t vec = obj->data.v_vector3; + trace_mark_object(gray_objects, vec.x); + trace_mark_object(gray_objects, vec.y); + trace_mark_object(gray_objects, vec.z); + break; + } + case ARRAY: { + snek_array_t arr = obj->data.v_array; + for (int i = 0; i < arr.size; i++) { + trace_mark_object(gray_objects, arr.elements[i]); + } + break; + } + default: + break; + }; +} + +void trace_mark_object(my_stack_t *gray_objects, snek_object_t *obj) { + if (gray_objects == NULL || obj == NULL) return; + if (obj->is_marked) return; + + obj->is_marked = true; + stack_push(gray_objects, obj); +} + +// don't touch below this line + +void mark(vm_t *vm) { + for (size_t i = 0; i < vm->frames->count; i++) { + frame_t *frame = vm->frames->data[i]; + for (size_t j = 0; j < frame->references->count; j++) { + snek_object_t *obj = frame->references->data[j]; + obj->is_marked = true; + } + } +} + +void frame_reference_object(frame_t *frame, snek_object_t *obj) { + stack_push(frame->references, obj); +} + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + // Free the stack frames, and then their container + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + + // Free the objects, and then their container + for (int i = 0; i < vm->objects->count; i++) { + snek_object_free(vm->objects->data[i]); + } + stack_free(vm->objects); + + boot_free(vm); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} + +void vm_track_object(vm_t *vm, snek_object_t *obj) { + stack_push(vm->objects, obj); +} diff --git a/src/11-09-trace/vm.h b/src/11-09-trace/vm.h new file mode 100644 index 0000000..f6590d2 --- /dev/null +++ b/src/11-09-trace/vm.h @@ -0,0 +1,41 @@ +#pragma once + +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + my_stack_t *frames; + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +/// Our main functions for garbage collection. +void mark(vm_t *vm); +void trace(vm_t *vm); +void sweep(vm_t *vm); + +/// Helper functions for `trace` +void trace_blacken_object(my_stack_t *gray_objects, snek_object_t *ref); +void trace_mark_object(my_stack_t *gray_objects, snek_object_t *ref); + +/// This is the function that gets called to actually do the garbage collection, +/// but is just composed of `mark`, `trace`, and `sweep`. +/// +/// Don't worry, it's not going to delete your code (hopefully!) +void vm_collect_garbage(vm_t *vm); + +/// Already implemented +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_new_frame(vm_t *vm); + +void frame_free(frame_t *frame); + +// Marks the object as referenced in the current stack frame. +void frame_reference_object(frame_t *frame, snek_object_t *obj); diff --git a/src/11-10-sweep/main.c b/src/11-10-sweep/main.c new file mode 100644 index 0000000..a24e10b --- /dev/null +++ b/src/11-10-sweep/main.c @@ -0,0 +1,99 @@ +#include +#include + +#include "bootlib.h" +#include "munit.h" +#include "sneknew.h" +#include "snekobject.h" +#include "vm.h" + +munit_case(RUN, test_simple, { + vm_t *vm = vm_new(); + frame_t *f1 = vm_new_frame(vm); + + snek_object_t *s = new_snek_string(vm, "I wish I knew how to read."); + frame_reference_object(f1, s); + vm_collect_garbage(vm); + // nothing should be collected because + // we haven't freed the frame + assert(!boot_is_freed(s)); + + frame_free(vm_frame_pop(vm)); + vm_collect_garbage(vm); + assert_true(boot_is_freed(s)); + + vm_free(vm); + assert_true(boot_all_freed()); +}); + +munit_case(SUBMIT, test_full, { + vm_t *vm = vm_new(); + frame_t *f1 = vm_new_frame(vm); + frame_t *f2 = vm_new_frame(vm); + frame_t *f3 = vm_new_frame(vm); + + snek_object_t *s1 = new_snek_string(vm, "This string is going into frame 1"); + frame_reference_object(f1, s1); + + snek_object_t *s2 = new_snek_string(vm, "This string is going into frame 2"); + frame_reference_object(f2, s2); + + snek_object_t *s3 = new_snek_string(vm, "This string is going into frame 3"); + frame_reference_object(f3, s3); + + snek_object_t *i1 = new_snek_integer(vm, 69); + snek_object_t *i2 = new_snek_integer(vm, 420); + snek_object_t *i3 = new_snek_integer(vm, 1337); + snek_object_t *v = new_snek_vector3( + vm, + i1, + i2, + i3 + ); + frame_reference_object(f2, v); + frame_reference_object(f3, v); + + assert_int( + vm->objects->count, + ==, + 7, + "Correct number of objects in the VM before GC" + ); + + // only free the top frame (f3) + frame_free(vm_frame_pop(vm)); + vm_collect_garbage(vm); + assert_true(boot_is_freed(s3)); + assert_false(boot_is_freed(s1)); + assert_false(boot_is_freed(s2)); + + // VM pass should free the string, but not the vector + // because its final frame hasn't been freed + frame_free(vm_frame_pop(vm)); + frame_free(vm_frame_pop(vm)); + vm_collect_garbage(vm); + assert_true(boot_is_freed(s1)); + assert_true(boot_is_freed(s2)); + assert_true(boot_is_freed(s3)); + assert_true(boot_is_freed(v)); + assert_true(boot_is_freed(i1)); + assert_true(boot_is_freed(i2)); + assert_true(boot_is_freed(i3)); + + assert_int(vm->objects->count, ==, 0, "No live objects remaining"); + + vm_free(vm); + assert_true(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/test_simple", test_simple), + munit_test("/test_full", test_full), + munit_null_test, + }; + + MunitSuite suite = munit_suite("mark-and-sweep", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/11-10-sweep/sneknew.c b/src/11-10-sweep/sneknew.c new file mode 100644 index 0000000..a2294f5 --- /dev/null +++ b/src/11-10-sweep/sneknew.c @@ -0,0 +1,96 @@ +#include "sneknew.h" +#include +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "vm.h" + +snek_object_t *_new_snek_object(vm_t *vm) { + snek_object_t *obj = boot_calloc(1, sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + obj->is_marked = false; + vm_track_object(vm, obj); + return obj; +} + +snek_object_t *new_snek_array(vm_t *vm, size_t size) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + snek_object_t **elements = boot_calloc(size, sizeof(snek_object_t *)); + if (elements == NULL) { + boot_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( + vm_t *vm, 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(vm); + 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(vm_t *vm, int value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + + return obj; +} + +snek_object_t *new_snek_float(vm_t *vm, float value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(vm_t *vm, char *value) { + snek_object_t *obj = _new_snek_object(vm); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = boot_malloc(len + 1); + if (dst == NULL) { + boot_free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/11-10-sweep/sneknew.h b/src/11-10-sweep/sneknew.h new file mode 100644 index 0000000..b998b82 --- /dev/null +++ b/src/11-10-sweep/sneknew.h @@ -0,0 +1,11 @@ +#pragma once +#include "snekobject.h" +#include "vm.h" + +snek_object_t *new_snek_integer(vm_t *vm, int value); +snek_object_t *new_snek_float(vm_t *vm, float value); +snek_object_t *new_snek_string(vm_t *vm, char *value); +snek_object_t *new_snek_vector3( + vm_t *vm, snek_object_t *x, snek_object_t *y, snek_object_t *z +); +snek_object_t *new_snek_array(vm_t *vm, size_t size); diff --git a/src/11-10-sweep/snekobject.c b/src/11-10-sweep/snekobject.c new file mode 100644 index 0000000..44ef6fa --- /dev/null +++ b/src/11-10-sweep/snekobject.c @@ -0,0 +1,143 @@ +#include + +#include "bootlib.h" +#include "snekobject.h" +#include "sneknew.h" + +void snek_object_free(snek_object_t *obj) { + switch (obj->kind) { + case INTEGER: + case FLOAT: + break; + case STRING: + boot_free(obj->data.v_string); + break; + case VECTOR3: { + break; + } + case ARRAY: { + boot_free(obj->data.v_array.elements); + break; + } + } + boot_free(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; + } + + // No need to change refcounts, we will find the garbage values + // later through mark-and-sweep. + + // 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 *snek_add(vm_t *vm, snek_object_t *a, snek_object_t *b) { + if (a == NULL || b == NULL) { + return NULL; + } + + switch (a->kind) { + case INTEGER: + switch (b->kind) { + case INTEGER: + return new_snek_integer(vm, a->data.v_int + b->data.v_int); + case FLOAT: + return new_snek_float(vm, (float)a->data.v_int + b->data.v_float); + default: + return NULL; + } + case FLOAT: + switch (b->kind) { + case FLOAT: + return new_snek_float(vm, a->data.v_float + b->data.v_float); + default: + return snek_add(vm, b, a); + } + case STRING: + switch (b->kind) { + case STRING: { + int a_len = strlen(a->data.v_string); + int b_len = strlen(b->data.v_string); + int len = a_len + b_len + 1; + char *dst = malloc(len * sizeof(char)); + dst[0] = '\0'; + + strcat(dst, a->data.v_string); + strcat(dst, b->data.v_string); + + snek_object_t *obj = new_snek_string(vm, dst); + boot_free(dst); + + return obj; + } + default: + return NULL; + } + case VECTOR3: + switch (b->kind) { + case VECTOR3: + return new_snek_vector3( + vm, + snek_add(vm, a->data.v_vector3.x, b->data.v_vector3.x), + snek_add(vm, a->data.v_vector3.y, b->data.v_vector3.y), + snek_add(vm, a->data.v_vector3.z, b->data.v_vector3.z) + ); + default: + return NULL; + } + case ARRAY: + switch (b->kind) { + case ARRAY: { + size_t a_len = a->data.v_array.size; + size_t b_len = b->data.v_array.size; + size_t length = a_len + b_len; + + snek_object_t *array = new_snek_array(vm, length); + + for (int i = 0; i < a_len; i++) { + snek_array_set(array, i, snek_array_get(a, i)); + } + + for (int i = 0; i < b_len; i++) { + snek_array_set(array, i + a_len, snek_array_get(b, i)); + } + + return array; + } + default: + return NULL; + } + default: + return NULL; + } +} diff --git a/src/11-10-sweep/snekobject.h b/src/11-10-sweep/snekobject.h new file mode 100644 index 0000000..fa70f55 --- /dev/null +++ b/src/11-10-sweep/snekobject.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +#include "stack.h" + +typedef struct SnekObject snek_object_t; + +typedef struct { + size_t size; + snek_object_t **elements; +} snek_array_t; + +typedef struct { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, + ARRAY, +} snek_object_kind_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 { + bool is_marked; + + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +void snek_object_free(snek_object_t *obj); + +bool snek_array_set(snek_object_t *array, size_t index, snek_object_t *value); +snek_object_t *snek_array_get(snek_object_t *array, size_t index); diff --git a/src/11-10-sweep/stack.c b/src/11-10-sweep/stack.c new file mode 100644 index 0000000..dd6c936 --- /dev/null +++ b/src/11-10-sweep/stack.c @@ -0,0 +1,81 @@ +#include + +#include "bootlib.h" +#include "munit.h" +#include "stack.h" + +void stack_push(my_stack_t *stack, void *obj) { + assert_ptr_not_null(obj, "must not have null obj"); + + if (stack->count == stack->capacity) { + // Double stack capacity to avoid reallocing often + stack->capacity *= 2; + stack->data = boot_realloc(stack->data, stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + // Unable to realloc, just exit :) get gud + exit(1); + } + } + + stack->data[stack->count] = obj; + stack->count++; + + return; +} + +void *stack_pop(my_stack_t *stack) { + if (stack->count == 0) { + return NULL; + } + + stack->count--; + return stack->data[stack->count]; +} + +void stack_free(my_stack_t *stack) { + if (stack == NULL) { + return; + } + + if (stack->data != NULL) { + boot_free(stack->data); + } + + boot_free(stack); +} + +void stack_remove_nulls(my_stack_t *stack) { + size_t new_count = 0; + + // Iterate through the stack and compact non-NULL pointers. + for (size_t i = 0; i < stack->count; ++i) { + if (stack->data[i] != NULL) { + stack->data[new_count++] = stack->data[i]; + } + } + + // Update the count to reflect the new number of elements. + stack->count = new_count; + + // Optionally, you might want to zero out the remaining slots. + for (size_t i = new_count; i < stack->capacity; ++i) { + stack->data[i] = NULL; + } +} + +my_stack_t *stack_new(size_t capacity) { + my_stack_t *stack = boot_malloc(sizeof(my_stack_t)); + if (stack == NULL) { + return NULL; + } + + stack->count = 0; + stack->capacity = capacity; + stack->data = boot_malloc(stack->capacity * sizeof(void *)); + if (stack->data == NULL) { + boot_free(stack); + return NULL; + } + + return stack; +} diff --git a/src/11-10-sweep/stack.h b/src/11-10-sweep/stack.h new file mode 100644 index 0000000..87f7b01 --- /dev/null +++ b/src/11-10-sweep/stack.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +typedef struct Stack { + size_t count; + size_t capacity; + void **data; +} my_stack_t; + +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); +void stack_remove_nulls(my_stack_t *stack); diff --git a/src/11-10-sweep/vm.c b/src/11-10-sweep/vm.c new file mode 100644 index 0000000..cc8546c --- /dev/null +++ b/src/11-10-sweep/vm.c @@ -0,0 +1,152 @@ +#include "bootlib.h" +#include "vm.h" +#include "snekobject.h" +#include "stack.h" + +void vm_collect_garbage(vm_t *vm) { + if (vm == NULL) return; + + mark(vm); + trace(vm); + sweep(vm); +} + +void sweep(vm_t *vm) { + if (vm == NULL) return; + + for (int i = 0; i < vm->objects->count; i++) { + snek_object_t *obj = (snek_object_t *)vm->objects->data[i]; + if (obj->is_marked) { + obj->is_marked = false; + continue; + } + snek_object_free(obj); + vm->objects->data[i] = NULL; + } + + stack_remove_nulls(vm->objects); +} + +// don't touch below this line + +void mark(vm_t *vm) { + for (size_t i = 0; i < vm->frames->count; i++) { + frame_t *frame = vm->frames->data[i]; + for (size_t j = 0; j < frame->references->count; j++) { + snek_object_t *obj = frame->references->data[j]; + obj->is_marked = true; + } + } +} + +void trace(vm_t *vm) { + my_stack_t *gray_objects = stack_new(8); + if (gray_objects == NULL) { + return; + } + + // Get previously marked objects (which are the roots) + for (int i = 0; i < vm->objects->count; i++) { + snek_object_t *obj = vm->objects->data[i]; + if (obj->is_marked) { + stack_push(gray_objects, obj); + } + } + + // Trace through the objects + while (gray_objects->count > 0) { + trace_blacken_object(gray_objects, stack_pop(gray_objects)); + } + + // Clean up after ourselves :) + stack_free(gray_objects); +} + +void trace_blacken_object(my_stack_t *gray_objects, snek_object_t *ref) { + snek_object_t *obj = ref; + + switch (obj->kind) { + case INTEGER: + case FLOAT: + case STRING: + break; + case VECTOR3: { + snek_vector_t vec = obj->data.v_vector3; + trace_mark_object(gray_objects, vec.x); + trace_mark_object(gray_objects, vec.y); + trace_mark_object(gray_objects, vec.z); + break; + } + case ARRAY: { + for (size_t i = 0; i < obj->data.v_array.size; i++) { + trace_mark_object(gray_objects, obj->data.v_array.elements[i]); + } + break; + } + } +} + +void trace_mark_object(my_stack_t *gray_objects, snek_object_t *obj) { + if (obj == NULL || obj->is_marked) { + return; + } + + stack_push(gray_objects, obj); + obj->is_marked = true; +} + +void frame_reference_object(frame_t *frame, snek_object_t *obj) { + stack_push(frame->references, obj); +} + +vm_t *vm_new() { + vm_t *vm = boot_malloc(sizeof(vm_t)); + if (vm == NULL) { + return NULL; + } + + vm->frames = stack_new(8); + vm->objects = stack_new(8); + return vm; +} + +void vm_free(vm_t *vm) { + // Free the stack frames, and then their container + for (int i = 0; i < vm->frames->count; i++) { + frame_free(vm->frames->data[i]); + } + stack_free(vm->frames); + + // Free the objects, and then their container + for (int i = 0; i < vm->objects->count; i++) { + snek_object_free(vm->objects->data[i]); + } + stack_free(vm->objects); + + boot_free(vm); +} + +void vm_frame_push(vm_t *vm, frame_t *frame) { + stack_push(vm->frames, frame); +} + +frame_t *vm_frame_pop(vm_t *vm) { + return stack_pop(vm->frames); +} + +frame_t *vm_new_frame(vm_t *vm) { + frame_t *frame = boot_malloc(sizeof(frame_t)); + frame->references = stack_new(8); + + vm_frame_push(vm, frame); + return frame; +} + +void frame_free(frame_t *frame) { + stack_free(frame->references); + boot_free(frame); +} + +void vm_track_object(vm_t *vm, snek_object_t *obj) { + stack_push(vm->objects, obj); +} diff --git a/src/11-10-sweep/vm.h b/src/11-10-sweep/vm.h new file mode 100644 index 0000000..6b92e06 --- /dev/null +++ b/src/11-10-sweep/vm.h @@ -0,0 +1,47 @@ +#pragma once + +#include "snekobject.h" +#include "stack.h" + +typedef struct VirtualMachine { + // stack frames: my_stack_t frame_t + my_stack_t *frames; + + // These are the rest of the objects: my_stack_t snek_object_t + my_stack_t *objects; +} vm_t; + +typedef struct StackFrame { + my_stack_t *references; +} frame_t; + +/// Our main functions for garbage collection. +void mark(vm_t *vm); +void trace(vm_t *vm); +void sweep(vm_t *vm); + +void vm_collect_garbage(vm_t *vm); + +/// Helper functions for `trace` +void trace_blacken_object(my_stack_t *gray_objects, snek_object_t *ref); +void trace_mark_object(my_stack_t *gray_objects, snek_object_t *ref); + +/// This is the function that gets called to actually do the garbage collection, +/// but is just composed of `mark`, `trace`, and `sweep`. +/// +/// Don't worry, it's not going to delete your code (hopefully!) +void vm_collect_garbage(vm_t *vm); + +/// Already implemented +vm_t *vm_new(); +void vm_free(vm_t *vm); +void vm_track_object(vm_t *vm, snek_object_t *obj); + +frame_t *vm_new_frame(vm_t *vm); +void vm_frame_push(vm_t *vm, frame_t *frame); +frame_t *vm_frame_pop(vm_t *vm); + +void frame_free(frame_t *frame); + +// Marks the object as referenced in the current stack frame. +void frame_reference_object(frame_t *frame, snek_object_t *obj); diff --git a/src/6-8-big-endian-little-endian/.DS_Store b/src/6-8-big-endian-little-endian/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0