Coverage Report

Created: 2025-11-11 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/bufq.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
#include "bufq.h"
27
28
/* The last 2 #include files should be in this order */
29
#include "curl_memory.h"
30
#include "memdebug.h"
31
32
static bool chunk_is_empty(const struct buf_chunk *chunk)
33
66.6M
{
34
66.6M
  return chunk->r_offset >= chunk->w_offset;
35
66.6M
}
36
37
static bool chunk_is_full(const struct buf_chunk *chunk)
38
30.7M
{
39
30.7M
  return chunk->w_offset >= chunk->dlen;
40
30.7M
}
41
42
static size_t chunk_len(const struct buf_chunk *chunk)
43
697M
{
44
697M
  return chunk->w_offset - chunk->r_offset;
45
697M
}
46
47
static void chunk_reset(struct buf_chunk *chunk)
48
16.1M
{
49
16.1M
  chunk->next = NULL;
50
16.1M
  chunk->r_offset = chunk->w_offset = 0;
51
16.1M
}
52
53
static size_t chunk_append(struct buf_chunk *chunk,
54
                           const unsigned char *buf, size_t len)
55
15.2M
{
56
15.2M
  unsigned char *p = &chunk->x.data[chunk->w_offset];
57
15.2M
  size_t n = chunk->dlen - chunk->w_offset;
58
15.2M
  DEBUGASSERT(chunk->dlen >= chunk->w_offset);
59
15.2M
  if(n) {
60
15.2M
    n = CURLMIN(n, len);
61
15.2M
    memcpy(p, buf, n);
62
15.2M
    chunk->w_offset += n;
63
15.2M
  }
64
15.2M
  return n;
65
15.2M
}
66
67
static size_t chunk_read(struct buf_chunk *chunk,
68
                         unsigned char *buf, size_t len)
69
2.12M
{
70
2.12M
  unsigned char *p = &chunk->x.data[chunk->r_offset];
71
2.12M
  size_t n = chunk->w_offset - chunk->r_offset;
72
2.12M
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
73
2.12M
  if(!n) {
74
425
    return 0;
75
425
  }
76
2.12M
  else if(n <= len) {
77
2.12M
    memcpy(buf, p, n);
78
2.12M
    chunk->r_offset = chunk->w_offset = 0;
79
2.12M
    return n;
80
2.12M
  }
81
3.27k
  else {
82
3.27k
    memcpy(buf, p, len);
83
3.27k
    chunk->r_offset += len;
84
3.27k
    return len;
85
3.27k
  }
86
2.12M
}
87
88
static CURLcode chunk_slurpn(struct buf_chunk *chunk, size_t max_len,
89
                             Curl_bufq_reader *reader,
90
                             void *reader_ctx, size_t *pnread)
91
10.4M
{
92
10.4M
  unsigned char *p = &chunk->x.data[chunk->w_offset];
93
10.4M
  size_t n = chunk->dlen - chunk->w_offset; /* free amount */
94
10.4M
  CURLcode result;
95
96
10.4M
  *pnread = 0;
97
10.4M
  DEBUGASSERT(chunk->dlen >= chunk->w_offset);
98
10.4M
  if(!n)
99
0
    return CURLE_AGAIN;
100
10.4M
  if(max_len && n > max_len)
101
3.02k
    n = max_len;
102
10.4M
  result = reader(reader_ctx, p, n, pnread);
103
10.4M
  if(!result) {
104
3.92M
    DEBUGASSERT(*pnread <= n);
105
3.92M
    chunk->w_offset += *pnread;
106
3.92M
  }
107
10.4M
  return result;
108
10.4M
}
109
110
static void chunk_peek(const struct buf_chunk *chunk,
111
                       const unsigned char **pbuf, size_t *plen)
112
3.02M
{
113
3.02M
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
114
3.02M
  *pbuf = &chunk->x.data[chunk->r_offset];
115
3.02M
  *plen = chunk->w_offset - chunk->r_offset;
116
3.02M
}
117
118
static void chunk_peek_at(const struct buf_chunk *chunk, size_t offset,
119
                          const unsigned char **pbuf, size_t *plen)
