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