com.marklogic.io.ChunkedInputStream Maven / Gradle / Ivy
/*
* Copyright 2003-2018 MarkLogic Corporation
*
* Licensed 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.
*/
package com.marklogic.io;
import java.io.IOException;
import java.io.InputStream;
public class ChunkedInputStream extends InputStream {
private InputStream stream;
private int chunkSize; // size of a chunk
private int position; // current position in a chunk
private boolean bof = true; // begin of stream
private boolean eof = false; // end of stream
private boolean closed = false;
public ChunkedInputStream(InputStream in) throws IOException {
if (in == null) {
throw new IllegalArgumentException("Input stream cannot be null");
}
this.stream = in;
this.position = 0;
}
@Override
public int read() throws IOException {
if (closed) {
throw new IOException("Attempted read from closed stream.");
}
if (eof) {
return -1;
}
if (position >= chunkSize) {
nextChunk();
if (eof) {
return -1;
}
}
position++;
return stream.read();
}
@Override
public int read (byte[] b, int off, int len) throws IOException {
if (closed) {
throw new IOException("Steam is closed");
}
if (eof) {
return -1;
}
if (position >= chunkSize) {
nextChunk();
if (eof) {
return -1;
}
}
len = Math.min(len, chunkSize - position);
int count = stream.read(b, off, len);
position += count;
return count;
}
@Override
public int read (byte[] b) throws IOException {
return read(b, 0, b.length);
}
private void nextChunk() throws IOException {
if (!bof) {
decodeChunkTrailer();
}
decodeChunkHeader();
bof = false;
position = 0;
if (chunkSize == 0) {
eof = true;
}
}
private void decodeChunkHeader() throws IOException {
int len = 0;
int ch = -1;
for (;;) {
switch (ch = stream.read()) {
case -1:
chunkSize = len;
return;
case '\n':
break;
case '\r':
continue;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
len = (len * 16) + (ch - '0');
continue;
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
len = (len * 16) + (10 + ch - 'a');
continue;
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
len = (len * 16) + (10 + ch - 'A');
continue;
default:
for (;;) {
switch (ch = stream.read()) {
case -1:
chunkSize = len;
return;
case '\n':
break;
default:
continue;
}
break;
}
break;
}
break;
}
chunkSize = len;
position = 0;
}
private void decodeChunkTrailer() throws IOException {
int cr = stream.read();
int lf = stream.read();
if ((cr != '\r') || (lf != '\n')) {
throw new IOException(
"CRLF expected at end of chunk: " + cr + "/" + lf);
}
}
public void close() throws IOException {
if (!closed) {
try {
if (!eof) {
byte buffer[] = new byte[1024];
while (stream.read(buffer) >= 0) {
;
}
}
} finally {
eof = true;
closed = true;
}
}
}
}