e9cdb7bbdd8ea7453c7ee46c653166ad899ad613
[jalview.git] / src / javajs / util / MessagePackReader.java
1 package javajs.util;
2
3 import java.util.Hashtable;
4 import java.util.Map;
5
6 import javajs.api.GenericBinaryDocumentReader;
7
8 /**
9  * A simple MessagePack reader. See https://github.com/msgpack/msgpack/blob/master/spec.md
10  * with very few dependencies.
11  * 
12  * Nuances: 
13  * 
14  *  Does not implement unsigned int32 or int64 (delivers simple integers in all cases).
15  *  Does not use doubles; just floats
16  *  
17  * Note: 
18  * 
19  *  homogeneousArrays == true will deliver null for empty array.
20  * 
21  * 
22  * Use in MMTF:
23  * 
24  * 
25  *     BufferedInputStream bs = [whatever]
26  *     
27  *      GenericBinaryDocument binaryDoc =  new javajs.util.BinaryDocument();
28  *   
29  *      binaryDoc.setStream(bs, true);
30  * 
31  * 
32  *     map = (new MessagePackReader(binaryDoc, true)).readMap();
33  * 
34  *     entities = (Object[]) map.get("entityList");
35  *
36  *     float[] x = (float[]) decode((byte[]) map.get("xCoordList"))
37  *     
38  * 
39  * @author Bob Hanson hansonr@stolaf.edu
40  */
41
42 public class MessagePackReader {
43
44   private GenericBinaryDocumentReader doc;
45
46   private boolean isHomo;// homogeneous arrays -- use int[] not Integer
47
48   // these maps must be checked for the specific number of bits, in the following order:
49   private final static int POSITIVEFIXINT_x80 = 0x80; //0xxxxxxx
50   private final static int FIXMAP_xF0         = 0x80; //1000xxxx
51 //  private final static int FIXARRAY_xF0       = 0x90; //1001xxxx
52   private final static int FIXSTR_xE0         = 0xa0; //101xxxxx
53   private final static int NEGATIVEFIXINT_xE0 = 0xe0; //111xxxxx
54   private final static int DEFINITE_xE0       = 0xc0; //110xxxxx
55   
56   private final static int NIL          = 0xc0;
57 //  private final static int (NEVERUSED)        = 0xc1;
58   private final static int FALSE        = 0xc2;
59   private final static int TRUE         = 0xc3;
60   private final static int BIN8         = 0xc4;
61   private final static int BIN16        = 0xc5;
62   private final static int BIN32        = 0xc6;
63   private final static int EXT8         = 0xc7;
64   private final static int EXT16        = 0xc8;
65   private final static int EXT32        = 0xc9;
66   private final static int FLOAT32      = 0xca;
67   private final static int FLOAT64      = 0xcb;
68   private final static int UINT8        = 0xcc;
69   private final static int UINT16       = 0xcd;
70   private final static int UINT32       = 0xce;
71   private final static int UINT64       = 0xcf;
72   private final static int INT8         = 0xd0;
73   private final static int INT16        = 0xd1;
74   private final static int INT32        = 0xd2;
75   private final static int INT64        = 0xd3;
76   private final static int FIXEXT1      = 0xd4;
77   private final static int FIXEXT2      = 0xd5;
78   private final static int FIXEXT4      = 0xd6;
79   private final static int FIXEXT8      = 0xd7;
80   private final static int FIXEXT16     = 0xd8;
81   private final static int STR8         = 0xd9;
82   private final static int STR16        = 0xda;
83   private final static int STR32        = 0xdb;
84   private final static int ARRAY16      = 0xdc;
85   private final static int ARRAY32      = 0xdd;
86   private final static int MAP16        = 0xde;
87   private final static int MAP32        = 0xdf;
88
89   public MessagePackReader(GenericBinaryDocumentReader binaryDoc, boolean isHomogeneousArrays) {
90     isHomo = isHomogeneousArrays;
91     doc = binaryDoc;
92   }
93
94   @SuppressWarnings("unchecked")
95   public Map<String, Object> readMap() throws Exception {
96     return (Map<String, Object>) getNext(null, 0);
97   }
98   
99   public Object getNext(Object array, int pt) throws Exception {
100     int b = doc.readByte() & 0xFF;
101     int be0 = b & 0xE0;
102     if ((b & POSITIVEFIXINT_x80) == 0) {
103       if (array != null) {
104         ((int[]) array)[pt] = b;
105         return null;
106       }
107       return Integer.valueOf(b);
108     }
109     switch (be0) {
110     case NEGATIVEFIXINT_xE0:
111       b = BC.intToSignedInt(b | 0xFFFFFF00);
112       if (array != null) {
113         ((int[]) array)[pt] = b;
114         return null;
115       }
116       return Integer.valueOf(b);
117     case FIXSTR_xE0: {
118       String s = doc.readString(b & 0x1F);
119       if (array != null) {
120         ((String[]) array)[pt] = s; 
121         return null;
122       } 
123       return s;
124     }
125     case FIXMAP_xF0:
126       return ((b & 0xF0) == FIXMAP_xF0 ? getMap(b & 0x0F) : getArray(b & 0x0F));
127     case DEFINITE_xE0:
128       switch (b) {
129       case NIL:
130         return null;
131       case FALSE:
132         return Boolean.FALSE;
133       case TRUE:
134         return Boolean.TRUE;
135       case EXT8:
136         return getObject(doc.readUInt8());
137       case EXT16:
138         return getObject(doc.readUnsignedShort());
139       case EXT32:
140         return getObject(doc.readInt()); // should be unsigned int
141       case FIXEXT1:
142         return getObject(1);
143       case FIXEXT2:
144         return getObject(2);
145       case FIXEXT4:
146         return getObject(4);
147       case FIXEXT8:
148         return getObject(8);
149       case FIXEXT16:
150         return getObject(16);
151       case ARRAY16:
152         return getArray(doc.readUnsignedShort());
153       case ARRAY32:
154         return getArray(doc.readInt());
155       case MAP16:
156         return getMap(doc.readUnsignedShort());
157       case MAP32:
158         return getMap(doc.readInt());
159
160         // binary arrays:
161
162       case BIN8:
163         return doc.readBytes(doc.readUInt8());
164       case BIN16:
165         return doc.readBytes(doc.readUnsignedShort());
166       case BIN32:
167         return doc.readBytes(doc.readInt());
168       }
169       if (array == null) {
170         switch (b) {
171         case FLOAT32:
172           return Float.valueOf(doc.readFloat());
173         case FLOAT64:
174           return Float.valueOf((float) doc.readDouble());
175         case UINT8:
176           return Integer.valueOf(doc.readUInt8());
177         case UINT16:
178           return Integer.valueOf(doc.readUnsignedShort());
179         case UINT32:
180           return Integer.valueOf(doc.readInt()); // technically should be UInt32
181         case UINT64:
182           return Long.valueOf(doc.readLong()); // should be unsigned long; incompatible with JavaScript!
183         case INT8:
184           return Integer.valueOf(doc.readByte());
185         case INT16:
186           return Integer.valueOf(doc.readShort());
187         case INT32:
188           return Integer.valueOf(doc.readInt()); // should be Unsigned Int here
189         case INT64:
190           return Long.valueOf(doc.readLong());
191         case STR8:
192           return doc.readString(doc.readUInt8());
193         case STR16:
194           return doc.readString(doc.readShort());
195         case STR32:
196           return doc.readString(doc.readInt());
197         }
198       } else {
199         switch (b) {
200         case FLOAT32:
201           ((float[]) array)[pt] = doc.readFloat();
202           break;
203         case FLOAT64:
204           ((float[]) array)[pt] = (float) doc.readDouble();
205           break;
206         case UINT8:
207           ((int[]) array)[pt] = doc.readUInt8();
208           break;
209         case UINT16:
210           ((int[]) array)[pt] = doc.readUnsignedShort();
211           break;
212         case UINT32:
213           ((int[]) array)[pt] =  doc.readInt(); // should be unsigned int
214           break;
215         case UINT64:
216           ((int[]) array)[pt] =  (int) doc.readLong(); // should be unsigned long; incompatible with JavaScript!
217           break;
218         case INT8:
219           ((int[]) array)[pt] =  doc.readByte();
220           break;
221         case INT16:
222           ((int[]) array)[pt] = doc.readShort();
223           break;
224         case INT32:
225           ((int[]) array)[pt] =  doc.readInt(); // should be Unsigned Int here
226           break;
227         case INT64:
228           ((int[]) array)[pt] =  (int) doc.readLong();
229           break;
230         case STR8:
231           ((String[]) array)[pt] = doc.readString(doc.readUInt8());
232           break;
233         case STR16:
234           ((String[]) array)[pt] = doc.readString(doc.readShort());
235           break;
236         case STR32:
237           ((String[]) array)[pt] = doc.readString(doc.readInt());
238           break;
239         }
240       }
241     }
242     return null;
243   }
244
245   private Object getObject(int n) throws Exception {
246     return new Object[] { Integer.valueOf(doc.readUInt8()), doc.readBytes(n) };
247   }
248
249   private Object getArray(int n) throws Exception {
250     if (isHomo) {
251       if (n == 0)
252         return null;
253       Object v = getNext(null, 0);
254       if (v instanceof Integer) {
255         int[] a = new int[n];
256         a[0] = ((Integer) v).intValue();
257         v = a;
258       } else if (v instanceof Float) {
259         float[] a = new float[n];
260         a[0] = ((Float) v).floatValue();
261         v = a;
262       } else if (v instanceof String) {
263         String[] a = new String[n];
264         a[0] = (String) v;
265         v = a;
266       } else {
267         Object[] o = new Object[n];
268         o[0] = v;
269         for (int i = 1; i < n; i++)
270           o[i] = getNext(null, 0);
271         return o;
272       }
273       for (int i = 1; i < n; i++)
274         getNext(v, i);
275       return v;
276     }
277     Object[] o = new Object[n];
278     for (int i = 0; i < n; i++)
279       o[i] = getNext(null, 0);
280     return o;
281   }
282
283   private Object getMap(int n) throws Exception {
284     Map<String, Object> map = new Hashtable<String, Object>();
285     for (int i = 0; i < n; i++) {
286       String key = getNext(null, 0).toString();
287       //Logger.info(key);
288
289       Object value = getNext(null, 0);
290       if (value == null) {
291         //Logger.info("null value for " + key);
292       } else {
293         map.put(key, value);
294       }
295     }
296     return map;
297   }
298
299   /////////////// MMTF MessagePack decoding ///////////////
300
301   /**
302    * This single method takes care of all MMTF needs.
303    * 
304    * See https://github.com/rcsb/mmtf/blob/master/spec.md
305    * 
306    * @param b
307    * 
308    * @return array of int, char, or float, depending upon the type
309    */
310   public static Object decode(byte[] b) {
311     int type = BC.bytesToInt(b, 0, true);
312     int n = BC.bytesToInt(b, 4, true);
313     int param = BC.bytesToInt(b, 8, true);
314     switch (type) {
315     case 1:
316       return getFloats(b, n, 1);
317     case 2: // 1-byte
318     case 3: // 2-byte
319     case 4: // 4-byte
320       return getInts(b, n);
321     case 5:
322       return rldecode32ToStr(b);
323     case 6:
324       return rldecode32ToChar(b, n);
325     case 7:
326       return rldecode32(b, n);
327     case 8:
328       return rldecode32Delta(b, n);
329     case 9:
330       return rldecodef(b, n, param);
331     case 10:
332       return unpack16Deltaf(b, n, param);
333     case 11:
334       return getFloats(b, n, param);
335     case 12: // two-byte
336     case 13: // one-byte
337       return unpackf(b, 14 - type, n, param);
338     case 14: // two-byte
339     case 15: // one-byte
340       return unpack(b, 16 - type, n);
341     default:
342       System.out.println("MMTF type " + type + " not found!");
343       return null;
344    }
345   }
346
347   /**
348    * mmtf type 1 and 11
349    * 
350    * byte[4] to float32
351    * 
352    * @param b
353    * @param n
354    * @param divisor
355    * @return array of floats
356    */
357   public static float[] getFloats(byte[] b, int n, float divisor) {
358     if (b == null)
359       return null;
360     float[] a = new float[n];
361     try {
362       switch ((b.length - 12) / n) {  
363       case 2:
364         for (int i = 0, j = 12; i < n; i++, j += 2)
365           a[i] = BC.bytesToShort(b, j, false) / divisor;
366         break;
367       case 4:
368         for (int i = 0, j = 12; i < n; i++, j += 4)
369           a[i] = BC.bytesToFloat(b, j, false);
370         break;
371       }
372     } catch (Exception e) {
373     }
374     return a;
375   }
376
377   /**
378    * mmtf types 2-4
379    * 
380    * Decode a byte array into a byte, short, or int array.
381    * 
382    * @param b
383    * @param n
384    *        
385    * @return array of integers
386    */
387   public static int[] getInts(byte[] b, int n) {
388     if (b == null)
389       return null;
390     int[] a = new int[n];
391     switch ((b.length - 12) / n) {
392     case 1:
393       for (int i = 0, j = 12; i < n; i++, j++)
394         a[i] = b[j];
395       break;
396     case 2:
397       for (int i = 0, j = 12; i < n; i++, j += 2)
398         a[i] = BC.bytesToShort(b, j, true);
399       break;
400     case 4:
401       for (int i = 0, j = 12; i < n; i++, j += 4)
402         a[i] = BC.bytesToInt(b, j, true);
403       break;
404     }
405     return a;
406   }
407
408   /**
409    * mmtf type 5
410    * 
411    * Decode each four bytes as a 1- to 4-character string label where a 0 byte
412    * indicates end-of-string.
413    * 
414    * @param b
415    *        a byte array
416    * @return String[]
417    */
418   public static String[] rldecode32ToStr(byte[] b) {
419     String[] id = new String[(b.length - 12) / 4];
420     out: for (int i = 0, len = id.length, pt = 12; i < len; i++) {
421       SB sb = new SB();
422       for (int j = 0; j < 4; j++) {
423         switch (b[pt]) {
424         case 0:
425           id[i] = sb.toString();
426           pt += 4 - j;
427           continue out;
428         default:
429           sb.appendC((char) b[pt++]);
430           if (j == 3)
431             id[i] = sb.toString();
432           continue;
433         }
434       }
435     }
436     return id;
437   }
438
439   /**
440    * mmtf type 6
441    * 
442    * Decode an array of int32 using run-length decoding to one char per int.
443    * 
444    * @param b
445    * @param n
446    * @return array of characters
447    */
448   public static char[] rldecode32ToChar(byte[] b, int n) {
449     if (b == null)
450       return null;
451     char[] ret = new char[n];
452     for (int i = 0, pt = 3; i < n;) {
453       char val = (char) b[((pt++) << 2) + 3];
454       for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
455         ret[i++] = val;
456     }
457     return ret;
458   }
459
460   /**
461    * mmtf type 7
462    * 
463    * Decode an array of int32 using run-length decoding.
464    * 
465    * @param b
466    * @param n
467    * @return array of integers
468    */
469   public static int[] rldecode32(byte[] b, int n) {
470     if (b == null)
471       return null;
472     int[] ret = new int[n];
473     for (int i = 0, pt = 3; i < n;) {
474       int val = BC.bytesToInt(b, (pt++) << 2, true);
475       for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
476         ret[i++] = val;
477     }
478     return ret;
479   }
480
481   /**
482    * mmtf type 8
483    * 
484    * Decode an array of int32 using run-length decoding of a difference array.
485    * 
486    * @param b
487    * @param n
488    * @return array of integers
489    */
490   public static int[] rldecode32Delta(byte[] b, int n) {
491     if (b == null)
492       return null;
493     int[] ret = new int[n];
494     for (int i = 0, pt = 3, val = 0; i < n;) {
495       int diff = BC.bytesToInt(b, (pt++) << 2, true);
496       for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
497         ret[i++] = (val = val + diff);
498     }
499     return ret;
500   }
501
502   /**
503    * mmtf type 9
504    * 
505    * Decode an array of int32 using run-length decoding and divide by a divisor
506    * to give a float32.
507    * 
508    * @param b
509    * @param n
510    * @param divisor
511    * @return array of floats
512    */
513   public static float[] rldecodef(byte[] b, int n, float divisor) {
514     if (b == null)
515       return null;
516     float[] ret = new float[n];
517     for (int i = 0, pt = 3; i < n;) {
518       int val = BC.bytesToInt(b, (pt++) << 2, true);
519       for (int j = BC.bytesToInt(b, (pt++) << 2, true); --j >= 0;)
520         ret[i++] = val / divisor;
521     }
522     return ret;
523   }
524
525   /**
526    * 
527    * mmtf type 10
528    * 
529    * Decode an array of int16 using run-length decoding of a difference array.
530    * 
531    * @param b
532    * @param n
533    * @param divisor
534    * @return array of floats
535    */
536   public static float[] unpack16Deltaf(byte[] b, int n, float divisor) {
537     if (b == null)
538       return null;
539     float[] ret = new float[n];
540     for (int i = 0, pt = 6, val = 0, buf = 0; i < n;) {
541       int diff = BC.bytesToShort(b, (pt++) << 1, true);
542       if (diff == Short.MAX_VALUE || diff == Short.MIN_VALUE) {
543         buf += diff;
544       } else {
545         ret[i++] = (val = val + diff + buf) / divisor;
546         buf = 0;
547       }
548     }
549     return ret;
550   }
551
552   /**
553    * 
554    * mmtf type 12 and 13
555    * 
556    * Unpack an array of int8 or int16 to int32 and divide to give a float32.
557    * 
558    * untested
559    * 
560    * @param b
561    * @param nBytes 
562    * @param n
563    * @param divisor 
564    * @return array of floats
565    */
566   public static float[] unpackf(byte[] b, int nBytes, int n, float divisor) {
567     if (b == null)
568       return null;
569     float[] ret = new float[n];
570     switch (nBytes) {
571     case 1:
572       for (int i = 0, pt = 12, offset = 0; i < n;) {
573         int val = b[pt++];
574         if (val == Byte.MAX_VALUE || val == Byte.MIN_VALUE) {
575           offset += val;
576         } else {
577           ret[i++] = (val + offset) / divisor;
578           offset = 0;
579         }
580       }
581       break;
582     case 2:
583       for (int i = 0, pt = 6, offset = 0; i < n;) {
584         int val = BC.bytesToShort(b, (pt++) << 1, true);
585         if (val == Short.MAX_VALUE || val == Short.MIN_VALUE) {
586           offset += val;
587         } else {
588           ret[i++] = (val + offset) / divisor;
589           offset = 0;
590         }
591       }
592       break;
593     }
594     return ret;
595   }
596
597   /**
598    * 
599    * mmtf type 14 and 15
600    * 
601    * Unpack an array of int8 or int16 to int32.
602    * 
603    * untested
604    * 
605    * @param b
606    * @param nBytes 
607    * @param n
608    * @return array of integers
609    */
610   public static int[] unpack(byte[] b, int nBytes, int n) {
611     if (b == null)
612       return null;
613     int[] ret = new int[n];
614     switch (nBytes) {
615     case 1:
616       for (int i = 0, pt = 12, offset = 0; i < n;) {
617         int val = b[pt++];
618         if (val == Byte.MAX_VALUE || val == Byte.MIN_VALUE) {
619           offset += val;
620         } else {
621           ret[i++] = val + offset;
622           offset = 0;
623         }
624       }
625       break;
626     case 2:
627       for (int i = 0, pt = 6, offset = 0; i < n;) {
628         int val = BC.bytesToShort(b, (pt++) << 1, true);
629         if (val == Short.MAX_VALUE || val == Short.MIN_VALUE) {
630           offset += val;
631         } else {
632           ret[i++] = val + offset;
633           offset = 0;
634         }
635       }
636       break;
637     }
638     return ret;
639   }
640
641   ///**
642   //* Decode an array of int16 using run-length decoding
643   //* of a difference array.
644   //* 
645   //* @param b
646   //* @param n
647   //* @param i0 
648   //* @return array of integers
649   //*/
650   //public static int[] rldecode16Delta(byte[] b, int n, int i0) {
651   //if (b == null)
652   //return null;
653   //int[] ret = new int[n];
654   //for (int i = 0, pt = i0 / 2, val = 0; i < n;) {
655   //int diff = BC.bytesToShort(b, (pt++) << 1, true);
656   //for (int j = BC.bytesToShort(b, (pt++) << 1, true); --j >= 0;)
657   //ret[i++] = (val = val + diff);
658   //}
659   //return ret;
660   //}
661
662   ///**
663   //* Do a split delta to a float[] array
664   //* @param xyz label "x", "y", "z", or "bFactor"
665   //* @param factor for dividing in the end -- 1000f or 100f 
666   //* @return float[]
667   //* 
668   //*/ 
669   //public static float[] getFloatsSplit(String xyz, float factor) {
670   //byte[] big = (byte[]) map.get(xyz + "Big");
671   //return (big == null ? null : splitDelta(big,
672   //(byte[]) map.get(xyz + "Small"), fileAtomCount, factor));
673   //}
674
675   ///**
676   //* Do a split delta to a float[] array
677   //* 
678   //* @param big
679   //*        [n m n m n m...] where n is a "big delta" and m is a number of
680   //*        "small deltas
681   //* @param small
682   //*        array containing the small deltas
683   //* @param n
684   //*        the size of the final array
685   //* @param factor
686   //*        to divide the final result by -- 1000f or 100f here
687   //* @return float[]
688   //*/
689   //public static float[] splitDelta(byte[] big, byte[] small, int n, float factor) {
690   //float[] ret = new float[n];
691   //for (int i = 0, smallpt = 0, val = 0, datapt = 0, len = big.length >> 2; i < len; i++) {
692   //ret[datapt++] = (val = val + BC.bytesToInt(big, i << 2, true)) / factor;
693   //if (++i < len)
694   //for (int j = BC.bytesToInt(big, i << 2, true); --j >= 0; smallpt++)
695   //ret[datapt++] = (val = val + BC.bytesToShort(small, smallpt << 1, true))
696   //  / factor;
697   //}
698   //return ret;
699   //}
700   //
701
702
703 }