stream.bmx 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. Strict
  2. Rem
  3. bbdoc: Streams/Streams
  4. End Rem
  5. Module BRL.Stream
  6. ModuleInfo "Version: 1.09"
  7. ModuleInfo "Author: Mark Sibly"
  8. ModuleInfo "License: zlib/libpng"
  9. ModuleInfo "Copyright: Blitz Research Ltd"
  10. ModuleInfo "Modserver: BRL"
  11. ModuleInfo "History: 1.09 Release"
  12. ModuleInfo "History: Fixed 'excpetion' typos"
  13. ModuleInfo "History: 1.08 Release"
  14. ModuleInfo "History: Fixed resource leak in CasedFileName"
  15. ModuleInfo "History: 1.07 Release"
  16. ModuleInfo "History: OpenStream protocol:: now case insensitive"
  17. ModuleInfo "History: Fixed ReadString with 0 length strings"
  18. ModuleInfo "History: Removed LoadStream - use LoadByteArray instead"
  19. ModuleInfo "History: Removed AddStreamFactory function"
  20. ModuleInfo "History: Added url parameter to TStreamFactory CreateStream method"
  21. ModuleInfo "History: 1.06 Release"
  22. ModuleInfo "History: Added checks to CStream for reading from write only stream and vice versa"
  23. ModuleInfo "History: 1.05 Release"
  24. ModuleInfo "History: Fixed Eof bug"
  25. ModuleInfo "History: 1.04 Release"
  26. ModuleInfo "History: Added LoadString"
  27. ModuleInfo "History: Added LoadByteArray"
  28. ModuleInfo "History: Cleaned up docs a bit"
  29. Import Pub.StdC
  30. Rem
  31. bbdoc: Base exception type thrown by streams
  32. End Rem
  33. Type TStreamException
  34. End Type
  35. Rem
  36. bbdoc: Exception type thrown by streams in the event of read errors
  37. about:
  38. #TStreamReadException is usually thrown when a stream operation failed to read enough
  39. bytes. For example, if the stream ReadInt method fails to read 4 bytes, it will throw
  40. a #TStreamReadException.
  41. End Rem
  42. Type TStreamReadException Extends TStreamException
  43. Method ToString$()
  44. Return "Error reading from stream"
  45. End Method
  46. End Type
  47. Rem
  48. bbdoc: Exception type thrown by streams in the event of write errors
  49. about:
  50. #TStreamWriteException is usually thrown when a stream operation failed to write enough
  51. bytes. For example, if the stream WriteInt method fails to write 4 bytes, it will throw
  52. a #TStreamWriteException.
  53. End Rem
  54. Type TStreamWriteException Extends TStreamException
  55. Method ToString$()
  56. Return "Error writing to stream"
  57. End Method
  58. End Type
  59. Rem
  60. bbdoc: Base input/output type
  61. about:
  62. To create your own stream types, you should extend TStream and implement
  63. at least these methods.
  64. You should also make sure your stream can handle multiple calls to the Close method.
  65. End Rem
  66. Type TIO
  67. Rem
  68. bbdoc: Get stream end of file status
  69. returns: True for end of file reached, otherwise False
  70. about:
  71. For seekable streams such as file streams, Eof generally returns True if the file
  72. position equals the file size. This means that no more bytes can be read from the
  73. stream. However, it may still be possible to write bytes, in which case the file will
  74. 'grow'.
  75. For communication type streams such as socket streams, Eof returns True if the stream
  76. has been shut down for some reason - either locally or by the remote host. In this case,
  77. no more bytes can be read from or written to the stream.
  78. End Rem
  79. Method Eof()
  80. Return Pos()=Size()
  81. End Method
  82. Rem
  83. bbdoc: Get position of seekable stream
  84. returns: Stream position as a byte offset, or -1 if stream is not seekable
  85. End Rem
  86. Method Pos()
  87. Return -1
  88. End Method
  89. Rem
  90. bbdoc: Get size of seekable stream
  91. returns: Size, in bytes, of seekable stream, or 0 if stream is not seekable
  92. End Rem
  93. Method Size()
  94. Return 0
  95. End Method
  96. Rem
  97. bbdoc: Seek to position in seekable stream
  98. returns: New stream position, or -1 if stream is not seekable
  99. End Rem
  100. Method Seek( pos )
  101. Return -1
  102. End Method
  103. Rem
  104. bbdoc: Flush stream
  105. about:
  106. Flushes any internal stream buffers.
  107. End Rem
  108. Method Flush()
  109. End Method
  110. Rem
  111. bbdoc: Close stream
  112. about:
  113. Closes the stream after flushing any internal stream buffers.
  114. End Rem
  115. Method Close()
  116. End Method
  117. Rem
  118. bbdoc: Read at least 1 byte from a stream
  119. returns: Number of bytes successfully read
  120. about:
  121. This method may 'block' if it is not possible to read at least one byte due to IO
  122. buffering.
  123. If this method returns 0, the stream has reached end of file.
  124. End Rem
  125. Method Read( buf:Byte Ptr,count )
  126. RuntimeError "Stream is not readable"
  127. Return 0
  128. End Method
  129. Rem
  130. bbdoc: Write at least 1 byte to a stream
  131. returns: Number of bytes successfully written
  132. about:
  133. This method may 'block' if it is not possible to write at least one byte due to IO
  134. buffering.
  135. If this method returns 0, the stream has reached end of file.
  136. End Rem
  137. Method Write( buf:Byte Ptr,count )
  138. RuntimeError "Stream is not writeable"
  139. Return 0
  140. End Method
  141. Method Delete()
  142. Close
  143. End Method
  144. End Type
  145. Rem
  146. bbdoc: Data stream type
  147. about:
  148. #TStream extends #TIO to provide methods for reading and writing various types of values
  149. to and from a stream.
  150. Note that methods dealing with strings - ReadLine, WriteLine, ReadString and WriteString -
  151. assume that strings are represented by bytes in the stream. In future, a more powerful
  152. TextStream type will be added capable of decoding text streams in multiple formats.
  153. End Rem
  154. Type TStream Extends TIO
  155. Rem
  156. bbdoc: Reads bytes from a stream
  157. about:
  158. #ReadBytes reads @count bytes from the stream into the memory block specified by @buf.
  159. If @count bytes were not successfully read, a #TStreamReadException is thrown. This typically
  160. occurs due to end of file.
  161. End Rem
  162. Method ReadBytes( buf:Byte Ptr,count )
  163. Local t=count
  164. While count>0
  165. Local n=Read( buf,count )
  166. If Not n Throw New TStreamReadException
  167. count:-n
  168. buf:+n
  169. Wend
  170. Return t
  171. End Method
  172. Rem
  173. bbdoc: Writes bytes to a stream
  174. about:
  175. #WriteBytes writes @count bytes from the memory block specified by @buf to the stream.
  176. If @count bytes were not successfully written, a #TStreamWriteException is thrown. This typically
  177. occurs due to end of file.
  178. End Rem
  179. Method WriteBytes( buf:Byte Ptr,count )
  180. Local t=count
  181. While count>0
  182. Local n=Write( buf,count )
  183. If Not n Throw New TStreamWriteException
  184. count:-n
  185. buf:+n
  186. Wend
  187. Return t
  188. End Method
  189. Rem
  190. bbdoc: Skip bytes in a stream
  191. about:
  192. #SkipBytes read @count bytes from the stream and throws them away.
  193. If @count bytes were not successfully read, a #TStreamReadException is thrown. This typically
  194. occurs due to end of file.
  195. End Rem
  196. Method SkipBytes( count )
  197. Local t=count
  198. Local buf:Byte[1024]
  199. While count>0
  200. Local n=Read( buf,Min(count,buf.length) )
  201. If Not n Throw New TStreamReadException
  202. count:-n
  203. Wend
  204. Return t
  205. End Method
  206. Rem
  207. bbdoc: Read a byte from the stream
  208. returns: The read value
  209. about:
  210. If a value could not be read (possibly due to end of file), a #TStreamReadException is thrown.
  211. End Rem
  212. Method ReadByte()
  213. Local n:Byte
  214. ReadBytes Varptr n,1
  215. Return n
  216. End Method
  217. Rem
  218. bbdoc: Write a byte to the stream
  219. about:
  220. If the value could not be written (possibly due to end of file), a #TStreamWriteException is thrown.
  221. End Rem
  222. Method WriteByte( n )
  223. Local q:Byte=n
  224. WriteBytes Varptr q,1
  225. End Method
  226. Rem
  227. bbdoc: Read a short (two bytes) from the stream
  228. returns: The read value
  229. about:
  230. If a value could not be read (possibly due to end of file), a #TStreamReadException is thrown.
  231. End Rem
  232. Method ReadShort()
  233. Local n:Short
  234. ReadBytes Varptr n,2
  235. Return n
  236. End Method
  237. Rem
  238. bbdoc: Write a short (two bytes) to the stream
  239. about:
  240. If the value could not be written (possibly due to end of file), a #TStreamWriteException is thrown.
  241. End Rem
  242. Method WriteShort( n )
  243. Local q:Short=n
  244. WriteBytes Varptr q,2
  245. End Method
  246. Rem
  247. bbdoc: Read an int (four bytes) from the stream
  248. returns: The read value
  249. about:
  250. If a value could not be read (possibly due to end of file), a #TStreamReadException is thrown.
  251. End Rem
  252. Method ReadInt()
  253. Local n
  254. ReadBytes Varptr n,4
  255. Return n
  256. End Method
  257. Rem
  258. bbdoc: Write an int (four bytes) to the stream
  259. about:
  260. If the value could not be written (possibly due to end of file), a #TStreamWriteException is thrown.
  261. End Rem
  262. Method WriteInt( n )
  263. Local q:Int=n
  264. WriteBytes Varptr q,4
  265. End Method
  266. Rem
  267. bbdoc: Read a long (eight bytes) from the stream
  268. returns: The read value
  269. about:
  270. If a value could not be read (possibly due to end of file), a #TStreamReadException is thrown.
  271. End Rem
  272. Method ReadLong:Long()
  273. Local n:Long
  274. ReadBytes Varptr n,8
  275. Return n
  276. End Method
  277. Rem
  278. bbdoc: Write a long (eight bytes) to the stream
  279. about:
  280. If the value could not be written (possibly due to end of file), a #TStreamWriteException is thrown.
  281. End Rem
  282. Method WriteLong( n:Long )
  283. Local q:Long=n
  284. WriteBytes Varptr q,8
  285. End Method
  286. Rem
  287. bbdoc: Read a float (four bytes) from the stream
  288. returns: The read value
  289. about:
  290. If a value could not be read (possibly due to end of file), a #TStreamReadException is thrown.
  291. End Rem
  292. Method ReadFloat#()
  293. Local n#
  294. ReadBytes Varptr n,4
  295. Return n
  296. End Method
  297. Rem
  298. bbdoc: Write a float (four bytes) to the stream
  299. about:
  300. If the value could not be written (possibly due to end of file), a #TStreamWriteException is thrown.
  301. End Rem
  302. Method WriteFloat( n# )
  303. Local q#=n
  304. WriteBytes Varptr q,4
  305. End Method
  306. Rem
  307. bbdoc: Read a double (eight bytes) from the stream
  308. returns: The read value
  309. about:
  310. If a value could not be read (possibly due to end of file), a #TStreamReadException is thrown.
  311. End Rem
  312. Method ReadDouble!()
  313. Local n!
  314. ReadBytes Varptr n,8
  315. Return n
  316. End Method
  317. Rem
  318. bbdoc: Write a double (eight bytes) to the stream
  319. about:
  320. If the value could not be written (possibly due to end of file), a #TStreamWriteException is thrown.
  321. End Rem
  322. Method WriteDouble( n! )
  323. Local q!=n
  324. WriteBytes Varptr q,8
  325. End Method
  326. Rem
  327. bbdoc: Read a line of text from the stream
  328. about:
  329. Bytes are read from the stream until a newline character (ascii code 10) or null
  330. character (ascii code 0) is read, or end of file is detected.
  331. Carriage return characters (ascii code 13) are silently ignored.
  332. The bytes read are returned in the form of a string, excluding any terminating newline
  333. or null character.
  334. End Rem
  335. Method ReadLine$()
  336. Local buf:Byte[1024],sz
  337. Repeat
  338. Local ch:Byte
  339. If Read( Varptr ch,1 )<>1 Or ch=0 Or ch=10 Exit
  340. If ch=13 Continue
  341. If sz=buf.length buf=buf[..sz*2]
  342. buf[sz]=ch
  343. sz:+1
  344. Forever
  345. Return String.FromBytes( buf,sz )
  346. End Method
  347. Rem
  348. bbdoc: Write a line of text to the stream
  349. returns: True if line successfully written, else False
  350. about: A sequence of bytes is written to the stream (one for each character in @str)
  351. followed by the line terminating sequence "~r~n".
  352. End Rem
  353. Method WriteLine( str$ )
  354. Local buf:Byte Ptr=str.ToCString()
  355. Local ok=Write( buf,str.length )=str.length And Write( [13:Byte,10:Byte],2 )=2
  356. MemFree buf
  357. Return ok
  358. End Method
  359. Rem
  360. bbdoc: Read characters from the stream
  361. returns: A string composed of @length bytes read from the stream
  362. about:
  363. A #TStreamReadException is thrown if not all bytes could be read.
  364. End Rem
  365. Method ReadString$( length )
  366. Assert length>=0 Else "Illegal String length"
  367. Local buf:Byte[length]
  368. Readbytes buf,length
  369. Return String.FromBytes( buf,length )
  370. End Method
  371. Rem
  372. bbdoc: Write characters to the stream
  373. about:
  374. A #TStreamWriteException is thrown if not all bytes could be written.
  375. End Rem
  376. Method WriteString( str$ )
  377. Local buf:Byte Ptr=str.ToCString()
  378. WriteBytes buf,str.length
  379. MemFree buf
  380. End Method
  381. Method ReadObject:Object()
  382. Throw "Unable to read object"
  383. End Method
  384. Method WriteObject( obj:Object )
  385. Throw "Unable to write object"
  386. End Method
  387. End Type
  388. Rem
  389. bbdoc: Utility stream wrapper type
  390. about:
  391. #TStreamWrapper 'wraps' an existing stream, redirecting all TIO method calls to the wrapped
  392. stream.
  393. This can be useful for writing stream 'filters' that modify the behaviour of existing
  394. streams.
  395. Note that the Close method causes the underlying stream to be closed, which may not always
  396. be desirable. If necessary, you should override Close with a NOP version.
  397. End Rem
  398. Type TStreamWrapper Extends TStream
  399. Field _stream:TStream
  400. Rem
  401. bbdoc: Set underlying stream
  402. about:
  403. Sets the stream to be 'wrapped'. All calls to TIO methods of this stream will be
  404. redirected to @stream.
  405. end rem
  406. Method SetStream( stream:TStream )
  407. _stream=stream
  408. End Method
  409. Method Eof()
  410. Return _stream.Eof()
  411. End Method
  412. Method Pos()
  413. Return _stream.Pos()
  414. End Method
  415. Method Size()
  416. Return _stream.Size()
  417. End Method
  418. Method Seek( pos )
  419. Return _stream.Seek( pos )
  420. End Method
  421. Method Flush()
  422. _stream.Flush
  423. End Method
  424. Method Close()
  425. _stream.Close
  426. End Method
  427. Method Read( buf:Byte Ptr,count )
  428. Return _stream.Read( buf,count )
  429. End Method
  430. Method Write( buf:Byte Ptr,count )
  431. Return _stream.Write( buf,count )
  432. End Method
  433. Method ReadByte()
  434. Return _stream.ReadByte()
  435. End Method
  436. Method WriteByte( n )
  437. _stream.WriteByte n
  438. End Method
  439. Method ReadShort()
  440. Return _stream.ReadShort()
  441. End Method
  442. Method WriteShort( n )
  443. _stream.WriteShort n
  444. End Method
  445. Method ReadInt()
  446. Return _stream.ReadInt()
  447. End Method
  448. Method WriteInt( n )
  449. _stream.WriteInt n
  450. End Method
  451. Method ReadFloat:Float()
  452. Return _stream.ReadFloat()
  453. End Method
  454. Method WriteFloat( n:Float )
  455. _stream.WriteFloat n
  456. End Method
  457. Method ReadDouble:Double()
  458. Return _stream.ReadDouble()
  459. End Method
  460. Method WriteDouble( n:Double )
  461. _stream.WriteDouble n
  462. End Method
  463. Method ReadLine$()
  464. Return _stream.ReadLine()
  465. End Method
  466. Method WriteLine( t$ )
  467. Return _stream.WriteLine( t )
  468. End Method
  469. Method ReadString$( n )
  470. Return _stream.ReadString( n )
  471. End Method
  472. Method WriteString( t$ )
  473. _stream.WriteString t
  474. End Method
  475. Method ReadObject:Object()
  476. Return _stream.ReadObject()
  477. End Method
  478. Method WriteObject( obj:Object )
  479. _stream.WriteObject obj
  480. End Method
  481. End Type
  482. Type TStreamStream Extends TStreamWrapper
  483. Method Close()
  484. SetStream Null
  485. End Method
  486. Function Create:TStreamStream( stream:TStream )
  487. Local t:TStreamStream=New TStreamStream
  488. t.SetStream stream
  489. Return t
  490. End Function
  491. End Type
  492. Rem
  493. bbdoc: Standard C file stream type
  494. about:
  495. End Rem
  496. Type TCStream Extends TStream
  497. Const MODE_READ=1
  498. Const MODE_WRITE=2
  499. Field _pos,_size,_cstream,_mode
  500. Method Pos()
  501. Return _pos
  502. End Method
  503. Method Size()
  504. Return _size
  505. End Method
  506. Method Seek( pos )
  507. Assert _cstream Else "Attempt to seek closed stream"
  508. fseek_ _cstream,pos,SEEK_SET_
  509. _pos=ftell_( _cstream )
  510. Return _pos
  511. End Method
  512. Method Read( buf:Byte Ptr,count )
  513. Assert _cstream Else "Attempt to read from closed stream"
  514. Assert _mode & MODE_READ Else "Attempt to read from write-only stream"
  515. count=fread_( buf,1,count,_cstream )
  516. _pos:+count
  517. Return count
  518. End Method
  519. Method Write( buf:Byte Ptr,count )
  520. Assert _cstream Else "Attempt to write to closed stream"
  521. Assert _mode & MODE_WRITE Else "Attempt to write to read-only stream"
  522. count=fwrite_( buf,1,count,_cstream )
  523. _pos:+count
  524. If _pos>_size _size=_pos
  525. Return count
  526. End Method
  527. Method Flush()
  528. If _cstream fflush_ _cstream
  529. End Method
  530. Method Close()
  531. If Not _cstream Return
  532. Flush
  533. fclose_ _cstream
  534. _pos=0
  535. _size=0
  536. _cstream=0
  537. End Method
  538. Method Delete()
  539. Close
  540. End Method
  541. Rem
  542. bbdoc: Create a TCStream from a 'C' filename
  543. End Rem
  544. Function OpenFile:TCStream( path$,readable,writeable )
  545. Local mode$,_mode
  546. If readable And writeable
  547. mode="r+b"
  548. _mode=MODE_READ|MODE_WRITE
  549. Else If writeable
  550. mode="wb"
  551. _mode=MODE_WRITE
  552. Else
  553. mode="rb"
  554. _mode=MODE_READ
  555. EndIf
  556. path=path.Replace( "\","/" )
  557. Local cstream=fopen_( path,mode )
  558. ?Linux
  559. If (Not cstream) And (Not writeable)
  560. path=CasedFileName(path)
  561. If path cstream=fopen_( path,mode )
  562. EndIf
  563. ?
  564. If cstream Return CreateWithCStream( cstream,_mode )
  565. End Function
  566. Rem
  567. bbdoc: Create a TCStream from a 'C' stream handle
  568. end rem
  569. Function CreateWithCStream:TCStream( cstream,mode )
  570. Local stream:TCStream=New TCStream
  571. stream._cstream=cstream
  572. stream._pos=ftell_( cstream )
  573. fseek_ cstream,0,SEEK_END_
  574. stream._size=ftell_( cstream )
  575. fseek_ cstream,stream._pos,SEEK_SET_
  576. stream._mode=mode
  577. Return stream
  578. End Function
  579. End Type
  580. Private
  581. Global stream_factories:TStreamFactory
  582. Public
  583. Rem
  584. bbdoc: Base stream factory type
  585. about:
  586. Stream factories are used by the #OpenStream, #ReadStream and #WriteStream functions
  587. to create streams based on a 'url object'.
  588. Url objects are usually strings, in which case the url is divided into 2 parts - a
  589. protocol and a path. These are separated by a double colon string - "::".
  590. To create your own stream factories, you should extend the TStreamFactory type and
  591. implement the CreateStream method.
  592. To install your stream factory, simply create an instance of it using 'New'.
  593. End Rem
  594. Type TStreamFactory
  595. Field _succ:TStreamFactory
  596. Method New()
  597. _succ=stream_factories
  598. stream_factories=Self
  599. End Method
  600. Rem
  601. bbdoc: Create a stream based on a url object
  602. about:
  603. Types which extends TStreamFactory must implement this method.
  604. @url contains the original url object as supplied to #OpenStream, #ReadStream or
  605. #WriteStream.
  606. If @url is a string, @proto contains the url protocol - for example, the "incbin" part
  607. of "incbin::myfile".
  608. If @url is a string, @path contains the remainder of the url - for example, the "myfile"
  609. part of "incbin::myfile".
  610. If @url is not a string, both @proto and @path will be Null.
  611. End Rem
  612. Method CreateStream:TStream( url:Object,proto$,path$,readable,writeable ) Abstract
  613. End Type
  614. Rem
  615. bbdoc: Open a stream for reading/writing
  616. returns: A stream object
  617. about: All streams created by #OpenStream, #ReadStream or #WriteStream should eventually be
  618. closed using #CloseStream.
  619. End Rem
  620. Function OpenStream:TStream( url:Object,readable=True,writeable=True )
  621. Local stream:TStream=TStream( url )
  622. If stream
  623. Return TStreamStream.Create( stream )
  624. EndIf
  625. Local str$=String( url ),proto$,path$
  626. If str
  627. Local i=str.Find( "::",0 )
  628. If i=-1 Return TCStream.OpenFile( str,readable,writeable )
  629. proto$=str[..i].ToLower()
  630. path$=str[i+2..]
  631. EndIf
  632. Local factory:TStreamFactory=stream_factories
  633. While factory
  634. Local stream:TStream=factory.CreateStream( url,proto,path,readable,writeable )
  635. If stream Return stream
  636. factory=factory._succ
  637. Wend
  638. End Function
  639. Rem
  640. bbdoc: Open a stream for reading
  641. returns: A stream object
  642. about: All streams created by #OpenStream, #ReadStream or #WriteStream should eventually
  643. be closed using #CloseStream.
  644. End Rem
  645. Function ReadStream:TStream( url:Object )
  646. Return OpenStream( url,True,False )
  647. End Function
  648. Rem
  649. bbdoc: Open a stream for writing
  650. returns: A stream object
  651. about: All streams created by #OpenStream, #ReadStream or #WriteStream should eventually
  652. be closed using #CloseStream.
  653. End Rem
  654. Function WriteStream:TStream( url:Object )
  655. Return OpenStream( url,False,True )
  656. End Function
  657. Rem
  658. bbdoc: Get stream end of file status
  659. returns: True If stream is at end of file
  660. End Rem
  661. Function Eof( stream:TStream )
  662. Return stream.Eof()
  663. End Function
  664. Rem
  665. bbdoc: Get current position of seekable stream
  666. returns: Current stream position, or -1 If stream is not seekable
  667. End Rem
  668. Function StreamPos( stream:TStream )
  669. Return stream.Pos()
  670. End Function
  671. Rem
  672. bbdoc: Get current size of seekable stream
  673. returns: Current stream size in bytes, or -1 If stream is not seekable
  674. End Rem
  675. Function StreamSize( stream:TStream )
  676. Return stream.Size()
  677. End Function
  678. Rem
  679. bbdoc: Set stream position of seekable stream
  680. returns: New stream position, or -1 If stream is not seekable
  681. End Rem
  682. Function SeekStream( stream:TStream,pos )
  683. Return stream.Seek( pos )
  684. End Function
  685. Rem
  686. bbdoc: Flush a stream
  687. about: #FlushStream writes any outstanding buffered data to @stream.
  688. End Rem
  689. Function FlushStream( stream:TStream )
  690. stream.Flush
  691. End Function
  692. Rem
  693. bbdoc: Close a stream
  694. about:
  695. All streams should be closed when they are no longer required.
  696. Closing a stream also flushes the stream before it closes.
  697. End Rem
  698. Function CloseStream( stream:TStream )
  699. stream.Close
  700. End Function
  701. Rem
  702. bbdoc: Read a Byte from a stream
  703. returns: A Byte value
  704. about: #ReadByte reads a single Byte from @stream.
  705. A TStreamReadException is thrown If there is not enough data available.
  706. End Rem
  707. Function ReadByte( stream:TStream )
  708. Return stream.ReadByte()
  709. End Function
  710. Rem
  711. bbdoc: Read a Short from a stream
  712. returns: A Short value
  713. about: #ReadShort reads 2 bytes from @stream.
  714. A TStreamReadException is thrown If there is not enough data available.
  715. End Rem
  716. Function ReadShort( stream:TStream )
  717. Return stream.ReadShort()
  718. End Function
  719. Rem
  720. bbdoc: Read an Int from a stream
  721. returns: An Int value
  722. about: #ReadInt reads 4 bytes from @stream.
  723. A TStreamReadException is thrown If there is not enough data available.
  724. End Rem
  725. Function ReadInt( stream:TStream )
  726. Return stream.ReadInt()
  727. End Function
  728. Rem
  729. bbdoc: Read a Long from a stream
  730. returns: A Long value
  731. about: #ReadLong reads 8 bytes from @stream.
  732. A TStreamReadException is thrown If there is not enough data available.
  733. End Rem
  734. Function ReadLong:Long( stream:TStream )
  735. Return stream.ReadLong()
  736. End Function
  737. Rem
  738. bbdoc: Read a Float from a stream
  739. returns: A Float value
  740. about: #ReadFloat reads 4 bytes from @stream.
  741. A TStreamReadException is thrown If there is not enough data available.
  742. End Rem
  743. Function ReadFloat#( stream:TStream )
  744. Return stream.ReadFloat()
  745. End Function
  746. Rem
  747. bbdoc: Read a Double from a stream
  748. returns: A Double value
  749. about: #ReadDouble reads 8 bytes from @stream.
  750. A TStreamWriteException is thrown If there is not enough data available.
  751. End Rem
  752. Function ReadDouble!( stream:TStream )
  753. Return stream.ReadDouble()
  754. End Function
  755. Rem
  756. bbdoc: Write a Byte to a stream
  757. about: #WriteByte writes a single Byte to @stream.
  758. A TStreamWriteException is thrown If the Byte could Not be written
  759. End Rem
  760. Function WriteByte( stream:TStream,n )
  761. stream.WriteByte n
  762. End Function
  763. Rem
  764. bbdoc: Write a Short to a stream
  765. about: #WriteShort writes 2 bytes to @stream.
  766. A TStreamWriteException is thrown if not all bytes could be written
  767. End Rem
  768. Function WriteShort( stream:TStream,n )
  769. stream.WriteShort n
  770. End Function
  771. Rem
  772. bbdoc: Write an Int to a stream
  773. about: #WriteInt writes 4 bytes to @stream.
  774. A TStreamWriteException is thrown if not all bytes could be written
  775. End Rem
  776. Function WriteInt( stream:TStream,n )
  777. stream.WriteInt n
  778. End Function
  779. Rem
  780. bbdoc: Write a Long to a stream
  781. about: #WriteLong writes 8 bytes to @stream.
  782. A TStreamWriteException is thrown if not all bytes could be written
  783. End Rem
  784. Function WriteLong( stream:TStream,n:Long )
  785. stream.WriteLong n
  786. End Function
  787. Rem
  788. bbdoc: Write a Float to a stream
  789. about: #WriteFloat writes 4 bytes to @stream.
  790. A TStreamWriteException is thrown if not all bytes could be written
  791. End Rem
  792. Function WriteFloat( stream:TStream,n# )
  793. stream.WriteFloat n
  794. End Function
  795. Rem
  796. bbdoc: Write a Double to a stream
  797. about: #WriteDouble writes 8 bytes to @stream.
  798. A TStreamWriteException is thrown if not all bytes could be written
  799. End Rem
  800. Function WriteDouble( stream:TStream,n! )
  801. stream.WriteDouble n
  802. End Function
  803. Rem
  804. bbdoc: Read a String from a stream
  805. returns: A String of length @length
  806. about:
  807. A #TStreamReadException is thrown if not all bytes could be read.
  808. end rem
  809. Function ReadString$( stream:TStream,length )
  810. Return stream.ReadString( length )
  811. End Function
  812. Rem
  813. bbdoc: Write a String to a stream
  814. about:
  815. Each character in @str is written to @stream.
  816. A #TStreamWriteException is thrown if not all bytes could be written.
  817. End Rem
  818. Function WriteString( stream:TStream,str$ )
  819. stream.WriteString str
  820. End Function
  821. Rem
  822. bbdoc: Read a line of text from a stream
  823. returns: A string
  824. about:
  825. Bytes are read from @stream until a newline character (ascii code 10) or null
  826. character (ascii code 0) is read, or end of file is detected.
  827. Carriage Return characters (ascii code 13) are silently ignored.
  828. The bytes read are returned in the form of a string, excluding any terminating newline
  829. or null character.
  830. End Rem
  831. Function ReadLine$( stream:TStream )
  832. Return stream.ReadLine()
  833. End Function
  834. Rem
  835. bbdoc: Write a line of text to a stream
  836. returns: True if line successfully written, else False
  837. about:
  838. A sequence of bytes is written to the stream (one for each character in @str)
  839. followed by the line terminating sequence "~r~n".
  840. End Rem
  841. Function WriteLine( stream:TStream,str$ )
  842. Return stream.WriteLine( str )
  843. End Function
  844. Rem
  845. bbdoc: Load a String from a stream
  846. returns: A String
  847. about:
  848. The specified @url is opened for reading, and each byte in the resultant stream
  849. (until eof of file is reached) is read into a string.
  850. A #TStreamReadException is thrown if the stream could not be read.
  851. End Rem
  852. Function LoadString$( url:Object )
  853. Local t:Byte[]=LoadByteArray(url)
  854. Return String.FromBytes( t,t.length )
  855. End Function
  856. Rem
  857. bbdoc: Save a String to a stream
  858. about:
  859. The specified @url is opened For writing, and each character of @str is written to the
  860. resultant stream.
  861. A #TStreamWriteException is thrown if not all bytes could be written.
  862. End Rem
  863. Function SaveString( str$,url:Object )
  864. Local stream:TStream=WriteStream( url )
  865. If Not stream Throw New TStreamWriteException
  866. Local t:Byte Ptr=str.ToCString()
  867. stream.WriteBytes t,str.length 'Should be in a try block...or t is leaked!
  868. MemFree t
  869. stream.Close
  870. End Function
  871. Function LoadObject:Object( url:Object )
  872. Local stream:TStream=ReadStream( url )
  873. If Not stream Throw New TStreamReadException
  874. Local obj:Object=stream.ReadObject()
  875. stream.Close
  876. Return obj
  877. End Function
  878. Function SaveObject( obj:Object,url:Object )
  879. Local stream:TStream=WriteStream( url )
  880. If Not stream Throw New TStreamWriteException
  881. stream.WriteObject obj
  882. stream.Close
  883. End Function
  884. Rem
  885. bbdoc: Load a Byte array from a stream
  886. returns: A Byte array
  887. about:
  888. The specified @url is opened for reading, and each byte in the resultant stream
  889. (until eof of reached) is read into a byte array.
  890. End Rem
  891. Function LoadByteArray:Byte[]( url:Object )
  892. Local stream:TStream=ReadStream( url )
  893. If Not stream Throw New TStreamReadException
  894. Local data:Byte[1024],size
  895. While Not stream.Eof()
  896. If size=data.length data=data[..size*3/2]
  897. size:+stream.Read( (Byte Ptr data)+size,data.length-size )
  898. Wend
  899. stream.Close
  900. Return data[..size]
  901. End Function
  902. Rem
  903. bbdoc: Save a Byte array to a stream
  904. about:
  905. The specified @url is opened For writing, and each element of @byteArray is written to the
  906. resultant stream.
  907. A #TStreamWriteException is thrown if not all bytes could be written.
  908. End Rem
  909. Function SaveByteArray( byteArray:Byte[],url:Object )
  910. Local stream:TStream=WriteStream( url )
  911. If Not stream Throw New TStreamWriteException
  912. stream.WriteBytes byteArray,byteArray.length
  913. stream.Close
  914. End Function
  915. Rem
  916. bbdoc: Copy stream contents to another stream
  917. about:
  918. #CopyStream copies bytes from @fromStream to @toStream Until @fromStream reaches end
  919. of file.
  920. A #TStreamWriteException is thrown if not all bytes could be written.
  921. End Rem
  922. Function CopyStream( fromStream:TStream,toStream:TStream,bufSize=4096 )
  923. Assert bufSize>0
  924. Local buf:Byte[bufSize]
  925. While Not fromStream.Eof()
  926. toStream.WriteBytes buf,fromStream.Read( buf,bufSize )
  927. Wend
  928. End Function
  929. Rem
  930. bbdoc: Copy bytes from one stream to another
  931. about:
  932. #CopyBytes copies @count bytes from @fromStream to @toStream.
  933. A #TStreamReadException is thrown if not all bytes could be read, and a
  934. #TStreamWriteException is thrown if not all bytes could be written.
  935. End Rem
  936. Function CopyBytes( fromStream:TStream,toStream:TStream,count,bufSize=4096 )
  937. Assert count>=0 And bufSize>0
  938. Local buf:Byte[bufSize]
  939. While count
  940. Local n=Min(count,bufSize)
  941. fromStream.ReadBytes buf,n
  942. toStream.WriteBytes buf,n
  943. count:-n
  944. Wend
  945. End Function
  946. Rem
  947. bbdoc: Returns a case sensitive filename if it exists from a case insensitive file path.
  948. End Rem
  949. Function CasedFileName$(path$)
  950. Local dir,sub$,s$,f$,folder$,p
  951. Local mode,size,mtime,ctime
  952. If stat_( path,mode,size,mtime,ctime )=0
  953. mode:&S_IFMT_
  954. If mode=S_IFREG_ Or mode=S_IFDIR_ Return path
  955. EndIf
  956. folder$="."
  957. For p=Len(path)-2 To 0 Step -1
  958. If path[p]=47 Exit
  959. Next
  960. If p>0
  961. sub=path[0..p]
  962. sub$=CasedFileName(sub$)
  963. If Not sub$ Return
  964. path=path$[Len(sub)+1..]
  965. folder$=sub
  966. EndIf
  967. s=path.ToLower()
  968. dir=opendir_(folder)
  969. If dir
  970. While True
  971. f=readdir_(dir)
  972. If Not f Exit
  973. If s=f.ToLower()
  974. If sub f=sub+"/"+f
  975. closedir_(dir)
  976. Return f
  977. EndIf
  978. Wend
  979. closedir_(dir)
  980. EndIf
  981. End Function