###################################################### ## ## Garbage: ## ## An implementation of a general-purpose garbage ## collector based on Cheney's algorithm. Pointers ## within allocated memory regions must be swizzled ## and manipulated using the provided 'p@', 'p!' ## and 'p?' words. User code should avoid using ## '>ptr' and 'ptr>' conversion words explicitly ## as they may allow memory to be accidentally ## reclaimed. ## ## This system requires and a constant ## named 'heap-size' defined. ## ## John Earnest ## ###################################################### :const ptr-mask 0x60000000 :const ptr-bits 0x9fffffff :const ptr-flag 0x40000000 : >ptr ptr-flag or ; ( addr -- ptr ) : ptr> ptr-bits and ; ( ptr -- addr ) : p? ptr-mask and ptr-flag = ; ( n -- flag ) : p! ptr> ! ; ( n ptr -- ) : p@ ptr> @ ; ( ptr -- n ) : ps ptr> 1 - @ ; ( ptr -- size ) :vector gc-fail ( -- ) "heap exhausted!" typeln halt ; ###################################################### ## ## The GC proper ## ###################################################### :array heap1 heap-size 0 :array heap2 heap-size 0 :proto managed-begin :proto managed-end :var data-min :var data-max :var return-min :var return-max :data from heap1 :data to heap2 :data head heap1 : broken? ps 0 < ; ( ptr -- flag ) : >move ( src dst len -- ) #"move src: " type >r over . r> # "dst: " type over . # "len: " type dup . cr 1 - for over i + @ over i + ! next 2drop ; : gc-copy ( ptr -- ) dup broken? if drop exit then dup ptr> 1 - head @ over @ 1 + >move # copy the object head @ 1 + >ptr over p! # leave behind link to new location dup ps 1 + head @ + head ! # increment the free pointer ptr> 1 - dup @ -1 * swap ! # break a heart ; : gc-walk ( -- ) to @ head @ >= if exit then to @ loop dup @ p? if dup @ gc-copy dup @ p@ over ! then 1 + dup head @ < while drop ; : gc-scan ( min max -- ) over - for dup i + @ dup p? if dup gc-copy p@ over i + ! else drop then next drop ; : gc ( -- ) #"collecting garbage..." typeln DP @ data-max ! RP @ return-max ! to @ head ! data-min @ data-max @ gc-scan return-min @ return-max @ gc-scan ' managed-begin 1 + ' managed-end 1 - gc-scan gc-walk from @ to @ from ! to ! ; : gc-init ( -- ) DP @ data-min ! RP @ 1 - return-min ! ; : enough? head @ + 1 + from @ heap-size + <= ; ( size -- flag ) : alloc ( size -- ptr ) dup enough? -if gc then dup enough? -if gc-fail then >r head @ 1 + >ptr # calculate new pointer i head @ ! # store block size head @ 1 + i + head ! # update free index rdrop ; ###################################################### ## ## Tests ## ###################################################### ( : managed-begin ; :var A :var B : managed-end ; :var array-size : [ 0 array-size ! ; : , array-size @ 1 + array-size ! ; : ]array , array-size @ alloc ptr> array-size @ 1 - + >r array-size @ 1 - for j ! r> r> 1 - >r >r next r> 1 + >ptr ; : .array "[ " type dup ps 1 - for dup p@ . 1 + next drop "]" type ; : main gc-init [ 666 ]array B ! [ B @ , 60 ]array A ! 0 B ! A @ ptr> . cr [ 1 , 2 , 3 ]array .array cr [ 9 , 8 , 7 , 6 ]array .array cr [ 4 , 5 , 4 , 5 ]array .array cr [ 4 , 5 , 4 , 5 ]array .array cr A @ .array cr A @ p@ .array cr A @ ptr> . cr ; )