122-deep-copy.scm (5107B)
1 ; Generic deep-copy: structural clone of pairs / bytevectors / records, 2 ; with identity preservation across shared substructure and cycle 3 ; tolerance via an eager stand-in registered before slot fill. Used by 4 ; cc.scm to promote scratch-allocated parse output into main; mirror 5 ; here in scheme1 itself to lock down the contract. See 6 ; docs/DEEP-COPY.md. 7 ; 8 ; By design, deep-copy short-circuits when an object is already in the 9 ; current heap (so re-promotion is O(1)). The structural tests below 10 ; therefore allocate sources in scratch and copy with main current. 11 12 (define-record-type cell 13 (mk-cell head tail) 14 cell? 15 (head cell-head cell-head-set!) 16 (tail cell-tail cell-tail-set!)) 17 18 ;; ---- Already-in-target short-circuit returns the same object ---- 19 (define m-pair (cons 1 2)) 20 (define ctx0 (make-deep-copy-context)) 21 (if (eq? m-pair (deep-copy ctx0 m-pair)) 0 (sys-exit 1)) 22 23 ;; ---- Symbols pass through untouched (interned) ---- 24 (if (eq? 'foo (deep-copy ctx0 'foo)) 0 (sys-exit 2)) 25 26 ;; ---- Fixnums / immediates pass through ---- 27 (if (= 42 (deep-copy ctx0 42)) 0 (sys-exit 3)) 28 (if (eq? #t (deep-copy ctx0 #t)) 0 (sys-exit 4)) 29 (if (eq? '() (deep-copy ctx0 '())) 0 (sys-exit 5)) 30 31 ;; ---- Pair deep-copy from scratch -> main ---- 32 (use-scratch-heap!) 33 (define s-list (cons 1 (cons 2 (cons 3 '())))) 34 (use-main-heap!) 35 (define ctx1 (make-deep-copy-context)) 36 (define m-list (deep-copy ctx1 s-list)) 37 (if (equal? s-list m-list) 0 (sys-exit 10)) 38 (if (not (eq? s-list m-list)) 0 (sys-exit 11)) 39 (if (heap-in-main? m-list) 0 (sys-exit 12)) 40 (if (heap-in-main? (cdr m-list)) 0 (sys-exit 13)) 41 (reset-scratch-heap!) 42 ;; m-list survives scratch reset 43 (if (equal? m-list (cons 1 (cons 2 (cons 3 '())))) 0 (sys-exit 14)) 44 45 ;; ---- Bytevector deep-copy from scratch -> main ---- 46 (use-scratch-heap!) 47 (define s-bv (bytevector 1 2 3 4 5)) 48 (use-main-heap!) 49 (define ctx2 (make-deep-copy-context)) 50 (define m-bv (deep-copy ctx2 s-bv)) 51 (if (bytevector=? s-bv m-bv) 0 (sys-exit 20)) 52 (if (not (eq? s-bv m-bv)) 0 (sys-exit 21)) 53 (if (heap-in-main? m-bv) 0 (sys-exit 22)) 54 (reset-scratch-heap!) 55 ;; main-heap copy survives scratch reset 56 (if (= 5 (bytevector-length m-bv)) 0 (sys-exit 23)) 57 (if (= 1 (bytevector-u8-ref m-bv 0)) 0 (sys-exit 24)) 58 (if (= 5 (bytevector-u8-ref m-bv 4)) 0 (sys-exit 25)) 59 60 ;; ---- Record deep-copy from scratch -> main ---- 61 (use-scratch-heap!) 62 (define s-cell (mk-cell 10 20)) 63 (use-main-heap!) 64 (define ctx3 (make-deep-copy-context)) 65 (define m-cell (deep-copy ctx3 s-cell)) 66 (if (cell? m-cell) 0 (sys-exit 30)) 67 (if (not (eq? s-cell m-cell)) 0 (sys-exit 31)) 68 (if (= 10 (cell-head m-cell)) 0 (sys-exit 32)) 69 (if (= 20 (cell-tail m-cell)) 0 (sys-exit 33)) 70 ;; Same TD: TDs are persistent and not copied 71 (if (eq? (record-td s-cell) (record-td m-cell)) 0 (sys-exit 34)) 72 (if (heap-in-main? m-cell) 0 (sys-exit 35)) 73 (reset-scratch-heap!) 74 (if (= 10 (cell-head m-cell)) 0 (sys-exit 36)) 75 76 ;; ---- Identity preservation across shared subobjects ---- 77 (use-scratch-heap!) 78 (define s-shared (cons 'a 'b)) 79 (define s-x (cons s-shared s-shared)) 80 (use-main-heap!) 81 (define ctx4 (make-deep-copy-context)) 82 (define m-x (deep-copy ctx4 s-x)) 83 ;; Both halves of the result reference one fresh shared cons in main. 84 (if (eq? (car m-x) (cdr m-x)) 0 (sys-exit 40)) 85 (if (not (eq? (car m-x) s-shared)) 0 (sys-exit 41)) 86 (if (eq? 'a (car (car m-x))) 0 (sys-exit 42)) 87 (if (eq? 'b (cdr (car m-x))) 0 (sys-exit 43)) 88 (reset-scratch-heap!) 89 90 ;; ---- Cycle handling (record points to itself) ---- 91 (use-scratch-heap!) 92 (define s-cyc (mk-cell 1 #f)) 93 (cell-tail-set! s-cyc s-cyc) 94 (use-main-heap!) 95 (define ctx5 (make-deep-copy-context)) 96 (define m-cyc (deep-copy ctx5 s-cyc)) 97 (if (not (eq? s-cyc m-cyc)) 0 (sys-exit 50)) 98 (if (= 1 (cell-head m-cyc)) 0 (sys-exit 51)) 99 (if (eq? m-cyc (cell-tail m-cyc)) 0 (sys-exit 52)) 100 (reset-scratch-heap!) 101 ;; m-cyc still its own tail after scratch reset 102 (if (eq? m-cyc (cell-tail m-cyc)) 0 (sys-exit 53)) 103 104 ;; ---- Mixed pair-of-record graph ---- 105 (use-scratch-heap!) 106 (define s-rec (mk-cell 9 (cons 1 (cons 2 '())))) 107 (use-main-heap!) 108 (define ctx6 (make-deep-copy-context)) 109 (define m-rec (deep-copy ctx6 s-rec)) 110 (if (not (eq? s-rec m-rec)) 0 (sys-exit 60)) 111 (if (= 9 (cell-head m-rec)) 0 (sys-exit 61)) 112 (if (equal? (cell-tail m-rec) '(1 2)) 0 (sys-exit 62)) 113 (if (heap-in-main? m-rec) 0 (sys-exit 63)) 114 (if (heap-in-main? (cell-tail m-rec)) 0 (sys-exit 64)) 115 (reset-scratch-heap!) 116 (if (equal? (cell-tail m-rec) '(1 2)) 0 (sys-exit 65)) 117 118 ;; ---- ctx reuse: second pass over the same already-copied root is O(1) 119 ;; via heap-in-current?, returning the main-heap object directly. 120 (define ctx7 (make-deep-copy-context)) 121 (define copy-once (deep-copy ctx7 (cons 'a (cons 'b '())))) 122 (define copy-again (deep-copy ctx7 copy-once)) 123 (if (eq? copy-once copy-again) 0 (sys-exit 70)) 124 125 (sys-exit 42)