Coverage Report

Created: 2024-09-08 06:32

/src/curl/lib/bufq.c
Line
Count
Source (jump to first uncovered line)
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 3 #include files should be in this order */
29
#include "curl_printf.h"
30
#include "curl_memory.h"
31
#include "memdebug.h"
32
33
static bool chunk_is_empty(const struct buf_chunk *chunk)
34
75.5M
{
35
75.5M
  return chunk->r_offset >= chunk->w_offset;
36
75.5M
}
37
38
static bool chunk_is_full(const struct buf_chunk *chunk)
39
34.4M
{
40
34.4M
  return chunk->w_offset >= chunk->dlen;
41
34.4M
}
42
43
static size_t chunk_len(const struct buf_chunk *chunk)
44
727M
{
45
727M
  return chunk->w_offset - chunk->r_offset;
46
727M
}
47
48
static size_t chunk_space(const struct buf_chunk *chunk)
49
0
{
50
0
  return chunk->dlen - chunk->w_offset;
51
0
}
52
53
static void chunk_reset(struct buf_chunk *chunk)
54
18.3M
{
55
18.3M
  chunk->next = NULL;
56
18.3M
  chunk->r_offset = chunk->w_offset = 0;
57
18.3M
}
58
59
static size_t chunk_append(struct buf_chunk *chunk,
60
                           const unsigned char *buf, size_t len)
61
12.4M
{
62
12.4M
  unsigned char *p = &chunk->x.data[chunk->w_offset];
63
12.4M
  size_t n = chunk->dlen - chunk->w_offset;
64
12.4M
  DEBUGASSERT(chunk->dlen >= chunk->w_offset);
65
12.4M
  if(n) {
66
12.4M
    n = CURLMIN(n, len);
67
12.4M
    memcpy(p, buf, n);
68
12.4M
    chunk->w_offset += n;
69
12.4M
  }
70
12.4M
  return n;
71
12.4M
}
72
73
static size_t chunk_read(struct buf_chunk *chunk,
74
                         unsigned char *buf, size_t len)
75
2.30M
{
76
2.30M
  unsigned char *p = &chunk->x.data[chunk->r_offset];
77
2.30M
  size_t n = chunk->w_offset - chunk->r_offset;
78
2.30M
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
79
2.30M
  if(!n) {
80
1.46k
    return 0;
81
1.46k
  }
82
2.30M
  else if(n <= len) {
83
2.29M
    memcpy(buf, p, n);
84
2.29M
    chunk->r_offset = chunk->w_offset = 0;
85
2.29M
    return n;
86
2.29M
  }
87
2.52k
  else {
88
2.52k
    memcpy(buf, p, len);
89
2.52k
    chunk->r_offset += len;
90
2.52k
    return len;
91
2.52k
  }
92
2.30M
}
93
94
static size_t chunk_unwrite(struct buf_chunk *chunk, size_t len)
95
0
{
96
0
  size_t n = chunk->w_offset - chunk->r_offset;
97
0
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
98
0
  if(!n) {
99
0
    return 0;
100
0
  }
101
0
  else if(n <= len) {
102
0
    chunk->r_offset = chunk->w_offset = 0;
103
0
    return n;
104
0
  }
105
0
  else {
106
0
    chunk->w_offset -= len;
107
0
    return len;
108
0
  }
109
0
}
110
111
static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len,
112
                            Curl_bufq_reader *reader,
113
                            void *reader_ctx, CURLcode *err)
114
14.0M
{
115
14.0M
  unsigned char *p = &chunk->x.data[chunk->w_offset];
116
14.0M
  size_t n = chunk->dlen - chunk->w_offset; /* free amount */
117
14.0M
  ssize_t nread;
118
119
14.0M
  DEBUGASSERT(chunk->dlen >= chunk->w_offset);
120
14.0M
  if(!n) {
121
0
    *err = CURLE_AGAIN;
122
0
    return -1;
123
0
  }
124
14.0M
  if(max_len && n > max_len)
125
474
    n = max_len;
126
14.0M
  nread = reader(reader_ctx, p, n, err);
127
14.0M
  if(nread > 0) {
128
3.98M
    DEBUGASSERT((size_t)nread <= n);
129
3.98M
    chunk->w_offset += nread;
130
3.98M
  }
131
14.0M
  return nread;
132
14.0M
}
133
134
static void chunk_peek(const struct buf_chunk *chunk,
135
                       const unsigned char **pbuf, size_t *plen)
