TJSONProtocol no longer uses borrow, and miscellaneous fixes.

Summary:
Added a LookaheadReader to the TJSONProtocol so it doesn't have to
rely on the transport to borrow.
Also added a check to a corner case and fixed up some comments and whitespace.

Reviewed By: mcslee

Test Plan: make check

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665491 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/protocol/TJSONProtocol.cpp b/lib/cpp/src/protocol/TJSONProtocol.cpp
index 179a178..0f3b102 100644
--- a/lib/cpp/src/protocol/TJSONProtocol.cpp
+++ b/lib/cpp/src/protocol/TJSONProtocol.cpp
@@ -81,46 +81,48 @@
 
 static TType getTypeIDForTypeName(const std::string &name) {
   TType result = T_STOP; // Sentinel value
-  switch (name[0]) {
-  case 'd':
-    result = T_DOUBLE;
-    break;
-  case 'i':
-    switch (name[1]) {
-    case '8':
-      result = T_BYTE;
+  if (name.length() > 0) {
+    switch (name[0]) {
+    case 'd':
+      result = T_DOUBLE;
       break;
-    case '1':
-      result = T_I16;
+    case 'i':
+      switch (name[1]) {
+      case '8':
+        result = T_BYTE;
+        break;
+      case '1':
+        result = T_I16;
+        break;
+      case '3':
+        result = T_I32;
+        break;
+      case '6':
+        result = T_I64;
+        break;
+      }
       break;
-    case '3':
-      result = T_I32;
+    case 'l':
+      result = T_LIST;
       break;
-    case '6':
-      result = T_I64;
+    case 'm':
+      result = T_MAP;
+      break;
+    case 'r':
+      result = T_STRUCT;
+      break;
+    case 's':
+      if (name[1] == 't') {
+        result = T_STRING;
+      }
+      else if (name[1] == 'e') {
+        result = T_SET;
+      }
+      break;
+    case 't':
+      result = T_BOOL;
       break;
     }
-    break;
-  case 'l':
-    result = T_LIST;
-    break;
-  case 'm':
-    result = T_MAP;
-    break;
-  case 'r':
-    result = T_STRUCT;
-    break;
-  case 's':
-    if (name[1] == 't') {
-      result = T_STRING;
-    }
-    else if (name[1] == 'e') {
-      result = T_SET;
-    }
-    break;
-  case 't':
-    result = T_BOOL;
-    break;
   }
   if (result == T_STOP) {
     throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
@@ -159,31 +161,18 @@
 // Read 1 character from the transport trans and verify that it is the
 // expected character ch.
 // Throw a protocol exception if it is not.
-static uint32_t readSyntaxChar(TTransport &trans, uint8_t ch) {
-  uint8_t b[1];
-  trans.readAll(b, 1);
-  if (b[0] != ch) {
+static uint32_t readSyntaxChar(TJSONProtocol::LookaheadReader &reader,
+                               uint8_t ch) {
+  uint8_t ch2 = reader.read();
+  if (ch2 != ch) {
     throw TProtocolException(TProtocolException::INVALID_DATA,
                              "Expected \'" + std::string((char *)&ch, 1) +
-                             "\'; got \'" + std::string((char *)b, 1) +
+                             "\'; got \'" + std::string((char *)&ch2, 1) +
                              "\'.");
   }
   return 1;
 }
 
-// Borrow 1 byte from the transport trans and return the value read
-// Throw a transport exception if the byte cannot be borrowed
-static uint8_t borrowByte(TTransport &trans) {
-  uint8_t b[1];
-  uint32_t len = 1;
-  const uint8_t *buf =  trans.borrow(b, &len);
-  if (!buf || !len) {
-    throw TTransportException(TTransportException::UNKNOWN,
-                              "Could not borrow 1 byte from transport.");
-  }
-  return *buf;
-}
-
 // Return the integer value of a hex character ch.
 // Throw a protocol exception if the character is not [0-9a-f].
 static uint8_t hexVal(uint8_t ch) {
@@ -237,7 +226,7 @@
 
 
 /**
- * Class to serve as base JSON context and base class for other context
+ * Class to serve as base JSON context and as base class for other context
  * implementations
  */
 class TJSONContext {
@@ -258,7 +247,7 @@
   /**
    * Read context data from the transport. Default is to do nothing.
    */
-  virtual uint32_t read(TTransport &trans) {
+  virtual uint32_t read(TJSONProtocol::LookaheadReader &reader) {
     return 0;
   };
 
@@ -294,7 +283,7 @@
     }
   }
 
-  uint32_t read(TTransport &trans) {
+  uint32_t read(TJSONProtocol::LookaheadReader &reader) {
     if (first_) {
       first_ = false;
       colon_ = true;
@@ -303,7 +292,7 @@
     else {
       uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
       colon_ = !colon_;
-      return readSyntaxChar(trans, ch);
+      return readSyntaxChar(reader, ch);
     }
   }
 
@@ -338,13 +327,13 @@
     }
   }
 
-  uint32_t read(TTransport &trans) {
+  uint32_t read(TJSONProtocol::LookaheadReader &reader) {
     if (first_) {
       first_ = false;
       return 0;
     }
     else {
-      return readSyntaxChar(trans, kJSONElemSeparator);
+      return readSyntaxChar(reader, kJSONElemSeparator);
     }
   }
 
@@ -355,7 +344,8 @@
 
 TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) :
   TProtocol(ptrans),
-  context_(new TJSONContext()) {
+  context_(new TJSONContext()),
+  reader_(*ptrans) {
 }
 
 TJSONProtocol::~TJSONProtocol() {}
@@ -624,7 +614,7 @@
 }
 
 uint32_t TJSONProtocol::writeByte(const int8_t byte) {
-  // writeByte() must be handled properly becuase boost::lexical cast sees
+  // writeByte() must be handled specially becuase boost::lexical cast sees
   // int8_t as a text type instead of an integer type
   return writeJSONInteger((int16_t)byte);
 }
@@ -657,9 +647,9 @@
    * Reading functions
    */
 
-// Reads 1 byte and verifires that it matches ch.
+// Reads 1 byte and verifies that it matches ch.
 uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
-  return readSyntaxChar(*trans_, ch);
+  return readSyntaxChar(reader_, ch);
 }
 
 // Decodes the four hex parts of a JSON escaped string character and returns
@@ -668,37 +658,40 @@
   uint8_t b[2];
   readJSONSyntaxChar(kJSONZeroChar);
   readJSONSyntaxChar(kJSONZeroChar);
-  trans_->readAll(b, 2);
+  b[0] = reader_.read();
+  b[1] = reader_.read();
   *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
   return 4;
 }
 
 // Decodes a JSON string, including unescaping, and returns the string via str
 uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
-  uint32_t result = (skipContext ? 0 : context_->read(*trans_));
+  uint32_t result = (skipContext ? 0 : context_->read(reader_));
   result += readJSONSyntaxChar(kJSONStringDelimiter);
-  uint8_t b[1];
+  uint8_t ch;
   while (true) {
-    result += trans_->readAll(b, 1);
-    if (b[0] == kJSONStringDelimiter) {
+    ch = reader_.read();
+    ++result;
+    if (ch == kJSONStringDelimiter) {
       break;
     }
-    if (b[0] == kJSONBackslash) {
-      result += trans_->readAll(b, 1);
-      if (b[0] == kJSONEscapeChar) {
-        result += readJSONEscapeChar(&b[0]);
+    if (ch == kJSONBackslash) {
+      ch = reader_.read();
+      ++result;
+      if (ch == kJSONEscapeChar) {
+        result += readJSONEscapeChar(&ch);
       }
       else {
-        size_t pos = kEscapeChars.find(b[0]);
+        size_t pos = kEscapeChars.find(ch);
         if (pos == std::string::npos) {
           throw TProtocolException(TProtocolException::INVALID_DATA,
                                    "Expected control char, got '" +
-                                     std::string((char *)b, 1) + "'.");
+                                   std::string((const char *)&ch, 1)  + "'.");
         }
-        b[0] = kEscapeCharVals[pos];
+        ch = kEscapeCharVals[pos];
       }
     }
-    str += b[0];
+    str += ch;
   }
   return result;
 }
@@ -729,11 +722,11 @@
 uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
   uint32_t result = 0;
   while (true) {
-    uint8_t ch = borrowByte(*trans_);
+    uint8_t ch = reader_.peek();
     if (!isJSONNumeric(ch)) {
       break;
     }
-    trans_->consume(1);
+    reader_.read();
     str += ch;
     ++result;
   }
@@ -744,7 +737,7 @@
 // returning them via num
 template <typename NumberType>
 uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
-  uint32_t result = context_->read(*trans_);
+  uint32_t result = context_->read(reader_);
   if (context_->escapeNum()) {
     result += readJSONSyntaxChar(kJSONStringDelimiter);
   }
@@ -766,9 +759,9 @@
 
 // Reads a JSON number or string and interprets it as a double.
 uint32_t TJSONProtocol::readJSONDouble(double &num) {
-  uint32_t result = context_->read(*trans_);
+  uint32_t result = context_->read(reader_);
   std::string str;
-  if (borrowByte(*trans_) == kJSONStringDelimiter) {
+  if (reader_.peek() == kJSONStringDelimiter) {
     result += readJSONString(str, true);
     // Check for NaN, Infinity and -Infinity
     if (str == kThriftNan) {
@@ -815,7 +808,7 @@
 }
 
 uint32_t TJSONProtocol::readJSONObjectStart() {
-  uint32_t result = context_->read(*trans_);
+  uint32_t result = context_->read(reader_);
   result += readJSONSyntaxChar(kJSONObjectStart);
   pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
   return result;
@@ -828,7 +821,7 @@
 }
 
 uint32_t TJSONProtocol::readJSONArrayStart() {
-  uint32_t result = context_->read(*trans_);
+  uint32_t result = context_->read(reader_);
   result += readJSONSyntaxChar(kJSONArrayStart);
   pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
   return result;
@@ -844,7 +837,6 @@
                                          TMessageType& messageType,
                                          int32_t& seqid) {
   uint32_t result = readJSONArrayStart();
-  std::string tmpStr;
   uint64_t tmpVal = 0;
   result += readJSONInteger(tmpVal);
   if (tmpVal != kThriftVersion1) {
@@ -874,16 +866,10 @@
 uint32_t TJSONProtocol::readFieldBegin(std::string& name,
                                        TType& fieldType,
                                        int16_t& fieldId) {
-  // Check if we hit the end of the list
-  uint8_t b[1];
-  uint32_t len = 1;
-  const uint8_t * buf = trans_->borrow(b, &len);
-  if (!buf || !len) {
-    throw TTransportException(TTransportException::UNKNOWN,
-                              "Could not borrow 1 byte from transport.");
-  }
   uint32_t result = 0;
-  if (buf[0] == kJSONObjectEnd) {
+  // Check if we hit the end of the list
+  uint8_t ch = reader_.peek();
+  if (ch == kJSONObjectEnd) {
     fieldType = facebook::thrift::protocol::T_STOP;
   }
   else {
diff --git a/lib/cpp/src/protocol/TJSONProtocol.h b/lib/cpp/src/protocol/TJSONProtocol.h
index 3096b03..ddf48c7 100644
--- a/lib/cpp/src/protocol/TJSONProtocol.h
+++ b/lib/cpp/src/protocol/TJSONProtocol.h
@@ -20,7 +20,8 @@
 /**
  * JSON protocol for Thrift.
  *
- * This protocol provides for protocol which uses JSON as the wire-format.
+ * Implements a protocol which uses JSON as the wire-format.
+ *
  * Thrift types are represented as described below:
  *
  * 1. Every Thrift integer type is represented as a JSON number.
@@ -245,10 +246,44 @@
 
   uint32_t readBinary(std::string& str);
 
+  class LookaheadReader {
+
+   public:
+
+    LookaheadReader(TTransport &trans) :
+      trans_(&trans),
+      hasData_(false) {
+    }
+
+    uint8_t read() {
+      if (hasData_) {
+        hasData_ = false;
+      }
+      else {
+        trans_->readAll(&data_, 1);
+      }
+      return data_;
+    }
+
+    uint8_t peek() {
+      if (!hasData_) {
+        trans_->readAll(&data_, 1);
+      }
+      hasData_ = true;
+      return data_;
+    }
+
+   private:
+    TTransport *trans_;
+    bool hasData_;
+    uint8_t data_;
+  };
+
  private:
 
   std::stack<boost::shared_ptr<TJSONContext> > contexts_;
   boost::shared_ptr<TJSONContext> context_;
+  LookaheadReader reader_;
 };
 
 /**