output-stream.sl 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. %
  3. % Output-Stream.SL (TOPS-20 Version) - File Output Stream Objects
  4. %
  5. % Author: Alan Snyder
  6. % Hewlett-Packard/CRC
  7. % Date: 29 July 1982
  8. %
  9. % This package is 6.7 times faster than the standard unbuffered I/O.
  10. % (Using message passing, it is only 1.9 times faster.)
  11. %
  12. % Note: this code will only run COMPILED.
  13. %
  14. % See TESTING code at the end of this file for examples of use.
  15. % Be sure to include "(CompileTime (load objects))" at the beginning
  16. % of any file that uses this package.
  17. %
  18. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  19. (CompileTime (load fast-int fast-vectors fast-strings))
  20. (BothTimes (load objects jsys))
  21. (de attempt-to-open-output (file-name)
  22. (let ((p (ErrorSet (list 'open-output file-name) NIL NIL)))
  23. (and (PairP p) (car p))
  24. ))
  25. (de attempt-to-open-append (file-name)
  26. (let ((p (ErrorSet (list 'open-append file-name) NIL NIL)))
  27. (and (PairP p) (car p))
  28. ))
  29. (de open-output (file-name)
  30. (let ((s (make-instance 'output-stream)))
  31. (=> s open file-name)
  32. s))
  33. (de open-append (file-name)
  34. (let ((s (make-instance 'output-stream)))
  35. (=> s open-append file-name)
  36. s))
  37. (defconst FILE-BUFFER-SIZE #.(* 5 512))
  38. (defflavor output-stream ((jfn NIL) % TOPS-20 file number
  39. ptr % "pointer" to next free slot in buffer
  40. file-name % full name of actual file
  41. buffer % output buffer
  42. )
  43. ()
  44. (gettable-instance-variables file-name)
  45. )
  46. (CompileTime (put 'SOUT 'OpenCode '((jsys 43) (move (reg 1) (reg 3)))))
  47. (CompileTime (put 'CLOSF 'OpenCode '((jsys 18) (move (reg 1) (reg 1)))))
  48. (defmethod (output-stream putc) (ch)
  49. % Append the character CH to the file. Line termination is indicated by
  50. % writing a single NEWLINE (LF) character.
  51. % Implementation note: It was determined by experiment that the PSL
  52. % compiler produces much better code if there are no function calls other
  53. % than tail-recursive ones. That's why this function is written the way
  54. % it is.
  55. (if (= ch #\LF)
  56. (=> self put-newline)
  57. % Otherwise:
  58. (string-store buffer ptr ch)
  59. (if (>= (setf ptr (+ ptr 1)) (const FILE-BUFFER-SIZE))
  60. (=> self flush))
  61. ))
  62. (defmethod (output-stream put-newline) ()
  63. % Output a line terminator.
  64. (string-store buffer ptr #\CR)
  65. (if (>= (setf ptr (+ ptr 1)) (const FILE-BUFFER-SIZE))
  66. (=> self flush))
  67. (string-store buffer ptr #\LF)
  68. (if (>= (setf ptr (+ ptr 1)) (const FILE-BUFFER-SIZE))
  69. (=> self flush))
  70. )
  71. (defmethod (output-stream putc-image) (ch)
  72. % Append the character CH to the file. No translation of LF character.
  73. (string-store buffer ptr ch)
  74. (if (>= (setf ptr (+ ptr 1)) (const FILE-BUFFER-SIZE))
  75. (=> self flush))
  76. )
  77. (defmethod (output-stream puts) (str)
  78. % Write string to output stream (highly optimized!)
  79. (let ((i 0)
  80. (high (string-upper-bound str))
  81. )
  82. (while (<= i high)
  83. (string-store buffer ptr (string-fetch str i))
  84. (if (>= (setf ptr (+ ptr 1)) (const FILE-BUFFER-SIZE))
  85. (=> self flush))
  86. (setf i (+ i 1))
  87. )))
  88. (defmethod (output-stream putl) (str)
  89. % Write string followed by line terminator to output stream.
  90. (=> self puts str)
  91. (=> self put-newline)
  92. )
  93. (defmethod (output-stream open) (name-of-file)
  94. % Open the specified file for output via SELF. If the file cannot
  95. % be opened, a Continuable Error is generated.
  96. (if jfn (=> self close))
  97. (setf jfn (Dec20Open name-of-file
  98. (int2sys 2#100000000000000001000000000000000000)
  99. (int2sys 2#000111000000000000001000000000000000)
  100. ))
  101. (if (= jfn 0) (setf jfn NIL))
  102. (if (null JFN)
  103. (=> self open
  104. (ContinuableError 0
  105. (BldMsg "Unable to Open '%w' for Output" name-of-file)
  106. name-of-file))
  107. (=> self &fixup)
  108. ))
  109. (defmethod (output-stream open-append) (name-of-file)
  110. % Open the specified file for append output via SELF. If the file cannot
  111. % be opened, a Continuable Error is generated.
  112. (if jfn (=> self close))
  113. (setf jfn (Dec20Open name-of-file
  114. (int2sys 2#000000000000000001000000000000000000)
  115. (int2sys 2#000111000000000000000010000000000000)
  116. ))
  117. (if (= jfn 0) (setf jfn NIL))
  118. (if (null JFN)
  119. (=> self open-append
  120. (ContinuableError 0
  121. (BldMsg "Unable to Open '%w' for Append" name-of-file)
  122. name-of-file))
  123. (=> self &fixup)
  124. ))
  125. (defmethod (output-stream attach-to-jfn) (new-jfn)
  126. % Attach the output-stream to the specified JFN.
  127. (if jfn (=> self close))
  128. (setf jfn new-jfn)
  129. (=> self &fixup)
  130. )
  131. (defmethod (output-stream &fixup) ()
  132. % Internal method for initializing instance variables after setting JFN.
  133. (setf buffer (make-string (const FILE-BUFFER-SIZE) #\space))
  134. % It is necessary to clear out the low-order bit, lest some programs
  135. % think we are writing "line numbers" (what a crock!).
  136. (for (from i 0 (- (/ (const FILE-BUFFER-SIZE) 5) 1))
  137. (do (vector-store buffer i 0)))
  138. (setf ptr 0)
  139. (setf file-name (jfn-truename jfn))
  140. )
  141. (defmethod (output-stream close) ()
  142. (when jfn
  143. (=> self flush)
  144. (CLOSF jfn)
  145. (setf jfn NIL)
  146. (setf buffer NIL)
  147. ))
  148. (defmethod (output-stream flush) ()
  149. (when (> ptr 0)
  150. (SOUT jfn (jconv buffer) (- ptr))
  151. (setf ptr 0)
  152. ))
  153. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  154. % TESTING CODE
  155. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  156. (CompileTime
  157. (setf time-output-test-string "This is a line of text for testing."))
  158. (CommentOutCode (progn
  159. (de time-buffered-output (n-lines)
  160. % This is the FAST way to do buffered output.
  161. (setf start-time (time))
  162. (setf s (open-output "test.output"))
  163. (for (from i 1 n-lines 1)
  164. (do (for (in ch '#.(String2List time-output-test-string))
  165. (do (output-stream$putc s ch))
  166. )
  167. (output-stream$put-newline s)
  168. ))
  169. (=> s close)
  170. (- (time) start-time)
  171. )
  172. (de time-buffered-output-1 (n-lines)
  173. % This is the SLOW (but GENERAL) way to do buffered output.
  174. (setf start-time (time))
  175. (setf s (open-output "test.output"))
  176. (for (from i 1 n-lines 1)
  177. (do (for (in ch '#.(String2List time-output-test-string))
  178. (do (=> s putc ch))
  179. )
  180. (=> s put-newline)
  181. ))
  182. (=> s close)
  183. (- (time) start-time)
  184. )
  185. (de time-standard-output (n-lines)
  186. (setf start-time (time))
  187. (setf chan (open "test.output" 'OUTPUT))
  188. (for (from i 1 n-lines 1)
  189. (do (for (in ch '#.(String2List time-output-test-string))
  190. (do (ChannelWriteChar chan ch))
  191. )
  192. (ChannelWriteChar chan #\LF)
  193. ))
  194. (close chan)
  195. (- (time) start-time)
  196. )
  197. (de time-output (n-lines)
  198. (list
  199. (time-buffered-output-string n-lines)
  200. (time-buffered-output n-lines)
  201. (time-buffered-output-1 n-lines)
  202. (time-standard-output n-lines)
  203. ))
  204. (de time-buffered-output-string (n-lines)
  205. % This is the FAST way to do buffered output from strings.
  206. (setf start-time (time))
  207. (setf s (open-output "test.output"))
  208. (for (from i 1 n-lines 1)
  209. (do (output-stream$putl s #.time-output-test-string))
  210. )
  211. (=> s close)
  212. (- (time) start-time)
  213. )
  214. )) % End CommentOutCode