136
2.96M
{
137
2.96M
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
138
2.96M
  *pbuf = &chunk->x.data[chunk->r_offset];
139
2.96M
  *plen = chunk->w_offset - chunk->r_offset;
140
2.96M
}
141
142
static void chunk_peek_at(const struct buf_chunk *chunk, size_t offset,
143
                          const unsigned char **pbuf, size_t *plen)
144
2.39k
{
145
2.39k
  offset += chunk->r_offset;
146
2.39k
  DEBUGASSERT(chunk->w_offset >= offset);
147
2.39k
  *pbuf = &chunk->x.data[offset];
148
2.39k
  *plen = chunk->w_offset - offset;
149
2.39k
}
150
151
static size_t chunk_skip(struct buf_chunk *chunk, size_t amount)
152
6.37M
{
153
6.37M
  size_t n = chunk->w_offset - chunk->r_offset;
154
6.37M
  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
155
6.37M
  if(n) {
156
6.37M
    n = CURLMIN(n, amount);
157
6.37M
    chunk->r_offset += n;
158
6.37M
    if(chunk->r_offset == chunk->w_offset)
159
6.36M
      chunk->r_offset = chunk->w_offset = 0;
160
6.37M
  }
161
6.37M
  return n;
162
6.37M
}
163
164
static void chunk_list_free(struct buf_chunk **anchor)
165
149k
{
166
149k
  struct buf_chunk *chunk;
167
454k
  while(*anchor) {
168
304k
    chunk = *anchor;
169
304k
    *anchor = chunk->next;
170
304k
    free(chunk);
171
304k
  }
172
149k
}
173
174
175
176
void Curl_bufcp_init(struct bufc_pool *pool,
177
                     size_t chunk_size, size_t spare_max)
178
8.87k
{
179
8.87k
  DEBUGASSERT(chunk_size > 0);
180
8.87k
  DEBUGASSERT(spare_max > 0);
181
8.87k
  memset(pool, 0, sizeof(*pool));
182
8.87k
  pool->chunk_size = chunk_size;
183
8.87k
  pool->spare_max = spare_max;
184
8.87k
}
185
186
static CURLcode bufcp_take(struct bufc_pool *pool,
187
                           struct buf_chunk **pchunk)
188
2.96M
{
189
2.96M
  struct buf_chunk *chunk = NULL;
190
191
2.96M
  if(pool->spare) {
192
2.64M
    chunk = pool->spare;
193
2.64M
    pool->spare = chunk->next;
194
2.64M
    --pool->spare_count;
195
2.64M
    chunk_reset(chunk);
196
2.64M
    *pchunk = chunk;
197
2.64M
    return CURLE_OK;
198
2.64M
  }
199
200
325k
  chunk = calloc(1, sizeof(*chunk) + pool->chunk_size);
201
325k
  if(!chunk) {
202
0
    *pchunk = NULL;
203
0
    return CURLE_OUT_OF_MEMORY;
204
0
  }
205
325k
  chunk->dlen = pool->chunk_size;
206
325k
  *pchunk = chunk;
207
325k
  return CURLE_OK;
208
325k
}
209
210
static void bufcp_put(struct bufc_pool *pool,
211
                      struct buf_chunk *chunk)
