private function _consume_char($c)
 {
     // valid whitespace characters in JSON (from RFC4627 for JSON) include:
     // space, horizontal tab, line feed or new line, and carriage return.
     // thanks: http://stackoverflow.com/questions/16042274/definition-of-whitespace-in-json
     if (($c === " " || $c === "\t" || $c === "\n" || $c === "\r") && !($this->_state === self::STATE_IN_STRING || $this->_state === self::STATE_UNICODE || $this->_state === self::STATE_START_ESCAPE || $this->_state === self::STATE_IN_NUMBER || $this->_state === self::STATE_START_DOCUMENT)) {
         // we wrap this so that we don't make a ton of unnecessary function calls
         // unless someone really, really cares about whitespace.
         if ($this->_emit_whitespace) {
             $this->_listener->whitespace($c);
         }
         return;
     }
     switch ($this->_state) {
         case self::STATE_START_DOCUMENT:
             $this->_listener->start_document();
             if ($c === '[') {
                 $this->_start_array();
             } elseif ($c === '{') {
                 $this->_start_object();
             } else {
                 throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Document must start with object or array.");
             }
             break;
         case self::STATE_IN_ARRAY:
             if ($c === ']') {
                 $this->_end_array();
             } else {
                 $this->_start_value($c);
             }
             break;
         case self::STATE_IN_OBJECT:
             if ($c === '}') {
                 $this->_end_object();
             } elseif ($c === '"') {
                 $this->_start_key();
             } else {
                 throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Start of string expected for object key. Instead got: " . $c);
             }
             break;
         case self::STATE_END_KEY:
             if ($c !== ':') {
                 throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Expected ':' after key.");
             }
             $this->_state = self::STATE_AFTER_KEY;
             break;
         case self::STATE_AFTER_KEY:
             $this->_start_value($c);
             break;
         case self::STATE_IN_STRING:
             if ($c === '"') {
                 $this->_end_string();
             } elseif ($c === '\\') {
                 $this->_state = self::STATE_START_ESCAPE;
             } elseif ($c < "" || $c === "") {
                 throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Unescaped control character encountered: " . $c);
             } else {
                 $this->_buffer .= $c;
             }
             break;
         case self::STATE_START_ESCAPE:
             $this->_process_escape_character($c);
             break;
         case self::STATE_UNICODE:
             $this->_process_unicode_character($c);
             break;
         case self::STATE_AFTER_VALUE:
             $within = end($this->_stack);
             if ($within === self::STACK_OBJECT) {
                 if ($c === '}') {
                     $this->_end_object();
                 } elseif ($c === ',') {
                     $this->_state = self::STATE_IN_OBJECT;
                 } else {
                     throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Expected ',' or '}' while parsing object. Got: " . $c);
                 }
             } elseif ($within === self::STACK_ARRAY) {
                 if ($c === ']') {
                     $this->_end_array();
                 } elseif ($c === ',') {
                     $this->_state = self::STATE_IN_ARRAY;
                 } else {
                     throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Expected ',' or ']' while parsing array. Got: " . $c);
                 }
             } else {
                 throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Finished a literal, but unclear what state to move to. Last state: " . $within);
             }
             break;
         case self::STATE_IN_NUMBER:
             if (preg_match('/\\d/', $c)) {
                 $this->_buffer .= $c;
             } elseif ($c === '.') {
                 if (strpos($this->_buffer, '.') !== false) {
                     throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Cannot have multiple decimal points in a number.");
                 } elseif (stripos($this->_buffer, 'e') !== false) {
                     throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Cannot have a decimal point in an exponent.");
                 }
                 $this->_buffer .= $c;
             } elseif ($c === 'e' || $c === 'E') {
                 if (stripos($this->_buffer, 'e') !== false) {
                     throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Cannot have multiple exponents in a number.");
                 }
                 $this->_buffer .= $c;
             } elseif ($c === '+' || $c === '-') {
                 $last = mb_substr($this->_buffer, -1);
                 if (!($last === 'e' || $last === 'E')) {
                     throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Can only have '+' or '-' after the 'e' or 'E' in a number.");
                 }
                 $this->_buffer .= $c;
             } else {
                 $this->_end_number();
                 // we have consumed one beyond the end of the number
                 $this->_consume_char($c);
             }
             break;
         case self::STATE_IN_TRUE:
             $this->_buffer .= $c;
             if (mb_strlen($this->_buffer) === 4) {
                 $this->_end_true();
             }
             break;
         case self::STATE_IN_FALSE:
             $this->_buffer .= $c;
             if (mb_strlen($this->_buffer) === 5) {
                 $this->_end_false();
             }
             break;
         case self::STATE_IN_NULL:
             $this->_buffer .= $c;
             if (mb_strlen($this->_buffer) === 4) {
                 $this->_end_null();
             }
             break;
         case self::STATE_DONE:
             throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Expected end of document.");
         default:
             throw new JsonStreamingParser_ParsingError($this->_line_number, $this->_char_number, "Internal error. Reached an unknown state: " . $this->_state);
     }
 }