113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci#include "bufq.h"
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci/* The last 3 #include files should be in this order */
2913498266Sopenharmony_ci#include "curl_printf.h"
3013498266Sopenharmony_ci#include "curl_memory.h"
3113498266Sopenharmony_ci#include "memdebug.h"
3213498266Sopenharmony_ci
3313498266Sopenharmony_cistatic bool chunk_is_empty(const struct buf_chunk *chunk)
3413498266Sopenharmony_ci{
3513498266Sopenharmony_ci  return chunk->r_offset >= chunk->w_offset;
3613498266Sopenharmony_ci}
3713498266Sopenharmony_ci
3813498266Sopenharmony_cistatic bool chunk_is_full(const struct buf_chunk *chunk)
3913498266Sopenharmony_ci{
4013498266Sopenharmony_ci  return chunk->w_offset >= chunk->dlen;
4113498266Sopenharmony_ci}
4213498266Sopenharmony_ci
4313498266Sopenharmony_cistatic size_t chunk_len(const struct buf_chunk *chunk)
4413498266Sopenharmony_ci{
4513498266Sopenharmony_ci  return chunk->w_offset - chunk->r_offset;
4613498266Sopenharmony_ci}
4713498266Sopenharmony_ci
4813498266Sopenharmony_cistatic size_t chunk_space(const struct buf_chunk *chunk)
4913498266Sopenharmony_ci{
5013498266Sopenharmony_ci  return chunk->dlen - chunk->w_offset;
5113498266Sopenharmony_ci}
5213498266Sopenharmony_ci
5313498266Sopenharmony_cistatic void chunk_reset(struct buf_chunk *chunk)
5413498266Sopenharmony_ci{
5513498266Sopenharmony_ci  chunk->next = NULL;
5613498266Sopenharmony_ci  chunk->r_offset = chunk->w_offset = 0;
5713498266Sopenharmony_ci}
5813498266Sopenharmony_ci
5913498266Sopenharmony_cistatic size_t chunk_append(struct buf_chunk *chunk,
6013498266Sopenharmony_ci                           const unsigned char *buf, size_t len)
6113498266Sopenharmony_ci{
6213498266Sopenharmony_ci  unsigned char *p = &chunk->x.data[chunk->w_offset];
6313498266Sopenharmony_ci  size_t n = chunk->dlen - chunk->w_offset;
6413498266Sopenharmony_ci  DEBUGASSERT(chunk->dlen >= chunk->w_offset);
6513498266Sopenharmony_ci  if(n) {
6613498266Sopenharmony_ci    n = CURLMIN(n, len);
6713498266Sopenharmony_ci    memcpy(p, buf, n);
6813498266Sopenharmony_ci    chunk->w_offset += n;
6913498266Sopenharmony_ci  }
7013498266Sopenharmony_ci  return n;
7113498266Sopenharmony_ci}
7213498266Sopenharmony_ci
7313498266Sopenharmony_cistatic size_t chunk_read(struct buf_chunk *chunk,
7413498266Sopenharmony_ci                         unsigned char *buf, size_t len)
7513498266Sopenharmony_ci{
7613498266Sopenharmony_ci  unsigned char *p = &chunk->x.data[chunk->r_offset];
7713498266Sopenharmony_ci  size_t n = chunk->w_offset - chunk->r_offset;
7813498266Sopenharmony_ci  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
7913498266Sopenharmony_ci  if(!n) {
8013498266Sopenharmony_ci    return 0;
8113498266Sopenharmony_ci  }
8213498266Sopenharmony_ci  else if(n <= len) {
8313498266Sopenharmony_ci    memcpy(buf, p, n);
8413498266Sopenharmony_ci    chunk->r_offset = chunk->w_offset = 0;
8513498266Sopenharmony_ci    return n;
8613498266Sopenharmony_ci  }
8713498266Sopenharmony_ci  else {
8813498266Sopenharmony_ci    memcpy(buf, p, len);
8913498266Sopenharmony_ci    chunk->r_offset += len;
9013498266Sopenharmony_ci    return len;
9113498266Sopenharmony_ci  }
9213498266Sopenharmony_ci}
9313498266Sopenharmony_ci
9413498266Sopenharmony_cistatic ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len,
9513498266Sopenharmony_ci                            Curl_bufq_reader *reader,
9613498266Sopenharmony_ci                            void *reader_ctx, CURLcode *err)
9713498266Sopenharmony_ci{
9813498266Sopenharmony_ci  unsigned char *p = &chunk->x.data[chunk->w_offset];
9913498266Sopenharmony_ci  size_t n = chunk->dlen - chunk->w_offset; /* free amount */
10013498266Sopenharmony_ci  ssize_t nread;
10113498266Sopenharmony_ci
10213498266Sopenharmony_ci  DEBUGASSERT(chunk->dlen >= chunk->w_offset);
10313498266Sopenharmony_ci  if(!n) {
10413498266Sopenharmony_ci    *err = CURLE_AGAIN;
10513498266Sopenharmony_ci    return -1;
10613498266Sopenharmony_ci  }
10713498266Sopenharmony_ci  if(max_len && n > max_len)
10813498266Sopenharmony_ci    n = max_len;
10913498266Sopenharmony_ci  nread = reader(reader_ctx, p, n, err);
11013498266Sopenharmony_ci  if(nread > 0) {
11113498266Sopenharmony_ci    DEBUGASSERT((size_t)nread <= n);
11213498266Sopenharmony_ci    chunk->w_offset += nread;
11313498266Sopenharmony_ci  }
11413498266Sopenharmony_ci  return nread;
11513498266Sopenharmony_ci}
11613498266Sopenharmony_ci
11713498266Sopenharmony_cistatic void chunk_peek(const struct buf_chunk *chunk,
11813498266Sopenharmony_ci                       const unsigned char **pbuf, size_t *plen)
11913498266Sopenharmony_ci{
12013498266Sopenharmony_ci  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
12113498266Sopenharmony_ci  *pbuf = &chunk->x.data[chunk->r_offset];
12213498266Sopenharmony_ci  *plen = chunk->w_offset - chunk->r_offset;
12313498266Sopenharmony_ci}
12413498266Sopenharmony_ci
12513498266Sopenharmony_cistatic void chunk_peek_at(const struct buf_chunk *chunk, size_t offset,
12613498266Sopenharmony_ci                          const unsigned char **pbuf, size_t *plen)
12713498266Sopenharmony_ci{
12813498266Sopenharmony_ci  offset += chunk->r_offset;
12913498266Sopenharmony_ci  DEBUGASSERT(chunk->w_offset >= offset);
13013498266Sopenharmony_ci  *pbuf = &chunk->x.data[offset];
13113498266Sopenharmony_ci  *plen = chunk->w_offset - offset;
13213498266Sopenharmony_ci}
13313498266Sopenharmony_ci
13413498266Sopenharmony_cistatic size_t chunk_skip(struct buf_chunk *chunk, size_t amount)
13513498266Sopenharmony_ci{
13613498266Sopenharmony_ci  size_t n = chunk->w_offset - chunk->r_offset;
13713498266Sopenharmony_ci  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
13813498266Sopenharmony_ci  if(n) {
13913498266Sopenharmony_ci    n = CURLMIN(n, amount);
14013498266Sopenharmony_ci    chunk->r_offset += n;
14113498266Sopenharmony_ci    if(chunk->r_offset == chunk->w_offset)
14213498266Sopenharmony_ci      chunk->r_offset = chunk->w_offset = 0;
14313498266Sopenharmony_ci  }
14413498266Sopenharmony_ci  return n;
14513498266Sopenharmony_ci}
14613498266Sopenharmony_ci
14713498266Sopenharmony_cistatic void chunk_list_free(struct buf_chunk **anchor)
14813498266Sopenharmony_ci{
14913498266Sopenharmony_ci  struct buf_chunk *chunk;
15013498266Sopenharmony_ci  while(*anchor) {
15113498266Sopenharmony_ci    chunk = *anchor;
15213498266Sopenharmony_ci    *anchor = chunk->next;
15313498266Sopenharmony_ci    free(chunk);
15413498266Sopenharmony_ci  }
15513498266Sopenharmony_ci}
15613498266Sopenharmony_ci
15713498266Sopenharmony_ci
15813498266Sopenharmony_ci
15913498266Sopenharmony_civoid Curl_bufcp_init(struct bufc_pool *pool,
16013498266Sopenharmony_ci                     size_t chunk_size, size_t spare_max)
16113498266Sopenharmony_ci{
16213498266Sopenharmony_ci  DEBUGASSERT(chunk_size > 0);
16313498266Sopenharmony_ci  DEBUGASSERT(spare_max > 0);
16413498266Sopenharmony_ci  memset(pool, 0, sizeof(*pool));
16513498266Sopenharmony_ci  pool->chunk_size = chunk_size;
16613498266Sopenharmony_ci  pool->spare_max = spare_max;
16713498266Sopenharmony_ci}
16813498266Sopenharmony_ci
16913498266Sopenharmony_cistatic CURLcode bufcp_take(struct bufc_pool *pool,
17013498266Sopenharmony_ci                           struct buf_chunk **pchunk)
17113498266Sopenharmony_ci{
17213498266Sopenharmony_ci  struct buf_chunk *chunk = NULL;
17313498266Sopenharmony_ci
17413498266Sopenharmony_ci  if(pool->spare) {
17513498266Sopenharmony_ci    chunk = pool->spare;
17613498266Sopenharmony_ci    pool->spare = chunk->next;
17713498266Sopenharmony_ci    --pool->spare_count;
17813498266Sopenharmony_ci    chunk_reset(chunk);
17913498266Sopenharmony_ci    *pchunk = chunk;
18013498266Sopenharmony_ci    return CURLE_OK;
18113498266Sopenharmony_ci  }
18213498266Sopenharmony_ci
18313498266Sopenharmony_ci  chunk = calloc(1, sizeof(*chunk) + pool->chunk_size);
18413498266Sopenharmony_ci  if(!chunk) {
18513498266Sopenharmony_ci    *pchunk = NULL;
18613498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
18713498266Sopenharmony_ci  }
18813498266Sopenharmony_ci  chunk->dlen = pool->chunk_size;
18913498266Sopenharmony_ci  *pchunk = chunk;
19013498266Sopenharmony_ci  return CURLE_OK;
19113498266Sopenharmony_ci}
19213498266Sopenharmony_ci
19313498266Sopenharmony_cistatic void bufcp_put(struct bufc_pool *pool,
19413498266Sopenharmony_ci                      struct buf_chunk *chunk)
19513498266Sopenharmony_ci{
19613498266Sopenharmony_ci  if(pool->spare_count >= pool->spare_max) {
19713498266Sopenharmony_ci    free(chunk);
19813498266Sopenharmony_ci  }
19913498266Sopenharmony_ci  else {
20013498266Sopenharmony_ci    chunk_reset(chunk);
20113498266Sopenharmony_ci    chunk->next = pool->spare;
20213498266Sopenharmony_ci    pool->spare = chunk;
20313498266Sopenharmony_ci    ++pool->spare_count;
20413498266Sopenharmony_ci  }
20513498266Sopenharmony_ci}
20613498266Sopenharmony_ci
20713498266Sopenharmony_civoid Curl_bufcp_free(struct bufc_pool *pool)
20813498266Sopenharmony_ci{
20913498266Sopenharmony_ci  chunk_list_free(&pool->spare);
21013498266Sopenharmony_ci  pool->spare_count = 0;
21113498266Sopenharmony_ci}
21213498266Sopenharmony_ci
21313498266Sopenharmony_cistatic void bufq_init(struct bufq *q, struct bufc_pool *pool,
21413498266Sopenharmony_ci                      size_t chunk_size, size_t max_chunks, int opts)
21513498266Sopenharmony_ci{
21613498266Sopenharmony_ci  DEBUGASSERT(chunk_size > 0);
21713498266Sopenharmony_ci  DEBUGASSERT(max_chunks > 0);
21813498266Sopenharmony_ci  memset(q, 0, sizeof(*q));
21913498266Sopenharmony_ci  q->chunk_size = chunk_size;
22013498266Sopenharmony_ci  q->max_chunks = max_chunks;
22113498266Sopenharmony_ci  q->pool = pool;
22213498266Sopenharmony_ci  q->opts = opts;
22313498266Sopenharmony_ci}
22413498266Sopenharmony_ci
22513498266Sopenharmony_civoid Curl_bufq_init2(struct bufq *q, size_t chunk_size, size_t max_chunks,
22613498266Sopenharmony_ci                     int opts)
22713498266Sopenharmony_ci{
22813498266Sopenharmony_ci  bufq_init(q, NULL, chunk_size, max_chunks, opts);
22913498266Sopenharmony_ci}
23013498266Sopenharmony_ci
23113498266Sopenharmony_civoid Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks)
23213498266Sopenharmony_ci{
23313498266Sopenharmony_ci  bufq_init(q, NULL, chunk_size, max_chunks, BUFQ_OPT_NONE);
23413498266Sopenharmony_ci}
23513498266Sopenharmony_ci
23613498266Sopenharmony_civoid Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool,
23713498266Sopenharmony_ci                     size_t max_chunks, int opts)
23813498266Sopenharmony_ci{
23913498266Sopenharmony_ci  bufq_init(q, pool, pool->chunk_size, max_chunks, opts);
24013498266Sopenharmony_ci}
24113498266Sopenharmony_ci
24213498266Sopenharmony_civoid Curl_bufq_free(struct bufq *q)
24313498266Sopenharmony_ci{
24413498266Sopenharmony_ci  chunk_list_free(&q->head);
24513498266Sopenharmony_ci  chunk_list_free(&q->spare);
24613498266Sopenharmony_ci  q->tail = NULL;
24713498266Sopenharmony_ci  q->chunk_count = 0;
24813498266Sopenharmony_ci}
24913498266Sopenharmony_ci
25013498266Sopenharmony_civoid Curl_bufq_reset(struct bufq *q)
25113498266Sopenharmony_ci{
25213498266Sopenharmony_ci  struct buf_chunk *chunk;
25313498266Sopenharmony_ci  while(q->head) {
25413498266Sopenharmony_ci    chunk = q->head;
25513498266Sopenharmony_ci    q->head = chunk->next;
25613498266Sopenharmony_ci    chunk->next = q->spare;
25713498266Sopenharmony_ci    q->spare = chunk;
25813498266Sopenharmony_ci  }
25913498266Sopenharmony_ci  q->tail = NULL;
26013498266Sopenharmony_ci}
26113498266Sopenharmony_ci
26213498266Sopenharmony_cisize_t Curl_bufq_len(const struct bufq *q)
26313498266Sopenharmony_ci{
26413498266Sopenharmony_ci  const struct buf_chunk *chunk = q->head;
26513498266Sopenharmony_ci  size_t len = 0;
26613498266Sopenharmony_ci  while(chunk) {
26713498266Sopenharmony_ci    len += chunk_len(chunk);
26813498266Sopenharmony_ci    chunk = chunk->next;
26913498266Sopenharmony_ci  }
27013498266Sopenharmony_ci  return len;
27113498266Sopenharmony_ci}
27213498266Sopenharmony_ci
27313498266Sopenharmony_cisize_t Curl_bufq_space(const struct bufq *q)
27413498266Sopenharmony_ci{
27513498266Sopenharmony_ci  size_t space = 0;
27613498266Sopenharmony_ci  if(q->tail)
27713498266Sopenharmony_ci    space += chunk_space(q->tail);
27813498266Sopenharmony_ci  if(q->spare) {
27913498266Sopenharmony_ci    struct buf_chunk *chunk = q->spare;
28013498266Sopenharmony_ci    while(chunk) {
28113498266Sopenharmony_ci      space += chunk->dlen;
28213498266Sopenharmony_ci      chunk = chunk->next;
28313498266Sopenharmony_ci    }
28413498266Sopenharmony_ci  }
28513498266Sopenharmony_ci  if(q->chunk_count < q->max_chunks) {
28613498266Sopenharmony_ci    space += (q->max_chunks - q->chunk_count) * q->chunk_size;
28713498266Sopenharmony_ci  }
28813498266Sopenharmony_ci  return space;
28913498266Sopenharmony_ci}
29013498266Sopenharmony_ci
29113498266Sopenharmony_cibool Curl_bufq_is_empty(const struct bufq *q)
29213498266Sopenharmony_ci{
29313498266Sopenharmony_ci  return !q->head || chunk_is_empty(q->head);
29413498266Sopenharmony_ci}
29513498266Sopenharmony_ci
29613498266Sopenharmony_cibool Curl_bufq_is_full(const struct bufq *q)
29713498266Sopenharmony_ci{
29813498266Sopenharmony_ci  if(!q->tail || q->spare)
29913498266Sopenharmony_ci    return FALSE;
30013498266Sopenharmony_ci  if(q->chunk_count < q->max_chunks)
30113498266Sopenharmony_ci    return FALSE;
30213498266Sopenharmony_ci  if(q->chunk_count > q->max_chunks)
30313498266Sopenharmony_ci    return TRUE;
30413498266Sopenharmony_ci  /* we have no spares and cannot make more, is the tail full? */
30513498266Sopenharmony_ci  return chunk_is_full(q->tail);
30613498266Sopenharmony_ci}
30713498266Sopenharmony_ci
30813498266Sopenharmony_cistatic struct buf_chunk *get_spare(struct bufq *q)
30913498266Sopenharmony_ci{
31013498266Sopenharmony_ci  struct buf_chunk *chunk = NULL;
31113498266Sopenharmony_ci
31213498266Sopenharmony_ci  if(q->spare) {
31313498266Sopenharmony_ci    chunk = q->spare;
31413498266Sopenharmony_ci    q->spare = chunk->next;
31513498266Sopenharmony_ci    chunk_reset(chunk);
31613498266Sopenharmony_ci    return chunk;
31713498266Sopenharmony_ci  }
31813498266Sopenharmony_ci
31913498266Sopenharmony_ci  if(q->chunk_count >= q->max_chunks && (!(q->opts & BUFQ_OPT_SOFT_LIMIT)))
32013498266Sopenharmony_ci    return NULL;
32113498266Sopenharmony_ci
32213498266Sopenharmony_ci  if(q->pool) {
32313498266Sopenharmony_ci    if(bufcp_take(q->pool, &chunk))
32413498266Sopenharmony_ci      return NULL;
32513498266Sopenharmony_ci    ++q->chunk_count;
32613498266Sopenharmony_ci    return chunk;
32713498266Sopenharmony_ci  }
32813498266Sopenharmony_ci  else {
32913498266Sopenharmony_ci    chunk = calloc(1, sizeof(*chunk) + q->chunk_size);
33013498266Sopenharmony_ci    if(!chunk)
33113498266Sopenharmony_ci      return NULL;
33213498266Sopenharmony_ci    chunk->dlen = q->chunk_size;
33313498266Sopenharmony_ci    ++q->chunk_count;
33413498266Sopenharmony_ci    return chunk;
33513498266Sopenharmony_ci  }
33613498266Sopenharmony_ci}
33713498266Sopenharmony_ci
33813498266Sopenharmony_cistatic void prune_head(struct bufq *q)
33913498266Sopenharmony_ci{
34013498266Sopenharmony_ci  struct buf_chunk *chunk;
34113498266Sopenharmony_ci
34213498266Sopenharmony_ci  while(q->head && chunk_is_empty(q->head)) {
34313498266Sopenharmony_ci    chunk = q->head;
34413498266Sopenharmony_ci    q->head = chunk->next;
34513498266Sopenharmony_ci    if(q->tail == chunk)
34613498266Sopenharmony_ci      q->tail = q->head;
34713498266Sopenharmony_ci    if(q->pool) {
34813498266Sopenharmony_ci      bufcp_put(q->pool, chunk);
34913498266Sopenharmony_ci      --q->chunk_count;
35013498266Sopenharmony_ci    }
35113498266Sopenharmony_ci    else if((q->chunk_count > q->max_chunks) ||
35213498266Sopenharmony_ci       (q->opts & BUFQ_OPT_NO_SPARES)) {
35313498266Sopenharmony_ci      /* SOFT_LIMIT allowed us more than max. free spares until
35413498266Sopenharmony_ci       * we are at max again. Or free them if we are configured
35513498266Sopenharmony_ci       * to not use spares. */
35613498266Sopenharmony_ci      free(chunk);
35713498266Sopenharmony_ci      --q->chunk_count;
35813498266Sopenharmony_ci    }
35913498266Sopenharmony_ci    else {
36013498266Sopenharmony_ci      chunk->next = q->spare;
36113498266Sopenharmony_ci      q->spare = chunk;
36213498266Sopenharmony_ci    }
36313498266Sopenharmony_ci  }
36413498266Sopenharmony_ci}
36513498266Sopenharmony_ci
36613498266Sopenharmony_cistatic struct buf_chunk *get_non_full_tail(struct bufq *q)
36713498266Sopenharmony_ci{
36813498266Sopenharmony_ci  struct buf_chunk *chunk;
36913498266Sopenharmony_ci
37013498266Sopenharmony_ci  if(q->tail && !chunk_is_full(q->tail))
37113498266Sopenharmony_ci    return q->tail;
37213498266Sopenharmony_ci  chunk = get_spare(q);
37313498266Sopenharmony_ci  if(chunk) {
37413498266Sopenharmony_ci    /* new tail, and possibly new head */
37513498266Sopenharmony_ci    if(q->tail) {
37613498266Sopenharmony_ci      q->tail->next = chunk;
37713498266Sopenharmony_ci      q->tail = chunk;
37813498266Sopenharmony_ci    }
37913498266Sopenharmony_ci    else {
38013498266Sopenharmony_ci      DEBUGASSERT(!q->head);
38113498266Sopenharmony_ci      q->head = q->tail = chunk;
38213498266Sopenharmony_ci    }
38313498266Sopenharmony_ci  }
38413498266Sopenharmony_ci  return chunk;
38513498266Sopenharmony_ci}
38613498266Sopenharmony_ci
38713498266Sopenharmony_cissize_t Curl_bufq_write(struct bufq *q,
38813498266Sopenharmony_ci                        const unsigned char *buf, size_t len,
38913498266Sopenharmony_ci                        CURLcode *err)
39013498266Sopenharmony_ci{
39113498266Sopenharmony_ci  struct buf_chunk *tail;
39213498266Sopenharmony_ci  ssize_t nwritten = 0;
39313498266Sopenharmony_ci  size_t n;
39413498266Sopenharmony_ci
39513498266Sopenharmony_ci  DEBUGASSERT(q->max_chunks > 0);
39613498266Sopenharmony_ci  while(len) {
39713498266Sopenharmony_ci    tail = get_non_full_tail(q);
39813498266Sopenharmony_ci    if(!tail) {
39913498266Sopenharmony_ci      if(q->chunk_count < q->max_chunks) {
40013498266Sopenharmony_ci        *err = CURLE_OUT_OF_MEMORY;
40113498266Sopenharmony_ci        return -1;
40213498266Sopenharmony_ci      }
40313498266Sopenharmony_ci      break;
40413498266Sopenharmony_ci    }
40513498266Sopenharmony_ci    n = chunk_append(tail, buf, len);
40613498266Sopenharmony_ci    if(!n)
40713498266Sopenharmony_ci      break;
40813498266Sopenharmony_ci    nwritten += n;
40913498266Sopenharmony_ci    buf += n;
41013498266Sopenharmony_ci    len -= n;
41113498266Sopenharmony_ci  }
41213498266Sopenharmony_ci  if(nwritten == 0 && len) {
41313498266Sopenharmony_ci    *err = CURLE_AGAIN;
41413498266Sopenharmony_ci    return -1;
41513498266Sopenharmony_ci  }
41613498266Sopenharmony_ci  *err = CURLE_OK;
41713498266Sopenharmony_ci  return nwritten;
41813498266Sopenharmony_ci}
41913498266Sopenharmony_ci
42013498266Sopenharmony_cissize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
42113498266Sopenharmony_ci                       CURLcode *err)
42213498266Sopenharmony_ci{
42313498266Sopenharmony_ci  ssize_t nread = 0;
42413498266Sopenharmony_ci  size_t n;
42513498266Sopenharmony_ci
42613498266Sopenharmony_ci  *err = CURLE_OK;
42713498266Sopenharmony_ci  while(len && q->head) {
42813498266Sopenharmony_ci    n = chunk_read(q->head, buf, len);
42913498266Sopenharmony_ci    if(n) {
43013498266Sopenharmony_ci      nread += n;
43113498266Sopenharmony_ci      buf += n;
43213498266Sopenharmony_ci      len -= n;
43313498266Sopenharmony_ci    }
43413498266Sopenharmony_ci    prune_head(q);
43513498266Sopenharmony_ci  }
43613498266Sopenharmony_ci  if(nread == 0) {
43713498266Sopenharmony_ci    *err = CURLE_AGAIN;
43813498266Sopenharmony_ci    return -1;
43913498266Sopenharmony_ci  }
44013498266Sopenharmony_ci  return nread;
44113498266Sopenharmony_ci}
44213498266Sopenharmony_ci
44313498266Sopenharmony_cibool Curl_bufq_peek(struct bufq *q,
44413498266Sopenharmony_ci                    const unsigned char **pbuf, size_t *plen)
44513498266Sopenharmony_ci{
44613498266Sopenharmony_ci  if(q->head && chunk_is_empty(q->head)) {
44713498266Sopenharmony_ci    prune_head(q);
44813498266Sopenharmony_ci  }
44913498266Sopenharmony_ci  if(q->head && !chunk_is_empty(q->head)) {
45013498266Sopenharmony_ci    chunk_peek(q->head, pbuf, plen);
45113498266Sopenharmony_ci    return TRUE;
45213498266Sopenharmony_ci  }
45313498266Sopenharmony_ci  *pbuf = NULL;
45413498266Sopenharmony_ci  *plen = 0;
45513498266Sopenharmony_ci  return FALSE;
45613498266Sopenharmony_ci}
45713498266Sopenharmony_ci
45813498266Sopenharmony_cibool Curl_bufq_peek_at(struct bufq *q, size_t offset,
45913498266Sopenharmony_ci                       const unsigned char **pbuf, size_t *plen)
46013498266Sopenharmony_ci{
46113498266Sopenharmony_ci  struct buf_chunk *c = q->head;
46213498266Sopenharmony_ci  size_t clen;
46313498266Sopenharmony_ci
46413498266Sopenharmony_ci  while(c) {
46513498266Sopenharmony_ci    clen = chunk_len(c);
46613498266Sopenharmony_ci    if(!clen)
46713498266Sopenharmony_ci      break;
46813498266Sopenharmony_ci    if(offset >= clen) {
46913498266Sopenharmony_ci      offset -= clen;
47013498266Sopenharmony_ci      c = c->next;
47113498266Sopenharmony_ci      continue;
47213498266Sopenharmony_ci    }
47313498266Sopenharmony_ci    chunk_peek_at(c, offset, pbuf, plen);
47413498266Sopenharmony_ci    return TRUE;
47513498266Sopenharmony_ci  }
47613498266Sopenharmony_ci  *pbuf = NULL;
47713498266Sopenharmony_ci  *plen = 0;
47813498266Sopenharmony_ci  return FALSE;
47913498266Sopenharmony_ci}
48013498266Sopenharmony_ci
48113498266Sopenharmony_civoid Curl_bufq_skip(struct bufq *q, size_t amount)
48213498266Sopenharmony_ci{
48313498266Sopenharmony_ci  size_t n;
48413498266Sopenharmony_ci
48513498266Sopenharmony_ci  while(amount && q->head) {
48613498266Sopenharmony_ci    n = chunk_skip(q->head, amount);
48713498266Sopenharmony_ci    amount -= n;
48813498266Sopenharmony_ci    prune_head(q);
48913498266Sopenharmony_ci  }
49013498266Sopenharmony_ci}
49113498266Sopenharmony_ci
49213498266Sopenharmony_cissize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
49313498266Sopenharmony_ci                       void *writer_ctx, CURLcode *err)
49413498266Sopenharmony_ci{
49513498266Sopenharmony_ci  const unsigned char *buf;
49613498266Sopenharmony_ci  size_t blen;
49713498266Sopenharmony_ci  ssize_t nwritten = 0;
49813498266Sopenharmony_ci
49913498266Sopenharmony_ci  while(Curl_bufq_peek(q, &buf, &blen)) {
50013498266Sopenharmony_ci    ssize_t chunk_written;
50113498266Sopenharmony_ci
50213498266Sopenharmony_ci    chunk_written = writer(writer_ctx, buf, blen, err);
50313498266Sopenharmony_ci    if(chunk_written < 0) {
50413498266Sopenharmony_ci      if(!nwritten || *err != CURLE_AGAIN) {
50513498266Sopenharmony_ci        /* blocked on first write or real error, fail */
50613498266Sopenharmony_ci        nwritten = -1;
50713498266Sopenharmony_ci      }
50813498266Sopenharmony_ci      break;
50913498266Sopenharmony_ci    }
51013498266Sopenharmony_ci    if(!chunk_written) {
51113498266Sopenharmony_ci      if(!nwritten) {
51213498266Sopenharmony_ci        /* treat as blocked */
51313498266Sopenharmony_ci        *err = CURLE_AGAIN;
51413498266Sopenharmony_ci        nwritten = -1;
51513498266Sopenharmony_ci      }
51613498266Sopenharmony_ci      break;
51713498266Sopenharmony_ci    }
51813498266Sopenharmony_ci    Curl_bufq_skip(q, (size_t)chunk_written);
51913498266Sopenharmony_ci    nwritten += chunk_written;
52013498266Sopenharmony_ci  }
52113498266Sopenharmony_ci  return nwritten;
52213498266Sopenharmony_ci}
52313498266Sopenharmony_ci
52413498266Sopenharmony_cissize_t Curl_bufq_write_pass(struct bufq *q,
52513498266Sopenharmony_ci                             const unsigned char *buf, size_t len,
52613498266Sopenharmony_ci                             Curl_bufq_writer *writer, void *writer_ctx,
52713498266Sopenharmony_ci                             CURLcode *err)
52813498266Sopenharmony_ci{
52913498266Sopenharmony_ci  ssize_t nwritten = 0, n;
53013498266Sopenharmony_ci
53113498266Sopenharmony_ci  *err = CURLE_OK;
53213498266Sopenharmony_ci  while(len) {
53313498266Sopenharmony_ci    if(Curl_bufq_is_full(q)) {
53413498266Sopenharmony_ci      /* try to make room in case we are full */
53513498266Sopenharmony_ci      n = Curl_bufq_pass(q, writer, writer_ctx, err);
53613498266Sopenharmony_ci      if(n < 0) {
53713498266Sopenharmony_ci        if(*err != CURLE_AGAIN) {
53813498266Sopenharmony_ci          /* real error, fail */
53913498266Sopenharmony_ci          return -1;
54013498266Sopenharmony_ci        }
54113498266Sopenharmony_ci        /* would block, bufq is full, give up */
54213498266Sopenharmony_ci        break;
54313498266Sopenharmony_ci      }
54413498266Sopenharmony_ci    }
54513498266Sopenharmony_ci
54613498266Sopenharmony_ci    /* Add whatever is remaining now to bufq */
54713498266Sopenharmony_ci    n = Curl_bufq_write(q, buf, len, err);
54813498266Sopenharmony_ci    if(n < 0) {
54913498266Sopenharmony_ci      if(*err != CURLE_AGAIN) {
55013498266Sopenharmony_ci        /* real error, fail */
55113498266Sopenharmony_ci        return -1;
55213498266Sopenharmony_ci      }
55313498266Sopenharmony_ci      /* no room in bufq */
55413498266Sopenharmony_ci      break;
55513498266Sopenharmony_ci    }
55613498266Sopenharmony_ci    /* edge case of writer returning 0 (and len is >0)
55713498266Sopenharmony_ci     * break or we might enter an infinite loop here */
55813498266Sopenharmony_ci    if(n == 0)
55913498266Sopenharmony_ci      break;
56013498266Sopenharmony_ci
56113498266Sopenharmony_ci    /* Maybe only part of `data` has been added, continue to loop */
56213498266Sopenharmony_ci    buf += (size_t)n;
56313498266Sopenharmony_ci    len -= (size_t)n;
56413498266Sopenharmony_ci    nwritten += (size_t)n;
56513498266Sopenharmony_ci  }
56613498266Sopenharmony_ci
56713498266Sopenharmony_ci  if(!nwritten && len) {
56813498266Sopenharmony_ci    *err = CURLE_AGAIN;
56913498266Sopenharmony_ci    return -1;
57013498266Sopenharmony_ci  }
57113498266Sopenharmony_ci  *err = CURLE_OK;
57213498266Sopenharmony_ci  return nwritten;
57313498266Sopenharmony_ci}
57413498266Sopenharmony_ci
57513498266Sopenharmony_cissize_t Curl_bufq_sipn(struct bufq *q, size_t max_len,
57613498266Sopenharmony_ci                       Curl_bufq_reader *reader, void *reader_ctx,
57713498266Sopenharmony_ci                       CURLcode *err)
57813498266Sopenharmony_ci{
57913498266Sopenharmony_ci  struct buf_chunk *tail = NULL;
58013498266Sopenharmony_ci  ssize_t nread;
58113498266Sopenharmony_ci
58213498266Sopenharmony_ci  *err = CURLE_AGAIN;
58313498266Sopenharmony_ci  tail = get_non_full_tail(q);
58413498266Sopenharmony_ci  if(!tail) {
58513498266Sopenharmony_ci    if(q->chunk_count < q->max_chunks) {
58613498266Sopenharmony_ci      *err = CURLE_OUT_OF_MEMORY;
58713498266Sopenharmony_ci      return -1;
58813498266Sopenharmony_ci    }
58913498266Sopenharmony_ci    /* full, blocked */
59013498266Sopenharmony_ci    *err = CURLE_AGAIN;
59113498266Sopenharmony_ci    return -1;
59213498266Sopenharmony_ci  }
59313498266Sopenharmony_ci
59413498266Sopenharmony_ci  nread = chunk_slurpn(tail, max_len, reader, reader_ctx, err);
59513498266Sopenharmony_ci  if(nread < 0) {
59613498266Sopenharmony_ci    return -1;
59713498266Sopenharmony_ci  }
59813498266Sopenharmony_ci  else if(nread == 0) {
59913498266Sopenharmony_ci    /* eof */
60013498266Sopenharmony_ci    *err = CURLE_OK;
60113498266Sopenharmony_ci  }
60213498266Sopenharmony_ci  return nread;
60313498266Sopenharmony_ci}
60413498266Sopenharmony_ci
60513498266Sopenharmony_ci/**
60613498266Sopenharmony_ci * Read up to `max_len` bytes and append it to the end of the buffer queue.
60713498266Sopenharmony_ci * if `max_len` is 0, no limit is imposed and the call behaves exactly
60813498266Sopenharmony_ci * the same as `Curl_bufq_slurp()`.
60913498266Sopenharmony_ci * Returns the total amount of buf read (may be 0) or -1 on other
61013498266Sopenharmony_ci * reader errors.
61113498266Sopenharmony_ci * Note that even in case of a -1 chunks may have been read and
61213498266Sopenharmony_ci * the buffer queue will have different length than before.
61313498266Sopenharmony_ci */
61413498266Sopenharmony_cistatic ssize_t bufq_slurpn(struct bufq *q, size_t max_len,
61513498266Sopenharmony_ci                           Curl_bufq_reader *reader, void *reader_ctx,
61613498266Sopenharmony_ci                           CURLcode *err)
61713498266Sopenharmony_ci{
61813498266Sopenharmony_ci  ssize_t nread = 0, n;
61913498266Sopenharmony_ci
62013498266Sopenharmony_ci  *err = CURLE_AGAIN;
62113498266Sopenharmony_ci  while(1) {
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci    n = Curl_bufq_sipn(q, max_len, reader, reader_ctx, err);
62413498266Sopenharmony_ci    if(n < 0) {
62513498266Sopenharmony_ci      if(!nread || *err != CURLE_AGAIN) {
62613498266Sopenharmony_ci        /* blocked on first read or real error, fail */
62713498266Sopenharmony_ci        nread = -1;
62813498266Sopenharmony_ci      }
62913498266Sopenharmony_ci      else
63013498266Sopenharmony_ci        *err = CURLE_OK;
63113498266Sopenharmony_ci      break;
63213498266Sopenharmony_ci    }
63313498266Sopenharmony_ci    else if(n == 0) {
63413498266Sopenharmony_ci      /* eof */
63513498266Sopenharmony_ci      *err = CURLE_OK;
63613498266Sopenharmony_ci      break;
63713498266Sopenharmony_ci    }
63813498266Sopenharmony_ci    nread += (size_t)n;
63913498266Sopenharmony_ci    if(max_len) {
64013498266Sopenharmony_ci      DEBUGASSERT((size_t)n <= max_len);
64113498266Sopenharmony_ci      max_len -= (size_t)n;
64213498266Sopenharmony_ci      if(!max_len)
64313498266Sopenharmony_ci        break;
64413498266Sopenharmony_ci    }
64513498266Sopenharmony_ci    /* give up slurping when we get less bytes than we asked for */
64613498266Sopenharmony_ci    if(q->tail && !chunk_is_full(q->tail))
64713498266Sopenharmony_ci      break;
64813498266Sopenharmony_ci  }
64913498266Sopenharmony_ci  return nread;
65013498266Sopenharmony_ci}
65113498266Sopenharmony_ci
65213498266Sopenharmony_cissize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
65313498266Sopenharmony_ci                        void *reader_ctx, CURLcode *err)
65413498266Sopenharmony_ci{
65513498266Sopenharmony_ci  return bufq_slurpn(q, 0, reader, reader_ctx, err);
65613498266Sopenharmony_ci}
657