~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 
 * contributor license agreements.  See the NOTICE file distributed with
4
 
 * this work for additional information regarding copyright ownership.
5
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 
 * (the "License"); you may not use this file except in compliance with
7
 
 * the License.  You may obtain a copy of the License at
8
 
 *
9
 
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 
 *
11
 
 * Unless required by applicable law or agreed to in writing, software
12
 
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 
 * See the License for the specific language governing permissions and
15
 
 * limitations under the License.
16
 
 */
17
 
package org.apache.solr.common.util;
18
 
 
19
 
import org.apache.solr.common.SolrDocument;
20
 
import org.apache.solr.common.SolrDocumentList;
21
 
 
22
 
import java.io.IOException;
23
 
import java.io.InputStream;
24
 
import java.io.OutputStream;
25
 
import java.util.*;
26
 
import java.nio.ByteBuffer;
27
 
 
28
 
/**
29
 
 * The class is designed to optimaly serialize/deserialize any supported types in Solr response. As we know there are only a limited type of
30
 
 * items this class can do it with very minimal amount of payload and code. There are 15 known types and if there is an
31
 
 * object in the object tree which does not fall into these types, It must be converted to one of these. Implement an
32
 
 * ObjectResolver and pass it over It is expected that this class is used on both end of the pipes. The class has one
33
 
 * read method and one write method for each of the datatypes
34
 
 * <p/>
35
 
 * Note -- Never re-use an instance of this class for more than one marshal or unmarshall operation. Always create a new
36
 
 * instance.
37
 
 */
38
 
public class JavaBinCodec {
39
 
 
40
 
  public static final byte
41
 
          NULL = 0,
42
 
          BOOL_TRUE = 1,
43
 
          BOOL_FALSE = 2,
44
 
          BYTE = 3,
45
 
          SHORT = 4,
46
 
          DOUBLE = 5,
47
 
          INT = 6,
48
 
          LONG = 7,
49
 
          FLOAT = 8,
50
 
          DATE = 9,
51
 
          MAP = 10,
52
 
          SOLRDOC = 11,
53
 
          SOLRDOCLST = 12,
54
 
          BYTEARR = 13,
55
 
          ITERATOR = 14,
56
 
          /**
57
 
           * this is a special tag signals an end. No value is associated with it
58
 
           */
59
 
          END = 15,
60
 
 
61
 
          // types that combine tag + length (or other info) in a single byte
62
 
          TAG_AND_LEN = (byte) (1 << 5),
63
 
          STR = (byte) (1 << 5),
64
 
          SINT = (byte) (2 << 5),
65
 
          SLONG = (byte) (3 << 5),
66
 
          ARR = (byte) (4 << 5), //
67
 
          ORDERED_MAP = (byte) (5 << 5), // SimpleOrderedMap (a NamedList subclass, and more common)
68
 
          NAMED_LST = (byte) (6 << 5), // NamedList
69
 
          EXTERN_STRING = (byte) (7 << 5);
70
 
 
71
 
 
72
 
  private static byte VERSION = 2;
73
 
  private ObjectResolver resolver;
74
 
  protected FastOutputStream daos;
75
 
 
76
 
  public JavaBinCodec() {
77
 
  }
78
 
 
79
 
  public JavaBinCodec(ObjectResolver resolver) {
80
 
    this.resolver = resolver;
81
 
  }
82
 
 
83
 
  public void marshal(Object nl, OutputStream os) throws IOException {
84
 
    daos = FastOutputStream.wrap(os);
85
 
    try {
86
 
      daos.writeByte(VERSION);
87
 
      writeVal(nl);
88
 
    } finally {
89
 
      daos.flushBuffer();
90
 
    }
91
 
  }
92
 
 
93
 
  byte version;
94
 
 
95
 
  public Object unmarshal(InputStream is) throws IOException {
96
 
    FastInputStream dis = FastInputStream.wrap(is);
97
 
    version = dis.readByte();
98
 
    if (version != VERSION) {
99
 
      throw new RuntimeException("Invalid version (expected " + VERSION +
100
 
          ", but " + version + ") or the data in not in 'javabin' format");
101
 
    }
102
 
    return readVal(dis);
103
 
  }
104
 
 
105
 
 
106
 
