Login
7 branches 0 tags
Ben (X13/Arch) Fixed memory issue 1d2f25c 2 years ago 964 Commits
nujel / stdlib / string / port.nuj
;;; Nujel - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
;;; This project uses the MIT license, a copy should be included under /LICENSE
;;;
;;; The port system for Nujel which provides an object oriented abstraction over
;;; low-level file streams

(def make-output-port #nil)
(def make-string-output-port #nil)
(def make-input-port #nil)
(let*
  (def temporary-buffer (buffer/allocate 1))
  (def temporary-buffer-view (buffer/u8* temporary-buffer))

  (def output-port-method-table {:flush-output (fn (handle)
                                                   (file/flush* handle))
                                               :block-write (fn (handle buffer size)
                                                                (file/write* handle buffer size))
                                               :char-write (fn (handle char)
                                                               (buffer/set! temporary-buffer-view 0 char)
                                                             (file/write* handle temporary-buffer-view))
                                               :close! (fn (handle)
                                                           (file/close* handle))
                                               :position (fn (handle)
                                                             (file/tell* handle))
                                               :position! (fn (handle new-position)
                                                              (file/seek* handle new-position 0))
                                               :file-handle (fn (handle) handle)
                                               :methods (fn (handle) output-port-method-table)})

  (def string-out-methods {:block-write (fn (self buffer size)
                                            (def buf (meta self :buffer))
                                          (when-not size (set! size (buffer/length buffer)))
                                          (when (> size
                                                   (- (buffer/length buf)
                                                      (meta self :position)))
                                            (buffer/length! buf
                                                            (+ #x80 (bit-and (+ (buffer/length buf) size) (bit-not #x7F)))))
                                          (buffer/copy buf buffer (meta self :position) size)
                                          (meta! self :position (+ (meta self :position) size)))
                                        :char-write (fn (self char)
                                                        (def buf (meta self :buffer))
                                                      (when (< (- (buffer/length buf)
                                                                  (meta self :position))
                                                               1)
                                                        (buffer/length! buf
                                                                        (+ #x80 (buffer/length buf))))
                                                      (buffer/set! buf (meta self :position) char)
                                                      (meta! self :position (+ (meta self :position) 1)))
                                        :write (fn (self . buffers)
                                                   (doseq (buf buffers self)
                                                          (self :block-write buf)))
                                        :close! (fn (self) self)
                                        :return-string (fn (self)
                                                           (buffer->string (meta self :buffer)
                                                                           (meta self :position)))
                                        :methods (fn (self) string-out-methods)})

  (def input-port-method-table {:block-read (fn (handle buffer size)
                                                (when (file/eof*? handle)
                                                  (return :end-of-file))
                                              (file/read* handle buffer size))
                                            :char-read (fn (handle char)
                                                           (when (file/eof*? handle)
                                                             (return :end-of-file))
                                                         (when-not (file/read* handle temporary-buffer-view 1)
                                                                   (return :end-of-file))
                                                         (ref temporary-buffer-view 0))
                                            :raw! (fn (handle)
                                                      (file/raw* handle #t))
                                            :close! (ref output-port-method-table :close!)
                                            :position (ref output-port-method-table :position)
                                            :position! (ref output-port-method-table :position!)
                                            :file-handle (ref output-port-method-table :file-handle)
                                            :methods (fn (handle) input-port-method-table)})

  (set! make-output-port (fn (handle)
                             "Create a new output port for HANDLE"
                           (fn (method . args)
                               (apply (ref output-port-method-table method) (cons handle args)))))

  (set! make-string-output-port (fn ()
                                    "Create a new string output port"
                                  (def self #nil)
                                  (set! self (fn (method . args)
                                                 (apply (ref string-out-methods method)
                                                        (cons self args))))
                                  (meta! self :buffer (buffer/allocate #x80))
                                  (meta! self :position 0)))

  (set! make-input-port (fn (handle)
                            "Create a new input port for HANDLE"
                          (fn (method . args)
                              (apply (ref input-port-method-table method) (cons handle args))))))

(defmacro with-string-port (name . body)
          `(let ((~name (make-string-output-port)))
                ~@body
                (~name 'return-string)))

(defn cat l
      "ConCATenates all arguments into a single string"
      (def p (make-string-output-port))
      (doseq (c l (p 'return-string))
             (p 'block-write (string/display c))))