private function formatMercurialArguments($command, array $arguments)
 {
     $spec = DiffusionMercurialWireProtocol::getCommandArgs($command);
     $out = array();
     // Mercurial takes normal arguments like this:
     //
     //   name <length(value)>
     //   value
     $has_star = false;
     foreach ($spec as $arg_key) {
         if ($arg_key == '*') {
             $has_star = true;
             continue;
         }
         if (isset($arguments[$arg_key])) {
             $value = $arguments[$arg_key];
             $size = strlen($value);
             $out[] = "{$arg_key} {$size}\n{$value}";
             unset($arguments[$arg_key]);
         }
     }
     if ($has_star) {
         // Mercurial takes arguments for variable argument lists roughly like
         // this:
         //
         //   * <count(args)>
         //   argname1 <length(argvalue1)>
         //   argvalue1
         //   argname2 <length(argvalue2)>
         //   argvalue2
         $count = count($arguments);
         $out[] = "* {$count}\n";
         foreach ($arguments as $key => $value) {
             if (in_array($key, $spec)) {
                 // We already added this argument above, so skip it.
                 continue;
             }
             $size = strlen($value);
             $out[] = "{$key} {$size}\n{$value}";
         }
     }
     return implode('', $out);
 }
 protected function decodeStream($data)
 {
     $this->buffer .= $data;
     $out = array();
     $messages = array();
     while (true) {
         if ($this->state == 'command') {
             $this->initializeState();
             // We're reading a command. It looks like:
             //
             //   <command>
             $line = $this->readProtocolLine();
             if ($line === null) {
                 break;
             }
             $this->command = $line;
             $this->state = 'arguments';
         } else {
             if ($this->state == 'arguments') {
                 // Check if we're still waiting for arguments.
                 $args = DiffusionMercurialWireProtocol::getCommandArgs($this->command);
                 $have = array_select_keys($this->arguments, $args);
                 if (count($have) == count($args)) {
                     // We have all the arguments. Emit a message and read the next
                     // command.
                     $messages[] = $this->newMessageAndResetState();
                 } else {
                     // We're still reading arguments. They can either look like:
                     //
                     //   <name> <length(value)>
                     //   <value>
                     //   ...
                     //
                     // ...or like this:
                     //
                     //   * <count>
                     //   <name1> <length(value1)>
                     //   <value1>
                     //   ...
                     $line = $this->readProtocolLine();
                     if ($line === null) {
                         break;
                     }
                     list($arg, $size) = explode(' ', $line, 2);
                     $size = (int) $size;
                     if ($arg != '*') {
                         $this->expectBytes = $size;
                         $this->argumentName = $arg;
                         $this->state = 'value';
                     } else {
                         $this->arguments['*'] = array();
                         $this->expectArgumentCount = $size;
                         $this->state = 'argv';
                     }
                 }
             } else {
                 if ($this->state == 'value' || $this->state == 'argv-value') {
                     // We're reading the value of an argument. We just need to wait for
                     // the right number of bytes to show up.
                     $bytes = $this->readProtocolBytes();
                     if ($bytes === null) {
                         break;
                     }
                     if ($this->state == 'argv-value') {
                         $this->arguments['*'][$this->argumentName] = $bytes;
                         $this->state = 'argv';
                     } else {
                         $this->arguments[$this->argumentName] = $bytes;
                         $this->state = 'arguments';
                     }
                 } else {
                     if ($this->state == 'argv') {
                         // We're reading a variable number of arguments. We need to wait for
                         // the arguments to arrive.
                         if ($this->expectArgumentCount) {
                             $line = $this->readProtocolLine();
                             if ($line === null) {
                                 break;
                             }
                             list($arg, $size) = explode(' ', $line, 2);
                             $size = (int) $size;
                             $this->expectBytes = $size;
                             $this->argumentName = $arg;
                             $this->state = 'argv-value';
                             $this->expectArgumentCount--;
                         } else {
                             $this->state = 'arguments';
                         }
                     } else {
                         if ($this->state == 'data-length') {
                             $line = $this->readProtocolLine();
                             if ($line === null) {
                                 break;
                             }
                             $this->expectBytes = (int) $line;
                             if (!$this->expectBytes) {
                                 $messages[] = $this->newDataMessage('');
                                 $this->initializeState();
                             } else {
                                 $this->state = 'data-bytes';
                             }
                         } else {
                             if ($this->state == 'data-bytes') {
                                 $bytes = substr($this->buffer, 0, $this->expectBytes);
                                 $this->buffer = substr($this->buffer, strlen($bytes));
                                 $this->expectBytes -= strlen($bytes);
                                 $messages[] = $this->newDataMessage($bytes);
                                 if (!$this->expectBytes) {
                                     // We've finished reading this chunk, so go read the next chunk.
                                     $this->state = 'data-length';
                                 } else {
                                     // We're waiting for more data, and have read everything available
                                     // to us so far.
                                     break;
                                 }
                             } else {
                                 throw new Exception("Bad parser state '{$this->state}'!");
                             }
                         }
                     }
                 }
             }
         }
     }
     return $messages;
 }