/usr/share/scsh-0.6/scsh/stringcoll.scm is in scsh-common-0.6 0.6.7-8.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | ;;; Copyright (c) 1994 by Olin Shivers
;;; String collectors
;;; ===========================================================================
;;; string-colllector
;;; (make-string-collector)
;;; (collect-string! SC S)
;;; (clear-string-collector! SC)
;;; (string-collector->string SC)
;;;
;;; A string collector is a data structure that reduces the overhead of
;;; accumulating a large string in bits and pieces. It is basically a
;;; "chunk list," where a chunk is a string of at least 128 chars. In this
;;; way, the list overhead is kept under 2% of the whole data structure.
;;; When a new string is added to the collection, it is added to the current
;;; chunk. When the chunk reaches 128 chars, it is added to the chunk list,
;;; and a new chunk is started. If a large string is added to the collection,
;;; it is added as a chunk itself, so large strings are not split into small
;;; pieces. (Actually, a *copy* of the original large string is saved as a
;;; single chunk, so the collector's chunks are not shared with client data.)
;;;
;;; MAKE-STRING-COLLECTOR allocates a new string collector data structure.
;;; COLLECT-STRING! appends a string to the current collection.
;;; CLEAR-STRING-COLLECTOR! clears out accumulated strings from a collector.
;;; STRING-COLLECTOR->STRING converts a collector into a contiguous string.
;;;
;;; This facility makes it reasonably efficient to accumulate strings
;;; of any size in increments of any size.
(define-record string-collector
(len 0) ; How many chars have we accumulated?
(chunks '()) ; The chunk list.
(chunk #f) ; The current chunk being filled, if any.
(chunk-left 0)) ; How many chars left to fill in the current chunk.
(define (clear-string-collector! sc)
(set-string-collector:len sc 0)
(set-string-collector:chunks sc '())
(set-string-collector:chunk sc #f)
sc)
;;; (COLLECT-STRING! sc s)
;;; ----------------------
;;; S is a string. Append it to the string being collected in the
;;; string-collector SC.
;;;
;;; The algorithm:
;;; First, do nothing if S is the empty string. Otherwise:
;;; If there is a current chunk:
;;; Copy characters from S into it.
;;; If we filled up the chunk
;;; Put the chunk on the chunk list.
;;; Look at the remaining chars from S we haven't copied yet.
;;; If there a lot of characters left (>= 128)
;;; Save them as a single chunk on the chunk list.
;;; No current chunk.
;;; Else if there a just a few characters left (> 0, < 128)
;;; Start a new current chunk, copy the chars left into it.
;;; Else if there aren't any characters left
;;; No current chunk.
;;;
;;; If there is no current chunk:
;;; If there are a lot of characters in S (>= 128)
;;; Save a copy of S as a single chunk on the chunk list.
;;; Still no current chunk.
;;; Else if there are a few characters in S (> 0, < 128)
;;; Start a new current chunk, copy the S into it.
(define (collect-string! sc s)
(let ((slen (string-length s))
(chunk (string-collector:chunk sc))
(chunk-left (string-collector:chunk-left sc))
;; Add the chunk C to the collector's chunk list.
(push-chunk! (lambda (c)
(set-string-collector:chunks sc
(cons c (string-collector:chunks sc)))))
;; Copy nchars characters from src[j] to dest[i]
;; *Way* too much bounds checking going on in this loop.
(copy-substring! (lambda (dest i src j nchars)
(do ((i i (+ i 1))
(j j (+ j 1))
(nchars nchars (- nchars 1)))
((zero? nchars))
(string-set! dest i (string-ref src j))))))
(cond ((zero? slen)) ; Empty string, do nothing.
(chunk
(let ((ncopy (min slen chunk-left)))
(copy-substring! chunk (- 128 chunk-left) s 0 ncopy)
(if (> chunk-left slen)
(set-string-collector:chunk-left sc (- chunk-left slen))
;; Current chunk is full.
(let ((s-left (- slen chunk-left)))
(push-chunk! chunk) ; Push the current chunk.
;; Handle remaining chars from S that weren't copied into
;; the current chunk we just pushed:
(cond ((>= s-left 128)
;; A lot more chars left. Push them as one chunk.
(push-chunk! (substring s chunk-left slen))
(set-string-collector:chunk sc #f))
((> s-left 0)
;; A few more chars left. Start a new chunk.
(let ((new-chunk (make-string 128)))
(copy-substring! new-chunk 0 s chunk-left s-left)
(set-string-collector:chunk sc new-chunk)
(set-string-collector:chunk-left sc
(- 128 s-left))))
;; No more chars left. No current chunk.
(else (set-string-collector:chunk sc #f)))))))
(else ; No current chunk.
(if (>= slen 128) ; How many chars is S?
(push-chunk! (string-copy s)) ; A lot. Push as one chunk.
(let ((chunk (make-string 128))) ; Not many. Start a new chunk.
(set-string-collector:chunk sc chunk)
(copy-substring! chunk 0 s 0 slen)
(set-string-collector:chunk-left sc (- 128 slen))))))
;; We don't actually do anything with this, but we keep it updated anyway.
(set-string-collector:len sc (+ (string-collector:len sc) slen))
sc))
;;; A bummed version for collecting a single character.
(define (collect-char! sc c)
(let ((chunk (string-collector:chunk sc))
(chunk-left (string-collector:chunk-left sc)))
(cond (chunk
(string-set! chunk (- 128 chunk-left) c)
(cond ((> chunk-left 1)
(set-string-collector:chunk-left sc (- chunk-left 1)))
(else
(set-string-collector:chunks sc
(cons chunk (string-collector:chunks sc)))
(set-string-collector:chunk sc #f))))
(else
(let ((new-chunk (make-string 128 c)))
(set-string-collector:chunk-left sc 127)
(set-string-collector:chunk sc new-chunk)))))
;; We don't actually do anything with this, but we keep it updated anyway.
(set-string-collector:len sc (+ (string-collector:len sc) 1))
sc)
;;; Convert the data in the string-collector SC to a single contiguous
;;; string and return it.
(define (string-collector->string sc)
(let ((chunk (string-collector:chunk sc))
(chunks (string-collector:chunks sc)))
(apply string-append
(reverse (if chunk
(cons (substring chunk 0
(- 128
(string-collector:chunk-left sc)))
chunks)
chunks)))))
;;; It's too bad we can't side-effect the string-collector's chunk list
;;; to be a single chunk after this coalescing operation, but we don't
;;; want to share the string we return -- the user might side-effect it.
|