void TMemoryBuffer::ensureCanWrite(uint32_t len) {
  // Check available space
  uint32_t avail = available_write();
  if (len <= avail) {
    return;
  }

  if (!owner_) {
    throw TTransportException("Insufficient space in external MemoryBuffer");
  }

  // Grow the buffer as necessary.
  uint32_t new_size = bufferSize_;
  while (len > avail) {
    new_size = new_size > 0 ? new_size * 2 : 1;
    avail = available_write() + (new_size - bufferSize_);
  }

  // Allocate into a new pointer so we don't bork ours if it fails.
  void* new_buffer = std::realloc(buffer_, new_size);
  if (new_buffer == NULL) {
    throw std::bad_alloc();
  }
  bufferSize_ = new_size;

  ptrdiff_t offset = (uint8_t*)new_buffer - buffer_;
  buffer_ += offset;
  rBase_ += offset;
  rBound_ += offset;
  wBase_ += offset;
  wBound_ = buffer_ + bufferSize_;
}
void TMemoryBuffer::wroteBytes(uint32_t len) {
  uint32_t avail = available_write();
  if (len > avail) {
    throw TTransportException("Client wrote more bytes than size of buffer.");
  }
  wBase_ += len;
}
void TMemoryBuffer::ensureCanWrite(uint32_t len) {
  // Check available space
  uint32_t avail = available_write();
  if (len <= avail) {
    return;
  }

  if (!owner_) {
    throw TTransportException("Insufficient space in external MemoryBuffer");
  }

  bool copy = false;
  if (observerCount_ > 0) {
    transferOwnership();
    // going to make a copy and shift out consumed data
    copy = true;
  }
  avail = bufferSize_ - available_read();

  // Grow the buffer as necessary.
  uint32_t new_size = bufferSize_;
  while (len > avail) {
    new_size = new_size > 0 ? new_size * 2 : 1;
    if (new_size <= bufferSize_) {
      // overflow
      throw TTransportException("Buffer size exceeded maximum (2GB)");
    }
    avail = available_write() + (new_size - bufferSize_);
  }

  // Allocate into a new pointer so we don't bork ours if it fails.
  void* new_buffer = nullptr;
  ptrdiff_t offset = 0;
  if (copy) {
    /* This is not a memory leak, because an observed buffer still owns the
     * previous buffer pointer.
     *
     * NOTE: If you are seeing a memory issue here, it's probable the
     * buffers are just growing very large and not being freed often.
     * Consider setting setResizeBufferEveryN,
     * setIdleWriteBufferLimit, and setIdleReadBufferLimit.
     * This will likely solve the issue.
     */
    new_buffer = std::malloc(new_size);
    if (new_buffer == nullptr) {
      throw std::bad_alloc();
    }
    // only copy the unconsumed data
    memcpy(new_buffer, rBase_, wBase_ - rBase_);
  } else {
    if (rBase_ != buffer_) {
      // shift data
      memmove(buffer_, rBase_, wBase_ - rBase_);
    }
    if (new_size > bufferSize_) {
      new_buffer = std::realloc(buffer_, new_size);
      if (new_buffer == nullptr) {
        throw std::bad_alloc();
      }
    } else {
      // plenty of space
      new_buffer = buffer_;
    }
  }
  offset = (uint8_t *)new_buffer - rBase_;
  bufferSize_ = new_size;

  buffer_ = (uint8_t *)new_buffer;
  rBase_ += offset;
  rBound_ += offset;
  wBase_ += offset;
  wBound_ = buffer_ + bufferSize_;
}