120
3.29k
{
121
3.29k
  offset += chunk->r_offset;
122
3.29k
  DEBUGASSERT(chunk->w_offset >= offset);
123
3.29k
  *pbuf = &chunk->x.data[offset];
124
3.29k
  *plen = chunk->w_offset - offset;
125
3.29k
}
126
127
static size_t chunk_skip(struct buf_chunk *chunk, size_t amount)
128
4.65M
{
129
4.65M
  size_t n = chunk->w_offset - chunk->r_offset;
130
4.65M
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
131
4.65M
  if(n) {
132
4.64M
    n = CURLMIN(n, amount);
133
4.64M
    chunk->r_offset += n;
134
4.64M
    if(chunk->r_offset == chunk->w_offset)
135
4.64M
      chunk->r_offset = chunk->w_offset = 0;
136
4.64M
  }
137
4.65M
  return n;
138
4.65M
}
139
140
static void chunk_list_free(struct buf_chunk **anchor)
141
419k
{
142
419k
  struct buf_chunk *chunk;
143
697k
  while(*anchor) {
144
278k
    chunk = *anchor;
145
278k
    *anchor = chunk->next;
146
278k
    free(chunk);
147
278k
  }
148
419k
}
149
150
151
void Curl_bufcp_init(struct bufc_pool *pool,
152
                     size_t chunk_size, size_t spare_max)
153
17.9k
{
154
17.9k
  DEBUGASSERT(chunk_size > 0);
155
17.9k
  DEBUGASSERT(spare_max > 0);
156
17.9k
  memset(pool, 0, sizeof(*pool));
157
17.9k
  pool->chunk_size = chunk_size;
158
17.9k
  pool->spare_max = spare_max;
159
17.9k
}
160
161
static CURLcode bufcp_take(struct bufc_pool *pool,
162
                           struct buf_chunk **pchunk)
163
3.19M
{
164
3.19M
  struct buf_chunk *chunk = NULL;
165
166
3.19M
  if(pool->spare) {
167
2.86M
    chunk = pool->spare;
168
2.86M
    pool->spare = chunk->next;
169
2.86M
    --pool->spare_count;
170
2.86M
    chunk_reset(chunk);
171
2.86M
    *pchunk = chunk;
172
2.86M
    return CURLE_OK;
173
2.86M
  }
174
175
  /* Check for integer overflow before allocation */
176
326k
  if(pool->chunk_size > SIZE_MAX - sizeof(*chunk)) {
177
0
    *pchunk = NULL;
178
0
    return CURLE_OUT_OF_MEMORY;
179
0
  }
180
181
326k
  chunk = calloc(1, sizeof(*chunk) + pool->chunk_size);
182
326k
  if(!chunk) {
183
0
    *pchunk = NULL;
184
0
    return CURLE_OUT_OF_MEMORY;
185
0
  }
186
326k
  chunk->dlen = pool->chunk_size;
187
326k
  *pchunk = chunk;
188
326k
  return CURLE_OK;
189
326k
}
190
191
static void bufcp_put(struct bufc_pool *pool,
192
                      struct buf_chunk *chunk)
193
3.07M
{
194
3.07M
  if(pool->spare_count >= pool->spare_max) {
195
169k
    free(chunk);
196
169k
  }
197
2.90M
  else {
198
2.90M
    chunk_reset(chunk);
199
2.90M
    chunk->next = pool->spare;
200
2.90M
    pool->spare = chunk;
201
2.90M
    ++pool->spare_count;
202
2.90M
  }
203
3.07M
}
204
205
void Curl_bufcp_free(struct bufc_pool *pool)
206
17.9k
{
207
17.9k
  chunk_list_free(&pool->spare);
208
17.9k
  pool->spare_count = 0;
209
17.9k
}
210
211
static void bufq_init(struct bufq *q, struct bufc_pool *pool,
212
                      size_t chunk_size, size_t max_chunks, int opts)