212
2.84M
{
213
2.84M
  if(pool->spare_count >= pool->spare_max) {
214
175k
    free(chunk);
215
175k
  }
216
2.66M
  else {
217
2.66M
    chunk_reset(chunk);
218
2.66M
    chunk->next = pool->spare;
219
2.66M
    pool->spare = chunk;
220
2.66M
    ++pool->spare_count;
221
2.66M
  }
222
2.84M
}
223
224
void Curl_bufcp_free(struct bufc_pool *pool)
225
8.87k
{
226
8.87k
  chunk_list_free(&pool->spare);
227
8.87k
  pool->spare_count = 0;
228
8.87k
}
229
230
static void bufq_init(struct bufq *q, struct bufc_pool *pool,
231
                      size_t chunk_size, size_t max_chunks, int opts)
232
70.5k
{
233
70.5k
  DEBUGASSERT(chunk_size > 0);
234
70.5k
  DEBUGASSERT(max_chunks > 0);
235
70.5k
  memset(q, 0, sizeof(*q));
236
70.5k
  q->chunk_size = chunk_size;
237
70.5k
  q->max_chunks = max_chunks;
238
70.5k
  q->pool = pool;
239
70.5k
  q->opts = opts;
240
70.5k
}
241
242
void Curl_bufq_init2(struct bufq *q, size_t chunk_size, size_t max_chunks,
243
                     int opts)
244
43.8k
{
245
43.8k
  bufq_init(q, NULL, chunk_size, max_chunks, opts);
246
43.8k
}
247
248
void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks)
249
675
{
250
675
  bufq_init(q, NULL, chunk_size, max_chunks, BUFQ_OPT_NONE);
251
675
}
252
253
void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool,
254
                     size_t max_chunks, int opts)
255
26.0k
{
256
26.0k
  bufq_init(q, pool, pool->chunk_size, max_chunks, opts);
257
26.0k
}
258
259
void Curl_bufq_free(struct bufq *q)
260
70.5k
{
261
70.5k
  chunk_list_free(&q->head);
262
70.5k
  chunk_list_free(&q->spare);
263
70.5k
  q->tail = NULL;
264
70.5k
  q->chunk_count = 0;
265
70.5k
}
266
267
void Curl_bufq_reset(struct bufq *q)
268
532k
{
269
532k
  struct buf_chunk *chunk;
270
7.82M
  while(q->head) {
271
7.29M
    chunk = q->head;
272
7.29M
    q->head = chunk->next;
273
7.29M
    chunk->next = q->spare;
274
7.29M
    q->spare = chunk;
275
7.29M
  }
276
532k
  q->tail = NULL;
277
532k
}
278
279
size_t Curl_bufq_len(const struct bufq *q)
280
7.78M
{
281
7.78M
  const struct buf_chunk *chunk = q->head;
282
7.78M
  size_t len = 0;
283
733M
  while(chunk) {
284
725M
    len += chunk_len(chunk);
285
725M
    chunk = chunk->next;
286
725M
  }
287
7.78M
  return len;
288
7.78M
}
289
290
size_t Curl_bufq_space(const struct bufq *q)
291
0
{
292
0
  size_t space = 0;
293
0
  if(q->tail)
294
0
    space += chunk_space(q->tail);
295
0
  if(q->spare) {
296
0
    struct buf_chunk *chunk = q->spare;
297
0
    while(chunk) {
298
0
      space += chunk->dlen;
299
0
      chunk = chunk->next;
300
0
    }
301
0
  }
302
0
  if(q->chunk_count < q->max_chunks) {
303
0
    space += (q->max_chunks - q->chunk_count) * q->chunk_size;
304
0
  }
305
0
  return space;
306
0
}
307
308
bool Curl_bufq_is_empty(const struct bufq *q)
309
96.3M
{
310
96.3M
  return !q->head || chunk_is_empty(q->head);
311
96.3M
}
312
313
bool Curl_bufq_is_full(const struct bufq *q)
314
3.39M
{
315
3.39M
  if(!q->tail || q->spare)
316
29.5k
    return FALSE;
317
3.36M
  if(q->chunk_count < q->max_chunks)
318
0
    return FALSE;
319
3.36M
  if(q->chunk_count > q->max_chunks)
320
107
    return TRUE;
321
  /* we have no spares and cannot make more, is the tail full? */
322
3.36M
  return chunk_is_full(q->tail);
323
3.36M
}
324
325
static struct buf_chunk *get_spare(struct bufq *q)
326
17.2M
{
327
17.2M
  struct buf_chunk *chunk = NULL;
328
329
17.2M
  if(q->spare) {
330
13.0M
    chunk = q->spare;
331
13.0M
    q->spare = chunk->next;
332
13.0M
    chunk_reset(chunk);
333
13.0M
    return chunk;
334
13.0M
  }
335
336
4.23M
  if(q->chunk_count >= q->max_chunks && (!(q->opts & BUFQ_OPT_SOFT_LIMIT)))
337
1.10M
    return NULL;
338
339
3.12M
  if(q->pool) {
340
2.96M
    if(bufcp_take(q->pool, &chunk))
341
0
      return NULL;
342
2.96M
    ++q->chunk_count;
343
2.96M
    return chunk;
344
2.96M
  }
345
157k
  else {
346
157k
    chunk = calloc(1, sizeof(*chunk) + q->chunk_size);
347
157k
    if(!chunk)
348
0
      return NULL;
349
157k
    chunk->dlen = q->chunk_size;
350
157k
    ++q->chunk_count;
351
157k
    return chunk;
352
157k
  }
353
3.12M
}
354
355
static void prune_head(struct bufq *q)
356
8.67M
{
357
8.67M
  struct buf_chunk *chunk;
358
359
17.3M
  while(q->head && chunk_is_empty(q->head)) {
360
8.67M
    chunk = q->head;
361
8.67M
    q->head = chunk->next;
362
8.67M
    if(q->tail == chunk)
363
97.0k
      q->tail = q->head;
364
8.67M
    if(q->pool) {
365
2.84M
      bufcp_put(q->pool, chunk);
366
2.84M
      --q->chunk_count;
367
2.84M
    }
368
5.82M
    else if((q->chunk_count > q->max_chunks) ||
369
5.82M
       (q->opts & BUFQ_OPT_NO_SPARES)) {
370
      /* SOFT_LIMIT allowed us more than max. free spares until
371
       * we are at max again. Or free them if we are configured
372
       * to not use spares. */
373
1.77k
      free(chunk);
374
1.77k
      --q->chunk_count;
375
1.77k
    }
376
5.82M
    else {
377
5.82M
      chunk->next = q->spare;
378
5.82M
      q->spare = chunk;
379
5.82M
    }
380
8.67M
  }
381
8.67M
}
382
383
static struct buf_chunk *chunk_prev(struct buf_chunk *head,
384
                                    struct buf_chunk *chunk)
