Re: svn commit: r1917266 - in /apr/apr/trunk: buffer/apr_buffer.c include/apr_buffer.h test/testbuffer.c

Yann Ylavic <[email protected]>
Newsgroups gmane.comp.apache.apr.devel
Message-ID <CAKQ1sVM4jpOriMH6xNrBeqqKsF_AGcak_eUQWnjF2duiTDMksQ@mail.gmail.com>
On Mon, Apr 22, 2024 at 2:46 PM <[email protected]> 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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.