/** Copyright (C) 2012 Delcyon, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.delcyon.capo.datastream; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; import java.util.Vector; import com.delcyon.capo.CapoApplication; import com.delcyon.capo.Configuration.PREFERENCE; import com.delcyon.capo.ContextThread; /** * @author jeremiah * */ public class StreamUtil { public static int fullyReadIntoBufferUntilPattern(BufferedInputStream inputStream, byte[] buffer, byte... pattern) throws Exception { int totalRead = 0; byte[] localBuffer = null; //make sure we don't read more than we need to, so choose the smallest buffer size. if(CapoApplication.getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE) > buffer.length) { localBuffer = new byte[buffer.length]; } else { localBuffer = new byte[CapoApplication.getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE)]; } int destPos = 0; while(true) { int count = inputStream.read(localBuffer); totalRead += count; if(count < 0) { break; } if(count > 0) { try { System.arraycopy(localBuffer, 0, buffer, destPos, count); } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) { throw arrayIndexOutOfBoundsException; } destPos += count; if(searchForBytePattern(pattern, buffer, totalRead - pattern.length, pattern.length).size() > 0) { break; } } } return totalRead; } public static byte[] fullyReadUntilPattern(BufferedInputStream inputStream,boolean includePattern, byte... pattern) throws Exception { AccessibleByteArrayOutputStream byteArrayOutputStream = new AccessibleByteArrayOutputStream(); int lastScanPosition = 0; byte[] localBuffer = new byte[CapoApplication.getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE)]; boolean isEOF = false; boolean needMoreData = false; int count = 0; while(true) { if(needMoreData == true) { inputStream.mark(localBuffer.length); } else { count = 0; } count += inputStream.read(localBuffer,count,localBuffer.length-count); if(count < 0) { isEOF = true; break; } if(count > 0) { if(byteArrayOutputStream.size() + count < pattern.length) { needMoreData = true; continue; } else { needMoreData = false; } byteArrayOutputStream.write(localBuffer, 0, count); List<Integer> posList = searchForBytePattern(pattern, byteArrayOutputStream.getBuffer(), lastScanPosition, count); if(posList.size() > 0) { if(posList.get(0) < byteArrayOutputStream.size()-1) { //There was more on the buffer than we needed //reset to the start of last read inputStream.reset(); //skip to our found pos + length of pattern. inputStream.skip(posList.get(0)-lastScanPosition+pattern.length); //now trim our buffer byte[] osBuffer = byteArrayOutputStream.toByteArray(0, posList.get(0)+pattern.length); byteArrayOutputStream.reset(); byteArrayOutputStream.write(osBuffer); } break; } else { lastScanPosition += count; } } } byteArrayOutputStream.close();//pointless, but gets warnings to go away. //use this to indicate that we're done with this stream, because it's closed. if(isEOF && byteArrayOutputStream.size() == 0) { return null; } else if(includePattern == true || isEOF == true) { return byteArrayOutputStream.toByteArray(); } else { return byteArrayOutputStream.toByteArray(0,byteArrayOutputStream.size()-pattern.length); } } public static long readInputStreamIntoOutputStream(InputStream inputStream, OutputStream outputStream) throws Exception { return readInputStreamIntoOutputStream(inputStream, outputStream,CapoApplication.getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE)); } /** * * @param inputStream * @param outputStream * @return total bytes read * @throws Exception */ public static long readInputStreamIntoOutputStream(InputStream inputStream, OutputStream outputStream, int bufferSize) throws Exception { long totalBytesRead = 0l; int bytesRead = 0; byte[] buffer = null; if (Thread.currentThread() instanceof ContextThread) { ContextThread thread = (ContextThread) Thread.currentThread(); if(bufferSize > CapoApplication.getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE)) //allocating huge buffer { if(thread.hugeBuffer == null || thread.hugeBuffer.length < bufferSize) { thread.hugeBuffer = new byte[bufferSize]; } buffer = thread.hugeBuffer; } } if(buffer == null) { buffer = new byte[bufferSize]; } while (bytesRead != -1) { bytesRead = inputStream.read(buffer); if (bytesRead > 0) { outputStream.write(buffer, 0, bytesRead); totalBytesRead += bytesRead; } } outputStream.flush(); return totalBytesRead; } public static String getMD5(byte[] data) throws NoSuchAlgorithmException { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(data); return new BigInteger(1, messageDigest.digest()).toString(16); } /** * Quickly search a byte array for a pattern of bytes. * @param pattern to search for * @param bytes byte array to search for pattern * @param start starting position in bytes to start searching array. * @param length how far passed that start to conclude the search * @return List<Integer> of position in bytes array that are the start of said pattern. */ public static List<Integer> searchForBytePattern(byte[] pattern, byte[] bytes, int start, int length) { Vector<Integer> matchPositions = new Vector<Integer>(); if(start < 0) { start = 0; } int endIndex = (start + length - 1); if (endIndex > bytes.length -1) { endIndex = bytes.length -1; } for(int index = start; index <= endIndex; index++) { //as we walk along the whole thing look for a match on the first byte of our pattern if (pattern[0] == bytes[index] && endIndex - index + 1>= pattern.length) { byte[] match = new byte[pattern.length]; //copy over enough of the array to do an array comparison against our pattern. System.arraycopy(bytes, index, match, 0, pattern.length); if (Arrays.equals(match, pattern)) { //add the index to our list matchPositions.add(index); //then skip ahead index += pattern.length - 1; } } } return matchPositions; } }