385
0
{
386
0
  while(head) {
387
0
    if(head == chunk)
388
0
      return NULL;
389
0
    if(head->next == chunk)
390
0
      return head;
391
0
    head = head->next;
392
0
  }
393
0
  return NULL;
394
0
}
395
396
static void prune_tail(struct bufq *q)
397
0
{
398
0
  struct buf_chunk *chunk;
399
400
0
  while(q->tail && chunk_is_empty(q->tail)) {
401
0
    chunk = q->tail;
402
0
    q->tail = chunk_prev(q->head, chunk);
403
0
    if(q->tail)
404
0
      q->tail->next = NULL;
405
0
    if(q->head == chunk)
406
0
      q->head = q->tail;
407
0
    if(q->pool) {
408
0
      bufcp_put(q->pool, chunk);
409
0
      --q->chunk_count;
410
0
    }
411
0
    else if((q->chunk_count > q->max_chunks) ||
412
0
       (q->opts & BUFQ_OPT_NO_SPARES)) {
413
      /* SOFT_LIMIT allowed us more than max. free spares until
414
       * we are at max again. Or free them if we are configured
415
       * to not use spares. */
416
0
      free(chunk);
417
0
      --q->chunk_count;
418
0
    }
419
0
    else {
420
0
      chunk->next = q->spare;
421
0
      q->spare = chunk;
422
0
    }
423
0
  }
424
0
}
425
426
static struct buf_chunk *get_non_full_tail(struct bufq *q)
427
27.6M
{
428
27.6M
  struct buf_chunk *chunk;
429
430
27.6M
  if(q->tail && !chunk_is_full(q->tail))
431
10.3M
    return q->tail;
432
17.2M
  chunk = get_spare(q);
433
17.2M
  if(chunk) {
434
    /* new tail, and possibly new head */
435
16.1M
    if(q->tail) {
436
16.0M
      q->tail->next = chunk;
437
16.0M
      q->tail = chunk;
438
16.0M
    }
439
130k
    else {
440
130k
      DEBUGASSERT(!q->head);
441
130k
      q->head = q->tail = chunk;
442
130k
    }
443
16.1M
  }
444
17.2M
  return chunk;
445
17.2M
}
446
447
ssize_t Curl_bufq_write(struct bufq *q,
448
                        const unsigned char *buf, size_t len,
449
                        CURLcode *err)
