1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/websockets/websocket_message_chunk_accumulator.h"
6
7 #include <string.h>
8 #include <algorithm>
9
10 namespace blink {
11
12 constexpr size_t WebSocketMessageChunkAccumulator::kSegmentSize;
13 constexpr base::TimeDelta WebSocketMessageChunkAccumulator::kFreeDelay;
14
WebSocketMessageChunkAccumulator(scoped_refptr<base::SingleThreadTaskRunner> task_runner)15 WebSocketMessageChunkAccumulator::WebSocketMessageChunkAccumulator(
16 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
17 : timer_(std::move(task_runner),
18 this,
19 &WebSocketMessageChunkAccumulator::OnTimerFired) {}
20
21 WebSocketMessageChunkAccumulator::~WebSocketMessageChunkAccumulator() = default;
22
Append(base::span<const char> data)23 void WebSocketMessageChunkAccumulator::Append(base::span<const char> data) {
24 if (!segments_.IsEmpty()) {
25 const size_t to_be_written =
26 std::min(data.size(), kSegmentSize - GetLastSegmentSize());
27 memcpy(segments_.back().get() + GetLastSegmentSize(), data.data(),
28 to_be_written);
29 data = data.subspan(to_be_written);
30 size_ += to_be_written;
31 }
32 while (!data.empty()) {
33 SegmentPtr segment_ptr;
34 if (pool_.IsEmpty()) {
35 segment_ptr = CreateSegment();
36 } else {
37 segment_ptr = std::move(pool_.back());
38 pool_.pop_back();
39 }
40 const size_t to_be_written = std::min(data.size(), kSegmentSize);
41 memcpy(segment_ptr.get(), data.data(), to_be_written);
42 data = data.subspan(to_be_written);
43 size_ += to_be_written;
44 segments_.push_back(std::move(segment_ptr));
45 }
46 }
47
GetView() const48 Vector<base::span<const char>> WebSocketMessageChunkAccumulator::GetView()
49 const {
50 Vector<base::span<const char>> view;
51 if (segments_.IsEmpty()) {
52 return view;
53 }
54
55 view.ReserveCapacity(segments_.size());
56 for (wtf_size_t i = 0; i < segments_.size() - 1; ++i) {
57 view.push_back(base::make_span(segments_[i].get(), kSegmentSize));
58 }
59 view.push_back(base::make_span(segments_.back().get(), GetLastSegmentSize()));
60 return view;
61 }
62
Clear()63 void WebSocketMessageChunkAccumulator::Clear() {
64 num_pooled_segments_to_be_removed_ =
65 std::min(num_pooled_segments_to_be_removed_, pool_.size());
66 size_ = 0;
67 pool_.ReserveCapacity(pool_.size() + segments_.size());
68 for (auto& segment : segments_) {
69 pool_.push_back(std::move(segment));
70 }
71 segments_.clear();
72
73 if (timer_.IsActive()) {
74 return;
75 }
76
77 // We will remove all the segments if no one uses them in the near future.
78 num_pooled_segments_to_be_removed_ = pool_.size();
79 if (num_pooled_segments_to_be_removed_ > 0) {
80 timer_.StartOneShot(kFreeDelay, FROM_HERE);
81 }
82 }
83
Reset()84 void WebSocketMessageChunkAccumulator::Reset() {
85 segments_.clear();
86 pool_.clear();
87 size_ = 0;
88 num_pooled_segments_to_be_removed_ = 0;
89 timer_.Stop();
90 }
91
OnTimerFired(TimerBase *)92 void WebSocketMessageChunkAccumulator::OnTimerFired(TimerBase*) {
93 DCHECK(!timer_.IsActive());
94 const auto to_be_removed =
95 std::min(num_pooled_segments_to_be_removed_, pool_.size());
96 pool_.EraseAt(pool_.size() - to_be_removed, to_be_removed);
97
98 // We will remove all the segments if no one uses them in the near future.
99 num_pooled_segments_to_be_removed_ = pool_.size();
100 if (num_pooled_segments_to_be_removed_ > 0) {
101 timer_.StartOneShot(kFreeDelay, FROM_HERE);
102 }
103 }
104
105 } // namespace blink
106