  public SimpleOrderedMap readOrderedMap(FastInputStream dis) throws IOException {
107
 
    int sz = readSize(dis);
108
 
    SimpleOrderedMap nl = new SimpleOrderedMap();
109
 
    for (int i = 0; i < sz; i++) {
110
 
      String name = (String) readVal(dis);
111
 
      Object val = readVal(dis);
112
 
      nl.add(name, val);
113
 
    }
114
 
    return nl;
115
 
  }
116
 
 
117
 
  public NamedList readNamedList(FastInputStream dis) throws IOException {
118
 
    int sz = readSize(dis);
119
 
    NamedList nl = new NamedList();
120
 
    for (int i = 0; i < sz; i++) {
121
 
      String name = (String) readVal(dis);
122
 
      Object val = readVal(dis);
123
 
      nl.add(name, val);
124
 
    }
125
 
    return nl;
126
 
  }
127
 
 
128
 
  public void writeNamedList(NamedList nl) throws IOException {
129
 
    writeTag(nl instanceof SimpleOrderedMap ? ORDERED_MAP : NAMED_LST, nl.size());
130
 
    for (int i = 0; i < nl.size(); i++) {
131
 
      String name = nl.getName(i);
132
 
      writeExternString(name);
133
 
      Object val = nl.getVal(i);
134
 
      writeVal(val);
135
 
    }
136
 
  }
137
 
 
138
 
  public void writeVal(Object val) throws IOException {
139
 
    if (writeKnownType(val)) {
140
 
      return;
141
 
    } else {
142
 
      Object tmpVal = val;
143
 
      if (resolver != null) {
144
 
        tmpVal = resolver.resolve(val, this);
145
 
        if (tmpVal == null) return; // null means the resolver took care of it fully
146
 
        if (writeKnownType(tmpVal)) return;
147
 
      }
148
 
    }
149
 
 
150
 
    writeVal(val.getClass().getName() + ':' + val.toString());
151
 
  }
152
 
 
153
 
  protected static final Object END_OBJ = new Object();
154
 
 
155
 
  byte tagByte;
156
 
 
157
 
  public Object readVal(FastInputStream dis) throws IOException {
158
 
    tagByte = dis.readByte();
159
 
 
160
 
    // if ((tagByte & 0xe0) == 0) {
161
 
    // if top 3 bits are clear, this is a normal tag
162
 
 
163
 
    // OK, try type + size in single byte
164
 
    switch (tagByte >>> 5) {
165
 
      case STR >>> 5:
166
 
        return readStr(dis);
167
 
      case SINT >>> 5:
168
 
        return readSmallInt(dis);
169
 
      case SLONG >>> 5:
170
 
        return readSmallLong(dis);
171
 
      case ARR >>> 5:
172
 
        return readArray(dis);
173
 
      case ORDERED_MAP >>> 5:
174
 
        return readOrderedMap(dis);
175
 
      case NAMED_LST >>> 5:
176
 
        return readNamedList(dis);
177
 
      case EXTERN_STRING >>> 5:
178
 
        return readExternString(dis);
179
 
    }
180
 
 
181
 
    switch (tagByte) {
182
 
      case NULL:
183
 
        return null;
184
 
      case DATE:
185
 
        return new Date(dis.readLong());
186
 
      case INT:
187
 
        return dis.readInt();
188
 
      case BOOL_TRUE:
189
 
        return Boolean.TRUE;
190
 
      case BOOL_FALSE:
191
 
        return Boolean.FALSE;
192
 
      case FLOAT:
193
 
        return dis.readFloat();
194
 
      case DOUBLE:
195
 
        return dis.readDouble();
196
 
      case LONG:
197
 
        return dis.readLong();
198
 
      case BYTE:
199
 
        return dis.readByte();
200
 
      case SHORT:
201
 
        return dis.readShort();
202
 
      case MAP:
203
 
        return readMap(dis);
204
 
      case SOLRDOC:
205
 
        return readSolrDocument(dis);
206
 
      case SOLRDOCLST:
207
 
        return readSolrDocumentList(dis);
208
 
      case BYTEARR:
209
 
        return readByteArray(dis);
210
 
      case ITERATOR:
211
 
        return readIterator(dis);
212
 
      case END:
213
 
        return END_OBJ;
214
 
    }
215
 
 
216
 
    throw new RuntimeException("Unknown type " + tagByte);
217
 
  }
218
 
 
219
 
