
org.jmrtd.io.FragmentBuffer Maven / Gradle / Ivy
/*
* JMRTD - A Java API for accessing machine readable travel documents.
*
* Copyright (C) 2006 - 2014 The JMRTD team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id: FragmentBuffer.java 1559 2014-11-14 12:46:26Z martijno $
*/
package org.jmrtd.io;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
/**
* A buffer that can be partially filled.
*
* @author Martijn Oostdijk ([email protected])
*
* @version $Revision: 1559 $
*/
public class FragmentBuffer implements Serializable {
private static final long serialVersionUID = -3510872461790499721L;
/** Buffer with the actual bytes. */
private byte[] buffer; // FIXME can we make this buffer grow dynamically?
/** Administration of which parts of buffer are filled. */
private Collection fragments;
/**
* Creates a fragment buffer.
*/
public FragmentBuffer() {
this(1024);
}
/**
* Creates a fragment buffer.
*
* @param length the length of the buffer
*/
public FragmentBuffer(int length) {
this.buffer = new byte[length];
this.fragments = new HashSet();
}
public synchronized void updateFrom(FragmentBuffer other) {
for (Fragment otherFragment: other.fragments) {
addFragment(otherFragment.offset, other.buffer, otherFragment.offset, otherFragment.length);
}
}
public synchronized void addFragment(int offset, byte b) {
/* FIXME: can this be done more efficiently for common case resulting from InputStreamBuffer read, scan all fragments and extend neighboring one */
addFragment(offset, new byte[] { b });
}
/**
* Adds a fragment of bytes at a specific offset to this file.
*
* @param offset the fragment offset
* @param bytes the bytes from which fragment content will be copied
*/
public synchronized void addFragment(int offset, byte[] bytes) {
addFragment(offset, bytes, 0, bytes.length);
}
/**
* Adds a fragment of bytes at a specific offset to this file.
*
* @param offset the fragment offset
* @param bytes the bytes from which fragment contents will be copied
* @param srcOffset the offset within bytes where the contents of the fragment start
* @param srcLength the length of the fragment
*/
public synchronized void addFragment(int offset, byte[] bytes, int srcOffset, int srcLength) {
if (offset + srcLength > buffer.length) {
setLength(2 * Math.max(offset + srcLength, buffer.length));
}
System.arraycopy(bytes, srcOffset, buffer, offset, srcLength);
int thisOffset = offset;
int thisLength = srcLength;
final Collection otherFragments = new ArrayList(fragments);
for (Fragment other: otherFragments) {
/* On partial overlap we change this fragment, possibly remove the other overlapping fragments we encounter. */
if (other.getOffset() <= thisOffset && thisOffset + thisLength <= other.getOffset() + other.getLength()) {
/*
* [...other fragment.........]
* [...this fragment...]
*
* This fragment is already contained in other. Don't add and return immediately.
*/
return;
} else if (other.getOffset() <= thisOffset && thisOffset <= other.getOffset() + other.getLength()) {
/*
* [...other fragment...]
* [...this fragment...]
*
* This fragment is partially contained in other. Extend this fragment to size of other, remove other.
*/
thisLength = thisOffset + thisLength - other.getOffset();
thisOffset = other.getOffset();
fragments.remove(other);
} else if (thisOffset <= other.getOffset() && other.getOffset() + other.getLength() <= thisOffset + thisLength) {
/*
* [...other fragment...]
* [...this fragment...........]
*
* The other fragment is contained in this fragment. Remove other.
*/
fragments.remove(other);
} else if (thisOffset <= other.getOffset() && other.getOffset() <= thisOffset + thisLength) {
/*
* [...other fragment...]
* [...this fragment...]
*
* This fragment is partially contained in other. Extend this fragment to size of other, remove other.
*/
thisLength = other.getOffset() + other.getLength() - thisOffset;
fragments.remove(other);
}
}
fragments.add(Fragment.getInstance(thisOffset, thisLength));
}
public synchronized int getPosition() {
int result = 0;
for (int i = 0; i < buffer.length; i++) {
if (isCoveredByFragment(i)) {
result = i + 1;
}
}
return result;
}
public synchronized int getBytesBuffered() {
int result = 0;
for (int i = 0; i < buffer.length; i++) {
if (isCoveredByFragment(i)) {
result++;
}
}
return result;
}
public synchronized boolean isCoveredByFragment(int offset) {
return isCoveredByFragment(offset, 1);
}
public synchronized boolean isCoveredByFragment(int offset, int length) {
for (Fragment fragment: fragments) {
int left = fragment.getOffset();
int right = fragment.getOffset() + fragment.getLength();
if (left <= offset && offset + length <= right) { return true; }
}
return false;
}
/**
* Calculates the number of bytes left in the buffer starting from index index
.
*
* @param index the index
*
* @return the number of bytes left in the buffer
*/
public synchronized int getBufferedLength(int index) {
int result = 0;
if (index >= buffer.length) { return 0; }
for (Fragment fragment: fragments) {
int left = fragment.getOffset();
int right = fragment.getOffset() + fragment.getLength();
if (left <= index && index < right) {
int newResult = right - index;
if (newResult > result) { result = newResult; }
}
}
return result;
}
public Collection getFragments() {
return fragments;
}
public byte[] getBuffer() {
return buffer;
}
public int getLength() {
return buffer.length;
}
/**
* Gets the smallest fragment that contains offset
and offset + length
* that has not been buffered in this buffer.
*
* @param offset the offset
* @param length the length
*
* @return the fragment that has not yet been buffered
*/
public synchronized Fragment getSmallestUnbufferedFragment(int offset, int length) {
int thisOffset = offset, thisLength = length;
for (Fragment other: fragments) {
/* On partial overlap we change this fragment, removing sections already buffered. */
if (other.getOffset() <= thisOffset && thisOffset + thisLength <= other.getOffset() + other.getLength()) {
/*
* [...other fragment.........]
* [...this fragment...]
*
* This fragment is already contained in other. Don't add and return immediately.
*/
thisLength = 0; /* NOTE: we don't care about offset */
break;
} else if (other.getOffset() <= thisOffset && thisOffset < other.getOffset() + other.getLength()) {
/*
* [...other fragment...]
* [...this fragment...]
*
* This fragment is partially contained in other. Only fetch the trailing part of this fragment.
*/
int newOffset = other.getOffset() + other.getLength();
int newLength = thisOffset + thisLength - newOffset;
thisOffset = newOffset;
thisLength = newLength;
} else if (thisOffset <= other.getOffset() && other.getOffset() + other.getLength() <= thisOffset + thisLength) {
/*
* [...other fragment...]
* [...this fragment...........]
*
* The other fragment is contained in this fragment. We send this fragment as is.
*/
continue;
} else if (offset <= other.getOffset() && other.getOffset() < thisOffset + thisLength) {
/*
* [...other fragment...]
* [...this fragment...]
*
* This fragment is partially contained in other. Only send the leading part of this fragment.
*/
thisLength = other.getOffset() - thisOffset;
}
}
return Fragment.getInstance(thisOffset, thisLength);
}
public synchronized String toString() {
return "FragmentBuffer [" + buffer.length + ", " + fragments + "]";
}
public synchronized boolean equals(Object otherObject) {
if (otherObject == null) { return false; }
if (otherObject == this) { return true; }
if (!otherObject.getClass().equals(FragmentBuffer.class)) { return false; }
FragmentBuffer otherBuffer = (FragmentBuffer)otherObject;
if (otherBuffer.buffer == null && this.buffer != null) { return false; }
if (otherBuffer.buffer != null && this.buffer == null) { return false; }
if (otherBuffer.fragments == null && this.fragments != null) { return false; }
if (otherBuffer.fragments != null && this.fragments == null) { return false; }
return Arrays.equals(otherBuffer.buffer, this.buffer) && otherBuffer.fragments.equals(this.fragments);
}
public int hashCode() {
return 3 * Arrays.hashCode(buffer) + 2 * fragments.hashCode() + 7;
}
private synchronized void setLength(int length) {
if (length <= buffer.length) { return; }
byte[] newBuffer = new byte[length];
System.arraycopy(this.buffer, 0, newBuffer, 0, this.buffer.length);
this.buffer = newBuffer;
}
/**
* Fragments encapsulate pairs of offset and length.
*/
public static class Fragment implements Serializable {
private static final long serialVersionUID = -3795931618553980328L;
private int offset, length;
public int getOffset() {
return offset;
}
public int getLength() {
return length;
}
private Fragment(int offset, int length) {
this.offset = offset;
this.length = length;
}
public static Fragment getInstance(int offset, int length) {
return new Fragment(offset, length);
}
public String toString() {
return "[" + offset + " .. " + (offset + length - 1) + " (" + length + ")]";
}
public boolean equals(Object otherObject) {
if (otherObject == null) { return false; }
if (otherObject == this) { return true; }
if (!otherObject.getClass().equals(Fragment.class)) { return false; }
Fragment otherFragment = (Fragment)otherObject;
return otherFragment.offset == offset && otherFragment.length == length;
}
public int hashCode() {
return 2 * offset + 3 * length + 5;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy