GameLib is a collection of libraries for creating applications in Cakelisp.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
8.8 KiB

;; Dynamic array
;; See Dependencies/stb/stb_ds.h for how to use this
;; In short, (var my-dynarray (* thing) null)
;; ...then have at it. (dynarray-free my-dynarray) once you're done
;;
;; Note that almost all dynarray functions are macros which may change the array pointer passed in,
;; which means you need to pass (* (* type)) if you are going to let another function manipulate it
(set-cakelisp-option cakelisp-src-dir "Dependencies/cakelisp/src")
(add-cakelisp-search-directory "Dependencies/cakelisp/runtime")
(import &comptime-only "CHelpers.cake")
(import &comptime-only "STB.cake")
(use-stb-ds clone-stb-headers-dynamic-array)
;; For dynstring
(c-import "<stdarg.h>" "<stdio.h>" "<assert.h>")
(def-c-function-alias dynarray-free arrfree) ;; (array (* T))
(def-c-function-alias dynarray-push arrput) ;; (array (* T) item T)
(def-c-function-alias dynarray-pop arrpop) ;; (array (* T) &return T)
(def-c-function-alias dynarray-insert arrins) ;; (array (* T) index int item T)
(def-c-function-alias dynarray-delete arrdel) ;; (array (* T) index int)
;; Delete array[index] and replace it with last item in array
(def-c-function-alias dynarray-delete-swap arrdelswap) ;; (array (* T) index int)
(def-c-function-alias dynarray-length arrlenu) ;; (array (* T) &return size_t)
(def-c-function-alias dynarray-capacity arrcap) ;; (array (* T) &return size_t)
(def-c-function-alias dynarray-set-length arrsetlen) ;; (array (* T))
;; Make room but don't change the length, so you can push without resize
(def-c-function-alias dynarray-set-capacity arrsetcap) ;; (array (* T))
;; Does not free, only sets length to 0
(defmacro dynarray-clear (array-to-clear any)
(tokenize-push output
(dynarray-set-length (token-splice array-to-clear) 0))
(return true))
(defmacro each-in-dynarray (dynarray any iterator-name symbol &rest body any)
(tokenize-push output
(c-for
(var (token-splice iterator-name) size_t 0)
;; We could hoist this out but it should be a quick op anyways
(< (token-splice iterator-name) (dynarray-length (token-splice dynarray)))
(incr (token-splice iterator-name))
(token-splice-rest body tokens)))
(return true))
(defmacro each-item-in-dynarray (dynarray any index-name symbol
item-name symbol item-type any
&rest body any)
(tokenize-push output
(each-in-dynarray (token-splice dynarray) (token-splice index-name)
(var (token-splice item-name item-type) (at (token-splice index-name dynarray)))
(token-splice-rest body tokens)))
(return true))
(defmacro each-item-addr-in-dynarray (dynarray any index-name symbol
item-name symbol ptr-to-item-type any
&rest body any)
(tokenize-push output
(each-in-dynarray (token-splice dynarray) (token-splice index-name)
(var (token-splice item-name ptr-to-item-type)
(addr (at (token-splice index-name dynarray))))
(token-splice-rest body tokens)))
(return true))
(defmacro dynarray-length-sizeof (dynarray any)
(tokenize-push output
(* (dynarray-length (token-splice dynarray))
(sizeof (at 0 (token-splice dynarray)))))
(return true))
(defmacro dynarray-capacity-sizeof (dynarray any)
(tokenize-push output
(* (dynarray-capacity (token-splice dynarray))
(sizeof (at 0 (token-splice dynarray)))))
(return true))
;; Dynamic string
;; Helpers for creating auto-resizing strings
;; The underlying data structure is DynamicArray. Its functions may be used on the string
;; Note that dynstrings never shrink, and are sized to powers of two
;; Use mainly for signaling you expect to be able to resize etc. the array
(def-type-alias-global dynstring (* char))
(def-type-alias-global dynstring-const (* (const char)))
;; Returns the length of the string, not including the null terminator
(defun dynstring-printf (str (* dynstring) format (* (const char))
&variable-arguments
&return int)
;; Start with something reasonable to work with
(unless (deref str)
(dynarray-set-capacity (deref str) 16))
(var num-chars-required int 0)
;; This will only run twice, thanks to vsnprintf returning the size needed
(while true
(var args va_list)
(va_start args format)
(set num-chars-required
(vsnprintf (deref str) (dynarray-capacity (deref str)) format args))
(va_end args)
(if (>= num-chars-required (dynarray-capacity (deref str)))
;; Grow the string. +1 for null terminator
(dynarray-set-capacity (deref str) (+ 1 num-chars-required))
;; Got the size right the first time (or a little over, oh well)
(break)))
;; dynarray doesn't know we're printing to it, so we must update length header to match
;; We do include the null terminator in the array length
(dynarray-set-length (deref str) (+ 1 num-chars-required))
(return num-chars-required))
;; Returns the length of the string, not including the null terminator
(defun dynstring-append (str (* dynstring) append-str (* (const char)) &return int)
;; Things will go bad if these aren't equal
(assert (!= (deref str) append-str))
;; Remove the null terminator
(when (> (dynstring-strlen (deref str)) 0)
(dynarray-pop (deref str)))
;; using strcat may be faster if it is optimized. I won't worry about it for now
(each-char-in-string-const append-str current-char
;; Will resize if necessary
(dynarray-push (deref str) (deref current-char)))
;; Add the null terminator
(dynarray-push (deref str) 0)
(return (dynstring-strlen (deref str))))
;; dynarray-length includes the null terminator. This function removes it
(defun dynstring-strlen (str dynstring-const &return size_t)
(var length size_t (dynarray-length str))
(return (? (> length 0)
(- length 1)
0)))
;; A very quick way to get a heap-allocated formatted string (quick for the programmer)
(defmacro dynstring-create-f (var-name symbol format-string string
&rest format-arguments any)
(tokenize-push output
(var (token-splice var-name) dynstring null)
(dynstring-printf (addr (token-splice var-name))
(token-splice format-string)
(token-splice-rest format-arguments tokens)))
(return true))
(comptime-cond
('auto-test
(c-import "stdio.h")
(defun test--dynamic-array (&return int)
(var my-dynarray (* int) null)
(dynarray-push my-dynarray 1)
(dynarray-push my-dynarray 2)
(dynarray-push my-dynarray 3)
(dynarray-push my-dynarray 4)
(dynarray-push my-dynarray 5)
(unless (and (= 5 (dynarray-length my-dynarray))
(= 2 (at 1 my-dynarray)))
(return 1))
(each-in-dynarray my-dynarray i (fprintf stderr "%d " (at i my-dynarray)))
(fprintf stderr "\nPop:\n")
(dynarray-pop my-dynarray)
(each-in-dynarray my-dynarray i (fprintf stderr "%d " (at i my-dynarray)))
(fprintf stderr "\nInsert:\n")
(dynarray-insert my-dynarray 1 3)
(each-in-dynarray my-dynarray i (fprintf stderr "%d " (at i my-dynarray)))
(fprintf stderr "\nDelete:\n")
(dynarray-delete my-dynarray 1)
(each-in-dynarray my-dynarray i (fprintf stderr "%d " (at i my-dynarray)))
(fprintf stderr "\nDelete swap:\n")
(dynarray-delete-swap my-dynarray 1)
(each-item-in-dynarray my-dynarray i current-int int (fprintf stderr "%d " current-int))
(fprintf stderr "\nItems with addresses:\n")
(each-item-addr-in-dynarray my-dynarray i current-int (* int)
(fprintf stderr "%d %p\n" (deref current-int) current-int))
(fprintf stderr "\nCapacity after all that: %d\n"
(type-cast (dynarray-capacity my-dynarray) int))
(fprintf stderr "Size of array: %d\n" (type-cast (dynarray-length-sizeof my-dynarray) int))
(dynarray-free my-dynarray)
(return 0))
(defun-local print-dynstring-details (str dynstring)
(fprintf stderr "dynstring: '%s'\n capacity: %d length: %d strlen: %d\n"
str
(type-cast (dynarray-capacity str) int)
(type-cast (dynarray-length str) int)
(type-cast (dynstring-strlen str) int)))
(defun test--dynamic-string (&return int)
(var my-string dynstring null)
(dynstring-printf (addr my-string) "The answer is %d" 42)
(print-dynstring-details my-string)
(dynstring-printf (addr my-string) "Short %d" 42)
(print-dynstring-details my-string)
(dynstring-append (addr my-string) "/")
(dynstring-append (addr my-string) "Test")
(print-dynstring-details my-string)
(dynstring-create-f my-quick-string "The answer is %d" 42)
(print-dynstring-details my-quick-string)
(var my-uninitialized-string dynstring null)
(dynstring-append (addr my-uninitialized-string) "/")
(dynarray-free my-uninitialized-string)
(dynarray-free my-string)
(dynarray-free my-quick-string)
(return 0))))