213
200k
{
214
200k
  DEBUGASSERT(chunk_size > 0);
215
200k
  DEBUGASSERT(max_chunks > 0);
216
200k
  memset(q, 0, sizeof(*q));
217
200k
  q->chunk_size = chunk_size;
218
200k
  q->max_chunks = max_chunks;
219
200k
  q->pool = pool;
220
200k
  q->opts = opts;
221
200k
}
222
223
void Curl_bufq_init2(struct bufq *q, size_t chunk_size, size_t max_chunks,
224
                     int opts)
225
147k
{
226
147k
  bufq_init(q, NULL, chunk_size, max_chunks, opts);
227
147k
}
228
229
void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks)
230
632
{
231
632
  bufq_init(q, NULL, chunk_size, max_chunks, BUFQ_OPT_NONE);
232
632
}
233
234
void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool,
235
                     size_t max_chunks, int opts)
236
53.0k
{
237
53.0k
  bufq_init(q, pool, pool->chunk_size, max_chunks, opts);
238
53.0k
}
239
240
void Curl_bufq_free(struct bufq *q)
241
200k
{
242
200k
  chunk_list_free(&q->head);
243
200k
  chunk_list_free(&q->spare);
244
200k
  q->tail = NULL;
245
200k
  q->chunk_count = 0;
246
200k
}
247
248
void Curl_bufq_reset(struct bufq *q)
249
2.69M
{
250
2.69M
  struct buf_chunk *chunk;
251
9.47M
  while(q->head) {
252
6.78M
    chunk = q->head;
253
6.78M
    q->head = chunk->next;
254
6.78M
    chunk->next = q->spare;
255
6.78M
    q->spare = chunk;
256
6.78M
  }
257
2.69M
  q->tail = NULL;
258
2.69M
}
259
260
size_t Curl_bufq_len(const struct bufq *q)
261
10.3M
{
262
10.3M
  const struct buf_chunk *chunk = q->head;
263
10.3M
  size_t len = 0;
264
705M
  while(chunk) {
265
695M
    len += chunk_len(chunk);
266
695M
    chunk = chunk->next;
267
695M
  }
268
10.3M
  return len;
269
10.3M
}
270
271
bool Curl_bufq_is_empty(const struct bufq *q)
272
89.9M
{
273
89.9M
  return !q->head || chunk_is_empty(q->head);
274
89.9M
}
275
276
bool Curl_bufq_is_full(const struct bufq *q)
277
266k
{
278
266k
  if(!q->tail || q->spare)
279
34.2k
    return FALSE;
280
232k
  if(q->chunk_count < q->max_chunks)
281
0
    return FALSE;
282
232k
  if(q->chunk_count > q->max_chunks)
283
434
    return TRUE;
284
  /* we have no spares and cannot make more, is the tail full? */
285
231k
  return chunk_is_full(q->tail);
286
232k
}
287
288
static struct buf_chunk *get_spare(struct bufq *q)
289
15.3M
{
290
15.3M
  struct buf_chunk *chunk = NULL;
291
292
15.3M
  if(q->spare) {
293
10.4M
    chunk = q->spare;
294
10.4M
    q->spare = chunk->next;
295
10.4M
    chunk_reset(chunk);
296
10.4M
    return chunk;
297
10.4M
  }
298
299
4.89M
  if(q->chunk_count >= q->max_chunks && (!(q->opts & BUFQ_OPT_SOFT_LIMIT)))
300
1.56M
    return NULL;
301
302
3.33M
  if(q->pool) {
303
3.19M
    if(bufcp_take(q->pool, &chunk))
304
0
      return NULL;
305
3.19M
    ++q->chunk_count;
306
3.19M
    return chunk;
307
3.19M
  }
308
139k
  else {
309
    /* Check for integer overflow before allocation */
310
139k
    if(q->chunk_size > SIZE_MAX - sizeof(*chunk)) {
311
0
      return NULL;
312
0
    }
313
314
139k
    chunk = calloc(1, sizeof(*chunk) + q->chunk_size);
315
139k
    if(!chunk)
316
0
      return NULL;
317
139k
    chunk->dlen = q->chunk_size;
318
139k
    ++q->chunk_count;
319
139k
    return chunk;
320
139k
  }
321
3.33M
}
322
323
static void prune_head(struct bufq *q)
324
6.78M
{
325
6.78M
  struct buf_chunk *chunk;
326
327
13.5M
  while(q->head && chunk_is_empty(q->head)) {
328
6.77M
    chunk = q->head;
329
6.77M
    q->head = chunk->next;
330
6.77M
    if(q->tail == chunk)
331
152k
      q->tail = q->head;
332
6.77M
    if(q->pool) {
333
3.07M
      bufcp_put(q->pool, chunk);
334
3.07M
      --q->chunk_count;
335
3.07M
    }
336
3.69M
    else if((q->chunk_count > q->max_chunks) ||
337
3.67M
       (q->opts & BUFQ_OPT_NO_SPARES)) {
338
      /* SOFT_LIMIT allowed us more than max. free spares until
339
       * we are at max again. Or free them if we are configured
340
       * to not use spares. */
341
17.0k
      free(chunk);
342
17.0k
      --q->chunk_count;
343
17.0k
    }
344
3.67M
    else {
345
3.67M
      chunk->next = q->spare;
346
3.67M
      q->spare = chunk;
347
3.67M
    }
348
6.77M
  }
349
6.78M
}
350
351
static struct buf_chunk *get_non_full_tail(struct bufq *q)
352
27.3M
{
353
27.3M
  struct buf_chunk *chunk;
354
355
27.3M
  if(q->tail && !chunk_is_full(q->tail))
356
12.0M
    return q->tail;
357
15.3M
  chunk = get_spare(q);
358
15.3M
  if(chunk) {
359
    /* new tail, and possibly new head */
360
13.7M
    if(q->tail) {
361
13.5M
      q->tail->next = chunk;
362
13.5M
      q->tail = chunk;
363
13.5M
    }
364
194k
    else {
365
194k
      DEBUGASSERT(!q->head);
366
194k
      q->head = q->tail = chunk;
367
194k
    }
368
13.7M
  }
369
15.3M
  return chunk;
370
15.3M
}
371
372
CURLcode Curl_bufq_write(struct bufq *q,
373
                         const unsigned char *buf, size_t len,
374
                         size_t *pnwritten)
