Mercurial > core / lisp/lib/io/smart-buffer.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
a37b1d3371fc
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; io/smart-buffer.lisp --- Smart Octet Buffers 3 ;; This is ported from Fukamachi's SMART-BUFFER 7 (defpackage io/smart-buffer 10 (:export :*default-memory-limit* 19 :delete-temporary-files 20 :buffer-limit-exceeded)) 22 (in-package :io/smart-buffer) 24 (defvar *default-memory-limit* (expt 2 20)) 25 (defvar *default-disk-limit* (expt 2 30)) 27 (defvar *temporary-directory* 28 (uiop:ensure-directory-pathname 29 (merge-pathnames (format nil "smart-buffer-~36R" (random (expt 36 #-gcl 8 #+gcl 5))) 30 (uiop:default-temporary-directory)))) 32 (defstruct (smart-buffer (:conc-name :buffer-) 33 (:constructor %make-smart-buffer)) 34 (memory-limit *default-memory-limit*) 35 (disk-limit *default-disk-limit*) 38 (memory-buffer (make-concatenated-xsubseqs)) 41 (defun make-smart-buffer (&rest initargs &key memory-limit disk-limit &allow-other-keys) 42 (let ((buffer (apply #'%make-smart-buffer initargs))) 43 (when (and memory-limit 45 (< disk-limit memory-limit)) 46 (setf (buffer-memory-limit buffer) disk-limit)) 49 (define-condition buffer-limit-exceeded (error) 50 ((limit :initarg :limit 52 (:report (lambda (condition stream) 53 (format stream "Buffer exceeded the limit~:[~;~:*: ~A~]" 54 (slot-value condition 'limit))))) 56 (defun write-to-buffer (buffer seq &optional (start 0) (end (length seq))) 57 (check-type seq (array (unsigned-byte 8) (*))) 58 (incf (buffer-current-len buffer) (- end start)) 60 (if (buffer-on-memory-p buffer) 61 (xnconcf (buffer-memory-buffer buffer) (xsubseq seq start end)) 62 (with-open-file (out (buffer-disk-buffer buffer) 64 :element-type '(unsigned-byte 8) 66 (write-sequence seq out :start start :end end)))) 68 (defun check-limit (buffer) 70 ((and (buffer-on-memory-p buffer) 71 (< (buffer-memory-limit buffer) 72 (buffer-current-len buffer))) 73 (when (< (buffer-disk-limit buffer) 74 (buffer-current-len buffer)) 75 (error 'buffer-limit-exceeded :limit (buffer-disk-limit buffer))) 76 (setf (buffer-disk-buffer buffer) 77 (uiop:with-temporary-file (:stream stream :pathname tmp 78 :directory *temporary-directory* 80 :element-type '(unsigned-byte 8) 82 (typecase (buffer-memory-buffer buffer) 83 (null-concatenated-xsubseqs) 84 (t (write-sequence (coerce-to-sequence (buffer-memory-buffer buffer)) stream))) 86 (buffer-on-memory-p buffer) nil 87 (buffer-memory-buffer buffer) nil)) 88 ((and (not (buffer-on-memory-p buffer)) 89 (< (buffer-disk-limit buffer) 90 (buffer-current-len buffer))) 91 (error 'buffer-limit-exceeded :limit (buffer-disk-limit buffer))))) 93 (defun finalize-buffer (buffer) 94 (if (buffer-on-memory-p buffer) 95 (flexi-streams:make-in-memory-input-stream 96 (typecase (buffer-memory-buffer buffer) 97 (null-concatenated-xsubseqs #()) 98 (t (coerce-to-sequence (buffer-memory-buffer buffer))))) 99 (open (buffer-disk-buffer buffer) :direction :input :element-type '(unsigned-byte 8)))) 101 (defmacro with-smart-buffer ((buffer &key 102 (memory-limit '*default-memory-limit*) 103 (disk-limit '*default-disk-limit*)) 105 `(let ((,buffer (make-smart-buffer :memory-limit ,memory-limit :disk-limit ,disk-limit))) 107 (finalize-buffer ,buffer))) 109 (defun delete-stream-file (stream) 110 (when (typep stream 'file-stream) 111 (ignore-errors (delete-file (pathname stream)))) 114 (defun delete-temporary-files (&key (stale-seconds 0)) 115 (let ((now (get-universal-time))) 116 (mapc #'uiop:delete-file-if-exists 117 (remove-if-not (lambda (file) 118 (< stale-seconds (- now (file-write-date file)))) 119 (uiop:directory-files *temporary-directory*)))))