001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012:
013: package org.tmatesoft.svn.core.io.diff;
014:
015: import java.io.IOException;
016: import java.io.OutputStream;
017: import java.nio.ByteBuffer;
018: import java.util.Iterator;
019: import java.util.zip.DeflaterOutputStream;
020:
021: import org.tmatesoft.svn.core.SVNErrorCode;
022: import org.tmatesoft.svn.core.SVNErrorMessage;
023: import org.tmatesoft.svn.core.SVNException;
024: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
025:
026: /**
027: * The <b>SVNDiffWindow</b> class represents a diff window that
028: * contains instructions and new data of a delta to apply to a file.
029: *
030: * <p>
031: * Instructions are not immediately contained in a window. A diff window
032: * provides an iterator that reads and constructs one <b>SVNDiffInstruction</b>
033: * from provided raw bytes per one iteration. There is even an ability to
034: * use a single <b>SVNDiffInstruction</b> object for read and decoded instructions:
035: * for subsequent iterations an iterator simply uses the same instruction object
036: * to return as a newly read and decoded instruction.
037: *
038: * @version 1.1.1
039: * @author TMate Software Ltd.
040: * @see SVNDiffInstruction
041: */
042: public class SVNDiffWindow {
043:
044: /**
045: * Bytes of the delta header of an uncompressed diff window.
046: */
047: public static final byte[] SVN_HEADER = new byte[] { 'S', 'V', 'N',
048: '\0' };
049:
050: /**
051: * Bytes of the delta header of a compressed diff window.
052: * @since 1.1, new in Subversion 1.4
053: */
054: public static final byte[] SVN1_HEADER = new byte[] { 'S', 'V',
055: 'N', '\1' };
056:
057: /**
058: * An empty window (in particular, its instructions length = 0). Corresponds
059: * to the case of an empty delta, so, it's passed to a delta consumer to
060: * create an empty file.
061: */
062: public static final SVNDiffWindow EMPTY = new SVNDiffWindow(0, 0,
063: 0, 0, 0);
064:
065: private final long mySourceViewOffset;
066: private final int mySourceViewLength;
067: private final int myTargetViewLength;
068: private final int myNewDataLength;
069: private int myInstructionsLength;
070:
071: private SVNDiffInstruction myTemplateInstruction = new SVNDiffInstruction(
072: 0, 0, 0);
073: private SVNDiffInstruction myTemplateNextInstruction = new SVNDiffInstruction(
074: 0, 0, 0);
075:
076: private byte[] myData;
077: private int myDataOffset;
078: private int myInstructionsCount;
079:
080: /**
081: * Constructs an <b>SVNDiffWindow</b> object. This constructor is
082: * used when bytes of instructions are not decoded and converted to
083: * <b>SVNDiffInstruction</b> objects yet, but are kept elsewhere
084: * along with new data.
085: *
086: * @param sourceViewOffset an offset in the source view
087: * @param sourceViewLength a number of bytes to read from the
088: * source view
089: * @param targetViewLength a length in bytes of the target view
090: * it must have after copying bytes
091: * @param instructionsLength a number of instructions bytes
092: * @param newDataLength a number of bytes of new data
093: * @see SVNDiffInstruction
094: */
095: public SVNDiffWindow(long sourceViewOffset, int sourceViewLength,
096: int targetViewLength, int instructionsLength,
097: int newDataLength) {
098: mySourceViewOffset = sourceViewOffset;
099: mySourceViewLength = sourceViewLength;
100: myTargetViewLength = targetViewLength;
101: myInstructionsLength = instructionsLength;
102: myNewDataLength = newDataLength;
103: }
104:
105: /**
106: * Returns the length of instructions in bytes.
107: *
108: * @return a number of instructions bytes
109: */
110: public int getInstructionsLength() {
111: return myInstructionsLength;
112: }
113:
114: /**
115: * Returns the source view offset.
116: *
117: * @return an offset in the source from where the source bytes
118: * must be copied
119: */
120: public long getSourceViewOffset() {
121: return mySourceViewOffset;
122: }
123:
124: /**
125: * Returns the number of bytes to copy from the source view to the target one.
126: *
127: * @return a number of source bytes to copy
128: */
129: public int getSourceViewLength() {
130: return mySourceViewLength;
131: }
132:
133: /**
134: * Returns the length in bytes of the target view. The length of the target
135: * view is actually the number of bytes that should be totally copied by all the
136: * instructions of this window.
137: *
138: * @return a length in bytes of the target view
139: */
140: public int getTargetViewLength() {
141: return myTargetViewLength;
142: }
143:
144: /**
145: * Returns the number of new data bytes to copy to the target view.
146: *
147: * @return a number of new data bytes
148: */
149: public int getNewDataLength() {
150: return myNewDataLength;
151: }
152:
153: /**
154: * Returns an iterator to read instructions in series.
155: * Objects returned by an iterator's <code>next()</code> method
156: * are separate <b>SVNDiffInstruction</b> objects.
157: *
158: * <p>
159: * Instructions as well as new data are read from a byte
160: * buffer that is passed to this window object via the
161: * {@link #setData(ByteBuffer) setData()} method.
162: *
163: * <p>
164: * A call to this routine is equivalent to a call
165: * <code>instructions(false)</code>.
166: *
167: * @return an instructions iterator
168: * @see #instructions(boolean)
169: * @see SVNDiffInstruction
170: */
171: public Iterator instructions() {
172: return instructions(false);
173: }
174:
175: /**
176: * Returns an iterator to read instructions in series.
177: *
178: * <p>
179: * If <code>template</code> is <span class="javakeyword">true</span>
180: * then each instruction returned by the iterator is actually the
181: * same <b>SVNDiffInstruction</b> object, but with proper options.
182: * This prevents from allocating new memory.
183: *
184: * <p>
185: * On the other hand, if <code>template</code> is <span class="javakeyword">false</span>
186: * then the iterator returns a new allocated <b>SVNDiffInstruction</b> object per
187: * each instruction read and decoded.
188: *
189: * <p>
190: * Instructions as well as new data are read from a byte buffer that is
191: * passed to this window object via the
192: * {@link #setData(ByteBuffer) setData()} method.
193: *
194: * @param template to use a single/multiple instruction objects
195: * @return an instructions iterator
196: * @see #instructions()
197: * @see SVNDiffInstruction
198: */
199: public Iterator instructions(boolean template) {
200: return new InstructionsIterator(template);
201: }
202:
203: /**
204: * Applies this window's instructions. The source and target streams
205: * are provided by <code>applyBaton</code>.
206: *
207: * <p>
208: * If this window has got any {@link SVNDiffInstruction#COPY_FROM_SOURCE} instructions, then:
209: * <ol>
210: * <li>At first copies a source view from the source stream
211: * of <code>applyBaton</code> to the baton's inner source buffer.
212: * {@link SVNDiffInstruction#COPY_FROM_SOURCE} instructions of this window are
213: * relative to the bounds of that source buffer (source view, in other words).
214: * <li>Second, according to instructions, copies source bytes from the source buffer
215: * to the baton's target buffer (or target view, in other words).
216: * <li>Then, if <code>applyBaton</code> is supplied with an MD5 digest, updates it with those bytes
217: * in the target buffer. So, after instructions applying completes, it will be the checksum for
218: * the full text expanded.
219: * <li>The last step - appends the target buffer bytes to the baton's
220: * target stream.
221: * </ol>
222: *
223: * <p>
224: * {@link SVNDiffInstruction#COPY_FROM_NEW_DATA} instructions rule to copy bytes from
225: * the instructions & new data buffer provided to this window object via a call to the
226: * {@link #setData(ByteBuffer) setData()} method.
227: *
228: * <p>
229: * {@link SVNDiffInstruction#COPY_FROM_TARGET} instructions are relative to the bounds of
230: * the target buffer.
231: *
232: * @param applyBaton a baton that provides the source and target
233: * views as well as holds the source and targed
234: * streams
235: * @throws SVNException
236: * @see #apply(byte[], byte[])
237: */
238: public void apply(SVNDiffWindowApplyBaton applyBaton)
239: throws SVNException {
240: // here we have streams and buffer from the previous calls (or nulls).
241:
242: // 1. buffer for target.
243: if (applyBaton.myTargetBuffer == null
244: || applyBaton.myTargetViewSize < getTargetViewLength()) {
245: applyBaton.myTargetBuffer = new byte[getTargetViewLength()];
246: }
247: applyBaton.myTargetViewSize = getTargetViewLength();
248:
249: // 2. buffer for source.
250: int length = 0;
251: if (getSourceViewOffset() != applyBaton.mySourceViewOffset
252: || getSourceViewLength() > applyBaton.mySourceViewLength) {
253: byte[] oldSourceBuffer = applyBaton.mySourceBuffer;
254: // create a new buffer
255: applyBaton.mySourceBuffer = new byte[getSourceViewLength()];
256: // copy from the old buffer.
257: if (applyBaton.mySourceViewOffset
258: + applyBaton.mySourceViewLength > getSourceViewOffset()) {
259: // copy overlapping part to the new buffer
260: int start = (int) (getSourceViewOffset() - applyBaton.mySourceViewOffset);
261: System.arraycopy(oldSourceBuffer, start,
262: applyBaton.mySourceBuffer, 0,
263: (applyBaton.mySourceViewLength - start));
264: length = (applyBaton.mySourceViewLength - start);
265: }
266: }
267: if (length < getSourceViewLength()) {
268: // fill what remains.
269: try {
270: int toSkip = (int) (getSourceViewOffset() - (applyBaton.mySourceViewOffset + applyBaton.mySourceViewLength));
271: if (toSkip > 0) {
272: applyBaton.mySourceStream.skip(toSkip);
273: }
274: applyBaton.mySourceStream.read(
275: applyBaton.mySourceBuffer, length,
276: applyBaton.mySourceBuffer.length - length);
277: } catch (IOException e) {
278: SVNErrorMessage err = SVNErrorMessage.create(
279: SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
280: SVNErrorManager.error(err, e);
281: }
282: }
283: // update offsets in baton.
284: applyBaton.mySourceViewLength = getSourceViewLength();
285: applyBaton.mySourceViewOffset = getSourceViewOffset();
286:
287: // apply instructions.
288: int tpos = 0;
289: int npos = myInstructionsLength;
290: try {
291: for (Iterator instructions = instructions(true); instructions
292: .hasNext();) {
293: SVNDiffInstruction instruction = (SVNDiffInstruction) instructions
294: .next();
295: int iLength = instruction.length < getTargetViewLength()
296: - tpos ? (int) instruction.length
297: : getTargetViewLength() - tpos;
298: switch (instruction.type) {
299: case SVNDiffInstruction.COPY_FROM_NEW_DATA:
300: System.arraycopy(myData, myDataOffset + npos,
301: applyBaton.myTargetBuffer, tpos, iLength);
302: npos += iLength;
303: break;
304: case SVNDiffInstruction.COPY_FROM_TARGET:
305: int start = instruction.offset;
306: int end = instruction.offset + iLength;
307: int tIndex = tpos;
308: for (int j = start; j < end; j++) {
309: applyBaton.myTargetBuffer[tIndex] = applyBaton.myTargetBuffer[j];
310: tIndex++;
311: }
312: break;
313: case SVNDiffInstruction.COPY_FROM_SOURCE:
314: System.arraycopy(applyBaton.mySourceBuffer,
315: instruction.offset,
316: applyBaton.myTargetBuffer, tpos, iLength);
317: break;
318: default:
319: }
320: tpos += instruction.length;
321: if (tpos >= getTargetViewLength()) {
322: break;
323: }
324: }
325: // save tbuffer.
326: if (applyBaton.myDigest != null) {
327: applyBaton.myDigest.update(applyBaton.myTargetBuffer,
328: 0, getTargetViewLength());
329: }
330: applyBaton.myTargetStream.write(applyBaton.myTargetBuffer,
331: 0, getTargetViewLength());
332: } catch (IOException e) {
333: SVNErrorMessage err = SVNErrorMessage.create(
334: SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
335: SVNErrorManager.error(err, e);
336: }
337: }
338:
339: /**
340: * Applies this window's instructions provided source and target view buffers.
341: *
342: * <p>
343: * If this window has got any {@link SVNDiffInstruction#COPY_FROM_SOURCE} instructions, then
344: * appropriate bytes described by such an instruction are copied from the <code>sourceBuffer</code>
345: * to the <code>targetBuffer</code>.
346: *
347: * <p>
348: * {@link SVNDiffInstruction#COPY_FROM_NEW_DATA} instructions rule to copy bytes from
349: * the instructions & new data buffer provided to this window object via a call to the
350: * {@link #setData(ByteBuffer) setData()} method.
351: *
352: * <p>
353: * {@link SVNDiffInstruction#COPY_FROM_TARGET} instructions are relative to the bounds of
354: * the <code>targetBuffer</code> itself.
355: *
356: * @param sourceBuffer a buffer containing a source view
357: * @param targetBuffer a buffer to get a target view
358: * @return the size of the resultant target view
359: * @see #apply(SVNDiffWindowApplyBaton)
360: */
361: public int apply(byte[] sourceBuffer, byte[] targetBuffer) {
362: int dataOffset = myInstructionsLength;
363: int tpos = 0;
364: for (Iterator instructions = instructions(true); instructions
365: .hasNext();) {
366: SVNDiffInstruction instruction = (SVNDiffInstruction) instructions
367: .next();
368: int iLength = instruction.length < getTargetViewLength()
369: - tpos ? (int) instruction.length
370: : getTargetViewLength() - tpos;
371: switch (instruction.type) {
372: case SVNDiffInstruction.COPY_FROM_NEW_DATA:
373: System.arraycopy(myData, myDataOffset + dataOffset,
374: targetBuffer, tpos, iLength);
375: dataOffset += iLength;
376: break;
377: case SVNDiffInstruction.COPY_FROM_TARGET:
378: int start = instruction.offset;
379: int end = instruction.offset + iLength;
380: int tIndex = tpos;
381: for (int j = start; j < end; j++) {
382: targetBuffer[tIndex] = targetBuffer[j];
383: tIndex++;
384: }
385: break;
386: case SVNDiffInstruction.COPY_FROM_SOURCE:
387: System.arraycopy(sourceBuffer, instruction.offset,
388: targetBuffer, tpos, iLength);
389: break;
390: default:
391: }
392: tpos += instruction.length;
393: if (tpos >= getTargetViewLength()) {
394: break;
395: }
396: }
397: return getTargetViewLength();
398: }
399:
400: /**
401: * Sets a byte buffer containing instruction and new data bytes
402: * of this window.
403: *
404: * <p>
405: * Instructions will go before new data within the buffer and should start
406: * at <code>buffer.position() + buffer.arrayOffset()</code>.
407: *
408: * <p>
409: * Applying a diff window prior to setting instruction and new data bytes
410: * may cause a NPE.
411: *
412: * @param buffer an input data buffer
413: */
414: public void setData(ByteBuffer buffer) {
415: myData = buffer.array();
416: myDataOffset = buffer.position() + buffer.arrayOffset();
417: }
418:
419: /**
420: * Gives a string representation of this object.
421: *
422: * @return a string representation of this object
423: */
424: public String toString() {
425: StringBuffer sb = new StringBuffer();
426: sb.append(getSourceViewOffset());
427: sb.append(":");
428: sb.append(getSourceViewLength());
429: sb.append(":");
430: sb.append(getTargetViewLength());
431: sb.append(":");
432: sb.append(getInstructionsLength());
433: sb.append(":");
434: sb.append(getNewDataLength());
435: sb.append(":");
436: sb.append(getDataLength());
437: sb.append(":");
438: sb.append(myDataOffset);
439: return sb.toString();
440: }
441:
442: /**
443: * Tells if this window is not empty, i.e. has got any instructions.
444: *
445: * @return <span class="javakeyword">true</span> if has instructions,
446: * <span class="javakeyword">false</span> if has not
447: */
448: public boolean hasInstructions() {
449: return myInstructionsLength > 0;
450: }
451:
452: /**
453: * Writes this window object to the provided stream.
454: *
455: * <p>
456: * If <code>writeHeader</code> is <span class="javakeyword">true</span>
457: * then writes {@link #SVN_HEADER} bytes also.
458: *
459: * @param os an output stream to write to
460: * @param writeHeader controls whether the header should be written
461: * or not
462: * @throws IOException if an I/O error occurs
463: */
464: public void writeTo(OutputStream os, boolean writeHeader)
465: throws IOException {
466: writeTo(os, writeHeader, false);
467: }
468:
469: /**
470: * Formats and writes this window bytes to the specified output stream.
471: *
472: * @param os an output stream to write the window to
473: * @param writeHeader if <span class="javakeyword">true</span> a window
474: * header will be also written
475: * @param compress if <span class="javakeyword">true</span> writes
476: * compressed window bytes using {@link #SVN1_HEADER}
477: * to indicate that (if <code>writeHeader</code> is
478: * <span class="javakeyword">true</span>), otherwise
479: * non-compressed window is written with {@link #SVN_HEADER}
480: * (again if <code>writeHeader</code> is <span class="javakeyword">true</span>)
481: * @throws IOException
482: * @since 1.1
483: */
484: public void writeTo(OutputStream os, boolean writeHeader,
485: boolean compress) throws IOException {
486: if (writeHeader) {
487: os.write(compress ? SVN1_HEADER : SVN_HEADER);
488: }
489: if (!hasInstructions()) {
490: return;
491: }
492: ByteBuffer offsets = ByteBuffer.allocate(100);
493: SVNDiffInstruction.writeLong(offsets, mySourceViewOffset);
494: SVNDiffInstruction.writeInt(offsets, mySourceViewLength);
495: SVNDiffInstruction.writeInt(offsets, myTargetViewLength);
496:
497: ByteBuffer instructions = null;
498: ByteBuffer newData = null;
499: int instLength = 0;
500: int dataLength = 0;
501: if (compress) {
502: instructions = inflate(myData, myDataOffset,
503: myInstructionsLength);
504: instLength = instructions.remaining();
505: newData = inflate(myData, myDataOffset
506: + myInstructionsLength, myNewDataLength);
507: dataLength = newData.remaining();
508: SVNDiffInstruction.writeInt(offsets, instLength);
509: SVNDiffInstruction.writeInt(offsets, dataLength);
510: } else {
511: SVNDiffInstruction.writeInt(offsets, myInstructionsLength);
512: SVNDiffInstruction.writeInt(offsets, myNewDataLength);
513: }
514: os.write(offsets.array(), offsets.arrayOffset(), offsets
515: .position());
516: if (compress) {
517: os.write(instructions.array(), instructions.arrayOffset(),
518: instructions.remaining());
519: os.write(newData.array(), newData.arrayOffset(), newData
520: .remaining());
521: } else {
522: os.write(myData, myDataOffset, myInstructionsLength);
523: if (myNewDataLength > 0) {
524: os.write(myData, myDataOffset + myInstructionsLength,
525: myNewDataLength);
526: }
527: }
528: }
529:
530: /**
531: * Returns the total amount of new data and instruction bytes.
532: *
533: * @return new data length + instructions length
534: */
535: public int getDataLength() {
536: return myNewDataLength + myInstructionsLength;
537: }
538:
539: /**
540: * Tells whether this window contains any copy-from-source
541: * instructions.
542: *
543: * @return <span class="javakeyword">true</span> if this window
544: * has got at least one {@link SVNDiffInstruction#COPY_FROM_SOURCE}
545: * instruction
546: */
547: public boolean hasCopyFromSourceInstructions() {
548: for (Iterator instrs = instructions(true); instrs.hasNext();) {
549: SVNDiffInstruction instruction = (SVNDiffInstruction) instrs
550: .next();
551: if (instruction.type == SVNDiffInstruction.COPY_FROM_SOURCE) {
552: return true;
553: }
554: }
555: return false;
556: }
557:
558: /**
559: * Creates an exact copy of this window object.
560: *
561: * <p>
562: * <code>targetData</code> is written instruction & new data bytes and
563: * then is set to a new window object via a call to its {@link #setData(ByteBuffer) setData()}
564: * method.
565: *
566: * @param targetData a byte buffer to receive a copy of this wondow data
567: * @return a new window object that is an exact copy of this one
568: */
569: public SVNDiffWindow clone(ByteBuffer targetData) {
570: int targetOffset = targetData.position()
571: + targetData.arrayOffset();
572: int position = targetData.position();
573: targetData.put(myData, myDataOffset, myInstructionsLength
574: + myNewDataLength);
575: targetData.position(position);
576: SVNDiffWindow clone = new SVNDiffWindow(getSourceViewOffset(),
577: getSourceViewLength(), getTargetViewLength(),
578: getInstructionsLength(), getNewDataLength());
579: clone.setData(targetData);
580: clone.myDataOffset = targetOffset;
581: return clone;
582: }
583:
584: private static ByteBuffer inflate(byte[] src, int offset, int length)
585: throws IOException {
586: final ByteBuffer buffer = ByteBuffer.allocate(length * 2 + 2);
587: SVNDiffInstruction.writeInt(buffer, length);
588: if (length < 512) {
589: buffer.put(src, offset, length);
590: } else {
591: DeflaterOutputStream out = new DeflaterOutputStream(
592: new OutputStream() {
593: public void write(int b) throws IOException {
594: buffer.put((byte) (b & 0xFF));
595: }
596:
597: public void write(byte[] b, int off, int len)
598: throws IOException {
599: buffer.put(b, off, len);
600: }
601:
602: public void write(byte[] b) throws IOException {
603: write(b, 0, b.length);
604: }
605: });
606: out.write(src, offset, length);
607: out.finish();
608: if (buffer.position() >= length) {
609: buffer.clear();
610: SVNDiffInstruction.writeInt(buffer, length);
611: buffer.put(src, offset, length);
612: }
613: }
614: buffer.flip();
615: return buffer;
616: }
617:
618: private class InstructionsIterator implements Iterator {
619:
620: private SVNDiffInstruction myNextInsruction;
621: private int myOffset;
622: private int myNewDataOffset;
623: private boolean myIsTemplate;
624:
625: public InstructionsIterator(boolean useTemplate) {
626: myIsTemplate = useTemplate;
627: myNextInsruction = readNextInstruction();
628: }
629:
630: public boolean hasNext() {
631: return myNextInsruction != null;
632: }
633:
634: public Object next() {
635: if (myNextInsruction == null) {
636: return null;
637: }
638:
639: if (myIsTemplate) {
640: myTemplateNextInstruction.type = myNextInsruction.type;
641: myTemplateNextInstruction.length = myNextInsruction.length;
642: myTemplateNextInstruction.offset = myNextInsruction.offset;
643: myNextInsruction = readNextInstruction();
644: return myTemplateNextInstruction;
645: }
646: Object next = myNextInsruction;
647: myNextInsruction = readNextInstruction();
648: return next;
649: }
650:
651: public void remove() {
652: }
653:
654: private SVNDiffInstruction readNextInstruction() {
655: if (myData == null || myOffset >= myInstructionsLength) {
656: return null;
657: }
658: SVNDiffInstruction instruction = myIsTemplate ? myTemplateInstruction
659: : new SVNDiffInstruction();
660: instruction.type = (myData[myDataOffset + myOffset] & 0xC0) >> 6;
661: instruction.length = myData[myDataOffset + myOffset] & 0x3f;
662: myOffset++;
663: if (instruction.length == 0) {
664: // read length from next byte
665: instruction.length = readInt();
666: }
667: if (instruction.type == 0 || instruction.type == 1) {
668: // read offset from next byte (no offset without length).
669: instruction.offset = readInt();
670: } else {
671: // set offset to offset in newdata.
672: instruction.offset = myNewDataOffset;
673: myNewDataOffset += instruction.length;
674: }
675: return instruction;
676: }
677:
678: private int readInt() {
679: int result = 0;
680: while (true) {
681: byte b = myData[myDataOffset + myOffset];
682: result = result << 7;
683: result = result | (b & 0x7f);
684: if ((b & 0x80) != 0) {
685: myOffset++;
686: if (myOffset >= myInstructionsLength) {
687: return -1;
688: }
689: continue;
690: }
691: myOffset++;
692: return result;
693: }
694: }
695: }
696:
697: /**
698: * Returns an array of instructions of this window.
699: *
700: * <p>
701: * If <code>target</code> is large enough to receive all instruction
702: * objects, then it's simply filled up to the end of instructions.
703: * However if it's not, it will be expanded to receive all instructions.
704: *
705: * @param target an instructions receiver
706: * @return an array containing all instructions
707: */
708: public SVNDiffInstruction[] loadDiffInstructions(
709: SVNDiffInstruction[] target) {
710: int index = 0;
711: for (Iterator instructions = instructions(); instructions
712: .hasNext();) {
713: if (index >= target.length) {
714: SVNDiffInstruction[] newTarget = new SVNDiffInstruction[index * 3 / 2];
715: System.arraycopy(target, 0, newTarget, 0, index);
716: target = newTarget;
717: }
718: target[index] = (SVNDiffInstruction) instructions.next();
719: index++;
720: }
721: myInstructionsCount = index;
722: return target;
723: }
724:
725: /**
726: * Returns the amount of instructions of this window object.
727: *
728: * @return a total number of instructions
729: */
730: public int getInstructionsCount() {
731: return myInstructionsCount;
732: }
733:
734: /**
735: * Fills a target buffer with the specified number of new data bytes
736: * of this window object taken at the specified offset.
737: *
738: * @param target a buffer to copy to
739: * @param offset an offset relative to the position of the first
740: * new data byte of this window object
741: * @param length a number of new data bytes to copy
742: */
743: public void writeNewData(ByteBuffer target, int offset, int length) {
744: offset += myDataOffset + myInstructionsLength;
745: target.put(myData, offset, length);
746: }
747:
748: }
|