Thrift: New methods for C++ transports.

Summary:
To support the upcoming variable-length encoding of integers in
TDenseProtocol, augment Thrift transports with two new methods:
borrow and consume.  Borrow copies some data out of the transport
without consuming it, and consume... consumes it.

Reviewed By: mcslee

Test Plan:
Thrift compiles.
Should check in variable-length integer code for TDenseProtocol
right after this (git ruulz).

Revert Plan: revert stuff that uses these methods also.


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665251 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/transport/TTransport.h b/lib/cpp/src/transport/TTransport.h
index 53d8fd5..b3d8820 100644
--- a/lib/cpp/src/transport/TTransport.h
+++ b/lib/cpp/src/transport/TTransport.h
@@ -66,7 +66,7 @@
   /**
    * Attempt to read up to the specified number of bytes into the string.
    *
-   * @param s     Reference to the location to append the read data
+   * @param buf  Reference to the location to write the data
    * @param len  How many bytes to read
    * @return How many bytes were actually read
    * @throws TTransportException If an error occurs
@@ -112,7 +112,7 @@
   /**
    * Writes the string in its entirety to the buffer.
    *
-   * @param s The string to write out
+   * @param buf  The data to write out
    * @throws TTransportException if an error occurs
    */
   virtual void write(const uint8_t* buf, uint32_t len) {
@@ -138,6 +138,37 @@
    */
   virtual void flush() {}
 
+  /**
+   * Attempts to copy len bytes from the transport into buf.  Does not consume
+   * the bytes read (i.e.: a later read will return the same data).  This
+   * method is meant to support protocols that need to read variable-length
+   * fields.  They can attempt to borrow the maximum amount of data that they
+   * will need, then consume (see next method) what they actually use.  Some
+   * transports will not support this method and others will fail occasionally,
+   * so protocols must be prepared to use read if borrow fails.
+   *
+   * @oaram buf  The buffer to store the data
+   * @param len  How much data to borrow
+   * @return true if the requested data has been borrowed, false otherwise
+   * @throws TTransportException if an error occurs
+   */
+  virtual bool borrow(uint8_t* buf, uint32_t len) {
+    return false;
+  }
+
+  /**
+   * Remove len bytes from the transport.  This should always follow a borrow
+   * of at least len bytes, and should always succeed.
+   * TODO(dreiss): Is there any transport that could borrow but fail to
+   * consume, or that would require a buffer to dump the consumed data?
+   *
+   * @param len  How many bytes to consume
+   * @throws TTransportException If an error occurs
+   */
+  virtual void consume(uint32_t len) {
+    throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot consume.");
+  }
+
  protected:
   /**
    * Simple constructor.
diff --git a/lib/cpp/src/transport/TTransportUtils.cpp b/lib/cpp/src/transport/TTransportUtils.cpp
index be70f47..b10958c 100644
--- a/lib/cpp/src/transport/TTransportUtils.cpp
+++ b/lib/cpp/src/transport/TTransportUtils.cpp
@@ -59,6 +59,26 @@
   }
 }
 
+bool TBufferedTransport::borrow(uint8_t* buf, uint32_t len) {
+  // Don't try to be clever with shifting buffers.
+  // If we have enough data, give it, otherwise
+  // let the protcol use its slow path.
+  if (rLen_-rPos_ >= len) {
+    memcpy(buf, rBuf_+rPos_, len);
+    return true;
+  }
+  return false;
+}
+
+void TBufferedTransport::consume(uint32_t len) {
+  if (rLen_-rPos_ >= len) {
+    rPos_ += len;
+  } else {
+    throw TTransportException(TTransportException::BAD_ARGS,
+                              "consume did not follow a borrow.");
+  }
+}
+
 void TBufferedTransport::flush()  {
   // Write out any data waiting in the write buffer
   if (wLen_ > 0) {
@@ -183,6 +203,26 @@
   transport_->flush();
 }
 
+bool TFramedTransport::borrow(uint8_t* buf, uint32_t len) {
+  // Don't try to be clever with shifting buffers.
+  // If we have enough data, give it, otherwise
+  // let the protcol use its slow path.
+  if (read_ && (rLen_-rPos_ >= len)) {
+    memcpy(buf, rBuf_+rPos_, len);
+    return true;
+  }
+  return false;
+}
+
+void TFramedTransport::consume(uint32_t len) {
+  if (rLen_-rPos_ >= len) {
+    rPos_ += len;
+  } else {
+    throw TTransportException(TTransportException::BAD_ARGS,
+                              "consume did not follow a borrow.");
+  }
+}
+
 uint32_t TMemoryBuffer::read(uint8_t* buf, uint32_t len) {
   // Check avaible data for reading
   uint32_t avail = wPos_ - rPos_;
@@ -253,6 +293,26 @@
   wPos_ += len;
 }
 
+bool TMemoryBuffer::borrow(uint8_t* buf, uint32_t len) {
+  // Don't try to be clever with shifting buffers.
+  // If we have enough data, give it, otherwise
+  // let the protcol use its slow path.
+  if (wPos_-rPos_ >= len) {
+    memcpy(buf, buffer_ + rPos_, len);
+    return true;
+  }
+  return false;
+}
+
+void TMemoryBuffer::consume(uint32_t len) {
+  if (wPos_-rPos_ >= len) {
+    rPos_ += len;
+  } else {
+    throw TTransportException(TTransportException::BAD_ARGS,
+                              "consume did not follow a borrow.");
+  }
+}
+
 uint32_t TPipedTransport::read(uint8_t* buf, uint32_t len) {
   uint32_t need = len;
   
diff --git a/lib/cpp/src/transport/TTransportUtils.h b/lib/cpp/src/transport/TTransportUtils.h
index 1a40975..033e472 100644
--- a/lib/cpp/src/transport/TTransportUtils.h
+++ b/lib/cpp/src/transport/TTransportUtils.h
@@ -105,6 +105,10 @@
 
   void flush();
 
+  bool borrow(uint8_t* buf, uint32_t len);
+
+  void consume(uint32_t len);
+
  protected:
   boost::shared_ptr<TTransport> transport_;
   uint8_t* rBuf_;
@@ -213,6 +217,10 @@
 
   void flush();
 
+  bool borrow(uint8_t* buf, uint32_t len);
+
+  void consume(uint32_t len);
+
  protected:
   boost::shared_ptr<TTransport> transport_;
   uint8_t* rBuf_;
@@ -383,6 +391,10 @@
     return wPos_ - rPos_;
   }
 
+  bool borrow(uint8_t* buf, uint32_t len);
+
+  void consume(uint32_t len);
+
  private: 
   // Data buffer
   uint8_t* buffer_;