450
5.45M
{
451
5.45M
  struct buf_chunk *tail;
452
5.45M
  ssize_t nwritten = 0;
453
5.45M
  size_t n;
454
455
5.45M
  DEBUGASSERT(q->max_chunks > 0);
456
17.9M
  while(len) {
457
13.1M
    tail = get_non_full_tail(q);
458
13.1M
    if(!tail) {
459
738k
      if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) {
460
0
        *err = CURLE_OUT_OF_MEMORY;
461
0
        return -1;
462
0
      }
463
738k
      break;
464
738k
    }
465
12.4M
    n = chunk_append(tail, buf, len);
466
12.4M
    if(!n)
467
0
      break;
468
12.4M
    nwritten += n;
469
12.4M
    buf += n;
470
12.4M
    len -= n;
471
12.4M
  }
472
5.45M
  if(nwritten == 0 && len) {
473
713k
    *err = CURLE_AGAIN;
474
713k
    return -1;
475
713k
  }
476
4.74M
  *err = CURLE_OK;
477
4.74M
  return nwritten;
478
5.45M
}
479
480
CURLcode Curl_bufq_cwrite(struct bufq *q,
481
                          const char *buf, size_t len,
482
                          size_t *pnwritten)
483
426k
{
484
426k
  ssize_t n;
485
426k
  CURLcode result;
486
426k
  n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result);
487
426k
  *pnwritten = (n < 0)? 0 : (size_t)n;
488
426k
  return result;
489
426k
}
490
491
CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len)
492
0
{
493
0
  while(len && q->tail) {
494
0
    len -= chunk_unwrite(q->head, len);
495
0
    prune_tail(q);
496
0
  }
497
0
  return len? CURLE_AGAIN : CURLE_OK;
498
0
}
499
500
ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
501
                       CURLcode *err)
502
242k
{
503
242k
  ssize_t nread = 0;
504
242k
  size_t n;
505
506
242k
  *err = CURLE_OK;
507
2.54M
  while(len && q->head) {
508
2.30M
    n = chunk_read(q->head, buf, len);
509
2.30M
    if(n) {
510
2.30M
      nread += n;
511
2.30M
      buf += n;
512
2.30M
      len -= n;
513
2.30M
    }
514
2.30M
    prune_head(q);
515
2.30M
  }
516
242k
  if(nread == 0) {
517
230k
    *err = CURLE_AGAIN;
518
230k
    return -1;
519
230k
  }
520
12.4k
  return nread;
521
242k
}
522
523
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
524
                         size_t *pnread)
525
1.16k
{
526
1.16k
  ssize_t n;
527
1.16k
  CURLcode result;
528
1.16k
  n = Curl_bufq_read(q, (unsigned char *)buf, len, &result);
529
1.16k
  *pnread = (n < 0)? 0 : (size_t)n;
530
1.16k
  return result;
531
1.16k
}
532
533
bool Curl_bufq_peek(struct bufq *q,
534
                    const unsigned char **pbuf, size_t *plen)
