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 <grpc/slice_buffer.h>
22 
23 #include <string.h>
24 
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/log.h>
27 
28 #include "src/core/lib/gpr/useful.h"
29 #include "src/core/lib/iomgr/exec_ctx.h"
30 #include "src/core/lib/slice/slice_internal.h"
31 
32 /* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */
33 #define GROW(x) (3 * (x) / 2)
34 
35 /* Typically, we do not actually need to embiggen (by calling
36  * memmove/malloc/realloc) - only if we were up against the full capacity of the
37  * slice buffer. If do_embiggen is inlined, the compiler clobbers multiple
38  * registers pointlessly in the common case. */
do_embiggen(grpc_slice_buffer * sb,const size_t slice_count,const size_t slice_offset)39 static void GPR_ATTRIBUTE_NOINLINE do_embiggen(grpc_slice_buffer* sb,
40                                                const size_t slice_count,
41                                                const size_t slice_offset) {
42   if (slice_offset != 0) {
43     /* Make room by moving elements if there's still space unused */
44     memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice));
45     sb->slices = sb->base_slices;
46   } else {
47     /* Allocate more memory if no more space is available */
48     const size_t new_capacity = GROW(sb->capacity);
49     sb->capacity = new_capacity;
50     if (sb->base_slices == sb->inlined) {
51       sb->base_slices = static_cast<grpc_slice*>(
52           gpr_malloc(new_capacity * sizeof(grpc_slice)));
53       memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice));
54     } else {
55       sb->base_slices = static_cast<grpc_slice*>(
56           gpr_realloc(sb->base_slices, new_capacity * sizeof(grpc_slice)));
57     }
58 
59     sb->slices = sb->base_slices + slice_offset;
60   }
61 }
62 
maybe_embiggen(grpc_slice_buffer * sb)63 static void maybe_embiggen(grpc_slice_buffer* sb) {
64   if (sb->count == 0) {
65     sb->slices = sb->base_slices;
66     return;
67   }
68 
69   /* How far away from sb->base_slices is sb->slices pointer */
70   size_t slice_offset = static_cast<size_t>(sb->slices - sb->base_slices);
71   size_t slice_count = sb->count + slice_offset;
72   if (GPR_UNLIKELY(slice_count == sb->capacity)) {
73     do_embiggen(sb, slice_count, slice_offset);
74   }
75 }
76 
grpc_slice_buffer_init(grpc_slice_buffer * sb)77 void grpc_slice_buffer_init(grpc_slice_buffer* sb) {
78   sb->count = 0;
79   sb->length = 0;
80   sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS;
81   sb->base_slices = sb->slices = sb->inlined;
82 }
83 
grpc_slice_buffer_destroy_internal(grpc_slice_buffer * sb)84 void grpc_slice_buffer_destroy_internal(grpc_slice_buffer* sb) {
85   grpc_slice_buffer_reset_and_unref_internal(sb);
86   if (sb->base_slices != sb->inlined) {
87     gpr_free(sb->base_slices);
88   }
89 }
90 
grpc_slice_buffer_destroy(grpc_slice_buffer * sb)91 void grpc_slice_buffer_destroy(grpc_slice_buffer* sb) {
92   if (grpc_core::ExecCtx::Get() == nullptr) {
93     grpc_core::ExecCtx exec_ctx;
94     grpc_slice_buffer_destroy_internal(sb);
95   } else {
96     grpc_slice_buffer_destroy_internal(sb);
97   }
98 }
99 
grpc_slice_buffer_tiny_add(grpc_slice_buffer * sb,size_t n)100 uint8_t* grpc_slice_buffer_tiny_add(grpc_slice_buffer* sb, size_t n) {
101   grpc_slice* back;
102   uint8_t* out;
103 
104   sb->length += n;
105 
106   if (sb->count == 0) goto add_first;
107   back = &sb->slices[sb->count - 1];
108   if (back->refcount) goto add_new;
109   if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes)) {
110     goto add_new;
111   }
112   out = back->data.inlined.bytes + back->data.inlined.length;
113   back->data.inlined.length =
114       static_cast<uint8_t>(back->data.inlined.length + n);
115   return out;
116 
117 add_new:
118   maybe_embiggen(sb);
119 add_first:
120   back = &sb->slices[sb->count];
121   sb->count++;
122   back->refcount = nullptr;
123   back->data.inlined.length = static_cast<uint8_t>(n);
124   return back->data.inlined.bytes;
125 }
126 
grpc_slice_buffer_add_indexed(grpc_slice_buffer * sb,grpc_slice s)127 size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer* sb, grpc_slice s) {
128   size_t out = sb->count;
129   maybe_embiggen(sb);
130   sb->slices[out] = s;
131   sb->length += GRPC_SLICE_LENGTH(s);
132   sb->count = out + 1;
133   return out;
134 }
135 
grpc_slice_buffer_add(grpc_slice_buffer * sb,grpc_slice s)136 void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice s) {
137   size_t n = sb->count;
138   /* if both the last slice in the slice buffer and the slice being added
139      are inlined (that is, that they carry their data inside the slice data
140      structure), and the back slice is not full, then concatenate directly
141      into the back slice, preventing many small slices being passed into
142      writes */
143   if (!s.refcount && n) {
144     grpc_slice* back = &sb->slices[n - 1];
145     if (!back->refcount &&
146         back->data.inlined.length < GRPC_SLICE_INLINED_SIZE) {
147       if (s.data.inlined.length + back->data.inlined.length <=
148           GRPC_SLICE_INLINED_SIZE) {
149         memcpy(back->data.inlined.bytes + back->data.inlined.length,
150                s.data.inlined.bytes, s.data.inlined.length);
151         back->data.inlined.length = static_cast<uint8_t>(
152             back->data.inlined.length + s.data.inlined.length);
153       } else {
154         size_t cp1 = GRPC_SLICE_INLINED_SIZE - back->data.inlined.length;
155         memcpy(back->data.inlined.bytes + back->data.inlined.length,
156                s.data.inlined.bytes, cp1);
157         back->data.inlined.length = GRPC_SLICE_INLINED_SIZE;
158         maybe_embiggen(sb);
159         back = &sb->slices[n];
160         sb->count = n + 1;
161         back->refcount = nullptr;
162         back->data.inlined.length =
163             static_cast<uint8_t>(s.data.inlined.length - cp1);
164         memcpy(back->data.inlined.bytes, s.data.inlined.bytes + cp1,
165                s.data.inlined.length - cp1);
166       }
167       sb->length += s.data.inlined.length;
168       return; /* early out */
169     }
170   }
171   grpc_slice_buffer_add_indexed(sb, s);
172 }
173 
grpc_slice_buffer_addn(grpc_slice_buffer * sb,grpc_slice * s,size_t n)174 void grpc_slice_buffer_addn(grpc_slice_buffer* sb, grpc_slice* s, size_t n) {
175   size_t i;
176   for (i = 0; i < n; i++) {
177     grpc_slice_buffer_add(sb, s[i]);
178   }
179 }
180 
grpc_slice_buffer_pop(grpc_slice_buffer * sb)181 void grpc_slice_buffer_pop(grpc_slice_buffer* sb) {
182   if (sb->count != 0) {
183     size_t count = --sb->count;
184     sb->length -= GRPC_SLICE_LENGTH(sb->slices[count]);
185   }
186 }
187 
grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer * sb)188 void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb) {
189   size_t i;
190   for (i = 0; i < sb->count; i++) {
191     grpc_slice_unref_internal(sb->slices[i]);
192   }
193 
194   sb->count = 0;
195   sb->length = 0;
196   sb->slices = sb->base_slices;
197 }
198 
grpc_slice_buffer_reset_and_unref(grpc_slice_buffer * sb)199 void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) {
200   if (grpc_core::ExecCtx::Get() == nullptr) {
201     grpc_core::ExecCtx exec_ctx;
202     grpc_slice_buffer_reset_and_unref_internal(sb);
203   } else {
204     grpc_slice_buffer_reset_and_unref_internal(sb);
205   }
206 }
207 
grpc_slice_buffer_swap(grpc_slice_buffer * a,grpc_slice_buffer * b)208 void grpc_slice_buffer_swap(grpc_slice_buffer* a, grpc_slice_buffer* b) {
209   size_t a_offset = static_cast<size_t>(a->slices - a->base_slices);
210   size_t b_offset = static_cast<size_t>(b->slices - b->base_slices);
211 
212   size_t a_count = a->count + a_offset;
213   size_t b_count = b->count + b_offset;
214 
215   if (a->base_slices == a->inlined) {
216     if (b->base_slices == b->inlined) {
217       /* swap contents of inlined buffer */
218       grpc_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
219       memcpy(temp, a->base_slices, a_count * sizeof(grpc_slice));
220       memcpy(a->base_slices, b->base_slices, b_count * sizeof(grpc_slice));
221       memcpy(b->base_slices, temp, a_count * sizeof(grpc_slice));
222     } else {
223       /* a is inlined, b is not - copy a inlined into b, fix pointers */
224       a->base_slices = b->base_slices;
225       b->base_slices = b->inlined;
226       memcpy(b->base_slices, a->inlined, a_count * sizeof(grpc_slice));
227     }
228   } else if (b->base_slices == b->inlined) {
229     /* b is inlined, a is not - copy b inlined int a, fix pointers */
230     b->base_slices = a->base_slices;
231     a->base_slices = a->inlined;
232     memcpy(a->base_slices, b->inlined, b_count * sizeof(grpc_slice));
233   } else {
234     /* no inlining: easy swap */
235     GPR_SWAP(grpc_slice*, a->base_slices, b->base_slices);
236   }
237 
238   /* Update the slices pointers (cannot do a GPR_SWAP on slices fields here).
239    * Also note that since the base_slices pointers are already swapped we need
240    * use 'b_offset' for 'a->base_slices' and vice versa */
241   a->slices = a->base_slices + b_offset;
242   b->slices = b->base_slices + a_offset;
243 
244   /* base_slices and slices fields are correctly set. Swap all other fields */
245   GPR_SWAP(size_t, a->count, b->count);
246   GPR_SWAP(size_t, a->capacity, b->capacity);
247   GPR_SWAP(size_t, a->length, b->length);
248 }
249 
grpc_slice_buffer_move_into(grpc_slice_buffer * src,grpc_slice_buffer * dst)250 void grpc_slice_buffer_move_into(grpc_slice_buffer* src,
251                                  grpc_slice_buffer* dst) {
252   /* anything to move? */
253   if (src->count == 0) {
254     return;
255   }
256   /* anything in dst? */
257   if (dst->count == 0) {
258     grpc_slice_buffer_swap(src, dst);
259     return;
260   }
261   /* both buffers have data - copy, and reset src */
262   grpc_slice_buffer_addn(dst, src->slices, src->count);
263   src->count = 0;
264   src->length = 0;
265 }
266 
267 template <bool incref>
slice_buffer_move_first_maybe_ref(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)268 static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer* src, size_t n,
269                                               grpc_slice_buffer* dst) {
270   GPR_ASSERT(src->length >= n);
271   if (src->length == n) {
272     grpc_slice_buffer_move_into(src, dst);
273     return;
274   }
275 
276   size_t output_len = dst->length + n;
277   size_t new_input_len = src->length - n;
278 
279   while (src->count > 0) {
280     grpc_slice slice = grpc_slice_buffer_take_first(src);
281     size_t slice_len = GRPC_SLICE_LENGTH(slice);
282     if (n > slice_len) {
283       grpc_slice_buffer_add(dst, slice);
284       n -= slice_len;
285     } else if (n == slice_len) {
286       grpc_slice_buffer_add(dst, slice);
287       break;
288     } else if (incref) { /* n < slice_len */
289       grpc_slice_buffer_undo_take_first(
290           src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_BOTH));
291       GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n);
292       grpc_slice_buffer_add(dst, slice);
293       break;
294     } else { /* n < slice_len */
295       grpc_slice_buffer_undo_take_first(
296           src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_TAIL));
297       GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n);
298       grpc_slice_buffer_add_indexed(dst, slice);
299       break;
300     }
301   }
302   GPR_ASSERT(dst->length == output_len);
303   GPR_ASSERT(src->length == new_input_len);
304   GPR_ASSERT(src->count > 0);
305 }
306 
grpc_slice_buffer_move_first(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)307 void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n,
308                                   grpc_slice_buffer* dst) {
309   slice_buffer_move_first_maybe_ref<true>(src, n, dst);
310 }
311 
grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer * src,size_t n,grpc_slice_buffer * dst)312 void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src, size_t n,
313                                          grpc_slice_buffer* dst) {
314   slice_buffer_move_first_maybe_ref<false>(src, n, dst);
315 }
316 
grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer * src,size_t n,void * dst)317 void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src, size_t n,
318                                               void* dst) {
319   char* dstp = static_cast<char*>(dst);
320   GPR_ASSERT(src->length >= n);
321 
322   while (n > 0) {
323     grpc_slice slice = grpc_slice_buffer_take_first(src);
324     size_t slice_len = GRPC_SLICE_LENGTH(slice);
325     if (slice_len > n) {
326       memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
327       grpc_slice_buffer_undo_take_first(
328           src, grpc_slice_sub_no_ref(slice, n, slice_len));
329       n = 0;
330     } else if (slice_len == n) {
331       memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
332       grpc_slice_unref_internal(slice);
333       n = 0;
334     } else {
335       memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len);
336       dstp += slice_len;
337       n -= slice_len;
338       grpc_slice_unref_internal(slice);
339     }
340   }
341 }
342 
grpc_slice_buffer_trim_end(grpc_slice_buffer * sb,size_t n,grpc_slice_buffer * garbage)343 void grpc_slice_buffer_trim_end(grpc_slice_buffer* sb, size_t n,
344                                 grpc_slice_buffer* garbage) {
345   GPR_ASSERT(n <= sb->length);
346   sb->length -= n;
347   for (;;) {
348     size_t idx = sb->count - 1;
349     grpc_slice slice = sb->slices[idx];
350     size_t slice_len = GRPC_SLICE_LENGTH(slice);
351     if (slice_len > n) {
352       sb->slices[idx] = grpc_slice_split_head(&slice, slice_len - n);
353       if (garbage) {
354         grpc_slice_buffer_add_indexed(garbage, slice);
355       } else {
356         grpc_slice_unref_internal(slice);
357       }
358       return;
359     } else if (slice_len == n) {
360       if (garbage) {
361         grpc_slice_buffer_add_indexed(garbage, slice);
362       } else {
363         grpc_slice_unref_internal(slice);
364       }
365       sb->count = idx;
366       return;
367     } else {
368       if (garbage) {
369         grpc_slice_buffer_add_indexed(garbage, slice);
370       } else {
371         grpc_slice_unref_internal(slice);
372       }
373       n -= slice_len;
374       sb->count = idx;
375     }
376   }
377 }
378 
grpc_slice_buffer_take_first(grpc_slice_buffer * sb)379 grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer* sb) {
380   grpc_slice slice;
381   GPR_ASSERT(sb->count > 0);
382   slice = sb->slices[0];
383   sb->slices++;
384   sb->count--;
385   sb->length -= GRPC_SLICE_LENGTH(slice);
386 
387   return slice;
388 }
389 
grpc_slice_buffer_remove_first(grpc_slice_buffer * sb)390 void grpc_slice_buffer_remove_first(grpc_slice_buffer* sb) {
391   GPR_DEBUG_ASSERT(sb->count > 0);
392   sb->length -= GRPC_SLICE_LENGTH(sb->slices[0]);
393   grpc_slice_unref_internal(sb->slices[0]);
394   sb->slices++;
395   if (--sb->count == 0) {
396     sb->slices = sb->base_slices;
397   }
398 }
399 
grpc_slice_buffer_sub_first(grpc_slice_buffer * sb,size_t begin,size_t end)400 void grpc_slice_buffer_sub_first(grpc_slice_buffer* sb, size_t begin,
401                                  size_t end) {
402   // TODO(soheil): Introduce a ptr version for sub.
403   sb->length -= GRPC_SLICE_LENGTH(sb->slices[0]);
404   sb->slices[0] = grpc_slice_sub_no_ref(sb->slices[0], begin, end);
405   sb->length += end - begin;
406 }
407 
grpc_slice_buffer_undo_take_first(grpc_slice_buffer * sb,grpc_slice slice)408 void grpc_slice_buffer_undo_take_first(grpc_slice_buffer* sb,
409                                        grpc_slice slice) {
410   sb->slices--;
411   sb->slices[0] = slice;
412   sb->count++;
413   sb->length += GRPC_SLICE_LENGTH(slice);
414 }
415