On Mon, Apr 22, 2024 at 2:46 PM <minf...@apache.org> wrote: > > --- apr/apr/trunk/buffer/apr_buffer.c (original) > +++ apr/apr/trunk/buffer/apr_buffer.c Mon Apr 22 12:46:37 2024 > @@ -28,7 +28,7 @@ > #include "apr_strings.h" > #include "apr_private.h" > > -#define APR_BUFFER_MAX APR_SIZE_MAX/2 > +#define APR_BUFFER_MAX (APR_SIZE_MAX/2-1)
It seems that the ->size does include the terminating NUL byte so APR_BUFFER_MAX could be APR_SIZE_MAX/2 no? Why -1 here? Also maybe ->size should be ->len then? > > @@ -74,21 +76,25 @@ APR_DECLARE(apr_status_t) apr_buffer_str Here in apr_buffer_str_set() we still have apr_ssize_t len, please make it an apr_size_t for the reason explained in the other thread (also the apr_buffer_mem_*() ones use apr_size_t already so it should be consistent)? > if (!str) { > buf->d.str = NULL; > buf->size = 0; > + buf->zero_terminated = 0; > } > else if (len < 0) { > apr_size_t slen = strlen(str); > if (slen <= APR_BUFFER_MAX) { > buf->d.str = str; > - buf->size = -(apr_off_t)(slen + 1); > + buf->size = slen; > + buf->zero_terminated = 1; > } > else { > buf->d.str = NULL; > buf->size = 0; > + buf->zero_terminated = 0; return APR_EINVAL? > } > } > else { > buf->d.str = str; > - buf->size = -(len + 1); > + buf->size = len; > + buf->zero_terminated = 1; Shouldn't we check that str[len] == '\0' here? Though if it's not the check might be out of bounds, but OTOH it's not a string buffer and ->zero_terminated is not true.. > } > > return APR_SUCCESS; Overall I think it could be simplified as: APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf, char *str, apr_size_t len) { if (len == APR_BUFFER_STRING) { len = str ? strlen(str) : 0; } else if (str ? str[len] != '\0' : len != 0) { return APR_EINVAL; } if (len > APR_BUFFER_MAX) { return APR_EINVAL; /* or APR_ENOSPC? */ } buf->d.str = str; buf->size = len; buf->zero_terminated = 1; return APR_SUCCESS; } > @@ -100,25 +106,25 @@ APR_DECLARE(apr_buffer_t *) apr_buffer_s Likewise apr_ssize_t => apr_size_t len for apr_buffer_str_make(), APR_BUFFER_STRING and co should be unsigned (e.g. APR_SIZE_MAX) too. > apr_buffer_t *buf; > apr_int64_t size; > apr_size_t slen; > + unsigned int zero_terminated; > > if (!str) { > str = NULL; > size = 0; > + zero_terminated = 0; > } > - if (APR_BUFFER_STRING == len && !str[0]) { > - size = len; > - } > - else if (len < 0) { > + if (APR_BUFFER_STRING == len) { > slen = strlen(str); > if (slen <= APR_BUFFER_MAX) { > - size = -(apr_off_t)(slen + 1); > + size = slen; > + zero_terminated = 1; > } > else { > return NULL; > } > } > else { > - size = -(len + 1); > + size = (apr_size_t)len; > } > > buf = apr_palloc(pool, sizeof(apr_buffer_t)); > @@ -126,6 +132,7 @@ APR_DECLARE(apr_buffer_t *) apr_buffer_s > if (buf) { > buf->d.str = str; > buf->size = size; > + buf->zero_terminated = zero_terminated; > } > > return buf; Since there could be multiple error cases for apr_buffer_str_make() too, I think we cannot return NULL so rather than a _make() we need a _create() like: APR_DECLARE(apr_status_t) apr_buffer_str_create(apr_pool_t *pool, apr_buffer_t **pbuf, char *str, apr_size_t len) { *pbuf = apr_palloc(pool, sizeof(apr_buffer_t)); return *pbuf ? apr_buffer_str_set(*pbuf, str, len) : APR_ENOMEM; } ? > > APR_DECLARE(apr_size_t) apr_buffer_allocated(const apr_buffer_t *buf) apr_buffer_size() if ->size is changed to ->len ? > { > - if (buf->size < 0) { > - return (apr_size_t)((-buf->size)); > - } > - else { > - return (apr_size_t)buf->size; > - } > + return buf->size + buf->zero_terminated; > } > > APR_DECLARE(void *) apr_buffer_mem(const apr_buffer_t *buf, apr_size_t *size) > @@ -227,11 +214,12 @@ APR_DECLARE(apr_buffer_t *) apr_buffer_a > for (i = 0; i < nelts; i++) { > > /* absolute value is size of mem buffer including optional > terminating zero */ > - apr_size_t size = src->size < 0 ? -src->size : src->size; > + apr_size_t size = src->size + src->zero_terminated; > > void *mem = alloc(ctx, size); Check mem == NULL => APR_ENOMEM ? > memcpy(mem, src->d.mem, size); > > + dst->zero_terminated = src->zero_terminated; > dst->size = src->size; > dst->d.mem = mem; > > @@ -256,22 +244,25 @@ APR_DECLARE(apr_buffer_t *) apr_buffer_c > > dst->d.mem = NULL; > dst->size = 0; > + dst->zero_terminated = 0; > > } > else if (!alloc) { > > dst->d.mem = src->d.mem; > dst->size = src->size; > - > + dst->zero_terminated = src->zero_terminated; > + > } > else { > > /* absolute value is size of mem buffer including optional > terminating zero */ > - apr_size_t size = src->size < 0 ? -src->size : src->size; > + apr_size_t size = src->size + src->zero_terminated; > > void *mem = alloc(ctx, size); > memcpy(mem, src->d.mem, size); > > + dst->zero_terminated = src->zero_terminated; > dst->size = src->size; > dst->d.mem = mem; Maybe we shouldn't check/accept src == NULL (and let it crash), and alloc == NULL (it's a simple memcpy functionally, the user can do it already), so just do the deep copy (apr_buffer_copy() => apr_buffer_clone() ?) > > @@ -280,33 +271,7 @@ APR_DECLARE(apr_buffer_t *) apr_buffer_c > return dst; > } > > - > APR_DECLARE(int) apr_buffer_cmp(const apr_buffer_t *src, > - const apr_buffer_t *dst) > -{ > - apr_size_t slen = apr_buffer_len(src); > - apr_size_t dlen = apr_buffer_len(dst); > - > - if (slen != dlen) { > - return slen < dlen ? -1 : 1; > - } > - else if (src->d.mem == dst->d.mem) { > - /* identical data or both NULL */ > - return 0; > - } > - else if (!src->d.mem) { > - return -1; > - } > - else if (!dst->d.mem) { > - return 1; > - } > - else { > - return memcmp(src->d.mem, dst->d.mem, slen); > - } > -} > - > - > -APR_DECLARE(int) apr_buffer_ncmp(const apr_buffer_t *src, > const apr_buffer_t *dst) > { > if (!src || !src->d.mem) { > @@ -322,7 +287,17 @@ APR_DECLARE(int) apr_buffer_ncmp(const a > return 1; > } > else { > - return apr_buffer_cmp(src, dst); > + > + apr_size_t slen = apr_buffer_len(src); > + apr_size_t dlen = apr_buffer_len(dst); > + > + if (slen != dlen) { > + return slen < dlen ? -1 : 1; > + } > + else { > + return memcmp(src->d.mem, dst->d.mem, slen); > + } > + > } > } > } APR_DECLARE(int) apr_buffer_cmp(const apr_buffer_t *src, const apr_buffer_t *dst) { apr_size_t slen, dlen; if (!src) { return dst ? 1 : 0; } if (!dst) { return -1; } if (src->size != dst->size) { return src->size < dst->size ? -1 : 1; } return memcmp(src->d.mem, dst->d.mem, slen); } Though I still don't think that we should handle NULLs here (let it crash..). > @@ -344,17 +319,17 @@ APR_DECLARE(char *) apr_buffer_pstrncat( > size += seplen; > } > > - if (src->size < 0) { > - size += (apr_size_t)(-src->size) - 1; > + if (src->zero_terminated) { > + size += src->size; Why not honor APR_BUFFER_BASE64 if it's a string buffer? > } > else { > if (APR_BUFFER_NONE == flags) { s/APR_BUFFER_NONE/APR_BUFFER_PLAIN/ ? > - size += (apr_size_t)src->size; > + size += src->size; > } > else if (APR_BUFFER_BASE64 == flags) { > apr_size_t b64len; > > - if (APR_SUCCESS != apr_encode_base64(NULL, src->d.mem, > (apr_size_t)src->size, > + if (APR_SUCCESS != apr_encode_base64(NULL, src->d.mem, > src->size, > APR_ENCODE_NONE, > &b64len)) { > return NULL; > } > @@ -379,19 +354,19 @@ APR_DECLARE(char *) apr_buffer_pstrncat( > strncpy(dst, sep, seplen); > dst += seplen; > } > - > - if (src->size < 0) { > - strncpy(dst, src->d.str, (apr_size_t)((-src->size) - 1)); > - dst += (-src->size) - 1; > + > + if (src->zero_terminated) { > + strncpy(dst, src->d.str, src->size); > + dst += src->size; > } > else { > if (APR_BUFFER_NONE == flags) { > - memcpy(dst, src->d.mem, (apr_size_t)src->size); > + memcpy(dst, src->d.mem, src->size); > } > else if (APR_BUFFER_BASE64 == flags) { > apr_size_t b64len; > > - if (APR_SUCCESS != apr_encode_base64(dst, src->d.mem, > (apr_size_t)src->size, > + if (APR_SUCCESS != apr_encode_base64(dst, src->d.mem, > src->size, > APR_ENCODE_NONE, > &b64len)) { > return NULL; > } > > --- apr/apr/trunk/include/apr_buffer.h (original) > +++ apr/apr/trunk/include/apr_buffer.h Mon Apr 22 12:46:37 2024 > @@ -73,13 +73,17 @@ typedef struct > void *mem; > } d; > > - /** size of the data. If positive, the data is of fixed size. If > - * negative, the data is zero terminated and the absolute value > - * represents the data length including terminating zero. > - * > - * we use apr_off_t to make it simple to detect overflow. > - */ > - apr_off_t size; > + /** is the data zero terminated */ > + unsigned int zero_terminated:1; > + > + /** size of the data, excluding terminating zero */ > +#if defined(SIZE_MAX) && SIZE_MAX == APR_UINT64_MAX > + apr_size_t size:63; > +#elif defined(SIZE_MAX) && SIZE_MAX == APR_UINT32_MAX > + apr_size_t size:31; > +#else > +#error sizeof size_t is neither 64 nor 32 bit (SIZE_MAX not defined) > +#endif Is sizeof(apr_buffer_t) still 8 (32bit) or 16 (64bit) with ->zero_terminated and ->size in two bitmaps of different type/width (unsigned int for the former, apr_size_t for the latter)? It would be simpler to have both use apr_size_t IMHO. Regards; Yann.