input-stream.sl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. %
  3. % Input-Stream.SL (TOPS-20 Version) - File Input Stream Objects
  4. %
  5. % Author: Alan Snyder
  6. % Hewlett-Packard/CRC
  7. % Date: 29 July 1982
  8. %
  9. % This package is 6.6 times faster than the standard unbuffered I/O.
  10. % (Using message passing, it is only 1.7 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. % Summary of public functions:
  19. %
  20. % (setf s (open-input "file name")) % generates error on failure
  21. % (setf s (attempt-to-open-input "file name")) % returns NIL on failure
  22. % (setf ch (=> s getc)) % read character (map CRLF to LF)
  23. % (setf ch (=> s getc-image)) % read character (don't map CRLF to LF)
  24. % (setf ch (=> s peekc)) % peek at next character
  25. % (setf ch (=> s peekc-image)) % peek at next character (don't map CRLF to LF)
  26. % (setf str (=> s getl)) % Read a line; return string without terminating LF.
  27. % (=> s empty?) % Are there no more characters?
  28. % (=> s close) % Close the file.
  29. % (setf fn (=> s file-name)) % Return "true" name of file.
  30. % (setf date (=> s read-date)) % Return date that file was last read.
  31. % (setf date (=> s write-date)) % Return date that file was last written.
  32. % (=> s delete-file) % Delete the associated file.
  33. % (=> s undelete-file) % Undelete the associated file.
  34. % (=> s delete-and-expunge) % Delete and expunge the associated file.
  35. % (setf name (=> s author)) % Return the name of the file's author.
  36. % (setf name (=> s original-author)) % Return the original author's name.
  37. % (setf count (=> s file-length)) % Return the byte count of the file.
  38. %
  39. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  40. %
  41. % Changes:
  42. %
  43. % 9/29/82 Alan Snyder
  44. % Changed GETC to return stray CRs.
  45. % Now uses (=> self ...) form (produces same object code).
  46. % Added operations PEEKC-IMAGE, GETL, TELL-POSITION, SEEK-POSITION
  47. % (written by Nancy Kendzierski).
  48. %
  49. % 11/22/82 Alan Snyder
  50. % Changed SEEK-POSITION to work with large byte pointers (> 256K).
  51. %
  52. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  53. (CompileTime (load fast-int fast-strings))
  54. (BothTimes (load objects jsys))
  55. (load directory file-support)
  56. (de attempt-to-open-input (file-name)
  57. (let ((p (ErrorSet (list 'open-input file-name) NIL NIL)))
  58. (and (PairP p) (car p))
  59. ))
  60. (de open-input (file-name)
  61. (let ((s (make-instance 'input-stream)))
  62. (=> s open file-name)
  63. s))
  64. (DefConst FILE-BUFFER-SIZE #.(* 5 512))
  65. (defflavor input-stream ((jfn NIL) % TOPS-20 file number
  66. ptr % "pointer" to next char in buffer
  67. count % number of valid chars in buffer
  68. eof-flag % T => this bufferfull is the last
  69. file-name % full name of actual file
  70. buffer % input buffer
  71. )
  72. ()
  73. (gettable-instance-variables file-name)
  74. )
  75. % Note: The JSYS function can't be used for the 'SIN' JSYS because the JSYS
  76. % function handles errors. The 'SIN' JSYS will report an error on end-of-file
  77. % if errors are being handled. We don't want that to happen!
  78. (CompileTime (progn
  79. (put 'SIN 'OpenCode '((jsys 8#52) (move (reg 1) (reg 3))))
  80. (put 'BIN 'OpenCode '((jsys 8#50) (move (reg 1) (reg 2))))
  81. (put 'CLOSF 'OpenCode '((jsys 8#22) (move (reg 1) (reg 1))))
  82. (put 'RFPTR 'OpenCode '((jsys 8#43) (jfcl) (move (reg 1) (reg 2))))
  83. (put 'SFPTR 'OpenCode '((jsys 8#27) (jfcl) (move (reg 1) (reg 1))))
  84. ))
  85. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  86. (defmethod (input-stream getc) ()
  87. % Return the next character from the file. Line termination is represented
  88. % by a single NEWLINE (LF) character. Returns NIL on end of file.
  89. % Implementation note: It was determined by experiment that the PSL
  90. % compiler produces much better code if there are no function calls other
  91. % than tail-recursive ones. That's why this function is written the way
  92. % it is.
  93. (if (< ptr count)
  94. (let ((ch (prog1
  95. (string-fetch buffer ptr)
  96. (setf ptr (+ ptr 1))
  97. )))
  98. % Ignore CR followed by LF
  99. (if (= ch #\CR)
  100. (=> self &getc-after-CR)
  101. ch
  102. ))
  103. (=> self &fill-buffer-and-getc)
  104. ))
  105. (defmethod (input-stream &getc-after-CR) () % Internal method.
  106. % We have just read a CR from the buffer. If the next character
  107. % is a LF, then we should ignore the CR and return the LF.
  108. % Otherwise, we should return the CR.
  109. (if (= (=> self peekc-image) #\LF)
  110. (=> self getc-image)
  111. #\CR
  112. ))
  113. (defmethod (input-stream &fill-buffer-and-getc) () % Internal method.
  114. (and (=> self &fill-buffer) (=> self getc)))
  115. (defmethod (input-stream getc-image) ()
  116. % Return the next character from the file. Do not perform any translation.
  117. % In particular, return all <CR>s. Returns NIL on end of file.
  118. (if (< ptr count)
  119. (prog1
  120. (string-fetch buffer ptr)
  121. (setf ptr (+ ptr 1))
  122. )
  123. (=> self &fill-buffer-and-getc-image)
  124. ))
  125. (defmethod (input-stream &fill-buffer-and-getc-image) () % Internal method.
  126. (and (=> self &fill-buffer) (=> self getc-image)))
  127. (defmethod (input-stream empty?) ()
  128. (null (=> self peekc-image)))
  129. (defmethod (input-stream peekc) ()
  130. % Return the next character from the file, but don't advance to the next
  131. % character. Returns NIL on end of file. Maps CRLF to LF.
  132. (if (< ptr count)
  133. (let ((ch (string-fetch buffer ptr)))
  134. % Ignore CR if followed by LF
  135. (if (and (= ch #\CR)
  136. (= (=> self &peek2) #\LF)
  137. )
  138. #\LF
  139. ch
  140. ))
  141. (=> self &fill-buffer-and-peekc)
  142. ))
  143. (defmethod (input-stream &fill-buffer-and-peekc) () % Internal method.
  144. (and (=> self &fill-buffer) (=> self peekc)))
  145. (defmethod (input-stream peekc-image) ()
  146. % Return the next character from the file, but don't advance to the next
  147. % character. Returns NIL on end of file.
  148. (if (< ptr count)
  149. (string-fetch buffer ptr)
  150. (=> self &fill-buffer-and-peekc-image)
  151. ))
  152. (defmethod (input-stream &fill-buffer-and-peekc-image) () % Internal method.
  153. (and (=> self &fill-buffer) (=> self peekc-image)))
  154. (defmethod (input-stream &peek2) () % Internal method.
  155. % Return the character after the next character in the file, but don't
  156. % advance. Does not map CRLF. Returns Ascii NUL on end of file. Requires
  157. % that the buffer contain at least one character. This is a hack required
  158. % to implement PEEKC.
  159. (let ((next-ptr (+ ptr 1)))
  160. (cond ((>= next-ptr count)
  161. % The next character has not yet been read into the buffer.
  162. (let* ((old-pos (RFPTR jfn))
  163. (ch (BIN jfn))
  164. )
  165. (SFPTR jfn old-pos)
  166. ch
  167. ))
  168. (t (string-fetch buffer next-ptr))
  169. )))
  170. (defmethod (input-stream &fill-buffer) () % Internal method.
  171. % Return NIL iff there are no more characters.
  172. (if eof-flag
  173. NIL
  174. (let ((n (SIN jfn (jconv buffer) (- (const FILE-BUFFER-SIZE)))))
  175. (if (~= n 0) (setf eof-flag T))
  176. (setf count (+ (const FILE-BUFFER-SIZE) n))
  177. (setf ptr 0)
  178. (~= count 0))))
  179. (defmethod (input-stream getl) ()
  180. % Read and return (the remainder of) the current input line.
  181. % Read, but don't return the terminating EOL (if any).
  182. % (EOL is interpreted as LF or CRLF)
  183. % Return NIL if no characters and end-of-file detected.
  184. (if (and (>= ptr count) (not (=> self &fill-buffer)))
  185. NIL
  186. % Else
  187. (let ((start ptr) (save-buffer NIL) (eof? NIL))
  188. (while (and (not eof?) (~= (string-fetch buffer ptr) #\LF))
  189. (setf ptr (+ ptr 1))
  190. (cond ((>= ptr count)
  191. (setf save-buffer
  192. (concat save-buffer (subseq buffer start ptr)))
  193. (setf eof? (not (=> self &fill-buffer)))
  194. (setf start ptr)
  195. ))
  196. )
  197. (if eof?
  198. save-buffer
  199. % Else
  200. (setf ptr (+ ptr 1))
  201. (if (= ptr 1)
  202. (if save-buffer
  203. (if (= (string-fetch save-buffer (size save-buffer)) #\CR)
  204. (subseq save-buffer 0 (size save-buffer))
  205. (sub save-buffer 0 (size save-buffer)))
  206. (subseq buffer start ptr))
  207. (if (= (string-fetch buffer (- ptr 2)) #\CR)
  208. (concat save-buffer (subseq buffer start (- ptr 2)))
  209. (concat save-buffer (subseq buffer start (- ptr 1)))
  210. )))
  211. )))
  212. (defmethod (input-stream tell-position) ()
  213. % Return an integer representing the current "position" of the stream. About
  214. % all we can guarantee about this integer is (1) it will be 0 at the
  215. % beginning of the file and (2) if you later SEEK-POSITION to this integer,
  216. % the stream will be reset to its current position. The reason for this
  217. % fuzziness is that the translation of CRLF into LF performed by the "normal"
  218. % input operations makes it impossible to predict the relationship between
  219. % the apparent file position and the actual file position.
  220. (- (RFPTR jfn) (- count ptr))
  221. )
  222. (defmethod (input-stream seek-position) (p)
  223. (setf p (int2sys p))
  224. (let* ((buffer-end (RFPTR jfn))
  225. (buffer-start (- buffer-end count)))
  226. (if (and (>= p buffer-start) (< p buffer-end))
  227. (setf ptr (- p buffer-start))
  228. % Else
  229. (SFPTR jfn p)
  230. (setf ptr 0)
  231. (setf count 0)
  232. (setf eof-flag NIL)
  233. )
  234. ))
  235. (defmethod (input-stream open) (name-of-file)
  236. % Open the specified file for input via SELF. If the file cannot be opened,
  237. % a Continuable Error is generated.
  238. (if jfn (=> self close))
  239. (setf buffer (MkString (const FILE-BUFFER-SIZE) #\space))
  240. (setf ptr 0)
  241. (setf count 0)
  242. (setf eof-flag NIL)
  243. (setf jfn (Dec20Open name-of-file
  244. (int2sys 2#001000000000000001000000000000000000)
  245. (int2sys 2#000111000000000000010000000000100000)
  246. ))
  247. (if (= jfn 0) (setf jfn NIL))
  248. (if (null jfn)
  249. (=> self open
  250. (ContinuableError
  251. 0
  252. (BldMsg "Unable to Open '%w' for Input." name-of-file)
  253. name-of-file))
  254. % Else
  255. (setf file-name (jfn-truename jfn))
  256. ))
  257. (defmethod (input-stream close) ()
  258. (when jfn
  259. (CLOSF jfn)
  260. (setf jfn NIL)
  261. (setf buffer NIL)
  262. (setf count 0)
  263. (setf ptr 0)
  264. (setf eof-flag T)
  265. ))
  266. (defmethod (input-stream read-date) ()
  267. (jfn-read-date jfn))
  268. (defmethod (input-stream write-date) ()
  269. (jfn-write-date jfn))
  270. (defmethod (input-stream delete-file) ()
  271. (jfn-delete jfn))
  272. (defmethod (input-stream undelete-file) ()
  273. (jfn-undelete jfn))
  274. (defmethod (input-stream delete-and-expunge-file) ()
  275. (jfn-delete-and-expunge jfn))
  276. (defmethod (input-stream author) ()
  277. (jfn-author jfn))
  278. (defmethod (input-stream original-author) ()
  279. (jfn-original-author jfn))
  280. (defmethod (input-stream file-length) ()
  281. (jfn-byte-count jfn))
  282. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  283. % TESTING CODE
  284. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  285. (CommentOutCode (progn
  286. (de test-buffered-input (name-of-file)
  287. (setf s (open-input name-of-file))
  288. (while (setf ch (input-stream$getc s))
  289. (WriteChar ch)
  290. )
  291. (=> s close)
  292. (Prin2 "---EOF---")
  293. NIL
  294. )
  295. (de time-buffered-input (name-of-file)
  296. (setf start-time (time))
  297. (setf s (open-input name-of-file))
  298. (while (setf ch (input-stream$getc s))
  299. )
  300. (=> s close)
  301. (- (time) start-time)
  302. )
  303. (de time-buffered-input-1 (name-of-file)
  304. (setf start-time (time))
  305. (setf s (open-input name-of-file))
  306. (while (setf ch (=> s getc))
  307. )
  308. (=> s close)
  309. (- (time) start-time)
  310. )
  311. (de time-standard-input (name-of-file)
  312. (setf start-time (time))
  313. (setf chan (open name-of-file 'INPUT))
  314. (while (not (= (setf ch (ChannelReadChar chan)) $EOF$))
  315. )
  316. (close chan)
  317. (- (time) start-time)
  318. )
  319. (de time-input (name-of-file)
  320. (list
  321. (time-buffered-input name-of-file)
  322. (time-buffered-input-1 name-of-file)
  323. (time-standard-input name-of-file)
  324. ))
  325. )) % End CommentOutCode