  public boolean writeKnownType(Object val) throws IOException {
220
 
    if (writePrimitive(val)) return true;
221
 
    if (val instanceof NamedList) {
222
 
      writeNamedList((NamedList) val);
223
 
      return true;
224
 
    }
225
 
    if (val instanceof SolrDocumentList) { // SolrDocumentList is a List, so must come before List check
226
 
      writeSolrDocumentList((SolrDocumentList) val);
227
 
      return true;
228
 
    }
229
 
    if (val instanceof Collection) {
230
 
      writeArray((Collection) val);
231
 
      return true;
232
 
    }
233
 
    if (val instanceof Object[]) {
234
 
      writeArray((Object[]) val);
235
 
      return true;
236
 
    }
237
 
    if (val instanceof SolrDocument) {
238
 
      //this needs special treatment to know which fields are to be written
239
 
      if (resolver == null) {
240
 
        writeSolrDocument((SolrDocument) val);
241
 
      } else {
242
 
        Object retVal = resolver.resolve(val, this);
243
 
        if (retVal != null) {
244
 
          if (retVal instanceof SolrDocument) {
245
 
            writeSolrDocument((SolrDocument) retVal);
246
 
          } else {
247
 
            writeVal(retVal);
248
 
          }
249
 
        }
250
 
      }
251
 
      return true;
252
 
    }
253
 
    if (val instanceof Map) {
254
 
      writeMap((Map) val);
255
 
      return true;
256
 
    }
257
 
    if (val instanceof Iterator) {
258
 
      writeIterator((Iterator) val);
259
 
      return true;
260
 
    }
261
 
    if (val instanceof Iterable) {
262
 
      writeIterator(((Iterable) val).iterator());
263
 
      return true;
264
 
    }
265
 
    return false;
266
 
  }
267
 
 
268
 
  public void writeTag(byte tag) throws IOException {
269
 
    daos.writeByte(tag);
270
 
  }
271
 
 
272
 
  public void writeTag(byte tag, int size) throws IOException {
273
 
    if ((tag & 0xe0) != 0) {
274
 
      if (size < 0x1f) {
275
 
        daos.writeByte(tag | size);
276
 
      } else {
277
 
        daos.writeByte(tag | 0x1f);
278
 
        writeVInt(size - 0x1f, daos);
279
 
      }
280
 
    } else {
281
 
      daos.writeByte(tag);
282
 
      writeVInt(size, daos);
283
 
    }
284
 
  }
285
 
 
286
 
  public void writeByteArray(byte[] arr, int offset, int len) throws IOException {
287
 
    writeTag(BYTEARR, len);
288
 
    daos.write(arr, offset, len);
289
 
  }
290
 
 
291
 
  public byte[] readByteArray(FastInputStream dis) throws IOException {
292
 
    byte[] arr = new byte[readVInt(dis)];
293
 
    dis.readFully(arr);
294
 
    return arr;
295
 
  }
296
 
 
297
 
  public void writeSolrDocument(SolrDocument doc) throws IOException {
298
 
    writeSolrDocument(doc, null);
299
 
  }
300
 
 
301
 
  public void writeSolrDocument(SolrDocument doc, Set<String> fields) throws IOException {
302
 
    int count = 0;
303
 
    if (fields == null) {
304
 
      count = doc.getFieldNames().size();
305
 
    } else {
306
 
      for (Map.Entry<String, Object> entry : doc) {
307
 
        if (fields.contains(entry.getKey())) count++;
308
 
      }
309
 
    }
310
 
    writeTag(SOLRDOC);
311
 
    writeTag(ORDERED_MAP, count);
312
 
    for (Map.Entry<String, Object> entry : doc) {
313
 
      if (fields == null || fields.contains(entry.getKey())) {
314
 
        String name = entry.getKey();
315
 
        writeExternString(name);
316
 
        Object val = entry.getValue();
317
 
        writeVal(val);
318
 
      }
319
 
    }
320
 
  }
321
 
 
322
 
