#include "node_quic_buffer-inl.h" // NOLINT(build/include) #include "node_bob-inl.h" #include "util.h" #include "uv.h" #include #include #include namespace node { namespace quic { void QuicBufferChunk::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("buf", data_buf_); tracker->TrackField("next", next_); } size_t QuicBuffer::Push(uv_buf_t* bufs, size_t nbufs, DoneCB done) { size_t len = 0; if (UNLIKELY(nbufs == 0)) { done(0); return 0; } DCHECK_NOT_NULL(bufs); size_t n = 0; while (nbufs > 1) { if (!is_empty(bufs[n])) { Push(bufs[n]); len += bufs[n].len; } n++; nbufs--; } if (!is_empty(bufs[n])) { Push(bufs[n], done); len += bufs[n].len; } // Special case if all the bufs were empty. if (UNLIKELY(len == 0)) done(0); return len; } void QuicBuffer::Push(std::unique_ptr chunk) { CHECK(!ended_); length_ += chunk->remaining(); remaining_ += chunk->remaining(); if (!tail_) { root_ = std::move(chunk); head_ = tail_ = root_.get(); } else { tail_->next_ = std::move(chunk); tail_ = tail_->next_.get(); if (!head_) head_ = tail_; } } size_t QuicBuffer::Seek(size_t amount) { size_t len = 0; while (head_ && amount > 0) { size_t amt = head_->Seek(amount); amount -= amt; len += amt; remaining_ -= amt; if (head_->remaining()) break; head_ = head_->next_.get(); } return len; } bool QuicBuffer::Pop(int status) { if (!root_) return false; std::unique_ptr root(std::move(root_)); root_ = std::move(root.get()->next_); if (head_ == root.get()) head_ = root_.get(); if (tail_ == root.get()) tail_ = root_.get(); root->Done(status); return true; } size_t QuicBuffer::Consume(int status, size_t amount) { size_t amt = std::min(amount, length_); size_t len = 0; while (root_ && amt > 0) { auto root = root_.get(); size_t consumed = root->Consume(amt); len += consumed; length_ -= consumed; amt -= consumed; if (root->length() > 0) break; Pop(status); } return len; } void QuicBuffer::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("root", root_); } int QuicBuffer::DoPull( bob::Next next, int options, ngtcp2_vec* data, size_t count, size_t max_count_hint) { size_t len = 0; size_t numbytes = 0; int status = bob::Status::STATUS_CONTINUE; // There's no data to read. if (!remaining() || head_ == nullptr) { status = is_ended() ? bob::Status::STATUS_END : bob::Status::STATUS_BLOCK; std::move(next)(status, nullptr, 0, [](size_t len) {}); return status; } // Ensure that there's storage space. MaybeStackBuffer vec; if (data == nullptr || count == 0) { vec.AllocateSufficientStorage(max_count_hint); data = vec.out(); } else { max_count_hint = std::min(count, max_count_hint); } // Build the list of buffers. QuicBufferChunk* pos = head_; while (pos != nullptr && len < max_count_hint) { data[len].base = reinterpret_cast(pos->buf().base); data[len].len = pos->buf().len; numbytes += data[len].len; len++; pos = pos->next_.get(); } // If the buffer is ended, and the number of bytes // matches the total remaining and OPTIONS_END is // used, set the status to STATUS_END. if (is_ended() && numbytes == remaining() && options & bob::OPTIONS_END) status = bob::Status::STATUS_END; // Pass the data back out to the caller. std::move(next)( status, data, len, std::move([this](size_t len) { Seek(len); })); return status; } } // namespace quic } // namespace node