output-stream.sl 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. (BothTimes (load objects jsys))
  20. (defun open-output (file-name)
  21. (let ((s (make-instance 'output-stream)))
  22. (=> s open file-name)
  23. s))
  24. (defun open-append (file-name)
  25. (let ((s (make-instance 'output-stream)))
  26. (=> s open-append file-name)
  27. s))
  28. %(CompileTime (setq *pgwd t))
  29. (CompileTime (setq FILE-BUFFER-SIZE (* 5 512)))
  30. (defflavor output-stream ((jfn NIL) % TOPS-20 file number
  31. ptr % "pointer" to next free slot in buffer
  32. file-name % full name of actual file
  33. buffer % output buffer
  34. )
  35. ()
  36. (gettable-instance-variables file-name)
  37. )
  38. (CompileTime (put 'sout 'OpenCode '((jsys 43) (move (reg 1) (reg 3)))))
  39. (CompileTime (put 'closf 'OpenCode '((jsys 18) (move (reg 1) (reg 1)))))
  40. (defmethod (output-stream putc) (ch)
  41. % Append the character CH to the file. Line termination
  42. % is indicated by writing a single NEWLINE (LF) character.
  43. (if (WEq ch (char lf))
  44. (output-stream$put-newline self)
  45. (iputs buffer ptr ch)
  46. (if (WGEQ (setf ptr (wplus2 ptr 1)) #.FILE-BUFFER-SIZE)
  47. (output-stream$flush self))
  48. ))
  49. % The above function was coded to produce good compiled code
  50. % using the current PSL compiler. Here's the output. Note
  51. % that no stack variables are used. The main path uses 16
  52. % instructions.
  53. % (*ENTRY OUTPUT-STREAM$PUTC EXPR 2)
  54. % (MOVE (REG 4) (REG 1))
  55. % (CAIE (REG 2) 10)
  56. % (JRST G0004)
  57. % (JRST (ENTRY OUTPUT-STREAM$PUT-NEWLINE))
  58. % G0004 (MOVE (REG 3) (REG 2))
  59. % (MOVE (REG 2) (INDEXED (REG 1) 5))
  60. % (MOVE (REG 1) (INDEXED (REG 1) 4))
  61. % (AOS (REG 1))
  62. % (ADJBP (REG 2) "L0008")
  63. % (DPB (REG 3) (REG 2))
  64. % (MOVE (REG 1) (INDEXED (REG 4) 5))
  65. % (AOS (REG 1))
  66. % (MOVEM (REG 1) (INDEXED (REG 4) 5))
  67. % (CAIGE (REG 1) 2560)
  68. % (JRST G0007)
  69. % (MOVE (REG 1) (REG 4))
  70. % (JRST (ENTRY OUTPUT-STREAM$FLUSH))
  71. % G0007 (MOVE (REG 1) (REG NIL))
  72. % (POPJ (REG ST) 0)
  73. % L0008 (FULLWORD (FIELDPOINTER (INDEXED (REG 1) 0) 0 7))
  74. (defmethod (output-stream put-newline) ()
  75. % Output a line terminator.
  76. (iputs buffer ptr (char cr))
  77. (if (WGEQ (setf ptr (wplus2 ptr 1)) #.FILE-BUFFER-SIZE)
  78. (output-stream$flush self))
  79. (iputs buffer ptr (char lf))
  80. (if (WGEQ (setf ptr (wplus2 ptr 1)) #.FILE-BUFFER-SIZE)
  81. (output-stream$flush self))
  82. )
  83. (defmethod (output-stream puts) (str)
  84. % Write string to output stream (highly optimized!)
  85. (let ((i 0)
  86. (high (isizes str))
  87. )
  88. (while (WLEQ i high)
  89. (iputs buffer ptr (igets str i))
  90. (if (WGEQ (setf ptr (wplus2 ptr 1)) #.FILE-BUFFER-SIZE)
  91. (output-stream$flush self))
  92. (setq i (WPlus2 i 1))
  93. )))
  94. (defmethod (output-stream putl) (str)
  95. % Write string followed by line terminator to output stream.
  96. (output-stream$puts self str)
  97. (output-stream$put-newline self)
  98. )
  99. (defmethod (output-stream open) (name-of-file)
  100. % Open the specified file for output via SELF. If the file cannot
  101. % be opened, a Continuable Error is generated.
  102. (if jfn (output-stream$close self))
  103. (setf buffer (MkString #.FILE-BUFFER-SIZE (char space)))
  104. (setf ptr 0)
  105. (setf jfn (Dec20Open name-of-file
  106. (int2sys 2#100000000000000001000000000000000000)
  107. (int2sys 2#000111000000000000001000000000000000)
  108. ))
  109. (if (= jfn 0) (setf jfn NIL))
  110. (if (null JFN)
  111. (=> self open
  112. (ContinuableError 0
  113. (BldMsg "Unable to Open '%w' for Output" name-of-file)
  114. name-of-file))
  115. (setf file-name (MkString 200 (char space)))
  116. (jsys1 file-name jfn #.(bits 2 5 8 11 14 35) 0 #.(get 'jsJFNS 'NewNam))
  117. (setf file-name (recopystringtonull file-name))
  118. ))
  119. (defmethod (output-stream open-append) (name-of-file)
  120. % Open the specified file for append output via SELF. If the file cannot
  121. % be opened, a Continuable Error is generated.
  122. (if jfn (output-stream$close self))
  123. (setf buffer (MkString #.FILE-BUFFER-SIZE (char space)))
  124. (setf ptr 0)
  125. (setf jfn (Dec20Open name-of-file
  126. (int2sys 2#000000000000000001000000000000000000)
  127. (int2sys 2#000111000000000000000010000000000000)
  128. ))
  129. (if (= jfn 0) (setf jfn NIL))
  130. (if (null JFN)
  131. (=> self open
  132. (ContinuableError 0
  133. (BldMsg "Unable to Open '%w' for Append" name-of-file)
  134. name-of-file))
  135. (setf file-name (MkString 200 (char space)))
  136. (jsys1 file-name jfn #.(bits 2 5 8 11 14 35) 0 #.(get 'jsJFNS 'NewNam))
  137. (setf file-name (recopystringtonull file-name))
  138. ))
  139. (defmethod (output-stream close) ()
  140. (if jfn (progn
  141. (output-stream$flush self)
  142. (closf jfn)
  143. (setf jfn NIL)
  144. (setf buffer NIL)
  145. )))
  146. (defmethod (output-stream flush) ()
  147. (if (WGreaterP ptr 0)
  148. (progn
  149. (sout jfn (jconv buffer) (WDifference 0 ptr))
  150. (setf ptr 0)
  151. ))
  152. )
  153. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  154. % TESTING CODE
  155. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  156. (CompileTime
  157. (setq 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. (setq start-time (time))
  162. (setq 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. (setq start-time (time))
  175. (setq 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. (setq start-time (time))
  187. (setq 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 (char 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. (setq start-time (time))
  207. (setq 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