--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/SafeChunkyInputStream.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+
+/**
+ * @see SafeChunkyOutputStream
+ */
+
+public class SafeChunkyInputStream extends InputStream {
+ protected static final int BUFFER_SIZE = 8192;
+ protected byte[] buffer;
+ protected int bufferLength = 0;
+ protected byte[] chunk;
+ protected int chunkLength = 0;
+ protected boolean endOfFile = false;
+ protected InputStream input;
+ protected int nextByteInBuffer = 0;
+ protected int nextByteInChunk = 0;
+
+ public SafeChunkyInputStream(File target) throws IOException {
+ this(target, BUFFER_SIZE);
+ }
+
+ public SafeChunkyInputStream(File target, int bufferSize) throws IOException {
+ input = new FileInputStream(target);
+ buffer = new byte[bufferSize];
+ }
+
+ protected void accumulate(byte[] data, int start, int end) {
+ byte[] result = new byte[chunk.length + end - start];
+ System.arraycopy(chunk, 0, result, 0, chunk.length);
+ System.arraycopy(data, start, result, chunk.length, end - start);
+ chunk = result;
+ chunkLength = chunkLength + end - start;
+ }
+
+ public int available() throws IOException {
+ return chunkLength - nextByteInChunk;
+ }
+
+ protected void buildChunk() throws IOException {
+ //read buffer loads of data until an entire chunk is accumulated
+ while (true) {
+ if (nextByteInBuffer + ILocalStoreConstants.CHUNK_DELIMITER_SIZE > bufferLength)
+ shiftAndFillBuffer();
+ int end = find(ILocalStoreConstants.END_CHUNK, nextByteInBuffer, bufferLength, true);
+ if (end != -1) {
+ accumulate(buffer, nextByteInBuffer, end);
+ nextByteInBuffer = end + ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ return;
+ }
+ accumulate(buffer, nextByteInBuffer, bufferLength);
+ bufferLength = input.read(buffer);
+ nextByteInBuffer = 0;
+ if (bufferLength == -1) {
+ endOfFile = true;
+ return;
+ }
+ }
+ }
+
+ public void close() throws IOException {
+ input.close();
+ }
+
+ protected boolean compare(byte[] source, byte[] target, int startIndex) {
+ for (int i = 0; i < target.length; i++) {
+ if (source[startIndex] != target[i])
+ return false;
+ startIndex++;
+ }
+ return true;
+ }
+
+ protected int find(byte[] pattern, int startIndex, int endIndex, boolean accumulate) throws IOException {
+ int pos = findByte(pattern[0], startIndex, endIndex);
+ if (pos == -1)
+ return -1;
+ if (pos + ILocalStoreConstants.CHUNK_DELIMITER_SIZE > bufferLength) {
+ if (accumulate)
+ accumulate(buffer, nextByteInBuffer, pos);
+ nextByteInBuffer = pos;
+ pos = 0;
+ shiftAndFillBuffer();
+ }
+ if (compare(buffer, pattern, pos))
+ return pos;
+ return find(pattern, pos + 1, endIndex, accumulate);
+ }
+
+ protected int findByte(byte target, int startIndex, int endIndex) {
+ while (startIndex < endIndex) {
+ if (buffer[startIndex] == target)
+ return startIndex;
+ startIndex++;
+ }
+ return -1;
+ }
+
+ protected void findChunkStart() throws IOException {
+ if (nextByteInBuffer + ILocalStoreConstants.CHUNK_DELIMITER_SIZE > bufferLength)
+ shiftAndFillBuffer();
+ int begin = find(ILocalStoreConstants.BEGIN_CHUNK, nextByteInBuffer, bufferLength, false);
+ if (begin != -1) {
+ nextByteInBuffer = begin + ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ return;
+ }
+ bufferLength = input.read(buffer);
+ nextByteInBuffer = 0;
+ if (bufferLength == -1) {
+ resetChunk();
+ endOfFile = true;
+ return;
+ }
+ findChunkStart();
+ }
+
+ public int read() throws IOException {
+ if (endOfFile)
+ return -1;
+ // if there are bytes left in the chunk, return the first available
+ if (nextByteInChunk < chunkLength)
+ return chunk[nextByteInChunk++] & 0xFF;
+ // Otherwise the chunk is empty so clear the current one, get the next
+ // one and recursively call read. Need to recur as the chunk may be
+ // real but empty.
+ resetChunk();
+ findChunkStart();
+ if (endOfFile)
+ return -1;
+ buildChunk();
+ refineChunk();
+ return read();
+ }
+
+ /**
+ * Skip over any begin chunks in the current chunk. This could be optimized
+ * to skip at the same time as we are scanning the buffer.
+ */
+ protected void refineChunk() {
+ int start = chunkLength - ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ if (start < 0)
+ return;
+ for (int i = start; i >= 0; i--) {
+ if (compare(chunk, ILocalStoreConstants.BEGIN_CHUNK, i)) {
+ nextByteInChunk = i + ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ return;
+ }
+ }
+ }
+
+ protected void resetChunk() {
+ chunk = new byte[0];
+ chunkLength = 0;
+ nextByteInChunk = 0;
+ }
+
+ protected void shiftAndFillBuffer() throws IOException {
+ int length = bufferLength - nextByteInBuffer;
+ System.arraycopy(buffer, nextByteInBuffer, buffer, 0, length);
+ nextByteInBuffer = 0;
+ bufferLength = length;
+ int read = input.read(buffer, bufferLength, buffer.length - bufferLength);
+ if (read != -1)
+ bufferLength += read;
+ else {
+ resetChunk();
+ endOfFile = true;
+ }
+ }
+}