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