void IOBuf::coalesceAndReallocate(size_t newHeadroom, size_t newLength, IOBuf* end, size_t newTailroom) { uint64_t newCapacity = newLength + newHeadroom + newTailroom; if (newCapacity > UINT32_MAX) { throw std::overflow_error("IOBuf chain too large to coalesce"); } // Allocate space for the coalesced buffer. // We always convert to an external buffer, even if we happened to be an // internal buffer before. uint8_t* newBuf; SharedInfo* newInfo; uint32_t actualCapacity; allocExtBuffer(newCapacity, &newBuf, &newInfo, &actualCapacity); // Copy the data into the new buffer uint8_t* newData = newBuf + newHeadroom; uint8_t* p = newData; IOBuf* current = this; size_t remaining = newLength; do { assert(current->length_ <= remaining); remaining -= current->length_; memcpy(p, current->data_, current->length_); p += current->length_; current = current->next_; } while (current != end); assert(remaining == 0); // Point at the new buffer decrementRefcount(); // Make sure kFlagUserOwned, kFlagMaybeShared, and kFlagFreeSharedInfo // are all cleared. flags_ = 0; capacity_ = actualCapacity; type_ = kExtAllocated; buf_ = newBuf; sharedInfo_ = newInfo; data_ = newData; length_ = newLength; // Separate from the rest of our chain. // Since we don't store the unique_ptr returned by separateChain(), // this will immediately delete the returned subchain. if (isChained()) { (void)separateChain(next_, current->prev_); } }
void IOBuf::coalesceAndReallocate(size_t newHeadroom, size_t newLength, IOBuf* end, size_t newTailroom) { uint64_t newCapacity = newLength + newHeadroom + newTailroom; // Allocate space for the coalesced buffer. // We always convert to an external buffer, even if we happened to be an // internal buffer before. uint8_t* newBuf; SharedInfo* newInfo; uint64_t actualCapacity; allocExtBuffer(newCapacity, &newBuf, &newInfo, &actualCapacity); // Copy the data into the new buffer uint8_t* newData = newBuf + newHeadroom; uint8_t* p = newData; IOBuf* current = this; size_t remaining = newLength; do { if (current->length_ > 0) { assert(current->length_ <= remaining); assert(current->data_ != nullptr); remaining -= current->length_; memcpy(p, current->data_, current->length_); p += current->length_; } current = current->next_; } while (current != end); assert(remaining == 0); // Point at the new buffer decrementRefcount(); // Make sure kFlagMaybeShared and kFlagFreeSharedInfo are all cleared. setFlagsAndSharedInfo(0, newInfo); capacity_ = actualCapacity; buf_ = newBuf; data_ = newData; length_ = newLength; // Separate from the rest of our chain. // Since we don't store the unique_ptr returned by separateChain(), // this will immediately delete the returned subchain. if (isChained()) { (void)separateChain(next_, current->prev_); } }