1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 #ifndef _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_ 21 #define _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_ 1 22 23 #include <cstdlib> 24 #include <cstring> 25 #include <limits> 26 #include <boost/scoped_array.hpp> 27 28 #include <thrift/transport/TTransport.h> 29 #include <thrift/transport/TVirtualTransport.h> 30 31 #ifdef __GNUC__ 32 #define TDB_LIKELY(val) (__builtin_expect((val), 1)) 33 #define TDB_UNLIKELY(val) (__builtin_expect((val), 0)) 34 #else 35 #define TDB_LIKELY(val) (val) 36 #define TDB_UNLIKELY(val) (val) 37 #endif 38 39 namespace apache { 40 namespace thrift { 41 namespace transport { 42 43 /** 44 * Base class for all transports that use read/write buffers for performance. 45 * 46 * TBufferBase is designed to implement the fast-path "memcpy" style 47 * operations that work in the common case. It does so with small and 48 * (eventually) nonvirtual, inlinable methods. TBufferBase is an abstract 49 * class. Subclasses are expected to define the "slow path" operations 50 * that have to be done when the buffers are full or empty. 51 * 52 */ 53 class TBufferBase : public TVirtualTransport<TBufferBase> { 54 55 public: 56 /** 57 * Fast-path read. 58 * 59 * When we have enough data buffered to fulfill the read, we can satisfy it 60 * with a single memcpy, then adjust our internal pointers. If the buffer 61 * is empty, we call out to our slow path, implemented by a subclass. 62 * This method is meant to eventually be nonvirtual and inlinable. 63 */ read(uint8_t * buf,uint32_t len)64 uint32_t read(uint8_t* buf, uint32_t len) { 65 uint8_t* new_rBase = rBase_ + len; 66 if (TDB_LIKELY(new_rBase <= rBound_)) { 67 std::memcpy(buf, rBase_, len); 68 rBase_ = new_rBase; 69 return len; 70 } 71 return readSlow(buf, len); 72 } 73 74 /** 75 * Shortcutted version of readAll. 76 */ readAll(uint8_t * buf,uint32_t len)77 uint32_t readAll(uint8_t* buf, uint32_t len) { 78 uint8_t* new_rBase = rBase_ + len; 79 if (TDB_LIKELY(new_rBase <= rBound_)) { 80 std::memcpy(buf, rBase_, len); 81 rBase_ = new_rBase; 82 return len; 83 } 84 return apache::thrift::transport::readAll(*this, buf, len); 85 } 86 87 /** 88 * Fast-path write. 89 * 90 * When we have enough empty space in our buffer to accommodate the write, we 91 * can satisfy it with a single memcpy, then adjust our internal pointers. 92 * If the buffer is full, we call out to our slow path, implemented by a 93 * subclass. This method is meant to eventually be nonvirtual and 94 * inlinable. 95 */ write(const uint8_t * buf,uint32_t len)96 void write(const uint8_t* buf, uint32_t len) { 97 uint8_t* new_wBase = wBase_ + len; 98 if (TDB_LIKELY(new_wBase <= wBound_)) { 99 std::memcpy(wBase_, buf, len); 100 wBase_ = new_wBase; 101 return; 102 } 103 writeSlow(buf, len); 104 } 105 106 /** 107 * Fast-path borrow. A lot like the fast-path read. 108 */ borrow(uint8_t * buf,uint32_t * len)109 const uint8_t* borrow(uint8_t* buf, uint32_t* len) { 110 if (TDB_LIKELY(static_cast<ptrdiff_t>(*len) <= rBound_ - rBase_)) { 111 // With strict aliasing, writing to len shouldn't force us to 112 // refetch rBase_ from memory. TODO(dreiss): Verify this. 113 *len = static_cast<uint32_t>(rBound_ - rBase_); 114 return rBase_; 115 } 116 return borrowSlow(buf, len); 117 } 118 119 /** 120 * Consume doesn't require a slow path. 121 */ consume(uint32_t len)122 void consume(uint32_t len) { 123 if (TDB_LIKELY(static_cast<ptrdiff_t>(len) <= rBound_ - rBase_)) { 124 rBase_ += len; 125 } else { 126 throw TTransportException(TTransportException::BAD_ARGS, "consume did not follow a borrow."); 127 } 128 } 129 130 protected: 131 /// Slow path read. 132 virtual uint32_t readSlow(uint8_t* buf, uint32_t len) = 0; 133 134 /// Slow path write. 135 virtual void writeSlow(const uint8_t* buf, uint32_t len) = 0; 136 137 /** 138 * Slow path borrow. 139 * 140 * POSTCONDITION: return == NULL || rBound_ - rBase_ >= *len 141 */ 142 virtual const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) = 0; 143 144 /** 145 * Trivial constructor. 146 * 147 * Initialize pointers safely. Constructing is not a very 148 * performance-sensitive operation, so it is okay to just leave it to 149 * the concrete class to set up pointers correctly. 150 */ TBufferBase()151 TBufferBase() : rBase_(NULL), rBound_(NULL), wBase_(NULL), wBound_(NULL) {} 152 153 /// Convenience mutator for setting the read buffer. setReadBuffer(uint8_t * buf,uint32_t len)154 void setReadBuffer(uint8_t* buf, uint32_t len) { 155 rBase_ = buf; 156 rBound_ = buf + len; 157 } 158 159 /// Convenience mutator for setting the write buffer. setWriteBuffer(uint8_t * buf,uint32_t len)160 void setWriteBuffer(uint8_t* buf, uint32_t len) { 161 wBase_ = buf; 162 wBound_ = buf + len; 163 } 164 ~TBufferBase()165 virtual ~TBufferBase() {} 166 167 /// Reads begin here. 168 uint8_t* rBase_; 169 /// Reads may extend to just before here. 170 uint8_t* rBound_; 171 172 /// Writes begin here. 173 uint8_t* wBase_; 174 /// Writes may extend to just before here. 175 uint8_t* wBound_; 176 }; 177 178 /** 179 * Buffered transport. For reads it will read more data than is requested 180 * and will serve future data out of a local buffer. For writes, data is 181 * stored to an in memory buffer before being written out. 182 * 183 */ 184 class TBufferedTransport : public TVirtualTransport<TBufferedTransport, TBufferBase> { 185 public: 186 static const int DEFAULT_BUFFER_SIZE = 512; 187 188 /// Use default buffer sizes. TBufferedTransport(stdcxx::shared_ptr<TTransport> transport)189 TBufferedTransport(stdcxx::shared_ptr<TTransport> transport) 190 : transport_(transport), 191 rBufSize_(DEFAULT_BUFFER_SIZE), 192 wBufSize_(DEFAULT_BUFFER_SIZE), 193 rBuf_(new uint8_t[rBufSize_]), 194 wBuf_(new uint8_t[wBufSize_]) { 195 initPointers(); 196 } 197 198 /// Use specified buffer sizes. TBufferedTransport(stdcxx::shared_ptr<TTransport> transport,uint32_t sz)199 TBufferedTransport(stdcxx::shared_ptr<TTransport> transport, uint32_t sz) 200 : transport_(transport), 201 rBufSize_(sz), 202 wBufSize_(sz), 203 rBuf_(new uint8_t[rBufSize_]), 204 wBuf_(new uint8_t[wBufSize_]) { 205 initPointers(); 206 } 207 208 /// Use specified read and write buffer sizes. TBufferedTransport(stdcxx::shared_ptr<TTransport> transport,uint32_t rsz,uint32_t wsz)209 TBufferedTransport(stdcxx::shared_ptr<TTransport> transport, uint32_t rsz, uint32_t wsz) 210 : transport_(transport), 211 rBufSize_(rsz), 212 wBufSize_(wsz), 213 rBuf_(new uint8_t[rBufSize_]), 214 wBuf_(new uint8_t[wBufSize_]) { 215 initPointers(); 216 } 217 open()218 void open() { transport_->open(); } 219 isOpen()220 bool isOpen() { return transport_->isOpen(); } 221 peek()222 bool peek() { 223 if (rBase_ == rBound_) { 224 setReadBuffer(rBuf_.get(), transport_->read(rBuf_.get(), rBufSize_)); 225 } 226 return (rBound_ > rBase_); 227 } 228 close()229 void close() { 230 flush(); 231 transport_->close(); 232 } 233 234 virtual uint32_t readSlow(uint8_t* buf, uint32_t len); 235 236 virtual void writeSlow(const uint8_t* buf, uint32_t len); 237 238 void flush(); 239 240 /** 241 * Returns the origin of the underlying transport 242 */ getOrigin()243 virtual const std::string getOrigin() { return transport_->getOrigin(); } 244 245 /** 246 * The following behavior is currently implemented by TBufferedTransport, 247 * but that may change in a future version: 248 * 1/ If len is at most rBufSize_, borrow will never return NULL. 249 * Depending on the underlying transport, it could throw an exception 250 * or hang forever. 251 * 2/ Some borrow requests may copy bytes internally. However, 252 * if len is at most rBufSize_/2, none of the copied bytes 253 * will ever have to be copied again. For optimial performance, 254 * stay under this limit. 255 */ 256 virtual const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len); 257 getUnderlyingTransport()258 stdcxx::shared_ptr<TTransport> getUnderlyingTransport() { return transport_; } 259 260 /* 261 * TVirtualTransport provides a default implementation of readAll(). 262 * We want to use the TBufferBase version instead. 263 */ readAll(uint8_t * buf,uint32_t len)264 uint32_t readAll(uint8_t* buf, uint32_t len) { return TBufferBase::readAll(buf, len); } 265 266 protected: initPointers()267 void initPointers() { 268 setReadBuffer(rBuf_.get(), 0); 269 setWriteBuffer(wBuf_.get(), wBufSize_); 270 // Write size never changes. 271 } 272 273 stdcxx::shared_ptr<TTransport> transport_; 274 275 uint32_t rBufSize_; 276 uint32_t wBufSize_; 277 boost::scoped_array<uint8_t> rBuf_; 278 boost::scoped_array<uint8_t> wBuf_; 279 }; 280 281 /** 282 * Wraps a transport into a buffered one. 283 * 284 */ 285 class TBufferedTransportFactory : public TTransportFactory { 286 public: TBufferedTransportFactory()287 TBufferedTransportFactory() {} 288 ~TBufferedTransportFactory()289 virtual ~TBufferedTransportFactory() {} 290 291 /** 292 * Wraps the transport into a buffered one. 293 */ getTransport(stdcxx::shared_ptr<TTransport> trans)294 virtual stdcxx::shared_ptr<TTransport> getTransport(stdcxx::shared_ptr<TTransport> trans) { 295 return stdcxx::shared_ptr<TTransport>(new TBufferedTransport(trans)); 296 } 297 }; 298 299 /** 300 * Framed transport. All writes go into an in-memory buffer until flush is 301 * called, at which point the transport writes the length of the entire 302 * binary chunk followed by the data payload. This allows the receiver on the 303 * other end to always do fixed-length reads. 304 * 305 */ 306 class TFramedTransport : public TVirtualTransport<TFramedTransport, TBufferBase> { 307 public: 308 static const int DEFAULT_BUFFER_SIZE = 512; 309 static const int DEFAULT_MAX_FRAME_SIZE = 256 * 1024 * 1024; 310 311 /// Use default buffer sizes. TFramedTransport()312 TFramedTransport() 313 : transport_(), 314 rBufSize_(0), 315 wBufSize_(DEFAULT_BUFFER_SIZE), 316 rBuf_(), 317 wBuf_(new uint8_t[wBufSize_]), 318 bufReclaimThresh_((std::numeric_limits<uint32_t>::max)()) { 319 initPointers(); 320 } 321 TFramedTransport(stdcxx::shared_ptr<TTransport> transport)322 TFramedTransport(stdcxx::shared_ptr<TTransport> transport) 323 : transport_(transport), 324 rBufSize_(0), 325 wBufSize_(DEFAULT_BUFFER_SIZE), 326 rBuf_(), 327 wBuf_(new uint8_t[wBufSize_]), 328 bufReclaimThresh_((std::numeric_limits<uint32_t>::max)()), 329 maxFrameSize_(DEFAULT_MAX_FRAME_SIZE) { 330 initPointers(); 331 } 332 333 TFramedTransport(stdcxx::shared_ptr<TTransport> transport, 334 uint32_t sz, 335 uint32_t bufReclaimThresh = (std::numeric_limits<uint32_t>::max)()) transport_(transport)336 : transport_(transport), 337 rBufSize_(0), 338 wBufSize_(sz), 339 rBuf_(), 340 wBuf_(new uint8_t[wBufSize_]), 341 bufReclaimThresh_(bufReclaimThresh), 342 maxFrameSize_(DEFAULT_MAX_FRAME_SIZE) { 343 initPointers(); 344 } 345 open()346 void open() { transport_->open(); } 347 isOpen()348 bool isOpen() { return transport_->isOpen(); } 349 peek()350 bool peek() { return (rBase_ < rBound_) || transport_->peek(); } 351 close()352 void close() { 353 flush(); 354 transport_->close(); 355 } 356 357 virtual uint32_t readSlow(uint8_t* buf, uint32_t len); 358 359 virtual void writeSlow(const uint8_t* buf, uint32_t len); 360 361 virtual void flush(); 362 363 uint32_t readEnd(); 364 365 uint32_t writeEnd(); 366 367 const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len); 368 getUnderlyingTransport()369 stdcxx::shared_ptr<TTransport> getUnderlyingTransport() { return transport_; } 370 371 /* 372 * TVirtualTransport provides a default implementation of readAll(). 373 * We want to use the TBufferBase version instead. 374 */ 375 using TBufferBase::readAll; 376 377 /** 378 * Returns the origin of the underlying transport 379 */ getOrigin()380 virtual const std::string getOrigin() { return transport_->getOrigin(); } 381 382 /** 383 * Set the maximum size of the frame at read 384 */ setMaxFrameSize(uint32_t maxFrameSize)385 void setMaxFrameSize(uint32_t maxFrameSize) { maxFrameSize_ = maxFrameSize; } 386 387 /** 388 * Get the maximum size of the frame at read 389 */ getMaxFrameSize()390 uint32_t getMaxFrameSize() { return maxFrameSize_; } 391 392 protected: 393 /** 394 * Reads a frame of input from the underlying stream. 395 * 396 * Returns true if a frame was read successfully, or false on EOF. 397 * (Raises a TTransportException if EOF occurs after a partial frame.) 398 */ 399 virtual bool readFrame(); 400 initPointers()401 void initPointers() { 402 setReadBuffer(NULL, 0); 403 setWriteBuffer(wBuf_.get(), wBufSize_); 404 405 // Pad the buffer so we can insert the size later. 406 int32_t pad = 0; 407 this->write((uint8_t*)&pad, sizeof(pad)); 408 } 409 410 stdcxx::shared_ptr<TTransport> transport_; 411 412 uint32_t rBufSize_; 413 uint32_t wBufSize_; 414 boost::scoped_array<uint8_t> rBuf_; 415 boost::scoped_array<uint8_t> wBuf_; 416 uint32_t bufReclaimThresh_; 417 uint32_t maxFrameSize_; 418 }; 419 420 /** 421 * Wraps a transport into a framed one. 422 * 423 */ 424 class TFramedTransportFactory : public TTransportFactory { 425 public: TFramedTransportFactory()426 TFramedTransportFactory() {} 427 ~TFramedTransportFactory()428 virtual ~TFramedTransportFactory() {} 429 430 /** 431 * Wraps the transport into a framed one. 432 */ getTransport(stdcxx::shared_ptr<TTransport> trans)433 virtual stdcxx::shared_ptr<TTransport> getTransport(stdcxx::shared_ptr<TTransport> trans) { 434 return stdcxx::shared_ptr<TTransport>(new TFramedTransport(trans)); 435 } 436 }; 437 438 /** 439 * A memory buffer is a tranpsort that simply reads from and writes to an 440 * in memory buffer. Anytime you call write on it, the data is simply placed 441 * into a buffer, and anytime you call read, data is read from that buffer. 442 * 443 * The buffers are allocated using C constructs malloc,realloc, and the size 444 * doubles as necessary. We've considered using scoped 445 * 446 */ 447 class TMemoryBuffer : public TVirtualTransport<TMemoryBuffer, TBufferBase> { 448 private: 449 // Common initialization done by all constructors. initCommon(uint8_t * buf,uint32_t size,bool owner,uint32_t wPos)450 void initCommon(uint8_t* buf, uint32_t size, bool owner, uint32_t wPos) { 451 452 maxBufferSize_ = (std::numeric_limits<uint32_t>::max)(); 453 454 if (buf == NULL && size != 0) { 455 assert(owner); 456 buf = (uint8_t*)std::malloc(size); 457 if (buf == NULL) { 458 throw std::bad_alloc(); 459 } 460 } 461 462 buffer_ = buf; 463 bufferSize_ = size; 464 465 rBase_ = buffer_; 466 rBound_ = buffer_ + wPos; 467 // TODO(dreiss): Investigate NULL-ing this if !owner. 468 wBase_ = buffer_ + wPos; 469 wBound_ = buffer_ + bufferSize_; 470 471 owner_ = owner; 472 473 // rBound_ is really an artifact. In principle, it should always be 474 // equal to wBase_. We update it in a few places (computeRead, etc.). 475 } 476 477 public: 478 static const uint32_t defaultSize = 1024; 479 480 /** 481 * This enum specifies how a TMemoryBuffer should treat 482 * memory passed to it via constructors or resetBuffer. 483 * 484 * OBSERVE: 485 * TMemoryBuffer will simply store a pointer to the memory. 486 * It is the callers responsibility to ensure that the pointer 487 * remains valid for the lifetime of the TMemoryBuffer, 488 * and that it is properly cleaned up. 489 * Note that no data can be written to observed buffers. 490 * 491 * COPY: 492 * TMemoryBuffer will make an internal copy of the buffer. 493 * The caller has no responsibilities. 494 * 495 * TAKE_OWNERSHIP: 496 * TMemoryBuffer will become the "owner" of the buffer, 497 * and will be responsible for freeing it. 498 * The membory must have been allocated with malloc. 499 */ 500 enum MemoryPolicy { OBSERVE = 1, COPY = 2, TAKE_OWNERSHIP = 3 }; 501 502 /** 503 * Construct a TMemoryBuffer with a default-sized buffer, 504 * owned by the TMemoryBuffer object. 505 */ TMemoryBuffer()506 TMemoryBuffer() { initCommon(NULL, defaultSize, true, 0); } 507 508 /** 509 * Construct a TMemoryBuffer with a buffer of a specified size, 510 * owned by the TMemoryBuffer object. 511 * 512 * @param sz The initial size of the buffer. 513 */ TMemoryBuffer(uint32_t sz)514 TMemoryBuffer(uint32_t sz) { initCommon(NULL, sz, true, 0); } 515 516 /** 517 * Construct a TMemoryBuffer with buf as its initial contents. 518 * 519 * @param buf The initial contents of the buffer. 520 * Note that, while buf is a non-const pointer, 521 * TMemoryBuffer will not write to it if policy == OBSERVE, 522 * so it is safe to const_cast<uint8_t*>(whatever). 523 * @param sz The size of @c buf. 524 * @param policy See @link MemoryPolicy @endlink . 525 */ 526 TMemoryBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE) { 527 if (buf == NULL && sz != 0) { 528 throw TTransportException(TTransportException::BAD_ARGS, 529 "TMemoryBuffer given null buffer with non-zero size."); 530 } 531 532 switch (policy) { 533 case OBSERVE: 534 case TAKE_OWNERSHIP: 535 initCommon(buf, sz, policy == TAKE_OWNERSHIP, sz); 536 break; 537 case COPY: 538 initCommon(NULL, sz, true, 0); 539 this->write(buf, sz); 540 break; 541 default: 542 throw TTransportException(TTransportException::BAD_ARGS, 543 "Invalid MemoryPolicy for TMemoryBuffer"); 544 } 545 } 546 ~TMemoryBuffer()547 ~TMemoryBuffer() { 548 if (owner_) { 549 std::free(buffer_); 550 } 551 } 552 isOpen()553 bool isOpen() { return true; } 554 peek()555 bool peek() { return (rBase_ < wBase_); } 556 open()557 void open() {} 558 close()559 void close() {} 560 561 // TODO(dreiss): Make bufPtr const. getBuffer(uint8_t ** bufPtr,uint32_t * sz)562 void getBuffer(uint8_t** bufPtr, uint32_t* sz) { 563 *bufPtr = rBase_; 564 *sz = static_cast<uint32_t>(wBase_ - rBase_); 565 } 566 getBufferAsString()567 std::string getBufferAsString() { 568 if (buffer_ == NULL) { 569 return ""; 570 } 571 uint8_t* buf; 572 uint32_t sz; 573 getBuffer(&buf, &sz); 574 return std::string((char*)buf, (std::string::size_type)sz); 575 } 576 appendBufferToString(std::string & str)577 void appendBufferToString(std::string& str) { 578 if (buffer_ == NULL) { 579 return; 580 } 581 uint8_t* buf; 582 uint32_t sz; 583 getBuffer(&buf, &sz); 584 str.append((char*)buf, sz); 585 } 586 resetBuffer()587 void resetBuffer() { 588 rBase_ = buffer_; 589 rBound_ = buffer_; 590 wBase_ = buffer_; 591 // It isn't safe to write into a buffer we don't own. 592 if (!owner_) { 593 wBound_ = wBase_; 594 bufferSize_ = 0; 595 } 596 } 597 598 /// See constructor documentation. 599 void resetBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE) { 600 // Use a variant of the copy-and-swap trick for assignment operators. 601 // This is sub-optimal in terms of performance for two reasons: 602 // 1/ The constructing and swapping of the (small) values 603 // in the temporary object takes some time, and is not necessary. 604 // 2/ If policy == COPY, we allocate the new buffer before 605 // freeing the old one, precluding the possibility of 606 // reusing that memory. 607 // I doubt that either of these problems could be optimized away, 608 // but the second is probably no a common case, and the first is minor. 609 // I don't expect resetBuffer to be a common operation, so I'm willing to 610 // bite the performance bullet to make the method this simple. 611 612 // Construct the new buffer. 613 TMemoryBuffer new_buffer(buf, sz, policy); 614 // Move it into ourself. 615 this->swap(new_buffer); 616 // Our old self gets destroyed. 617 } 618 619 /// See constructor documentation. resetBuffer(uint32_t sz)620 void resetBuffer(uint32_t sz) { 621 // Construct the new buffer. 622 TMemoryBuffer new_buffer(sz); 623 // Move it into ourself. 624 this->swap(new_buffer); 625 // Our old self gets destroyed. 626 } 627 readAsString(uint32_t len)628 std::string readAsString(uint32_t len) { 629 std::string str; 630 (void)readAppendToString(str, len); 631 return str; 632 } 633 634 uint32_t readAppendToString(std::string& str, uint32_t len); 635 636 // return number of bytes read readEnd()637 uint32_t readEnd() { 638 // This cast should be safe, because buffer_'s size is a uint32_t 639 uint32_t bytes = static_cast<uint32_t>(rBase_ - buffer_); 640 if (rBase_ == wBase_) { 641 resetBuffer(); 642 } 643 return bytes; 644 } 645 646 // Return number of bytes written writeEnd()647 uint32_t writeEnd() { 648 // This cast should be safe, because buffer_'s size is a uint32_t 649 return static_cast<uint32_t>(wBase_ - buffer_); 650 } 651 available_read()652 uint32_t available_read() const { 653 // Remember, wBase_ is the real rBound_. 654 return static_cast<uint32_t>(wBase_ - rBase_); 655 } 656 available_write()657 uint32_t available_write() const { return static_cast<uint32_t>(wBound_ - wBase_); } 658 659 // Returns a pointer to where the client can write data to append to 660 // the TMemoryBuffer, and ensures the buffer is big enough to accommodate a 661 // write of the provided length. The returned pointer is very convenient for 662 // passing to read(), recv(), or similar. You must call wroteBytes() as soon 663 // as data is written or the buffer will not be aware that data has changed. getWritePtr(uint32_t len)664 uint8_t* getWritePtr(uint32_t len) { 665 ensureCanWrite(len); 666 return wBase_; 667 } 668 669 // Informs the buffer that the client has written 'len' bytes into storage 670 // that had been provided by getWritePtr(). 671 void wroteBytes(uint32_t len); 672 673 /* 674 * TVirtualTransport provides a default implementation of readAll(). 675 * We want to use the TBufferBase version instead. 676 */ readAll(uint8_t * buf,uint32_t len)677 uint32_t readAll(uint8_t* buf, uint32_t len) { return TBufferBase::readAll(buf, len); } 678 679 //! \brief Get the current buffer size 680 //! \returns the current buffer size getBufferSize()681 uint32_t getBufferSize() const { 682 return bufferSize_; 683 } 684 685 //! \brief Get the current maximum buffer size 686 //! \returns the current maximum buffer size getMaxBufferSize()687 uint32_t getMaxBufferSize() const { 688 return maxBufferSize_; 689 } 690 691 //! \brief Change the maximum buffer size 692 //! \param[in] maxSize the new maximum buffer size allowed to grow to 693 //! \throws TTransportException(BAD_ARGS) if maxSize is less than the current buffer size setMaxBufferSize(uint32_t maxSize)694 void setMaxBufferSize(uint32_t maxSize) { 695 if (maxSize < bufferSize_) { 696 throw TTransportException(TTransportException::BAD_ARGS, 697 "Maximum buffer size would be less than current buffer size"); 698 } 699 maxBufferSize_ = maxSize; 700 } 701 702 protected: swap(TMemoryBuffer & that)703 void swap(TMemoryBuffer& that) { 704 using std::swap; 705 swap(buffer_, that.buffer_); 706 swap(bufferSize_, that.bufferSize_); 707 708 swap(rBase_, that.rBase_); 709 swap(rBound_, that.rBound_); 710 swap(wBase_, that.wBase_); 711 swap(wBound_, that.wBound_); 712 713 swap(owner_, that.owner_); 714 } 715 716 // Make sure there's at least 'len' bytes available for writing. 717 void ensureCanWrite(uint32_t len); 718 719 // Compute the position and available data for reading. 720 void computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give); 721 722 uint32_t readSlow(uint8_t* buf, uint32_t len); 723 724 void writeSlow(const uint8_t* buf, uint32_t len); 725 726 const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len); 727 728 // Data buffer 729 uint8_t* buffer_; 730 731 // Allocated buffer size 732 uint32_t bufferSize_; 733 734 // Maximum allowed size 735 uint32_t maxBufferSize_; 736 737 // Is this object the owner of the buffer? 738 bool owner_; 739 740 // Don't forget to update constrctors, initCommon, and swap if 741 // you add new members. 742 }; 743 } 744 } 745 } // apache::thrift::transport 746 747 #endif // #ifndef _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_ 748