new library functions (#867)

* ares__buf_split_str: Ability to split a buf object into a C array of C
strings
* ares__array_insertdata_{at,last,first}: copy data provided into array
member as a helper
* ares_free_array: Free a normal C array with custom callback to free
each element

Authored-By: Brad House (@bradh352)
pull/868/head
Brad House 3 months ago committed by GitHub
parent ed2d7f6e1d
commit 24f7cb93d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 40
      src/lib/dsa/ares__array.c
  2. 47
      src/lib/include/ares__array.h
  3. 22
      src/lib/include/ares__buf.h
  4. 12
      src/lib/include/ares_str.h
  5. 69
      src/lib/str/ares__buf.c
  6. 22
      src/lib/str/ares_str.c
  7. 11
      src/lib/str/ares_strsplit.c
  8. 18
      test/ares-test-internal.cc

@ -265,6 +265,46 @@ ares_status_t ares__array_insert_first(void **elem_ptr, ares__array_t *arr)
return ares__array_insert_at(elem_ptr, arr, 0);
}
ares_status_t ares__array_insertdata_at(ares__array_t *arr, size_t idx,
void *data_ptr)
{
ares_status_t status;
void *ptr = NULL;
status = ares__array_insert_at(&ptr, arr, idx);
if (status != ARES_SUCCESS) {
return status;
}
memcpy(ptr, data_ptr, arr->member_size);
return ARES_SUCCESS;
}
ares_status_t ares__array_insertdata_last(ares__array_t *arr, void *data_ptr)
{
ares_status_t status;
void *ptr = NULL;
status = ares__array_insert_last(&ptr, arr);
if (status != ARES_SUCCESS) {
return status;
}
memcpy(ptr, data_ptr, arr->member_size);
return ARES_SUCCESS;
}
ares_status_t ares__array_insertdata_first(ares__array_t *arr, void *data_ptr)
{
ares_status_t status;
void *ptr = NULL;
status = ares__array_insert_last(&ptr, arr);
if (status != ARES_SUCCESS) {
return status;
}
memcpy(ptr, data_ptr, arr->member_size);
return ARES_SUCCESS;
}
void *ares__array_first(ares__array_t *arr)
{
return ares__array_at(arr, 0);

@ -153,6 +153,52 @@ CARES_EXTERN ares_status_t ares__array_insert_last(void **elem_ptr,
CARES_EXTERN ares_status_t ares__array_insert_first(void **elem_ptr,
ares__array_t *arr);
/*! Insert a new array member at the given index and copy the data pointed
* to by the data pointer into the array. This will copy member_size bytes
* from the provided pointer, this may not be safe for some data types
* that may have a smaller size than the provided member_size which includes
* padding as discussed in ares__array_create().
*
* \param[in] arr Initialized array object.
* \param[in] idx Index in array to place new element, will shift any
* elements down that exist after this point.
* \param[in] data_ptr Pointer to data to copy into array.
* \return ARES_SUCCESS on success, ARES_EFORMERR on bad index or null data ptr,
* ARES_ENOMEM on out of memory.
*/
CARES_EXTERN ares_status_t ares__array_insertdata_at(ares__array_t *arr,
size_t idx,
void *data_ptr);
/*! Insert a new array member at the end of the array and copy the data pointed
* to by the data pointer into the array. This will copy member_size bytes
* from the provided pointer, this may not be safe for some data types
* that may have a smaller size than the provided member_size which includes
* padding as discussed in ares__array_create().
*
* \param[in] arr Initialized array object.
* \param[in] data_ptr Pointer to data to copy into array.
* \return ARES_SUCCESS on success, ARES_EFORMERR on bad index or null data ptr,
* ARES_ENOMEM on out of memory.
*/
CARES_EXTERN ares_status_t ares__array_insertdata_last(ares__array_t *arr,
void *data_ptr);
/*! Insert a new array member at the beginning of the array and copy the data pointed
* to by the data pointer into the array. This will copy member_size bytes
* from the provided pointer, this may not be safe for some data types
* that may have a smaller size than the provided member_size which includes
* padding as discussed in ares__array_create().
*
* \param[in] arr Initialized array object.
* \param[in] data_ptr Pointer to data to copy into array.
* \return ARES_SUCCESS on success, ARES_EFORMERR on bad index or null data ptr,
* ARES_ENOMEM on out of memory.
*/
CARES_EXTERN ares_status_t ares__array_insertdata_first(ares__array_t *arr,
void *data_ptr);
/*! Fetch a pointer to the given element in the array
* \param[in] array Initialized array object
* \param[in] idx Index to fetch
@ -227,6 +273,7 @@ CARES_EXTERN ares_status_t ares__array_remove_first(ares__array_t *arr);
*/
CARES_EXTERN ares_status_t ares__array_remove_last(ares__array_t *arr);
/*! @} */
#endif /* __ARES__ARRAY_H */

@ -361,7 +361,7 @@ CARES_EXTERN ares_status_t ares__buf_fetch_bytes_into_buf(ares__buf_t *buf,
/*! Fetch the requested number of bytes and return a new buffer that must be
* ares_free()'d by the caller. The returned buffer is a null terminated
* string.
* string. The data is validated to be ASCII-printable.
*
* \param[in] buf Initialized buffer object
* \param[in] len Requested number of bytes (must be > 0)
@ -482,6 +482,26 @@ CARES_EXTERN ares_status_t ares__buf_split(
ares__buf_t *buf, const unsigned char *delims, size_t delims_len,
ares__buf_split_t flags, size_t max_sections, ares__llist_t **list);
/*! Split the provided buffer into a C array of C strings.
*
* \param[in] buf Initialized buffer object
* \param[in] delims Possible delimiters
* \param[in] delims_len Length of possible delimiters
* \param[in] flags One more more flags
* \param[in] max_sections Maximum number of sections. Use 0 for
* unlimited. Useful for splitting key/value
* pairs where the delimiter may be a valid
* character in the value. A value of 1 would
* have little usefulness and would effectively
* ignore the delimiter itself.
* \param[out] strs Array of strings. Free using
* ares_free_array(strs, nstrs, ares_free)
* \param[out] nstrs Number of elements in the array.
* \return ARES_SUCCESS on success, or error like ARES_ENOMEM.
*/
CARES_EXTERN ares_status_t ares__buf_split_str(
ares__buf_t *buf, const unsigned char *delims, size_t delims_len,
ares__buf_split_t flags, size_t max_sections, char ***strs, size_t *nstrs);
/*! Check the unprocessed buffer to see if it begins with the sequence of
* characters provided.

@ -214,4 +214,16 @@ CARES_EXTERN ares_bool_t ares_strcaseeq(const char *a, const char *b);
CARES_EXTERN ares_bool_t ares_strcaseeq_max(const char *a, const char *b,
size_t n);
/*! Free a C array, each element in the array will be freed by the provided
* free function. Both NULL-terminated arrays and known length arrays are
* supported.
*
* \param[in] arr Array to be freed.
* \param[in] nmembers Number of members in the array, or SIZE_MAX for
* NULL-terminated arrays
* \param[in] freefunc Function to call on each array member (e.g. ares_free)
*/
CARES_EXTERN void ares_free_array(void *arr, size_t nmembers,
void (*freefunc)(void *));
#endif /* __ARES_STR_H */

@ -553,12 +553,20 @@ ares_status_t ares__buf_fetch_bytes_dup(ares__buf_t *buf, size_t len,
ares_status_t ares__buf_fetch_str_dup(ares__buf_t *buf, size_t len, char **str)
{
size_t remaining_len;
size_t i;
const unsigned char *ptr = ares__buf_fetch(buf, &remaining_len);
if (buf == NULL || str == NULL || len == 0 || remaining_len < len) {
return ARES_EBADRESP;
}
/* Validate string is printable */
for (i = 0; i < len; i++) {
if (!ares__isprint(ptr[i])) {
return ARES_EBADSTR;
}
}
*str = ares_malloc(len + 1);
if (*str == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
@ -884,6 +892,67 @@ done:
return status;
}
static void ares__free_split_array(void *arg)
{
void **ptr = arg;
ares_free(*ptr);
}
ares_status_t ares__buf_split_str(ares__buf_t *buf, const unsigned char *delims,
size_t delims_len, ares__buf_split_t flags,
size_t max_sections, char ***strs,
size_t *nstrs)
{
ares_status_t status;
ares__llist_t *list = NULL;
ares__llist_node_t *node;
ares__array_t *arr = NULL;
if (strs == NULL || nstrs == NULL) {
return ARES_EFORMERR;
}
*strs = NULL;
*nstrs = 0;
status = ares__buf_split(buf, delims, delims_len, flags, max_sections, &list);
if (status != ARES_SUCCESS) {
goto done;
}
arr = ares__array_create(sizeof(char *), ares__free_split_array);
if (arr == NULL) {
status = ARES_ENOMEM;
goto done;
}
for (node = ares__llist_node_first(list); node != NULL;
node = ares__llist_node_next(node)) {
ares__buf_t *lbuf = ares__llist_node_val(node);
char *str = NULL;
status = ares__buf_fetch_str_dup(lbuf, ares__buf_len(lbuf), &str);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares__array_insertdata_last(arr, &str);
if (status != ARES_SUCCESS) {
ares_free(str);
goto done;
}
}
done:
ares__llist_destroy(list);
if (status == ARES_SUCCESS) {
*strs = ares__array_finish(arr, nstrs);
} else {
ares__array_destroy(arr);
}
return status;
}
ares_bool_t ares__buf_begins_with(const ares__buf_t *buf,
const unsigned char *data, size_t data_len)
{

@ -394,3 +394,25 @@ ares_bool_t ares_streq_max(const char *a, const char *b, size_t n)
return ares_strncmp(a,b,n) == 0?ARES_TRUE:ARES_FALSE;
}
void ares_free_array(void *arrp, size_t nmembers, void (*freefunc)(void *))
{
size_t i;
void **arr = arrp;
if (arr == NULL)
return;
if (freefunc != NULL) {
if (nmembers == SIZE_MAX) {
for (i = 0; arr[i] != NULL; i++) {
freefunc(arr[i]);
}
} else {
for (i = 0; i<nmembers; i++) {
freefunc(arr[i]);
}
}
}
ares_free(arr);
}

@ -27,16 +27,7 @@
void ares__strsplit_free(char **elms, size_t num_elm)
{
size_t i;
if (elms == NULL) {
return;
}
for (i = 0; i < num_elm; i++) {
ares_free(elms[i]);
}
ares_free(elms);
ares_free_array(elms, num_elm, ares_free);
}
char **ares__strsplit_duplicate(char **elms, size_t num_elm)

@ -1349,6 +1349,24 @@ TEST_F(LibraryTest, HtableVpvp) {
ares__htable_vpvp_destroy(h);
}
TEST_F(LibraryTest, BufSplitStr) {
ares__buf_t *buf = NULL;
char **strs = NULL;
size_t nstrs = 0;
buf = ares__buf_create();
ares__buf_append_str(buf, "string1\nstring2 string3\t \nstring4");
ares__buf_split_str(buf, (const unsigned char *)"\n \t", 2, ARES_BUF_SPLIT_TRIM, 0, &strs, &nstrs);
ares__buf_destroy(buf);
EXPECT_EQ(4, nstrs);
EXPECT_TRUE(ares_streq(strs[0], "string1"));
EXPECT_TRUE(ares_streq(strs[1], "string2"));
EXPECT_TRUE(ares_streq(strs[2], "string3"));
EXPECT_TRUE(ares_streq(strs[3], "string4"));
ares_free_array(strs, nstrs, ares_free);
}
typedef struct {
ares_socket_t s;
} test_htable_asvp_t;

Loading…
Cancel
Save