  public SolrDocument readSolrDocument(FastInputStream dis) throws IOException {
323
 
    NamedList nl = (NamedList) readVal(dis);
324
 
    SolrDocument doc = new SolrDocument();
325
 
    for (int i = 0; i < nl.size(); i++) {
326
 
      String name = nl.getName(i);
327
 
      Object val = nl.getVal(i);
328
 
      doc.setField(name, val);
329
 
    }
330
 
    return doc;
331
 
  }
332
 
 
333
 
  public SolrDocumentList readSolrDocumentList(FastInputStream dis) throws IOException {
334
 
    SolrDocumentList solrDocs = new SolrDocumentList();
335
 
    List list = (List) readVal(dis);
336
 
    solrDocs.setNumFound((Long) list.get(0));
337
 
    solrDocs.setStart((Long) list.get(1));
338
 
    solrDocs.setMaxScore((Float) list.get(2));
339
 
 
340
 
    List l = (List) readVal(dis);
341
 
    solrDocs.addAll(l);
342
 
    return solrDocs;
343
 
  }
344
 
 
345
 
  public void writeSolrDocumentList(SolrDocumentList docs)
346
 
          throws IOException {
347
 
    writeTag(SOLRDOCLST);
348
 
    List l = new ArrayList(3);
349
 
    l.add(docs.getNumFound());
350
 
    l.add(docs.getStart());
351
 
    l.add(docs.getMaxScore());
352
 
    writeArray(l);
353
 
    writeArray(docs);
354
 
  }
355
 
 
356
 
  public Map readMap(FastInputStream dis)
357
 
          throws IOException {
358
 
    int sz = readVInt(dis);
359
 
    Map m = new LinkedHashMap();
360
 
    for (int i = 0; i < sz; i++) {
361
 
      Object key = readVal(dis);
362
 
      Object val = readVal(dis);
363
 
      m.put(key, val);
364
 
 
365
 
    }
366
 
    return m;
367
 
  }
368
 
 
369
 
  public void writeIterator(Iterator iter) throws IOException {
370
 
    writeTag(ITERATOR);
371
 
    while (iter.hasNext()) {
372
 
      writeVal(iter.next());
373
 
    }
374
 
    writeVal(END_OBJ);
375
 
  }
376
 
 
377
 
  public List readIterator(FastInputStream fis) throws IOException {
378
 
    ArrayList l = new ArrayList();
379
 
    while (true) {
380
 
      Object o = readVal(fis);
381
 
      if (o == END_OBJ) break;
382
 
      l.add(o);
383
 
    }
384
 
    return l;
385
 
  }
386
 
 
387
 
  public void writeArray(List l) throws IOException {
388
 
    writeTag(ARR, l.size());
389
 
    for (int i = 0; i < l.size(); i++) {
390
 
      writeVal(l.get(i));
391
 
    }
392
 
  }
393
 
 
394
 
  public void writeArray(Collection coll) throws IOException {
395
 
    writeTag(ARR, coll.size());
396
 
    for (Object o : coll) {
397
 
      writeVal(o);
398
 
    }
399
 
 
400
 
  }
401
 
 
402
 
  public void writeArray(Object[] arr) throws IOException {
403
 
    writeTag(ARR, arr.length);
404
 
    for (int i = 0; i < arr.length; i++) {
405
 
      Object o = arr[i];
406
 
      writeVal(o);
407
 
    }
408
 
  }
409
 
 
410
 
  public List readArray(FastInputStream dis) throws IOException {
411
 
    int sz = readSize(dis);
412
 
    ArrayList l = new ArrayList(sz);
413
 
    for (int i = 0; i < sz; i++) {
414
 
      l.add(readVal(dis));
415
 
    }
416
 
    return l;
417
 
  }
418
 
 
419
 
  /**
420
 
   * write the string as tag+length, with length being the number of UTF-8 bytes
421
 
   */
422
 
