package hep.io.root.core;
import hep.io.root.RootClassNotFound;
import hep.io.root.RootObject;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.Inflater;
/**
* An implementation of RootInput
* @author Tony Johnson (tonyj@slac.stanford.edu)
* @version $Id: RootInputStream.java 13655 2009-05-19 23:26:52Z tonyj $
*/
class RootInputStream extends DataInputStream implements RootInput
{
private static int kByteCountMask = 0x40000000;
private static int kNewClassTag = 0xFFFFFFFF;
private static int kClassMask = 0x80000000;
private static int kMapOffset = 2;
private Hashtable readMap = new Hashtable();
private RootInput top;
public RootInputStream(RootByteArrayInputStream in, RootInput top)
{
super(in);
this.top = top;
}
public RootClassFactory getFactory()
{
return top.getFactory();
}
public void setMap(int offset)
{
((RootByteArrayInputStream) in).setOffset(offset);
}
public void setPosition(long pos)
{
((RootByteArrayInputStream) in).setPosition(pos);
}
public long getPosition()
{
return ((RootByteArrayInputStream) in).getPosition();
}
public int getRootVersion()
{
return top.getRootVersion();
}
public RootInput getTop()
{
return top;
}
public void checkLength(AbstractRootObject obj) throws IOException
{
RootInputStream.checkLength(this, obj);
}
public void clearMap()
{
((RootByteArrayInputStream) in).setOffset(0);
}
public int readArray(int[] data) throws IOException
{
return RootInputStream.readArray(this, data);
}
public int readArray(byte[] data) throws IOException
{
return RootInputStream.readArray(this, data);
}
public int readArray(short[] data) throws IOException
{
return RootInputStream.readArray(this, data);
}
public int readArray(float[] data) throws IOException
{
return RootInputStream.readArray(this, data);
}
public int readArray(double[] data) throws IOException
{
return RootInputStream.readArray(this, data);
}
public void readFixedArray(int[] data) throws IOException
{
RootInputStream.readFixedArray(this, data);
}
public void readFixedArray(long[] data) throws IOException
{
RootInputStream.readFixedArray(this, data);
}
public void readFixedArray(byte[] data) throws IOException
{
int l = data.length;
for (int i = 0;i<l;)
{
int n = in.read(data,i,l-i);
if (n < 0) throw new EOFException();
i += n;
}
}
public void readFixedArray(short[] data) throws IOException
{
RootInputStream.readFixedArray(this, data);
}
public void readFixedArray(float[] data) throws IOException
{
RootInputStream.readFixedArray(this, data);
}
public void readFixedArray(double[] data) throws IOException
{
RootInputStream.readFixedArray(this, data);
}
public void readMultiArray(Object[] array) throws IOException
{
RootInputStream.readMultiArray(this, array);
}
public String readNullTerminatedString(int maxLength) throws IOException
{
return RootInputStream.readNullTerminatedString(this, maxLength);
}
public RootObject readObject(String type) throws IOException
{
return RootInputStream.readObject(this, type);
}
public RootObject readObjectRef() throws IOException
{
return RootInputStream.readObjectRef(this, readMap);
}
public String readString() throws IOException
{
return RootInputStream.readString(this);
}
public int readVersion() throws IOException
{
return RootInputStream.readVersion(this, null);
}
public int readVersion(AbstractRootObject obj) throws IOException
{
return RootInputStream.readVersion(this, obj);
}
public RootInput slice(int size) throws IOException
{
return RootInputStream.slice(this, size);
}
public RootInput slice(int inSize, int outSize) throws IOException
{
return RootInputStream.slice(this, inSize, outSize);
}
public double readTwistedDouble() throws IOException
{
return RootInputStream.readTwistedDouble(this);
}
public void dump() throws IOException
{
RootInputStream.dump(this,200);
}
public void skipObject() throws IOException
{
RootInputStream.skipObject(this);
}
static void checkLength(RootInput in, AbstractRootObject obj) throws IOException
{
obj.checkLength(in.getPosition());
}
static int readArray(RootInput in, int[] data) throws IOException
{
int n = in.readInt();
for (int i = 0; i < n; i++)
data[i] = in.readInt();
return n;
}
static int readArray(RootInput in, byte[] data) throws IOException
{
int n = in.readInt();
for (int i = 0; i < n; i++)
data[i] = in.readByte();
return n;
}
static int readArray(RootInput in, short[] data) throws IOException
{
int n = in.readInt();
for (int i = 0; i < n; i++)
data[i] = in.readShort();
return n;
}
static int readArray(RootInput in, float[] data) throws IOException
{
int n = in.readInt();
for (int i = 0; i < n; i++)
data[i] = in.readFloat();
return n;
}
static int readArray(RootInput in, double[] data) throws IOException
{
int n = in.readInt();
for (int i = 0; i < n; i++)
data[i] = in.readDouble();
return n;
}
static void readFixedArray(RootInput in, int[] data) throws IOException
{
int n = data.length;
for (int i = 0; i < n; i++)
data[i] = in.readInt();
}
static void readFixedArray(RootInput in, long[] data) throws IOException
{
int n = data.length;
for (int i = 0; i < n; i++)
data[i] = in.readLong();
}
static void readFixedArray(RootInput in, byte[] data) throws IOException
{
int n = data.length;
for (int i = 0; i < n; i++)
data[i] = in.readByte();
}
static void readFixedArray(RootInput in, short[] data) throws IOException
{
int n = data.length;
for (int i = 0; i < n; i++)
data[i] = in.readShort();
}
static void readFixedArray(RootInput in, float[] data) throws IOException
{
int n = data.length;
for (int i = 0; i < n; i++)
data[i] = in.readFloat();
}
static void readFixedArray(RootInput in, double[] data) throws IOException
{
int n = data.length;
for (int i = 0; i < n; i++)
data[i] = in.readDouble();
}
static void readMultiArray(RootInput in, Object[] array) throws IOException
{
for (int i = 0; i < array.length; i++)
{
Object o = array[i];
if (o instanceof double[])
readFixedArray(in, (double[]) o);
else if (o instanceof float[])
readFixedArray(in, (float[]) o);
else if (o instanceof short[])
readFixedArray(in, (short[]) o);
else if (o instanceof byte[])
readFixedArray(in, (byte[]) o);
else if (o instanceof int[])
readFixedArray(in, (int[]) o);
else if (o instanceof long[])
readFixedArray(in, (long[]) o);
else if (o instanceof Object[])
readMultiArray(in, (Object[]) o);
else
throw new IOException("Unknown multiarray element: "+o.getClass());
}
}
static String readNullTerminatedString(DataInput in, int maxLength) throws IOException
{
int actualLength = maxLength - 1;
byte[] data = new byte[maxLength];
;
for (int i = 0; i < maxLength; i++)
{
data[i] = in.readByte();
if (data[i] == 0)
{
actualLength = i;
break;
}
}
return new String(data, 0, actualLength);
}
static RootObject readObject(RootInput in, String type) throws IOException
{
try
{
AbstractRootObject obj = ((GenericRootClass) in.getFactory().create(type)).newInstance();
obj.read(in);
return obj;
}
catch (RootClassNotFound x)
{
throw new IOException("Could not find class " + x.getClassName());
}
}
static RootObject readObjectRef(RootInput in, Map map) throws IOException
{
long objStartPos = in.getPosition();
int tag;
int fVersion = 0;
long startpos = 0;
int bcnt = in.readInt();
if (((bcnt & kByteCountMask) == 0) || (bcnt == kNewClassTag))
{
tag = bcnt;
bcnt = 0;
}
else
{
fVersion = 1;
startpos = in.getPosition();
tag = in.readInt();
}
// in case tag is object tag return object
//if (tag != 0) System.out.println("ReadObject tag="+tag+" ("+Integer.toHexString(tag)+")");
if ((tag & kClassMask) == 0)
{
if (tag == 0) return null;
// FixMe: tag == 1 means "self", but don't currently have self available.
if (tag == 1) return null;
Object obj = map.get(new Long(tag));
if ((obj == null) || !(obj instanceof RootObject))
throw new IOException("Invalid tag found " + tag);
return (RootObject) obj;
}
if (tag == kNewClassTag)
{
try
{
String className = in.readNullTerminatedString(80);
GenericRootClass rootClass = (GenericRootClass) in.getFactory().create(className);
// Add this class to the map
if (fVersion > 0)
map.put(new Long(startpos + kMapOffset), rootClass);
else
map.put(new Long(map.size() + 1), rootClass);
AbstractRootObject obj = rootClass.newInstance();
// Add this class to the map
if (fVersion > 0)
map.put(new Long(objStartPos + kMapOffset), obj);
else
map.put(new Long(map.size() + 1), obj);
obj.read(in);
return obj;
}
catch (RootClassNotFound x)
{
throw new IOException("Class not found during object read: " + x.getClassName());
}
}
else
{
tag &= ~kClassMask;
Object cls = map.get(new Long(tag));
if ((cls == null) || !(cls instanceof BasicRootClass))
{
System.out.println("Map Dump");
Iterator i = map.entrySet().iterator();
while (i.hasNext())
System.out.println(i.next());
throw new IOException("Invalid object tag " + tag);
}
GenericRootClass rootClass = (GenericRootClass) cls;
AbstractRootObject obj = rootClass.newInstance();
if (fVersion > 0)
{
Long offset = new Long(objStartPos + kMapOffset);
map.put(offset, obj);
//System.out.println("Added map entry at "+offset);
}
else
map.put(new Long(map.size() + 1), obj);
obj.read(in);
return obj;
}
}
static String readString(DataInput in) throws IOException
{
int l = in.readByte();
if (l==-1) l = in.readInt();
byte[] data = new byte[l];
for (int i = 0; i < l; i++)
data[i] = in.readByte();
return new String(data);
}
static int readVersion(RootInput in, AbstractRootObject obj) throws IOException
{
int version = in.readShort();
if ((version & 0x4000) == 0)
return version;
int byteCount = ((version & 0x3fff) << 16) + in.readUnsignedShort();
if (obj != null)
obj.setExpectedLength(in.getPosition(), byteCount);
return in.readShort();
}
static void skipObject(RootInput in) throws IOException
{
int version = in.readShort();
if ((version & 0x4000) == 0) throw new IOException("Cannot skip object with no length");
int byteCount = ((version & 0x3fff) << 16) + in.readUnsignedShort();
System.err.println("skipping "+byteCount);
in.skipBytes(byteCount);
}
static RootInput slice(RootInput in, int size) throws IOException
{
// Is it really necessary to buffer here, can't we reflect requests
// on the slice back to the underlying parent?
byte[] buf = new byte[size];
in.readFixedArray(buf);
return new RootInputStream(new RootByteArrayInputStream(buf, 0), in.getTop());
}
static RootInput slice(RootInput in, int size, int decompressedSize) throws IOException
{
// Currently we read the whole buffer before starting to decompress.
// It would be better to decompress each component as we read it, but perhaps
// not possible if we need to support random access into the unpacked array.
try
{
byte[] buf = new byte[size];
in.readFixedArray(buf);
byte[] out = new byte[decompressedSize];
int nout = 0;
int nin = 0;
while (nout < decompressedSize)
{
boolean hasHeader = buf[0] == 'Z' && buf[1] == 'L';
Inflater inf = new Inflater(!hasHeader);
try
{
nin += 9;
inf.setInput(buf, nin, buf.length - nin);
int rc = inf.inflate(out, nout, out.length - nout);
if (rc==0) throw new IOException("Inflate unexpectedly returned 0 (perhaps OutOfMemory?)");
nout += rc;
nin += inf.getTotalIn();
}
finally
{
inf.end();
}
}
return new RootInputStream(new RootByteArrayInputStream(out, 0), in.getTop());
}
catch (Exception x)
{
IOException xx = new IOException("Error during decompression (size="+size+"/"+decompressedSize+")");
xx.initCause(x);
throw xx;
}
catch (OutOfMemoryError x)
{
IOException xx = new IOException("Error during decompression (size="+size+"/"+decompressedSize+")");
xx.initCause(x);
throw xx;
}
}
static double readTwistedDouble(RootInput in) throws IOException
{
int i1 = in.readInt();
int i2 = in.readInt();
long val = i1 + (((long) i2)<<32);
return Double.longBitsToDouble(val);
}
static void dump(RootInput in, int n) throws IOException
{
for (int i=0; i<n; i++)
{
int b = in.readByte();
char c = b<32 ? ' ' : (char) b;
System.out.println("dump["+i+"]: "+b+" "+c);
}
throw new IOException("dump");
}
}