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