375
15.5M
{
376
15.5M
  struct buf_chunk *tail;
377
15.5M
  size_t n;
378
379
15.5M
  DEBUGASSERT(q->max_chunks > 0);
380
15.5M
  *pnwritten = 0;
381
30.7M
  while(len) {
382
15.9M
    tail = get_non_full_tail(q);
383
15.9M
    if(!tail) {
384
689k
      if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT))
385
        /* should have gotten a tail, but did not */
386
0
        return CURLE_OUT_OF_MEMORY;
387
689k
      break;
388
689k
    }
389
15.2M
    n = chunk_append(tail, buf, len);
390
15.2M
    if(!n)
391
0
      break;
392
15.2M
    *pnwritten += n;
393
15.2M
    buf += n;
394
15.2M
    len -= n;
395
15.2M
  }
396
15.5M
  return (!*pnwritten && len) ? CURLE_AGAIN : CURLE_OK;
397
15.5M
}
398
399
CURLcode Curl_bufq_cwrite(struct bufq *q,
400
                          const char *buf, size_t len,
401
                          size_t *pnwritten)
402
10.6M
{
403
10.6M
  return Curl_bufq_write(q, (const unsigned char *)buf, len, pnwritten);
404
10.6M
}
405
406
CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
407
                        size_t *pnread)
408
251k
{
409
251k
  *pnread = 0;
410
2.37M
  while(len && q->head) {
411
2.12M
    size_t n = chunk_read(q->head, buf, len);
412
2.12M
    if(n) {
413
2.12M
      *pnread += n;
414
2.12M
      buf += n;
415
2.12M
      len -= n;
416
2.12M
    }
417
2.12M
    prune_head(q);
418
2.12M
  }
419
251k
  return (!*pnread) ? CURLE_AGAIN : CURLE_OK;
420
251k
}
421
422
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
423
                         size_t *pnread)
