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