535
3.72M
{
536
3.72M
  if(q->head && chunk_is_empty(q->head)) {
537
3.65k
    prune_head(q);
538
3.65k
  }
539
3.72M
  if(q->head && !chunk_is_empty(q->head)) {
540
2.96M
    chunk_peek(q->head, pbuf, plen);
541
2.96M
    return TRUE;
542
2.96M
  }
543
760k
  *pbuf = NULL;
544
760k
  *plen = 0;
545
760k
  return FALSE;
546
3.72M
}
547
548
bool Curl_bufq_peek_at(struct bufq *q, size_t offset,
549
                       const unsigned char **pbuf, size_t *plen)
550
227k
{
551
227k
  struct buf_chunk *c = q->head;
552
227k
  size_t clen;
553
554
2.65M
  while(c) {
555
2.43M
    clen = chunk_len(c);
556
2.43M
    if(!clen)
557
227
      break;
558
2.42M
    if(offset >= clen) {
559
2.42M
      offset -= clen;
560
2.42M
      c = c->next;
561
2.42M
      continue;
562
2.42M
    }
563
2.39k
    chunk_peek_at(c, offset, pbuf, plen);
564
2.39k
    return TRUE;
565
2.42M
  }
566
225k
  *pbuf = NULL;
567
225k
  *plen = 0;
568
225k
  return FALSE;
569
227k
}
570
571
void Curl_bufq_skip(struct bufq *q, size_t amount)
572
3.40M
{
573
3.40M
  size_t n;
574
575
9.77M
  while(amount && q->head) {
576
6.37M
    n = chunk_skip(q->head, amount);
577
6.37M
    amount -= n;
578
6.37M
    prune_head(q);
579
6.37M
  }
580
3.40M
}
581
582
ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
583
                       void *writer_ctx, CURLcode *err)
584
724k
{
585
724k
  const unsigned char *buf;
586
724k
  size_t blen;
587
724k
  ssize_t nwritten = 0;
588
589
2.97M
  while(Curl_bufq_peek(q, &buf, &blen)) {
590
2.25M
    ssize_t chunk_written;
591
592
2.25M
    chunk_written = writer(writer_ctx, buf, blen, err);
593
2.25M
    if(chunk_written < 0) {
594
3.55k
      if(!nwritten || *err != CURLE_AGAIN) {
595
        /* blocked on first write or real error, fail */
596
1.04k
        nwritten = -1;
597
1.04k
      }
598
3.55k
      break;
599
3.55k
    }
600
2.24M
    if(!chunk_written) {
601
0
      if(!nwritten) {
602
        /* treat as blocked */
603
0
        *err = CURLE_AGAIN;
604
0
        nwritten = -1;
605
0
      }
606
0
      break;
607
0
    }
608
2.24M
    Curl_bufq_skip(q, (size_t)chunk_written);
609
2.24M
    nwritten += chunk_written;
610
2.24M
  }
611
724k
  return nwritten;
612
724k
}
613
614
ssize_t Curl_bufq_write_pass(struct bufq *q,
615
                             const unsigned char *buf, size_t len,
616
                             Curl_bufq_writer *writer, void *writer_ctx,
617
                             CURLcode *err)
618
48.2k
{
619
48.2k
  ssize_t nwritten = 0, n;
620
621
48.2k
  *err = CURLE_OK;
622
97.3k
  while(len) {
623
49.1k
    if(Curl_bufq_is_full(q)) {
624
      /* try to make room in case we are full */
625
850
      n = Curl_bufq_pass(q, writer, writer_ctx, err);
626
850
      if(n < 0) {
627
0
        if(*err != CURLE_AGAIN) {
628
          /* real error, fail */
629
0
          return -1;
630
0
        }
631
        /* would block, bufq is full, give up */
632
0
        break;
633
0
      }
634
850
    }
635
636
    /* Add whatever is remaining now to bufq */
637
49.1k
    n = Curl_bufq_write(q, buf, len, err);
638
49.1k
    if(n < 0) {
639
0
      if(*err != CURLE_AGAIN) {
640
        /* real error, fail */
641
0
        return -1;
642
0
      }
643
      /* no room in bufq */
644
0
      break;
645
0
    }
646
    /* edge case of writer returning 0 (and len is >0)
647
     * break or we might enter an infinite loop here */
648
49.1k
    if(n == 0)
649
0
      break;
650
651
    /* Maybe only part of `data` has been added, continue to loop */
652
49.1k
    buf += (size_t)n;
653
49.1k
    len -= (size_t)n;
654
49.1k
    nwritten += (size_t)n;
655
49.1k
  }
656
657
48.2k
  if(!nwritten && len) {
658
0
    *err = CURLE_AGAIN;
659
0
    return -1;
660
0
  }
661
48.2k
  *err = CURLE_OK;
662
48.2k
  return nwritten;
663
48.2k
}
664
665
ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len,
666
                       Curl_bufq_reader *reader, void *reader_ctx,
