~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Util/TemporaryBuffer.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using System;
 
45
using System.IO;
 
46
using NGit;
 
47
using NGit.Util;
 
48
using Sharpen;
 
49
 
 
50
namespace NGit.Util
 
51
{
 
52
        /// <summary>A fully buffered output stream.</summary>
 
53
        /// <remarks>
 
54
        /// A fully buffered output stream.
 
55
        /// <p>
 
56
        /// Subclasses determine the behavior when the in-memory buffer capacity has been
 
57
        /// exceeded and additional bytes are still being received for output.
 
58
        /// </remarks>
 
59
        public abstract class TemporaryBuffer : OutputStream
 
60
        {
 
61
                /// <summary>Default limit for in-core storage.</summary>
 
62
                /// <remarks>Default limit for in-core storage.</remarks>
 
63
                protected internal const int DEFAULT_IN_CORE_LIMIT = 1024 * 1024;
 
64
 
 
65
                /// <summary>Chain of data, if we are still completely in-core; otherwise null.</summary>
 
66
                /// <remarks>Chain of data, if we are still completely in-core; otherwise null.</remarks>
 
67
                private AList<TemporaryBuffer.Block> blocks;
 
68
 
 
69
                /// <summary>Maximum number of bytes we will permit storing in memory.</summary>
 
70
                /// <remarks>
 
71
                /// Maximum number of bytes we will permit storing in memory.
 
72
                /// <p>
 
73
                /// When this limit is reached the data will be shifted to a file on disk,
 
74
                /// preventing the JVM heap from growing out of control.
 
75
                /// </remarks>
 
76
                private int inCoreLimit;
 
77
 
 
78
                /// <summary>
 
79
                /// If
 
80
                /// <see cref="inCoreLimit">inCoreLimit</see>
 
81
                /// has been reached, remainder goes here.
 
82
                /// </summary>
 
83
                private OutputStream overflow;
 
84
 
 
85
                /// <summary>Create a new empty temporary buffer.</summary>
 
86
                /// <remarks>Create a new empty temporary buffer.</remarks>
 
87
                /// <param name="limit">
 
88
                /// maximum number of bytes to store in memory before entering the
 
89
                /// overflow output path.
 
90
                /// </param>
 
91
                protected internal TemporaryBuffer(int limit)
 
92
                {
 
93
                        inCoreLimit = limit;
 
94
                        Reset();
 
95
                }
 
96
 
 
97
                /// <exception cref="System.IO.IOException"></exception>
 
98
                public override void Write(int b)
 
99
                {
 
100
                        if (overflow != null)
 
101
                        {
 
102
                                overflow.Write(b);
 
103
                                return;
 
104
                        }
 
105
                        TemporaryBuffer.Block s = Last();
 
106
                        if (s.IsFull())
 
107
                        {
 
108
                                if (ReachedInCoreLimit())
 
109
                                {
 
110
                                        overflow.Write(b);
 
111
                                        return;
 
112
                                }
 
113
                                s = new TemporaryBuffer.Block();
 
114
                                blocks.AddItem(s);
 
115
                        }
 
116
                        s.buffer[s.count++] = unchecked((byte)b);
 
117
                }
 
118
 
 
119
                /// <exception cref="System.IO.IOException"></exception>
 
120
                public override void Write(byte[] b, int off, int len)
 
121
                {
 
122
                        if (overflow == null)
 
123
                        {
 
124
                                while (len > 0)
 
125
                                {
 
126
                                        TemporaryBuffer.Block s = Last();
 
127
                                        if (s.IsFull())
 
128
                                        {
 
129
                                                if (ReachedInCoreLimit())
 
130
                                                {
 
131
                                                        break;
 
132
                                                }
 
133
                                                s = new TemporaryBuffer.Block();
 
134
                                                blocks.AddItem(s);
 
135
                                        }
 
136
                                        int n = Math.Min(s.buffer.Length - s.count, len);
 
137
                                        System.Array.Copy(b, off, s.buffer, s.count, n);
 
138
                                        s.count += n;
 
139
                                        len -= n;
 
140
                                        off += n;
 
141
                                }
 
142
                        }
 
143
                        if (len > 0)
 
144
                        {
 
145
                                overflow.Write(b, off, len);
 
146
                        }
 
147
                }
 
148
 
 
149
                /// <summary>Dumps the entire buffer into the overflow stream, and flushes it.</summary>
 
150
                /// <remarks>Dumps the entire buffer into the overflow stream, and flushes it.</remarks>
 
151
                /// <exception cref="System.IO.IOException">
 
152
                /// the overflow stream cannot be started, or the buffer contents
 
153
                /// cannot be written to it, or it failed to flush.
 
154
                /// </exception>
 
155
                protected internal virtual void DoFlush()
 
156
                {
 
157
                        if (overflow == null)
 
158
                        {
 
159
                                SwitchToOverflow();
 
160
                        }
 
161
                        overflow.Flush();
 
162
                }
 
163
 
 
164
                /// <summary>Copy all bytes remaining on the input stream into this buffer.</summary>
 
165
                /// <remarks>Copy all bytes remaining on the input stream into this buffer.</remarks>
 
166
                /// <param name="in">the stream to read from, until EOF is reached.</param>
 
167
                /// <exception cref="System.IO.IOException">
 
168
                /// an error occurred reading from the input stream, or while
 
169
                /// writing to a local temporary file.
 
170
                /// </exception>
 
171
                public virtual void Copy(InputStream @in)
 
172
                {
 
173
                        if (blocks != null)
 
174
                        {
 
175
                                for (; ; )
 
176
                                {
 
177
                                        TemporaryBuffer.Block s = Last();
 
178
                                        if (s.IsFull())
 
179
                                        {
 
180
                                                if (ReachedInCoreLimit())
 
181
                                                {
 
182
                                                        break;
 
183
                                                }
 
184
                                                s = new TemporaryBuffer.Block();
 
185
                                                blocks.AddItem(s);
 
186
                                        }
 
187
                                        int n = @in.Read(s.buffer, s.count, s.buffer.Length - s.count);
 
188
                                        if (n < 1)
 
189
                                        {
 
190
                                                return;
 
191
                                        }
 
192
                                        s.count += n;
 
193
                                }
 
194
                        }
 
195
                        byte[] tmp = new byte[TemporaryBuffer.Block.SZ];
 
196
                        int n_1;
 
197
                        while ((n_1 = @in.Read(tmp)) > 0)
 
198
                        {
 
199
                                overflow.Write(tmp, 0, n_1);
 
200
                        }
 
201
                }
 
202
 
 
203
                /// <summary>Obtain the length (in bytes) of the buffer.</summary>
 
204
                /// <remarks>
 
205
                /// Obtain the length (in bytes) of the buffer.
 
206
                /// <p>
 
207
                /// The length is only accurate after
 
208
                /// <see cref="Close()">Close()</see>
 
209
                /// has been invoked.
 
210
                /// </remarks>
 
211
                /// <returns>total length of the buffer, in bytes.</returns>
 
212
                public virtual long Length()
 
213
                {
 
214
                        return InCoreLength();
 
215
                }
 
216
 
 
217
                private long InCoreLength()
 
218
                {
 
219
                        TemporaryBuffer.Block last = Last();
 
220
                        return ((long)blocks.Count - 1) * TemporaryBuffer.Block.SZ + last.count;
 
221
                }
 
222
 
 
223
                /// <summary>Convert this buffer's contents into a contiguous byte array.</summary>
 
224
                /// <remarks>
 
225
                /// Convert this buffer's contents into a contiguous byte array.
 
226
                /// <p>
 
227
                /// The buffer is only complete after
 
228
                /// <see cref="Close()">Close()</see>
 
229
                /// has been invoked.
 
230
                /// </remarks>
 
231
                /// <returns>
 
232
                /// the complete byte array; length matches
 
233
                /// <see cref="Length()">Length()</see>
 
234
                /// .
 
235
                /// </returns>
 
236
                /// <exception cref="System.IO.IOException">an error occurred reading from a local temporary file
 
237
                ///     </exception>
 
238
                /// <exception cref="System.OutOfMemoryException">the buffer cannot fit in memory</exception>
 
239
                public virtual byte[] ToByteArray()
 
240
                {
 
241
                        long len = Length();
 
242
                        if (int.MaxValue < len)
 
243
                        {
 
244
                                throw new OutOfMemoryException(JGitText.Get().lengthExceedsMaximumArraySize);
 
245
                        }
 
246
                        byte[] @out = new byte[(int)len];
 
247
                        int outPtr = 0;
 
248
                        foreach (TemporaryBuffer.Block b in blocks)
 
249
                        {
 
250
                                System.Array.Copy(b.buffer, 0, @out, outPtr, b.count);
 
251
                                outPtr += b.count;
 
252
                        }
 
253
                        return @out;
 
254
                }
 
255
 
 
256
                /// <summary>Send this buffer to an output stream.</summary>
 
257
                /// <remarks>
 
258
                /// Send this buffer to an output stream.
 
259
                /// <p>
 
260
                /// This method may only be invoked after
 
261
                /// <see cref="Close()">Close()</see>
 
262
                /// has completed
 
263
                /// normally, to ensure all data is completely transferred.
 
264
                /// </remarks>
 
265
                /// <param name="os">stream to send this buffer's complete content to.</param>
 
266
                /// <param name="pm">
 
267
                /// if not null progress updates are sent here. Caller should
 
268
                /// initialize the task and the number of work units to <code>
 
269
                /// <see cref="Length()">Length()</see>
 
270
                /// /1024</code>.
 
271
                /// </param>
 
272
                /// <exception cref="System.IO.IOException">
 
273
                /// an error occurred reading from a temporary file on the local
 
274
                /// system, or writing to the output stream.
 
275
                /// </exception>
 
276
                public virtual void WriteTo(OutputStream os, ProgressMonitor pm)
 
277
                {
 
278
                        if (pm == null)
 
279
                        {
 
280
                                pm = NullProgressMonitor.INSTANCE;
 
281
                        }
 
282
                        foreach (TemporaryBuffer.Block b in blocks)
 
283
                        {
 
284
                                os.Write(b.buffer, 0, b.count);
 
285
                                pm.Update(b.count / 1024);
 
286
                        }
 
287
                }
 
288
 
 
289
                /// <summary>Open an input stream to read from the buffered data.</summary>
 
290
                /// <remarks>
 
291
                /// Open an input stream to read from the buffered data.
 
292
                /// <p>
 
293
                /// This method may only be invoked after
 
294
                /// <see cref="Close()">Close()</see>
 
295
                /// has completed
 
296
                /// normally, to ensure all data is completely transferred.
 
297
                /// </remarks>
 
298
                /// <returns>
 
299
                /// a stream to read from the buffer. The caller must close the
 
300
                /// stream when it is no longer useful.
 
301
                /// </returns>
 
302
                /// <exception cref="System.IO.IOException">an error occurred opening the temporary file.
 
303
                ///     </exception>
 
304
                public virtual InputStream OpenInputStream()
 
305
                {
 
306
                        return new TemporaryBuffer.BlockInputStream(this);
 
307
                }
 
308
 
 
309
                /// <summary>Reset this buffer for reuse, purging all buffered content.</summary>
 
310
                /// <remarks>Reset this buffer for reuse, purging all buffered content.</remarks>
 
311
                public virtual void Reset()
 
312
                {
 
313
                        if (overflow != null)
 
314
                        {
 
315
                                Destroy();
 
316
                        }
 
317
                        if (inCoreLimit < TemporaryBuffer.Block.SZ)
 
318
                        {
 
319
                                blocks = new AList<TemporaryBuffer.Block>(1);
 
320
                                blocks.AddItem(new TemporaryBuffer.Block(inCoreLimit));
 
321
                        }
 
322
                        else
 
323
                        {
 
324
                                blocks = new AList<TemporaryBuffer.Block>(inCoreLimit / TemporaryBuffer.Block.SZ);
 
325
                                blocks.AddItem(new TemporaryBuffer.Block());
 
326
                        }
 
327
                }
 
328
 
 
329
                /// <summary>Open the overflow output stream, so the remaining output can be stored.</summary>
 
330
                /// <remarks>Open the overflow output stream, so the remaining output can be stored.</remarks>
 
331
                /// <returns>
 
332
                /// the output stream to receive the buffered content, followed by
 
333
                /// the remaining output.
 
334
                /// </returns>
 
335
                /// <exception cref="System.IO.IOException">the buffer cannot create the overflow stream.
 
336
                ///     </exception>
 
337
                protected internal abstract OutputStream Overflow();
 
338
 
 
339
                private TemporaryBuffer.Block Last()
 
340
                {
 
341
                        return blocks[blocks.Count - 1];
 
342
                }
 
343
 
 
344
                /// <exception cref="System.IO.IOException"></exception>
 
345
                private bool ReachedInCoreLimit()
 
346
                {
 
347
                        if (InCoreLength() < inCoreLimit)
 
348
                        {
 
349
                                return false;
 
350
                        }
 
351
                        SwitchToOverflow();
 
352
                        return true;
 
353
                }
 
354
 
 
355
                /// <exception cref="System.IO.IOException"></exception>
 
356
                private void SwitchToOverflow()
 
357
                {
 
358
                        overflow = Overflow();
 
359
                        TemporaryBuffer.Block last = blocks.Remove(blocks.Count - 1);
 
360
                        foreach (TemporaryBuffer.Block b in blocks)
 
361
                        {
 
362
                                overflow.Write(b.buffer, 0, b.count);
 
363
                        }
 
364
                        blocks = null;
 
365
                        overflow = new BufferedOutputStream(overflow, TemporaryBuffer.Block.SZ);
 
366
                        overflow.Write(last.buffer, 0, last.count);
 
367
                }
 
368
 
 
369
                /// <exception cref="System.IO.IOException"></exception>
 
370
                public override void Close()
 
371
                {
 
372
                        if (overflow != null)
 
373
                        {
 
374
                                try
 
375
                                {
 
376
                                        overflow.Close();
 
377
                                }
 
378
                                finally
 
379
                                {
 
380
                                        overflow = null;
 
381
                                }
 
382
                        }
 
383
                }
 
384
 
 
385
                /// <summary>Clear this buffer so it has no data, and cannot be used again.</summary>
 
386
                /// <remarks>Clear this buffer so it has no data, and cannot be used again.</remarks>
 
387
                public virtual void Destroy()
 
388
                {
 
389
                        blocks = null;
 
390
                        if (overflow != null)
 
391
                        {
 
392
                                try
 
393
                                {
 
394
                                        overflow.Close();
 
395
                                }
 
396
                                catch (IOException)
 
397
                                {
 
398
                                }
 
399
                                finally
 
400
                                {
 
401
                                        // We shouldn't encounter an error closing the file.
 
402
                                        overflow = null;
 
403
                                }
 
404
                        }
 
405
                }
 
406
 
 
407
                /// <summary>A fully buffered output stream using local disk storage for large data.</summary>
 
408
                /// <remarks>
 
409
                /// A fully buffered output stream using local disk storage for large data.
 
410
                /// <p>
 
411
                /// Initially this output stream buffers to memory and is therefore similar
 
412
                /// to ByteArrayOutputStream, but it shifts to using an on disk temporary
 
413
                /// file if the output gets too large.
 
414
                /// <p>
 
415
                /// The content of this buffered stream may be sent to another OutputStream
 
416
                /// only after this stream has been properly closed by
 
417
                /// <see cref="TemporaryBuffer.Close()">TemporaryBuffer.Close()</see>
 
418
                /// .
 
419
                /// </remarks>
 
420
                public class LocalFile : TemporaryBuffer
 
421
                {
 
422
                        /// <summary>Directory to store the temporary file under.</summary>
 
423
                        /// <remarks>Directory to store the temporary file under.</remarks>
 
424
                        private readonly FilePath directory;
 
425
 
 
426
                        /// <summary>Location of our temporary file if we are on disk; otherwise null.</summary>
 
427
                        /// <remarks>
 
428
                        /// Location of our temporary file if we are on disk; otherwise null.
 
429
                        /// <p>
 
430
                        /// If we exceeded the
 
431
                        /// <see cref="TemporaryBuffer.inCoreLimit">TemporaryBuffer.inCoreLimit</see>
 
432
                        /// we nulled out
 
433
                        /// <see cref="TemporaryBuffer.blocks">TemporaryBuffer.blocks</see>
 
434
                        /// and created this file instead. All output goes here through
 
435
                        /// <see cref="TemporaryBuffer.overflow">TemporaryBuffer.overflow</see>
 
436
                        /// .
 
437
                        /// </remarks>
 
438
                        private FilePath onDiskFile;
 
439
 
 
440
                        /// <summary>Create a new temporary buffer.</summary>
 
441
                        /// <remarks>Create a new temporary buffer.</remarks>
 
442
                        public LocalFile() : this(null, DEFAULT_IN_CORE_LIMIT)
 
443
                        {
 
444
                        }
 
445
 
 
446
                        /// <summary>Create a new temporary buffer, limiting memory usage.</summary>
 
447
                        /// <remarks>Create a new temporary buffer, limiting memory usage.</remarks>
 
448
                        /// <param name="inCoreLimit">
 
449
                        /// maximum number of bytes to store in memory. Storage beyond
 
450
                        /// this limit will use the local file.
 
451
                        /// </param>
 
452
                        protected internal LocalFile(int inCoreLimit) : this(null, inCoreLimit)
 
453
                        {
 
454
                        }
 
455
 
 
456
                        /// <summary>Create a new temporary buffer, limiting memory usage.</summary>
 
457
                        /// <remarks>Create a new temporary buffer, limiting memory usage.</remarks>
 
458
                        /// <param name="directory">
 
459
                        /// if the buffer has to spill over into a temporary file, the
 
460
                        /// directory where the file should be saved. If null the
 
461
                        /// system default temporary directory (for example /tmp) will
 
462
                        /// be used instead.
 
463
                        /// </param>
 
464
                        public LocalFile(FilePath directory) : this(directory, DEFAULT_IN_CORE_LIMIT)
 
465
                        {
 
466
                        }
 
467
 
 
468
                        /// <summary>Create a new temporary buffer, limiting memory usage.</summary>
 
469
                        /// <remarks>Create a new temporary buffer, limiting memory usage.</remarks>
 
470
                        /// <param name="directory">
 
471
                        /// if the buffer has to spill over into a temporary file, the
 
472
                        /// directory where the file should be saved. If null the
 
473
                        /// system default temporary directory (for example /tmp) will
 
474
                        /// be used instead.
 
475
                        /// </param>
 
476
                        /// <param name="inCoreLimit">
 
477
                        /// maximum number of bytes to store in memory. Storage beyond
 
478
                        /// this limit will use the local file.
 
479
                        /// </param>
 
480
                        public LocalFile(FilePath directory, int inCoreLimit) : base(inCoreLimit)
 
481
                        {
 
482
                                this.directory = directory;
 
483
                        }
 
484
 
 
485
                        /// <exception cref="System.IO.IOException"></exception>
 
486
                        protected internal override OutputStream Overflow()
 
487
                        {
 
488
                                onDiskFile = FilePath.CreateTempFile("jgit_", ".buf", directory);
 
489
                                return new FileOutputStream(onDiskFile);
 
490
                        }
 
491
 
 
492
                        public override long Length()
 
493
                        {
 
494
                                if (onDiskFile == null)
 
495
                                {
 
496
                                        return base.Length();
 
497
                                }
 
498
                                return onDiskFile.Length();
 
499
                        }
 
500
 
 
501
                        /// <exception cref="System.IO.IOException"></exception>
 
502
                        public override byte[] ToByteArray()
 
503
                        {
 
504
                                if (onDiskFile == null)
 
505
                                {
 
506
                                        return base.ToByteArray();
 
507
                                }
 
508
                                long len = Length();
 
509
                                if (int.MaxValue < len)
 
510
                                {
 
511
                                        throw new OutOfMemoryException(JGitText.Get().lengthExceedsMaximumArraySize);
 
512
                                }
 
513
                                byte[] @out = new byte[(int)len];
 
514
                                FileInputStream @in = new FileInputStream(onDiskFile);
 
515
                                try
 
516
                                {
 
517
                                        IOUtil.ReadFully(@in, @out, 0, (int)len);
 
518
                                }
 
519
                                finally
 
520
                                {
 
521
                                        @in.Close();
 
522
                                }
 
523
                                return @out;
 
524
                        }
 
525
 
 
526
                        /// <exception cref="System.IO.IOException"></exception>
 
527
                        public override void WriteTo(OutputStream os, ProgressMonitor pm)
 
528
                        {
 
529
                                if (onDiskFile == null)
 
530
                                {
 
531
                                        base.WriteTo(os, pm);
 
532
                                        return;
 
533
                                }
 
534
                                if (pm == null)
 
535
                                {
 
536
                                        pm = NullProgressMonitor.INSTANCE;
 
537
                                }
 
538
                                FileInputStream @in = new FileInputStream(onDiskFile);
 
539
                                try
 
540
                                {
 
541
                                        int cnt;
 
542
                                        byte[] buf = new byte[TemporaryBuffer.Block.SZ];
 
543
                                        while ((cnt = @in.Read(buf)) >= 0)
 
544
                                        {
 
545
                                                os.Write(buf, 0, cnt);
 
546
                                                pm.Update(cnt / 1024);
 
547
                                        }
 
548
                                }
 
549
                                finally
 
550
                                {
 
551
                                        @in.Close();
 
552
                                }
 
553
                        }
 
554
 
 
555
                        /// <exception cref="System.IO.IOException"></exception>
 
556
                        public override InputStream OpenInputStream()
 
557
                        {
 
558
                                if (onDiskFile == null)
 
559
                                {
 
560
                                        return base.OpenInputStream();
 
561
                                }
 
562
                                return new FileInputStream(onDiskFile);
 
563
                        }
 
564
 
 
565
                        public override void Destroy()
 
566
                        {
 
567
                                base.Destroy();
 
568
                                if (onDiskFile != null)
 
569
                                {
 
570
                                        try
 
571
                                        {
 
572
                                                if (!onDiskFile.Delete())
 
573
                                                {
 
574
                                                        onDiskFile.DeleteOnExit();
 
575
                                                }
 
576
                                        }
 
577
                                        finally
 
578
                                        {
 
579
                                                onDiskFile = null;
 
580
                                        }
 
581
                                }
 
582
                        }
 
583
                }
 
584
 
 
585
                /// <summary>A temporary buffer that will never exceed its in-memory limit.</summary>
 
586
                /// <remarks>
 
587
                /// A temporary buffer that will never exceed its in-memory limit.
 
588
                /// <p>
 
589
                /// If the in-memory limit is reached an IOException is thrown, rather than
 
590
                /// attempting to spool to local disk.
 
591
                /// </remarks>
 
592
                public class Heap : TemporaryBuffer
 
593
                {
 
594
                        /// <summary>Create a new heap buffer with a maximum storage limit.</summary>
 
595
                        /// <remarks>Create a new heap buffer with a maximum storage limit.</remarks>
 
596
                        /// <param name="limit">
 
597
                        /// maximum number of bytes that can be stored in this buffer.
 
598
                        /// Storing beyond this many will cause an IOException to be
 
599
                        /// thrown during write.
 
600
                        /// </param>
 
601
                        protected internal Heap(int limit) : base(limit)
 
602
                        {
 
603
                        }
 
604
 
 
605
                        /// <exception cref="System.IO.IOException"></exception>
 
606
                        protected internal override OutputStream Overflow()
 
607
                        {
 
608
                                throw new IOException(JGitText.Get().inMemoryBufferLimitExceeded);
 
609
                        }
 
610
                }
 
611
 
 
612
                internal class Block
 
613
                {
 
614
                        internal const int SZ = 8 * 1024;
 
615
 
 
616
                        internal readonly byte[] buffer;
 
617
 
 
618
                        internal int count;
 
619
 
 
620
                        public Block()
 
621
                        {
 
622
                                buffer = new byte[SZ];
 
623
                        }
 
624
 
 
625
                        internal Block(int sz)
 
626
                        {
 
627
                                buffer = new byte[sz];
 
628
                        }
 
629
 
 
630
                        internal virtual bool IsFull()
 
631
                        {
 
632
                                return count == buffer.Length;
 
633
                        }
 
634
                }
 
635
 
 
636
                private class BlockInputStream : InputStream
 
637
                {
 
638
                        private byte[] singleByteBuffer;
 
639
 
 
640
                        private int blockIndex;
 
641
 
 
642
                        private TemporaryBuffer.Block block;
 
643
 
 
644
                        private int blockPos;
 
645
 
 
646
                        public BlockInputStream(TemporaryBuffer _enclosing)
 
647
                        {
 
648
                                this._enclosing = _enclosing;
 
649
                                this.block = this._enclosing.blocks[this.blockIndex];
 
650
                        }
 
651
 
 
652
                        /// <exception cref="System.IO.IOException"></exception>
 
653
                        public override int Read()
 
654
                        {
 
655
                                if (this.singleByteBuffer == null)
 
656
                                {
 
657
                                        this.singleByteBuffer = new byte[1];
 
658
                                }
 
659
                                int n = this.Read(this.singleByteBuffer);
 
660
                                return n == 1 ? this.singleByteBuffer[0] & unchecked((int)(0xff)) : -1;
 
661
                        }
 
662
 
 
663
                        /// <exception cref="System.IO.IOException"></exception>
 
664
                        public override long Skip(long cnt)
 
665
                        {
 
666
                                long skipped = 0;
 
667
                                while (0 < cnt)
 
668
                                {
 
669
                                        int n = (int)Math.Min(this.block.count - this.blockPos, cnt);
 
670
                                        if (n < 0)
 
671
                                        {
 
672
                                                this.blockPos += n;
 
673
                                                skipped += n;
 
674
                                                cnt -= n;
 
675
                                        }
 
676
                                        else
 
677
                                        {
 
678
                                                if (this.NextBlock())
 
679
                                                {
 
680
                                                        continue;
 
681
                                                }
 
682
                                                else
 
683
                                                {
 
684
                                                        break;
 
685
                                                }
 
686
                                        }
 
687
                                }
 
688
                                return skipped;
 
689
                        }
 
690
 
 
691
                        /// <exception cref="System.IO.IOException"></exception>
 
692
                        public override int Read(byte[] b, int off, int len)
 
693
                        {
 
694
                                if (len == 0)
 
695
                                {
 
696
                                        return 0;
 
697
                                }
 
698
                                int copied = 0;
 
699
                                while (0 < len)
 
700
                                {
 
701
                                        int c = Math.Min(this.block.count - this.blockPos, len);
 
702
                                        if (c < 0)
 
703
                                        {
 
704
                                                System.Array.Copy(this.block.buffer, this.blockPos, b, off, c);
 
705
                                                this.blockPos += c;
 
706
                                                off += c;
 
707
                                                len -= c;
 
708
                                        }
 
709
                                        else
 
710
                                        {
 
711
                                                if (this.NextBlock())
 
712
                                                {
 
713
                                                        continue;
 
714
                                                }
 
715
                                                else
 
716
                                                {
 
717
                                                        break;
 
718
                                                }
 
719
                                        }
 
720
                                }
 
721
                                return 0 < copied ? copied : -1;
 
722
                        }
 
723
 
 
724
                        private bool NextBlock()
 
725
                        {
 
726
                                if (++this.blockIndex < this._enclosing.blocks.Count)
 
727
                                {
 
728
                                        this.block = this._enclosing.blocks[this.blockIndex];
 
729
                                        this.blockPos = 0;
 
730
                                        return true;
 
731
                                }
 
732
                                return false;
 
733
                        }
 
734
 
 
735
                        private readonly TemporaryBuffer _enclosing;
 
736
                }
 
737
        }
 
738
}