PageRenderTime 714ms CodeModel.GetById 168ms app.highlight 239ms RepoModel.GetById 133ms app.codeStats 1ms

/lib/php/src/protocol/TCompactProtocol.php

http://github.com/apache/thrift
PHP | 678 lines | 537 code | 95 blank | 46 comment | 81 complexity | 1e2df5b05521094ad57a7c241355f598 MD5 | raw file
  1<?php
  2/*
  3 * Licensed to the Apache Software Foundation (ASF) under one
  4 * or more contributor license agreements. See the NOTICE file
  5 * distributed with this work for additional information
  6 * regarding copyright ownership. The ASF licenses this file
  7 * to you under the Apache License, Version 2.0 (the
  8 * "License"); you may not use this file except in compliance
  9 * with the License. You may obtain a copy of the License at
 10 *
 11 *   http://www.apache.org/licenses/LICENSE-2.0
 12 *
 13 * Unless required by applicable law or agreed to in writing,
 14 * software distributed under the License is distributed on an
 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 16 * KIND, either express or implied. See the License for the
 17 * specific language governing permissions and limitations
 18 * under the License.
 19 *
 20 * @package thrift.protocol
 21 */
 22
 23include_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
 24
 25/**
 26 * Compact implementation of the Thrift protocol.
 27 *
 28 */
 29class TCompactProtocol extends TProtocol {
 30
 31  const COMPACT_STOP = 0x00;
 32  const COMPACT_TRUE = 0x01;
 33  const COMPACT_FALSE = 0x02;
 34  const COMPACT_BYTE = 0x03;
 35  const COMPACT_I16 = 0x04;
 36  const COMPACT_I32 = 0x05;
 37  const COMPACT_I64 = 0x06;
 38  const COMPACT_DOUBLE = 0x07;
 39  const COMPACT_BINARY = 0x08;
 40  const COMPACT_LIST = 0x09;
 41  const COMPACT_SET = 0x0A;
 42  const COMPACT_MAP = 0x0B;
 43  const COMPACT_STRUCT = 0x0C;
 44
 45  const STATE_CLEAR = 0;
 46  const STATE_FIELD_WRITE = 1;
 47  const STATE_VALUE_WRITE = 2;
 48  const STATE_CONTAINER_WRITE = 3;
 49  const STATE_BOOL_WRITE = 4;
 50  const STATE_FIELD_READ = 5;
 51  const STATE_CONTAINER_READ = 6;
 52  const STATE_VALUE_READ = 7;
 53  const STATE_BOOL_READ = 8;
 54
 55  const VERSION_MASK = 0x1f;
 56  const VERSION = 1;
 57  const PROTOCOL_ID = 0x82;
 58  const TYPE_MASK = 0xe0;
 59  const TYPE_SHIFT_AMOUNT = 5;
 60
 61  protected static $ctypes = array(
 62    TType::STOP => TCompactProtocol::COMPACT_STOP,
 63    TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection
 64    TType::BYTE => TCompactProtocol::COMPACT_BYTE,
 65    TType::I16 => TCompactProtocol::COMPACT_I16,
 66    TType::I32 => TCompactProtocol::COMPACT_I32,
 67    TType::I64 => TCompactProtocol::COMPACT_I64,
 68    TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE,
 69    TType::STRING => TCompactProtocol::COMPACT_BINARY,
 70    TType::STRUCT => TCompactProtocol::COMPACT_STRUCT,
 71    TType::LST => TCompactProtocol::COMPACT_LIST,
 72    TType::SET => TCompactProtocol::COMPACT_SET,
 73    TType::MAP => TCompactProtocol::COMPACT_MAP,
 74  );
 75
 76  protected static $ttypes = array(
 77    TCompactProtocol::COMPACT_STOP => TType::STOP ,
 78    TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection
 79    TCompactProtocol::COMPACT_FALSE => TType::BOOL,
 80    TCompactProtocol::COMPACT_BYTE => TType::BYTE,
 81    TCompactProtocol::COMPACT_I16 => TType::I16,
 82    TCompactProtocol::COMPACT_I32 => TType::I32,
 83    TCompactProtocol::COMPACT_I64 => TType::I64,
 84    TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE,
 85    TCompactProtocol::COMPACT_BINARY => TType::STRING,
 86    TCompactProtocol::COMPACT_STRUCT => TType::STRUCT,
 87    TCompactProtocol::COMPACT_LIST => TType::LST,
 88    TCompactProtocol::COMPACT_SET => TType::SET,
 89    TCompactProtocol::COMPACT_MAP => TType::MAP,
 90  );
 91
 92  protected $state = TCompactProtocol::STATE_CLEAR;
 93  protected $lastFid = 0;
 94  protected $boolFid = null;
 95  protected $boolValue = null;
 96  protected $structs = array();
 97  protected $containers = array();
 98
 99  // Some varint / zigzag helper methods
100  public function toZigZag($n, $bits) {
101    return ($n << 1) ^ ($n >> ($bits - 1));
102  }
103
104  public function fromZigZag($n) {
105    return ($n >> 1) ^ -($n & 1);
106  }
107
108  public function getVarint($data) {
109    $out = "";
110    while (true) {
111      if (($data & ~0x7f) === 0) {
112        $out .= chr($data);
113        break;
114      } else {
115        $out .= chr(($data & 0xff) | 0x80);
116        $data = $data >> 7;
117      }
118    }
119    return $out;
120  }
121
122  public function writeVarint($data) {
123    $out = $this->getVarint($data);
124    $result = TStringFuncFactory::create()->strlen($out);
125    $this->trans_->write($out, $result);
126    return $result;
127  }
128
129  public function readVarint(&$result) {
130    $idx = 0;
131    $shift = 0;
132    $result = 0;
133    while (true) {
134      $x = $this->trans_->readAll(1);
135      $arr = unpack('C', $x);
136      $byte = $arr[1];
137      $idx += 1;
138      $result |= ($byte & 0x7f) << $shift;
139      if (($byte >> 7) === 0) {
140        return $idx;
141      }
142      $shift += 7;
143    }
144
145    return $idx;
146  }
147
148  public function __construct($trans) {
149    parent::__construct($trans);
150  }
151
152  public function writeMessageBegin($name, $type, $seqid) {
153    $written =
154      $this->writeUByte(TCompactProtocol::PROTOCOL_ID) +
155      $this->writeUByte(TCompactProtocol::VERSION |
156                        ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) +
157      $this->writeVarint($seqid) +
158      $this->writeString($name);
159    $this->state = TCompactProtocol::STATE_VALUE_WRITE;
160    return $written;
161  }
162
163  public function writeMessageEnd() {
164    $this->state = TCompactProtocol::STATE_CLEAR;
165    return 0;
166  }
167
168  public function writeStructBegin($name) {
169    $this->structs[] = array($this->state, $this->lastFid);
170    $this->state = TCompactProtocol::STATE_FIELD_WRITE;
171    $this->lastFid = 0;
172    return 0;
173  }
174
175  public function writeStructEnd() {
176    $old_values = array_pop($this->structs);
177    $this->state = $old_values[0];
178    $this->lastFid = $old_values[1];
179    return 0;
180  }
181
182  public function writeFieldStop() {
183    return $this->writeByte(0);
184  }
185
186  public function writeFieldHeader($type, $fid) {
187    $written = 0;
188    $delta = $fid - $this->lastFid;
189    if (0 < $delta && $delta <= 15) {
190      $written = $this->writeUByte(($delta << 4) | $type);
191    } else {
192      $written = $this->writeByte($type) +
193        $this->writeI16($fid);
194    }
195    $this->lastFid = $fid;
196    return $written;
197  }
198
199  public function writeFieldBegin($field_name, $field_type, $field_id) {
200    if ($field_type == TTYPE::BOOL) {
201      $this->state = TCompactProtocol::STATE_BOOL_WRITE;
202      $this->boolFid = $field_id;
203      return 0;
204    } else {
205      $this->state = TCompactProtocol::STATE_VALUE_WRITE;
206      return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id);
207    }
208  }
209
210  public function writeFieldEnd() {
211    $this->state = TCompactProtocol::STATE_FIELD_WRITE;
212    return 0;
213  }
214
215  public function writeCollectionBegin($etype, $size) {
216    $written = 0;
217    if ($size <= 14) {
218      $written = $this->writeUByte($size << 4 |
219                                    self::$ctypes[$etype]);
220    } else {
221      $written = $this->writeUByte(0xf0 |
222                                   self::$ctypes[$etype]) +
223        $this->writeVarint($size);
224    }
225    $this->containers[] = $this->state;
226    $this->state = TCompactProtocol::STATE_CONTAINER_WRITE;
227
228    return $written;
229  }
230
231  public function writeMapBegin($key_type, $val_type, $size) {
232    $written = 0;
233    if ($size == 0) {
234      $written = $this->writeByte(0);
235    } else {
236      $written = $this->writeVarint($size) +
237        $this->writeUByte(self::$ctypes[$key_type] << 4 |
238                          self::$ctypes[$val_type]);
239    }
240    $this->containers[] = $this->state;
241    return $written;
242  }
243
244  public function writeCollectionEnd() {
245    $this->state = array_pop($this->containers);
246    return 0;
247  }
248
249  public function writeMapEnd() {
250    return $this->writeCollectionEnd();
251  }
252
253  public function writeListBegin($elem_type, $size) {
254    return $this->writeCollectionBegin($elem_type, $size);
255  }
256
257  public function writeListEnd() {
258    return $this->writeCollectionEnd();
259  }
260
261  public function writeSetBegin($elem_type, $size) {
262    return $this->writeCollectionBegin($elem_type, $size);
263  }
264
265  public function writeSetEnd() {
266    return $this->writeCollectionEnd();
267  }
268
269  public function writeBool($value) {
270    if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) {
271      $ctype = TCompactProtocol::COMPACT_FALSE;
272      if ($value) {
273        $ctype = TCompactProtocol::COMPACT_TRUE;
274      }
275      return $this->writeFieldHeader($ctype, $this->boolFid);
276    } else if ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) {
277      return $this->writeByte($value ? 1 : 0);
278    } else {
279      throw new TProtocolException('Invalid state in compact protocol');
280    }
281  }
282
283  public function writeByte($value) {
284    $data = pack('c', $value);
285    $this->trans_->write($data, 1);
286    return 1;
287  }
288
289  public function writeUByte($byte) {
290    $this->trans_->write(pack('C', $byte), 1);
291    return 1;
292  }
293
294  public function writeI16($value) {
295    $thing = $this->toZigZag($value, 16);
296    return $this->writeVarint($thing);
297  }
298
299  public function writeI32($value) {
300    $thing = $this->toZigZag($value, 32);
301    return $this->writeVarint($thing);
302  }
303
304  public function writeDouble($value) {
305    $data = pack('d', $value);
306    $this->trans_->write(strrev($data), 8);
307    return 8;
308  }
309
310  public function writeString($value) {
311    $len = TStringFuncFactory::create()->strlen($value);
312    $result = $this->writeVarint($len);
313    if ($len) {
314      $this->trans_->write($value, $len);
315    }
316    return $result + $len;
317  }
318
319  public function readFieldBegin(&$name, &$field_type, &$field_id) {
320    $result = $this->readUByte($field_type);
321
322    if (($field_type & 0x0f) == TType::STOP) {
323      $field_id = 0;
324      return $result;
325    }
326    $delta = $field_type >> 4;
327    if ($delta == 0) {
328      $result += $this->readI16($field_id);
329    } else {
330      $field_id = $this->lastFid + $delta;
331    }
332    $this->lastFid = $field_id;
333    $field_type = $this->getTType($field_type & 0x0f);
334    if ($field_type == TCompactProtocol::COMPACT_TRUE) {
335      $this->state = TCompactProtocol::STATE_BOOL_READ;
336      $this->boolValue = true;
337    } else if ($field_type == TCompactProtocol::COMPACT_FALSE) {
338      $this->state = TCompactProtocol::STATE_BOOL_READ;
339      $this->boolValue = false;
340    } else {
341      $this->state = TCompactProtocol::STATE_VALUE_READ;
342    }
343    return $result;
344  }
345
346  public function readFieldEnd() {
347    $this->state = TCompactProtocol::STATE_FIELD_READ;
348    return 0;
349  }
350
351  public function readUByte(&$value) {
352    $data = $this->trans_->readAll(1);
353    $arr = unpack('C', $data);
354    $value = $arr[1];
355    return 1;
356  }
357
358  public function readByte(&$value) {
359    $data = $this->trans_->readAll(1);
360    $arr = unpack('c', $data);
361    $value = $arr[1];
362    return 1;
363  }
364
365  public function readZigZag(&$value) {
366    $result = $this->readVarint($value);
367    $value = $this->fromZigZag($value);
368    return $result;
369  }
370
371  public function readMessageBegin(&$name, &$type, &$seqid) {
372    $protoId = 0;
373    $result = $this->readUByte($protoId);
374    if ($protoId != TCompactProtocol::PROTOCOL_ID) {
375      throw new TProtocolException('Bad protocol id in TCompact message');
376    }
377    $verType = 0;
378    $result += $this->readUByte($verType);
379    $type = ($verType & TCompactProtocol::TYPE_MASK) >>
380      TCompactProtocol::TYPE_SHIFT_AMOUNT;
381    $version = $verType & TCompactProtocol::VERSION_MASK;
382    if ($version != TCompactProtocol::VERSION) {
383      throw new TProtocolException('Bad version in TCompact message');
384    }
385    $result += $this->readVarint($seqId);
386    $name += $this->readString($name);
387
388    return $result;
389  }
390
391  public function readMessageEnd() {
392    return 0;
393  }
394
395  public function readStructBegin(&$name) {
396    $name = ''; // unused
397    $this->structs[] = array($this->state, $this->lastFid);
398    $this->state = TCompactProtocol::STATE_FIELD_READ;
399    $this->lastFid = 0;
400    return 0;
401  }
402
403  public function readStructEnd() {
404    $last = array_pop($this->structs);
405    $this->state = $last[0];
406    $this->lastFid = $last[1];
407    return 0;
408  }
409
410  public function readCollectionBegin(&$type, &$size) {
411    $sizeType = 0;
412    $result = $this->readUByte($sizeType);
413    $size = $sizeType >> 4;
414    $type = $this->getTType($sizeType);
415    if ($size == 15) {
416      $result += $this->readVarint($size);
417    }
418    $this->containers[] = $this->state;
419    $this->state = TCompactProtocol::STATE_CONTAINER_READ;
420
421    return $result;
422  }
423
424  public function readMapBegin(&$key_type, &$val_type, &$size) {
425    $result = $this->readVarint($size);
426    $types = 0;
427    if ($size > 0) {
428      $result += $this->readUByte($types);
429    }
430    $val_type = $this->getTType($types);
431    $key_type = $this->getTType($types >> 4);
432    $this->containers[] = $this->state;
433    $this->state = TCompactProtocol::STATE_CONTAINER_READ;
434
435    return $result;
436  }
437
438  public function readCollectionEnd() {
439    $this->state = array_pop($this->containers);
440    return 0;
441  }
442
443  public function readMapEnd() {
444    return $this->readCollectionEnd();
445  }
446
447  public function readListBegin(&$elem_type, &$size) {
448    return $this->readCollectionBegin($elem_type, $size);
449  }
450
451  public function readListEnd() {
452    return $this->readCollectionEnd();
453  }
454
455  public function readSetBegin(&$elem_type, &$size) {
456    return $this->readCollectionBegin($elem_type, $size);
457  }
458
459  public function readSetEnd() {
460    return $this->readCollectionEnd();
461  }
462
463  public function readBool(&$value) {
464    if ($this->state == TCompactProtocol::STATE_BOOL_READ) {
465      $value = $this->boolValue;
466      return 0;
467    } else if ($this->state == TCompactProtocol::STATE_CONTAINER_READ) {
468      return $this->readByte($value);
469    } else {
470      throw new TProtocolException('Invalid state in compact protocol');
471    }
472  }
473
474  public function readI16(&$value) {
475    return $this->readZigZag($value);
476  }
477
478  public function readI32(&$value) {
479    return $this->readZigZag($value);
480  }
481
482  public function readDouble(&$value) {
483    $data = strrev($this->trans_->readAll(8));
484    $arr = unpack('d', $data);
485    $value = $arr[1];
486    return 8;
487  }
488
489  public function readString(&$value) {
490    $result = $this->readVarint($len);
491    if ($len) {
492      $value = $this->trans_->readAll($len);
493    } else {
494      $value = '';
495    }
496    return $result + $len;
497  }
498
499  public function getTType($byte) {
500    return self::$ttypes[$byte & 0x0f];
501  }
502
503  // If we are on a 32bit architecture we have to explicitly deal with
504  // 64-bit twos-complement arithmetic since PHP wants to treat all ints
505  // as signed and any int over 2^31 - 1 as a float
506
507  // Read and write I64 as two 32 bit numbers $hi and $lo
508
509  public function readI64(&$value) {
510    // Read varint from wire
511    $hi = 0;
512    $lo = 0;
513
514    $idx = 0;
515    $shift = 0;
516
517    while (true) {
518      $x = $this->trans_->readAll(1);
519      $arr = unpack('C', $x);
520      $byte = $arr[1];
521      $idx += 1;
522      if ($shift < 32) {
523        $lo |= (($byte & 0x7f) << $shift) &
524          0x00000000ffffffff;
525      }
526      // Shift hi and lo together.
527      if ($shift >= 32) {
528        $hi |= (($byte & 0x7f) << ($shift - 32));
529      } else if ($shift > 25) {
530        $hi |= (($byte & 0x7f) >> ($shift - 25));
531      }
532      if (($byte >> 7) === 0) {
533        break;
534      }
535      $shift += 7;
536    }
537
538    // Now, unzig it.
539    $xorer = 0;
540    if ($lo & 1) {
541      $xorer = 0xffffffff;
542    }
543    $lo = ($lo >> 1) & 0x7fffffff;
544    $lo = $lo | (($hi & 1) << 31);
545    $hi = ($hi >> 1) ^ $xorer;
546    $lo = $lo ^ $xorer;
547
548    // Now put $hi and $lo back together
549    if (true) {
550      $isNeg = $hi  < 0;
551
552      // Check for a negative
553      if ($isNeg) {
554        $hi = ~$hi & (int)0xffffffff;
555        $lo = ~$lo & (int)0xffffffff;
556
557        if ($lo == (int)0xffffffff) {
558          $hi++;
559          $lo = 0;
560        } else {
561          $lo++;
562        }
563      }
564
565      // Force 32bit words in excess of 2G to be positive - we deal with sign
566      // explicitly below
567
568      if ($hi & (int)0x80000000) {
569        $hi &= (int)0x7fffffff;
570        $hi += 0x80000000;
571      }
572
573      if ($lo & (int)0x80000000) {
574        $lo &= (int)0x7fffffff;
575        $lo += 0x80000000;
576      }
577
578      $value = $hi * 4294967296 + $lo;
579
580      if ($isNeg) {
581        $value = 0 - $value;
582      }
583    } else {
584
585      // Upcast negatives in LSB bit
586      if ($arr[2] & 0x80000000) {
587        $arr[2] = $arr[2] & 0xffffffff;
588      }
589
590      // Check for a negative
591      if ($arr[1] & 0x80000000) {
592        $arr[1] = $arr[1] & 0xffffffff;
593        $arr[1] = $arr[1] ^ 0xffffffff;
594        $arr[2] = $arr[2] ^ 0xffffffff;
595        $value = 0 - $arr[1] * 4294967296 - $arr[2] - 1;
596      } else {
597        $value = $arr[1] * 4294967296 + $arr[2];
598      }
599    }
600
601    return $idx;
602  }
603
604  public function writeI64($value) {
605    // If we are in an I32 range, use the easy method below.
606    if (($value > 4294967296) || ($value < -4294967296)) {
607      // Convert $value to $hi and $lo
608      $neg = $value < 0;
609
610      if ($neg) {
611        $value *= -1;
612      }
613
614      $hi = (int)$value >> 32;
615      $lo = (int)$value & 0xffffffff;
616
617      if ($neg) {
618        $hi = ~$hi;
619        $lo = ~$lo;
620        if (($lo & (int)0xffffffff) == (int)0xffffffff) {
621          $lo = 0;
622          $hi++;
623        } else {
624          $lo++;
625        }
626      }
627
628      // Now do the zigging and zagging.
629      $xorer = 0;
630      if ($neg) {
631        $xorer = 0xffffffff;
632      }
633      $lowbit = ($lo >> 31) & 1;
634      $hi = ($hi << 1) | $lowbit;
635      $lo = ($lo << 1);
636      $lo = ($lo ^ $xorer) & 0xffffffff;
637      $hi = ($hi ^ $xorer) & 0xffffffff;
638
639      // now write out the varint, ensuring we shift both hi and lo
640      $out = "";
641      while (true) {
642        if (($lo & ~0x7f) === 0 &&
643           $hi === 0) {
644          $out .= chr($lo);
645          break;
646        } else {
647          $out .= chr(($lo & 0xff) | 0x80);
648          $lo = $lo >> 7;
649          $lo = $lo | ($hi << 25);
650          $hi = $hi >> 7;
651          // Right shift carries sign, but we don't want it to.
652          $hi = $hi & (127 << 25);
653        }
654      }
655
656      $ret = TStringFuncFactory::create()->strlen($out);
657      $this->trans_->write($out, $ret);
658
659      return $ret;
660    } else {
661      return $this->writeVarint($this->toZigZag($value, 64));
662    }
663  }
664}
665
666/**
667 * Compact Protocol Factory
668 */
669class TCompcatProtocolFactory implements TProtocolFactory {
670
671  public function __construct() {
672  }
673
674  public function getProtocol($trans) {
675    return new TCompactProtocol($trans);
676  }
677}
678