424
5.28k
{
425
5.28k
  return Curl_bufq_read(q, (unsigned char *)buf, len, pnread);
426
5.28k
}
427
428
bool Curl_bufq_peek(struct bufq *q,
429
                    const unsigned char **pbuf, size_t *plen)
430
3.73M
{
431
3.73M
  if(q->head && chunk_is_empty(q->head)) {
432
3.90k
    prune_head(q);
433
3.90k
  }
434
3.73M
  if(q->head && !chunk_is_empty(q->head)) {
435
3.02M
    chunk_peek(q->head, pbuf, plen);
436
3.02M
    return TRUE;
437
3.02M
  }
438
709k
  *pbuf = NULL;
439
709k
  *plen = 0;
440
709k
  return FALSE;
441
3.73M
}
442
443
bool Curl_bufq_peek_at(struct bufq *q, size_t offset,
444
                       const unsigned char **pbuf, size_t *plen)
445
227k
{
446
227k
  struct buf_chunk *c = q->head;
447
227k
  size_t clen;
448
449
2.17M
  while(c) {
450
1.94M
    clen = chunk_len(c);
451
1.94M
    if(!clen)
452
272
      break;
453
1.94M
    if(offset >= clen) {
454
1.94M
      offset -= clen;
455
1.94M
      c = c->next;
456
1.94M
      continue;
457
1.94M
    }
458
3.29k
    chunk_peek_at(c, offset, pbuf, plen);
459
3.29k
    return TRUE;
460
1.94M
  }
461
224k
  *pbuf = NULL;
462
224k
  *plen = 0;
463
224k
  return FALSE;
464
227k
}
465
466
void Curl_bufq_skip(struct bufq *q, size_t amount)
467
3.74M
{
468
3.74M
  size_t n;
469
470
8.39M
  while(amount && q->head) {
471
4.65M
    n = chunk_skip(q->head, amount);
472
4.65M
    amount -= n;
473
4.65M
    prune_head(q);
474
4.65M
  }
475
3.74M
}
476
477
CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
478
                        void *writer_ctx, size_t *pwritten)
479
666k
{
480
666k
  const unsigned char *buf;
481
666k
  size_t blen;
482
666k
  CURLcode result = CURLE_OK;
483
484
666k
  *pwritten = 0;
485
3.03M
  while(Curl_bufq_peek(q, &buf, &blen)) {
486
2.37M
    size_t chunk_written;
487
488
2.37M
    result = writer(writer_ctx, buf, blen, &chunk_written);
489
2.37M
    if(result) {
490
4.90k
      if((result == CURLE_AGAIN) && *pwritten) {
491
        /* blocked on subsequent write, report success */
492
3.62k
        result = CURLE_OK;
493
3.62k
      }
494
4.90k
      break;
495
4.90k
    }
496
2.37M
    if(!chunk_written) {
497
0
      if(!*pwritten) {
498
        /* treat as blocked */
499
0
        result = CURLE_AGAIN;
500
0
      }
501
0
      break;
502
0
    }
503
2.37M
    *pwritten += chunk_written;
504
2.37M
    Curl_bufq_skip(q, chunk_written);
505
2.37M
  }
506
666k
  return result;
507
666k
}
508
509
CURLcode Curl_bufq_write_pass(struct bufq *q,
510
                              const unsigned char *buf, size_t len,
511
                              Curl_bufq_writer *writer, void *writer_ctx,
512
                              size_t *pwritten)
