xref: /aosp_15_r20/external/grpc-grpc/src/core/lib/slice/slice_buffer.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/slice/slice_buffer.h"
22 
23 #include <string.h>
24 
25 #include <utility>
26 
27 #include <grpc/slice.h>
28 #include <grpc/slice_buffer.h>
29 #include <grpc/support/alloc.h>
30 #include <grpc/support/log.h>
31 
32 #include "src/core/lib/slice/slice_internal.h"
33 
34 namespace grpc_core {
35 
Append(Slice slice)36 void SliceBuffer::Append(Slice slice) {
37   grpc_slice_buffer_add(&slice_buffer_, slice.TakeCSlice());
38 }
39 
Append(const SliceBuffer & other)40 void SliceBuffer::Append(const SliceBuffer& other) {
41   for (size_t i = 0; i < other.Count(); i++) {
42     Append(other.RefSlice(i));
43   }
44 }
45 
AppendIndexed(Slice slice)46 size_t SliceBuffer::AppendIndexed(Slice slice) {
47   return grpc_slice_buffer_add_indexed(&slice_buffer_, slice.TakeCSlice());
48 }
49 
TakeFirst()50 Slice SliceBuffer::TakeFirst() {
51   return Slice(grpc_slice_buffer_take_first(&slice_buffer_));
52 }
53 
Prepend(Slice slice)54 void SliceBuffer::Prepend(Slice slice) {
55   grpc_slice_buffer_undo_take_first(&slice_buffer_, slice.TakeCSlice());
56 }
57 
RefSlice(size_t index) const58 Slice SliceBuffer::RefSlice(size_t index) const {
59   return Slice(CSliceRef(slice_buffer_.slices[index]));
60 }
61 
JoinIntoString() const62 std::string SliceBuffer::JoinIntoString() const {
63   std::string result;
64   result.reserve(slice_buffer_.length);
65   for (size_t i = 0; i < slice_buffer_.count; i++) {
66     result.append(reinterpret_cast<const char*>(
67                       GRPC_SLICE_START_PTR(slice_buffer_.slices[i])),
68                   GRPC_SLICE_LENGTH(slice_buffer_.slices[i]));
69   }
70   return result;
71 }
72 
JoinIntoSlice() const73 Slice SliceBuffer::JoinIntoSlice() const {
74   if (slice_buffer_.count == 0) return Slice();
75   if (slice_buffer_.count == 1) return RefSlice(0);
76   grpc_slice slice = GRPC_SLICE_MALLOC(slice_buffer_.length);
77   size_t ofs = 0;
78   for (size_t i = 0; i < slice_buffer_.count; i++) {
79     memcpy(GRPC_SLICE_START_PTR(slice) + ofs,
80            GRPC_SLICE_START_PTR(slice_buffer_.slices[i]),
81            GRPC_SLICE_LENGTH(slice_buffer_.slices[i]));
82     ofs += GRPC_SLICE_LENGTH(slice_buffer_.slices[i]);
83   }
84   GPR_ASSERT(ofs == slice_buffer_.length);
85   return Slice(slice);
86 }
87 
88 }  // namespace grpc_core
89 
90 // grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1
91 #define GROW(x) (3 * (x) / 2)
92 
93 // Typically, we do not actually need to embiggen (by calling
94 // memmove/malloc/realloc) - only if we were up against the full capacity of the
95 // slice buffer. If do_embiggen is inlined, the compiler clobbers multiple
96 // registers pointlessly in the common case.
do_embiggen(grpc_slice_buffer * sb,const size_t slice_count,const size_t slice_offset)97 static void GPR_ATTRIBUTE_NOINLINE do_embiggen(grpc_slice_buffer* sb,
98                                                const size_t slice_count,
99                                                const size_t slice_offset) {
100   if (slice_offset != 0) {
101     // Make room by moving elements if there's still space unused
102     memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice));
103     sb->slices = sb->base_slices;
104   } else {
105     // Allocate more memory if no more space is available
106     const size_t new_capacity = GROW(sb->capacity);
107     sb->capacity = new_capacity;
108     if (sb->base_slices == sb->inlined) {
109       sb->base_slices = static_cast<grpc_slice*>(
110           gpr_malloc(new_capacity * sizeof(grpc_slice)));
111       memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice));
112     } else {
113       sb->base_slices = static_cast<grpc_slice*>(
114           gpr_realloc(sb->base_slices, new_capacity * sizeof(grpc_slice)));
115     }
116 
117     sb->slices = sb->base_slices + slice_offset;
118   }
119 }
120 
maybe_embiggen(grpc_slice_buffer * sb)121 static void maybe_embiggen(grpc_slice_buffer* sb) {
122   if (sb->count == 0) {
123     sb->slices = sb->base_slices;
124     return;
125   }
126 
127   // How far away from sb->base_slices is sb->slices pointer
128   size_t slice_offset = static_cast<size_t>(sb->slices - sb->base_slices);
129   size_t slice_count = sb->count + slice_offset;
130   if (GPR_UNLIKELY(slice_count == sb->capacity)) {
131     do_embiggen(sb, slice_count, slice_offset);
132   }
133 }
134 
grpc_slice_buffer_init(grpc_slice_buffer * sb)135 void grpc_slice_buffer_init(grpc_slice_buffer* sb) {
136   sb->count = 0;
137   sb->length = 0;
138   sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS;
139   sb->base_slices = sb->slices = sb->inlined;
140 }
141 
grpc_slice_buffer_destroy(grpc_slice_buffer * sb)142 void grpc_slice_buffer_destroy(grpc_slice_buffer* sb) {
143   grpc_slice_buffer_reset_and_unref(sb);
144   if (sb->base_slices != sb->inlined) {
145     gpr_free(sb->base_slices);
146     // As a precaution, set sb->base_slices to equal sb->inlined
147     // to prevent a double free attempt if grpc_slice_buffer_destroy
148     // is invoked two times on the same slice buffer.
149     sb->base_slices = sb->slices = sb->inlined;
150   }
151 }
152 
grpc_slice_buffer_tiny_add(grpc_slice_buffer * sb,size_t n)153 uint8_t* grpc_slice_buffer_tiny_add(grpc_slice_buffer* sb, size_t n) {
154   grpc_slice* back;
155   uint8_t* out;
156 
157   sb->length += n;
158 
159   if (sb->count == 0) goto add_first;
160   back = &sb->slices[sb->count - 1];
161   if (back->refcount) goto add_new;
162   if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes)) {
163     goto add_new;
164   }
165   out = back->data.inlined.bytes + back->data.inlined.length;
166   back->data.inlined.length =
167       static_cast<uint8_t>(back->data.inlined.length + n);
168   return out;
169 
170 add_new:
171   maybe_embiggen(sb);
172 add_first:
173   back = &sb->slices[sb->count];
174   sb->count++;
175   back->refcount = nullptr;
176   back->data.inlined.length = static_cast<uint8_t>(n);
177   return back->data.inlined.bytes;
178 }
179 
grpc_slice_buffer_add_indexed(grpc_slice_buffer * sb,grpc_slice s)180 size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer* sb, grpc_slice s) {
181   size_t out = sb->count;
182   maybe_embiggen(sb);
183   sb->slices[out] = s;
184   sb->length += GRPC_SLICE_LENGTH(s);
185   sb->count = out + 1;
186   return out;
187 }
188 
grpc_slice_buffer_add(grpc_slice_buffer * sb,grpc_slice s)189 void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice s) {
190   size_t n = sb->count;
191   grpc_slice* back = nullptr;
192   if (n != 0) {
193     back = &sb->slices[n - 1];
194   }
195   if (s.refcount != nullptr && back != nullptr &&
196       s.refcount == back->refcount &&
197       GRPC_SLICE_START_PTR(s) == GRPC_SLICE_END_PTR(*back)) {
198     // Merge the two slices into one because they are contiguous and share the
199     // same refcount object.
200     back->data.refcounted.length += GRPC_SLICE_LENGTH(s);
201     sb->length += GRPC_SLICE_LENGTH(s);
202     // Unref the merged slice.
203     grpc_core::CSliceUnref(s);
204     // early out
205     return;
206   }
207 
208   if (!s.refcount && n) {
209     // if both the last slice in the slice buffer and the slice being added
210     // are inlined (that is, that they carry their data inside the slice data
211     // structure), and the back slice is not full, then concatenate directly
212     // into the back slice, preventing many small slices being passed into
213     // writes
214     if (!back->refcount &&
215         back->data.inlined.length < GRPC_SLICE_INLINED_SIZE) {
216       if (s.data.inlined.length + back->data.inlined.length <=
217           GRPC_SLICE_INLINED_SIZE) {
218         memcpy(back->data.inlined.bytes + back->data.inlined.length,
219                s.data.inlined.bytes, s.data.inlined.length);
220         back->data.inlined.length = static_cast<uint8_t>(
221             back->data.inlined.length + s.data.inlined.length);
222       } else {
223         size_t cp1 = GRPC_SLICE_INLINED_SIZE - back->data.inlined.length;
224         memcpy(back->data.inlined.bytes + back->data.inlined.length,
225                s.data.inlined.bytes, cp1);
226         back->data.inlined.length = GRPC_SLICE_INLINED_SIZE;
227         maybe_embiggen(sb);
228         back = &sb->slices[n];
229         sb->count = n + 1;
230         back->refcount = nullptr;
231         back->data.inlined.length =
232             static_cast<uint8_t>(s.data.inlined.length - cp1);
233         memcpy(back->data.inlined.bytes, s.data.inlined.bytes + cp1,
234                s.data.inlined.length - cp1);
235       }
236       sb->length += s.data.inlined.length;
237       return;  // early out
238     }
239   }
240   grpc_slice_buffer_add_indexed(sb, s);
241 }
242 
grpc_slice_buffer_addn(grpc_slice_buffer * sb,grpc_slice * s,size_t n)243 void grpc_slice_buffer_addn(grpc_slice_buffer* sb, grpc_slice* s, size_t n) {
244   size_t i;
245   for (i = 0; i < n; i++) {
246     grpc_slice_buffer_add(sb, s[i]);
247   }
248 }
249 
grpc_slice_buffer_pop(grpc_slice_buffer * sb)250 void grpc_slice_buffer_pop(grpc_slice_buffer* sb) {
251   if (sb->count != 0) {
252     size_t count = --sb->count;
253     sb->length -= GRPC_SLICE_LENGTH(sb->slices[count]);
254   }
255 }
256 
grpc_slice_buffer_reset_and_unref(grpc_slice_buffer * sb)257 void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) {
258   size_t i;
259   for (i = 0; i < sb->count; i++) {
260     grpc_core::CSliceUnref(sb->slices[i]);
261   }
262 
263   sb->count = 0;
264   sb->length = 0;
265   sb->slices = sb->base_slices;
266 }
267 
grpc_slice_buffer_swap(grpc_slice_buffer * a,grpc_slice_buffer * b)268 void grpc_slice_buffer_swap(grpc_slice_buffer* a, grpc_slice_buffer* b) {
269   size_t a_offset = static_cast<size_t>(a->slices - a->base_slices);
270   size_t b_offset = static_cast<size_t>(b->slices - b->base_slices);
271 
272   size_t a_count = a->count + a_offset;
273   size_t b_count = b->count + b_offset;
274 
275   if (a->base_slices == a->inlined) {
276     if (b->base_slices == b->inlined) {
277       // swap contents of inlined buffer
278       grpc_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
279       memcpy(temp, a->base_slices, a_count * sizeof(grpc_slice));
280       memcpy(a->base_slices, b->base_slices, b_count * sizeof(grpc_slice));
281       memcpy(b->base_slices, temp, a_count * sizeof(grpc_slice));
282     } else {
283       // a is inlined, b is not - copy a inlined into b, fix pointers
284       a->base_slices = b->base_slices;
285       b->base_slices = b->inlined;
286       memcpy(b->base_slices, a->inlined, a_count * sizeof(grpc_slice));
287     }
288   } else if (b->base_slices == b->inlined) {
289     // b is inlined, a is not - copy b inlined int a, fix pointers
290     b->base_slices = a->base_slices;
291     a->base_slices = a->inlined;
292     memcpy(a->base_slices, b->inlined, b_count * sizeof(grpc_slice));
293   } else {
294     // no inlining: easy swap
295     std::swap(a->base_slices, b->base_slices);
296   }
297 
298   // Update the slices pointers (cannot do a std::swap on slices fields here).
299   // Also note that since the base_slices pointers are already swapped we need
300   // use 'b_offset' for 'a->base_slices' and vice versa
301   a->slices = a->base_slices + b_offset;
302   b->slices = b->base_slices + a_offset;
303 
304   // base_slices and slices fields are correctly set. Swap all other fields
305   std::swap(a->count, b->count);
306   std::swap(a->capacity, b->capacity);
307   std::swap(a->length, b->length);
308 }
309 
grpc_slice_buffer_move_into(grpc_slice_buffer * src,grpc_slice_buffer * dst)310 void grpc_slice_buffer_move_into(grpc_slice_buffer* src,
311                                  grpc_slice_buffer* dst) {
312   // anything to move?
313   if (src->count == 0) {
314     return;
315   }
316   // anything in dst?
317   if (dst->count == 0) {
318     grpc_slice_buffer_swap(src, dst);
319     return;
320   }
321   // both buffers have data - copy, and reset src
322   grpc_slice_buffer_addn(dst, src->slices, src->count);
323   src->count = 0;
324   src->length = 0;
325 }
326 
327 template <bool incref, bool allow_inline>
slice_buffer_move_first_maybe_ref(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)328 static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer* src, size_t n,
329                                               grpc_slice_buffer* dst) {
330   if (n == 0) {
331     return;
332   }
333 
334   GPR_ASSERT(src->length >= n);
335   if (src->length == n) {
336     grpc_slice_buffer_move_into(src, dst);
337     return;
338   }
339 
340   size_t output_len = dst->length + n;
341   size_t new_input_len = src->length - n;
342 
343   while (src->count > 0) {
344     grpc_slice slice = grpc_slice_buffer_take_first(src);
345     size_t slice_len = GRPC_SLICE_LENGTH(slice);
346     if (n > slice_len) {
347       grpc_slice_buffer_add(dst, slice);
348       n -= slice_len;
349     } else if (n == slice_len) {
350       grpc_slice_buffer_add(dst, slice);
351       break;
352     } else if (incref) {  // n < slice_len
353       if (allow_inline) {
354         grpc_slice_buffer_undo_take_first(
355             src,
356             grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_BOTH));
357       } else {
358         grpc_slice_buffer_undo_take_first(
359             src, grpc_slice_split_tail_maybe_ref_no_inline(
360                      &slice, n, GRPC_SLICE_REF_BOTH));
361       }
362       GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n);
363       grpc_slice_buffer_add(dst, slice);
364       break;
365     } else {  // n < slice_len
366       if (allow_inline) {
367         grpc_slice_buffer_undo_take_first(
368             src,
369             grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_TAIL));
370       } else {
371         grpc_slice_buffer_undo_take_first(
372             src, grpc_slice_split_tail_maybe_ref_no_inline(
373                      &slice, n, GRPC_SLICE_REF_TAIL));
374       }
375       GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n);
376       grpc_slice_buffer_add_indexed(dst, slice);
377       break;
378     }
379   }
380   GPR_ASSERT(dst->length == output_len);
381   GPR_ASSERT(src->length == new_input_len);
382   GPR_ASSERT(src->count > 0);
383 }
384 
grpc_slice_buffer_move_first_no_inline(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)385 void grpc_slice_buffer_move_first_no_inline(grpc_slice_buffer* src, size_t n,
386                                             grpc_slice_buffer* dst) {
387   slice_buffer_move_first_maybe_ref<true, false>(src, n, dst);
388 }
389 
grpc_slice_buffer_move_first(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)390 void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n,
391                                   grpc_slice_buffer* dst) {
392   slice_buffer_move_first_maybe_ref<true, true>(src, n, dst);
393 }
394 
grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)395 void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src, size_t n,
396                                          grpc_slice_buffer* dst) {
397   slice_buffer_move_first_maybe_ref<false, true>(src, n, dst);
398 }
399 
grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer * src,size_t n,void * dst)400 void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src, size_t n,
401                                               void* dst) {
402   char* dstp = static_cast<char*>(dst);
403   GPR_ASSERT(src->length >= n);
404 
405   while (n > 0) {
406     grpc_slice slice = grpc_slice_buffer_take_first(src);
407     size_t slice_len = GRPC_SLICE_LENGTH(slice);
408     if (slice_len > n) {
409       memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
410       grpc_slice_buffer_undo_take_first(
411           src, grpc_slice_sub_no_ref(slice, n, slice_len));
412       n = 0;
413     } else if (slice_len == n) {
414       memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
415       grpc_core::CSliceUnref(slice);
416       n = 0;
417     } else {
418       memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len);
419       dstp += slice_len;
420       n -= slice_len;
421       grpc_core::CSliceUnref(slice);
422     }
423   }
424 }
425 
grpc_slice_buffer_copy_first_into_buffer(grpc_slice_buffer * src,size_t n,void * dst)426 void grpc_slice_buffer_copy_first_into_buffer(grpc_slice_buffer* src, size_t n,
427                                               void* dst) {
428   uint8_t* dstp = static_cast<uint8_t*>(dst);
429   GPR_ASSERT(src->length >= n);
430 
431   for (size_t i = 0; i < src->count; i++) {
432     grpc_slice slice = src->slices[i];
433     size_t slice_len = GRPC_SLICE_LENGTH(slice);
434     if (slice_len >= n) {
435       memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
436       return;
437     }
438     memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len);
439     dstp += slice_len;
440     n -= slice_len;
441   }
442 }
443 template <bool allow_inline>
grpc_slice_buffer_trim_end_impl(grpc_slice_buffer * sb,size_t n,grpc_slice_buffer * garbage)444 void grpc_slice_buffer_trim_end_impl(grpc_slice_buffer* sb, size_t n,
445                                      grpc_slice_buffer* garbage) {
446   GPR_ASSERT(n <= sb->length);
447   sb->length -= n;
448   for (;;) {
449     size_t idx = sb->count - 1;
450     grpc_slice slice = sb->slices[idx];
451     size_t slice_len = GRPC_SLICE_LENGTH(slice);
452     if (slice_len > n) {
453       if (allow_inline) {
454         sb->slices[idx] = grpc_slice_split_head(&slice, slice_len - n);
455       } else {
456         sb->slices[idx] =
457             grpc_slice_split_head_no_inline(&slice, slice_len - n);
458       }
459       if (garbage) {
460         grpc_slice_buffer_add_indexed(garbage, slice);
461       } else {
462         grpc_core::CSliceUnref(slice);
463       }
464       return;
465     } else if (slice_len == n) {
466       if (garbage) {
467         grpc_slice_buffer_add_indexed(garbage, slice);
468       } else {
469         grpc_core::CSliceUnref(slice);
470       }
471       sb->count = idx;
472       return;
473     } else {
474       if (garbage) {
475         grpc_slice_buffer_add_indexed(garbage, slice);
476       } else {
477         grpc_core::CSliceUnref(slice);
478       }
479       n -= slice_len;
480       sb->count = idx;
481     }
482   }
483 }
484 
grpc_slice_buffer_trim_end_no_inline(grpc_slice_buffer * sb,size_t n,grpc_slice_buffer * garbage)485 void grpc_slice_buffer_trim_end_no_inline(grpc_slice_buffer* sb, size_t n,
486                                           grpc_slice_buffer* garbage) {
487   return grpc_slice_buffer_trim_end_impl<false>(sb, n, garbage);
488 }
489 
grpc_slice_buffer_trim_end(grpc_slice_buffer * sb,size_t n,grpc_slice_buffer * garbage)490 void grpc_slice_buffer_trim_end(grpc_slice_buffer* sb, size_t n,
491                                 grpc_slice_buffer* garbage) {
492   return grpc_slice_buffer_trim_end_impl<true>(sb, n, garbage);
493 }
494 
grpc_slice_buffer_take_first(grpc_slice_buffer * sb)495 grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer* sb) {
496   grpc_slice slice;
497   GPR_ASSERT(sb->count > 0);
498   slice = sb->slices[0];
499   sb->slices++;
500   sb->count--;
501   sb->length -= GRPC_SLICE_LENGTH(slice);
502 
503   return slice;
504 }
505 
grpc_slice_buffer_remove_first(grpc_slice_buffer * sb)506 void grpc_slice_buffer_remove_first(grpc_slice_buffer* sb) {
507   GPR_DEBUG_ASSERT(sb->count > 0);
508   sb->length -= GRPC_SLICE_LENGTH(sb->slices[0]);
509   grpc_core::CSliceUnref(sb->slices[0]);
510   sb->slices++;
511   if (--sb->count == 0) {
512     sb->slices = sb->base_slices;
513   }
514 }
515 
grpc_slice_buffer_sub_first(grpc_slice_buffer * sb,size_t begin,size_t end)516 void grpc_slice_buffer_sub_first(grpc_slice_buffer* sb, size_t begin,
517                                  size_t end) {
518   // TODO(soheil): Introduce a ptr version for sub.
519   sb->length -= GRPC_SLICE_LENGTH(sb->slices[0]);
520   sb->slices[0] = grpc_slice_sub_no_ref(sb->slices[0], begin, end);
521   sb->length += end - begin;
522 }
523 
grpc_slice_buffer_undo_take_first(grpc_slice_buffer * sb,grpc_slice slice)524 void grpc_slice_buffer_undo_take_first(grpc_slice_buffer* sb,
525                                        grpc_slice slice) {
526   sb->slices--;
527   sb->slices[0] = slice;
528   sb->count++;
529   sb->length += GRPC_SLICE_LENGTH(slice);
530 }
531