xref: /aosp_15_r20/external/pigweed/pw_multibuf/multibuf.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_multibuf/multibuf.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <algorithm>
18*61c4878aSAndroid Build Coastguard Worker #include <cstring>
19*61c4878aSAndroid Build Coastguard Worker 
20*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
21*61c4878aSAndroid Build Coastguard Worker 
22*61c4878aSAndroid Build Coastguard Worker namespace pw::multibuf {
23*61c4878aSAndroid Build Coastguard Worker 
Release()24*61c4878aSAndroid Build Coastguard Worker void MultiBufChunks::Release() noexcept {
25*61c4878aSAndroid Build Coastguard Worker   while (first_ != nullptr) {
26*61c4878aSAndroid Build Coastguard Worker     Chunk* removed = first_;
27*61c4878aSAndroid Build Coastguard Worker     first_ = first_->next_in_buf_;
28*61c4878aSAndroid Build Coastguard Worker     removed->Free();
29*61c4878aSAndroid Build Coastguard Worker   }
30*61c4878aSAndroid Build Coastguard Worker }
31*61c4878aSAndroid Build Coastguard Worker 
size_bytes() const32*61c4878aSAndroid Build Coastguard Worker size_t MultiBufChunks::size_bytes() const {
33*61c4878aSAndroid Build Coastguard Worker   size_t len = 0;
34*61c4878aSAndroid Build Coastguard Worker   for (const auto& chunk : *this) {
35*61c4878aSAndroid Build Coastguard Worker     len += chunk.size();
36*61c4878aSAndroid Build Coastguard Worker   }
37*61c4878aSAndroid Build Coastguard Worker   return len;
38*61c4878aSAndroid Build Coastguard Worker }
39*61c4878aSAndroid Build Coastguard Worker 
empty() const40*61c4878aSAndroid Build Coastguard Worker bool MultiBuf::empty() const {
41*61c4878aSAndroid Build Coastguard Worker   return std::all_of(Chunks().begin(), Chunks().end(), [](const Chunk& c) {
42*61c4878aSAndroid Build Coastguard Worker     return c.empty();
43*61c4878aSAndroid Build Coastguard Worker   });
44*61c4878aSAndroid Build Coastguard Worker }
45*61c4878aSAndroid Build Coastguard Worker 
ContiguousSpan() const46*61c4878aSAndroid Build Coastguard Worker std::optional<ConstByteSpan> MultiBuf::ContiguousSpan() const {
47*61c4878aSAndroid Build Coastguard Worker   ConstByteSpan contiguous;
48*61c4878aSAndroid Build Coastguard Worker   for (const Chunk& chunk : Chunks()) {
49*61c4878aSAndroid Build Coastguard Worker     if (chunk.empty()) {
50*61c4878aSAndroid Build Coastguard Worker       continue;
51*61c4878aSAndroid Build Coastguard Worker     }
52*61c4878aSAndroid Build Coastguard Worker     if (contiguous.empty()) {
53*61c4878aSAndroid Build Coastguard Worker       contiguous = chunk;
54*61c4878aSAndroid Build Coastguard Worker     } else if (contiguous.data() + contiguous.size() == chunk.data()) {
55*61c4878aSAndroid Build Coastguard Worker       contiguous = {contiguous.data(), contiguous.size() + chunk.size()};
56*61c4878aSAndroid Build Coastguard Worker     } else {  // Non-empty chunks are not contiguous
57*61c4878aSAndroid Build Coastguard Worker       return std::nullopt;
58*61c4878aSAndroid Build Coastguard Worker     }
59*61c4878aSAndroid Build Coastguard Worker   }
60*61c4878aSAndroid Build Coastguard Worker   // Return either the one non-empty chunk or an empty span.
61*61c4878aSAndroid Build Coastguard Worker   return contiguous;
62*61c4878aSAndroid Build Coastguard Worker }
63*61c4878aSAndroid Build Coastguard Worker 
ClaimPrefix(size_t bytes_to_claim)64*61c4878aSAndroid Build Coastguard Worker bool MultiBuf::ClaimPrefix(size_t bytes_to_claim) {
65*61c4878aSAndroid Build Coastguard Worker   if (Chunks().empty()) {
66*61c4878aSAndroid Build Coastguard Worker     return false;
67*61c4878aSAndroid Build Coastguard Worker   }
68*61c4878aSAndroid Build Coastguard Worker   return Chunks().front().ClaimPrefix(bytes_to_claim);
69*61c4878aSAndroid Build Coastguard Worker }
70*61c4878aSAndroid Build Coastguard Worker 
ClaimSuffix(size_t bytes_to_claim)71*61c4878aSAndroid Build Coastguard Worker bool MultiBuf::ClaimSuffix(size_t bytes_to_claim) {
72*61c4878aSAndroid Build Coastguard Worker   if (Chunks().empty()) {
73*61c4878aSAndroid Build Coastguard Worker     return false;
74*61c4878aSAndroid Build Coastguard Worker   }
75*61c4878aSAndroid Build Coastguard Worker   return Chunks().back().ClaimSuffix(bytes_to_claim);
76*61c4878aSAndroid Build Coastguard Worker }
77*61c4878aSAndroid Build Coastguard Worker 
DiscardPrefix(size_t bytes_to_discard)78*61c4878aSAndroid Build Coastguard Worker void MultiBuf::DiscardPrefix(size_t bytes_to_discard) {
79*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(bytes_to_discard <= size());
80*61c4878aSAndroid Build Coastguard Worker   while (bytes_to_discard != 0) {
81*61c4878aSAndroid Build Coastguard Worker     if (Chunks().begin()->size() > bytes_to_discard) {
82*61c4878aSAndroid Build Coastguard Worker       Chunks().begin()->DiscardPrefix(bytes_to_discard);
83*61c4878aSAndroid Build Coastguard Worker       return;
84*61c4878aSAndroid Build Coastguard Worker     }
85*61c4878aSAndroid Build Coastguard Worker     OwnedChunk front_chunk = TakeFrontChunk();
86*61c4878aSAndroid Build Coastguard Worker     bytes_to_discard -= front_chunk.size();
87*61c4878aSAndroid Build Coastguard Worker   }
88*61c4878aSAndroid Build Coastguard Worker }
89*61c4878aSAndroid Build Coastguard Worker 
Slice(size_t begin,size_t end)90*61c4878aSAndroid Build Coastguard Worker void MultiBuf::Slice(size_t begin, size_t end) {
91*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(end >= begin);
92*61c4878aSAndroid Build Coastguard Worker   DiscardPrefix(begin);
93*61c4878aSAndroid Build Coastguard Worker   Truncate(end - begin);
94*61c4878aSAndroid Build Coastguard Worker }
95*61c4878aSAndroid Build Coastguard Worker 
Truncate(size_t len)96*61c4878aSAndroid Build Coastguard Worker void MultiBuf::Truncate(size_t len) {
97*61c4878aSAndroid Build Coastguard Worker   if (len == 0) {
98*61c4878aSAndroid Build Coastguard Worker     Release();
99*61c4878aSAndroid Build Coastguard Worker     return;
100*61c4878aSAndroid Build Coastguard Worker   }
101*61c4878aSAndroid Build Coastguard Worker   TruncateAfter(begin() + (len - 1));
102*61c4878aSAndroid Build Coastguard Worker }
103*61c4878aSAndroid Build Coastguard Worker 
TruncateAfter(iterator pos)104*61c4878aSAndroid Build Coastguard Worker void MultiBuf::TruncateAfter(iterator pos) {
105*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(pos != end());
106*61c4878aSAndroid Build Coastguard Worker   pos.chunk()->Truncate(pos.byte_index() + 1);
107*61c4878aSAndroid Build Coastguard Worker   Chunk* remainder = pos.chunk()->next_in_buf_;
108*61c4878aSAndroid Build Coastguard Worker   pos.chunk()->next_in_buf_ = nullptr;
109*61c4878aSAndroid Build Coastguard Worker   MultiBuf discard(remainder);
110*61c4878aSAndroid Build Coastguard Worker }
111*61c4878aSAndroid Build Coastguard Worker 
PushPrefix(MultiBuf && front)112*61c4878aSAndroid Build Coastguard Worker void MultiBuf::PushPrefix(MultiBuf&& front) {
113*61c4878aSAndroid Build Coastguard Worker   front.PushSuffix(std::move(*this));
114*61c4878aSAndroid Build Coastguard Worker   *this = std::move(front);
115*61c4878aSAndroid Build Coastguard Worker }
116*61c4878aSAndroid Build Coastguard Worker 
PushSuffix(MultiBufChunks && tail)117*61c4878aSAndroid Build Coastguard Worker void MultiBufChunks::PushSuffix(MultiBufChunks&& tail) {
118*61c4878aSAndroid Build Coastguard Worker   if (first_ == nullptr) {
119*61c4878aSAndroid Build Coastguard Worker     first_ = tail.first_;
120*61c4878aSAndroid Build Coastguard Worker     tail.first_ = nullptr;
121*61c4878aSAndroid Build Coastguard Worker     return;
122*61c4878aSAndroid Build Coastguard Worker   }
123*61c4878aSAndroid Build Coastguard Worker   back().next_in_buf_ = tail.first_;
124*61c4878aSAndroid Build Coastguard Worker   tail.first_ = nullptr;
125*61c4878aSAndroid Build Coastguard Worker }
126*61c4878aSAndroid Build Coastguard Worker 
CopyTo(ByteSpan dest,const size_t position) const127*61c4878aSAndroid Build Coastguard Worker StatusWithSize MultiBuf::CopyTo(ByteSpan dest, const size_t position) const {
128*61c4878aSAndroid Build Coastguard Worker   const_iterator byte_in_chunk = begin() + position;
129*61c4878aSAndroid Build Coastguard Worker 
130*61c4878aSAndroid Build Coastguard Worker   MultiBufChunks::const_iterator chunk(byte_in_chunk.chunk());
131*61c4878aSAndroid Build Coastguard Worker   size_t chunk_offset = byte_in_chunk.byte_index();
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker   size_t bytes_copied = 0;
134*61c4878aSAndroid Build Coastguard Worker   for (; chunk != Chunks().end(); ++chunk) {
135*61c4878aSAndroid Build Coastguard Worker     const size_t chunk_bytes = chunk->size() - chunk_offset;
136*61c4878aSAndroid Build Coastguard Worker     const size_t to_copy = std::min(chunk_bytes, dest.size() - bytes_copied);
137*61c4878aSAndroid Build Coastguard Worker     if (to_copy != 0u) {
138*61c4878aSAndroid Build Coastguard Worker       std::memcpy(
139*61c4878aSAndroid Build Coastguard Worker           dest.data() + bytes_copied, chunk->data() + chunk_offset, to_copy);
140*61c4878aSAndroid Build Coastguard Worker       bytes_copied += to_copy;
141*61c4878aSAndroid Build Coastguard Worker     }
142*61c4878aSAndroid Build Coastguard Worker 
143*61c4878aSAndroid Build Coastguard Worker     if (chunk_bytes > to_copy) {
144*61c4878aSAndroid Build Coastguard Worker       return StatusWithSize::ResourceExhausted(bytes_copied);
145*61c4878aSAndroid Build Coastguard Worker     }
146*61c4878aSAndroid Build Coastguard Worker     chunk_offset = 0;
147*61c4878aSAndroid Build Coastguard Worker   }
148*61c4878aSAndroid Build Coastguard Worker 
149*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize(bytes_copied);  // all chunks were copied
150*61c4878aSAndroid Build Coastguard Worker }
151*61c4878aSAndroid Build Coastguard Worker 
CopyFromAndOptionallyTruncate(ConstByteSpan source,size_t position,bool truncate)152*61c4878aSAndroid Build Coastguard Worker StatusWithSize MultiBuf::CopyFromAndOptionallyTruncate(ConstByteSpan source,
153*61c4878aSAndroid Build Coastguard Worker                                                        size_t position,
154*61c4878aSAndroid Build Coastguard Worker                                                        bool truncate) {
155*61c4878aSAndroid Build Coastguard Worker   if (source.empty()) {
156*61c4878aSAndroid Build Coastguard Worker     if (truncate) {
157*61c4878aSAndroid Build Coastguard Worker       Truncate(position);
158*61c4878aSAndroid Build Coastguard Worker     }
159*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(0u);
160*61c4878aSAndroid Build Coastguard Worker   }
161*61c4878aSAndroid Build Coastguard Worker 
162*61c4878aSAndroid Build Coastguard Worker   iterator byte_in_chunk = begin() + position;
163*61c4878aSAndroid Build Coastguard Worker   size_t chunk_offset = byte_in_chunk.byte_index();
164*61c4878aSAndroid Build Coastguard Worker 
165*61c4878aSAndroid Build Coastguard Worker   size_t bytes_copied = 0;
166*61c4878aSAndroid Build Coastguard Worker   for (MultiBufChunks::iterator chunk(byte_in_chunk.chunk());
167*61c4878aSAndroid Build Coastguard Worker        chunk != Chunks().end();
168*61c4878aSAndroid Build Coastguard Worker        ++chunk) {
169*61c4878aSAndroid Build Coastguard Worker     if (chunk->empty()) {
170*61c4878aSAndroid Build Coastguard Worker       continue;
171*61c4878aSAndroid Build Coastguard Worker     }
172*61c4878aSAndroid Build Coastguard Worker     const size_t to_copy =
173*61c4878aSAndroid Build Coastguard Worker         std::min(chunk->size() - chunk_offset, source.size() - bytes_copied);
174*61c4878aSAndroid Build Coastguard Worker     std::memcpy(
175*61c4878aSAndroid Build Coastguard Worker         chunk->data() + chunk_offset, source.data() + bytes_copied, to_copy);
176*61c4878aSAndroid Build Coastguard Worker     bytes_copied += to_copy;
177*61c4878aSAndroid Build Coastguard Worker 
178*61c4878aSAndroid Build Coastguard Worker     if (bytes_copied == source.size()) {
179*61c4878aSAndroid Build Coastguard Worker       if (truncate) {
180*61c4878aSAndroid Build Coastguard Worker         // to_copy is always at least one byte, since source.empty() is checked
181*61c4878aSAndroid Build Coastguard Worker         // above and empty chunks are skipped.
182*61c4878aSAndroid Build Coastguard Worker         TruncateAfter(iterator(&*chunk, chunk_offset + to_copy - 1));
183*61c4878aSAndroid Build Coastguard Worker       }
184*61c4878aSAndroid Build Coastguard Worker       return StatusWithSize(bytes_copied);
185*61c4878aSAndroid Build Coastguard Worker     }
186*61c4878aSAndroid Build Coastguard Worker     chunk_offset = 0;
187*61c4878aSAndroid Build Coastguard Worker   }
188*61c4878aSAndroid Build Coastguard Worker 
189*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize::ResourceExhausted(bytes_copied);  // ran out of space
190*61c4878aSAndroid Build Coastguard Worker }
191*61c4878aSAndroid Build Coastguard Worker 
TakePrefix(size_t bytes_to_take)192*61c4878aSAndroid Build Coastguard Worker std::optional<MultiBuf> MultiBuf::TakePrefix(size_t bytes_to_take) {
193*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(bytes_to_take <= size());
194*61c4878aSAndroid Build Coastguard Worker   MultiBuf front;
195*61c4878aSAndroid Build Coastguard Worker   if (bytes_to_take == 0) {
196*61c4878aSAndroid Build Coastguard Worker     return front;
197*61c4878aSAndroid Build Coastguard Worker   }
198*61c4878aSAndroid Build Coastguard Worker   // Pointer to the last element of `front`, allowing constant-time appending.
199*61c4878aSAndroid Build Coastguard Worker   Chunk* last_front_chunk = nullptr;
200*61c4878aSAndroid Build Coastguard Worker   while (bytes_to_take > Chunks().begin()->size()) {
201*61c4878aSAndroid Build Coastguard Worker     OwnedChunk new_chunk = TakeFrontChunk().Take();
202*61c4878aSAndroid Build Coastguard Worker     Chunk* new_chunk_ptr = &*new_chunk;
203*61c4878aSAndroid Build Coastguard Worker     bytes_to_take -= new_chunk.size();
204*61c4878aSAndroid Build Coastguard Worker     if (last_front_chunk == nullptr) {
205*61c4878aSAndroid Build Coastguard Worker       front.PushFrontChunk(std::move(new_chunk));
206*61c4878aSAndroid Build Coastguard Worker     } else {
207*61c4878aSAndroid Build Coastguard Worker       last_front_chunk->next_in_buf_ = std::move(new_chunk).Take();
208*61c4878aSAndroid Build Coastguard Worker     }
209*61c4878aSAndroid Build Coastguard Worker     last_front_chunk = new_chunk_ptr;
210*61c4878aSAndroid Build Coastguard Worker   }
211*61c4878aSAndroid Build Coastguard Worker   if (bytes_to_take == 0) {
212*61c4878aSAndroid Build Coastguard Worker     return front;
213*61c4878aSAndroid Build Coastguard Worker   }
214*61c4878aSAndroid Build Coastguard Worker   std::optional<OwnedChunk> last_front_bit =
215*61c4878aSAndroid Build Coastguard Worker       Chunks().front().TakePrefix(bytes_to_take);
216*61c4878aSAndroid Build Coastguard Worker   if (last_front_bit.has_value()) {
217*61c4878aSAndroid Build Coastguard Worker     if (last_front_chunk != nullptr) {
218*61c4878aSAndroid Build Coastguard Worker       Chunk* last_front_bit_ptr = std::move(*last_front_bit).Take();
219*61c4878aSAndroid Build Coastguard Worker       last_front_chunk->next_in_buf_ = last_front_bit_ptr;
220*61c4878aSAndroid Build Coastguard Worker     } else {
221*61c4878aSAndroid Build Coastguard Worker       front.PushFrontChunk(std::move(*last_front_bit));
222*61c4878aSAndroid Build Coastguard Worker     }
223*61c4878aSAndroid Build Coastguard Worker     return front;
224*61c4878aSAndroid Build Coastguard Worker   }
225*61c4878aSAndroid Build Coastguard Worker   // The front chunk could not be split, so we must put the front back on.
226*61c4878aSAndroid Build Coastguard Worker   PushPrefix(std::move(front));
227*61c4878aSAndroid Build Coastguard Worker   return std::nullopt;
228*61c4878aSAndroid Build Coastguard Worker }
229*61c4878aSAndroid Build Coastguard Worker 
TakeSuffix(size_t bytes_to_take)230*61c4878aSAndroid Build Coastguard Worker std::optional<MultiBuf> MultiBuf::TakeSuffix(size_t bytes_to_take) {
231*61c4878aSAndroid Build Coastguard Worker   size_t front_size = size() - bytes_to_take;
232*61c4878aSAndroid Build Coastguard Worker   std::optional<MultiBuf> front_opt = TakePrefix(front_size);
233*61c4878aSAndroid Build Coastguard Worker   if (!front_opt.has_value()) {
234*61c4878aSAndroid Build Coastguard Worker     return std::nullopt;
235*61c4878aSAndroid Build Coastguard Worker   }
236*61c4878aSAndroid Build Coastguard Worker   MultiBuf front_then_back = std::move(*front_opt);
237*61c4878aSAndroid Build Coastguard Worker   std::swap(front_then_back, *this);
238*61c4878aSAndroid Build Coastguard Worker   return front_then_back;
239*61c4878aSAndroid Build Coastguard Worker }
240*61c4878aSAndroid Build Coastguard Worker 
push_front(OwnedChunk && chunk)241*61c4878aSAndroid Build Coastguard Worker void MultiBufChunks::push_front(OwnedChunk&& chunk) {
242*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(chunk->next_in_buf_ == nullptr);
243*61c4878aSAndroid Build Coastguard Worker   Chunk* new_chunk = std::move(chunk).Take();
244*61c4878aSAndroid Build Coastguard Worker   Chunk* old_first = first_;
245*61c4878aSAndroid Build Coastguard Worker   new_chunk->next_in_buf_ = old_first;
246*61c4878aSAndroid Build Coastguard Worker   first_ = new_chunk;
247*61c4878aSAndroid Build Coastguard Worker }
248*61c4878aSAndroid Build Coastguard Worker 
push_back(OwnedChunk && chunk)249*61c4878aSAndroid Build Coastguard Worker void MultiBufChunks::push_back(OwnedChunk&& chunk) {
250*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(chunk->next_in_buf_ == nullptr);
251*61c4878aSAndroid Build Coastguard Worker   Chunk* new_chunk = std::move(chunk).Take();
252*61c4878aSAndroid Build Coastguard Worker   if (first_ == nullptr) {
253*61c4878aSAndroid Build Coastguard Worker     first_ = new_chunk;
254*61c4878aSAndroid Build Coastguard Worker     return;
255*61c4878aSAndroid Build Coastguard Worker   }
256*61c4878aSAndroid Build Coastguard Worker   Chunk* cur = first_;
257*61c4878aSAndroid Build Coastguard Worker   while (cur->next_in_buf_ != nullptr) {
258*61c4878aSAndroid Build Coastguard Worker     cur = cur->next_in_buf_;
259*61c4878aSAndroid Build Coastguard Worker   }
260*61c4878aSAndroid Build Coastguard Worker   cur->next_in_buf_ = new_chunk;
261*61c4878aSAndroid Build Coastguard Worker }
262*61c4878aSAndroid Build Coastguard Worker 
insert(iterator position,OwnedChunk && chunk)263*61c4878aSAndroid Build Coastguard Worker MultiBufChunks::iterator MultiBufChunks::insert(iterator position,
264*61c4878aSAndroid Build Coastguard Worker                                                 OwnedChunk&& chunk) {
265*61c4878aSAndroid Build Coastguard Worker   // Note: this also catches the cases where ``first_ == nullptr``
266*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(chunk->next_in_buf_ == nullptr);
267*61c4878aSAndroid Build Coastguard Worker   if (position == begin()) {
268*61c4878aSAndroid Build Coastguard Worker     push_front(std::move(chunk));
269*61c4878aSAndroid Build Coastguard Worker     return iterator(first_);
270*61c4878aSAndroid Build Coastguard Worker   }
271*61c4878aSAndroid Build Coastguard Worker   Chunk* previous = Previous(position.chunk_);
272*61c4878aSAndroid Build Coastguard Worker   Chunk* old_next = previous->next_in_buf_;
273*61c4878aSAndroid Build Coastguard Worker   Chunk* new_chunk = std::move(chunk).Take();
274*61c4878aSAndroid Build Coastguard Worker   new_chunk->next_in_buf_ = old_next;
275*61c4878aSAndroid Build Coastguard Worker   previous->next_in_buf_ = new_chunk;
276*61c4878aSAndroid Build Coastguard Worker   return iterator(new_chunk);
277*61c4878aSAndroid Build Coastguard Worker }
278*61c4878aSAndroid Build Coastguard Worker 
take_front()279*61c4878aSAndroid Build Coastguard Worker OwnedChunk MultiBufChunks::take_front() {
280*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(!empty());
281*61c4878aSAndroid Build Coastguard Worker   Chunk* old_first = first_;
282*61c4878aSAndroid Build Coastguard Worker   first_ = old_first->next_in_buf_;
283*61c4878aSAndroid Build Coastguard Worker   old_first->next_in_buf_ = nullptr;
284*61c4878aSAndroid Build Coastguard Worker   return OwnedChunk(old_first);
285*61c4878aSAndroid Build Coastguard Worker }
286*61c4878aSAndroid Build Coastguard Worker 
take(iterator position)287*61c4878aSAndroid Build Coastguard Worker std::tuple<MultiBufChunks::iterator, OwnedChunk> MultiBufChunks::take(
288*61c4878aSAndroid Build Coastguard Worker     iterator position) {
289*61c4878aSAndroid Build Coastguard Worker   Chunk* chunk = position.chunk_;
290*61c4878aSAndroid Build Coastguard Worker   if (position == begin()) {
291*61c4878aSAndroid Build Coastguard Worker     OwnedChunk old_first = take_front();
292*61c4878aSAndroid Build Coastguard Worker     return std::make_tuple(iterator(first_), std::move(old_first));
293*61c4878aSAndroid Build Coastguard Worker   }
294*61c4878aSAndroid Build Coastguard Worker   Chunk* previous = Previous(chunk);
295*61c4878aSAndroid Build Coastguard Worker   previous->next_in_buf_ = chunk->next_in_buf_;
296*61c4878aSAndroid Build Coastguard Worker   chunk->next_in_buf_ = nullptr;
297*61c4878aSAndroid Build Coastguard Worker   return std::make_tuple(iterator(previous->next_in_buf_), OwnedChunk(chunk));
298*61c4878aSAndroid Build Coastguard Worker }
299*61c4878aSAndroid Build Coastguard Worker 
Previous(Chunk * chunk) const300*61c4878aSAndroid Build Coastguard Worker Chunk* MultiBufChunks::Previous(Chunk* chunk) const {
301*61c4878aSAndroid Build Coastguard Worker   Chunk* previous = first_;
302*61c4878aSAndroid Build Coastguard Worker   while (previous != nullptr && previous->next_in_buf_ != chunk) {
303*61c4878aSAndroid Build Coastguard Worker     previous = previous->next_in_buf_;
304*61c4878aSAndroid Build Coastguard Worker   }
305*61c4878aSAndroid Build Coastguard Worker   return previous;
306*61c4878aSAndroid Build Coastguard Worker }
307*61c4878aSAndroid Build Coastguard Worker 
operator ++()308*61c4878aSAndroid Build Coastguard Worker MultiBuf::const_iterator& MultiBuf::const_iterator::operator++() {
309*61c4878aSAndroid Build Coastguard Worker   if (byte_index_ + 1 == chunk_->size()) {
310*61c4878aSAndroid Build Coastguard Worker     chunk_ = chunk_->next_in_buf_;
311*61c4878aSAndroid Build Coastguard Worker     byte_index_ = 0;
312*61c4878aSAndroid Build Coastguard Worker     AdvanceToData();
313*61c4878aSAndroid Build Coastguard Worker   } else {
314*61c4878aSAndroid Build Coastguard Worker     ++byte_index_;
315*61c4878aSAndroid Build Coastguard Worker   }
316*61c4878aSAndroid Build Coastguard Worker   return *this;
317*61c4878aSAndroid Build Coastguard Worker }
318*61c4878aSAndroid Build Coastguard Worker 
operator +=(size_t advance)319*61c4878aSAndroid Build Coastguard Worker MultiBuf::const_iterator& MultiBuf::const_iterator::operator+=(size_t advance) {
320*61c4878aSAndroid Build Coastguard Worker   if (advance == 0) {
321*61c4878aSAndroid Build Coastguard Worker     return *this;
322*61c4878aSAndroid Build Coastguard Worker   }
323*61c4878aSAndroid Build Coastguard Worker 
324*61c4878aSAndroid Build Coastguard Worker   while (chunk_ != nullptr && advance >= (chunk_->size() - byte_index_)) {
325*61c4878aSAndroid Build Coastguard Worker     advance -= (chunk_->size() - byte_index_);
326*61c4878aSAndroid Build Coastguard Worker     chunk_ = chunk_->next_in_buf_;
327*61c4878aSAndroid Build Coastguard Worker     byte_index_ = 0;
328*61c4878aSAndroid Build Coastguard Worker   }
329*61c4878aSAndroid Build Coastguard Worker 
330*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(chunk_ != nullptr || advance == 0u,
331*61c4878aSAndroid Build Coastguard Worker             "Iterated past the end of the MultiBuf");
332*61c4878aSAndroid Build Coastguard Worker   byte_index_ += advance;
333*61c4878aSAndroid Build Coastguard Worker   return *this;
334*61c4878aSAndroid Build Coastguard Worker }
335*61c4878aSAndroid Build Coastguard Worker 
back() const336*61c4878aSAndroid Build Coastguard Worker const Chunk& MultiBufChunks::back() const {
337*61c4878aSAndroid Build Coastguard Worker   Chunk* current = first_;
338*61c4878aSAndroid Build Coastguard Worker   while (current->next_in_buf_ != nullptr) {
339*61c4878aSAndroid Build Coastguard Worker     current = current->next_in_buf_;
340*61c4878aSAndroid Build Coastguard Worker   }
341*61c4878aSAndroid Build Coastguard Worker   return *current;
342*61c4878aSAndroid Build Coastguard Worker }
343*61c4878aSAndroid Build Coastguard Worker 
344*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::multibuf
345