513
51.5k
{
514
51.5k
  CURLcode result = CURLE_OK;
515
51.5k
  size_t n;
516
517
51.5k
  *pwritten = 0;
518
105k
  while(len) {
519
54.0k
    if(Curl_bufq_is_full(q)) {
520
      /* try to make room in case we are full */
521
2.50k
      result = Curl_bufq_pass(q, writer, writer_ctx, &n);
522
2.50k
      if(result) {
523
0
        if(result != CURLE_AGAIN) {
524
          /* real error, fail */
525
0
          return result;
526
0
        }
527
        /* would block, bufq is full, give up */
528
0
        break;
529
0
      }
530
2.50k
    }
531
532
    /* Add to bufq as much as there is room for */
533
54.0k
    result = Curl_bufq_write(q, buf, len, &n);
534
54.0k
    if(result) {
535
0
      if(result != CURLE_AGAIN)
536
        /* real error, fail */
537
0
        return result;
538
      /* result == CURLE_AGAIN */
539
0
      if(*pwritten)
540
        /* we did write successfully before */
541
0
        result = CURLE_OK;
542
0
      return result;
543
0
    }
544
54.0k
    else if(n == 0)
545
      /* edge case of writer returning 0 (and len is >0)
546
       * break or we might enter an infinite loop here */
547
0
      break;
548
549
    /* Track what we added to bufq */
550
54.0k
    buf += n;
551
54.0k
    len -= n;
552
54.0k
    *pwritten += n;
553
54.0k
  }
554
555
51.5k
  return (!*pwritten && len) ? CURLE_AGAIN : CURLE_OK;
556
51.5k
}
557
558
CURLcode Curl_bufq_sipn(struct bufq *q, size_t max_len,
559
                        Curl_bufq_reader *reader, void *reader_ctx,
560
                        size_t *pnread)
561
11.3M
{
562
11.3M
  struct buf_chunk *tail = NULL;
563
564
11.3M
  *pnread = 0;
565
11.3M
  tail = get_non_full_tail(q);
566
11.3M
  if(!tail) {
567
872k
    if(q->chunk_count < q->max_chunks)
568
0
      return CURLE_OUT_OF_MEMORY;
569
    /* full, blocked */
570
872k
    return CURLE_AGAIN;
571
872k
  }
572
573
10.4M
  return chunk_slurpn(tail, max_len, reader, reader_ctx, pnread);
574
11.3M
}
575
576
/**
577
 * Read up to `max_len` bytes and append it to the end of the buffer queue.
578
 * if `max_len` is 0, no limit is imposed and the call behaves exactly
579
 * the same as `Curl_bufq_slurp()`.
580
 * Returns the total amount of buf read (may be 0) in `pnread` or error
581
 * Note that even in case of an error chunks may have been read and
582
 * the buffer queue will have different length than before.
583
 */
584
static CURLcode bufq_slurpn(struct bufq *q, size_t max_len,
585
                            Curl_bufq_reader *reader, void *reader_ctx,
586
                            size_t *pnread)
587
454k
{
588
454k
  CURLcode result;
589
590
454k
  *pnread = 0;
591
3.89M
  while(1) {
592
3.89M
    size_t n;
593
3.89M
    result = Curl_bufq_sipn(q, max_len, reader, reader_ctx, &n);
594
3.89M
    if(result) {
595
450k
      if(!*pnread || result != CURLE_AGAIN) {
596
        /* blocked on first read or real error, fail */
597
440k
        return result;
598
440k
      }
599
9.91k
      result = CURLE_OK;
600
9.91k
      break;
601
450k
    }
602
3.44M
    else if(n == 0) {
603
      /* eof, result remains CURLE_OK */
604
0
      break;
605
0
    }
606
3.44M
    *pnread += n;
607
3.44M
    if(max_len) {
608
0
      DEBUGASSERT(n <= max_len);
609
0
      max_len -= n;
610
0
      if(!max_len)
611
0
        break;
612
0
    }
613
    /* give up slurping when we get less bytes than we asked for */
614
3.44M
    if(q->tail && !chunk_is_full(q->tail))
615
4.01k
      break;
616
3.44M
  }
617
13.9k
  return result;
618
454k
}
619
620
CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
621
                         void *reader_ctx, size_t *pnread)
622
454k
{
623
454k
  return bufq_slurpn(q, 0, reader, reader_ctx, pnread);
624
454k
}