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