private function deserializeStringTable()
 {
     $typeNameCount = $this->readInt();
     $buffer = new SSSR_BoundedList(String::clazz(), $typeNameCount);
     for ($typeNameIndex = 0; $typeNameIndex < $typeNameCount; ++$typeNameIndex) {
         $str = $this->extract();
         // Change quoted characters back.
         $idx = mb_strpos($str, '\\');
         if ($idx !== false) {
             $buf = '';
             $pos = 0;
             while ($idx >= 0) {
                 $buf .= mb_substr($str, $pos, $idx - $pos);
                 if (++$idx == mb_strlen($str)) {
                     throw new SerializationException("Unmatched backslash: \"{$str}\"");
                 }
                 $ch = mb_substr($str, $idx, 1);
                 $pos = $idx + 1;
                 switch (Character::ord($ch)) {
                     case Character::ord('0'):
                         $buf .= '\\u0000';
                         break;
                     case Character::ord('!'):
                         $buf .= self::RPC_SEPARATOR_CHAR;
                         break;
                     case Character::ord('\\'):
                         $buf .= $ch;
                         break;
                     case Character::ord('u'):
                         try {
                             $ch = Character::chr(Integer::parseHex(mb_substr($str, $idx + 1, 4)));
                         } catch (NumberFormatException $e) {
                             throw new SerializationException("Invalid Unicode escape sequence \"{$str}\"");
                         }
                         $buf .= $ch;
                         $pos += 4;
                         break;
                     default:
                         throw new SerializationException("Unexpected escape character {$ch} after backslash: \"{$str}\"");
                 }
                 $idx = mb_strpos($str, '\\', $pos);
             }
             $buf .= mb_substr($str, $pos);
             $str = buf;
         }
         $buffer->add($str);
     }
     if ($buffer->size() != $buffer->getExpectedSize()) {
         throw new SerializationException('Expected ' . $buffer->getExpectedSize() . ' string table elements; received ' . $buffer->size());
     }
     $this->stringTable = $buffer->toArray();
 }
 private function decodeCommand()
 {
     $command = $this->next();
     if ($command == NL_CHAR) {
         $command = $this->next();
     }
     $token = $this->token();
     switch ($command) {
         case BOOLEAN_TYPE:
             $this->pushScalar(new BooleanValueCommand($token == '1'));
             break;
         case BYTE_TYPE:
             $this->pushScalar(new ByteValueCommand(Byte::valueOf($token)));
             break;
         case CHAR_TYPE:
             $this->pushScalar(new CharValueCommand(Character::chr(intval($token))));
             break;
         case DOUBLE_TYPE:
             $this->pushScalar(new DoubleValueCommand(Double::valueOf($token)));
             break;
         case FLOAT_TYPE:
             $this->pushScalar(new FloatValueCommand(Float::valueOf($token)));
             break;
         case INT_TYPE:
             $this->pushScalar(new IntValueCommand(Integer::valueOf($token)));
             break;
         case LONG_TYPE:
             $this->pushScalar(new LongValueCommand(Long::valueOf($token)));
             break;
         case VOID_TYPE:
             $this->pushScalar(NullValueCommand::INSTANCE());
             break;
         case SHORT_TYPE:
             $this->pushScalar(new ShortValueCommand(Short::valueOf($token)));
             break;
         case STRING_TYPE:
             // "4~abcd
             $length = Integer::valueOf($token);
             $value = $this->nextCount($length);
             if ($this->next() != RPC_SEPARATOR_CHAR) {
                 throw new RuntimeException('Overran string');
             }
             $this->pushString(new StringValueCommand($value));
             break;
         case ENUM_TYPE:
             // ETypeSeedName~IOrdinal~
             $ordinal = $this->readCommand('IntValueCommand')->getValue();
             $clazz = $this->findClass($token);
             $x = new EnumValueCommand($clazz);
             $this->pushIdentity($x);
             $x->setValue($ordinal);
             break;
         case ARRAY_TYPE:
             // Encoded as (leafType, dimensions, length, ...)
             $leaf = $this->findClass($token);
             $numDims = $this->readCommand('IntValueCommand')->getValue();
             $clazz = null;
             if ($numDims > 1) {
                 $clazz = ArrayType::clazz($leaf, $numDims);
             } else {
                 $clazz = $leaf;
             }
             $x = new ArrayValueCommand($clazz);
             $this->pushIdentity($x);
             $length = $this->readCommand('IntValueCommand')->getValue();
             for ($i = 0; $i < $length; $i++) {
                 $x->add($this->readCommand('ValueCommand'));
             }
             break;
         case OBJECT_TYPE:
             // LTypeSeedName~3... N-many setters ...
             $clazz = $this->findClass($token);
             $x = new InstantiateCommand($clazz);
             $this->pushIdentity($x);
             $this->readSetters($clazz, $x);
             break;
         case INVOKE_TYPE:
             // !TypeSeedName~Number of objects written by CFS~...CFS objects...~
             // Number of extra fields~...N-many setters...
             $clazz = $this->findClass($token);
             $serializerClass = null;
             $manualType = $clazz;
             while ($manualType != null) {
                 $serializerClass = SerializabilityUtil::hasCustomFieldSerializer($manualType);
                 if ($serializerClass != null) {
                     break;
                 }
                 $manualType = $manualType->getSuperClass();
             }
             if ($serializerClass == null) {
                 throw new IncompatibleRemoteServiceException('Class [' . $clazz->getName() . '] has no custom serializer on server');
             }
             $x = new InvokeCustomFieldSerializerCommand($clazz, $serializerClass, $manualType);
             $this->pushIdentity($x);
             $this->readFields($x);
             $this->readSetters($clazz, $x);
             break;
         case RETURN_TYPE:
             // R4~...values...
             $this->toReturn = new ReturnCommand();
             $toRead = Integer::valueOf($token);
             for ($i = 0; $i < $toRead; $i++) {
                 $this->toReturn->addValue($this->readCommand('ValueCommand'));
             }
             break;
         case THROW_TYPE:
             // T...value...
             $this->toThrow = $this->readCommand('ValueCommand');
             break;
         case BACKREF_TYPE:
             // @backrefNumber~
             $x = $this->backRefs[Integer::valueOf($token)];
             assert($x != null);
             array_push($this->commands, $x);
             break;
         case RPC_SEPARATOR_CHAR:
             throw new RuntimeException('Segmentation overrun at ' + $this->idx);
         default:
             throw new RuntimeException('Unknown Command ' + $command);
     }
 }