667
                       CURLcode *err)
668
14.4M
{
669
14.4M
  struct buf_chunk *tail = NULL;
670
14.4M
  ssize_t nread;
671
672
14.4M
  *err = CURLE_AGAIN;
673
14.4M
  tail = get_non_full_tail(q);
674
14.4M
  if(!tail) {
675
368k
    if(q->chunk_count < q->max_chunks) {
676
0
      *err = CURLE_OUT_OF_MEMORY;
677
0
      return -1;
678
0
    }
679
    /* full, blocked */
680
368k
    *err = CURLE_AGAIN;
681
368k
    return -1;
682
368k
  }
683
684
14.0M
  nread = chunk_slurpn(tail, max_len, reader, reader_ctx, err);
685
14.0M
  if(nread < 0) {
686
6.81M
    return -1;
687
6.81M
  }
688
7.23M
  else if(nread == 0) {
689
    /* eof */
690
3.25M
    *err = CURLE_OK;
691
3.25M
  }
692
7.23M
  return nread;
693
14.0M
}
694
695
/**
696
 * Read up to `max_len` bytes and append it to the end of the buffer queue.
697
 * if `max_len` is 0, no limit is imposed and the call behaves exactly
698
 * the same as `Curl_bufq_slurp()`.
699
 * Returns the total amount of buf read (may be 0) or -1 on other
700
 * reader errors.
701
 * Note that even in case of a -1 chunks may have been read and
702
 * the buffer queue will have different length than before.
703
 */
704
static ssize_t bufq_slurpn(struct bufq *q, size_t max_len,
705
                           Curl_bufq_reader *reader, void *reader_ctx,
706
                           CURLcode *err)
707
24.2k
{
708
24.2k
  ssize_t nread = 0, n;
709
710
24.2k
  *err = CURLE_AGAIN;
711
3.62M
  while(1) {
712
713
3.62M
    n = Curl_bufq_sipn(q, max_len, reader, reader_ctx, err);
714
3.62M
    if(n < 0) {
715
21.2k
      if(!nread || *err != CURLE_AGAIN) {
716
        /* blocked on first read or real error, fail */
717
14.2k
        nread = -1;
718
14.2k
      }
719
7.01k
      else
720
7.01k
        *err = CURLE_OK;
721
21.2k
      break;
722
21.2k
    }
723
3.60M
    else if(n == 0) {
724
      /* eof */
725
193
      *err = CURLE_OK;
726
193
      break;
727
193
    }
728
3.60M
    nread += (size_t)n;
729
3.60M
    if(max_len) {
730
0
      DEBUGASSERT((size_t)n <= max_len);
731
0
      max_len -= (size_t)n;
732
0
      if(!max_len)
733
0
        break;
734
0
    }
735
    /* give up slurping when we get less bytes than we asked for */
736
3.60M
    if(q->tail && !chunk_is_full(q->tail))
737
2.86k
      break;
738
3.60M
  }
739
24.2k
  return nread;
740
24.2k
}
741
742
ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
743
                        void *reader_ctx, CURLcode *err)
744
24.2k
{
745
24.2k
  return bufq_slurpn(q, 0, reader, reader_ctx, err);
746
24.2k
}