  public void writeStr(String s) throws IOException {
423
 
    if (s == null) {
424
 
      writeTag(NULL);
425
 
      return;
426
 
    }
427
 
    int end = s.length();
428
 
    int maxSize = end * 4;
429
 
    if (bytes == null || bytes.length < maxSize) bytes = new byte[maxSize];
430
 
    int upto = 0;
431
 
    for(int i=0;i<end;i++) {
432
 
      final int code = (int) s.charAt(i);
433
 
 
434
 
      if (code < 0x80)
435
 
        bytes[upto++] = (byte) code;
436
 
      else if (code < 0x800) {
437
 
        bytes[upto++] = (byte) (0xC0 | (code >> 6));
438
 
        bytes[upto++] = (byte)(0x80 | (code & 0x3F));
439
 
      } else if (code < 0xD800 || code > 0xDFFF) {
440
 
        bytes[upto++] = (byte)(0xE0 | (code >> 12));
441
 
        bytes[upto++] = (byte)(0x80 | ((code >> 6) & 0x3F));
442
 
        bytes[upto++] = (byte)(0x80 | (code & 0x3F));
443
 
      } else {
444
 
        // surrogate pair
445
 
        // confirm valid high surrogate
446
 
        if (code < 0xDC00 && (i < end-1)) {
447
 
          int utf32 = (int) s.charAt(i+1);
448
 
          // confirm valid low surrogate and write pair
449
 
          if (utf32 >= 0xDC00 && utf32 <= 0xDFFF) { 
450
 
            utf32 = ((code - 0xD7C0) << 10) + (utf32 & 0x3FF);
451
 
            i++;
452
 
            bytes[upto++] = (byte)(0xF0 | (utf32 >> 18));
453
 
            bytes[upto++] = (byte)(0x80 | ((utf32 >> 12) & 0x3F));
454
 
            bytes[upto++] = (byte)(0x80 | ((utf32 >> 6) & 0x3F));
455
 
            bytes[upto++] = (byte)(0x80 | (utf32 & 0x3F));
456
 
            continue;
457
 
          }
458
 
        }
459
 
        // replace unpaired surrogate or out-of-order low surrogate
460
 
        // with substitution character
461
 
        bytes[upto++] = (byte) 0xEF;
462
 
        bytes[upto++] = (byte) 0xBF;
463
 
        bytes[upto++] = (byte) 0xBD;
464
 
      }
465
 
    }
466
 
    writeTag(STR, upto);
467
 
    daos.write(bytes, 0, upto);
468
 
  }
469
 
 
470
 
  byte[] bytes;
471
 
  char[] chars;
472
 
 
473
 
  public String readStr(FastInputStream dis) throws IOException {
474
 
    int sz = readSize(dis);
475
 
    if (chars == null || chars.length < sz) chars = new char[sz];
476
 
    if (bytes == null || bytes.length < sz) bytes = new byte[sz];
477
 
    dis.readFully(bytes, 0, sz);
478
 
    int outUpto=0;
479
 
    for (int i = 0; i < sz;) {
480
 
      final int b = bytes[i++]&0xff;
481
 
      final int ch;
482
 
      if (b < 0xc0) {
483
 
        assert b < 0x80;
484
 
        ch = b;
485
 
      } else if (b < 0xe0) {
486
 
        ch = ((b&0x1f)<<6) + (bytes[i++]&0x3f);
487
 
      } else if (b < 0xf0) {
488
 
        ch = ((b&0xf)<<12) + ((bytes[i++]&0x3f)<<6) + (bytes[i++]&0x3f);
489
 
      } else {
490
 
        assert b < 0xf8;
491
 
        ch = ((b&0x7)<<18) + ((bytes[i++]&0x3f)<<12) + ((bytes[i++]&0x3f)<<6) + (bytes[i++]&0x3f);
492
 
      }
493
 
      if (ch <= 0xFFFF) {
494
 
        // target is a character <= 0xFFFF
495
 
        chars[outUpto++] = (char) ch;
496
 
      } else {
497
 
        // target is a character in range 0xFFFF - 0x10FFFF
498
 
        final int chHalf = ch - 0x10000;
499
 
        chars[outUpto++] = (char) ((chHalf >> 0xA) + 0xD800);
500
 
        chars[outUpto++] = (char) ((chHalf & 0x3FF) + 0xDC00);
501
 
      }
502
 
    }
503
 
    return new String(chars, 0, outUpto);
504
 
  }
505
 
 
506
 
