slice.rkt
#lang racket/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?)