MemoryBlock.cs 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. using PaintDotNet.SystemLayer;
  2. using System;
  3. using System.Collections;
  4. using System.Drawing;
  5. using System.IO;
  6. using System.IO.Compression;
  7. using System.Runtime.Serialization;
  8. using System.Threading;
  9. namespace PaintDotNet
  10. {
  11. /// <summary>
  12. /// Manages an arbitrarily sized block of memory. You can also create child MemoryBlocks
  13. /// which reference a portion of the memory allocated by a parent MemoryBlock. If the parent
  14. /// is disposed, the children will not be valid.
  15. /// </summary>
  16. [Serializable]
  17. public unsafe sealed class MemoryBlock : IDisposable, ICloneable, IDeferredSerializable
  18. {
  19. public static int bigFile = 10000 * 10000;//int.MaxValue;
  20. // serialize 1MB at a time: this enables us to serialize very large blocks, and to conserve memory while doing so
  21. private const int serializationChunkSize = 1048576;
  22. // blocks this size or larger are allocated with AllocateLarge (VirtualAlloc) instead of Allocate (HeapAlloc)
  23. private const long largeBlockThreshold = 65536;
  24. private long length;
  25. // if parentBlock == null, then we allocated the pointer and are responsible for deallocating it
  26. // if parentBlock != null, then the parentBlock allocated it, not us
  27. [NonSerialized]
  28. public void* voidStar;
  29. [NonSerialized]
  30. private bool valid; // if voidStar is null, and this is false, we know that it's null because allocation failed. otherwise we have a real error
  31. private MemoryBlock parentBlock = null;
  32. [NonSerialized]
  33. private IntPtr bitmapHandle = IntPtr.Zero; // if allocated using the "width, height" constructor, we keep track of a bitmap handle
  34. private long bitmapWidth;
  35. private long bitmapHeight;
  36. private bool disposed = false;
  37. public MemoryBlock Parent
  38. {
  39. get
  40. {
  41. return this.parentBlock;
  42. }
  43. set
  44. {
  45. this.parentBlock = value;
  46. }
  47. }
  48. public long Length
  49. {
  50. get
  51. {
  52. if (disposed)
  53. {
  54. throw new ObjectDisposedException("MemoryBlock");
  55. }
  56. return length;
  57. }
  58. }
  59. public IntPtr Pointer
  60. {
  61. get
  62. {
  63. if (disposed)
  64. {
  65. throw new ObjectDisposedException("MemoryBlock");
  66. }
  67. return new IntPtr(voidStar);
  68. }
  69. }
  70. public IntPtr BitmapHandle
  71. {
  72. get
  73. {
  74. if (disposed)
  75. {
  76. throw new ObjectDisposedException("MemoryBlock");
  77. }
  78. return this.bitmapHandle;
  79. }
  80. }
  81. public void* VoidStar
  82. {
  83. get
  84. {
  85. if (disposed)
  86. {
  87. throw new ObjectDisposedException("MemoryBlock");
  88. }
  89. return voidStar;
  90. }
  91. set
  92. {
  93. this.voidStar = value;
  94. }
  95. }
  96. public byte this[long index]
  97. {
  98. get
  99. {
  100. if (disposed)
  101. {
  102. throw new ObjectDisposedException("MemoryBlock");
  103. }
  104. if (index < 0 || index >= length)
  105. {
  106. throw new ArgumentOutOfRangeException("index must be positive and less than Length");
  107. }
  108. unsafe
  109. {
  110. return ((byte*)this.VoidStar)[index];
  111. }
  112. }
  113. set
  114. {
  115. if (disposed)
  116. {
  117. throw new ObjectDisposedException("MemoryBlock");
  118. }
  119. if (index < 0 || index >= length)
  120. {
  121. throw new ArgumentOutOfRangeException("index must be positive and less than Length");
  122. }
  123. unsafe
  124. {
  125. ((byte*)this.VoidStar)[index] = value;
  126. }
  127. }
  128. }
  129. public bool MaySetAllowWrites
  130. {
  131. get
  132. {
  133. if (disposed)
  134. {
  135. throw new ObjectDisposedException("MemoryBlock");
  136. }
  137. if (this.parentBlock != null)
  138. {
  139. return this.parentBlock.MaySetAllowWrites;
  140. }
  141. else
  142. {
  143. return (this.length >= largeBlockThreshold && this.bitmapHandle != IntPtr.Zero);
  144. }
  145. }
  146. }
  147. /// <summary>
  148. /// Sets a flag indicating whether the memory that this instance of MemoryBlock points to
  149. /// may be written to.
  150. /// </summary>
  151. /// <remarks>
  152. /// This flag is meant to be set to false for short periods of time. The value of this
  153. /// property is not persisted with serialization.
  154. /// </remarks>
  155. public bool AllowWrites
  156. {
  157. set
  158. {
  159. if (disposed)
  160. {
  161. throw new ObjectDisposedException("MemoryBlock");
  162. }
  163. if (!MaySetAllowWrites)
  164. {
  165. throw new InvalidOperationException("May not set write protection on this memory block");
  166. }
  167. Memory.ProtectBlockLarge(new IntPtr(this.voidStar), (ulong)this.length, true, value);
  168. }
  169. }
  170. /// <summary>
  171. /// Copies bytes from one area of memory to another. Since this function works
  172. /// with MemoryBlock instances, it does bounds checking.
  173. /// </summary>
  174. /// <param name="dst">The MemoryBlock to copy bytes to.</param>
  175. /// <param name="dstOffset">The offset within dst to copy bytes to.</param>
  176. /// <param name="src">The MemoryBlock to copy bytes from.</param>
  177. /// <param name="srcOffset">The offset within src to copy bytes from.</param>
  178. /// <param name="length">The number of bytes to copy.</param>
  179. public static void CopyBlock(MemoryBlock dst, long dstOffset, MemoryBlock src, long srcOffset, long length)
  180. {
  181. if ((dstOffset + length > dst.length) || (srcOffset + length > src.length))
  182. {
  183. throw new ArgumentOutOfRangeException("", "copy ranges were out of bounds");
  184. }
  185. if (dstOffset < 0)
  186. {
  187. throw new ArgumentOutOfRangeException("dstOffset", dstOffset, "must be >= 0");
  188. }
  189. if (srcOffset < 0)
  190. {
  191. throw new ArgumentOutOfRangeException("srcOffset", srcOffset, "must be >= 0");
  192. }
  193. if (length < 0)
  194. {
  195. throw new ArgumentOutOfRangeException("length", length, "must be >= 0");
  196. }
  197. void* dstPtr = (void*)((byte*)dst.VoidStar + dstOffset);
  198. void* srcPtr = (void*)((byte*)src.VoidStar + srcOffset);
  199. Memory.Copy(dstPtr, srcPtr, (ulong)length);
  200. }
  201. /// <summary>
  202. /// Creates a new parent MemoryBlock and copies our contents into it
  203. /// </summary>
  204. object ICloneable.Clone()
  205. {
  206. if (disposed)
  207. {
  208. throw new ObjectDisposedException("MemoryBlock");
  209. }
  210. return (object)Clone();
  211. }
  212. /// <summary>
  213. /// Creates a new parent MemoryBlock and copies our contents into it
  214. /// </summary>
  215. public MemoryBlock Clone()
  216. {
  217. if (disposed)
  218. {
  219. throw new ObjectDisposedException("MemoryBlock");
  220. }
  221. MemoryBlock dupe = new MemoryBlock(this.length);
  222. CopyBlock(dupe, 0, this, 0, length);
  223. return dupe;
  224. }
  225. /// <summary>
  226. /// Creates a new MemoryBlock instance and allocates the requested number of bytes.
  227. /// </summary>
  228. /// <param name="bytes"></param>
  229. public MemoryBlock(long bytes)
  230. {
  231. if (bytes <= 0)
  232. {
  233. throw new ArgumentOutOfRangeException("bytes", bytes, "Bytes must be greater than zero");
  234. }
  235. this.length = bytes;
  236. this.parentBlock = null;
  237. if (bytes < bigFile)
  238. this.voidStar = Allocate(bytes).ToPointer();
  239. this.valid = true;
  240. }
  241. public MemoryBlock(long width, long height)
  242. {
  243. if (width < 0 && height < 0)
  244. {
  245. throw new ArgumentOutOfRangeException("width/height", new Size((int)width, (int)height), "width and height must be >= 0");
  246. }
  247. else if (width < 0)
  248. {
  249. throw new ArgumentOutOfRangeException("width", width, "width must be >= 0");
  250. }
  251. else if (height < 0)
  252. {
  253. throw new ArgumentOutOfRangeException("height", width, "height must be >= 0");
  254. }
  255. this.length = width * height * ColorBgra.SizeOf;
  256. this.parentBlock = null;
  257. if (width * height < bigFile)
  258. this.voidStar = Allocate((int)width, (int)height, out this.bitmapHandle).ToPointer();
  259. this.valid = true;
  260. this.bitmapWidth = width;
  261. this.bitmapHeight = height;
  262. }
  263. public MemoryBlock(long width, long height, long ch)
  264. {
  265. if (width < 0 && height < 0)
  266. {
  267. throw new ArgumentOutOfRangeException("width/height", new Size((int)width, (int)height), "width and height must be >= 0");
  268. }
  269. else if (width < 0)
  270. {
  271. throw new ArgumentOutOfRangeException("width", width, "width must be >= 0");
  272. }
  273. else if (height < 0)
  274. {
  275. throw new ArgumentOutOfRangeException("height", width, "height must be >= 0");
  276. }
  277. this.length = width * height * ch;
  278. this.parentBlock = null;
  279. if (width * height < bigFile)
  280. this.voidStar = Allocate((int)width, (int)height, out this.bitmapHandle).ToPointer();
  281. this.valid = true;
  282. this.bitmapWidth = width;
  283. this.bitmapHeight = height;
  284. }
  285. /// <summary>
  286. /// Creates a new MemoryBlock instance that refers to part of another MemoryBlock.
  287. /// The other MemoryBlock is the parent, and this new instance is the child.
  288. /// </summary>
  289. public unsafe MemoryBlock(MemoryBlock parentBlock, long offset, long length)
  290. {
  291. if (offset + length > parentBlock.length)
  292. {
  293. throw new ArgumentOutOfRangeException();
  294. }
  295. this.parentBlock = parentBlock;
  296. byte* bytePointer = (byte*)parentBlock.VoidStar;
  297. bytePointer += offset;
  298. this.voidStar = (void*)bytePointer;
  299. this.valid = true;
  300. this.length = length;
  301. }
  302. ~MemoryBlock()
  303. {
  304. Dispose(false);
  305. }
  306. public void Dispose()
  307. {
  308. Dispose(true);
  309. GC.SuppressFinalize(this);
  310. }
  311. private void Dispose(bool disposing)
  312. {
  313. if (!disposed)
  314. {
  315. disposed = true;
  316. if (disposing)
  317. {
  318. }
  319. if (this.valid && parentBlock == null)
  320. {
  321. if (this.bitmapHandle != IntPtr.Zero)
  322. {
  323. Memory.FreeBitmap(this.bitmapHandle, (int)this.bitmapWidth, (int)this.bitmapHeight);
  324. }
  325. else if (this.length >= largeBlockThreshold)
  326. {
  327. Memory.FreeLarge(new IntPtr(voidStar), (ulong)this.length);
  328. }
  329. else
  330. {
  331. Memory.Free(new IntPtr(voidStar));
  332. }
  333. }
  334. parentBlock = null;
  335. voidStar = null;
  336. this.valid = false;
  337. }
  338. }
  339. private static IntPtr Allocate(int width, int height, out IntPtr handle)
  340. {
  341. return Allocate(width, height, out handle, true);
  342. }
  343. private static IntPtr Allocate(int width, int height, out IntPtr handle, bool allowRetry)
  344. {
  345. IntPtr block;
  346. try
  347. {
  348. block = Memory.AllocateBitmap(width, height, out handle);
  349. }
  350. catch (OutOfMemoryException)
  351. {
  352. if (allowRetry)
  353. {
  354. Utility.GCFullCollect();
  355. return Allocate(width, height, out handle, false);
  356. }
  357. else
  358. {
  359. throw;
  360. }
  361. }
  362. return block;
  363. }
  364. private static IntPtr Allocate(long bytes)
  365. {
  366. return Allocate(bytes, true);
  367. }
  368. private static IntPtr Allocate(long bytes, bool allowRetry)
  369. {
  370. IntPtr block;
  371. try
  372. {
  373. if (bytes >= largeBlockThreshold)
  374. {
  375. block = Memory.AllocateLarge((ulong)bytes);
  376. }
  377. else
  378. {
  379. block = Memory.Allocate((ulong)bytes);
  380. }
  381. }
  382. catch (OutOfMemoryException)
  383. {
  384. if (allowRetry)
  385. {
  386. Utility.GCFullCollect();
  387. return Allocate(bytes, false);
  388. }
  389. else
  390. {
  391. throw;
  392. }
  393. }
  394. return block;
  395. }
  396. public byte[] ToByteArray()
  397. {
  398. return ToByteArray(0, this.length);
  399. }
  400. public byte[] ToByteArray(long startOffset, long lengthDesired)
  401. {
  402. if (disposed)
  403. {
  404. throw new ObjectDisposedException("MemoryBlock");
  405. }
  406. if (startOffset < 0)
  407. {
  408. throw new ArgumentOutOfRangeException("startOffset", "must be greater than or equal to zero");
  409. }
  410. if (lengthDesired < 0)
  411. {
  412. throw new ArgumentOutOfRangeException("length", "must be greater than or equal to zero");
  413. }
  414. if (startOffset + lengthDesired > this.length)
  415. {
  416. throw new ArgumentOutOfRangeException("startOffset, length", "startOffset + length must be less than Length");
  417. }
  418. byte[] dstArray = new byte[lengthDesired];
  419. byte* pbSrcArray = (byte*)this.VoidStar;
  420. fixed (byte* pbDstArray = dstArray)
  421. {
  422. Memory.Copy(pbDstArray, pbSrcArray + startOffset, (ulong)lengthDesired);
  423. }
  424. return dstArray;
  425. }
  426. private class OurSerializationException
  427. : SerializationException
  428. {
  429. public OurSerializationException()
  430. {
  431. }
  432. public OurSerializationException(string message)
  433. : base(message)
  434. {
  435. }
  436. public OurSerializationException(SerializationInfo info, StreamingContext context)
  437. : base(info, context)
  438. {
  439. }
  440. public OurSerializationException(string message, Exception innerException)
  441. : base(message, innerException)
  442. {
  443. }
  444. }
  445. private MemoryBlock(SerializationInfo info, StreamingContext context)
  446. {
  447. disposed = false;
  448. // Try to read a 64-bit value, and for backwards compatibility fall back on a 32-bit value.
  449. try
  450. {
  451. this.length = info.GetInt64("length64");
  452. }
  453. catch (SerializationException)
  454. {
  455. this.length = (long)info.GetInt32("length");
  456. }
  457. try
  458. {
  459. this.bitmapWidth = (int)info.GetInt32("bitmapWidth");
  460. this.bitmapHeight = (int)info.GetInt32("bitmapHeight");
  461. if (this.bitmapWidth != 0 || this.bitmapHeight != 0)
  462. {
  463. long bytes = (long)this.bitmapWidth * (long)this.bitmapHeight * (long)ColorBgra.SizeOf;
  464. if (bytes != this.length)
  465. {
  466. throw new ApplicationException("Invalid file format: width * height * 4 != length");
  467. }
  468. }
  469. }
  470. catch (SerializationException)
  471. {
  472. this.bitmapWidth = 0;
  473. this.bitmapHeight = 0;
  474. }
  475. bool hasParent = info.GetBoolean("hasParent");
  476. if (hasParent)
  477. {
  478. this.parentBlock = (MemoryBlock)info.GetValue("parentBlock", typeof(MemoryBlock));
  479. // Try to read a 64-bit value, and for backwards compatibility fall back on a 32-bit value.
  480. long parentOffset;
  481. try
  482. {
  483. parentOffset = info.GetInt64("parentOffset64");
  484. }
  485. catch (SerializationException)
  486. {
  487. parentOffset = (long)info.GetInt32("parentOffset");
  488. }
  489. this.voidStar = (void*)((byte*)parentBlock.VoidStar + parentOffset);
  490. this.valid = true;
  491. }
  492. else
  493. {
  494. DeferredFormatter deferredFormatter = context.Context as DeferredFormatter;
  495. bool deferred = false;
  496. // Was this stream serialized with deferment?
  497. foreach (SerializationEntry entry in info)
  498. {
  499. if (entry.Name == "deferred")
  500. {
  501. deferred = (bool)entry.Value;
  502. break;
  503. }
  504. }
  505. if (deferred && deferredFormatter != null)
  506. {
  507. // The newest PDN files use deferred deserialization. This lets us read straight from the stream,
  508. // minimizing memory use and adding the potential for multithreading
  509. // Deserialization will complete in IDeferredDeserializer.FinishDeserialization()
  510. deferredFormatter.AddDeferredObject(this, this.length);
  511. }
  512. else if (deferred && deferredFormatter == null)
  513. {
  514. throw new InvalidOperationException("stream has deferred serialization streams, but a DeferredFormatter was not provided");
  515. }
  516. else
  517. {
  518. this.voidStar = Allocate(this.length).ToPointer();
  519. this.valid = true;
  520. // Non-deferred format serializes one big byte[] chunk. This is also
  521. // how PDN files were saved with v2.1 Beta 2 and before.
  522. byte[] array = (byte[])info.GetValue("pointerData", typeof(byte[]));
  523. fixed (byte* pbArray = array)
  524. {
  525. Memory.Copy(this.VoidStar, (void*)pbArray, (ulong)array.LongLength);
  526. }
  527. }
  528. }
  529. }
  530. public void WriteFormat1Data(SerializationInfo info, StreamingContext context)
  531. {
  532. byte[] bytes = this.ToByteArray();
  533. info.AddValue("pointerData", bytes, typeof(byte[]));
  534. }
  535. public void WriteFormat2Data(SerializationInfo info, StreamingContext context)
  536. {
  537. DeferredFormatter deferred = context.Context as DeferredFormatter;
  538. if (deferred != null)
  539. {
  540. info.AddValue("deferred", true);
  541. deferred.AddDeferredObject(this, this.length);
  542. }
  543. else
  544. {
  545. WriteFormat1Data(info, context);
  546. }
  547. }
  548. private static void WriteUInt(Stream output, UInt32 theUInt)
  549. {
  550. output.WriteByte((byte)((theUInt >> 24) & 0xff));
  551. output.WriteByte((byte)((theUInt >> 16) & 0xff));
  552. output.WriteByte((byte)((theUInt >> 8) & 0xff));
  553. output.WriteByte((byte)(theUInt & 0xff));
  554. }
  555. private static uint ReadUInt(Stream output)
  556. {
  557. uint theUInt = 0;
  558. for (int i = 0; i < 4; ++i)
  559. {
  560. theUInt <<= 8;
  561. int theByte = output.ReadByte();
  562. if (theByte == -1)
  563. {
  564. throw new EndOfStreamException();
  565. }
  566. theUInt += (UInt32)theByte;
  567. }
  568. return theUInt;
  569. }
  570. // Data starts with:
  571. // 1 byte: formatVersion
  572. // 0 for compressed w/ gzip chunks
  573. // 1 for non-compressed chunks
  574. //
  575. // IF formatVersion == 0:
  576. // 4 byte uint: chunkSize
  577. //
  578. // then compute: chunkCount = (length + chunkSize - 1) / chunkSize
  579. // 'length' is written as part of the usual .NET Serialization process in GetObjectData()
  580. //
  581. // Each chunk has the following format:
  582. // 4 byte uint: chunkNumber
  583. // 4 byte uint: raw dataSize 'N' bytes (this will expand to more bytes after decompression)
  584. // N bytes: data
  585. //
  586. // The chunks may appear in any order; that is, chunk N is not necessarily followed by N+1,
  587. // nor is it necessarily preceded by N-1.
  588. //
  589. // uints are written in big-endian order.
  590. private class DecompressChunkParms
  591. {
  592. private byte[] compressedBytes;
  593. private uint chunkSize;
  594. private long chunkOffset;
  595. private DeferredFormatter deferredFormatter;
  596. private ArrayList exceptions;
  597. public byte[] CompressedBytes
  598. {
  599. get
  600. {
  601. return compressedBytes;
  602. }
  603. }
  604. public uint ChunkSize
  605. {
  606. get
  607. {
  608. return chunkSize;
  609. }
  610. }
  611. public long ChunkOffset
  612. {
  613. get
  614. {
  615. return chunkOffset;
  616. }
  617. }
  618. public DeferredFormatter DeferredFormatter
  619. {
  620. get
  621. {
  622. return deferredFormatter;
  623. }
  624. }
  625. public ArrayList Exceptions
  626. {
  627. get
  628. {
  629. return exceptions;
  630. }
  631. }
  632. public DecompressChunkParms(byte[] compressedBytes, uint chunkSize, long chunkOffset, DeferredFormatter deferredFormatter, ArrayList exceptions)
  633. {
  634. this.compressedBytes = compressedBytes;
  635. this.chunkSize = chunkSize;
  636. this.chunkOffset = chunkOffset;
  637. this.deferredFormatter = deferredFormatter;
  638. this.exceptions = exceptions;
  639. }
  640. }
  641. private void DecompressChunk(object context)
  642. {
  643. DecompressChunkParms parms = (DecompressChunkParms)context;
  644. try
  645. {
  646. DecompressChunk(parms.CompressedBytes, parms.ChunkSize, parms.ChunkOffset, parms.DeferredFormatter);
  647. }
  648. catch (Exception ex)
  649. {
  650. parms.Exceptions.Add(ex);
  651. }
  652. }
  653. private void DecompressChunk(byte[] compressedBytes, uint chunkSize, long chunkOffset, DeferredFormatter deferredFormatter)
  654. {
  655. // decompress data
  656. MemoryStream compressedStream = new MemoryStream(compressedBytes, false);
  657. GZipStream gZipStream = new GZipStream(compressedStream, CompressionMode.Decompress, true);
  658. byte[] decompressedBytes = new byte[chunkSize];
  659. int dstOffset = 0;
  660. while (dstOffset < decompressedBytes.Length)
  661. {
  662. int bytesRead = gZipStream.Read(decompressedBytes, dstOffset, (int)chunkSize - dstOffset);
  663. if (bytesRead == 0)
  664. {
  665. throw new SerializationException("ran out of data to decompress");
  666. }
  667. dstOffset += bytesRead;
  668. deferredFormatter.ReportBytes((long)bytesRead);
  669. }
  670. // copy data
  671. fixed (byte* pbDecompressedBytes = decompressedBytes)
  672. {
  673. byte* pbDst = (byte*)this.VoidStar + chunkOffset;
  674. Memory.Copy(pbDst, pbDecompressedBytes, (ulong)chunkSize);
  675. }
  676. }
  677. void IDeferredSerializable.FinishDeserialization(Stream input, DeferredFormatter context)
  678. {
  679. // Allocate the memory
  680. if (this.bitmapWidth != 0 && this.bitmapHeight != 0)
  681. {
  682. this.voidStar = Allocate((int)this.bitmapWidth, (int)this.bitmapHeight, out this.bitmapHandle).ToPointer();
  683. this.valid = true;
  684. }
  685. else
  686. {
  687. this.voidStar = Allocate(this.length).ToPointer();
  688. this.valid = true;
  689. }
  690. // formatVersion should equal 0
  691. int formatVersion = input.ReadByte();
  692. if (formatVersion == -1)
  693. {
  694. throw new EndOfStreamException();
  695. }
  696. if (formatVersion != 0 && formatVersion != 1)
  697. {
  698. throw new SerializationException("formatVersion was neither zero nor one");
  699. }
  700. // chunkSize
  701. uint chunkSize = ReadUInt(input);
  702. PaintDotNet.Threading.ThreadPool threadPool = new PaintDotNet.Threading.ThreadPool(Processor.LogicalCpuCount);
  703. ArrayList exceptions = new ArrayList(Processor.LogicalCpuCount);
  704. WaitCallback callback = new WaitCallback(DecompressChunk);
  705. // calculate chunkCount
  706. uint chunkCount = (uint)((this.length + (long)chunkSize - 1) / (long)chunkSize);
  707. bool[] chunksFound = new bool[chunkCount];
  708. for (uint i = 0; i < chunkCount; ++i)
  709. {
  710. // chunkNumber
  711. uint chunkNumber = ReadUInt(input);
  712. if (chunkNumber >= chunkCount)
  713. {
  714. throw new SerializationException("chunkNumber read from stream is out of bounds");
  715. }
  716. if (chunksFound[chunkNumber])
  717. {
  718. throw new SerializationException("already encountered chunk #" + chunkNumber.ToString());
  719. }
  720. chunksFound[chunkNumber] = true;
  721. // dataSize
  722. uint dataSize = ReadUInt(input);
  723. // calculate chunkOffset
  724. long chunkOffset = (long)chunkNumber * (long)chunkSize;
  725. // calculate decompressed chunkSize
  726. uint thisChunkSize = Math.Min(chunkSize, (uint)(this.length - chunkOffset));
  727. // bounds checking
  728. if (chunkOffset < 0 || chunkOffset >= this.length || chunkOffset + thisChunkSize > this.length)
  729. {
  730. throw new SerializationException("data was specified to be out of bounds");
  731. }
  732. // read compressed data
  733. byte[] compressedBytes = new byte[dataSize];
  734. Utility.ReadFromStream(input, compressedBytes, 0, compressedBytes.Length);
  735. // decompress data
  736. if (formatVersion == 0)
  737. {
  738. DecompressChunkParms parms = new DecompressChunkParms(compressedBytes, thisChunkSize, chunkOffset, context, exceptions);
  739. threadPool.QueueUserWorkItem(callback, parms);
  740. }
  741. else
  742. {
  743. fixed (byte* pbSrc = compressedBytes)
  744. {
  745. Memory.Copy((void*)((byte*)this.VoidStar + chunkOffset), (void*)pbSrc, thisChunkSize);
  746. }
  747. }
  748. }
  749. threadPool.Drain();
  750. if (exceptions.Count > 0)
  751. {
  752. throw new SerializationException("Exception thrown by worker thread", (Exception)exceptions[0]);
  753. }
  754. }
  755. private class SerializeChunkParms
  756. {
  757. private Stream output;
  758. private uint chunkNumber;
  759. private long chunkOffset;
  760. private long chunkSize;
  761. private object previousLock;
  762. private DeferredFormatter deferredFormatter;
  763. private ArrayList exceptions;
  764. public Stream Output
  765. {
  766. get
  767. {
  768. return output;
  769. }
  770. }
  771. public uint ChunkNumber
  772. {
  773. get
  774. {
  775. return chunkNumber;
  776. }
  777. }
  778. public long ChunkOffset
  779. {
  780. get
  781. {
  782. return chunkOffset;
  783. }
  784. }
  785. public long ChunkSize
  786. {
  787. get
  788. {
  789. return chunkSize;
  790. }
  791. }
  792. public object PreviousLock
  793. {
  794. get
  795. {
  796. return (previousLock == null) ? this : previousLock;
  797. }
  798. }
  799. public DeferredFormatter DeferredFormatter
  800. {
  801. get
  802. {
  803. return deferredFormatter;
  804. }
  805. }
  806. public ArrayList Exceptions
  807. {
  808. get
  809. {
  810. return exceptions;
  811. }
  812. }
  813. public SerializeChunkParms(Stream output, uint chunkNumber, long chunkOffset, long chunkSize, object previousLock,
  814. DeferredFormatter deferredFormatter, ArrayList exceptions)
  815. {
  816. this.output = output;
  817. this.chunkNumber = chunkNumber;
  818. this.chunkOffset = chunkOffset;
  819. this.chunkSize = chunkSize;
  820. this.previousLock = previousLock;
  821. this.deferredFormatter = deferredFormatter;
  822. this.exceptions = exceptions;
  823. }
  824. }
  825. private void SerializeChunk(object context)
  826. {
  827. SerializeChunkParms parms = (SerializeChunkParms)context;
  828. try
  829. {
  830. SerializeChunk(parms.Output, parms.ChunkNumber, parms.ChunkOffset, parms.ChunkSize, parms, parms.PreviousLock, parms.DeferredFormatter);
  831. }
  832. catch (Exception ex)
  833. {
  834. parms.Exceptions.Add(ex);
  835. }
  836. }
  837. private void SerializeChunk(Stream output, uint chunkNumber, long chunkOffset, long chunkSize,
  838. object currentLock, object previousLock, DeferredFormatter deferredFormatter)
  839. {
  840. lock (currentLock)
  841. {
  842. bool useCompression = deferredFormatter.UseCompression;
  843. MemoryStream chunkOutput = new MemoryStream();
  844. // chunkNumber
  845. WriteUInt(chunkOutput, chunkNumber);
  846. // dataSize
  847. long rewindPos = chunkOutput.Position;
  848. WriteUInt(chunkOutput, 0); // we'll rewind and write this later
  849. long startPos = chunkOutput.Position;
  850. // Compress data
  851. byte[] array = new byte[chunkSize];
  852. fixed (byte* pbArray = array)
  853. {
  854. Memory.Copy(pbArray, (byte*)this.VoidStar + chunkOffset, (ulong)chunkSize);
  855. }
  856. chunkOutput.Flush();
  857. if (useCompression)
  858. {
  859. GZipStream gZipStream = new GZipStream(chunkOutput, CompressionMode.Compress, true);
  860. gZipStream.Write(array, 0, array.Length);
  861. gZipStream.Close();
  862. }
  863. else
  864. {
  865. chunkOutput.Write(array, 0, array.Length);
  866. }
  867. long endPos = chunkOutput.Position;
  868. // dataSize
  869. chunkOutput.Position = rewindPos;
  870. uint dataSize = (uint)(endPos - startPos);
  871. WriteUInt(chunkOutput, dataSize);
  872. // bytes
  873. chunkOutput.Flush();
  874. lock (previousLock)
  875. {
  876. output.Write(chunkOutput.GetBuffer(), 0, (int)chunkOutput.Length);
  877. deferredFormatter.ReportBytes(chunkSize);
  878. }
  879. }
  880. }
  881. void IDeferredSerializable.FinishSerialization(Stream output, DeferredFormatter context)
  882. {
  883. bool useCompression = context.UseCompression;
  884. // formatVersion = 0 for GZIP, or 1 for uncompressed
  885. if (useCompression)
  886. {
  887. output.WriteByte(0);
  888. }
  889. else
  890. {
  891. output.WriteByte(1);
  892. }
  893. // chunkSize
  894. WriteUInt(output, serializationChunkSize);
  895. uint chunkCount = (uint)((this.length + (long)serializationChunkSize - 1) / (long)serializationChunkSize);
  896. PaintDotNet.Threading.ThreadPool threadPool = new PaintDotNet.Threading.ThreadPool(Processor.LogicalCpuCount);
  897. ArrayList exceptions = ArrayList.Synchronized(new ArrayList(Processor.LogicalCpuCount));
  898. WaitCallback callback = new WaitCallback(SerializeChunk);
  899. object previousLock = null;
  900. for (uint chunk = 0; chunk < chunkCount; ++chunk)
  901. {
  902. long chunkOffset = (long)chunk * (long)serializationChunkSize;
  903. uint chunkSize = Math.Min((uint)serializationChunkSize, (uint)(this.length - chunkOffset));
  904. SerializeChunkParms parms = new SerializeChunkParms(output, chunk, chunkOffset, chunkSize, previousLock, context, exceptions);
  905. threadPool.QueueUserWorkItem(callback, parms);
  906. previousLock = parms;
  907. }
  908. threadPool.Drain();
  909. output.Flush();
  910. if (exceptions.Count > 0)
  911. {
  912. throw new SerializationException("Exception thrown by worker thread", (Exception)exceptions[0]);
  913. }
  914. return;
  915. }
  916. public void GetObjectData(SerializationInfo info, StreamingContext context)
  917. {
  918. if (disposed)
  919. {
  920. throw new ObjectDisposedException("MemoryBlock");
  921. }
  922. info.AddValue("length64", this.length);
  923. if (this.bitmapWidth != 0 || this.bitmapHeight != 0 || this.bitmapHandle != IntPtr.Zero)
  924. {
  925. info.AddValue("bitmapWidth", bitmapWidth);
  926. info.AddValue("bitmapHeight", bitmapHeight);
  927. }
  928. info.AddValue("hasParent", this.parentBlock != null);
  929. if (parentBlock == null)
  930. {
  931. WriteFormat2Data(info, context);
  932. }
  933. else
  934. {
  935. info.AddValue("parentBlock", parentBlock, typeof(MemoryBlock));
  936. info.AddValue("parentOffset64", (long)((byte*)voidStar - (byte*)parentBlock.VoidStar));
  937. }
  938. }
  939. }
  940. }