blob: b59ce6665f1a4e2ced1607d8a88b44dca94731c3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.abdera2.common.io;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* InputStream that reads a given inputStream and skips the boundary tokens.
*/
public class MultipartInputStream extends FilterInputStream {
private InputStream input;
private byte[] boundary;
private byte[] storedBuffer;
private int storedBufferPosition;
private boolean fakeEof;
private boolean realEof;
private boolean bufferEnd;
private int[] lastTable = new int[256];
public MultipartInputStream(InputStream input, byte[] boundary) {
super(input);
this.input = input;
this.boundary = boundary;
computeLastTable();
}
public void skipBoundary() throws IOException {
byte[] buffer = new byte[256];
while (read(buffer) != -1) {
}
}
@Override
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b) == -1) {
return -1;
}
return b[0] & 0xff;
}
@Override
public int read(byte[] bytes) throws IOException {
return read(bytes, 0, bytes.length);
}
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
if (length == 0) {
return 0;
}
int bytesReaded = -1;
if (!checkEndOfFile()) {
int bufferLength =
Math.max(boundary.length * 3/* number of tokens into the stream */, length + boundary.length);
byte[] newBuffer = new byte[bufferLength];
int position = cleanStoredBuffer(newBuffer, 0, bufferLength);
if (bufferLength >= position) {
position += readBuffer(newBuffer, position, bufferLength - position);
}
if (realEof && position == 0) {
return -1;
}
bytesReaded = getBytesReaded(buffer, newBuffer, offset, position, length);
}
return bytesReaded != 0 ? bytesReaded : -1;
}
private int readBuffer(byte[] buffer, int offset, int length) throws IOException {
int count = 0;
int read = 0;
do {
read = input.read(buffer, offset + count, length - count);
if (read > 0) {
count += read;
}
} while (read > 0 && count < length);
if (read < 0) {
realEof = true;
}
return count;
}
private boolean checkEndOfFile() {
if (fakeEof) {
fakeEof = false;
return true;
}
if (realEof && storedBuffer == null) {
return true;
}
if (realEof && !bufferEnd) {
bufferEnd = true;
}
return false;
}
private int getBytesReaded(byte[] buffer, byte[] newBuffer, int offSet, int position, int length) {
int boundaryPosition = locateBoundary(newBuffer, boundary.length - 1, position);
int bytesReaded;
if (length < boundaryPosition || boundaryPosition == -1) {// boundary not found
bytesReaded = Math.min(length, position);
createStoredBuffer(newBuffer, bytesReaded, position);
} else {
bytesReaded = boundaryPosition;
createStoredBuffer(newBuffer, bytesReaded + boundary.length, position);
if (bytesReaded == 0) {
return -1;
}
fakeEof = true;
}
System.arraycopy(newBuffer, 0, buffer, offSet, bytesReaded);
return bytesReaded;
}
private void createStoredBuffer(byte[] buffer, int start, int end) {
int length = end - start;
if (length > 0) {
if (bufferEnd && storedBuffer != null) {
storedBufferPosition -= length;
} else {
int bufferLength = (storedBuffer == null ? 0 : storedBuffer.length - storedBufferPosition);
byte[] newBuffer = new byte[length + bufferLength];
System.arraycopy(buffer, start, newBuffer, 0, length);
if (storedBuffer != null) {
System.arraycopy(storedBuffer, storedBufferPosition, newBuffer, length, bufferLength);
}
storedBuffer = newBuffer;
storedBufferPosition = 0;
}
}
}
private int cleanStoredBuffer(byte[] buffer, int offset, int length) {
int i = 0;
if (storedBuffer != null) {
for (i = 0; i < length && storedBufferPosition < storedBuffer.length; i++) {
buffer[offset + i] = storedBuffer[storedBufferPosition++];
}
if (storedBufferPosition >= storedBuffer.length) {
storedBuffer = null;
storedBufferPosition = 0;
}
}
return i;
}
/* computation of the last table */
private void computeLastTable() {
Arrays.fill(lastTable, boundary.length);
for (int i = 0; i < boundary.length - 1; i++) {
lastTable[boundary[i] & 0xff] = boundary.length - i - 1;
}
}
/* simplified boyer-moore algorithm */
private int locateBoundary(byte[] bytes, int start, int end) {
int position = -1;
if (end > boundary.length) {
int j = 0;
int k = 0;
for (int i = start; i < end; i += lastTable[bytes[i] & 0xff]) {
for (k = i, j = boundary.length - 1; j >= 0 && boundary[j] == bytes[k]; j--) {
k--;
}
if (j == -1) {
position = k + 1;
}
}
}
return position;
}
}