-package javajs.util;
-
-import java.io.BufferedInputStream;
-import java.io.InputStream;
-import java.util.Hashtable;
-import java.util.Map;
-
-import javajs.api.GenericBinaryDocumentReader;
-
-/**
- * A simple MessagePack reader. See https://github.com/msgpack/msgpack/blob/master/spec.md
- * with very few dependencies.
- *
- * Nuances:
- *
- * Does not implement unsigned int32 or int64 (delivers simple integers in all cases).
- * Does not use doubles; just floats
- *
- * Note:
- *
- * homogeneousArrays == true will deliver null for empty array.
- *
- *
- * Use in MMTF:
- *
- *
- * BufferedInputStream bs = [whatever]
- *
- * GenericBinaryDocument binaryDoc = new javajs.util.BinaryDocument();
- *
- * binaryDoc.setStream(bs, true);
- *
- *
- * map = (new MessagePackReader(binaryDoc, true)).readMap();
- *
- * entities = (Object[]) map.get("entityList");
- *
- * float[] x = (float[]) decode((byte[]) map.get("xCoordList"))
- *
- *
- * @author Bob Hanson hansonr@stolaf.edu
- */
-
-public class MessagePackReader {
-
- private GenericBinaryDocumentReader doc;
-
- private boolean isHomo;// homogeneous arrays -- use int[] not Integer
-
- // these maps must be checked for the specific number of bits, in the following order:
- private final static int POSITIVEFIXINT_x80 = 0x80; //0xxxxxxx
- private final static int FIXMAP_xF0 = 0x80; //1000xxxx
-// private final static int FIXARRAY_xF0 = 0x90; //1001xxxx
- private final static int FIXSTR_xE0 = 0xa0; //101xxxxx
- private final static int NEGATIVEFIXINT_xE0 = 0xe0; //111xxxxx
- private final static int DEFINITE_xE0 = 0xc0; //110xxxxx
-
- private final static int NIL = 0xc0;
-// private final static int (NEVERUSED) = 0xc1;
- private final static int FALSE = 0xc2;
- private final static int TRUE = 0xc3;
- private final static int BIN8 = 0xc4;
- private final static int BIN16 = 0xc5;
- private final static int BIN32 = 0xc6;
- private final static int EXT8 = 0xc7;
- private final static int EXT16 = 0xc8;
- private final static int EXT32 = 0xc9;
- private final static int FLOAT32 = 0xca;
- private final static int FLOAT64 = 0xcb;
- private final static int UINT8 = 0xcc;
- private final static int UINT16 = 0xcd;
- private final static int UINT32 = 0xce;
- private final static int UINT64 = 0xcf;
- private final static int INT8 = 0xd0;
- private final static int INT16 = 0xd1;
- private final static int INT32 = 0xd2;
- private final static int INT64 = 0xd3;
- private final static int FIXEXT1 = 0xd4;
- private final static int FIXEXT2 = 0xd5;
- private final static int FIXEXT4 = 0xd6;
- private final static int FIXEXT8 = 0xd7;
- private final static int FIXEXT16 = 0xd8;
- private final static int STR8 = 0xd9;
- private final static int STR16 = 0xda;
- private final static int STR32 = 0xdb;
- private final static int ARRAY16 = 0xdc;
- private final static int ARRAY32 = 0xdd;
- private final static int MAP16 = 0xde;
- private final static int MAP32 = 0xdf;
-
- public MessagePackReader(GenericBinaryDocumentReader binaryDoc, boolean isHomogeneousArrays) {
- isHomo = isHomogeneousArrays;
- doc = binaryDoc;
- }
-
- public MessagePackReader() {
- // for reflection
- }
-
- public Map<String, Object> getMapForStream(BufferedInputStream is) throws Exception {
- doc = new BinaryDocument().setStream(is, true);
- Map<String, Object> map = readMap();
- is.close();
- return map;
- }
-
- @SuppressWarnings("unchecked")
- public Map<String, Object> readMap() throws Exception {
- return (Map<String, Object>) getNext(null, 0);
- }
-
- public Object getNext(Object array, int pt) throws Exception {
- int b = doc.readByte() & 0xFF;
- int be0 = b & 0xE0;
- if ((b & POSITIVEFIXINT_x80) == 0) {
- if (array != null) {
- ((int[]) array)[pt] = b;
- return null;
- }
- return Integer.valueOf(b);
- }
- switch (be0) {
- case NEGATIVEFIXINT_xE0:
- b = BC.intToSignedInt(b | 0xFFFFFF00);
- if (array != null) {
- ((int[]) array)[pt] = b;
- return null;
- }
- return Integer.valueOf(b);
- case FIXSTR_xE0: {
- String s = doc.readString(b & 0x1F);
- if (array != null) {
- ((String[]) array)[pt] = s;
- return null;
- }
- return s;
- }
- case FIXMAP_xF0:
- return ((b & 0xF0) == FIXMAP_xF0 ? getMap(b & 0x0F) : getArray(b & 0x0F));
- case DEFINITE_xE0:
- switch (b) {
- case NIL:
- return null;
- case FALSE:
- return Boolean.FALSE;
- case TRUE:
- return Boolean.TRUE;
- case EXT8:
- return getObject(doc.readUInt8());
- case EXT16:
- return getObject(doc.readUnsignedShort());
- case EXT32:
- return getObject(doc.readInt()); // should be unsigned int
- case FIXEXT1:
- return getObject(1);
- case FIXEXT2:
- return getObject(2);
- case FIXEXT4:
- return getObject(4);
- case FIXEXT8:
- return getObject(8);
- case FIXEXT16:
- return getObject(16);
- case ARRAY16:
- return getArray(doc.readUnsignedShort());
- case ARRAY32:
- return getArray(doc.readInt());
- case MAP16:
- return getMap(doc.readUnsignedShort());
- case MAP32:
- return getMap(doc.readInt());
-
- // binary arrays:
-
- case BIN8:
- return doc.readBytes(doc.readUInt8());
- case BIN16:
- return doc.readBytes(doc.readUnsignedShort());
- case BIN32:
- return doc.readBytes(doc.readInt());
- }
- if (array == null) {
- switch (b) {
- case FLOAT32:
- return Float.valueOf(doc.readFloat());
- case FLOAT64:
- return Float.valueOf((float) doc.readDouble());
- case UINT8:
- return Integer.valueOf(doc.readUInt8());
- case UINT16:
- return Integer.valueOf(doc.readUnsignedShort());
- case UINT32:
- return Integer.valueOf(doc.readInt()); // technically should be UInt32
- case UINT64:
- return Long.valueOf(doc.readLong()); // should be unsigned long; incompatible with JavaScript!
- case INT8:
- return Integer.valueOf(doc.readByte());
- case INT16:
- return Integer.valueOf(doc.readShort());
- case INT32:
- return Integer.valueOf(doc.readInt()); // should be Unsigned Int here
- case INT64:
- return Long.valueOf(doc.readLong());
- case STR8:
- return doc.readString(doc.readUInt8());
- case STR16:
- return doc.readString(doc.readShort());
- case STR32:
- return doc.readString(doc.readInt());
- }
- } else {
- switch (b) {
- case FLOAT32:
- ((float[]) array)[pt] = doc.readFloat();
- break;
- case FLOAT64:
- ((float[]) array)[pt] = (float) doc.readDouble();
- break;
- case UINT8:
- ((int[]) array)[pt] = doc.readUInt8();
- break;
- case UINT16:
- ((int[]) array)[pt] = doc.readUnsignedShort();
- break;
- case UINT32:
- ((int[]) array)[pt] = doc.readInt(); // should be unsigned int
- break;
- case UINT64:
- ((int[]) array)[pt] = (int) doc.readLong(); // should be unsigned long; incompatible with JavaScript!
- break;
- case INT8:
- ((int[]) array)[pt] = doc.readByte();
- break;
- case INT16:
- ((int[]) array)[pt] = doc.readShort();
- break;
- case INT32:
- ((int[]) array)[pt] = doc.readInt(); // should be Unsigned Int here
- break;
- case INT64:
- ((int[]) array)[pt] = (int) doc.readLong();
- break;
- case STR8:
- ((String[]) array)[pt] = doc.readString(doc.readUInt8());
- break;
- case STR16:
- ((String[]) array)[pt] = doc.readString(doc.readShort());
- break;
- case STR32:
- ((String[]) array)[pt] = doc.readString(doc.readInt());
- break;
- }
- }
- }
- return null;
- }
-
- private Object getObject(int n) throws Exception {
- return new Object[] { Integer.valueOf(doc.readUInt8()), doc.readBytes(n) };
- }
-
- private Object getArray(int n) throws Exception {
- if (isHomo) {
- if (n == 0)
- return null;
- Object v = getNext(null, 0);
- if (v instanceof Integer) {
- int[] a = new int[n];
- a[0] = ((Integer) v).intValue();
- v = a;
- } else if (v instanceof Float) {
- float[] a = new float[n];
- a[0] = ((Float) v).floatValue();
- v = a;
- } else if (v instanceof String) {
- String[] a = new String[n];
- a[0] = (String) v;
- v = a;
- } else {
- Object[] o = new Object[n];
- o[0] = v;
- for (int i = 1; i < n; i++)
- o[i] = getNext(null, 0);
- return o;
- }
- for (int i = 1; i < n; i++)
- getNext(v, i);
- return v;
- }
- Object[] o = new Object[n];
- for (int i = 0; i < n; i++)
- o[i] = getNext(null, 0);
- return o;
- }
-
- private Object getMap(int n) throws Exception {
- Map<String, Object> map = new Hashtable<String, Object>();
- for (int i = 0; i < n; i++) {
- String key = getNext(null, 0).toString();
- //Logger.info(key);
-
- Object value = getNext(null, 0);
- if (value == null) {
- //Logger.info("null value for " + key);
- } else {
- map.put(key, value);
- }
- }
- return map;
- }
-
- /////////////// MMTF MessagePack decoding ///////////////
-
- /**
- * This single method takes care of all MMTF needs.
- *
- * See https://github.com/rcsb/mmtf/blob/master/spec.md
- *
- * @param b
- *
- * @return array of int, char, or float, depending upon the type
- */
- public static Object decode(byte[] b) {
- int type = BC.bytesToInt(b, 0, true);
- int n = BC.bytesToInt(b, 4, true);
- int param = BC.bytesToInt(b, 8, true);
- switch (type) {
- case 1:
- return getFloats(b, n, 1);
- case 2: // 1-byte
- case 3: // 2-byte
- case 4: // 4-byte
- return getInts(b, n);
- case 5:
- return rldecode32ToStr(b);
- case 6:
- return rldecode32ToChar(b, n);
- case 7:
- return rldecode32(b, n);
- case 8:
- return rldecode32Delta(b, n);
- case 9:
- return rldecodef(b, n, param);
- case 10:
- return unpack16Deltaf(b, n, param);
- case 11:
- return getFloats(b, n, param);
- case 12: // two-byte
- case 13: // one-byte
- return unpackf(b, 14 - type, n, param);
- case 14: // two-byte
- case 15: // one-byte
- return unpack(b, 16 - type, n);
- default:
- System.out.println("MMTF type " + type + " not found!");
- return null;
- }
- }
-
- /**
- * mmtf type 1 and 11
- *
- * byte[4] to float32
- *
- * @param b
- * @param n
- * @param divisor
- * @return array of floats
- */
- public static float[] getFloats(byte[] b, int n, float divisor) {
- if (b == null)
- return null;
- float[] a = new float[n];
- try {
- switch ((b.length - 12) / n) {
- case 2:
- for (int i = 0, j = 12; i < n; i++, j += 2)
- a[i] = BC.bytesToShort(b, j, false) / divisor;
- break;
- case 4:
- for (int i = 0, j = 12; i < n; i++, j += 4)
- a[i] = BC.bytesToFloat(b, j, false);
- break;
- }
- } catch (Exception e) {
- }
- return a;
- }
-
- /**
- * mmtf types 2-4
- *
- * Decode a byte array into a byte, short, or int array.
- *
- * @param b
- * @param n
- *
- * @return array of integers
- */
- public static int[] getInts(byte[] b, int n) {
- if (b == null)
- return null;
- int[] a = new int[n];
- switch ((b.length - 12) / n) {
- case 1:
- for (int i = 0, j = 12; i < n; i++, j++)
- a[i] = b[j];
- break;
- case 2:
- for (int i = 0, j = 12; i < n; i++, j += 2)
- a[i] = BC.bytesToShort(b, j, true);
- break;
- case 4:
- for (int i = 0, j = 12; i < n; i++, j += 4)
- a[i] = BC.bytesToInt(b, j, true);
- break;
- }
- return a;
- }
-
- /**
- * mmtf type 5
- *
- * Decode each four bytes as a 1- to 4-character string label where a 0 byte
- * indicates end-of-string.
- *
- * @param b
- * a byte array
- * @return String[]
- */
- public static String[] rldecode32ToStr(byte[] b) {
- String[] id = new String[(b.length - 12) / 4];
- out: for (int i = 0, len = id.length, pt = 12; i < len; i++) {
- SB sb = new SB();
- for (int j = 0; j < 4; j++) {
- switch (b[pt]) {
- case 0:
- id[i] = sb.toString();
- pt += 4 - j;
- continue out;
- default:
- sb.appendC((char) b[pt++]);
- if (j == 3)
- id[i] = sb.toString();
- continue;
- }
- }
- }
- return id;
- }
-
- /**
- * mmtf type 6
- *
- * Decode an array of int32 using run-length decoding to one char per int.
- *
- * @param b
- * @param n
- * @return array of characters
- */
- public static char[] rldecode32ToChar(byte[] b, int n) {
- if (b == null)
- return null;
- char[] ret = new char[n];
- for (int i = 0, pt = 3; i < n;) {
- char val = (char) b[((pt++) << 2) + 3];
- for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
- ret[i++] = val;
- }
- return ret;
- }
-
- /**
- * mmtf type 7
- *
- * Decode an array of int32 using run-length decoding.
- *
- * @param b
- * @param n
- * @return array of integers
- */
- public static int[] rldecode32(byte[] b, int n) {
- if (b == null)
- return null;
- int[] ret = new int[n];
- for (int i = 0, pt = 3; i < n;) {
- int val = BC.bytesToInt(b, (pt++) << 2, true);
- for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
- ret[i++] = val;
- }
- return ret;
- }
-
- /**
- * mmtf type 8
- *
- * Decode an array of int32 using run-length decoding of a difference array.
- *
- * @param b
- * @param n
- * @return array of integers
- */
- public static int[] rldecode32Delta(byte[] b, int n) {
- if (b == null)
- return null;
- int[] ret = new int[n];
- for (int i = 0, pt = 3, val = 0; i < n;) {
- int diff = BC.bytesToInt(b, (pt++) << 2, true);
- for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
- ret[i++] = (val = val + diff);
- }
- return ret;
- }
-
- /**
- * mmtf type 9
- *
- * Decode an array of int32 using run-length decoding and divide by a divisor
- * to give a float32.
- *
- * @param b
- * @param n
- * @param divisor
- * @return array of floats
- */
- public static float[] rldecodef(byte[] b, int n, float divisor) {
- if (b == null)
- return null;
- float[] ret = new float[n];
- for (int i = 0, pt = 3; i < n;) {
- int val = BC.bytesToInt(b, (pt++) << 2, true);
- for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
- ret[i++] = val / divisor;
- }
- return ret;
- }
-
- /**
- *
- * mmtf type 10
- *
- * Decode an array of int16 using run-length decoding of a difference array.
- *
- * @param b
- * @param n
- * @param divisor
- * @return array of floats
- */
- public static float[] unpack16Deltaf(byte[] b, int n, float divisor) {
- if (b == null)
- return null;
- float[] ret = new float[n];
- for (int i = 0, pt = 6, val = 0, buf = 0; i < n;) {
- int diff = BC.bytesToShort(b, (pt++) << 1, true);
- if (diff == Short.MAX_VALUE || diff == Short.MIN_VALUE) {
- buf += diff;
- } else {
- ret[i++] = (val = val + diff + buf) / divisor;
- buf = 0;
- }
- }
- return ret;
- }
-
- /**
- *
- * mmtf type 12 and 13
- *
- * Unpack an array of int8 or int16 to int32 and divide to give a float32.
- *
- * untested
- *
- * @param b
- * @param nBytes
- * @param n
- * @param divisor
- * @return array of floats
- */
- public static float[] unpackf(byte[] b, int nBytes, int n, float divisor) {
- if (b == null)
- return null;
- float[] ret = new float[n];
- switch (nBytes) {
- case 1:
- for (int i = 0, pt = 12, offset = 0; i < n;) {
- int val = b[pt++];
- if (val == Byte.MAX_VALUE || val == Byte.MIN_VALUE) {
- offset += val;
- } else {
- ret[i++] = (val + offset) / divisor;
- offset = 0;
- }
- }
- break;
- case 2:
- for (int i = 0, pt = 6, offset = 0; i < n;) {
- int val = BC.bytesToShort(b, (pt++) << 1, true);
- if (val == Short.MAX_VALUE || val == Short.MIN_VALUE) {
- offset += val;
- } else {
- ret[i++] = (val + offset) / divisor;
- offset = 0;
- }
- }
- break;
- }
- return ret;
- }
-
- /**
- *
- * mmtf type 14 and 15
- *
- * Unpack an array of int8 or int16 to int32.
- *
- * untested
- *
- * @param b
- * @param nBytes
- * @param n
- * @return array of integers
- */
- public static int[] unpack(byte[] b, int nBytes, int n) {
- if (b == null)
- return null;
- int[] ret = new int[n];
- switch (nBytes) {
- case 1:
- for (int i = 0, pt = 12, offset = 0; i < n;) {
- int val = b[pt++];
- if (val == Byte.MAX_VALUE || val == Byte.MIN_VALUE) {
- offset += val;
- } else {
- ret[i++] = val + offset;
- offset = 0;
- }
- }
- break;
- case 2:
- for (int i = 0, pt = 6, offset = 0; i < n;) {
- int val = BC.bytesToShort(b, (pt++) << 1, true);
- if (val == Short.MAX_VALUE || val == Short.MIN_VALUE) {
- offset += val;
- } else {
- ret[i++] = val + offset;
- offset = 0;
- }
- }
- break;
- }
- return ret;
- }
-
- ///**
- //* Decode an array of int16 using run-length decoding
- //* of a difference array.
- //*
- //* @param b
- //* @param n
- //* @param i0
- //* @return array of integers
- //*/
- //public static int[] rldecode16Delta(byte[] b, int n, int i0) {
- //if (b == null)
- //return null;
- //int[] ret = new int[n];
- //for (int i = 0, pt = i0 / 2, val = 0; i < n;) {
- //int diff = BC.bytesToShort(b, (pt++) << 1, true);
- //for (int j = BC.bytesToShort(b, (pt++) << 1, true); --j >= 0;)
- //ret[i++] = (val = val + diff);
- //}
- //return ret;
- //}
-
- ///**
- //* Do a split delta to a float[] array
- //* @param xyz label "x", "y", "z", or "bFactor"
- //* @param factor for dividing in the end -- 1000f or 100f
- //* @return float[]
- //*
- //*/
- //public static float[] getFloatsSplit(String xyz, float factor) {
- //byte[] big = (byte[]) map.get(xyz + "Big");
- //return (big == null ? null : splitDelta(big,
- //(byte[]) map.get(xyz + "Small"), fileAtomCount, factor));
- //}
-
- ///**
- //* Do a split delta to a float[] array
- //*
- //* @param big
- //* [n m n m n m...] where n is a "big delta" and m is a number of
- //* "small deltas
- //* @param small
- //* array containing the small deltas
- //* @param n
- //* the size of the final array
- //* @param factor
- //* to divide the final result by -- 1000f or 100f here
- //* @return float[]
- //*/
- //public static float[] splitDelta(byte[] big, byte[] small, int n, float factor) {
- //float[] ret = new float[n];
- //for (int i = 0, smallpt = 0, val = 0, datapt = 0, len = big.length >> 2; i < len; i++) {
- //ret[datapt++] = (val = val + BC.bytesToInt(big, i << 2, true)) / factor;
- //if (++i < len)
- //for (int j = BC.bytesToInt(big, i << 2, true); --j >= 0; smallpt++)
- //ret[datapt++] = (val = val + BC.bytesToShort(small, smallpt << 1, true))
- // / factor;
- //}
- //return ret;
- //}
- //
-
-
-}