MemoryBlock.cs 35 KB

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