  public void writeInt(int val) throws IOException {
507
 
    if (val > 0) {
508
 
      int b = SINT | (val & 0x0f);
509
 
 
510
 
      if (val >= 0x0f) {
511
 
        b |= 0x10;
512
 
        daos.writeByte(b);
513
 
        writeVInt(val >>> 4, daos);
514
 
      } else {
515
 
        daos.writeByte(b);
516
 
      }
517
 
 
518
 
    } else {
519
 
      daos.writeByte(INT);
520
 
      daos.writeInt(val);
521
 
    }
522
 
  }
523
 
 
524
 
  public int readSmallInt(FastInputStream dis) throws IOException {
525
 
    int v = tagByte & 0x0F;
526
 
    if ((tagByte & 0x10) != 0)
527
 
      v = (readVInt(dis) << 4) | v;
528
 
    return v;
529
 
  }
530
 
 
531
 
 
532
 
  public void writeLong(long val) throws IOException {
533
 
    if ((val & 0xff00000000000000L) == 0) {
534
 
      int b = SLONG | ((int) val & 0x0f);
535
 
      if (val >= 0x0f) {
536
 
        b |= 0x10;
537
 
        daos.writeByte(b);
538
 
        writeVLong(val >>> 4, daos);
539
 
      } else {
540
 
        daos.writeByte(b);
541
 
      }
542
 
    } else {
543
 
      daos.writeByte(LONG);
544
 
      daos.writeLong(val);
545
 
    }
546
 
  }
547
 
 
548
 
  public long readSmallLong(FastInputStream dis) throws IOException {
549
 
    long v = tagByte & 0x0F;
550
 
    if ((tagByte & 0x10) != 0)
551
 
      v = (readVLong(dis) << 4) | v;
552
 
    return v;
553
 
  }
554
 
 
555
 
  public boolean writePrimitive(Object val) throws IOException {
556
 
    if (val == null) {
557
 
      daos.writeByte(NULL);
558
 
      return true;
559
 
    } else if (val instanceof String) {
560
 
      writeStr((String) val);
561
 
      return true;
562
 
    } else if (val instanceof Integer) {
563
 
      writeInt(((Integer) val).intValue());
564
 
      return true;
565
 
    } else if (val instanceof Long) {
566
 
      writeLong(((Long) val).longValue());
567
 
      return true;
568
 
    } else if (val instanceof Float) {
569
 
      daos.writeByte(FLOAT);
570
 
      daos.writeFloat(((Float) val).floatValue());
571
 
      return true;
572
 
    } else if (val instanceof Date) {
573
 
      daos.writeByte(DATE);
574
 
      daos.writeLong(((Date) val).getTime());
575
 
      return true;
576
 
    } else if (val instanceof Boolean) {
577
 
      if ((Boolean) val) daos.writeByte(BOOL_TRUE);
578
 
      else daos.writeByte(BOOL_FALSE);
579
 
      return true;
580
 
    } else if (val instanceof Double) {
581
 
      daos.writeByte(DOUBLE);
582
 
      daos.writeDouble(((Double) val).doubleValue());
583
 
      return true;
584
 
    } else if (val instanceof Byte) {
585
 
      daos.writeByte(BYTE);
586
 
      daos.writeByte(((Byte) val).intValue());
587
 
      return true;
588
 
    } else if (val instanceof Short) {
589
 
      daos.writeByte(SHORT);
590
 
      daos.writeShort(((Short) val).intValue());
591
 
      return true;
592
 
    } else if (val instanceof byte[]) {
593
 
      writeByteArray((byte[]) val, 0, ((byte[]) val).length);
594
 
      return true;
595
 
    }else if (val instanceof ByteBuffer) {
596
 
      ByteBuffer buf = (ByteBuffer) val;
597
 
      writeByteArray(buf.array(),buf.position(),buf.limit() - buf.position());
598
 
      return true;
599
 
    } else if (val == END_OBJ) {
600
 
      writeTag(END);
601
 
      return true;
602
 
    }
603
 
    return false;
604
 
  }
605
 
 
606
 
 
607
 
  public void writeMap(Map val)
608
 
