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.
171 lines
6.8 KiB
171 lines
6.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-append 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))
|
|
|
|
(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))
|
|
|
|
;; 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))
|
|
|
|
;; 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-concat (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-append (deref str) (deref current-char)))
|
|
|
|
;; Add the null terminator
|
|
(dynarray-append (deref str) 0)
|
|
(return (dynstring-strlen (deref str))))
|
|
|
|
;; dynarray-length includes the null terminator. This function removes it
|
|
(defun dynstring-strlen (str dynstring &return size_t)
|
|
(return (- (dynarray-length str) 1)))
|
|
|
|
(defmacro make-dynstring-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-append my-dynarray 1)
|
|
(dynarray-append my-dynarray 2)
|
|
(dynarray-append my-dynarray 3)
|
|
(dynarray-append my-dynarray 4)
|
|
(dynarray-append 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-in-dynarray my-dynarray i (fprintf stderr "%d " (at i my-dynarray)))
|
|
|
|
(fprintf stderr "\nCapacity after all that: %d\n"
|
|
(type-cast (dynarray-capacity 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-concat (addr my-string) "/")
|
|
(dynstring-concat (addr my-string) "Test")
|
|
(print-dynstring-details my-string)
|
|
|
|
(make-dynstring-f my-quick-string "The answer is %d" 42)
|
|
(print-dynstring-details my-quick-string)
|
|
|
|
(dynarray-free my-string)
|
|
(dynarray-free my-quick-string)
|
|
(return 0))))
|
|
|