slice.ss
#lang scheme/base

(define (absolutely-write-bytes bytes output start end)
  (let loop ((cur start))
    (if (>= cur end) (- end start)
        (let ((num (write-bytes bytes output cur end)))
          (when (not (number? num))
            (error "Hit end of file while writing!" output))
          (loop (+ cur num))))))

(define (absolutely-read-bytes num input)
  (let loop ((left num) (result null))
    (if (<= left 0) (apply bytes-append (reverse result))
        (let ((bytes (read-bytes left input)))
          (when (eof-object? bytes)
            (error "Hit end of file while reading" input))
          (let ((left (- left (bytes-length bytes))))
            (loop left (cons bytes result)))))))

(define-struct slice (bytes start end) #:transparent)

(define (create (num 0))
  (make-slice (make-bytes num) 0 num))

(define (from-bytes bytes)
  (make-slice bytes 0 (bytes-length bytes)))

(define (subslice slice start end)
  (make-slice (slice-bytes slice) start end))

(define (to-bytes slice)
  (subbytes (slice-bytes slice) (slice-start slice) (slice-end slice)))

(define (write-slice slice (output (current-output-port)))
  (absolutely-write-bytes 
   (slice-bytes slice) output
   (slice-start slice)
   (slice-end slice)))

(define (read-slice num (input (current-input-port)))
  (from-bytes (absolutely-read-bytes num input)))

(define (read-slice! slice (input (current-input-port)))
  (read-bytes! (slice-bytes slice) input 
               (slice-start slice)
               (slice-end slice)))

(define (slice-length slice)
  (- (slice-end slice) (slice-start slice)))

(define (slice-append slices)
  (define bytes (make-bytes 
                 (foldl (λ (slice length) (+ length (slice-length slice))) 0 slices)))
  
  (let loop ((offset 0) (slices slices))
    (if (null? slices) (from-bytes bytes)
        (let ((slice (car slices)))
          (bytes-copy! bytes offset 
                       (slice-bytes slice) (slice-start slice) (slice-end slice))
          (loop
           (+ offset (slice-length slice))
           (cdr slices))))))

(provide
 (rename-out
  (read-slice read)
  (read-slice! read!)
  (write-slice write)
  (slice-length length)
  (slice-append append)
  (subslice sub)
  (slice-start start)
  (slice-bytes bytes)
  (slice-end end))
 create
 from-bytes
 to-bytes
 slice?)
; to-bytes should never need to use this.