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