databuffer.monkey2 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. Namespace std.memory
  2. Using std.stream
  3. #rem monkeydoc DataBuffer class.
  4. #end
  5. Class DataBuffer Extends std.resource.Resource
  6. #rem monkeydoc Creates a new data buffer.
  7. Creates a data buffer with a newly allocated or existing block of memory.
  8. If you provide a `start` pointer value when creating a data buffer, it should point to a valid block of memory at least `length` bytes in size.
  9. This pointer value will be used as the 'base address' when poking/peeking the data buffer, and will be returned by the [[Data]] property.
  10. Such data buffer cannot be [[Resize]]d.
  11. If you do not provide a `start` value, a block of memory `length` bytes long is allocated for you. This memory will be released when the data
  12. buffer is discarded or becomes unreachable by the garbage collector.
  13. The new databuffer initally uses little endian byte order. You can change this via the ByteOrder property.
  14. When you have finished with the data buffer, you should call its inherited [[Resource.Discard]] method.
  15. @example
  16. \#Import "<std>"
  17. Using std.memory
  18. Function Main()
  19. Local buf:=New DataBuffer( 10 )
  20. Print buf.Length
  21. For Local i:=0 Until 10
  22. buf.PokeByte( i,i*2 )
  23. Next
  24. For Local i:=0 Until 10
  25. Print buf.PeekByte( i )
  26. Next
  27. End
  28. @end
  29. @param start The start of the data buffer memory.
  30. @param length The length of the data buffer memory in bytes.
  31. @param byteOrder Initial byte order of the data.
  32. #end
  33. Method New( length:Int,byteOrder:ByteOrder=std.memory.ByteOrder.LittleEndian )
  34. _data=Cast<UByte Ptr>( GCMalloc( length ) )
  35. _length=length
  36. _byteOrder=byteOrder
  37. _swapEndian=(_byteOrder=ByteOrder.BigEndian)
  38. _free=True
  39. End
  40. Method New( data:Void Ptr,length:Int,byteOrder:ByteOrder=std.memory.ByteOrder.LittleEndian )
  41. _data=Cast<UByte Ptr>( data )
  42. _length=length
  43. _byteOrder=byteOrder
  44. _swapEndian=(_byteOrder=ByteOrder.BigEndian)
  45. _free=False
  46. End
  47. #rem monkeydoc A raw pointer to the databuffer's internal memory.
  48. Note: This pointer will change if the databuffer is resized using Resize.
  49. #end
  50. Property Data:UByte Ptr()
  51. Return _data
  52. End
  53. #rem monkeydoc The length of the databuffer in bytes.
  54. #end
  55. Property Length:Int()
  56. Return _length
  57. End
  58. #rem monkeydoc The byte order of the databuffer.
  59. #end
  60. Property ByteOrder:ByteOrder()
  61. Return _byteOrder
  62. Setter( byteOrder:ByteOrder )
  63. _byteOrder=byteOrder
  64. _swapEndian=(_byteOrder=ByteOrder.BigEndian)
  65. End
  66. #rem monkeydoc Resizes the databuffer.
  67. Note: This method reallocates the internal memory buffer and will cause the [[Data]] property to change.
  68. @param length The new length of the databuffer.
  69. #end
  70. Method Resize( length:Int )
  71. If Not _free RuntimeError( "DataBuffer memory cannot be resized" )
  72. Local data:=Cast<UByte Ptr>( GCMalloc( length ) )
  73. libc.memcpy( data,_data,Min( length,_length ) )
  74. GCFree( _data )
  75. _length=length
  76. _data=data
  77. End
  78. #rem monkeydoc Reads a byte from the databuffer.
  79. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  80. @param offset The offset to read.
  81. @return The byte read from `offset`.
  82. #end
  83. Method PeekByte:Byte( offset:Int )
  84. DebugAssert( offset>=0 And offset<_length )
  85. Return Cast<Byte Ptr>( _data+offset )[0]
  86. End
  87. #rem monkeydoc Reads a ubyte from the databuffer.
  88. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  89. @param offset The offset to read.
  90. @return The ubyte read from `offset`.
  91. #end
  92. Method PeekUByte:UByte( offset:Int )
  93. DebugAssert( offset>=0 And offset<_length )
  94. Return Cast<UByte Ptr>( _data+offset )[0]
  95. End
  96. #rem monkeydoc Reads a 16 bit short from the databuffer.
  97. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  98. @param offset The offset to read.
  99. @return The short read from `offset`.
  100. #end
  101. Method PeekShort:Short( offset:Int )
  102. DebugAssert( offset>=0 And offset<_length-1 )
  103. Local t:=Cast<Short Ptr>( _data+offset )[0]
  104. If _swapEndian Swap2( Varptr t )
  105. Return t
  106. End
  107. #rem monkeydoc Reads a 16 bit ushort from the databuffer.
  108. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  109. @param offset The offset to read.
  110. @return The ushort read from `offset`.
  111. #end
  112. Method PeekUShort:UShort( offset:Int )
  113. DebugAssert( offset>=0 And offset<_length-1 )
  114. Local t:=Cast<UShort Ptr>( _data+offset )[0]
  115. If _swapEndian Swap2( Varptr t )
  116. Return t
  117. End
  118. #rem monkeydoc Reads a 32 bit int from the databuffer.
  119. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  120. @param offset The offset to read.
  121. @return The int read from `offset`.
  122. #end
  123. Method PeekInt:Int( offset:Int )
  124. DebugAssert( offset>=0 And offset<_length-3 )
  125. Local t:=Cast<Int Ptr>( _data+offset )[0]
  126. If _swapEndian Swap4( Varptr t )
  127. Return t
  128. End
  129. #rem monkeydoc Reads a 32 bit uint from the databuffer.
  130. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  131. @param offset The offset to read.
  132. @return The uint read from `offset`.
  133. #end
  134. Method PeekUInt:UInt( offset:Int )
  135. DebugAssert( offset>=0 And offset<_length-3 )
  136. Local t:=Cast<UInt Ptr>( _data+offset )[0]
  137. If _swapEndian Swap4( Varptr t )
  138. Return t
  139. End
  140. #rem monkeydoc Reads a 64 bit long from the databuffer.
  141. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  142. @param offset The offset to read.
  143. @return The long read from `offset`.
  144. #end
  145. Method PeekLong:Long( offset:Int )
  146. DebugAssert( offset>=0 And offset<_length-7 )
  147. Local t:=Cast<Long Ptr>( _data+offset )[0]
  148. If _swapEndian Swap8( Varptr t )
  149. Return t
  150. End
  151. #rem monkeydoc Reads a 64 bit ulong from the databuffer.
  152. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  153. @param offset The offset to read.
  154. @return The ulong read from `offset`.
  155. #end
  156. Method PeekULong:ULong( offset:Int )
  157. DebugAssert( offset>=0 And offset<_length-7 )
  158. Local t:=Cast<ULong Ptr>( _data+offset )[0]
  159. If _swapEndian Swap8( Varptr t )
  160. Return t
  161. End
  162. #rem monkeydoc Reads a 32 bit float from the databuffer.
  163. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  164. @param offset The offset to read.
  165. @return The float read from `offset`.
  166. #end
  167. Method PeekFloat:Float( offset:Int )
  168. DebugAssert( offset>=0 And offset<_length-3 )
  169. Local t:=Cast<Float Ptr>( _data+offset )[0]
  170. If _swapEndian Swap4( Varptr t )
  171. Return t
  172. End
  173. #rem monkeydoc Reads a 64 bit double from the databuffer.
  174. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  175. @param offset The offset to read.
  176. @return The double read from `offset`.
  177. #end
  178. Method PeekDouble:Double( offset:Int )
  179. DebugAssert( offset>=0 And offset<_length-7 )
  180. Local t:=Cast<Double Ptr>( _data+offset )[0]
  181. If _swapEndian Swap8( Varptr t )
  182. Return t
  183. End
  184. #rem monkeydoc Reads a string from the databuffer.
  185. If `count` is omitted, all bytes from `offset` until the end of the data buffer are read.
  186. @param offset Byte offset to read the string.
  187. @param count Number of bytes to read.
  188. #end
  189. Method PeekString:String( offset:Int )
  190. DebugAssert( offset>=0 And offset<=_length )
  191. Return PeekString( offset,_length-offset )
  192. End
  193. Method PeekString:String( offset:Int,count:Int )
  194. DebugAssert( offset>=0 And count>=0 And offset+count<=_length )
  195. Return String.FromCString( _data+offset,count )
  196. End
  197. #rem monkeydoc Reads a null terminated CString from the databuffer.
  198. #end
  199. Method PeekCString:String( offset:Int )
  200. DebugAssert( offset>=0 And offset<=_length )
  201. Local i:=offset
  202. While i<_length And _data[i]
  203. i+=1
  204. Wend
  205. Return PeekString( offset,i-offset )
  206. End
  207. #rem monkeydoc Writes a byte to the databuffer.
  208. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  209. @param offset The offset to write.
  210. #end
  211. Method PokeByte( offset:Int,value:Byte )
  212. DebugAssert( offset>=0 And offset<_length )
  213. Cast<Byte Ptr>( _data+offset )[0]=value
  214. End
  215. #rem monkeydoc Writes an unsigned byte to the databuffer.
  216. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  217. @param offset The offset to write.
  218. #end
  219. Method PokeUByte( offset:Int,value:UByte )
  220. DebugAssert( offset>=0 And offset<_length )
  221. Cast<UByte Ptr>( _data+offset )[0]=value
  222. End
  223. #rem monkeydoc Writes a 16 bit short to the databuffer
  224. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  225. @param offset The offset to write.
  226. #end
  227. Method PokeShort( offset:Int,value:Short )
  228. DebugAssert( offset>=0 And offset<_length-1 )
  229. If _swapEndian Swap2( Varptr value )
  230. Cast<Short Ptr>( _data+offset )[0]=value
  231. End
  232. #rem monkeydoc Writes a 16 bit unsigned short to the databuffer
  233. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  234. @param offset The offset to write.
  235. #end
  236. Method PokeUShort( offset:Int,value:UShort )
  237. DebugAssert( offset>=0 And offset<_length-1 )
  238. If _swapEndian Swap2( Varptr value )
  239. Cast<UShort Ptr>( _data+offset )[0]=value
  240. End
  241. #rem monkeydoc Writes a 32 bit int to the databuffer
  242. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  243. @param offset The offset to write.
  244. #end
  245. Method PokeInt( offset:Int,value:Int )
  246. DebugAssert( offset>=0 And offset<_length-3 )
  247. If _swapEndian Swap4( Varptr value )
  248. Cast<Int Ptr>( _data+offset )[0]=value
  249. End
  250. #rem monkeydoc Writes a 32 bit unsigned int to the databuffer
  251. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  252. @param offset The offset to write.
  253. #end
  254. Method PokeUInt( offset:Int,value:UInt )
  255. DebugAssert( offset>=0 And offset<_length-3 )
  256. If _swapEndian Swap4( Varptr value )
  257. Cast<UInt Ptr>( _data+offset )[0]=value
  258. End
  259. #rem monkeydoc Writes a 64 bit long to the databuffer
  260. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  261. @param offset The offset to write.
  262. #end
  263. Method PokeLong( offset:Int,value:Long )
  264. DebugAssert( offset>=0 And offset<_length-7 )
  265. If _swapEndian Swap8( Varptr value )
  266. Cast<Long Ptr>( _data+offset )[0]=value
  267. End
  268. #rem monkeydoc Writes a 64 bit unsigned long to the databuffer
  269. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  270. @param offset The offset to write.
  271. #end
  272. Method PokeULong( offset:Int,value:ULong )
  273. DebugAssert( offset>=0 And offset<_length-7 )
  274. If _swapEndian Swap8( Varptr value )
  275. Cast<ULong Ptr>( _data+offset )[0]=value
  276. End
  277. #rem monkeydoc Writes a 32 bit float to the databuffer
  278. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  279. @param offset The offset to write.
  280. #end
  281. Method PokeFloat( offset:Int,value:Float )
  282. DebugAssert( offset>=0 And offset<_length-3 )
  283. If _swapEndian Swap4( Varptr value )
  284. Cast<Float Ptr>( _data+offset )[0]=value
  285. End
  286. #rem monkeydoc Writes a 64 bit double to the databuffer
  287. In debug builds, a runtime error will occur if `offset` is outside the range of the databuffer.
  288. @param offset The offset to write.
  289. #end
  290. Method PokeDouble( offset:Int,value:Double )
  291. DebugAssert( offset>=0 And offset<_length-7 )
  292. If _swapEndian Swap8( Varptr value )
  293. Cast<Double Ptr>( _data+offset )[0]=value
  294. End
  295. #rem monkeydoc Write a string to the databuffer.
  296. If there is not enough room in the data buffer, the string data is truncated.
  297. The string is written in utf8 format, but no null terminator is written. Use [[PokeCString]] to write a null terminated string.
  298. @param offset Byte offset to write the string.
  299. @param value The string to write.
  300. #end
  301. Method PokeString( offset:Int,value:String )
  302. DebugAssert( offset>=0 And offset<=_length )
  303. Local count:=value.CStringLength
  304. If offset+count>_length count=_length-offset
  305. value.ToCString( _data+offset,count )
  306. End
  307. #rem monkeydoc Writes a null terminated CString to the data buffer.
  308. #end
  309. Method PokeCString( offset:Int,value:String )
  310. DebugAssert( offset>=0 And offset<=_length )
  311. value.ToCString( _data+offset,_length-offset )
  312. End
  313. #rem monkeydoc Creates a slice of the databuffer.
  314. #end
  315. Method Slice:DataBuffer( from:Int=0 )
  316. Return Slice( from,_length )
  317. End
  318. Method Slice:DataBuffer( from:Int,term:int )
  319. If from<0
  320. from+=_length
  321. Else If from>_length
  322. from=_length
  323. Endif
  324. If term<0
  325. term+=_length
  326. If term<from term=from
  327. Else If term<from
  328. term=from
  329. Else If term>_length
  330. term=_length
  331. Endif
  332. Local newlen:=term-from
  333. Local data:=New DataBuffer( newlen )
  334. CopyTo( data,from,0,newlen )
  335. Return data
  336. End
  337. #rem monkeydoc Copies databuffer data to another databuffer.
  338. In debug builds, a runtime error while occur if an attempt it made to copy data outside the range of either databuffer.
  339. @param dst The destination databuffer.
  340. @param srcOffset The starting byte offset in this databuffer to copy from.
  341. @param dstOffset The starting byte offest in the destination databuffer to copy to.
  342. @param count The number of bytes to copy.
  343. #end
  344. Method CopyTo( dst:DataBuffer,srcOffset:Int,dstOffset:Int,count:Int )
  345. DebugAssert( srcOffset>=0 And srcOffset+count<=_length And dstOffset>=0 And dstOffset+count<=dst._length )
  346. libc.memmove( dst._data+dstOffset,_data+srcOffset,count )
  347. End
  348. #rem monkeydoc Compresses data buffer.
  349. The `compressionLevel` parameter should be a value from 0 to 9 (inclusive), or -1 for default compression (currently 6).
  350. A compreesion level of 1 gives best speed, 9 gives best compression.
  351. This method prepends an 8 byte 'header' to the returned data.
  352. #end
  353. Method Compress:DataBuffer( compressionLevel:Int=-1 )
  354. Assert( compressionLevel>=-1 And compressionLevel<=9,"Compression level out of range" )
  355. Local destLen:=zlib.compressBound( Length )
  356. Local dest:=New DataBuffer( destLen+8 )
  357. dest.PokeUInt( 0,Length )
  358. dest.PokeUInt( 4,~Length )
  359. Local destLenOut:=destLen
  360. Local r:=zlib.compress2( dest.Data+8,Cast<zlib.z_uLong Ptr>( Varptr destLenOut ),Data,Length,compressionLevel )
  361. If r<>zlib.Z_OK RuntimeError( "Error compressing data: zlib return code="+r )
  362. If destLenOut=destLen Return dest
  363. Local dest2:=New DataBuffer( destLenOut+8 )
  364. dest.CopyTo( dest2,0,0,destLenOut+8 )
  365. dest.Discard()
  366. Return dest2
  367. End
  368. #rem monkeydoc Decompresses data buffer.
  369. If there was an error decompressing the data, null is returned. Erros can occur if the compressed data
  370. has somehow been corrupted.
  371. #end
  372. Method Decompress:DataBuffer()
  373. If Length<8 Or PeekUInt( 0 )<>~PeekUInt( 4 ) Return Null
  374. Local destLen:=PeekUInt( 0 )
  375. Local dest:=New DataBuffer( destLen )
  376. Local r:=zlib.uncompress( dest.Data,Cast<zlib.z_uLong Ptr>( Varptr destLen ),Data+8,Length-8 )
  377. If r<>zlib.Z_OK
  378. dest.Discard()
  379. Return Null
  380. Endif
  381. Return dest
  382. End
  383. #rem monkeydoc Saves the contents of the databuffer to a file.
  384. @param path The file path.
  385. @return True if successful, false if `path` could not be opened or not all data could be written.
  386. #end
  387. Method Save:Bool( path:String )
  388. Local stream:=Stream.Open( path,"w" )
  389. If Not stream Return False
  390. Local n:=stream.Write( _data,_length )
  391. stream.Close()
  392. Return n=_length
  393. End
  394. #rem monkeydoc Creates a databuffer with the contents of a file.
  395. @param path The file path.
  396. @return A new databuffer containing the contents of `path`, or null if `path` could not be opened.
  397. #end
  398. Function Load:DataBuffer( path:String )
  399. Local stream:=Stream.Open( path,"r" )
  400. If Not stream Return Null
  401. Local data:=stream.ReadAll()
  402. stream.Close()
  403. Return data
  404. End
  405. Protected
  406. #rem monkeydoc @hidden
  407. #end
  408. Method OnDiscard() Override
  409. If _free GCFree( _data )
  410. _data=Null
  411. End
  412. #rem monkeydoc @hidden
  413. #end
  414. Method OnFinalize() Override
  415. If _free GCFree( _data )
  416. End
  417. Private
  418. Field _data:UByte Ptr
  419. Field _length:Int
  420. Field _byteOrder:ByteOrder
  421. Field _swapEndian:Bool
  422. Field _free:Bool
  423. Function Swap2( v:Void Ptr )
  424. Local t:=Cast<UShort Ptr>( v )[0]
  425. Cast<UShort Ptr>( v )[0]=(t Shr 8 & $ff) | (t & $ff) Shl 8
  426. End
  427. Function Swap4( v:Void Ptr )
  428. Local t:=Cast<UInt Ptr>( v )[0]
  429. Cast<UInt Ptr>( v )[0]=(t Shr 24 & $ff) | (t & $ff) Shl 24 | (t Shr 8 & $ff00) | (t & $ff00) Shl 8
  430. End
  431. Function Swap8( v:Void Ptr )
  432. Local t:=Cast<ULong Ptr>( v )[0]
  433. Cast<ULong Ptr>( v )[0]=(t Shr 56 & $ff) | (t & $ff) Shl 56 | (t Shr 40 & $ff00) | (t & $ff00) Shl 40 | (t Shr 24 & $ff0000) | (t & $ff0000) Shl 24 | (t Shr 8 & $ff000000) | (t & $ff000000) Shl 8
  434. End
  435. End