blob: ebe230271ac8465248fd0d5175ca8a6d59e2b75f [file] [edit]
#include "node_quic_buffer-inl.h" // NOLINT(build/include)
#include "node_bob-inl.h"
#include "util.h"
#include "uv.h"
#include <algorithm>
#include <memory>
#include <utility>
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<QuicBufferChunk> 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<QuicBufferChunk> 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<ngtcp2_vec> 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<ngtcp2_vec, kMaxVectorCount> 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<uint8_t*>(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,
[this](size_t len) { Seek(len); });
return status;
}
} // namespace quic
} // namespace node