1/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package org.apache.thrift.protocol;
21
22import haxe.io.Bytes;
23import haxe.io.BytesInput;
24import haxe.io.BytesOutput;
25import haxe.io.BytesBuffer;
26import haxe.ds.GenericStack;
27import haxe.Int32;
28import haxe.Int64;
29import haxe.Utf8;
30
31import org.apache.thrift.TException;
32import org.apache.thrift.transport.TTransport;
33import org.apache.thrift.helper.ZigZag;
34import org.apache.thrift.helper.BitConverter;
35
36
37/**
38* Compact protocol implementation for thrift.
39*/
40class TCompactProtocol extends TRecursionTracker implements TProtocol {
41
42    private static var ANONYMOUS_STRUCT : TStruct = new TStruct("");
43    private static var TSTOP : TField = new TField("", TType.STOP, 0);
44
45    private static inline var PROTOCOL_ID : Int = 0x82;
46    private static inline var VERSION : Int = 1;
47    private static inline var VERSION_MASK : Int = 0x1f; // 0001 1111
48    private static inline var TYPE_MASK : Int = 0xE0; // 1110 0000
49    private static inline var TYPE_BITS : Int = 0x07; // 0000 0111
50    private static inline var TYPE_SHIFT_AMOUNT : Int = 5;
51
52
53    private static var ttypeToCompactType = [
54        TType.STOP    => TCompactTypes.STOP,
55        TType.BOOL    => TCompactTypes.BOOLEAN_TRUE,
56        TType.BYTE    => TCompactTypes.BYTE,
57        TType.DOUBLE  => TCompactTypes.DOUBLE,
58        TType.I16     => TCompactTypes.I16,
59        TType.I32     => TCompactTypes.I32,
60        TType.I64     => TCompactTypes.I64,
61        TType.STRING  => TCompactTypes.BINARY,
62        TType.STRUCT  => TCompactTypes.STRUCT,
63        TType.MAP     => TCompactTypes.MAP,
64        TType.SET     => TCompactTypes.SET,
65        TType.LIST    => TCompactTypes.LIST
66    ];
67
68    private static var tcompactTypeToType = [
69        TCompactTypes.STOP          => TType.STOP,
70        TCompactTypes.BOOLEAN_TRUE  => TType.BOOL,
71        TCompactTypes.BOOLEAN_FALSE => TType.BOOL,
72        TCompactTypes.BYTE          => TType.BYTE,
73        TCompactTypes.I16           => TType.I16,
74        TCompactTypes.I32           => TType.I32,
75        TCompactTypes.I64           => TType.I64,
76        TCompactTypes.DOUBLE        => TType.DOUBLE,
77        TCompactTypes.BINARY        => TType.STRING,
78        TCompactTypes.LIST          => TType.LIST,
79        TCompactTypes.SET           => TType.SET,
80        TCompactTypes.MAP           => TType.MAP,
81        TCompactTypes.STRUCT        => TType.STRUCT
82    ];
83
84
85    /**
86     * Used to keep track of the last field for the current and previous structs,
87     * so we can do the delta stuff.
88     */
89    private var lastField_ : GenericStack<Int> = new GenericStack<Int>();
90    private var lastFieldId_ : Int = 0;
91
92    /**
93     * If we encounter a boolean field begin, save the TField here so it can
94     * have the value incorporated.
95     */
96    private var booleanField_ : Null<TField>;
97
98    /**
99     * If we Read a field header, and it's a boolean field, save the boolean
100     * value here so that ReadBool can use it.
101     */
102    private var boolValue_ : Null<Bool>;
103
104
105    // whether the underlying system holds Strings as UTF-8
106    // http://old.haxe.org/manual/encoding
107    private static var utf8Strings = haxe.Utf8.validate("Ç-ß-Æ-Ю-Ш");
108
109    // the transport used
110    public var trans(default,null) : TTransport;
111
112
113    // TCompactProtocol Constructor
114    public function new( trans : TTransport) {
115        this.trans = trans;
116    }
117
118    public function getTransport() : TTransport {
119      return trans;
120    }
121
122
123    public function Reset() : Void{
124        while ( ! lastField_.isEmpty()) {
125            lastField_.pop();
126        }
127        lastFieldId_ = 0;
128    }
129
130
131    /**
132     * Writes a byte without any possibility of all that field header nonsense.
133     * Used internally by other writing methods that know they need to Write a byte.
134     */
135    private function WriteByteDirect( b : Int) : Void {
136        var buf = Bytes.alloc(1);
137        buf.set( 0, b);
138        trans.write( buf, 0, 1);
139    }
140
141    /**
142     * Write an i32 as a varint. Results in 1-5 bytes on the wire.
143     */
144    private function WriteVarint32( n : UInt) : Void {
145        var i32buf = new BytesBuffer();
146        while (true)
147        {
148            if ((n & ~0x7F) == 0)
149            {
150                i32buf.addByte( n & 0xFF);
151                break;
152            }
153            else
154            {
155                i32buf.addByte( (n & 0x7F) | 0x80);
156                n >>= 7;
157            }
158        }
159
160        var tmp = i32buf.getBytes();
161        trans.write( tmp, 0, tmp.length);
162    }
163
164    /**
165    * Write a message header to the wire. Compact Protocol messages contain the
166    * protocol version so we can migrate forwards in the future if need be.
167    */
168    public function writeMessageBegin( message : TMessage) : Void {
169        Reset();
170
171        var versionAndType : Int =  (VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK);
172        WriteByteDirect( PROTOCOL_ID);
173        WriteByteDirect( versionAndType);
174        WriteVarint32( cast( message.seqid, UInt));
175        writeString( message.name);
176    }
177
178    /**
179     * Write a struct begin. This doesn't actually put anything on the wire. We
180     * use it as an opportunity to put special placeholder markers on the field
181     * stack so we can get the field id deltas correct.
182     */
183    public function writeStructBegin(struct:TStruct) : Void {
184        lastField_.add( lastFieldId_);
185        lastFieldId_ = 0;
186    }
187
188    /**
189     * Write a struct end. This doesn't actually put anything on the wire. We use
190     * this as an opportunity to pop the last field from the current struct off
191     * of the field stack.
192     */
193    public function writeStructEnd() : Void {
194        lastFieldId_ = lastField_.pop();
195    }
196
197    /**
198     * Write a field header containing the field id and field type. If the
199     * difference between the current field id and the last one is small (< 15),
200     * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
201     * field id will follow the type header as a zigzag varint.
202     */
203    public function writeFieldBegin(field:TField) : Void {
204        if (field.type == TType.BOOL)
205            booleanField_ = field; // we want to possibly include the value, so we'll wait.
206        else
207            WriteFieldBeginInternal(field, 0xFF);
208    }
209
210    /**
211     * The workhorse of WriteFieldBegin. It has the option of doing a
212     * 'type override' of the type header. This is used specifically in the
213     * boolean field case.
214     */
215    private function WriteFieldBeginInternal( field : TField, typeOverride : Int) : Void {
216        // if there's a type override, use that.
217        var typeToWrite : Int;
218        if ( typeOverride == 0xFF)
219            typeToWrite = getCompactType( field.type);
220        else
221            typeToWrite = typeOverride;
222
223        // check if we can use delta encoding for the field id
224        if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15)
225        {
226            // Write them together
227            WriteByteDirect((field.id - lastFieldId_) << 4 | typeToWrite);
228        }
229        else
230        {
231            // Write them separate
232            WriteByteDirect(typeToWrite);
233            writeI16(field.id);
234        }
235
236        lastFieldId_ = field.id;
237    }
238
239    /**
240     * Write the STOP symbol so we know there are no more fields in this struct.
241     */
242    public function writeFieldStop() : Void {
243        WriteByteDirect( cast(TCompactTypes.STOP, Int));
244    }
245
246    /**
247     * Write a map header. If the map is empty, omit the key and value type
248     * headers, as we don't need any additional information to skip it.
249     */
250    public function writeMapBegin(map:TMap) : Void {
251        if (map.size == 0)
252        {
253            WriteByteDirect(0);
254        }
255        else
256        {
257            var kvtype = (getCompactType(map.keyType) << 4) | getCompactType(map.valueType);
258            WriteVarint32( cast( map.size, UInt));
259            WriteByteDirect( kvtype);
260        }
261    }
262
263    /**
264     * Write a list header.
265     */
266    public function writeListBegin( list : TList) : Void {
267        WriteCollectionBegin( list.elemType, list.size);
268    }
269
270    /**
271     * Write a set header.
272     */
273    public function writeSetBegin( set : TSet) : Void {
274        WriteCollectionBegin( set.elemType, set.size);
275    }
276
277    /**
278     * Write a boolean value. Potentially, this could be a boolean field, in
279     * which case the field header info isn't written yet. If so, decide what the
280     * right type header is for the value and then Write the field header.
281     * Otherwise, Write a single byte.
282     */
283    public function writeBool(b : Bool) : Void {
284        var bct : Int = b ? TCompactTypes.BOOLEAN_TRUE : TCompactTypes.BOOLEAN_FALSE;
285
286        if (booleanField_ != null)
287        {
288            // we haven't written the field header yet
289            WriteFieldBeginInternal( booleanField_, bct);
290            booleanField_ = null;
291        }
292        else
293        {
294            // we're not part of a field, so just Write the value.
295            WriteByteDirect( bct);
296        }
297    }
298
299    /**
300     * Write a byte. Nothing to see here!
301     */
302    public function writeByte( b : Int) : Void {
303        WriteByteDirect( b);
304    }
305
306    /**
307     * Write an I16 as a zigzag varint.
308     */
309    public function writeI16( i16 : Int) : Void {
310        WriteVarint32( ZigZag.FromInt( i16));
311    }
312
313    /**
314     * Write an i32 as a zigzag varint.
315     */
316    public function writeI32( i32 : Int) : Void {
317        WriteVarint32( ZigZag.FromInt( i32));
318    }
319
320    /**
321     * Write an i64 as a zigzag varint.
322     */
323    public function writeI64( i64 : haxe.Int64) : Void {
324        WriteVarint64(  ZigZag.FromLong( i64));
325    }
326
327    /**
328     * Write a double to the wire as 8 bytes.
329     */
330    public function writeDouble( dub : Float) : Void {
331        var data = BitConverter.fixedLongToBytes( BitConverter.DoubleToInt64Bits(dub));
332        trans.write( data, 0, data.length);
333    }
334
335    /**
336     * Write a string to the wire with a varint size preceding.
337     */
338    public function writeString(str : String) : Void {
339        var buf = new BytesBuffer();
340        if( utf8Strings)
341            buf.addString( str);  // no need to encode on UTF8 targets, the string is just fine
342        else
343            buf.addString( Utf8.encode( str));
344        var tmp = buf.getBytes();
345        writeBinary( tmp);
346    }
347
348    /**
349     * Write a byte array, using a varint for the size.
350     */
351    public function writeBinary( bin : Bytes) : Void {
352        WriteVarint32( cast(bin.length,UInt));
353        trans.write( bin, 0, bin.length);
354    }
355
356
357    // These methods are called by structs, but don't actually have any wire
358    // output or purpose.
359    public function writeMessageEnd() : Void { }
360    public function writeMapEnd() : Void { }
361    public function writeListEnd() : Void { }
362    public function writeSetEnd() : Void { }
363    public function writeFieldEnd() : Void { }
364
365    //
366    // Internal writing methods
367    //
368
369    /**
370     * Abstract method for writing the start of lists and sets. List and sets on
371     * the wire differ only by the type indicator.
372     */
373    private function WriteCollectionBegin( elemType : Int, size : Int) : Void {
374        if (size <= 14)    {
375            WriteByteDirect( size << 4 | getCompactType(elemType));
376        }
377        else {
378            WriteByteDirect( 0xf0 | getCompactType(elemType));
379            WriteVarint32( cast(size, UInt));
380        }
381    }
382
383    /**
384     * Write an i64 as a varint. Results in 1-10 bytes on the wire.
385     */
386    private function WriteVarint64(n : haxe.Int64) : Void    {
387        var varint64out = new BytesBuffer();
388        while (true)
389        {
390            if( Int64.isZero( Int64.and( n, Int64.neg(Int64.make(0,0x7F)))))
391            {
392                #if( haxe_ver < 3.2)
393                varint64out.addByte( Int64.getLow(n));
394                #else
395                varint64out.addByte( n.low);
396                #end
397                break;
398            }
399            else
400            {
401                #if ( haxe_ver < 3.2)
402                varint64out.addByte( (Int64.getLow(n) & 0x7F) | 0x80);
403                #else
404                varint64out.addByte( (n.low & 0x7F) | 0x80);
405                #end
406                n = Int64.shr( n, 7);
407                n = Int64.and( n, Int64.make(0x01FFFFFF,0xFFFFFFFF));  // clean out the shifted 7 bits
408            }
409        }
410        var tmp = varint64out.getBytes();
411        trans.write( tmp, 0, tmp.length);
412    }
413
414
415    /**
416     * Read a message header.
417     */
418    public function readMessageBegin():TMessage {
419        Reset();
420
421        var protocolId : Int = readByte();
422        if (protocolId != PROTOCOL_ID) {
423            throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected protocol id " + StringTools.hex(PROTOCOL_ID,2) + " but got " + StringTools.hex(protocolId));
424        }
425
426        var versionAndType : Int = readByte();
427        var version : Int = (versionAndType & VERSION_MASK);
428        if (version != VERSION) {
429            throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected version " + VERSION + " but got " + version);
430        }
431
432        var type : Int = ((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
433        var seqid : Int = cast( ReadVarint32(), Int);
434        var msgNm : String = readString();
435        return new TMessage( msgNm, type, seqid);
436    }
437
438    /**
439     * Read a struct begin. There's nothing on the wire for this, but it is our
440     * opportunity to push a new struct begin marker onto the field stack.
441     */
442    public function readStructBegin():TStruct {
443        lastField_.add(lastFieldId_);
444        lastFieldId_ = 0;
445        return ANONYMOUS_STRUCT;
446    }
447
448    /**
449     * Doesn't actually consume any wire data, just removes the last field for
450     * this struct from the field stack.
451     */
452    public function readStructEnd() : Void {
453        // consume the last field we Read off the wire.
454        lastFieldId_ = lastField_.pop();
455    }
456
457    /**
458     * Read a field header off the wire.
459     */
460    public function readFieldBegin() : TField {
461        var type : Int = readByte();
462
463        // if it's a stop, then we can return immediately, as the struct is over.
464        if (type == cast(TCompactTypes.STOP,Int)) {
465            return TSTOP;
466        }
467
468        var fieldId : Int;
469
470        // mask off the 4 MSB of the type header. it could contain a field id delta.
471        var modifier : Int = ((type & 0xf0) >> 4);
472        if (modifier == 0)
473            fieldId = readI16();  // not a delta. look ahead for the zigzag varint field id.
474        else
475            fieldId = lastFieldId_ + modifier; // add the delta to the last Read field id.
476
477        var field : TField  = new TField( "", cast(getTType(type & 0x0f),Int), fieldId);
478
479        // if this happens to be a boolean field, the value is encoded in the type
480        if (isBoolType(type)) {
481            // save the boolean value in a special instance variable.
482            boolValue_ = ((type & 0x0f) == cast(TCompactTypes.BOOLEAN_TRUE,Int));
483        }
484
485        // push the new field onto the field stack so we can keep the deltas going.
486        lastFieldId_ = field.id;
487        return field;
488    }
489
490    /**
491     * Read a map header off the wire. If the size is zero, skip Reading the key
492     * and value type. This means that 0-length maps will yield TMaps without the
493     * "correct" types.
494     */
495    public function readMapBegin() : TMap {
496        var size : Int = cast( ReadVarint32(), Int);
497        var keyAndValueType : Int = ((size == 0)  ?  0  :  readByte());
498        var key : Int = cast( getTType( (keyAndValueType & 0xF0) >> 4), Int);
499        var val : Int = cast( getTType( keyAndValueType & 0x0F), Int);
500        return new TMap( key, val, size);
501    }
502
503    /**
504     * Read a list header off the wire. If the list size is 0-14, the size will
505     * be packed into the element type header. If it's a longer list, the 4 MSB
506     * of the element type header will be 0xF, and a varint will follow with the
507     * true size.
508     */
509    public function readListBegin():TList {
510        var size_and_type : Int = readByte();
511
512        var size : Int = ((size_and_type & 0xF0) >> 4) & 0x0F;
513        if (size == 15) {
514            size = cast( ReadVarint32(), Int);
515        }
516
517        var type = getTType(size_and_type);
518        return new TList( type, size);
519    }
520
521    /**
522     * Read a set header off the wire. If the set size is 0-14, the size will
523     * be packed into the element type header. If it's a longer set, the 4 MSB
524     * of the element type header will be 0xF, and a varint will follow with the
525     * true size.
526     */
527    public function readSetBegin() : TSet {
528        var size_and_type : Int = readByte();
529
530        var size : Int = ((size_and_type & 0xF0) >> 4) & 0x0F;
531        if (size == 15) {
532            size = cast( ReadVarint32(), Int);
533        }
534
535        var type = getTType(size_and_type);
536        return new TSet( type, size);
537    }
538
539    /**
540     * Read a boolean off the wire. If this is a boolean field, the value should
541     * already have been Read during ReadFieldBegin, so we'll just consume the
542     * pre-stored value. Otherwise, Read a byte.
543     */
544    public function readBool() : Bool {
545        if (boolValue_ != null) {
546            var result : Bool = boolValue_;
547            boolValue_ = null;
548            return result;
549        }
550
551        return (readByte() == cast(TCompactTypes.BOOLEAN_TRUE,Int));
552    }
553
554    /**
555     * Read a single byte off the wire. Nothing interesting here.
556     */
557    public function readByte() : Int {
558        var byteRawBuf = new BytesBuffer();
559        trans.readAll( byteRawBuf, 0, 1);
560        return byteRawBuf.getBytes().get(0);
561    }
562
563    /**
564     * Read an i16 from the wire as a zigzag varint.
565     */
566    public function readI16() : Int {
567        return ZigZag.ToInt( ReadVarint32());
568    }
569
570    /**
571     * Read an i32 from the wire as a zigzag varint.
572     */
573    public function readI32() : Int {
574        return ZigZag.ToInt( ReadVarint32());
575    }
576
577    /**
578     * Read an i64 from the wire as a zigzag varint.
579     */
580    public function readI64() : haxe.Int64 {
581        return ZigZag.ToLong( ReadVarint64());
582    }
583
584    /**
585     * No magic here - just Read a double off the wire.
586     */
587    public function readDouble():Float {
588        var longBits = new BytesBuffer();
589        trans.readAll( longBits, 0, 8);
590        return BitConverter.Int64BitsToDouble( BitConverter.bytesToLong( longBits.getBytes()));
591    }
592
593    /**
594     * Reads a byte[] (via ReadBinary), and then UTF-8 decodes it.
595     */
596    public function readString() : String {
597        var length : Int = cast( ReadVarint32(), Int);
598
599        if (length == 0) {
600            return "";
601        }
602
603        var buf = new BytesBuffer();
604        trans.readAll( buf, 0, length);
605
606        length = buf.length;
607        var inp = new BytesInput( buf.getBytes());
608        var str = inp.readString( length);
609        if( utf8Strings)
610            return str;  // no need to decode on UTF8 targets, the string is just fine
611        else
612            return Utf8.decode( str);
613    }
614
615    /**
616     * Read a byte[] from the wire.
617     */
618    public function readBinary() : Bytes {
619        var length : Int = cast( ReadVarint32(), Int);
620        if (length == 0) {
621            return Bytes.alloc(0);
622        }
623
624        var buf = new BytesBuffer();
625        trans.readAll( buf, 0, length);
626        return buf.getBytes();
627    }
628
629
630    // These methods are here for the struct to call, but don't have any wire
631    // encoding.
632    public function readMessageEnd() : Void { }
633    public function readFieldEnd() : Void { }
634    public function readMapEnd() : Void { }
635    public function readListEnd() : Void { }
636    public function readSetEnd() : Void { }
637
638    //
639    // Internal Reading methods
640    //
641
642    /**
643     * Read an i32 from the wire as a varint. The MSB of each byte is set
644     * if there is another byte to follow. This can Read up to 5 bytes.
645     */
646    private function ReadVarint32() : UInt {
647        var result : UInt = 0;
648        var shift : Int = 0;
649        while (true) {
650            var b : Int = readByte();
651            result |= cast((b & 0x7f) << shift, UInt);
652            if ((b & 0x80) != 0x80) {
653                break;
654            }
655            shift += 7;
656        }
657        return result;
658    }
659
660    /**
661     * Read an i64 from the wire as a proper varint. The MSB of each byte is set
662     * if there is another byte to follow. This can Read up to 10 bytes.
663     */
664    private function ReadVarint64() : Int64 {
665        var shift : Int = 0;
666        var result : Int64 = Int64.make(0,0);
667        while (true) {
668            var b : Int = readByte();
669            result = Int64.or( result, Int64.shl( Int64.make(0,b & 0x7f), shift));
670            if ((b & 0x80) != 0x80) {
671                break;
672            }
673            shift += 7;
674        }
675
676        return result;
677    }
678
679
680    //
681    // type testing and converting
682    //
683
684    private function isBoolType( b : Int) : Bool {
685        var lowerNibble : Int = b & 0x0f;
686        switch(lowerNibble)
687        {
688            case TCompactTypes.BOOLEAN_TRUE: return true;
689            case TCompactTypes.BOOLEAN_FALSE: return true;
690            default: return false;
691        }
692    }
693
694
695    /**
696     * Given a TCompactProtocol.TCompactTypes constant, convert it to its corresponding
697     * TType value.
698     */
699    private function getTType( type : Int) : Int {
700        try
701        {
702            return tcompactTypeToType[type];
703        }
704        catch ( e : Dynamic)
705        {
706            var tt : Int = (type & 0x0f);
707            throw new TProtocolException( TProtocolException.UNKNOWN, 'don\'t know what type: $tt ($e)');
708        }
709    }
710
711    /**
712     * Given a TType value, find the appropriate TCompactProtocol.TCompactTypes constant.
713     */
714    private function getCompactType( ttype : Int) : Int
715    {
716        return cast( ttypeToCompactType[ttype], Int);
717    }
718}
719