THRIFT-5114 Simplified reallocation of TMemoryBuffer
Client: cpp
Patch: Mario Emmenlauer

This closes #2030
diff --git a/lib/cpp/src/thrift/transport/TBufferTransports.cpp b/lib/cpp/src/thrift/transport/TBufferTransports.cpp
index 534067f..329d220 100644
--- a/lib/cpp/src/thrift/transport/TBufferTransports.cpp
+++ b/lib/cpp/src/thrift/transport/TBufferTransports.cpp
@@ -361,16 +361,18 @@
   }
 
   // Grow the buffer as necessary.
-  uint64_t new_size = bufferSize_;
-  while (len > avail) {
-    new_size = new_size > 0 ? new_size * 2 : 1;
-    if (new_size > maxBufferSize_) {
-      throw TTransportException(TTransportException::BAD_ARGS,
-                                "Internal buffer size overflow");
-    }
-    avail = available_write() + (static_cast<uint32_t>(new_size) - bufferSize_);
+  const uint32_t current_used = bufferSize_ - avail;
+  const uint32_t required_buffer_size = len + current_used;
+  if (required_buffer_size > maxBufferSize_) {
+    throw TTransportException(TTransportException::BAD_ARGS,
+                              "Internal buffer size overflow when requesting a buffer of size " + std::to_string(required_buffer_size));
   }
 
+  // Always grow to the next bigger power of two:
+  const double suggested_buffer_size = std::exp2(std::ceil(std::log2(required_buffer_size)));
+  // Unless the power of two exceeds maxBufferSize_:
+  const uint64_t new_size = static_cast<uint64_t>((std::min)(suggested_buffer_size, static_cast<double>(maxBufferSize_)));
+
   // Allocate into a new pointer so we don't bork ours if it fails.
   auto* new_buffer = static_cast<uint8_t*>(std::realloc(buffer_, static_cast<std::size_t>(new_size)));
   if (new_buffer == nullptr) {
@@ -381,6 +383,7 @@
   rBound_ = new_buffer + (rBound_ - buffer_);
   wBase_ = new_buffer + (wBase_ - buffer_);
   wBound_ = new_buffer + new_size;
+  // Note: with realloc() we do not need to free the previous buffer:
   buffer_ = new_buffer;
   bufferSize_ = static_cast<uint32_t>(new_size);
 }