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