From e043158aa58284e020da774863dc98c51f930cd7 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Tue, 16 Apr 2024 20:54:51 +0000 Subject: [PATCH] Add the apr_buffer API. An APR buffer is a structure that can contain a zero terminated string, or a non zero terminated block of memory, and allow such structures to be passed around and handled in a memory efficient way. We allow two buffers to be compared without duplicating strings. Memory buffers can be converted to string buffers safely. The contents of buffers can be copied into and out of other systems like caches using memory allocation callbacks. Used by the new LDAP API, where string support has been deprecated in favour of fixed sized buffers. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1917047 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 2 + buffer/apr_buffer.c | 408 ++++++++++++++++++++++++++++++++++++++++++ build.conf | 1 + include/apr_buffer.h | 409 +++++++++++++++++++++++++++++++++++++++++++ test/Makefile.in | 2 +- test/Makefile.win | 1 + test/NWGNUaprtest | 1 + test/abts_tests.h | 3 +- test/testbuffer.c | 233 ++++++++++++++++++++++++ test/testutil.h | 1 + 10 files changed, 1059 insertions(+), 2 deletions(-) create mode 100644 buffer/apr_buffer.c create mode 100644 include/apr_buffer.h create mode 100644 test/testbuffer.c diff --git a/CHANGES b/CHANGES index 9f1f764be0d..f47c457d901 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -*- coding: utf-8 -*- Changes for APR 2.0.0 + *) apr_buffer: Add the apr_buffer API. [Graham Leggett] + *) apr_escape: Add apr_escape_json() and apr_pescape_json(). [Graham Leggett] diff --git a/buffer/apr_buffer.c b/buffer/apr_buffer.c new file mode 100644 index 00000000000..10e331100ac --- /dev/null +++ b/buffer/apr_buffer.c @@ -0,0 +1,408 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "apr.h" +#include "apr_lib.h" +#if 0 +#define APR_WANT_STDIO +#define APR_WANT_STRFUNC +#endif +#include "apr_want.h" +#include "apr_buffer.h" +#include "apr_encode.h" +#include "apr_strings.h" +#include "apr_private.h" + + +APR_DECLARE(apr_status_t) apr_buffer_mem_set(apr_buffer_t *buf, + void *mem, apr_size_t len) +{ + + if (len > APR_INT64_MAX) { + return APR_EINVAL; + } + + buf->d.mem = mem; + buf->size = len; + + return APR_SUCCESS; +} + +APR_DECLARE(apr_buffer_t *) apr_buffer_mem_make(apr_pool_t *pool, + void *mem, apr_size_t len) +{ + apr_buffer_t *buf; + + if (len > APR_INT64_MAX) { + return NULL; + } + + buf = apr_palloc(pool, sizeof(apr_buffer_t)); + + if (buf) { + buf->d.mem = mem; + buf->size = len; + } + + return buf; +} + +APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf, + char *str, apr_ssize_t len) +{ + + if (len > APR_INT64_MAX) { + return APR_EINVAL; + } + + if (!str) { + buf->d.str = NULL; + buf->size = 0; + } + else if (len < 0) { + apr_size_t slen = strlen(str); + if (slen <= APR_INT64_MAX) { + buf->d.str = str; + buf->size = -(slen + 1); + } + else { + buf->d.str = NULL; + buf->size = 0; + } + } + else { + buf->d.str = str; + buf->size = -(len + 1); + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_buffer_t *) apr_buffer_str_make(apr_pool_t *pool, + char *str, apr_ssize_t len) +{ + apr_buffer_t *buf; + apr_int64_t size; + apr_size_t slen; + + if (!str) { + str = NULL; + size = 0; + } + if (APR_BUFFER_STRING == len && !str[0]) { + size = len; + } + else if (len < 0) { + slen = strlen(str); + if (slen <= APR_INT64_MAX) { + size = -(slen + 1); + } + else { + return NULL; + } + } + else { + size = -(len + 1); + } + + buf = apr_palloc(pool, sizeof(apr_buffer_t)); + + if (buf) { + buf->d.str = str; + buf->size = size; + } + + return buf; +} + +APR_DECLARE(apr_buffer_t *) apr_buffer_null_make(apr_pool_t *pool) +{ + return apr_pcalloc(pool, sizeof(apr_buffer_t)); +} + +APR_DECLARE(apr_size_t) apr_buffer_len(const apr_buffer_t *buf) +{ + if (buf->size < 0) { + return (apr_size_t)((-buf->size) - 1); + } + else { + return (apr_size_t)buf->size; + } +} + +APR_DECLARE(apr_size_t) apr_buffer_allocated(const apr_buffer_t *buf) +{ + if (buf->size < 0) { + return (apr_size_t)((-buf->size)); + } + else { + return (apr_size_t)buf->size; + } +} + +APR_DECLARE(int) apr_buffer_is_null(const apr_buffer_t *buf) +{ + if (!buf->d.mem) { + return 1; + } + else { + return 0; + } +} + +APR_DECLARE(int) apr_buffer_is_str(const apr_buffer_t *buf) +{ + if (buf->size < 0) { + return 1; + } + else { + return 0; + } +} + +APR_DECLARE(char *) apr_buffer_str(const apr_buffer_t *buf) +{ + if (buf->size < 0) { + return buf->d.str; + } + else { + return NULL; + } +} + +APR_DECLARE(char *) apr_buffer_pstrdup(apr_pool_t *pool, const apr_buffer_t *buf) +{ + if (buf->size < 0) { + return apr_pstrmemdup(pool, buf->d.str, -(buf->size + 1)); + } + else { + return apr_pstrmemdup(pool, buf->d.str, buf->size); + } +} + +APR_DECLARE(void *) apr_buffer_mem(const apr_buffer_t *buf, apr_size_t *size) +{ + if (size) { + size[0] = apr_buffer_len(buf); + } + + return buf->d.mem; +} + +APR_DECLARE(void *) apr_buffer_pmemdup(apr_pool_t *pool, const apr_buffer_t *buf, apr_size_t *size) +{ + apr_size_t len = apr_buffer_len(buf); + + if (size) { + size[0] = len; + } + + return apr_pmemdup(pool, buf->d.mem, len); +} + +APR_DECLARE(apr_buffer_t *) apr_buffer_arraydup(const apr_buffer_t *src, + apr_buffer_alloc alloc, void *ctx, + int nelts) +{ + apr_buffer_t *dst = alloc(ctx, nelts * sizeof(apr_buffer_t)); + apr_buffer_t *d = dst; + + int i; + for (i = 0; i < nelts; i++) { + + /* absolute value is size of mem buffer including optional terminating zero */ + apr_int64_t size = src->size < 0 ? -src->size : src->size; + + void *mem = alloc(ctx, size); + memcpy(mem, src->d.mem, size); + + dst->size = src->size; + dst->d.mem = mem; + + src++; + dst++; + } + + return d; +} + +APR_DECLARE(apr_buffer_t *) apr_buffer_dup(const apr_buffer_t *buf, + apr_buffer_alloc alloc, void *ctx) +{ + return apr_buffer_arraydup(buf, alloc, ctx, 1); +} + +APR_DECLARE(apr_buffer_t *) apr_buffer_cpy(apr_buffer_t *dst, + const apr_buffer_t *src, + apr_buffer_alloc alloc, void *ctx) +{ + if (!src) { + + dst->d.mem = NULL; + dst->size = 0; + + } + else if (!alloc) { + + dst->d.mem = src->d.mem; + dst->size = src->size; + + } + else { + + /* absolute value is size of mem buffer including optional terminating zero */ + apr_int64_t size = src->size < 0 ? -src->size : src->size; + + void *mem = alloc(ctx, size); + memcpy(mem, src->d.mem, size); + + dst->size = src->size; + dst->d.mem = mem; + + } + + 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) { + if (!dst || !dst->d.mem) { + return 0; + } + else { + return -1; + } + } + else { + if (!dst || !dst->d.mem) { + return 1; + } + else { + return apr_buffer_cmp(src, dst); + } + } +} + +APR_DECLARE(char *) apr_buffer_pstrncat(apr_pool_t *p, const apr_buffer_t *buf, + int nelts, const char *sep, int flags, + apr_size_t *nbytes) +{ + const apr_buffer_t *src = buf; + apr_size_t seplen = sep ? strlen(sep) : 0; + apr_size_t size = 0; + + char *dst, *str; + + int i; + for (i = 0; i < nelts; i++) { + + if (i > 0) { + size += seplen; + } + + if (src->size < 0) { + size += (-src->size) - 1; + } + else { + if (APR_BUFFER_NONE == flags) { + size += src->size; + } + else if (APR_BUFFER_BASE64 == flags) { + apr_size_t b64len; + + if (APR_SUCCESS != apr_encode_base64(NULL, src->d.mem, src->size, + APR_ENCODE_NONE, &b64len)) { + return NULL; + } + size += b64len - 1; + } + } + + src++; + } + + if (nbytes) { + *nbytes = size; + } + + str = dst = apr_palloc(p, size + 1); + + src = buf; + + for (i = 0; i < nelts; i++) { + + if (i > 0 && sep) { + strncpy(dst, sep, seplen); + dst += seplen; + } + + if (src->size < 0) { + strncpy(dst, src->d.str, (-src->size) - 1); + dst += (-src->size) - 1; + } + else { + if (APR_BUFFER_NONE == flags) { + 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, src->size, + APR_ENCODE_NONE, &b64len)) { + return NULL; + } + dst += b64len; + } + } + + src++; + } + + dst[0] = 0; + + return str; +} + diff --git a/build.conf b/build.conf index 9a16e17903a..e25a1eee8fa 100644 --- a/build.conf +++ b/build.conf @@ -10,6 +10,7 @@ paths = strings/*.c tables/*.c buckets/*.c + buffer/*.c crypto/apr_crypto.c crypto/apr_crypto_prng.c crypto/apr_md4.c diff --git a/include/apr_buffer.h b/include/apr_buffer.h new file mode 100644 index 00000000000..ad481f6672e --- /dev/null +++ b/include/apr_buffer.h @@ -0,0 +1,409 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file apr_buffer.h + * @brief APR-UTIL Buffer + */ +#ifndef APR_BUFFER_H +#define APR_BUFFER_H + +/** + * @defgroup APR_Util_Buffer Buffer handling + * + * An APR buffer is a structure that can contain a zero terminated string, or + * a non zero terminated block of memory, and allow such structures to be + * passed around and handled in a memory efficient way. + * + * We allow two buffers to be compared without duplicating strings. Memory + * buffers can be converted to string buffers safely. The contents of buffers + * can be copied into and out of other systems like caches using memory + * allocation callbacks. + * @ingroup APR_Util + * @{ + */ + +#include "apr.h" +#include "apr_pools.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * When passing a string to apr_buffer_str_make, this value can be + * passed to indicate a string with unknown length, and have apr_buffer_str_make + * compute the length automatically. + */ +#define APR_BUFFER_STRING (-1) + + +/** + * Perform no encoding on memory buffers during apr_buffer_pstrcat(). + */ +#define APR_BUFFER_NONE 0 +/** + * Perform base64 encoding on memory buffers during apr_buffer_pstrcat(). + */ +#define APR_BUFFER_BASE64 1 + + +/** + * Structure for efficiently tracking a buffer that could contain + * a zero terminated string, or a fixed length non zero string. + */ +typedef struct +{ + /** pointer to the data, which could be a string or a memory block. */ + union { + char *str; + 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_int64_t to make it simple to detect overflow. + */ + apr_int64_t size; + +} apr_buffer_t; + + +/** + * Set a apr_buffer_t with non zero terminated memory. + * + * @param buf The buffer to allocate to + * @param mem The memory buffer to assign to the buffer + * @param len The length of the memory buffer + * @return APR_SUCCESS, or APR_EINVAL if len overflows. + */ +APR_DECLARE(apr_status_t) apr_buffer_mem_set(apr_buffer_t *buf, + void *mem, apr_size_t len) + __attribute__((nonnull(1))); + + +/** + * Make a apr_buffer_t containing a non zero terminated memory. + * + * The buffer structure is allocated from the pool, while the contents are + * stored as is. It is the responsibility of the caller to ensure the + * contents have a lifetime as long as the pool. + * @param pool The pool to allocate from + * @param mem The memory to assign to the buffer + * @param len The length of the memory + * @return The apr_buffer_t we just made. Returns NULL if we could not + * allocate enough memory, or if len overflows. + */ +APR_DECLARE(apr_buffer_t *) apr_buffer_mem_make(apr_pool_t *pool, + void *mem, apr_size_t len) + __attribute__((nonnull(1))); + +/** + * Set a apr_buffer_t with a zero terminated string. + * + * @param buf The buffer to assign the data to. + * @param str The zero terminated string to assign to the buffer. + * @param len The length of the string without terminating zero, or + * APR_BUFFER_STRING to have the length calculated. + * @return APR_SUCCESS, or APR_EINVAL if len overflows. + */ +APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf, + char *str, apr_ssize_t len) + __attribute__((nonnull(1))); + +/** + * Make a apr_buffer_t containing a zero terminated string. + * + * The buffer structure is allocated from the pool, while the contents are + * stored as is. It is the responsibility of the caller to ensure the + * contents have a lifetime as long as the pool. + * @param pool The pool to allocate from. + * @param str The string to assign to the buffer. + * @param len The length of the string, or APR_BUFFER_STRING to have the length + * calculated. + * @return The apr_buffer_t we just made. Returns NULL if we could not + * allocate memory, or if len overflows. + */ +APR_DECLARE(apr_buffer_t *) apr_buffer_str_make(apr_pool_t *pool, + char *str, apr_ssize_t len) + __attribute__((nonnull(1))); + +/** + * Make a apr_buffer_t containing a NULL payload. + * + * The buffer structure is allocated from the pool. + * @param pool The pool to allocate from. + * @return The apr_buffer_t we just made. Returns NULL if we could not + * allocate memory. + */ +APR_DECLARE(apr_buffer_t *) apr_buffer_null_make(apr_pool_t *pool) + __attribute__((nonnull(1))); + + +/** + * Does the buffer contain a NULL buffer. + * + * If the internal buffer is NULL, 1 is returned, otherwise 0. + * + * @param buf The buffer. + * @return Returns 1 if buffer is null, otherwise 0. + */ +APR_DECLARE(int) apr_buffer_is_null(const apr_buffer_t *buf) + __attribute__((nonnull(1))); + + +/** + * Does the buffer contain a zero terminated string. + * + * If the buffer is already zero terminated, 1 is returned, otherwise 0. + * + * @param buf The buffer. + * @return Returns 1 if zero terminated, otherwise 0. + */ +APR_DECLARE(int) apr_buffer_is_str(const apr_buffer_t *buf) + __attribute__((nonnull(1))); + + +/** + * Return the zero terminated string from a buffer containing a + * string. + * + * If the buffer contains a string, the original string + * is returned. + * + * If the buffer contains non zero terminated memory, NULL will be + * returned. + * + * Use this function when we want to be sure you're dealing with + * a string, and want to avoid duplication. + * @param buf The string/memory buffer. + * @return The zero terminated string. Returns NULL if the buffer + * contains memory. + */ +APR_DECLARE(char *) apr_buffer_str(const apr_buffer_t *buf) + __attribute__((nonnull(1))); + + +/** + * Return a copy of the buffer as a zero terminated string allocated from + * a pool. + * + * The memory or string buffer will be copied, as appropriate. + * + * Use this function when you need the buffer to become a string with + * the lifetime of the pool provided. + * @param pool The pool to allocate from. + * @param buf The buffer. + * @return The zero terminated string. Returns NULL if we could not + * allocate memory. + */ +APR_DECLARE(char *) apr_buffer_pstrdup(apr_pool_t *pool, const apr_buffer_t *buf) + __attribute__((nonnull(1,2))); + + +/** + * Return the non zero terminated string/memory buffer. + * + * If a size is provided, the size of the buffer without the terminating zero + * will be returned. + * + * Use this function when you need to pass the content of the buffer to an + * API requiring an area of memory and a length. + * @param buf The string/memory buffer. + * @param size Location to write the size to. + * @return The memory buffer. + */ +APR_DECLARE(void *) apr_buffer_mem(const apr_buffer_t *buf, apr_size_t *size) + __attribute__((nonnull(1))); + + +/** + * Return a copy of the content of a buffer as non zero terminated memory + * allocated from a pool. + * + * If a size is provided, the size of the buffer will be returned. + * @param pool The pool to allocate from. + * @param buf The string/memory buffer. + * @param size Location to write the size to. + * @return The zero memory buffer. + */ +APR_DECLARE(void *) apr_buffer_pmemdup(apr_pool_t *pool, const apr_buffer_t *buf, apr_size_t *size) + __attribute__((nonnull(1,2))); + + +/** + * Return the buffer length. + * + * The size of the underlying buffer is returned, excluding the terminating + * zero if present. + * + * Use this function to know the length of the data in the buffer. + * @param buf The string/memory buffer. + * @return The size of the buffer, excluding terminating zero if present. + */ +APR_DECLARE(apr_size_t) apr_buffer_len(const apr_buffer_t *buf) + __attribute__((nonnull(1))); + + +/** + * Return the allocated length. + * + * The size of the underlying buffer is returned, including the terminating + * zero if present. + * + * Use this function when you need to know how much memory the buffer is + * taking up. + * @param buf The string/memory buffer. + * @return The size of the buffer, including terminating zero if present. + */ +APR_DECLARE(apr_size_t) apr_buffer_allocated(const apr_buffer_t *buf) + __attribute__((nonnull(1))); + + +/** + * Function called to allocate memory in the buffer functions. + * + * This allows buffers to be copied into and out of shared memory, or memory + * from other systems. + */ +typedef void *(*apr_buffer_alloc)(void *ctx, apr_size_t size); + +/** + * Return a copy of an array of memory buffers. + * + * This function allows you to make a copy of one or more buffers, controlling + * the memory allocation yourself. + * + * Use this function to copy buffers, and the contents of the buffers, into and + * out of a cache. + * @param buf The string/memory buffer. + * @param alloc The function callback to allocate memory for the buffer + * @param ctx Context to pass to the callback function + * @param nelts Number of buffers to duplicate + * @return The array of duplicated buffers. + */ +APR_DECLARE(apr_buffer_t *) apr_buffer_arraydup(const apr_buffer_t *buf, + apr_buffer_alloc alloc, void *ctx, + int nelts) + __attribute__((nonnull(1,2))); + +/** + * Return a copy of a string/memory buffer. + * + * This function allows you to make a copy of a buffer, controlling + * the memory allocation yourself. + * + * Use this function to copy a buffer, and the content of the buffer, into and + * out of a cache. + * + * @param buf The string/memory buffer. + * @param alloc The function callback to allocate memory for the buffer + * @param ctx Context to pass to the callback function + * @return The duplicated buffer. + */ +APR_DECLARE(apr_buffer_t *) apr_buffer_dup(const apr_buffer_t *buf, + apr_buffer_alloc alloc, void *ctx) + __attribute__((nonnull(1,2))); + +/** + * Copy the contents a buffer into another buffer. + * + * This function allows you to make a copy of the contents of a buffer, into + * and out of a cache. + * + * If the source buffer is NULL, the destination buffer will be assigned NULL + * as content. + * + * If the memory allocator callback is NULL, the contents of the source buffer + * will be assigned to the destination buffer as is. + * + * @param dst The first buffer + * @param src The second buffer + * @param alloc The function callback to allocate memory for the buffer + * @param ctx The context for the callback + * @return Returns dst. + */ +APR_DECLARE(apr_buffer_t *) apr_buffer_cpy(apr_buffer_t *dst, + const apr_buffer_t *src, + apr_buffer_alloc alloc, void *ctx) + __attribute__((nonnull(1))); + +/** + * Compare two buffers for equality. + * + * Each buffer can be either a string or memory buffer. + * + * A string buffer and a memory buffer are considered equal if the length + * excluding any trailing zero is equal, and the contents without the trailing + * zero are the same. + * @param dst The first buffer + * @param src The second buffer + * @return Positive, negative, or zero, depending on whether b1 is greater + * than, less than, or equal to b2. + */ +APR_DECLARE(int) apr_buffer_cmp(const apr_buffer_t *dst, + const apr_buffer_t *src) + __attribute__((nonnull(1,2))); + +/** + * Compare two possibly NULL buffers for equality. + * + * Each buffer can be either a string or memory buffer, or NULL. + * + * Two NULL buffers are considered equal. + * + * A string buffer and a memory buffer are considered equal if the length + * excluding any trailing zero is equal, and the contents without the trailing + * zero are the same. + * @param dst The first buffer + * @param src The second buffer + * @return Positive, negative, or zero, depending on whether b1 is greater + * than, less than, or equal to b2. + */ +APR_DECLARE(int) apr_buffer_ncmp(const apr_buffer_t *dst, + const apr_buffer_t *src); + +/** + * Concatenate multiple buffers and return a string. + * + * If the buffer contains a string, it will be copied across as is, memory + * buffers will be transformed by the flags specified before concatenation. + * + * This function can be used with an apr_array_header_t. + * + * @param p The pool from which to allocate + * @param buf The buffers to concatenate + * @param nelts The number of buffers to concatenate + * @param sep The optional separator between strings + * @param flags Allow memory buffers to be transformed before concatenation. + * APR_BUFFER_NONE copies memory buffer as is. APR_BUFFER_BASE64 + * applies base64 encoding to the memory buffer. + * @param nbytes (output) strlen of new string (pass in NULL to omit) + * @return The new string + */ +APR_DECLARE(char *) apr_buffer_pstrncat(apr_pool_t *p, const apr_buffer_t *buf, + int nelts, const char *sep, int flags, + apr_size_t *nbytes); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* APR_BUFFER_H */ + diff --git a/test/Makefile.in b/test/Makefile.in index 4af81ab6066..0e6d5ac5a9d 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -38,7 +38,7 @@ TESTS = testtime.lo teststr.lo testvsn.lo testipsub.lo testshm.lo \ testreslist.lo testbase64.lo testhooks.lo testlfsabi.lo \ testlfsabi32.lo testlfsabi64.lo testescape.lo testskiplist.lo \ testsiphash.lo testredis.lo testencode.lo testjson.lo \ - testjose.lo + testjose.lo testbuffer.lo OTHER_PROGRAMS = \ echod@EXEEXT@ \ diff --git a/test/Makefile.win b/test/Makefile.win index ff96bee331e..585b509c44d 100644 --- a/test/Makefile.win +++ b/test/Makefile.win @@ -83,6 +83,7 @@ ALL_TESTS = \ $(INTDIR)\testatomic.obj \ $(INTDIR)\testbase64.obj \ $(INTDIR)\testbuckets.obj \ + $(INTDIR)\testbuffer.obj \ $(INTDIR)\testcond.obj \ $(INTDIR)\testcrypto.obj \ $(INTDIR)\testdate.obj \ diff --git a/test/NWGNUaprtest b/test/NWGNUaprtest index 0a8bc01ceb9..da608381306 100644 --- a/test/NWGNUaprtest +++ b/test/NWGNUaprtest @@ -174,6 +174,7 @@ FILES_nlm_objs = \ $(OBJDIR)/testatomic.o \ $(OBJDIR)/testbase64.o \ $(OBJDIR)/testbuckets.o \ + $(OBJDIR)/testbuffer.o \ $(OBJDIR)/testcond.o \ $(OBJDIR)/testcrypto.o \ $(OBJDIR)/testdate.o \ diff --git a/test/abts_tests.h b/test/abts_tests.h index b0302067cd8..9d891ee8238 100644 --- a/test/abts_tests.h +++ b/test/abts_tests.h @@ -93,7 +93,8 @@ const struct testlist { {testskiplist}, {testsiphash}, {testjson}, - {testjose} + {testjose}, + {testbuffer} }; #endif /* APR_TEST_INCLUDES */ diff --git a/test/testbuffer.c b/test/testbuffer.c new file mode 100644 index 00000000000..4a7df0e3028 --- /dev/null +++ b/test/testbuffer.c @@ -0,0 +1,233 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testutil.h" +#include "apr_general.h" +#include "apr_buffer.h" +#include "apr_tables.h" + +static unsigned char test_memory[] = {1, 2, 3, 4}; +static char *test_string = "Hello"; + +static void test_memory_buffer(abts_case *tc, void *data) +{ + apr_pool_t *pool; + apr_buffer_t *mb; + char *str; + void *mem; + apr_size_t len; + + apr_pool_create(&pool, p); + + mb = apr_buffer_mem_make(pool, test_memory, sizeof(test_memory)); + + ABTS_ASSERT(tc, "mem_make stored wrong data", + memcmp(test_memory, mb->d.mem, sizeof(test_memory)) == 0); + + ABTS_ASSERT(tc, "memory buffer is NULL", + !apr_buffer_is_null(mb)); + + ABTS_ASSERT(tc, "memory buffer is a string", + !apr_buffer_is_str(mb)); + + ABTS_ASSERT(tc, "memory buffer returned a string", + apr_buffer_str(mb) == NULL); + + ABTS_ASSERT(tc, "memory buffer returned memory", + apr_buffer_mem(mb, NULL) != NULL); + + str = apr_buffer_pstrdup(pool, mb); + ABTS_ASSERT(tc, "memory buffer returned same memory", + str != mb->d.mem && !memcmp(str, mb->d.mem, strlen(str))); + + mem = apr_buffer_pmemdup(pool, mb, &len); + ABTS_ASSERT(tc, "memory buffer returned same memory", + mem != mb->d.mem && !memcmp(mem, mb->d.mem, len)); + + ABTS_ASSERT(tc, "memory buffer has wrong length", + apr_buffer_len(mb) == sizeof(test_memory)); + + ABTS_ASSERT(tc, "memory buffer has wrong allocation", + apr_buffer_allocated(mb) == sizeof(test_memory)); + + + apr_pool_destroy(pool); +} + +static void test_string_buffer(abts_case *tc, void *data) +{ + apr_pool_t *pool; + apr_buffer_t *sb; + char *str; + void *mem; + apr_size_t len; + + apr_pool_create(&pool, p); + + sb = apr_buffer_str_make(pool, test_string, APR_BUFFER_STRING); + + ABTS_ASSERT(tc, "str_make stored wrong data", + strncmp(test_string, sb->d.str, strlen(test_string)) == 0); + + ABTS_ASSERT(tc, "str_make's buffer is NULL", + !apr_buffer_is_null(sb)); + + ABTS_ASSERT(tc, "str_make's buffer is not a string", + apr_buffer_is_str(sb)); + + ABTS_ASSERT(tc, "str_make's buffer didn't return a string", + apr_buffer_str(sb) != NULL); + + ABTS_ASSERT(tc, "str_make's buffer returned memory", + apr_buffer_mem(sb, NULL) != NULL); + + str = apr_buffer_pstrdup(pool, sb); + ABTS_ASSERT(tc, "string buffer returned same string", + str != sb->d.str && !strncmp(str, sb->d.str, strlen(str))); + + mem = apr_buffer_pmemdup(pool, sb, &len); + ABTS_ASSERT(tc, "string buffer returned same memory", + mem != sb->d.str && !memcmp(mem, sb->d.str, len)); + + ABTS_ASSERT(tc, "string buffer has wrong length", + apr_buffer_len(sb) == strlen(test_string)); + + ABTS_ASSERT(tc, "string buffer has wrong allocation", + apr_buffer_allocated(sb) == strlen(test_string) + 1); + + apr_pool_destroy(pool); +} + +static void *test_buffers_palloc(void *ctx, apr_size_t size) +{ + apr_pool_t *pool = ctx; + + return apr_palloc(pool, size); +} + +static void test_buffers(abts_case *tc, void *data) +{ + apr_pool_t *pool; + apr_array_header_t *vals; + + apr_buffer_t src[2]; + apr_buffer_t *dst; + + char *str; + apr_size_t len; + + memset(&src, 0, sizeof(src)); + + apr_pool_create(&pool, p); + + /* populate our source buffers */ + apr_buffer_mem_set(&src[0], test_memory, sizeof(test_memory)); + apr_buffer_str_set(&src[1], test_string, strlen(test_string)); + + /* duplicate the source buffers, allocating memory from a pool */ + vals = apr_array_make(pool, 2, sizeof(apr_buffer_t)); + vals->elts = (char *)apr_buffer_arraydup(src, test_buffers_palloc, pool, 2); + vals->nelts = 2; + + dst = apr_array_pop(vals); + + ABTS_ASSERT(tc, "second buffer compare fail", + !apr_buffer_cmp(dst, &src[1])); + + dst = apr_array_pop(vals); + + ABTS_ASSERT(tc, "first buffer compare fail", + !apr_buffer_cmp(dst, &src[0])); + + dst = apr_buffer_cpy(dst, &src[1], test_buffers_palloc, pool); + + ABTS_ASSERT(tc, "buffer copy fail", + !apr_buffer_cmp(dst, &src[1])); + + str = apr_buffer_pstrncat(pool, &src[0], 2, "; ", APR_BUFFER_BASE64, &len); + + ABTS_ASSERT(tc, "buffer strcat fail", + !strcmp(str, "AQIDBA==; Hello")); + + apr_pool_destroy(pool); +} + +static void test_compare_buffers(abts_case *tc, void *data) +{ + apr_pool_t *pool; + + apr_buffer_t *small; + apr_buffer_t *large; + + char *same = "same"; + + apr_pool_create(&pool, p); + + small = NULL; + large = NULL; + ABTS_ASSERT(tc, "NULL equals NULL", + !apr_buffer_ncmp(small, large)); + + small = apr_buffer_null_make(pool); + ABTS_ASSERT(tc, "null buffer equals NULL", + !apr_buffer_ncmp(small, large)); + + large = apr_buffer_null_make(pool); + ABTS_ASSERT(tc, "null buffer equals null buffer", + !apr_buffer_ncmp(small, large)); + + small = NULL; + ABTS_ASSERT(tc, "NULL equals null buffer", + !apr_buffer_ncmp(small, large)); + + small = apr_buffer_str_make(pool, same, APR_BUFFER_STRING); + large = apr_buffer_str_make(pool, same, APR_BUFFER_STRING); + ABTS_ASSERT(tc, "pointer equals same pointer", + !apr_buffer_ncmp(small, large)); + + small = apr_buffer_str_make(pool, "same", APR_BUFFER_STRING); + large = apr_buffer_str_make(pool, "same", APR_BUFFER_STRING); + ABTS_ASSERT(tc, "'same' equals 'same'", + !apr_buffer_ncmp(small, large)); + + small = apr_buffer_str_make(pool, "short", APR_BUFFER_STRING); + large = apr_buffer_str_make(pool, "l o n g", APR_BUFFER_STRING); + ABTS_ASSERT(tc, "'short' less than 'l o n g'", + apr_buffer_cmp(small, large) < 0); + ABTS_ASSERT(tc, "'l o n g' greater than 'short'", + apr_buffer_cmp(large, small) > 0); + + small = apr_buffer_str_make(pool, "aardvark", APR_BUFFER_STRING); + large = apr_buffer_str_make(pool, "zucchini", APR_BUFFER_STRING); + ABTS_ASSERT(tc, "'aardvark' less than 'zucchini'", + apr_buffer_cmp(small, large) < 0); + ABTS_ASSERT(tc, "'zucchini' greater than 'aardvark'", + apr_buffer_cmp(large, small) > 0); + + apr_pool_destroy(pool); +} + +abts_suite *testbuffer(abts_suite *suite) +{ + suite = ADD_SUITE(suite); + + abts_run_test(suite, test_memory_buffer, NULL); + abts_run_test(suite, test_string_buffer, NULL); + abts_run_test(suite, test_buffers, NULL); + abts_run_test(suite, test_compare_buffers, NULL); + + return suite; +} diff --git a/test/testutil.h b/test/testutil.h index 8ac6084f8c2..ceab97afa48 100644 --- a/test/testutil.h +++ b/test/testutil.h @@ -136,5 +136,6 @@ abts_suite *testskiplist(abts_suite *suite); abts_suite *testsiphash(abts_suite *suite); abts_suite *testjson(abts_suite *suite); abts_suite *testjose(abts_suite *suite); +abts_suite *testbuffer(abts_suite *suite); #endif /* APR_TEST_INCLUDES */