          throws IOException {
609
 
    writeTag(MAP, val.size());
610
 
    for (Map.Entry entry : (Set<Map.Entry>) val.entrySet()) {
611
 
      Object key = entry.getKey();
612
 
      if (key instanceof String) {
613
 
        writeExternString((String) key);
614
 
      } else {
615
 
        writeVal(key);
616
 
      }
617
 
      writeVal(entry.getValue());
618
 
    }
619
 
  }
620
 
 
621
 
 
622
 
  public int readSize(FastInputStream in) throws IOException {
623
 
    int sz = tagByte & 0x1f;
624
 
    if (sz == 0x1f) sz += readVInt(in);
625
 
    return sz;
626
 
  }
627
 
 
628
 
 
629
 
  /**
630
 
   * Special method for variable length int (copied from lucene). Usually used for writing the length of a
631
 
   * collection/array/map In most of the cases the length can be represented in one byte (length < 127) so it saves 3
632
 
   * bytes/object
633
 
   *
634
 
   * @param i
635
 
   * @param out
636
 
   *
637
 
   * @throws IOException
638
 
   */
639
 
  public static void writeVInt(int i, FastOutputStream out) throws IOException {
640
 
    while ((i & ~0x7F) != 0) {
641
 
      out.writeByte((byte) ((i & 0x7f) | 0x80));
642
 
      i >>>= 7;
643
 
    }
644
 
    out.writeByte((byte) i);
645
 
  }
646
 
 
647
 
  /**
648
 
   * The counterpart for the above
649
 
   *
650
 
   * @param in
651
 
   *
652
 
   * @return the int value
653
 
   *
654
 
   * @throws IOException
655
 
   */
656
 
  public static int readVInt(FastInputStream in) throws IOException {
657
 
    byte b = in.readByte();
658
 
    int i = b & 0x7F;
659
 
    for (int shift = 7; (b & 0x80) != 0; shift += 7) {
660
 
      b = in.readByte();
661
 
      i |= (b & 0x7F) << shift;
662
 
    }
663
 
    return i;
664
 
  }
665
 
 
666
 
 
667
 
  public static void writeVLong(long i, FastOutputStream out) throws IOException {
668
 
    while ((i & ~0x7F) != 0) {
669
 
      out.writeByte((byte) ((i & 0x7f) | 0x80));
670
 
      i >>>= 7;
671
 
    }
672
 
    out.writeByte((byte) i);
673
 
  }
674
 
 
675
 
  public static long readVLong(FastInputStream in) throws IOException {
676
 
    byte b = in.readByte();
677
 
    long i = b & 0x7F;
678
 
    for (int shift = 7; (b & 0x80) != 0; shift += 7) {
679
 
      b = in.readByte();
680
 
      i |= (long) (b & 0x7F) << shift;
681
 
    }
682
 
    return i;
683
 
  }
684
 
 
685
 
  private int stringsCount = 0;
686
 
  private Map<String, Integer> stringsMap;
687
 
  private List<String> stringsList;
688
 
 
689
 
  public void writeExternString(String s) throws IOException {
690
 
    if (s == null) {
691
 
      writeTag(NULL);
692
 
      return;
693
 
    }
694
 
    Integer idx = stringsMap == null ? null : stringsMap.get(s);
695
 
    if (idx == null) idx = 0;
696
 
    writeTag(EXTERN_STRING, idx);
697
 
    if (idx == 0) {
698
 
      writeStr(s);
699
 
      if (stringsMap == null) stringsMap = new HashMap<String, Integer>();
700
 
      stringsMap.put(s, ++stringsCount);
701
 
    }
702
 
 
703
 
  }
704
 
 
705
 
  public String readExternString(FastInputStream fis) throws IOException {
706
 
    int idx = readSize(fis);
707
 
    if (idx != 0) {// idx != 0 is the index of the extern string
708
 
      return stringsList.get(idx - 1);
709
 
    } else {// idx == 0 means it has a string value
710
 
      String s = (String) readVal(fis);
711
 
      if (stringsList == null) stringsList = new ArrayList<String>();
712
 
      stringsList.add(s);
713
 
      return s;
714
 
    }
715
 
  }
716
 
 
717
 
 
718
 
  public static interface ObjectResolver {
719
 
    public Object resolve(Object o, JavaBinCodec codec) throws IOException;
720
 
  }
721
 
 
722
 
 
723
 
}